Ecosyste.ms: Awesome

An open API service indexing awesome lists of open source software.

Awesome Lists | Featured Topics | Projects

https://github.com/alekseymakhankov/hyper-tree

React treeview component
https://github.com/alekseymakhankov/hyper-tree

custom-hook react react-component react-hooks reactjs tree tree-structure treeview

Last synced: 30 days ago
JSON representation

React treeview component

Awesome Lists containing this project

README

        

# 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

## 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
```

## 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 (

)
}
```

## 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 |

## 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.

## 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 |

## 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)
```

## 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_

## 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
}
```

## Road map

- Coverage by tests
- Inner improvements and extending functionality
- Documentation improvements

## Contributing

Pull requests are welcome. For major changes, please open an issue first to discuss what you would like to change.

## License

[MIT](https://choosealicense.com/licenses/mit/)