# co-tree

---

Tree ui component for react

## 特性

- 支持所有流行的浏览器，包括 IE9 及以上版本
- 用于多层次结构列表展示
- 具有展开收起选择等交互功能

### 使用场景

---

任何可以利用树形结构来展现事物层级关系的场景

## Example

http://localhost:3000/

## Usage

```bash
npm install solidoc-az-rc
or
cnpm install solidoc-az-rc
```

### Draggable Demo

`Based on the toolkit nwb`

You can based on some toolkit or framework, whatever `create-react-app` etc...

```bash
npm install nwb -g;
nwb new react-component test;
cd test;
npm install solidoc-rc-az
```

```js
import React from "react";
import { Tree } from "solidoc-az-rc";

//Some very simple styles, just embed the js code
const STYLE = `
.draggable-demo {
	width: 20%;
	margin-left: 30px;
}
.wrapper {
	background-color: #f5f7f9;
}
.node-motion {
	transition: all .3s;
	overflow-y: hidden;
}
`;

//Basic animation setting.
const motion = {
  motionName: "node-motion",
  motionAppear: false,
  onAppearStart: () => ({ height: 0 }),
  onAppearActive: node => ({ height: node.scrollHeight }),
  onLeaveStart: node => ({ height: node.offsetHeight }),
  onLeaveActive: () => ({ height: 0 })
};

//Generate Data Function
const gData = (x = 3, y = 2, z = 1, data = []) => {
  //x: 每一级下的节点总数。y: 每一级节点里有y个节点、存在子节点
  //y: 树的level层级（0代表一级）
  const loop = (_level, _prevKey, _tns) => {
    const preKey = _prevKey || "0";
    const tns = _tns || data;

    const children = [];
    for (let i = 0; i < x; i++) {
      const key = `${preKey}-${i}`;
      tns.push({ title: `${key}`, key: `${key}-key` });
      if (i < y) {
        children.push(key);
      }
    }
    if (_level < 0) {
      return tns;
    }
    const __level = _level - 1;
    children.forEach((key, index) => {
      tns[index].children = [];
      return loop(__level, key, tns[index].children);
    });
    return null;
  };
  loop(z);
  return data;
};

//By the way, we can also just simply hard code data like the following way in the component
//const gData = [
//	{ title: '0-0', key: '0-0' },
//	{ title: '0-1', key: '0-1' },
//	{ title: '0-2', key: '0-2', children: [{ title: '0-2-0', key: 	'0-2-0' }] },
//];

const DragTree = () => {
  const [_gData, setGData] = React.useState(gData);
  const [autoExpandParent, setAutoExpandParent] = React.useState(true);
  const [expandedKeys, setExpandedKeys] = React.useState([
    "0-0-key",
    "0-0-0-key",
    "0-0-0-0-key"
  ]);

  const onDragEnter = ({ expandedKeys }) => {
    setExpandedKeys(expandedKeys);
  };

  const onDrop = info => {
    const dropKey = info.node.props.eventKey;
    const dragKey = info.dragNode.props.eventKey;
    const dropPos = info.node.props.pos.split("-");

    const dropPosition =
      info.dropPosition - Number(dropPos[dropPos.length - 1]);

    //Traverse the whole tree node structure
    const loop = (data, key, callback) => {
      data.forEach((item, index, arr) => {
        if (item.key === key) {
          callback(item, index, arr);
          return;
        }
        if (item.children) {
          loop(item.children, key, callback);
        }
      });
    };

    //获取原始节点数据结构
    const data = [..._gData];
    //被拖拽的节点对象
    let dragObj;
    loop(data, dragKey, (item, index, arr) => {
      arr.splice(index, 1);
      dragObj = item;
    });

    if (!info.dropToGap) {
      // Drop on the content 释放在其他节点上
      loop(data, dropKey, item => {
        item.children = item.children || [];
        // where to insert 示例添加到尾部，可以是随意位置
        item.children.push(dragObj);
      });
    } else if (
      (info.node.props.children || []).length > 0 && // Has children
      info.node.props.expanded && // Is expanded
      dropPosition === 1 // On the bottom gap
    ) {
      loop(data, dropKey, item => {
        item.children = item.children || [];
        // where to insert 示例添加到尾部，可以是随意位置
        item.children.unshift(dragObj);
      });
    } else {
      // Drop on the gap 释放在节点间的gap上
      let ar;
      let i;
      loop(data, dropKey, (_item, index, arr) => {
        ar = arr;
        i = index;
      });
      if (dropPosition === -1) {
        ar.splice(i, 0, dragObj);
      } else {
        ar.splice(i + 1, 0, dragObj);
      }
    }
    setGData(data);
  };

  const onExpand = expandedKeys => {
    setExpandedKeys(expandedKeys);
    setAutoExpandParent(false);
  };

  return (
    <div className="draggable-demo">
      <style dangerouslySetInnerHTML={{ __html: STYLE }} />
      <h2>Draggable</h2>
      <p>drag a node into another node</p>
      <div className="wrapper">
        <Tree
          expandedKeys={expandedKeys}
          onExpand={onExpand}
          autoExpandParent={autoExpandParent}
          draggable
          onDragEnter={onDragEnter}
          onDrop={onDrop}
          treeData={_gData}
          motion={motion}
        />
      </div>
    </div>
  );
};

export default DragTree;
```

### 效果图

<img src="/Users/levi/Library/Application Support/typora-user-images/image-20200316122231392.png" alt="image-20200316122231392" style="zoom:50%;" />

### Draggable Demo 使用的 api 解析

- expandedKeys: 用于展开指定的树节点，决定树形结构数据最初呈现(展开/收起)的样子，也可以作为其他函数的参数，用来设定节点最初的展开状态。
- onExpand: 将 expandedKeys 作为参数的函数，来决定哪些节点最初呈现展开状态，并且用于控制节点的开合状态。
- autoExpandParent：是否自动展开父节点。配合前两个 api 一起使用，来决定 ui 呈现时，哪些父节点自动展开。
- draggable：用于决定组件的节点是否能被拖拽。
- onDragEnter：当被拖拽的节点进入另外的父节点时，该节点自动展开。
- onDrop: 处理被拖拽节点释放位置的具体情况(放置于`gap`之间节点变换位置，放置于其他节点上，变成该节点的子节点等）。
- treeData：决定组件的数据结构，具体收到怎样的数据，最终节点便会有怎样的 ui 呈现。
- motion：处理运用于组件的简单动画设置。

#### 组件其他部分常用 api 解析

- disabled: 禁用某节点，主要用于禁止节点的拖拽、选中等功能，并不影响父节点的展开/收起以及图标的更换。
- icon：用于自定义图标，可直接用在数据中，在 json 数据中添加 icon 的选项信息，也可以设置更换图标的函数，配合 switcherIcon 一起使用。
- multiple：通过点击可以选中多个节点（配合 selectable 一起使用）。
- selectable：是否可以选中节点，默认值为 true。
- title：用于设置节点标题信息的 API，可直接放置于 json 信息中，也可具体设置。
- showIcon: 是否显示节点图标，默认值为 true，我们也可以将其手动设置成 false；
- switcherIcon: 切换 switcher 图标，可以直接使用第三方图标库进行切换。

## API

### Tree Props

| 参数                | 描述                                                                             | 类型                                                                                 | 默认值    |
| ------------------- | -------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------ | --------- |
| autoExpandParent    | 是否自动展开父节点                                                               | bool                                                                                 | false     |
| className           | 根 dom 节点的附加 css 类                                                         | string                                                                               | ‘’        |
| defaultExpandedKey  | 默认展开指定的树节点                                                             | string[]                                                                             | []        |
| defaultExpandAll    | 默认展开所有树节点                                                               | bool                                                                                 | false     |
| defaultExpandParent | 默认展开父节点                                                                   | bool                                                                                 | true      |
| defaultSelectedKeys | 默认选中的树节点                                                                 | string[]                                                                             | []        |
| disabled            | 将树禁用                                                                         | bool                                                                                 | false     |
| draggable           | 设置节点可拖拽(IE > 8 & Safari > 5.1)                                            | bool                                                                                 | false     |
| expandedKeys        | (受控)展开指定的树节点                                                           | string[]                                                                             | -         |
| filterTreeNode      | 按需筛选树节点(高亮)， 返回 true                                                 | function (node)                                                                      | -         |
| icon                | 自定义图标。可接受组件， props 为当前节点 props                                  | element/Function(props)                                                              | -         |
| loadedKeys          | (受控)已加载的节点，需要配合`loadData`使用                                       | string[]                                                                             | -         |
| loadData            | 异步加载数据，返回一个 promise                                                   | function(node)                                                                       | -         |
| multiple            | 支持点选多个节点（节点本身）                                                     | bool                                                                                 | false     |
| prefixCls           | 类名前缀                                                                         | string                                                                               | 'co-tree' |
| selectable          | 是否可选中                                                                       | bool                                                                                 | true      |
| selectedKeys        | （受控）设置选中的树节点(若设置了 defaultSelectedKeys 便不会生效)                | string[]                                                                             | []        |
| showIcon            | 是否显示图标                                                                     | bool                                                                                 | true      |
| showLine            | 是否显示连接线                                                                   | bool                                                                                 | false     |
| treeData            | treeNodes 数据，如果设置则不需要手动构造 TreeNode 节点（key 在整个树范围内唯一） | array<{key, title,children,[disabled,selectable]}>                                   | -         |
| onExpand            | 展开/收起节点时触发                                                              | function(expandedKeys, {expanded: bool, node, nativeEvent})                          | -         |
| onDragEnd           | dragEnd 触发时调用                                                               | function({event, node})                                                              | -         |
| onDragEnter         | dragEnter 触发时调用                                                             | function({event, node, expandedKeys})                                                | -         |
| onDragLeave         | dragLeave 触发时调用                                                             | function({event, node})                                                              | -         |
| onDragStart         | dragStart 触发时调用                                                             | function({event, node})                                                              | -         |
| onDrop              | drop 触发时调用                                                                  | function({event, node, dragNode, dragNodesKeys})                                     | -         |
| onLoad              | 节点加载完毕时触发， 如果设置了`loadedKeys`, 便必需设置`onLoad`来避免无限循环    | function(loadedKeys, {event, node})                                                  | -         |
| onMouseEnter        | 当光标进入树节点时响应                                                           | function({event, node})                                                              | -         |
| onMouseLeave        | 当光标离开树节点时响应                                                           | function({event, node})                                                              | -         |
| onRightClick        | 响应右键点击显示自定义的内容菜单                                                 | function({event, node})                                                              | -         |
| onSelect            | 点击树节点时触发                                                                 | function(selectedKeys, e: {selected: bool, selectedNodes, node, event, nativeEvent}) | -         |
| switcherIcon        | 切换具体图标                                                                     | ReactNode / (props: TreeNodeAttribute) => ReactNode                                  | -         |

### TreeNode Props

> note: 如果你有很多个树节点，若有超过一千个节点，
>
> 建议将父节点设置成默认`收起`，这样整体树结构展示便会非常有效迅速，
>
> 因为藏起来的子节点是不会插入 dom 中的

| 属性         | 描述                                                                                                                                  | 类型                                                | 默认值              |
| ------------ | ------------------------------------------------------------------------------------------------------------------------------------- | --------------------------------------------------- | ------------------- |
| className    | 树节点的附加类                                                                                                                        | string                                              | ''                  |
| style        | 为树节点设置样式                                                                                                                      | object                                              | -                   |
| disabled     | 禁止响应                                                                                                                              | bool                                                | false               |
| title        | 标题                                                                                                                                  | string/element/((data:DataNode) => React.ReactNode) | '---'               |
| key          | 被树的 (default)ExpandedKeys / (default)CheckedKeys / (default)SelectedKeys 属性所用。注意：整个树范围内的所有节点的 key 值不能重复！ | string                                              | treeNode's position |
| isLeaf       | 设置为叶子节点                                                                                                                        | bool                                                | false               |
| icon         | 自定义图标。可接收组件，props 为当前节点 props                                                                                        | element/Function(props)                             | -                   |
| switcherIcon | 切换具体的图标                                                                                                                        | ReactNode /(props: TreeNodeAttribute) => ReactNode  | -                   |

### Development

---

```bash
npm install
npm start
```

### License

---

co-tree is released under the MIT license
