Fully customizable react tree component
npm install react-hyper-tree#### Fully customizable tree view react component
Welcome to the react hyper tree component 😄
I want to introduce you to an awesome react component for displaying tree data structure
!dependecies
!license
!min
!minzip
- render tree-like data structure
- show/hide lines
- fully custom component by providing render functions (node and drag zone) or custom class names
- tree management by global utility (treeHandlers)
- single/multiple node selection
- async loading of children
- drag and drop using 3 types of insertion (before, children, after)
- Installation
- Usage
- Properties
- API
- useTreeState API
- Node API
- Global State Manager (GSM)
- Async children
- Default properties
- Road map
- Contributing
- License
###### You can use  or  package managers
``console`
$ npm i --save react-hyper-tree
or
`console`
$ yarn add react-hyper-tree
`javascript
import React from 'react'
import Tree, { useTreeState } from 'react-hyper-tree'
const data = {
id: 1,
name: 'Parent 1',
children: [
{
id: 2,
name: 'Child 1',
children: [
{
id: 5,
name: 'Child 1__1',
},
{
id: 6,
name: 'Child 1__2',
},
{
id: 7,
name: 'Child 1__3',
},
],
},
],
}
...
const MyTreeComponent = () => {
const { required, handlers } = useTreeState({
data,
id: 'your_tree_id',
})
return (
{...handlers}
/>
)
}
`
| Props | Description |
| ----------------------- | -------------------------------------------------------------------------------------- |
| classes? | object with elements class names |
| data | nodes data, provided by _required_ prop |
| depthGap? | children indentation related to parent |
| disableHorizontalLines? | disable horizontal lines |
| disableLines? | disable all lines |
| disableVerticalLines? | disable vertical lines |
| disableTransitions? | disable transitions (improves performance) |
| displayedName? | format node content, if you use default node renderer |
| draggable?: | enable draggable mode |
| gapMode? | indentation mode |
| horizontalLineStyles? | horizontal line styles, SVG properties |
| renderDragZone? | function to render your custom drag zone |
| renderNode? | function to render your custom node |
| setOpen? | open node children, provided by _handlers_ prop |
| setSelected? | select node, provided by _handlers_ prop |
| staticNodeHeight? | set static height of node, otherwise dynamic height will be used |
| verticalLineOffset? | vertical line offset related to parent |
| verticalLineStyles? | vertical line styles, SVG properties |
| verticalLineTopOffset? | vertical line top offset |
useTreeState React hook includes the state management functionality. It prepares and transforms the data to use all functionality of the Node API.
| Property | Description |
| ------------------ | --------------------------------------------- |
| childrenKey? | set the children key, e.g. 'children' |
| data | tree-like data |
| defaultOpened? | if true, all parent will be opened |
| filter? | function to filter tree nodes |
| id | tree id, required |
| idKey? | set the data id key, e.g. 'id' |
| multipleSelect? | if true, a several nodes can be selected |
| sort? | function to sort tree nodes |
| refreshAsyncNodes? | load async children every time when open node |
| Property | Description |
| -------- | ---------------------------------------------------------------------------------------------------------- |
| handlers | handlers to manipulate node state. _setOpen_, _setLoading_, _setSelected_, _setChildren_, _setRawChildren_ |
| instance | tree view instance including all tree methods |
| required | includes enhanced tree structure |
Actually TreeView component is a renderer. It hasn't any functionality to manipulate of tree state.
| Method | Description | Typings |
| --------------- | ------------------------------------------ | ----------------------------------------------------------------------------- |
| getChildren | returns node children or empty array | () => TreeNode[] |
| getData | returns raw node data | () => any |
| getFirstChild | returns the first child | () => TreeNode | null | |
| getLastChild | returns the last child | () => TreeNode null |
| getPath | get node path | (array?: boolean) => string | string[] |
| hasChildren | returns true if node has atleast one child | () => boolean |
| isLoading | returns true if node is loading | () => boolean |
| isOpened | returns true if node is opened | () => boolean |
| isSelected | returns true if node is selected | () => boolean |
| setChildren | a simple equivalent of setNodeChildren | (children: TreeNode[]) => void |
| setData | sets node data | (data?: any) => void |
| setLoading | set node loading | (loading?: boolean) => void |
| setNodeChildren | insert node children | (children: TreeNode[], type?: InsertChildType, reset?: boolean) => TreeNode[] |
| setOpened | set node opened | (opened?: boolean) => void |
| setParent | set node parent | (parent?: TreeNode) => void |
| setSelected | set node selected | (selected?: boolean) => void |
| getPath | get node path | (array?: boolean) => string \| string[] |
| getReactKey | returns calculated property for react key | () => string |
The main goal to implement the tree view library was a simple usage and global tree manager.
Actually, global state manager (GSM) is represented as _treeHandlers_ object. It has all instances of trees in the project.
Every time you use useTreeState hook. It will create a new TreeView instance and add the instance to _treeHandlers_ object.
The GSM object has the one property _trees_.
`typescript
type Handler = (...args: any[]) => any
interface IHandlers {
[key: string]: Handler
}
interface ITreeItem {
instance: TreeView
handlers: IHandlers
}
interface ITrees {
[key: string]: ITreeItem
}
trees: ITrees
`
When you use useTreeState with the tree id, it will add tree instance to GSM. To access to tree instance you should do the next:
`javascript
import { treeHandlers } from 'react-hyper-tree'
treeHandlers.trees[your - tree - id].instance
`
You can use the full tree instance functionality from the GSM.
Also the GSM has the _handlers_ property for every tree instance.
Every tree has a default set of methods to manipulate the data
| Method | Descriptipn | Typings |
| ----------------- | --------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------- |
| rerender | rerender the tree component | (callback? () => void) => void |
| setLoading | set loading property | (node: TreeNode \| string \| number, loading?: boolean) => void |
| setOpen | set opened property | (node: TreeNode \| string \| number, toggle?: boolean) => void |
| setOpenByPath | set opened by path | (path: string) => void |
| setRawChildren | set node children, use it if you have a raw children data | (parent: TreeNode \| string \| number, children: IData[], type?: InsertChildType, reset?: boolean) => void |
| setChildren | set node children, use it if you have an enhanced children data | (parent: TreeNode \| string \| number, children: TreeNode[], type?: InsertChildType, reset?: boolean) => void |
| setSelected | set selected property | (node: TreeNode \| string \| number, selected?: boolean) => void |
| setSelectedByPath | set selected by path | (path: string, all?: boolean, toggle?: boolean) => void |
| setSiblings | set node siblings | (node: TreeNode \| string \| number, siblings: TreeNode[], type: InsertSiblingType) => void |
| getNodeData | returns node data | (node: TreeNode \| string \| number, siblings: TreeNode[]) => void |
| getNode | returns node | (node: TreeNode \| string \| number, siblings: TreeNode[]) => void |
| selectAll | select all nodes (if multipleSelect is true) | () => void |
| unselectAll | unselect all nodes | () => void |
To call any method you should do the next:
`javascript
import { treeHandlers } from 'react-hyper-tree'
treeHandlers.trees[your-tree-id].handlers.setOpen(...)
`
| Method | Description | Typings |
| ----------------- | ----------------------------- | -------------------------------------------------------------------------------------- |
| getIds | get trees ids | () => string[] |
| remove | remove tree from the GSM | (id: string): TreeHandlers |
| removeHandler | remove handler from the tree | removeHandler(treeId: string, handlerName: string): TreeHandlers |
| safeUpdate | add or update tree in the GSM | safeUpdate(id: string, tree: TreeView) => TreeHandlers |
| safeUpdateHandler | add or update tree handler | safeUpdateHandler(treeId: string, handlerName: string, handler: Handler): TreeHandlers |
You can also use _treeHandlers_ like call chain
`javascript`
treeHandlers
.safeUpdateHandler(id, 'setLoading', setLoading)
.safeUpdateHandler(id, 'setSelected', setSelected)
.safeUpdateHandler(id, 'setRawChildren', setRawChildren)
.safeUpdateHandler(id, 'setChildren', setChildren)
You also can use loadable children. To enable the feature you should provide _getChildren_ function to node data
`javascript
const getChildren = ({ node }) => {
return getChildrenByParentId(node.id)
}
const data = {
id: 1,
name: 'Parent 1',
getChildren
}
`
_getChildren_ function can return Promise and resolve the children data in format like this:
`javascript`
const getChildren = () =>
new Promise(resolve =>
setTimeout(
() =>
resolve([
{
id: 2,
name: 'Child'
}
]),
1000
)
)
You can also fire any events like redux-actions in the getChildren function. In this case you can set the children by the _GSM_
`typescript``
export const defaultProps = {
childrenKey: 'children',
classes: {} as ClassesType,
depthGap: 20,
displayedName: (node: TreeNode) => node.data.name,
filter: () => true,
gapMode: 'margin' as const,
horizontalLineStyles: { stroke: 'black', strokeWidth: 1, strokeDasharray: '1 1' },
idKey: 'id',
opened: [],
verticalLineOffset: 5,
verticalLineStyles: { stroke: 'black', strokeWidth: 1, strokeDasharray: '1 1' },
verticalLineTopOffset: 0
}
- Coverage by tests
- Inner improvements and extending functionality
- Documentation improvements
Pull requests are welcome. For major changes, please open an issue first to discuss what you would like to change.