UNPKG

react-hyper-tree

Version:
337 lines (265 loc) 17.2 kB
# 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](https://img.shields.io/badge/dependecies-no%20dependencies-green.svg) ![license](https://img.shields.io/badge/license-MIT-brightgreen.svg) ![min](https://img.shields.io/bundlephobia/min/react-hyper-tree) ![minzip](https://img.shields.io/bundlephobia/minzip/react-hyper-tree) ## Features - 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) ## Table of contents - [Installation](#installation) - [Usage](#usage) - [Properties](#properties) - [API](#use-tree-state) - [useTreeState API](#use-tree-state) - [Node API](#node-api) - [Global State Manager (GSM)](#global-state-manager) - [Async children](#async-children) - [Default properties](#default-props) - [Road map](#road-map) - [Contributing](#contributing) - [License](#license) ## [Live demo is available!](https://alekseymakhankov.github.io/packages/?package=hyper-tree) ### Check also [react-hyper-modal](https://www.npmjs.com/package/react-hyper-modal) library ## <a id="installation"></a>Installation ###### You can use [![npm](https://api.iconify.design/logos:npm.svg?height=14)](https://www.npmjs.com/get-npm) or [![yarn](https://api.iconify.design/logos:yarn.svg?height=14)](https://yarnpkg.com/lang/en/docs/install) package managers ```console $ npm i --save react-hyper-tree ``` **or** ```console $ yarn add react-hyper-tree ``` ## <a id="usage"></a>Usage ### Simple Usage ```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 ( <Tree {...required} {...handlers} /> ) } ``` ## <a id="properties"></a>Properties | 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](https://www.w3schools.com/html/html5_svg.asp) 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](https://www.w3schools.com/html/html5_svg.asp) properties | | verticalLineTopOffset? | vertical line top offset | ## <a id="use-tree-state"></a>useTreeState API useTreeState React hook includes the state management functionality. It prepares and transforms the data to use all functionality of the Node API. ### useTreeState input | 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 | ### useTreeState output | 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. ## <a id="node-api"></a>Node API | 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 | ## <a id="global-state-manager"></a>Global state manager 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 structure 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(...) ``` ### treeHandlers API | 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) ``` ## <a id="async-children"></a>Async children 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_ ## <a id="default-props"></a>Default properties ```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 } ``` ## <a id="road-map"></a> Road map - Coverage by tests - Inner improvements and extending functionality - Documentation improvements ## <a id="contributing"></a>Contributing Pull requests are welcome. For major changes, please open an issue first to discuss what you would like to change. ## <a id="license"></a>License [MIT](https://choosealicense.com/licenses/mit/)