commit d53c451d4d77c6fc5c3547875c8f61a3cacc2c5b
Author: ahua52 <1468492018@qq.com>
Date: Fri Dec 30 15:43:08 2016 +0800
bee-tree
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..25052a1
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,38 @@
+# Logs
+logs
+*.log
+npm-debug.log*
+
+# Runtime data
+pids
+*.pid
+*.seed
+
+# Directory for instrumented libs generated by jscoverage/JSCover
+lib-cov
+
+# Coverage directory used by tools like istanbul
+coverage
+
+# nyc test coverage
+.nyc_output
+
+# Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files)
+.grunt
+
+# node-waf configuration
+.lock-wscript
+
+
+# Dependency directories
+node_modules
+jspm_packages
+
+# Optional npm cache directory
+.npm
+
+# Optional REPL history
+.node_repl_history
+
+dist
+coverage
diff --git a/.npmignore b/.npmignore
new file mode 100644
index 0000000..b6fadc1
--- /dev/null
+++ b/.npmignore
@@ -0,0 +1,28 @@
+bower_components/
+*.cfg
+node_modules/
+nohup.out
+*.iml
+.idea/
+.ipr
+.iws
+*~
+~*
+*.diff
+*.log
+*.patch
+*.bak
+.DS_Store
+Thumbs.db
+.project
+.*proj
+.svn/
+*.swp
+out/
+.build
+node_modules
+_site
+sea-modules
+spm_modules
+.cache
+coverage
diff --git a/.travis.yml b/.travis.yml
new file mode 100644
index 0000000..e5a6d34
--- /dev/null
+++ b/.travis.yml
@@ -0,0 +1,22 @@
+sudo: false
+
+language: node_js
+
+node_js:
+ - "6"
+
+service_name: travis-ci
+repo_token: add
+
+env:
+ global:
+ - NODE_ENV=travisci
+ - NPM_CONFIG_PROGRESS="false"
+
+before_install:
+ - npm install -g bee-tools
+
+script: npm test
+
+after_script:
+ - npm run coveralls
diff --git a/HISTORY.md b/HISTORY.md
new file mode 100644
index 0000000..e69de29
diff --git a/README.md b/README.md
new file mode 100644
index 0000000..5829a6d
--- /dev/null
+++ b/README.md
@@ -0,0 +1,43 @@
+# bee-tree
+
+[![npm version](https://img.shields.io/npm/v/bee-tree.svg)](https://www.npmjs.com/package/bee-tree)
+[![Build Status](https://img.shields.io/travis/tinper-bee/bee-tree/master.svg)](https://travis-ci.org/tinper-bee/bee-tree)
+[![Coverage Status](https://coveralls.io/repos/github/tinper-bee/bee-tree/badge.svg?branch=master)](https://coveralls.io/github/tinper-bee/bee-tree?branch=master)
+[![devDependency Status](https://img.shields.io/david/dev/tinper-bee/bee-tree.svg)](https://david-dm.org/tinper-bee/bee-tree#info=devDependencies)
+[![NPM downloads](http://img.shields.io/npm/dm/bee-tree.svg?style=flat)](https://npmjs.org/package/bee-tree)
+[![Average time to resolve an issue](http://isitmaintained.com/badge/resolution/tinper-bee/bee-tree.svg)](http://isitmaintained.com/project/tinper-bee/bee-tree "Average time to resolve an issue")
+[![Percentage of issues still open](http://isitmaintained.com/badge/open/tinper-bee/bee-tree.svg)](http://isitmaintained.com/project/tinper-bee/bee-tree "Percentage of issues still open")
+
+## Browser Support
+
+|![IE](https://raw.github.com/alrra/browser-logos/master/internet-explorer/internet-explorer_48x48.png) | ![Chrome](https://raw.github.com/alrra/browser-logos/master/chrome/chrome_48x48.png) | ![Firefox](https://raw.github.com/alrra/browser-logos/master/firefox/firefox_48x48.png) | ![Opera](https://raw.github.com/alrra/browser-logos/master/opera/opera_48x48.png) | ![Safari](https://raw.github.com/alrra/browser-logos/master/safari/safari_48x48.png)|
+| --- | --- | --- | --- | --- |
+| IE 9+ ✔ | Chrome 31.0+ ✔ | Firefox 31.0+ ✔ | Opera 30.0+ ✔ | Safari 7.0+ ✔ |
+
+
+react bee-tree component for tinper-bee
+
+some description...
+
+## 使用方法
+
+```js
+
+```
+
+
+
+## API
+
+|参数|说明|类型|默认值|
+|:--|:---:|:--:|---:|
+
+#### 开发调试
+
+```sh
+$ npm install -g bee-tools
+$ git clone https://github.com/tinper-bee/bee-tree
+$ cd bee-tree
+$ npm install
+$ npm run dev
+```
diff --git a/README_EN.md b/README_EN.md
new file mode 100644
index 0000000..08ea79e
--- /dev/null
+++ b/README_EN.md
@@ -0,0 +1,43 @@
+# bee-dropdown
+
+[![npm version](https://img.shields.io/npm/v/bee-dropdown.svg)](https://www.npmjs.com/package/bee-dropdown)
+[![Build Status](https://img.shields.io/travis/tinper-bee/bee-dropdown/master.svg)](https://travis-ci.org/tinper-bee/bee-dropdown)
+[![Coverage Status](https://coveralls.io/repos/github/tinper-bee/bee-dropdown/badge.svg?branch=master)](https://coveralls.io/github/tinper-bee/bee-dropdown?branch=master)
+[![devDependency Status](https://img.shields.io/david/dev/tinper-bee/bee-dropdown.svg)](https://david-dm.org/tinper-bee/bee-dropdown#info=devDependencies)
+[![NPM downloads](http://img.shields.io/npm/dm/<%= packageName%>.svg?style=flat)](https://npmjs.org/package/<%= packageName%>)
+[![Average time to resolve an issue](http://isitmaintained.com/badge/resolution/tinper-bee/<%= packageName%>.svg)](http://isitmaintained.com/project/tinper-bee/<%= packageName%> "Average time to resolve an issue")
+[![Percentage of issues still open](http://isitmaintained.com/badge/open/tinper-bee/<%= packageName%>.svg)](http://isitmaintained.com/project/tinper-bee/<%= packageName%> "Percentage of issues still open")
+
+## Browser Support
+
+|![IE](https://raw.github.com/alrra/browser-logos/master/internet-explorer/internet-explorer_48x48.png) | ![Chrome](https://raw.github.com/alrra/browser-logos/master/chrome/chrome_48x48.png) | ![Firefox](https://raw.github.com/alrra/browser-logos/master/firefox/firefox_48x48.png) | ![Opera](https://raw.github.com/alrra/browser-logos/master/opera/opera_48x48.png) | ![Safari](https://raw.github.com/alrra/browser-logos/master/safari/safari_48x48.png)|
+| --- | --- | --- | --- | --- |
+| IE 9+ ✔ | Chrome 31.0+ ✔ | Firefox 31.0+ ✔ | Opera 30.0+ ✔ | Safari 7.0+ ✔ |
+
+
+react bee-dropdown component for tinper-bee
+
+
+## Usage
+
+```js
+
+
+```
+
+
+
+## API
+
+|参数|说明|类型|默认值|
+|:--|:---:|:--:|---:|
+
+#### develop
+
+```sh
+$ npm install -g bee-tools
+$ git clone https://github.com/tinper-bee/bee-dropdown
+$ cd bee-dropdown
+$ npm install
+$ npm run dev
+```
diff --git a/demo/TreeDemo.js b/demo/TreeDemo.js
new file mode 100644
index 0000000..3d65aef
--- /dev/null
+++ b/demo/TreeDemo.js
@@ -0,0 +1,5 @@
+import Tree from '../src/index';
+import React, { Component } from 'react';
+import ReactDOM from 'react-dom';
+class Demo extends Component {render(){return( )}}
+export default Demo;
\ No newline at end of file
diff --git a/demo/TreeDemo.scss b/demo/TreeDemo.scss
new file mode 100644
index 0000000..86e7870
--- /dev/null
+++ b/demo/TreeDemo.scss
@@ -0,0 +1,11 @@
+@import "../node_modules/tinper-bee-core/scss/index.scss";
+@import "../src/Tree.scss";
+@import "../node_modules/bee-panel/src/Panel.scss";
+@import "../node_modules/bee-layout/src/Layout.scss";
+@import "../node_modules/bee-button/src/Button.scss";
+@import "../node_modules/bee-transition/src/Transition.scss";
+@import "../node_modules/bee-form-control/src/FormControl.scss";
+.ant-tree-searchable-filter {
+ color: #f50;
+ transition: all .3s ease;
+}
\ No newline at end of file
diff --git a/demo/atom-one-dark.css b/demo/atom-one-dark.css
new file mode 100644
index 0000000..1616aaf
--- /dev/null
+++ b/demo/atom-one-dark.css
@@ -0,0 +1,96 @@
+/*
+
+Atom One Dark by Daniel Gamage
+Original One Dark Syntax theme from https://github.com/atom/one-dark-syntax
+
+base: #282c34
+mono-1: #abb2bf
+mono-2: #818896
+mono-3: #5c6370
+hue-1: #56b6c2
+hue-2: #61aeee
+hue-3: #c678dd
+hue-4: #98c379
+hue-5: #e06c75
+hue-5-2: #be5046
+hue-6: #d19a66
+hue-6-2: #e6c07b
+
+*/
+
+.hljs {
+ display: block;
+ overflow-x: auto;
+ padding: 0.5em;
+ color: #abb2bf;
+ background: #282c34;
+}
+
+.hljs-comment,
+.hljs-quote {
+ color: #5c6370;
+ font-style: italic;
+}
+
+.hljs-doctag,
+.hljs-keyword,
+.hljs-formula {
+ color: #c678dd;
+}
+
+.hljs-section,
+.hljs-name,
+.hljs-selector-tag,
+.hljs-deletion,
+.hljs-subst {
+ color: #e06c75;
+}
+
+.hljs-literal {
+ color: #56b6c2;
+}
+
+.hljs-string,
+.hljs-regexp,
+.hljs-addition,
+.hljs-attribute,
+.hljs-meta-string {
+ color: #98c379;
+}
+
+.hljs-built_in,
+.hljs-class .hljs-title {
+ color: #e6c07b;
+}
+
+.hljs-attr,
+.hljs-variable,
+.hljs-template-variable,
+.hljs-type,
+.hljs-selector-class,
+.hljs-selector-attr,
+.hljs-selector-pseudo,
+.hljs-number {
+ color: #d19a66;
+}
+
+.hljs-symbol,
+.hljs-bullet,
+.hljs-link,
+.hljs-meta,
+.hljs-selector-id,
+.hljs-title {
+ color: #61aeee;
+}
+
+.hljs-emphasis {
+ font-style: italic;
+}
+
+.hljs-strong {
+ font-weight: bold;
+}
+
+.hljs-link {
+ text-decoration: underline;
+}
diff --git a/demo/demolist/Demo1.js b/demo/demolist/Demo1.js
new file mode 100644
index 0000000..7eb44b9
--- /dev/null
+++ b/demo/demolist/Demo1.js
@@ -0,0 +1,48 @@
+/**
+*
+* @title Tree基本使用事例
+* @description 事例涵盖 checkbox如何选择,disable状态和部分选择状态。
+*
+*/
+const defaultProps = {
+ keys: ['0-0-0', '0-0-1']
+}
+class Demo1 extends Component {
+ constructor(props) {
+ super(props);
+ const keys = this.props.keys;
+ this.state = {
+ defaultExpandedKeys: keys,
+ defaultSelectedKeys: keys,
+ defaultCheckedKeys: keys,
+ };
+ }
+ onSelect(info) {
+ console.log('selected', info);
+ }
+ onCheck(info) {
+ console.log('onCheck', info);
+ }
+ render() {
+ return (
+
+
+
+
+
+
+
+ sss} key="0-0-1-0" />
+
+
+
+ );
+ }
+}
+
+Demo1.defaultProps = defaultProps;
\ No newline at end of file
diff --git a/demo/demolist/Demo2.js b/demo/demolist/Demo2.js
new file mode 100644
index 0000000..77b8f3d
--- /dev/null
+++ b/demo/demolist/Demo2.js
@@ -0,0 +1,91 @@
+/**
+*
+* @title Tree数据可控事例
+* @description
+*
+*/
+/*
+const x = 3;
+const y = 2;
+const z = 1;
+const gData = [];
+
+const generateData = (_level, _preKey, _tns) => {
+ const preKey = _preKey || '0';
+ const tns = _tns || gData;
+
+ const children = [];
+ for (let i = 0; i < x; i++) {
+ const key = `${preKey}-${i}`;
+ tns.push({ title: 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 generateData(level, key, tns[index].children);
+ });
+};
+generateData(z);
+*/
+class Demo2 extends Component{
+ constructor(props) {
+ super(props);
+ this.state = {
+ expandedKeys: ['0-0-0', '0-0-1'],
+ autoExpandParent: true,
+ checkedKeys: ['0-0-0'],
+ selectedKeys: [],
+ };
+ this.onExpand = this.onExpand.bind(this);
+ this.onCheck = this.onCheck.bind(this);
+ this.onSelect = this.onSelect.bind(this);
+ }
+ onExpand(expandedKeys) {
+ console.log('onExpand', arguments);
+ // if not set autoExpandParent to false, if children expanded, parent can not collapse.
+ // or, you can remove all expanded children keys.
+ this.setState({
+ expandedKeys,
+ autoExpandParent: false,
+ });
+ }
+ onCheck(checkedKeys) {
+ this.setState({
+ checkedKeys,
+ selectedKeys: ['0-3', '0-4'],
+ });
+ }
+ onSelect(selectedKeys, info) {
+ console.log('onSelect', info);
+ this.setState({ selectedKeys });
+ }
+ render() {
+ const loop = data => data.map((item) => {
+ if (item.children) {
+ return (
+
+ {loop(item.children)}
+
+ );
+ }
+ return ;
+ });
+ return (
+
+ {loop(gData)}
+
+ );
+ }
+};
\ No newline at end of file
diff --git a/demo/demolist/Demo3.js b/demo/demolist/Demo3.js
new file mode 100644
index 0000000..cb5d6bf
--- /dev/null
+++ b/demo/demolist/Demo3.js
@@ -0,0 +1,83 @@
+/**
+*
+* @title Tree 拖拽使用事例
+* @description 拖动结点插入到另一个结点后面或者其他的父节点里面。
+*
+*/
+
+class Demo3 extends Component{
+ constructor(props) {
+ super(props);
+ this.state = {
+ gData,
+ expandedKeys: ['0-0', '0-0-0', '0-0-0-0'],
+ };
+ this.onDragEnter = this.onDragEnter.bind(this);
+ this.onDrop = this.onDrop.bind(this);
+ }
+ onDragEnter(info) {
+ console.log(info);
+ // expandedKeys 需要受控时设置
+ // this.setState({
+ // expandedKeys: info.expandedKeys,
+ // });
+ }
+ onDrop(info) {
+ console.log(info);
+ const dropKey = info.node.props.eventKey;
+ const dragKey = info.dragNode.props.eventKey;
+ // const dragNodesKeys = info.dragNodesKeys;
+ const loop = (data, key, callback) => {
+ data.forEach((item, index, arr) => {
+ if (item.key === key) {
+ return callback(item, index, arr);
+ }
+ if (item.children) {
+ return loop(item.children, key, callback);
+ }
+ });
+ };
+ const data = [...this.state.gData];
+ let dragObj;
+ loop(data, dragKey, (item, index, arr) => {
+ arr.splice(index, 1);
+ dragObj = item;
+ });
+ if (info.dropToGap) {
+ let ar;
+ let i;
+ loop(data, dropKey, (item, index, arr) => {
+ ar = arr;
+ i = index;
+ });
+ ar.splice(i, 0, dragObj);
+ } else {
+ loop(data, dropKey, (item) => {
+ item.children = item.children || [];
+ // where to insert 示例添加到尾部,可以是随意位置
+ item.children.push(dragObj);
+ });
+ }
+ this.setState({
+ gData: data,
+ });
+ }
+ render() {
+ const loop = data => data.map((item) => {
+ if (item.children && item.children.length) {
+ return {loop(item.children)};
+ }
+ return ;
+ });
+ return (
+
+ {loop(this.state.gData)}
+
+ );
+ }
+};
\ No newline at end of file
diff --git a/demo/demolist/Demo4.js b/demo/demolist/Demo4.js
new file mode 100644
index 0000000..7511e5d
--- /dev/null
+++ b/demo/demolist/Demo4.js
@@ -0,0 +1,110 @@
+/**
+*
+* @title Tree可搜索事例
+* @description
+*
+*/
+const dataList = [];
+const generateList = (data) => {
+ for (let i = 0; i < data.length; i++) {
+ const node = data[i];
+ const key = node.key;
+ dataList.push({ key, title: key });
+ if (node.children) {
+ generateList(node.children, node.key);
+ }
+ }
+};
+generateList(gData);
+
+const getParentKey = (key, tree) => {
+ let parentKey;
+ for (let i = 0; i < tree.length; i++) {
+ const node = tree[i];
+ if (node.children) {
+ if (node.children.some(item => item.key === key)) {
+ parentKey = node.key;
+ } else if (getParentKey(key, node.children)) {
+ parentKey = getParentKey(key, node.children);
+ }
+ }
+ }
+ return parentKey;
+};
+
+
+class Demo4 extends Component {
+ constructor(props) {
+ super(props);
+ this.state = {
+ expandedKeys: [],
+ searchValue: '',
+ autoExpandParent: true,
+ }
+ }
+ onExpand = (expandedKeys) => {
+ this.setState({
+ expandedKeys,
+ autoExpandParent: false,
+ });
+ }
+ onChange = (e) => {
+ const value = e.target.value;
+ const expandedKeys = [];
+ dataList.forEach((item) => {
+ if (item.key.indexOf(value) > -1) {
+ expandedKeys.push(getParentKey(item.key, gData));
+ }
+ });
+ const uniqueExpandedKeys = [];
+ expandedKeys.forEach((item) => {
+ if (item && uniqueExpandedKeys.indexOf(item) === -1) {
+ uniqueExpandedKeys.push(item);
+ }
+ });
+ this.setState({
+ expandedKeys: uniqueExpandedKeys,
+ searchValue: value,
+ autoExpandParent: true,
+ });
+ }
+ render() {
+ const { searchValue, expandedKeys, autoExpandParent } = this.state;
+ const loop = data => data.map((item) => {
+ const index = item.key.search(searchValue);
+ const beforeStr = item.key.substr(0, index);
+ const afterStr = item.key.substr(index + searchValue.length);
+ const title = index > -1 ? (
+
+ {beforeStr}
+ {searchValue}
+ {afterStr}
+
+ ) : {item.key};
+ if (item.children) {
+ return (
+
+ {loop(item.children)}
+
+ );
+ }
+ return ;
+ });
+ return (
+
+
+
+ {loop(gData)}
+
+
+ );
+ }
+}
\ No newline at end of file
diff --git a/demo/demolist/Demo5.js b/demo/demolist/Demo5.js
new file mode 100644
index 0000000..99c5d61
--- /dev/null
+++ b/demo/demolist/Demo5.js
@@ -0,0 +1,98 @@
+/**
+*
+* @title Tree异步数据加载
+* @description 当点击展开,异步获取子节点数据
+*
+*/
+function generateTreeNodes(treeNode) {
+ const arr = [];
+ const key = treeNode.props.eventKey;
+ for (let i = 0; i < 3; i++) {
+ arr.push({ name: `leaf ${key}-${i}`, key: `${key}-${i}` });
+ }
+ return arr;
+}
+
+function setLeaf(treeData, curKey, level) {
+ const loopLeaf = (data, lev) => {
+ const l = lev - 1;
+ data.forEach((item) => {
+ if ((item.key.length > curKey.length) ? item.key.indexOf(curKey) !== 0 :
+ curKey.indexOf(item.key) !== 0) {
+ return;
+ }
+ if (item.children) {
+ loopLeaf(item.children, l);
+ } else if (l < 1) {
+ item.isLeaf = true;
+ }
+ });
+ };
+ loopLeaf(treeData, level + 1);
+}
+
+function getNewTreeData(treeData, curKey, child, level) {
+ const loop = (data) => {
+ if (level < 1 || curKey.length - 3 > level * 2) return;
+ data.forEach((item) => {
+ if (curKey.indexOf(item.key) === 0) {
+ if (item.children) {
+ loop(item.children);
+ } else {
+ item.children = child;
+ }
+ }
+ });
+ };
+ loop(treeData);
+ setLeaf(treeData, curKey, level);
+}
+
+class Demo5 extends Component{
+ constructor(props) {
+ super(props);
+ this.state = {
+ treeData: [],
+ };
+ this.onSelect = this.onSelect.bind(this);
+ this.onLoadData = this.onLoadData.bind(this);
+ }
+ componentDidMount() {
+ setTimeout(() => {
+ this.setState({
+ treeData: [
+ { name: 'pNode 01', key: '0-0' },
+ { name: 'pNode 02', key: '0-1' },
+ { name: 'pNode 03', key: '0-2', isLeaf: true },
+ ],
+ });
+ }, 100);
+ }
+ onSelect(info) {
+ console.log('selected', info);
+ }
+ onLoadData(treeNode) {
+ return new Promise((resolve) => {
+ setTimeout(() => {
+ const treeData = [...this.state.treeData];
+ getNewTreeData(treeData, treeNode.props.eventKey, generateTreeNodes(treeNode), 2);
+ this.setState({ treeData });
+ resolve();
+ }, 1000);
+ });
+ }
+ render() {
+ const loop = data => data.map((item) => {
+ if (item.children) {
+ return {loop(item.children)};
+ }
+ return ;
+ });
+ const treeNodes = loop(this.state.treeData);
+ return (
+
+ {treeNodes}
+
+ );
+ }
+};
\ No newline at end of file
diff --git a/demo/index-demo-base.js b/demo/index-demo-base.js
new file mode 100644
index 0000000..03b9817
--- /dev/null
+++ b/demo/index-demo-base.js
@@ -0,0 +1,113 @@
+
+import { Con, Row, Col } from 'bee-layout';
+import { Panel } from 'bee-panel';
+import Button from 'bee-button';
+import React, { Component } from 'react';
+import ReactDOM from 'react-dom';
+import FormControl from 'bee-form-control';
+import Tree from '../src';
+
+const x = 3;
+const y = 2;
+const z = 1;
+const gData = [];
+
+const generateData = (_level, _preKey, _tns) => {
+ const preKey = _preKey || '0';
+ const tns = _tns || gData;
+
+ const children = [];
+ for (let i = 0; i < x; i++) {
+ const key = `${preKey}-${i}`;
+ tns.push({ title: 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 generateData(level, key, tns[index].children);
+ });
+};
+generateData(z);
+
+const TreeNode = Tree.TreeNode;
+
+const CARET = ;
+
+const CARETUP = ;
+
+
+{demolist}
+
+class Demo extends Component {
+ constructor(props){
+ super(props);
+ this.state = {
+ open: false
+ }
+ this.handleClick = this.handleClick.bind(this);
+ }
+ handleClick() {
+ this.setState({ open: !this.state.open })
+ }
+
+ render () {
+ const { title, example, code, desc } = this.props;
+ let caret = this.state.open ? CARETUP : CARET;
+ let text = this.state.open ? "隐藏代码" : "查看代码";
+
+ const footer = (
+
+ );
+ const header = (
+
+
+ { example }
+
+
+
+
+
+ );
+ return (
+
+ { title }
+ { desc }
+
+ { code }
+
+
+ )
+ }
+}
+
+class DemoGroup extends Component {
+ constructor(props){
+ super(props)
+ }
+ render () {
+ return (
+
+ {DemoArray.map((child,index) => {
+
+ return (
+
+ )
+
+ })}
+
+ )
+ }
+}
+
+ReactDOM.render(, document.getElementById('tinperBeeDemo'));
diff --git a/demo/index.js b/demo/index.js
new file mode 100644
index 0000000..939d688
--- /dev/null
+++ b/demo/index.js
@@ -0,0 +1,539 @@
+
+import { Con, Row, Col } from 'bee-layout';
+import { Panel } from 'bee-panel';
+import Button from 'bee-button';
+import React, { Component } from 'react';
+import ReactDOM from 'react-dom';
+import FormControl from 'bee-form-control';
+import Tree from '../src';
+
+const x = 3;
+const y = 2;
+const z = 1;
+const gData = [];
+
+const generateData = (_level, _preKey, _tns) => {
+ const preKey = _preKey || '0';
+ const tns = _tns || gData;
+
+ const children = [];
+ for (let i = 0; i < x; i++) {
+ const key = `${preKey}-${i}`;
+ tns.push({ title: 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 generateData(level, key, tns[index].children);
+ });
+};
+generateData(z);
+
+const TreeNode = Tree.TreeNode;
+
+const CARET = ;
+
+const CARETUP = ;
+
+
+/**
+*
+* @title Tree基本使用事例
+* @description 事例涵盖 checkbox如何选择,disable状态和部分选择状态。
+*
+*/
+const defaultProps = {
+ keys: ['0-0-0', '0-0-1']
+}
+class Demo1 extends Component {
+ constructor(props) {
+ super(props);
+ const keys = this.props.keys;
+ this.state = {
+ defaultExpandedKeys: keys,
+ defaultSelectedKeys: keys,
+ defaultCheckedKeys: keys,
+ };
+ }
+ onSelect(info) {
+ console.log('selected', info);
+ }
+ onCheck(info) {
+ console.log('onCheck', info);
+ }
+ render() {
+ return (
+
+
+
+
+
+
+
+ sss} key="0-0-1-0" />
+
+
+
+ );
+ }
+}
+
+Demo1.defaultProps = defaultProps;/**
+*
+* @title Tree数据可控事例
+* @description
+*
+*/
+/*
+const x = 3;
+const y = 2;
+const z = 1;
+const gData = [];
+
+const generateData = (_level, _preKey, _tns) => {
+ const preKey = _preKey || '0';
+ const tns = _tns || gData;
+
+ const children = [];
+ for (let i = 0; i < x; i++) {
+ const key = `${preKey}-${i}`;
+ tns.push({ title: 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 generateData(level, key, tns[index].children);
+ });
+};
+generateData(z);
+*/
+class Demo2 extends Component{
+ constructor(props) {
+ super(props);
+ this.state = {
+ expandedKeys: ['0-0-0', '0-0-1'],
+ autoExpandParent: true,
+ checkedKeys: ['0-0-0'],
+ selectedKeys: [],
+ };
+ this.onExpand = this.onExpand.bind(this);
+ this.onCheck = this.onCheck.bind(this);
+ this.onSelect = this.onSelect.bind(this);
+ }
+ onExpand(expandedKeys) {
+ console.log('onExpand', arguments);
+ // if not set autoExpandParent to false, if children expanded, parent can not collapse.
+ // or, you can remove all expanded children keys.
+ this.setState({
+ expandedKeys,
+ autoExpandParent: false,
+ });
+ }
+ onCheck(checkedKeys) {
+ this.setState({
+ checkedKeys,
+ selectedKeys: ['0-3', '0-4'],
+ });
+ }
+ onSelect(selectedKeys, info) {
+ console.log('onSelect', info);
+ this.setState({ selectedKeys });
+ }
+ render() {
+ const loop = data => data.map((item) => {
+ if (item.children) {
+ return (
+
+ {loop(item.children)}
+
+ );
+ }
+ return ;
+ });
+ return (
+
+ {loop(gData)}
+
+ );
+ }
+};/**
+*
+* @title Tree 拖拽使用事例
+* @description 拖动结点插入到另一个结点后面或者其他的父节点里面。
+*
+*/
+
+class Demo3 extends Component{
+ constructor(props) {
+ super(props);
+ this.state = {
+ gData,
+ expandedKeys: ['0-0', '0-0-0', '0-0-0-0'],
+ };
+ this.onDragEnter = this.onDragEnter.bind(this);
+ this.onDrop = this.onDrop.bind(this);
+ }
+ onDragEnter(info) {
+ console.log(info);
+ // expandedKeys 需要受控时设置
+ // this.setState({
+ // expandedKeys: info.expandedKeys,
+ // });
+ }
+ onDrop(info) {
+ console.log(info);
+ const dropKey = info.node.props.eventKey;
+ const dragKey = info.dragNode.props.eventKey;
+ // const dragNodesKeys = info.dragNodesKeys;
+ const loop = (data, key, callback) => {
+ data.forEach((item, index, arr) => {
+ if (item.key === key) {
+ return callback(item, index, arr);
+ }
+ if (item.children) {
+ return loop(item.children, key, callback);
+ }
+ });
+ };
+ const data = [...this.state.gData];
+ let dragObj;
+ loop(data, dragKey, (item, index, arr) => {
+ arr.splice(index, 1);
+ dragObj = item;
+ });
+ if (info.dropToGap) {
+ let ar;
+ let i;
+ loop(data, dropKey, (item, index, arr) => {
+ ar = arr;
+ i = index;
+ });
+ ar.splice(i, 0, dragObj);
+ } else {
+ loop(data, dropKey, (item) => {
+ item.children = item.children || [];
+ // where to insert 示例添加到尾部,可以是随意位置
+ item.children.push(dragObj);
+ });
+ }
+ this.setState({
+ gData: data,
+ });
+ }
+ render() {
+ const loop = data => data.map((item) => {
+ if (item.children && item.children.length) {
+ return {loop(item.children)};
+ }
+ return ;
+ });
+ return (
+
+ {loop(this.state.gData)}
+
+ );
+ }
+};/**
+*
+* @title Tree可搜索事例
+* @description
+*
+*/
+const dataList = [];
+const generateList = (data) => {
+ for (let i = 0; i < data.length; i++) {
+ const node = data[i];
+ const key = node.key;
+ dataList.push({ key, title: key });
+ if (node.children) {
+ generateList(node.children, node.key);
+ }
+ }
+};
+generateList(gData);
+
+const getParentKey = (key, tree) => {
+ let parentKey;
+ for (let i = 0; i < tree.length; i++) {
+ const node = tree[i];
+ if (node.children) {
+ if (node.children.some(item => item.key === key)) {
+ parentKey = node.key;
+ } else if (getParentKey(key, node.children)) {
+ parentKey = getParentKey(key, node.children);
+ }
+ }
+ }
+ return parentKey;
+};
+
+
+class Demo4 extends Component {
+ constructor(props) {
+ super(props);
+ this.state = {
+ expandedKeys: [],
+ searchValue: '',
+ autoExpandParent: true,
+ }
+ }
+ onExpand = (expandedKeys) => {
+ this.setState({
+ expandedKeys,
+ autoExpandParent: false,
+ });
+ }
+ onChange = (e) => {
+ const value = e.target.value;
+ const expandedKeys = [];
+ dataList.forEach((item) => {
+ if (item.key.indexOf(value) > -1) {
+ expandedKeys.push(getParentKey(item.key, gData));
+ }
+ });
+ const uniqueExpandedKeys = [];
+ expandedKeys.forEach((item) => {
+ if (item && uniqueExpandedKeys.indexOf(item) === -1) {
+ uniqueExpandedKeys.push(item);
+ }
+ });
+ this.setState({
+ expandedKeys: uniqueExpandedKeys,
+ searchValue: value,
+ autoExpandParent: true,
+ });
+ }
+ render() {
+ const { searchValue, expandedKeys, autoExpandParent } = this.state;
+ const loop = data => data.map((item) => {
+ const index = item.key.search(searchValue);
+ const beforeStr = item.key.substr(0, index);
+ const afterStr = item.key.substr(index + searchValue.length);
+ const title = index > -1 ? (
+
+ {beforeStr}
+ {searchValue}
+ {afterStr}
+
+ ) : {item.key};
+ if (item.children) {
+ return (
+
+ {loop(item.children)}
+
+ );
+ }
+ return ;
+ });
+ return (
+
+
+
+ {loop(gData)}
+
+
+ );
+ }
+}/**
+*
+* @title Tree异步数据加载
+* @description 当点击展开,异步获取子节点数据
+*
+*/
+function generateTreeNodes(treeNode) {
+ const arr = [];
+ const key = treeNode.props.eventKey;
+ for (let i = 0; i < 3; i++) {
+ arr.push({ name: `leaf ${key}-${i}`, key: `${key}-${i}` });
+ }
+ return arr;
+}
+
+function setLeaf(treeData, curKey, level) {
+ const loopLeaf = (data, lev) => {
+ const l = lev - 1;
+ data.forEach((item) => {
+ if ((item.key.length > curKey.length) ? item.key.indexOf(curKey) !== 0 :
+ curKey.indexOf(item.key) !== 0) {
+ return;
+ }
+ if (item.children) {
+ loopLeaf(item.children, l);
+ } else if (l < 1) {
+ item.isLeaf = true;
+ }
+ });
+ };
+ loopLeaf(treeData, level + 1);
+}
+
+function getNewTreeData(treeData, curKey, child, level) {
+ const loop = (data) => {
+ if (level < 1 || curKey.length - 3 > level * 2) return;
+ data.forEach((item) => {
+ if (curKey.indexOf(item.key) === 0) {
+ if (item.children) {
+ loop(item.children);
+ } else {
+ item.children = child;
+ }
+ }
+ });
+ };
+ loop(treeData);
+ setLeaf(treeData, curKey, level);
+}
+
+class Demo5 extends Component{
+ constructor(props) {
+ super(props);
+ this.state = {
+ treeData: [],
+ };
+ this.onSelect = this.onSelect.bind(this);
+ this.onLoadData = this.onLoadData.bind(this);
+ }
+ componentDidMount() {
+ setTimeout(() => {
+ this.setState({
+ treeData: [
+ { name: 'pNode 01', key: '0-0' },
+ { name: 'pNode 02', key: '0-1' },
+ { name: 'pNode 03', key: '0-2', isLeaf: true },
+ ],
+ });
+ }, 100);
+ }
+ onSelect(info) {
+ console.log('selected', info);
+ }
+ onLoadData(treeNode) {
+ return new Promise((resolve) => {
+ setTimeout(() => {
+ const treeData = [...this.state.treeData];
+ getNewTreeData(treeData, treeNode.props.eventKey, generateTreeNodes(treeNode), 2);
+ this.setState({ treeData });
+ resolve();
+ }, 1000);
+ });
+ }
+ render() {
+ const loop = data => data.map((item) => {
+ if (item.children) {
+ return {loop(item.children)};
+ }
+ return ;
+ });
+ const treeNodes = loop(this.state.treeData);
+ return (
+
+ {treeNodes}
+
+ );
+ }
+};var DemoArray = [{"example":,"title":" Tree基本使用事例","code":"/**\n*\n* @title Tree基本使用事例\n* @description 事例涵盖 checkbox如何选择,disable状态和部分选择状态。\n*\n*/\nconst defaultProps = {\n\tkeys: ['0-0-0', '0-0-1']\n}\nclass Demo1 extends Component {\n\tconstructor(props) {\n\t\tsuper(props);\n\t const keys = this.props.keys;\n\t this.state = {\n\t defaultExpandedKeys: keys,\n\t defaultSelectedKeys: keys,\n\t defaultCheckedKeys: keys,\n\t };\n\t}\n\tonSelect(info) {\n\t console.log('selected', info);\n\t}\n\tonCheck(info) {\n\t console.log('onCheck', info);\n\t}\n\trender() {\n\t return (\n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t sss} key=\"0-0-1-0\" />\n\t \n\t \n\t \n\t );\n\t}\n}\n\nDemo1.defaultProps = defaultProps;","desc":" 事例涵盖 checkbox如何选择,disable状态和部分选择状态。"},{"example":,"title":" Tree数据可控事例","code":"/**\n*\n* @title Tree数据可控事例\n* @description\n*\n*/\n/*\nconst x = 3;\nconst y = 2;\nconst z = 1;\nconst gData = [];\n\nconst generateData = (_level, _preKey, _tns) => {\n const preKey = _preKey || '0';\n const tns = _tns || gData;\n\n const children = [];\n for (let i = 0; i < x; i++) {\n const key = `${preKey}-${i}`;\n tns.push({ title: key, key });\n if (i < y) {\n children.push(key);\n }\n }\n if (_level < 0) {\n return tns;\n }\n const level = _level - 1;\n children.forEach((key, index) => {\n tns[index].children = [];\n return generateData(level, key, tns[index].children);\n });\n};\ngenerateData(z);\n*/\nclass Demo2 extends Component{\n constructor(props) {\n \tsuper(props);\n this.state = {\n expandedKeys: ['0-0-0', '0-0-1'],\n autoExpandParent: true,\n checkedKeys: ['0-0-0'],\n selectedKeys: [],\n };\n this.onExpand = this.onExpand.bind(this);\n this.onCheck = this.onCheck.bind(this);\n this.onSelect = this.onSelect.bind(this);\n }\n onExpand(expandedKeys) {\n console.log('onExpand', arguments);\n // if not set autoExpandParent to false, if children expanded, parent can not collapse.\n // or, you can remove all expanded children keys.\n this.setState({\n expandedKeys,\n autoExpandParent: false,\n });\n }\n onCheck(checkedKeys) {\n this.setState({\n checkedKeys,\n selectedKeys: ['0-3', '0-4'],\n });\n }\n onSelect(selectedKeys, info) {\n console.log('onSelect', info);\n this.setState({ selectedKeys });\n }\n render() {\n const loop = data => data.map((item) => {\n if (item.children) {\n return (\n \n {loop(item.children)}\n \n );\n }\n return ;\n });\n return (\n \n {loop(gData)}\n \n );\n }\n};","desc":""},{"example":,"title":" Tree 拖拽使用事例","code":"/**\n*\n* @title Tree 拖拽使用事例\n* @description 拖动结点插入到另一个结点后面或者其他的父节点里面。\n*\n*/\n\nclass Demo3 extends Component{\n constructor(props) {\n super(props);\n this.state = {\n gData,\n expandedKeys: ['0-0', '0-0-0', '0-0-0-0'],\n };\n this.onDragEnter = this.onDragEnter.bind(this);\n this.onDrop = this.onDrop.bind(this);\n }\n onDragEnter(info) {\n console.log(info);\n // expandedKeys 需要受控时设置\n // this.setState({\n // expandedKeys: info.expandedKeys,\n // });\n }\n onDrop(info) {\n console.log(info);\n const dropKey = info.node.props.eventKey;\n const dragKey = info.dragNode.props.eventKey;\n // const dragNodesKeys = info.dragNodesKeys;\n const loop = (data, key, callback) => {\n data.forEach((item, index, arr) => {\n if (item.key === key) {\n return callback(item, index, arr);\n }\n if (item.children) {\n return loop(item.children, key, callback);\n }\n });\n };\n const data = [...this.state.gData];\n let dragObj;\n loop(data, dragKey, (item, index, arr) => {\n arr.splice(index, 1);\n dragObj = item;\n });\n if (info.dropToGap) {\n let ar;\n let i;\n loop(data, dropKey, (item, index, arr) => {\n ar = arr;\n i = index;\n });\n ar.splice(i, 0, dragObj);\n } else {\n loop(data, dropKey, (item) => {\n item.children = item.children || [];\n // where to insert 示例添加到尾部,可以是随意位置\n item.children.push(dragObj);\n });\n }\n this.setState({\n gData: data,\n });\n }\n render() {\n const loop = data => data.map((item) => {\n if (item.children && item.children.length) {\n return {loop(item.children)};\n }\n return ;\n });\n return (\n \n {loop(this.state.gData)}\n \n );\n }\n};","desc":" 拖动结点插入到另一个结点后面或者其他的父节点里面。"},{"example":,"title":" Tree可搜索事例","code":"/**\n*\n* @title Tree可搜索事例\n* @description\n*\n*/\nconst dataList = [];\nconst generateList = (data) => {\n for (let i = 0; i < data.length; i++) {\n const node = data[i];\n const key = node.key;\n dataList.push({ key, title: key });\n if (node.children) {\n generateList(node.children, node.key);\n }\n }\n};\ngenerateList(gData);\n\nconst getParentKey = (key, tree) => {\n let parentKey;\n for (let i = 0; i < tree.length; i++) {\n const node = tree[i];\n if (node.children) {\n if (node.children.some(item => item.key === key)) {\n parentKey = node.key;\n } else if (getParentKey(key, node.children)) {\n parentKey = getParentKey(key, node.children);\n }\n }\n }\n return parentKey;\n};\n\n\nclass Demo4 extends Component {\n constructor(props) {\n super(props);\n this.state = {\n expandedKeys: [],\n searchValue: '',\n autoExpandParent: true,\n }\n }\n onExpand = (expandedKeys) => {\n this.setState({\n expandedKeys,\n autoExpandParent: false,\n });\n }\n onChange = (e) => {\n const value = e.target.value;\n const expandedKeys = [];\n dataList.forEach((item) => {\n if (item.key.indexOf(value) > -1) {\n expandedKeys.push(getParentKey(item.key, gData));\n }\n });\n const uniqueExpandedKeys = [];\n expandedKeys.forEach((item) => {\n if (item && uniqueExpandedKeys.indexOf(item) === -1) {\n uniqueExpandedKeys.push(item);\n }\n });\n this.setState({\n expandedKeys: uniqueExpandedKeys,\n searchValue: value,\n autoExpandParent: true,\n });\n }\n render() {\n const { searchValue, expandedKeys, autoExpandParent } = this.state;\n const loop = data => data.map((item) => {\n const index = item.key.search(searchValue);\n const beforeStr = item.key.substr(0, index);\n const afterStr = item.key.substr(index + searchValue.length);\n const title = index > -1 ? (\n \n {beforeStr}\n {searchValue}\n {afterStr}\n \n ) : {item.key};\n if (item.children) {\n return (\n \n {loop(item.children)}\n \n );\n }\n return ;\n });\n return (\n \n \n \n {loop(gData)}\n \n
\n );\n }\n}","desc":""},{"example":,"title":" Tree异步数据加载","code":"/**\n*\n* @title Tree异步数据加载\n* @description 当点击展开,异步获取子节点数据\n*\n*/\nfunction generateTreeNodes(treeNode) {\n const arr = [];\n const key = treeNode.props.eventKey;\n for (let i = 0; i < 3; i++) {\n arr.push({ name: `leaf ${key}-${i}`, key: `${key}-${i}` });\n }\n return arr;\n}\n\nfunction setLeaf(treeData, curKey, level) {\n const loopLeaf = (data, lev) => {\n const l = lev - 1;\n data.forEach((item) => {\n if ((item.key.length > curKey.length) ? item.key.indexOf(curKey) !== 0 :\n curKey.indexOf(item.key) !== 0) {\n return;\n }\n if (item.children) {\n loopLeaf(item.children, l);\n } else if (l < 1) {\n item.isLeaf = true;\n }\n });\n };\n loopLeaf(treeData, level + 1);\n}\n\nfunction getNewTreeData(treeData, curKey, child, level) {\n const loop = (data) => {\n if (level < 1 || curKey.length - 3 > level * 2) return;\n data.forEach((item) => {\n if (curKey.indexOf(item.key) === 0) {\n if (item.children) {\n loop(item.children);\n } else {\n item.children = child;\n }\n }\n });\n };\n loop(treeData);\n setLeaf(treeData, curKey, level);\n}\n\nclass Demo5 extends Component{\n constructor(props) {\n super(props);\n this.state = {\n treeData: [],\n };\n this.onSelect = this.onSelect.bind(this);\n this.onLoadData = this.onLoadData.bind(this);\n }\n componentDidMount() {\n setTimeout(() => {\n this.setState({\n treeData: [\n { name: 'pNode 01', key: '0-0' },\n { name: 'pNode 02', key: '0-1' },\n { name: 'pNode 03', key: '0-2', isLeaf: true },\n ],\n });\n }, 100);\n }\n onSelect(info) {\n console.log('selected', info);\n }\n onLoadData(treeNode) {\n return new Promise((resolve) => {\n setTimeout(() => {\n const treeData = [...this.state.treeData];\n getNewTreeData(treeData, treeNode.props.eventKey, generateTreeNodes(treeNode), 2);\n this.setState({ treeData });\n resolve();\n }, 1000);\n });\n }\n render() {\n const loop = data => data.map((item) => {\n if (item.children) {\n return {loop(item.children)};\n }\n return ;\n });\n const treeNodes = loop(this.state.treeData);\n return (\n \n {treeNodes}\n \n );\n }\n};","desc":" 当点击展开,异步获取子节点数据"}]
+
+
+class Demo extends Component {
+ constructor(props){
+ super(props);
+ this.state = {
+ open: false
+ }
+ this.handleClick = this.handleClick.bind(this);
+ }
+ handleClick() {
+ this.setState({ open: !this.state.open })
+ }
+
+ render () {
+ const { title, example, code, desc } = this.props;
+ let caret = this.state.open ? CARETUP : CARET;
+ let text = this.state.open ? "隐藏代码" : "查看代码";
+
+ const footer = (
+
+ );
+ const header = (
+
+
+ { example }
+
+
+
+
+
+ );
+ return (
+
+ { title }
+ { desc }
+
+ { code }
+
+
+ )
+ }
+}
+
+class DemoGroup extends Component {
+ constructor(props){
+ super(props)
+ }
+ render () {
+ return (
+
+ {DemoArray.map((child,index) => {
+
+ return (
+
+ )
+
+ })}
+
+ )
+ }
+}
+
+ReactDOM.render(, document.getElementById('tinperBeeDemo'));
diff --git a/docs/api.md b/docs/api.md
new file mode 100644
index 0000000..8f4181e
--- /dev/null
+++ b/docs/api.md
@@ -0,0 +1,42 @@
+# Tree
+
+## 代码演示
+
+## API
+
+## Tree
+|参数|说明|类型|默认值|
+|:---|:-----|:----|:------|
+multiple|是否允许选择多个树节点|bool|false
+checkable|是否支持添加在树节点前添加Checkbox|bool|false
+defaultExpandAll|默认是否展开所有节点|bool|false
+defaultExpandedKeys|默认展开指定的节点|String[]|[]
+expandedKeys|指定展开的节点(controlled)String[]|[]
+autoExpandParent|是否自定展开父节点|bool|true
+defaultCheckedKeys|指定默认被选中的节点key|String[]|[]
+checkedKeys|指定被选中的节点(controlled)(PS:当指定的是父节点,所有的子节点也会被指定;当指定的是子节点,父节点也会被选中。当checkable和checkStrictly都为true,子节点与父节点的选择情况都不会影响到对方|String[]/{checked:Array,halfChecked:Array}|[]
+checkStrictly|精细的检查每个节点|bool|false
+defaultSelectedKeys|指定选中的节点key|String[]|[]
+selectedKeys|指定选中的节点keys(controlled)|String[]|-
+onExpand|当打开或关闭树节点触发的方法|function(expandedKeys, {expanded: bool, node})|-
+onCheck|当选择事件发生触发的方法|function(checkedKeys, e:{checked: bool, checkedNodes, node, event})|-
+onSelect|当用户选择树节点触发的回调函数|function(selectedKeys, e:{selected: bool, selectedNodes, node, event})|-
+filterTreeNode|过滤树节点的方法(highlight),当返回true,相关联的节点会高亮|function(node)|-
+loadData|异步加载数据|function(node)|-
+onRightClick|当用户点击右键触发的回调函数|function({event,node})|-
+draggable|树是否可拖拽(IE>8| bool|false
+onDragStart|当树节点刚开始拖拽所触发的放方法|function({event,node})|-
+onDragEnter|当拖拽进入触发的方法|function({event,node,expandedKeys})|-
+onDragOver|当拖拽经过触发的方法|function({event,node})|-
+onDragLeave|当拖拽离开触发的方法|function({event,node})|-
+onDragEnd当拖拽结束触发的方法|function({event,node})|-
+onDrop|当节点放下触发方法function({event, node, dragNode, dragNodesKeys})|-
+
+## TreeNode
+|参数|说明|类型|默认值|
+|:---|:-----|:----|:------|
+disabled|节点是否不可用|bool|false
+disableCheckbox|节点的checkbox是否不可用|bool|false
+title|名称标题|String/element |--
+key|节点key,和(default)ExpandedKeys / (default)CheckedKeys / (default)SelectedKeys一起用,必须是唯一的|String|-
+isLeaf|是否是叶子节点|bool|false
diff --git a/docs/api_en.md b/docs/api_en.md
new file mode 100644
index 0000000..4424a74
--- /dev/null
+++ b/docs/api_en.md
@@ -0,0 +1,5 @@
+## Tree
+## Code display
+## API
+|Property|Description|Type|Default|
+|:---|:-----|:----|:------|
\ No newline at end of file
diff --git a/index.html b/index.html
new file mode 100644
index 0000000..dfc6777
--- /dev/null
+++ b/index.html
@@ -0,0 +1,22 @@
+
+
+
+
+ tinper-bee demo
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/package.json b/package.json
new file mode 100644
index 0000000..913dac8
--- /dev/null
+++ b/package.json
@@ -0,0 +1,55 @@
+{
+ "name": "bee-tree",
+ "version": "0.0.1",
+ "description": "Tree ui component for react",
+ "keywords": [
+ "react",
+ "react-component",
+ "bee-tree",
+ "iuap-design",
+ "tinper-bee",
+ "Tree"
+ ],
+ "engines": {
+ "node": ">=4.0.0"
+ },
+ "homepage": "https://github.com/tinper-bee/bee-tree.git",
+ "author": "Yonyou FED",
+ "repository": "http://github.com/tinper-bee/bee-tree",
+ "bugs": "https://github.com/tinper-bee/bee-tree.git/issues",
+ "license": "MIT",
+ "main": "./build/index.js",
+ "config": {
+ "port": 3000
+ },
+ "scripts": {
+ "dev": "bee-tools run start",
+ "build": "bee-tools run build",
+ "lint": "bee-tools run lint",
+ "test": "bee-tools run test",
+ "chrome": "bee-tools run chrome",
+ "coveralls": "bee-tools run coverage",
+ "browsers": "bee-tools run browsers",
+ "pub": "bee-tools run pub"
+ },
+ "dependencies": {
+ "bee-animate": "latest",
+ "bee-checkbox": "^0.1.4",
+ "bee-form-control": "^0.1.5",
+ "classnames": "^2.2.5",
+ "object-assign": "latest",
+ "tinper-bee-core": "latest"
+ },
+ "devDependencies": {
+ "chai": "^3.5.0",
+ "enzyme": "^2.4.1",
+ "react": "~0.14.0",
+ "react-addons-test-utils": "15.3.2",
+ "react-dom": "~0.14.0",
+ "console-polyfill": "~0.2.1",
+ "es5-shim": "~4.1.10",
+ "bee-panel": "latest",
+ "bee-layout": "latest",
+ "bee-button": "latest"
+ }
+}
diff --git a/src copy/Tree.js b/src copy/Tree.js
new file mode 100644
index 0000000..4b82601
--- /dev/null
+++ b/src copy/Tree.js
@@ -0,0 +1,109 @@
+import React from 'react';
+import RcTree, { TreeNode } from './src/index';
+import animation from './openAnimation';
+
+export interface AntTreeNodeProps {
+ disabled?: boolean;
+ disableCheckbox?: boolean;
+ title?: string | React.ReactNode;
+ key?: string;
+ isLeaf?: boolean;
+}
+
+export class AntTreeNode extends React.Component {
+ render() {
+ return ;
+ }
+}
+
+export interface AntTreeNodeEvent {
+ event: 'check' | 'select';
+ node: AntTreeNode;
+ checked?: boolean;
+ checkedNodes?: Array;
+ selected?: boolean;
+ selectedNodes?: Array;
+}
+
+export interface AntTreeNodeMouseEvent {
+ node: AntTreeNode;
+ event: React.MouseEventHandler;
+}
+
+export interface TreeProps {
+ showLine?: boolean;
+ className?: string;
+ /** 是否支持多选 */
+ multiple?: boolean;
+ /** 是否自动展开父节点 */
+ autoExpandParent?: boolean;
+ /** checkable状态下节点选择完全受控(父子节点选中状态不再关联)*/
+ checkStrictly?: boolean;
+ /** 是否支持选中 */
+ checkable?: boolean;
+ /** 默认展开所有树节点 */
+ defaultExpandAll?: boolean;
+ /** 默认展开指定的树节点 */
+ defaultExpandedKeys?: Array;
+ /** (受控)展开指定的树节点 */
+ expandedKeys?: Array;
+ /** (受控)选中复选框的树节点 */
+ checkedKeys?: Array | { checked: Array, halfChecked: Array };
+ /** 默认选中复选框的树节点 */
+ defaultCheckedKeys?: Array;
+ /** (受控)设置选中的树节点 */
+ selectedKeys?: Array;
+ /** 默认选中的树节点 */
+ defaultSelectedKeys?: Array;
+ /** 展开/收起节点时触发 */
+ onExpand?: (expandedKeys: Array, info: { node: AntTreeNode, expanded: boolean }) => void | PromiseLike;
+ /** 点击复选框触发 */
+ onCheck?: (checkedKeys: Array, e: AntTreeNodeEvent) => void;
+ /** 点击树节点触发 */
+ onSelect?: (selectedKeys: Array, e: AntTreeNodeEvent) => void;
+ /** filter some AntTreeNodes as you need. it should return true */
+ filterAntTreeNode?: (node: AntTreeNode) => boolean;
+ /** 异步加载数据 */
+ loadData?: (node: AntTreeNode) => PromiseLike;
+ /** 响应右键点击 */
+ onRightClick?: (options: AntTreeNodeMouseEvent) => void;
+ /** 设置节点可拖拽(IE>8)*/
+ draggable?: boolean;
+ /** 开始拖拽时调用 */
+ onDragStart?: (options: AntTreeNodeMouseEvent) => void;
+ /** dragenter 触发时调用 */
+ onDragEnter?: (options: AntTreeNodeMouseEvent) => void;
+ /** dragover 触发时调用 */
+ onDragOver?: (options: AntTreeNodeMouseEvent) => void;
+ /** dragleave 触发时调用 */
+ onDragLeave?: (options: AntTreeNodeMouseEvent) => void;
+ /** drop 触发时调用 */
+ onDrop?: (options: AntTreeNodeMouseEvent) => void;
+ style?: React.CSSProperties;
+ prefixCls?: string;
+ filterTreeNode?: (node: AntTreeNode) => boolean;
+}
+
+export default class Tree extends React.Component {
+ static TreeNode = TreeNode;
+
+ static defaultProps = {
+ prefixCls: 'ant-tree',
+ checkable: false,
+ showIcon: false,
+ openAnimation: animation,
+ };
+
+ render() {
+ const props = this.props;
+ let checkable = props.checkable;
+ return (
+ ) : checkable }
+ >
+ {this.props.children}
+
+ );
+ }
+}
diff --git a/src copy/Tree.scss b/src copy/Tree.scss
new file mode 100644
index 0000000..ad7f06e
--- /dev/null
+++ b/src copy/Tree.scss
@@ -0,0 +1,313 @@
+@import "../node_modules/tinper-bee-core/scss/minxin-variables";
+@import "../node_modules/tinper-bee-core/scss/minxin-mixins";
+@import "../node_modules/bee-checkbox/src/Checkbox";
+
+
+//css 分割线
+.ant-tree li span.ant-tree-checkbox {
+ margin: 2px 4px 0 0;
+}
+.ant-tree-checkbox {
+ white-space: nowrap;
+ cursor: pointer;
+ outline: none;
+ display: inline-block;
+ line-height: 1;
+ position: relative;
+ vertical-align: middle;
+}
+.ant-tree-checkbox-checked .ant-tree-checkbox-inner, .ant-tree-checkbox-indeterminate .ant-tree-checkbox-inner {
+ background-color: #108ee9;
+ border-color: #108ee9;
+}
+.ant-tree-checkbox-inner {
+ position: relative;
+ top: 0;
+ left: 0;
+ display: inline-block;
+ width: 14px;
+ height: 14px;
+ border: 1px solid #d9d9d9;
+ border-radius: 3px;
+ background-color: #fff;
+ -webkit-transition: all .3s;
+ transition: all .3s;
+}
+.ant-tree-checkbox-checked .ant-tree-checkbox-inner, .ant-tree-checkbox-indeterminate .ant-tree-checkbox-inner {
+ background-color: #108ee9;
+ border-color: #108ee9;
+}
+ant-tree-checkbox-disabled .ant-tree-checkbox-inner {
+ border-color: #d9d9d9 !important;
+ background-color: #f3f3f3;
+}
+.ant-tree-checkbox-indeterminate .ant-tree-checkbox-inner:after {
+ content: ' ';
+ -webkit-transform: scale(1);
+ -ms-transform: scale(1);
+ transform: scale(1);
+ position: absolute;
+ left: 2px;
+ top: 5px;
+ width: 8px;
+ height: 1px;
+}
+.ant-tree-checkbox-disabled .ant-tree-checkbox-inner {
+ border-color: #d9d9d9 !important;
+ background-color: #f3f3f3;
+}
+.ant-tree-checkbox-disabled.ant-tree-checkbox-checked .ant-tree-checkbox-inner:after {
+ -webkit-animation-name: none;
+ animation-name: none;
+ border-color: #ccc;
+}
+.ant-tree-checkbox-disabled .ant-tree-checkbox-inner:after {
+ -webkit-animation-name: none;
+ animation-name: none;
+ border-color: #f3f3f3;
+}
+.ant-tree-checkbox-checked .ant-tree-checkbox-inner:after {
+ -webkit-transform: rotate(45deg) scale(1);
+ -ms-transform: rotate(45deg) scale(1);
+ transform: rotate(45deg) scale(1);
+ position: absolute;
+ left: 4px;
+ top: 1px;
+ display: table;
+ width: 5px;
+ height: 8px;
+ border: 2px solid #fff;
+ border-top: 0;
+ border-left: 0;
+ content: ' ';
+ -webkit-transition: all 0.2s cubic-bezier(0.12, 0.4, 0.29, 1.46) 0.1s;
+ transition: all 0.2s cubic-bezier(0.12, 0.4, 0.29, 1.46) 0.1s;
+}
+.ant-tree-checkbox-inner:after {
+ -webkit-transform: rotate(45deg) scale(0);
+ -ms-transform: rotate(45deg) scale(0);
+ transform: rotate(45deg) scale(0);
+ position: absolute;
+ left: 4px;
+ top: 1px;
+ display: table;
+ width: 5px;
+ height: 8px;
+ border: 2px solid #fff;
+ border-top: 0;
+ border-left: 0;
+ content: ' ';
+ -webkit-transition: all 0.1s cubic-bezier(0.71, -0.46, 0.88, 0.6);
+ transition: all 0.1s cubic-bezier(0.71, -0.46, 0.88, 0.6);
+}
+.ant-tree {
+ margin: 0;
+ padding: 5px;
+ font-size: 12px;
+}
+.ant-tree li {
+ padding: 0;
+ margin: 7px 0;
+ list-style: none;
+ white-space: nowrap;
+ outline: 0;
+}
+.ant-tree li a[draggable],
+.ant-tree li a[draggable="true"] {
+ -webkit-user-select: none;
+ -moz-user-select: none;
+ -ms-user-select: none;
+ user-select: none;
+ /* Required to make elements draggable in old WebKit */
+ -khtml-user-drag: element;
+ -webkit-user-drag: element;
+}
+.ant-tree li.drag-over > a[draggable] {
+ background-color: #108ee9;
+ color: white;
+ opacity: 0.8;
+}
+.ant-tree li.drag-over-gap-top > a[draggable] {
+ border-top: 2px #108ee9 solid;
+}
+.ant-tree li.drag-over-gap-bottom > a[draggable] {
+ border-bottom: 2px #108ee9 solid;
+}
+.ant-tree li.filter-node > a {
+ color: #f50 !important;
+ font-weight: bold!important;
+}
+.ant-tree li ul {
+ margin: 0;
+ padding: 0 0 0 18px;
+}
+.ant-tree li a {
+ display: inline-block;
+ padding: 1px 5px;
+ border-radius: 2px;
+ margin: 0;
+ cursor: pointer;
+ text-decoration: none;
+ vertical-align: top;
+ color: #666;
+ -webkit-transition: all 0.3s ease;
+ transition: all 0.3s ease;
+}
+.ant-tree li a:hover {
+ background-color: #e7f4fd;
+}
+.ant-tree li a.ant-tree-node-selected {
+ background-color: #cfe8fb;
+}
+.ant-tree li span.u-checkbox {
+ margin: 2px 4px 0 0;
+}
+.ant-tree li span.ant-tree-switcher,
+.ant-tree li span.ant-tree-iconEle {
+ margin: 0;
+ width: 16px;
+ height: 16px;
+ line-height: 16px;
+ display: inline-block;
+ vertical-align: middle;
+ border: 0 none;
+ cursor: pointer;
+ outline: none;
+}
+.ant-tree li span.ant-tree-icon_loading:after {
+ display: inline-block;
+ font-family: 'uf';
+ text-rendering: optimizeLegibility;
+ -webkit-font-smoothing: antialiased;
+ -moz-osx-font-smoothing: grayscale;
+ content: "\E6AE";
+ -webkit-animation: loadingCircle 1s infinite linear;
+ animation: loadingCircle 1s infinite linear;
+ color: #108ee9;
+}
+.ant-tree li span.ant-tree-switcher.ant-tree-switcher-noop {
+ cursor: auto;
+}
+.ant-tree li span.ant-tree-switcher.ant-tree-roots_open,
+.ant-tree li span.ant-tree-switcher.ant-tree-center_open,
+.ant-tree li span.ant-tree-switcher.ant-tree-bottom_open,
+.ant-tree li span.ant-tree-switcher.ant-tree-noline_open {
+ position: relative;
+}
+.ant-tree li span.ant-tree-switcher.ant-tree-roots_open:after,
+.ant-tree li span.ant-tree-switcher.ant-tree-center_open:after,
+.ant-tree li span.ant-tree-switcher.ant-tree-bottom_open:after,
+.ant-tree li span.ant-tree-switcher.ant-tree-noline_open:after {
+ font-size: 12px;
+ font-size: 7px \9;
+ -webkit-transform: scale(0.58333333) rotate(0deg);
+ -ms-transform: scale(0.58333333) rotate(0deg);
+ transform: scale(0.58333333) rotate(0deg);
+ /* IE6-IE8 */
+ -ms-filter: "progid:DXImageTransform.Microsoft.Matrix(sizingMethod='auto expand', M11=1, M12=0, M21=0, M22=1)";
+ zoom: 1;
+ display: inline-block;
+ font-family: 'uf';
+ text-rendering: optimizeLegibility;
+ -webkit-font-smoothing: antialiased;
+ -moz-osx-font-smoothing: grayscale;
+ content: "\e639";
+ font-weight: bold;
+ position: absolute;
+ top: 0;
+ right: 4px;
+ color: #666;
+ -webkit-transition: -webkit-transform .3s ease;
+ transition: -webkit-transform .3s ease;
+ transition: transform .3s ease;
+ transition: transform .3s ease, -webkit-transform .3s ease;
+}
+:root .ant-tree li span.ant-tree-switcher.ant-tree-roots_open:after,
+:root .ant-tree li span.ant-tree-switcher.ant-tree-center_open:after,
+:root .ant-tree li span.ant-tree-switcher.ant-tree-bottom_open:after,
+:root .ant-tree li span.ant-tree-switcher.ant-tree-noline_open:after {
+ -webkit-filter: none;
+ filter: none;
+}
+:root .ant-tree li span.ant-tree-switcher.ant-tree-roots_open:after,
+:root .ant-tree li span.ant-tree-switcher.ant-tree-center_open:after,
+:root .ant-tree li span.ant-tree-switcher.ant-tree-bottom_open:after,
+:root .ant-tree li span.ant-tree-switcher.ant-tree-noline_open:after {
+ font-size: 12px;
+}
+.ant-tree li span.ant-tree-switcher.ant-tree-roots_close,
+.ant-tree li span.ant-tree-switcher.ant-tree-center_close,
+.ant-tree li span.ant-tree-switcher.ant-tree-bottom_close,
+.ant-tree li span.ant-tree-switcher.ant-tree-noline_close {
+ position: relative;
+ -ms-filter: "progid:DXImageTransform.Microsoft.BasicImage(rotation=3)";
+}
+.ant-tree li span.ant-tree-switcher.ant-tree-roots_close:after,
+.ant-tree li span.ant-tree-switcher.ant-tree-center_close:after,
+.ant-tree li span.ant-tree-switcher.ant-tree-bottom_close:after,
+.ant-tree li span.ant-tree-switcher.ant-tree-noline_close:after {
+ font-size: 12px;
+ font-size: 7px \9;
+ -webkit-transform: scale(0.58333333) rotate(0deg);
+ -ms-transform: scale(0.58333333) rotate(0deg);
+ transform: scale(0.58333333) rotate(0deg);
+ /* IE6-IE8 */
+ -ms-filter: "progid:DXImageTransform.Microsoft.Matrix(sizingMethod='auto expand', M11=1, M12=0, M21=0, M22=1)";
+ zoom: 1;
+ display: inline-block;
+ font-family: 'uf';
+ text-rendering: optimizeLegibility;
+ -webkit-font-smoothing: antialiased;
+ -moz-osx-font-smoothing: grayscale;
+ content: "\e639";
+ font-weight: bold;
+ position: absolute;
+ top: 0;
+ right: 4px;
+ color: #666;
+ -webkit-transition: -webkit-transform .3s ease;
+ transition: -webkit-transform .3s ease;
+ transition: transform .3s ease;
+ transition: transform .3s ease, -webkit-transform .3s ease;
+}
+:root .ant-tree li span.ant-tree-switcher.ant-tree-roots_close:after,
+:root .ant-tree li span.ant-tree-switcher.ant-tree-center_close:after,
+:root .ant-tree li span.ant-tree-switcher.ant-tree-bottom_close:after,
+:root .ant-tree li span.ant-tree-switcher.ant-tree-noline_close:after {
+ -webkit-filter: none;
+ filter: none;
+}
+:root .ant-tree li span.ant-tree-switcher.ant-tree-roots_close:after,
+:root .ant-tree li span.ant-tree-switcher.ant-tree-center_close:after,
+:root .ant-tree li span.ant-tree-switcher.ant-tree-bottom_close:after,
+:root .ant-tree li span.ant-tree-switcher.ant-tree-noline_close:after {
+ font-size: 12px;
+}
+.ant-tree li span.ant-tree-switcher.ant-tree-roots_close:after,
+.ant-tree li span.ant-tree-switcher.ant-tree-center_close:after,
+.ant-tree li span.ant-tree-switcher.ant-tree-bottom_close:after,
+.ant-tree li span.ant-tree-switcher.ant-tree-noline_close:after {
+ -webkit-transform: rotate(270deg) scale(0.6);
+ -ms-transform: rotate(270deg) scale(0.6);
+ transform: rotate(270deg) scale(0.6);
+}
+.ant-tree-child-tree {
+ display: none;
+}
+.ant-tree-child-tree-open {
+ display: block;
+}
+.ant-tree-treenode-disabled > span,
+.ant-tree-treenode-disabled > a,
+.ant-tree-treenode-disabled > a span {
+ color: #ccc;
+ cursor: not-allowed;
+}
+.ant-tree-icon__open {
+ margin-right: 2px;
+ vertical-align: top;
+}
+.ant-tree-icon__close {
+ margin-right: 2px;
+ vertical-align: top;
+}
diff --git a/src copy/TreeNode.js b/src copy/TreeNode.js
new file mode 100644
index 0000000..069fa00
--- /dev/null
+++ b/src copy/TreeNode.js
@@ -0,0 +1,389 @@
+import React, { PropTypes } from 'react';
+import assign from 'object-assign';
+import classNames from 'classnames';
+import Animate from 'rc-animate';
+import Checkbox from 'bee-checkbox';
+import { browser } from './util';
+
+const browserUa = typeof window !== 'undefined' ? browser(window.navigator) : '';
+const ieOrEdge = /.*(IE|Edge).+/.test(browserUa);
+// const uaArray = browserUa.split(' ');
+// const gtIE8 = uaArray.length !== 2 || uaArray[0].indexOf('IE') === -1 || Number(uaArray[1]) > 8;
+
+const defaultTitle = '---';
+
+class TreeNode extends React.Component {
+ constructor(props) {
+ super(props);
+ [
+ 'onExpand',
+ 'onCheck',
+ 'onContextMenu',
+ 'onMouseEnter',
+ 'onMouseLeave',
+ 'onDragStart',
+ 'onDragEnter',
+ 'onDragOver',
+ 'onDragLeave',
+ 'onDrop',
+ 'onDragEnd',
+ ].forEach((m) => {
+ this[m] = this[m].bind(this);
+ });
+ this.state = {
+ dataLoading: false,
+ dragNodeHighlight: false,
+ };
+ }
+
+ componentDidMount() {
+ if (!this.props.root._treeNodeInstances) {
+ this.props.root._treeNodeInstances = [];
+ }
+ this.props.root._treeNodeInstances.push(this);
+ }
+ // shouldComponentUpdate(nextProps) {
+ // if (!nextProps.expanded) {
+ // return false;
+ // }
+ // return true;
+ // }
+
+ onCheck() {
+ this.props.root.onCheck(this);
+ }
+
+ onSelect() {
+ this.props.root.onSelect(this);
+ }
+
+ onMouseEnter(e) {
+ e.preventDefault();
+ this.props.root.onMouseEnter(e, this);
+ }
+
+ onMouseLeave(e) {
+ e.preventDefault();
+ this.props.root.onMouseLeave(e, this);
+ }
+
+ onContextMenu(e) {
+ e.preventDefault();
+ this.props.root.onContextMenu(e, this);
+ }
+
+ onDragStart(e) {
+ // console.log('dragstart', this.props.eventKey, e);
+ // e.preventDefault();
+ e.stopPropagation();
+ this.setState({
+ dragNodeHighlight: true,
+ });
+ this.props.root.onDragStart(e, this);
+ try {
+ // ie throw error
+ // firefox-need-it
+ e.dataTransfer.setData('text/plain', '');
+ } finally {
+ // empty
+ }
+ }
+
+ onDragEnter(e) {
+ e.preventDefault();
+ e.stopPropagation();
+ this.props.root.onDragEnter(e, this);
+ }
+
+ onDragOver(e) {
+ // todo disabled
+ e.preventDefault();
+ e.stopPropagation();
+ this.props.root.onDragOver(e, this);
+ return false;
+ }
+
+ onDragLeave(e) {
+ e.stopPropagation();
+ this.props.root.onDragLeave(e, this);
+ }
+
+ onDrop(e) {
+ e.preventDefault();
+ e.stopPropagation();
+ this.setState({
+ dragNodeHighlight: false,
+ });
+ this.props.root.onDrop(e, this);
+ }
+
+ onDragEnd(e) {
+ e.stopPropagation();
+ this.setState({
+ dragNodeHighlight: false,
+ });
+ this.props.root.onDragEnd(e, this);
+ }
+
+ onExpand() {
+ const callbackPromise = this.props.root.onExpand(this);
+ if (callbackPromise && typeof callbackPromise === 'object') {
+ const setLoading = (dataLoading) => {
+ this.setState({ dataLoading });
+ };
+ setLoading(true);
+ callbackPromise.then(() => {
+ setLoading(false);
+ }, () => {
+ setLoading(false);
+ });
+ }
+ }
+
+ // keyboard event support
+ onKeyDown(e) {
+ e.preventDefault();
+ }
+
+ renderSwitcher(props, expandedState) {
+ const prefixCls = props.prefixCls;
+ const switcherCls = {
+ [`${prefixCls}-switcher`]: true,
+ };
+ if (!props.showLine) {
+ switcherCls[`${prefixCls}-noline_${expandedState}`] = true;
+ } else if (props.pos === '0-0') {
+ switcherCls[`${prefixCls}-roots_${expandedState}`] = true;
+ } else {
+ switcherCls[`${prefixCls}-center_${expandedState}`] = !props.last;
+ switcherCls[`${prefixCls}-bottom_${expandedState}`] = props.last;
+ }
+ if (props.disabled) {
+ switcherCls[`${prefixCls}-switcher-disabled`] = true;
+ return ;
+ }
+ return ;
+ }
+
+ renderCheckbox(props) {
+ const prefixCls = props.prefixCls;
+ const checkboxCls = {
+ [`${prefixCls}-checkbox`]: true,
+ };
+ if (props.checked) {
+ checkboxCls[`${prefixCls}-checkbox-checked`] = true;
+ } else if (props.halfChecked) {
+ checkboxCls[`${prefixCls}-checkbox-indeterminate`] = true;
+ }
+ let customEle = null;
+ if (typeof props.checkable !== 'boolean') {
+ customEle = props.checkable;
+ }
+ if (props.disabled || props.disableCheckbox) {
+ checkboxCls[`${prefixCls}-checkbox-disabled`] = true;
+ return {customEle};
+ }
+ return (
+ {customEle}
+
+ );
+ /*return (
+
+ )*/
+ }
+
+ renderChildren(props) {
+ const renderFirst = this.renderFirst;
+ this.renderFirst = 1;
+ let transitionAppear = true;
+ if (!renderFirst && props.expanded) {
+ transitionAppear = false;
+ }
+ const children = props.children;
+ let newChildren = children;
+ if (children &&
+ (children.type === TreeNode ||
+ Array.isArray(children) &&
+ children.every((item) => {
+ return item.type === TreeNode;
+ }))) {
+ const cls = {
+ [`${props.prefixCls}-child-tree`]: true,
+ [`${props.prefixCls}-child-tree-open`]: props.expanded,
+ };
+ if (props.showLine) {
+ cls[`${props.prefixCls}-line`] = !props.last;
+ }
+ const animProps = {};
+ if (props.openTransitionName) {
+ animProps.transitionName = props.openTransitionName;
+ } else if (typeof props.openAnimation === 'object') {
+ animProps.animation = assign({}, props.openAnimation);
+ if (!transitionAppear) {
+ delete animProps.animation.appear;
+ }
+ }
+ newChildren = (
+
+ {!props.expanded ? null :
+ {React.Children.map(children, (item, index) => {
+ return props.root.renderTreeNode(item, index, props.pos);
+ }, props.root)}
+
}
+
+ );
+ }
+ return newChildren;
+ }
+
+ render() {
+ const props = this.props;
+ const prefixCls = props.prefixCls;
+ const expandedState = props.expanded ? 'open' : 'close';
+ let iconState = expandedState;
+
+ let canRenderSwitcher = true;
+ const content = props.title;
+ let newChildren = this.renderChildren(props);
+ if (!newChildren || newChildren === props.children) {
+ // content = newChildren;
+ newChildren = null;
+ if (!props.loadData || props.isLeaf) {
+ canRenderSwitcher = false;
+ iconState = 'docu';
+ }
+ }
+ // For performance, does't render children into dom when `!props.expanded` (move to Animate)
+ // if (!props.expanded) {
+ // newChildren = null;
+ // }
+
+ const iconEleCls = {
+ [`${prefixCls}-iconEle`]: true,
+ [`${prefixCls}-icon_loading`]: this.state.dataLoading,
+ [`${prefixCls}-icon__${iconState}`]: true,
+ };
+
+ const selectHandle = () => {
+ const icon = (props.showIcon || props.loadData && this.state.dataLoading) ?
+ : null;
+ const title = {content};
+ const wrap = `${prefixCls}-node-content-wrapper`;
+ const domProps = {
+ className: `${wrap} ${wrap}-${iconState === expandedState ? iconState : 'normal'}`,
+ };
+ if (!props.disabled) {
+ if (props.selected || !props._dropTrigger && this.state.dragNodeHighlight) {
+ domProps.className += ` ${prefixCls}-node-selected`;
+ }
+ domProps.onClick = (e) => {
+ e.preventDefault();
+ if (props.selectable) {
+ this.onSelect();
+ }
+ // not fire check event
+ // if (props.checkable) {
+ // this.onCheck();
+ // }
+ };
+ if (props.onRightClick) {
+ domProps.onContextMenu = this.onContextMenu;
+ }
+ if (props.onMouseEnter) {
+ domProps.onMouseEnter = this.onMouseEnter;
+ }
+ if (props.onMouseLeave) {
+ domProps.onMouseLeave = this.onMouseLeave;
+ }
+ if (props.draggable) {
+ domProps.className += ' draggable';
+ if (ieOrEdge) {
+ // ie bug!
+ domProps.href = '#';
+ }
+ domProps.draggable = true;
+ domProps['aria-grabbed'] = true;
+ domProps.onDragStart = this.onDragStart;
+ }
+ }
+ return (
+
+ {icon}{title}
+
+ );
+ };
+
+ const liProps = {};
+ if (props.draggable) {
+ liProps.onDragEnter = this.onDragEnter;
+ liProps.onDragOver = this.onDragOver;
+ liProps.onDragLeave = this.onDragLeave;
+ liProps.onDrop = this.onDrop;
+ liProps.onDragEnd = this.onDragEnd;
+ }
+
+ let disabledCls = '';
+ let dragOverCls = '';
+ if (props.disabled) {
+ disabledCls = `${prefixCls}-treenode-disabled`;
+ } else if (props.dragOver) {
+ dragOverCls = 'drag-over';
+ } else if (props.dragOverGapTop) {
+ dragOverCls = 'drag-over-gap-top';
+ } else if (props.dragOverGapBottom) {
+ dragOverCls = 'drag-over-gap-bottom';
+ }
+
+ const filterCls = props.filterTreeNode(this) ? 'filter-node' : '';
+
+ const noopSwitcher = () => {
+ const cls = {
+ [`${prefixCls}-switcher`]: true,
+ [`${prefixCls}-switcher-noop`]: true,
+ };
+ if (props.showLine) {
+ cls[`${prefixCls}-center_docu`] = !props.last;
+ cls[`${prefixCls}-bottom_docu`] = props.last;
+ } else {
+ cls[`${prefixCls}-noline_docu`] = true;
+ }
+ return ;
+ };
+
+ return (
+
+ {canRenderSwitcher ? this.renderSwitcher(props, expandedState) : noopSwitcher()}
+ {props.checkable ? this.renderCheckbox(props) : null}
+ {selectHandle()}
+ {newChildren}
+
+ );
+ }
+}
+
+TreeNode.isTreeNode = 1;
+
+TreeNode.propTypes = {
+ prefixCls: PropTypes.string,
+ disabled: PropTypes.bool,
+ disableCheckbox: PropTypes.bool,
+ expanded: PropTypes.bool,
+ isLeaf: PropTypes.bool,
+ root: PropTypes.object,
+ onSelect: PropTypes.func,
+};
+
+TreeNode.defaultProps = {
+ title: defaultTitle,
+};
+
+export default TreeNode;
diff --git a/src copy/Treecopy.scss b/src copy/Treecopy.scss
new file mode 100644
index 0000000..885fc4f
--- /dev/null
+++ b/src copy/Treecopy.scss
@@ -0,0 +1,363 @@
+
+$treePrefixCls: 'ant-tree';
+.#{$treePrefixCls} {
+ margin: 0;
+ padding: 5px;
+ li {
+ padding: 0;
+ margin: 0;
+ list-style: none;
+ white-space: nowrap;
+ outline: 0;
+ .draggable {
+ color: #333;
+ -moz-user-select: none;
+ -khtml-user-select: none;
+ -webkit-user-select: none;
+ user-select: none;
+ /* Required to make elements draggable in old WebKit */
+ -khtml-user-drag: element;
+ -webkit-user-drag: element;
+ }
+ &.drag-over {
+ > .draggable {
+ background-color: #316ac5;
+ color: white;
+ border: 1px #316ac5 solid;
+ opacity: 0.8;
+ }
+ }
+ &.drag-over-gap-top {
+ > .draggable {
+ border-top: 2px blue solid;
+ }
+ }
+ &.drag-over-gap-bottom {
+ > .draggable {
+ border-bottom: 2px blue solid;
+ }
+ }
+ &.filter-node {
+ > .#{$treePrefixCls}-node-content-wrapper {
+ color: #a60000!important;
+ font-weight: bold!important;
+ }
+ }
+ ul {
+ margin: 0;
+ padding: 0 0 0 18px;
+ &.#{$treePrefixCls}-line {
+ background: url("https://t.alipayobjects.com/images/T13BtfXl0mXXXXXXXX.gif") 0 0 repeat-y;
+ }
+ }
+ .#{$treePrefixCls}-node-content-wrapper {
+ display: inline-block;
+ padding: 1px 3px 0 0;
+ margin: 0;
+ cursor: pointer;
+ height: 17px;
+ text-decoration: none;
+ vertical-align: top;
+ }
+ span {
+ &.#{$treePrefixCls}-switcher,
+ &.#{$treePrefixCls}-checkbox,
+ &.#{$treePrefixCls}-iconEle {
+ line-height: 16px;
+ margin-right: 2px;
+ width: 16px;
+ height: 16px;
+ display: inline-block;
+ vertical-align: middle;
+ border: 0 none;
+ cursor: pointer;
+ outline: none;
+ background-color: transparent;
+ background-repeat: no-repeat;
+ background-attachment: scroll;
+ background-image: url("https://t.alipayobjects.com/images/T1.ANfXhXtXXXXXXXX.png");
+ }
+ &.#{$treePrefixCls}-icon_loading {
+ margin-right: 2px;
+ vertical-align: top;
+ background: url(https://t.alipayobjects.com/images/rmsweb/T1YxhiXgJbXXXXXXXX.gif) no-repeat scroll 0 0 transparent;
+ }
+ &.#{$treePrefixCls}-switcher {
+ &.#{$treePrefixCls}-switcher-noop {
+ cursor: auto;
+ }
+ &.#{$treePrefixCls}-roots_open {
+ background-position: -93px -56px;
+ }
+ &.#{$treePrefixCls}-roots_close {
+ background-position: -75px -56px;
+ }
+ &.#{$treePrefixCls}-center_open {
+ background-position: -92px -18px;
+ }
+ &.#{$treePrefixCls}-center_close {
+ background-position: -74px -18px;
+ }
+ &.#{$treePrefixCls}-bottom_open {
+ background-position: -92px -36px;
+ }
+ &.#{$treePrefixCls}-bottom_close {
+ background-position: -74px -36px;
+ }
+ &.#{$treePrefixCls}-noline_open {
+ background-position: -92px -72px;
+ }
+ &.#{$treePrefixCls}-noline_close {
+ background-position: -74px -72px;
+ }
+ &.#{$treePrefixCls}-center_docu {
+ background-position: -56px -18px;
+ }
+ &.#{$treePrefixCls}-bottom_docu {
+ background-position: -56px -36px;
+ }
+ &.#{$treePrefixCls}-noline_docu {
+ background: none;
+ }
+ }
+ &.#{$treePrefixCls}-checkbox {
+ width: 13px;
+ height: 13px;
+ margin: 0 3px;
+ background-position: 0 0;
+ &-checked {
+ background-position: -14px 0;
+ }
+ &-indeterminate {
+ background-position: -14px -28px;
+ }
+ &-disabled {
+ background-position: 0 -56px;
+ }
+ &.#{$treePrefixCls}-checkbox-checked.#{$treePrefixCls}-checkbox-disabled {
+ background-position: -14px -56px;
+ }
+ &.#{$treePrefixCls}-checkbox-indeterminate.#{$treePrefixCls}-checkbox-disabled {
+ position: relative;
+ background: #ccc;
+ border-radius: 3px;
+ &::after {
+ content: ' ';
+ -webkit-transform: scale(1);
+ transform: scale(1);
+ position: absolute;
+ left: 3px;
+ top: 5px;
+ width: 5px;
+ height: 0;
+ border: 2px solid #fff;
+ border-top: 0;
+ border-left: 0;
+ }
+ }
+ }
+ }
+ }
+ &-child-tree {
+ display: none;
+ &-open {
+ display: block;
+ }
+ }
+ &-treenode-disabled {
+ >span,
+ >a,
+ >a span {
+ color: #ccc;
+ cursor: not-allowed;
+ }
+ }
+ &-node-selected {
+ background-color: #ffe6b0;
+ border: 1px #ffb951 solid;
+ opacity: 0.8;
+ }
+ &-icon__open {
+ margin-right: 2px;
+ background-position: -110px -16px;
+ vertical-align: top;
+ }
+ &-icon__close {
+ margin-right: 2px;
+ background-position: -110px 0;
+ vertical-align: top;
+ }
+ &-icon__docu {
+ margin-right: 2px;
+ background-position: -110px -32px;
+ vertical-align: top;
+ }
+}
+
+$tree-prefix-cls: "ant-tree";
+$primary-color: '#108ee9';
+$highlight-color: '#f50';
+$font-size-base: '12px';
+//.antCheckboxFn($checkbox-prefix-cls: "ant-tree-checkbox");
+
+.#{$tree-prefix-cls} {
+ margin: 0;
+ padding: 5px;
+ font-size: $font-size-base;
+ li {
+ padding: 0;
+ margin: 7px 0;
+ list-style: none;
+ white-space: nowrap;
+ outline: 0;
+ a[draggable],
+ a[draggable="true"] {
+ user-select: none;
+ /* Required to make elements draggable in old WebKit */
+ -khtml-user-drag: element;
+ -webkit-user-drag: element;
+ }
+ &.drag-over {
+ > a[draggable] {
+ background-color: $primary-color;
+ color: white;
+ opacity: 0.8;
+ }
+ }
+ &.drag-over-gap-top {
+ > a[draggable] {
+ border-top: 2px $primary-color solid;
+ }
+ }
+ &.drag-over-gap-bottom {
+ > a[draggable] {
+ border-bottom: 2px $primary-color solid;
+ }
+ }
+ &.filter-node {
+ > a {
+ color: $highlight-color!important;
+ font-weight: bold!important;
+ }
+ }
+ ul {
+ margin: 0;
+ padding: 0 0 0 18px;
+ }
+ a {
+ display: inline-block;
+ padding: 1px 5px;
+ border-radius: 2px;
+ margin: 0;
+ cursor: pointer;
+ text-decoration: none;
+ vertical-align: top;
+ color: $text-color;
+ transition: all 0.3s ease;
+ &:hover {
+ background-color: tint($primary-color, 90%);
+ }
+ &.#{$tree-prefix-cls}-node-selected {
+ background-color: tint($primary-color, 80%);
+ }
+ }
+ span {
+ &.#{$tree-prefix-cls}-checkbox {
+ margin: 2px 4px 0 0;
+ }
+ &.#{$tree-prefix-cls}-switcher,
+ &.#{$tree-prefix-cls}-iconEle {
+ margin: 0;
+ width: 16px;
+ height: 16px;
+ line-height: 16px;
+ display: inline-block;
+ vertical-align: middle;
+ border: 0 none;
+ cursor: pointer;
+ outline: none;
+ }
+ &.#{$tree-prefix-cls}-icon_loading {
+ &:after {
+ display: inline-block;
+ animation: loadingCircle 1s infinite linear;
+ color: $primary-color;
+ font-family: 'uf';
+ text-rendering: optimizeLegibility;
+ -webkit-font-smoothing: antialiased;
+ -moz-osx-font-smoothing: grayscale;
+ content: "\e6ae";
+ }
+ }
+ &.#{$tree-prefix-cls}-switcher {
+ &.#{$tree-prefix-cls}-switcher-noop {
+ cursor: auto;
+ }
+ &.#{$tree-prefix-cls}-roots_open,
+ &.#{$tree-prefix-cls}-center_open,
+ &.#{$tree-prefix-cls}-bottom_open,
+ &.#{$tree-prefix-cls}-noline_open {
+ //@include antTreeSwitcherIcon;
+ }
+ &.#{$tree-prefix-cls}-roots_close,
+ &.#{$tree-prefix-cls}-center_close,
+ &.#{$tree-prefix-cls}-bottom_close,
+ &.#{$tree-prefix-cls}-noline_close {
+ //@include antTreeSwitcherIcon;
+ -ms-filter: "progid:DXImageTransform.Microsoft.BasicImage(rotation=3)";
+ &:after {
+ transform: rotate(270deg) scale(0.6);
+ }
+ }
+ }
+ }
+ }
+ &-child-tree {
+ display: none;
+ &-open {
+ display: block;
+ }
+ }
+ &-treenode-disabled {
+ >span,
+ >a,
+ >a span {
+ color: #ccc;
+ cursor: not-allowed;
+ }
+ }
+ &-icon__open {
+ margin-right: 2px;
+ vertical-align: top;
+ }
+ &-icon__close {
+ margin-right: 2px;
+ vertical-align: top;
+ }
+}
+@mixin antTreeSwitcherIcon {
+ position: relative;
+ &:after {
+ font-size: 12px;
+ -webkit-transform: scale(0.58333333) rotate(0deg);
+ -ms-transform: scale(0.58333333) rotate(0deg);
+ transform: scale(0.58333333) rotate(0deg);
+ -ms-filter: "progid:DXImageTransform.Microsoft.Matrix(sizingMethod='auto expand', M11=1, M12=0, M21=0, M22=1)";
+ zoom: 1;
+ display: inline-block;
+ font-family: 'anticon';
+ text-rendering: optimizeLegibility;
+ -webkit-font-smoothing: antialiased;
+ -moz-osx-font-smoothing: grayscale;
+ content: "\E606";
+ font-weight: bold;
+ position: absolute;
+ top: 0;
+ right: 4px;
+ color: #666;
+ -webkit-transition: -webkit-transform .3s ease;
+ transition: -webkit-transform .3s ease;
+ transition: transform .3s ease;
+ transition: transform .3s ease, -webkit-transform .3s ease;
+ }
+}
\ No newline at end of file
diff --git a/src copy/index.js b/src copy/index.js
new file mode 100644
index 0000000..9a52664
--- /dev/null
+++ b/src copy/index.js
@@ -0,0 +1,2 @@
+import Tree from './Tree';
+export default Tree;
\ No newline at end of file
diff --git a/src copy/openAnimation.js b/src copy/openAnimation.js
new file mode 100644
index 0000000..bb05a41
--- /dev/null
+++ b/src copy/openAnimation.js
@@ -0,0 +1,36 @@
+import cssAnimation from 'rc-animate';
+
+function animate(node, show, done) {
+ let height;
+ return cssAnimation(node, 'ant-motion-collapse', {
+ start() {
+ if (!show) {
+ node.style.height = `${node.offsetHeight}px`;
+ } else {
+ height = node.offsetHeight;
+ node.style.height = 0;
+ }
+ },
+ active() {
+ node.style.height = `${show ? height : 0}px`;
+ },
+ end() {
+ node.style.height = '';
+ done();
+ },
+ });
+}
+
+const animation = {
+ enter(node, done) {
+ return animate(node, true, done);
+ },
+ leave(node, done) {
+ return animate(node, false, done);
+ },
+ appear(node, done) {
+ return animate(node, true, done);
+ },
+};
+
+export default animation;
diff --git a/src copy/rcTree.js b/src copy/rcTree.js
new file mode 100644
index 0000000..6b454cd
--- /dev/null
+++ b/src copy/rcTree.js
@@ -0,0 +1,641 @@
+/* eslint no-console:0 */
+import React, { PropTypes } from 'react';
+import assign from 'object-assign';
+import classNames from 'classnames';
+import {
+ loopAllChildren, isInclude, getOffset,
+ filterParentPosition, handleCheckState, getCheck,
+ getStrictlyValue, arraysEqual,
+} from './util';
+
+function noop() {
+}
+
+class Tree extends React.Component {
+ constructor(props) {
+ super(props);
+ ['onKeyDown', 'onCheck'].forEach((m) => {
+ this[m] = this[m].bind(this);
+ });
+ this.contextmenuKeys = [];
+ this.checkedKeysChange = true;
+
+ this.state = {
+ expandedKeys: this.getDefaultExpandedKeys(props),
+ checkedKeys: this.getDefaultCheckedKeys(props),
+ selectedKeys: this.getDefaultSelectedKeys(props),
+ dragNodesKeys: '',
+ dragOverNodeKey: '',
+ dropNodeKey: '',
+ };
+ }
+
+ componentWillReceiveProps(nextProps) {
+ const expandedKeys = this.getDefaultExpandedKeys(nextProps, true);
+ const checkedKeys = this.getDefaultCheckedKeys(nextProps, true);
+ const selectedKeys = this.getDefaultSelectedKeys(nextProps, true);
+ const st = {};
+ if (expandedKeys) {
+ st.expandedKeys = expandedKeys;
+ }
+ if (checkedKeys) {
+ if (nextProps.checkedKeys === this.props.checkedKeys) {
+ this.checkedKeysChange = false;
+ } else {
+ this.checkedKeysChange = true;
+ }
+ st.checkedKeys = checkedKeys;
+ }
+ if (selectedKeys) {
+ st.selectedKeys = selectedKeys;
+ }
+ this.setState(st);
+ }
+
+ onDragStart(e, treeNode) {
+ this.dragNode = treeNode;
+ this.dragNodesKeys = this.getDragNodes(treeNode);
+ const st = {
+ dragNodesKeys: this.dragNodesKeys,
+ };
+ const expandedKeys = this.getExpandedKeys(treeNode, false);
+ if (expandedKeys) {
+ // Controlled expand, save and then reset
+ this.getRawExpandedKeys();
+ st.expandedKeys = expandedKeys;
+ }
+ this.setState(st);
+ this.props.onDragStart({
+ event: e,
+ node: treeNode,
+ });
+ this._dropTrigger = false;
+ }
+
+ onDragEnterGap(e, treeNode) {
+ const offsetTop = (0, getOffset)(treeNode.refs.selectHandle).top;
+ const offsetHeight = treeNode.refs.selectHandle.offsetHeight;
+ const pageY = e.pageY;
+ const gapHeight = 2;
+ if (pageY > offsetTop + offsetHeight - gapHeight) {
+ this.dropPosition = 1;
+ return 1;
+ }
+ if (pageY < offsetTop + gapHeight) {
+ this.dropPosition = -1;
+ return -1;
+ }
+ this.dropPosition = 0;
+ return 0;
+ }
+
+ onDragEnter(e, treeNode) {
+ const enterGap = this.onDragEnterGap(e, treeNode);
+ if (this.dragNode.props.eventKey === treeNode.props.eventKey && enterGap === 0) {
+ this.setState({
+ dragOverNodeKey: '',
+ });
+ return;
+ }
+ const st = {
+ dragOverNodeKey: treeNode.props.eventKey,
+ };
+ const expandedKeys = this.getExpandedKeys(treeNode, true);
+ if (expandedKeys) {
+ this.getRawExpandedKeys();
+ st.expandedKeys = expandedKeys;
+ }
+ this.setState(st);
+ this.props.onDragEnter({
+ event: e,
+ node: treeNode,
+ expandedKeys: expandedKeys && [...expandedKeys] || [...this.state.expandedKeys],
+ });
+ }
+
+ onDragOver(e, treeNode) {
+ this.props.onDragOver({ event: e, node: treeNode });
+ }
+
+ onDragLeave(e, treeNode) {
+ this.props.onDragLeave({ event: e, node: treeNode });
+ }
+
+ onDrop(e, treeNode) {
+ const key = treeNode.props.eventKey;
+ this.setState({
+ dragOverNodeKey: '',
+ dropNodeKey: key,
+ });
+ if (this.dragNodesKeys.indexOf(key) > -1) {
+ if (console.warn) {
+ console.warn('can not drop to dragNode(include it\'s children node)');
+ }
+ return false;
+ }
+
+ const posArr = treeNode.props.pos.split('-');
+ const res = {
+ event: e,
+ node: treeNode,
+ dragNode: this.dragNode,
+ dragNodesKeys: [...this.dragNodesKeys],
+ dropPosition: this.dropPosition + Number(posArr[posArr.length - 1]),
+ };
+ if (this.dropPosition !== 0) {
+ res.dropToGap = true;
+ }
+ if ('expandedKeys' in this.props) {
+ res.rawExpandedKeys = [...this._rawExpandedKeys] || [...this.state.expandedKeys];
+ }
+ this.props.onDrop(res);
+ this._dropTrigger = true;
+ }
+
+ onDragEnd(e, treeNode) {
+ this.setState({
+ dragOverNodeKey: '',
+ });
+ this.props.onDragEnd({ event: e, node: treeNode });
+ }
+
+ onExpand(treeNode) {
+ const expanded = !treeNode.props.expanded;
+ const controlled = 'expandedKeys' in this.props;
+ const expandedKeys = [...this.state.expandedKeys];
+ const index = expandedKeys.indexOf(treeNode.props.eventKey);
+ if (expanded && index === -1) {
+ expandedKeys.push(treeNode.props.eventKey);
+ } else if (!expanded && index > -1) {
+ expandedKeys.splice(index, 1);
+ }
+ if (!controlled) {
+ this.setState({ expandedKeys });
+ }
+ this.props.onExpand(expandedKeys, { node: treeNode, expanded });
+
+ // after data loaded, need set new expandedKeys
+ if (expanded && this.props.loadData) {
+ return this.props.loadData(treeNode).then(() => {
+ if (!controlled) {
+ this.setState({ expandedKeys });
+ }
+ });
+ }
+ }
+
+ onCheck(treeNode) {
+ let checked = !treeNode.props.checked;
+ if (treeNode.props.halfChecked) {
+ checked = true;
+ }
+ const key = treeNode.props.eventKey;
+ let checkedKeys = [...this.state.checkedKeys];
+ const index = checkedKeys.indexOf(key);
+
+ const newSt = {
+ event: 'check',
+ node: treeNode,
+ checked,
+ };
+
+ if (this.props.checkStrictly && ('checkedKeys' in this.props)) {
+ if (checked && index === -1) {
+ checkedKeys.push(key);
+ }
+ if (!checked && index > -1) {
+ checkedKeys.splice(index, 1);
+ }
+ newSt.checkedNodes = [];
+ loopAllChildren(this.props.children, (item, ind, pos, keyOrPos) => {
+ if (checkedKeys.indexOf(keyOrPos) !== -1) {
+ newSt.checkedNodes.push(item);
+ }
+ });
+ this.props.onCheck(getStrictlyValue(checkedKeys, this.props.checkedKeys.halfChecked), newSt);
+ } else {
+ if (checked && index === -1) {
+ this.treeNodesStates[treeNode.props.pos].checked = true;
+ const checkedPositions = [];
+ Object.keys(this.treeNodesStates).forEach(i => {
+ if (this.treeNodesStates[i].checked) {
+ checkedPositions.push(i);
+ }
+ });
+ handleCheckState(this.treeNodesStates, filterParentPosition(checkedPositions), true);
+ }
+ if (!checked) {
+ this.treeNodesStates[treeNode.props.pos].checked = false;
+ this.treeNodesStates[treeNode.props.pos].halfChecked = false;
+ handleCheckState(this.treeNodesStates, [treeNode.props.pos], false);
+ }
+ const checkKeys = getCheck(this.treeNodesStates);
+ newSt.checkedNodes = checkKeys.checkedNodes;
+ newSt.checkedNodesPositions = checkKeys.checkedNodesPositions;
+ newSt.halfCheckedKeys = checkKeys.halfCheckedKeys;
+ this.checkKeys = checkKeys;
+
+ this._checkedKeys = checkedKeys = checkKeys.checkedKeys;
+ if (!('checkedKeys' in this.props)) {
+ this.setState({
+ checkedKeys,
+ });
+ }
+ this.props.onCheck(checkedKeys, newSt);
+ }
+ }
+
+ onSelect(treeNode) {
+ const props = this.props;
+ const selectedKeys = [...this.state.selectedKeys];
+ const eventKey = treeNode.props.eventKey;
+ const index = selectedKeys.indexOf(eventKey);
+ let selected;
+ if (index !== -1) {
+ selected = false;
+ selectedKeys.splice(index, 1);
+ } else {
+ selected = true;
+ if (!props.multiple) {
+ selectedKeys.length = 0;
+ }
+ selectedKeys.push(eventKey);
+ }
+ const selectedNodes = [];
+ if (selectedKeys.length) {
+ loopAllChildren(this.props.children, (item) => {
+ if (selectedKeys.indexOf(item.key) !== -1) {
+ selectedNodes.push(item);
+ }
+ });
+ }
+ const newSt = {
+ event: 'select',
+ node: treeNode,
+ selected,
+ selectedNodes,
+ };
+ if (!('selectedKeys' in this.props)) {
+ this.setState({
+ selectedKeys,
+ });
+ }
+ props.onSelect(selectedKeys, newSt);
+ }
+
+ onMouseEnter(e, treeNode) {
+ this.props.onMouseEnter({ event: e, node: treeNode });
+ }
+
+ onMouseLeave(e, treeNode) {
+ this.props.onMouseLeave({ event: e, node: treeNode });
+ }
+
+ onContextMenu(e, treeNode) {
+ const selectedKeys = [...this.state.selectedKeys];
+ const eventKey = treeNode.props.eventKey;
+ if (this.contextmenuKeys.indexOf(eventKey) === -1) {
+ this.contextmenuKeys.push(eventKey);
+ }
+ this.contextmenuKeys.forEach((key) => {
+ const index = selectedKeys.indexOf(key);
+ if (index !== -1) {
+ selectedKeys.splice(index, 1);
+ }
+ });
+ if (selectedKeys.indexOf(eventKey) === -1) {
+ selectedKeys.push(eventKey);
+ }
+ this.setState({
+ selectedKeys,
+ });
+ this.props.onRightClick({ event: e, node: treeNode });
+ }
+
+ // all keyboard events callbacks run from here at first
+ onKeyDown(e) {
+ e.preventDefault();
+ }
+
+ getFilterExpandedKeys(props, expandKeyProp, expandAll) {
+ const keys = props[expandKeyProp];
+ if (!expandAll && !props.autoExpandParent) {
+ return keys || [];
+ }
+ const expandedPositionArr = [];
+ if (props.autoExpandParent) {
+ loopAllChildren(props.children, (item, index, pos, newKey) => {
+ if (keys.indexOf(newKey) > -1) {
+ expandedPositionArr.push(pos);
+ }
+ });
+ }
+ const filterExpandedKeys = [];
+ loopAllChildren(props.children, (item, index, pos, newKey) => {
+ if (expandAll) {
+ filterExpandedKeys.push(newKey);
+ } else if (props.autoExpandParent) {
+ expandedPositionArr.forEach(p => {
+ if ((p.split('-').length > pos.split('-').length
+ && isInclude(pos.split('-'), p.split('-')) || pos === p)
+ && filterExpandedKeys.indexOf(newKey) === -1) {
+ filterExpandedKeys.push(newKey);
+ }
+ });
+ }
+ });
+ return filterExpandedKeys.length ? filterExpandedKeys : keys;
+ }
+
+ getDefaultExpandedKeys(props, willReceiveProps) {
+ let expandedKeys = willReceiveProps ? undefined :
+ this.getFilterExpandedKeys(props, 'defaultExpandedKeys',
+ props.defaultExpandedKeys.length ? false : props.defaultExpandAll);
+ if ('expandedKeys' in props) {
+ expandedKeys = (props.autoExpandParent ?
+ this.getFilterExpandedKeys(props, 'expandedKeys', false) :
+ props.expandedKeys) || [];
+ }
+ return expandedKeys;
+ }
+
+ getDefaultCheckedKeys(props, willReceiveProps) {
+ let checkedKeys = willReceiveProps ? undefined : props.defaultCheckedKeys;
+ if ('checkedKeys' in props) {
+ checkedKeys = props.checkedKeys || [];
+ if (props.checkStrictly) {
+ if (props.checkedKeys.checked) {
+ checkedKeys = props.checkedKeys.checked;
+ } else if (!Array.isArray(props.checkedKeys)) {
+ checkedKeys = [];
+ }
+ }
+ }
+ return checkedKeys;
+ }
+
+ getDefaultSelectedKeys(props, willReceiveProps) {
+ const getKeys = (keys) => {
+ if (props.multiple) {
+ return [...keys];
+ }
+ if (keys.length) {
+ return [keys[0]];
+ }
+ return keys;
+ };
+ let selectedKeys = willReceiveProps ? undefined : getKeys(props.defaultSelectedKeys);
+ if ('selectedKeys' in props) {
+ selectedKeys = getKeys(props.selectedKeys);
+ }
+ return selectedKeys;
+ }
+
+ getRawExpandedKeys() {
+ if (!this._rawExpandedKeys && ('expandedKeys' in this.props)) {
+ this._rawExpandedKeys = [...this.state.expandedKeys];
+ }
+ }
+
+ getOpenTransitionName() {
+ const props = this.props;
+ let transitionName = props.openTransitionName;
+ const animationName = props.openAnimation;
+ if (!transitionName && typeof animationName === 'string') {
+ transitionName = `${props.prefixCls}-open-${animationName}`;
+ }
+ return transitionName;
+ }
+
+ getDragNodes(treeNode) {
+ const dragNodesKeys = [];
+ const tPArr = treeNode.props.pos.split('-');
+ loopAllChildren(this.props.children, (item, index, pos, newKey) => {
+ const pArr = pos.split('-');
+ if (treeNode.props.pos === pos || tPArr.length < pArr.length && isInclude(tPArr, pArr)) {
+ dragNodesKeys.push(newKey);
+ }
+ });
+ return dragNodesKeys;
+ }
+
+ getExpandedKeys(treeNode, expand) {
+ const key = treeNode.props.eventKey;
+ const expandedKeys = this.state.expandedKeys;
+ const expandedIndex = expandedKeys.indexOf(key);
+ let exKeys;
+ if (expandedIndex > -1 && !expand) {
+ exKeys = [...expandedKeys];
+ exKeys.splice(expandedIndex, 1);
+ return exKeys;
+ }
+ if (expand && expandedKeys.indexOf(key) === -1) {
+ return expandedKeys.concat([key]);
+ }
+ }
+
+ filterTreeNode(treeNode) {
+ const filterTreeNode = this.props.filterTreeNode;
+ if (typeof filterTreeNode !== 'function' || treeNode.props.disabled) {
+ return false;
+ }
+ return filterTreeNode.call(this, treeNode);
+ }
+
+ renderTreeNode(child, index, level = 0) {
+ const pos = `${level}-${index}`;
+ const key = child.key || pos;
+ const state = this.state;
+ const props = this.props;
+
+ // prefer to child's own selectable property if passed
+ let selectable = props.selectable;
+ if (child.props.hasOwnProperty('selectable')) {
+ selectable = child.props.selectable;
+ }
+
+ const cloneProps = {
+ ref: `treeNode-${key}`,
+ root: this,
+ eventKey: key,
+ pos,
+ selectable,
+ loadData: props.loadData,
+ onMouseEnter: props.onMouseEnter,
+ onMouseLeave: props.onMouseLeave,
+ onRightClick: props.onRightClick,
+ prefixCls: props.prefixCls,
+ showLine: props.showLine,
+ showIcon: props.showIcon,
+ draggable: props.draggable,
+ dragOver: state.dragOverNodeKey === key && this.dropPosition === 0,
+ dragOverGapTop: state.dragOverNodeKey === key && this.dropPosition === -1,
+ dragOverGapBottom: state.dragOverNodeKey === key && this.dropPosition === 1,
+ _dropTrigger: this._dropTrigger,
+ expanded: state.expandedKeys.indexOf(key) !== -1,
+ selected: state.selectedKeys.indexOf(key) !== -1,
+ openTransitionName: this.getOpenTransitionName(),
+ openAnimation: props.openAnimation,
+ filterTreeNode: this.filterTreeNode.bind(this),
+ };
+ if (props.checkable) {
+ cloneProps.checkable = props.checkable;
+ if (props.checkStrictly) {
+ if (state.checkedKeys) {
+ cloneProps.checked = state.checkedKeys.indexOf(key) !== -1 || false;
+ }
+ if (props.checkedKeys.halfChecked) {
+ cloneProps.halfChecked = props.checkedKeys.halfChecked.indexOf(key) !== -1 || false;
+ } else {
+ cloneProps.halfChecked = false;
+ }
+ } else {
+ if (this.checkedKeys) {
+ cloneProps.checked = this.checkedKeys.indexOf(key) !== -1 || false;
+ }
+ cloneProps.halfChecked = this.halfCheckedKeys.indexOf(key) !== -1;
+ }
+ }
+ if (this.treeNodesStates && this.treeNodesStates[pos]) {
+ assign(cloneProps, this.treeNodesStates[pos].siblingPosition);
+ }
+ return React.cloneElement(child, cloneProps);
+ }
+
+ render() {
+ const props = this.props;
+ const domProps = {
+ className: classNames(props.className, props.prefixCls),
+ role: 'tree-node',
+ };
+ if (props.focusable) {
+ domProps.tabIndex = '0';
+ domProps.onKeyDown = this.onKeyDown;
+ }
+ const getTreeNodesStates = () => {
+ this.treeNodesStates = {};
+ loopAllChildren(props.children, (item, index, pos, keyOrPos, siblingPosition) => {
+ this.treeNodesStates[pos] = {
+ siblingPosition,
+ };
+ });
+ };
+ if (props.showLine && !props.checkable) {
+ getTreeNodesStates();
+ }
+ if (props.checkable && (this.checkedKeysChange || props.loadData)) {
+ if (props.checkStrictly) {
+ getTreeNodesStates();
+ } else if (props._treeNodesStates) {
+ this.treeNodesStates = props._treeNodesStates.treeNodesStates;
+ this.halfCheckedKeys = props._treeNodesStates.halfCheckedKeys;
+ this.checkedKeys = props._treeNodesStates.checkedKeys;
+ } else {
+ const checkedKeys = this.state.checkedKeys;
+ let checkKeys;
+ if (!props.loadData && this.checkKeys && this._checkedKeys &&
+ arraysEqual(this._checkedKeys, checkedKeys)) {
+ // if checkedKeys the same as _checkedKeys from onCheck, use _checkedKeys.
+ checkKeys = this.checkKeys;
+ } else {
+ const checkedPositions = [];
+ this.treeNodesStates = {};
+ loopAllChildren(props.children, (item, index, pos, keyOrPos, siblingPosition) => {
+ this.treeNodesStates[pos] = {
+ node: item,
+ key: keyOrPos,
+ checked: false,
+ halfChecked: false,
+ siblingPosition,
+ };
+ if (checkedKeys.indexOf(keyOrPos) !== -1) {
+ this.treeNodesStates[pos].checked = true;
+ checkedPositions.push(pos);
+ }
+ });
+ // if the parent node's key exists, it all children node will be checked
+ handleCheckState(this.treeNodesStates, filterParentPosition(checkedPositions), true);
+ checkKeys = getCheck(this.treeNodesStates);
+ }
+ this.halfCheckedKeys = checkKeys.halfCheckedKeys;
+ this.checkedKeys = checkKeys.checkedKeys;
+ }
+ }
+
+ return (
+
+ {React.Children.map(props.children, this.renderTreeNode, this)}
+
+ );
+ }
+}
+
+Tree.propTypes = {
+ prefixCls: PropTypes.string,
+ children: PropTypes.any,
+ showLine: PropTypes.bool,
+ showIcon: PropTypes.bool,
+ selectable: PropTypes.bool,
+ multiple: PropTypes.bool,
+ checkable: PropTypes.oneOfType([
+ PropTypes.bool,
+ PropTypes.node,
+ ]),
+ _treeNodesStates: PropTypes.object,
+ checkStrictly: PropTypes.bool,
+ draggable: PropTypes.bool,
+ autoExpandParent: PropTypes.bool,
+ defaultExpandAll: PropTypes.bool,
+ defaultExpandedKeys: PropTypes.arrayOf(PropTypes.string),
+ expandedKeys: PropTypes.arrayOf(PropTypes.string),
+ defaultCheckedKeys: PropTypes.arrayOf(PropTypes.string),
+ checkedKeys: PropTypes.oneOfType([
+ PropTypes.arrayOf(PropTypes.string),
+ PropTypes.object,
+ ]),
+ defaultSelectedKeys: PropTypes.arrayOf(PropTypes.string),
+ selectedKeys: PropTypes.arrayOf(PropTypes.string),
+ onExpand: PropTypes.func,
+ onCheck: PropTypes.func,
+ onSelect: PropTypes.func,
+ loadData: PropTypes.func,
+ onMouseEnter: PropTypes.func,
+ onMouseLeave: PropTypes.func,
+ onRightClick: PropTypes.func,
+ onDragStart: PropTypes.func,
+ onDragEnter: PropTypes.func,
+ onDragOver: PropTypes.func,
+ onDragLeave: PropTypes.func,
+ onDrop: PropTypes.func,
+ onDragEnd: PropTypes.func,
+ filterTreeNode: PropTypes.func,
+ openTransitionName: PropTypes.string,
+ openAnimation: PropTypes.oneOfType([PropTypes.string, PropTypes.object]),
+};
+
+Tree.defaultProps = {
+ prefixCls: 'rc-tree',
+ showLine: false,
+ showIcon: true,
+ selectable: true,
+ multiple: false,
+ checkable: false,
+ checkStrictly: false,
+ draggable: false,
+ autoExpandParent: true,
+ defaultExpandAll: false,
+ defaultExpandedKeys: [],
+ defaultCheckedKeys: [],
+ defaultSelectedKeys: [],
+ onExpand: noop,
+ onCheck: noop,
+ onSelect: noop,
+ onDragStart: noop,
+ onDragEnter: noop,
+ onDragOver: noop,
+ onDragLeave: noop,
+ onDrop: noop,
+ onDragEnd: noop,
+};
+
+export default Tree;
diff --git a/src copy/rcindex.js b/src copy/rcindex.js
new file mode 100644
index 0000000..97537ef
--- /dev/null
+++ b/src copy/rcindex.js
@@ -0,0 +1,5 @@
+import Tree from './rcTree';
+import TreeNode from './TreeNode';
+Tree.TreeNode = TreeNode;
+
+export default Tree;
diff --git a/src copy/src/Tree.jsx b/src copy/src/Tree.jsx
new file mode 100644
index 0000000..6b454cd
--- /dev/null
+++ b/src copy/src/Tree.jsx
@@ -0,0 +1,641 @@
+/* eslint no-console:0 */
+import React, { PropTypes } from 'react';
+import assign from 'object-assign';
+import classNames from 'classnames';
+import {
+ loopAllChildren, isInclude, getOffset,
+ filterParentPosition, handleCheckState, getCheck,
+ getStrictlyValue, arraysEqual,
+} from './util';
+
+function noop() {
+}
+
+class Tree extends React.Component {
+ constructor(props) {
+ super(props);
+ ['onKeyDown', 'onCheck'].forEach((m) => {
+ this[m] = this[m].bind(this);
+ });
+ this.contextmenuKeys = [];
+ this.checkedKeysChange = true;
+
+ this.state = {
+ expandedKeys: this.getDefaultExpandedKeys(props),
+ checkedKeys: this.getDefaultCheckedKeys(props),
+ selectedKeys: this.getDefaultSelectedKeys(props),
+ dragNodesKeys: '',
+ dragOverNodeKey: '',
+ dropNodeKey: '',
+ };
+ }
+
+ componentWillReceiveProps(nextProps) {
+ const expandedKeys = this.getDefaultExpandedKeys(nextProps, true);
+ const checkedKeys = this.getDefaultCheckedKeys(nextProps, true);
+ const selectedKeys = this.getDefaultSelectedKeys(nextProps, true);
+ const st = {};
+ if (expandedKeys) {
+ st.expandedKeys = expandedKeys;
+ }
+ if (checkedKeys) {
+ if (nextProps.checkedKeys === this.props.checkedKeys) {
+ this.checkedKeysChange = false;
+ } else {
+ this.checkedKeysChange = true;
+ }
+ st.checkedKeys = checkedKeys;
+ }
+ if (selectedKeys) {
+ st.selectedKeys = selectedKeys;
+ }
+ this.setState(st);
+ }
+
+ onDragStart(e, treeNode) {
+ this.dragNode = treeNode;
+ this.dragNodesKeys = this.getDragNodes(treeNode);
+ const st = {
+ dragNodesKeys: this.dragNodesKeys,
+ };
+ const expandedKeys = this.getExpandedKeys(treeNode, false);
+ if (expandedKeys) {
+ // Controlled expand, save and then reset
+ this.getRawExpandedKeys();
+ st.expandedKeys = expandedKeys;
+ }
+ this.setState(st);
+ this.props.onDragStart({
+ event: e,
+ node: treeNode,
+ });
+ this._dropTrigger = false;
+ }
+
+ onDragEnterGap(e, treeNode) {
+ const offsetTop = (0, getOffset)(treeNode.refs.selectHandle).top;
+ const offsetHeight = treeNode.refs.selectHandle.offsetHeight;
+ const pageY = e.pageY;
+ const gapHeight = 2;
+ if (pageY > offsetTop + offsetHeight - gapHeight) {
+ this.dropPosition = 1;
+ return 1;
+ }
+ if (pageY < offsetTop + gapHeight) {
+ this.dropPosition = -1;
+ return -1;
+ }
+ this.dropPosition = 0;
+ return 0;
+ }
+
+ onDragEnter(e, treeNode) {
+ const enterGap = this.onDragEnterGap(e, treeNode);
+ if (this.dragNode.props.eventKey === treeNode.props.eventKey && enterGap === 0) {
+ this.setState({
+ dragOverNodeKey: '',
+ });
+ return;
+ }
+ const st = {
+ dragOverNodeKey: treeNode.props.eventKey,
+ };
+ const expandedKeys = this.getExpandedKeys(treeNode, true);
+ if (expandedKeys) {
+ this.getRawExpandedKeys();
+ st.expandedKeys = expandedKeys;
+ }
+ this.setState(st);
+ this.props.onDragEnter({
+ event: e,
+ node: treeNode,
+ expandedKeys: expandedKeys && [...expandedKeys] || [...this.state.expandedKeys],
+ });
+ }
+
+ onDragOver(e, treeNode) {
+ this.props.onDragOver({ event: e, node: treeNode });
+ }
+
+ onDragLeave(e, treeNode) {
+ this.props.onDragLeave({ event: e, node: treeNode });
+ }
+
+ onDrop(e, treeNode) {
+ const key = treeNode.props.eventKey;
+ this.setState({
+ dragOverNodeKey: '',
+ dropNodeKey: key,
+ });
+ if (this.dragNodesKeys.indexOf(key) > -1) {
+ if (console.warn) {
+ console.warn('can not drop to dragNode(include it\'s children node)');
+ }
+ return false;
+ }
+
+ const posArr = treeNode.props.pos.split('-');
+ const res = {
+ event: e,
+ node: treeNode,
+ dragNode: this.dragNode,
+ dragNodesKeys: [...this.dragNodesKeys],
+ dropPosition: this.dropPosition + Number(posArr[posArr.length - 1]),
+ };
+ if (this.dropPosition !== 0) {
+ res.dropToGap = true;
+ }
+ if ('expandedKeys' in this.props) {
+ res.rawExpandedKeys = [...this._rawExpandedKeys] || [...this.state.expandedKeys];
+ }
+ this.props.onDrop(res);
+ this._dropTrigger = true;
+ }
+
+ onDragEnd(e, treeNode) {
+ this.setState({
+ dragOverNodeKey: '',
+ });
+ this.props.onDragEnd({ event: e, node: treeNode });
+ }
+
+ onExpand(treeNode) {
+ const expanded = !treeNode.props.expanded;
+ const controlled = 'expandedKeys' in this.props;
+ const expandedKeys = [...this.state.expandedKeys];
+ const index = expandedKeys.indexOf(treeNode.props.eventKey);
+ if (expanded && index === -1) {
+ expandedKeys.push(treeNode.props.eventKey);
+ } else if (!expanded && index > -1) {
+ expandedKeys.splice(index, 1);
+ }
+ if (!controlled) {
+ this.setState({ expandedKeys });
+ }
+ this.props.onExpand(expandedKeys, { node: treeNode, expanded });
+
+ // after data loaded, need set new expandedKeys
+ if (expanded && this.props.loadData) {
+ return this.props.loadData(treeNode).then(() => {
+ if (!controlled) {
+ this.setState({ expandedKeys });
+ }
+ });
+ }
+ }
+
+ onCheck(treeNode) {
+ let checked = !treeNode.props.checked;
+ if (treeNode.props.halfChecked) {
+ checked = true;
+ }
+ const key = treeNode.props.eventKey;
+ let checkedKeys = [...this.state.checkedKeys];
+ const index = checkedKeys.indexOf(key);
+
+ const newSt = {
+ event: 'check',
+ node: treeNode,
+ checked,
+ };
+
+ if (this.props.checkStrictly && ('checkedKeys' in this.props)) {
+ if (checked && index === -1) {
+ checkedKeys.push(key);
+ }
+ if (!checked && index > -1) {
+ checkedKeys.splice(index, 1);
+ }
+ newSt.checkedNodes = [];
+ loopAllChildren(this.props.children, (item, ind, pos, keyOrPos) => {
+ if (checkedKeys.indexOf(keyOrPos) !== -1) {
+ newSt.checkedNodes.push(item);
+ }
+ });
+ this.props.onCheck(getStrictlyValue(checkedKeys, this.props.checkedKeys.halfChecked), newSt);
+ } else {
+ if (checked && index === -1) {
+ this.treeNodesStates[treeNode.props.pos].checked = true;
+ const checkedPositions = [];
+ Object.keys(this.treeNodesStates).forEach(i => {
+ if (this.treeNodesStates[i].checked) {
+ checkedPositions.push(i);
+ }
+ });
+ handleCheckState(this.treeNodesStates, filterParentPosition(checkedPositions), true);
+ }
+ if (!checked) {
+ this.treeNodesStates[treeNode.props.pos].checked = false;
+ this.treeNodesStates[treeNode.props.pos].halfChecked = false;
+ handleCheckState(this.treeNodesStates, [treeNode.props.pos], false);
+ }
+ const checkKeys = getCheck(this.treeNodesStates);
+ newSt.checkedNodes = checkKeys.checkedNodes;
+ newSt.checkedNodesPositions = checkKeys.checkedNodesPositions;
+ newSt.halfCheckedKeys = checkKeys.halfCheckedKeys;
+ this.checkKeys = checkKeys;
+
+ this._checkedKeys = checkedKeys = checkKeys.checkedKeys;
+ if (!('checkedKeys' in this.props)) {
+ this.setState({
+ checkedKeys,
+ });
+ }
+ this.props.onCheck(checkedKeys, newSt);
+ }
+ }
+
+ onSelect(treeNode) {
+ const props = this.props;
+ const selectedKeys = [...this.state.selectedKeys];
+ const eventKey = treeNode.props.eventKey;
+ const index = selectedKeys.indexOf(eventKey);
+ let selected;
+ if (index !== -1) {
+ selected = false;
+ selectedKeys.splice(index, 1);
+ } else {
+ selected = true;
+ if (!props.multiple) {
+ selectedKeys.length = 0;
+ }
+ selectedKeys.push(eventKey);
+ }
+ const selectedNodes = [];
+ if (selectedKeys.length) {
+ loopAllChildren(this.props.children, (item) => {
+ if (selectedKeys.indexOf(item.key) !== -1) {
+ selectedNodes.push(item);
+ }
+ });
+ }
+ const newSt = {
+ event: 'select',
+ node: treeNode,
+ selected,
+ selectedNodes,
+ };
+ if (!('selectedKeys' in this.props)) {
+ this.setState({
+ selectedKeys,
+ });
+ }
+ props.onSelect(selectedKeys, newSt);
+ }
+
+ onMouseEnter(e, treeNode) {
+ this.props.onMouseEnter({ event: e, node: treeNode });
+ }
+
+ onMouseLeave(e, treeNode) {
+ this.props.onMouseLeave({ event: e, node: treeNode });
+ }
+
+ onContextMenu(e, treeNode) {
+ const selectedKeys = [...this.state.selectedKeys];
+ const eventKey = treeNode.props.eventKey;
+ if (this.contextmenuKeys.indexOf(eventKey) === -1) {
+ this.contextmenuKeys.push(eventKey);
+ }
+ this.contextmenuKeys.forEach((key) => {
+ const index = selectedKeys.indexOf(key);
+ if (index !== -1) {
+ selectedKeys.splice(index, 1);
+ }
+ });
+ if (selectedKeys.indexOf(eventKey) === -1) {
+ selectedKeys.push(eventKey);
+ }
+ this.setState({
+ selectedKeys,
+ });
+ this.props.onRightClick({ event: e, node: treeNode });
+ }
+
+ // all keyboard events callbacks run from here at first
+ onKeyDown(e) {
+ e.preventDefault();
+ }
+
+ getFilterExpandedKeys(props, expandKeyProp, expandAll) {
+ const keys = props[expandKeyProp];
+ if (!expandAll && !props.autoExpandParent) {
+ return keys || [];
+ }
+ const expandedPositionArr = [];
+ if (props.autoExpandParent) {
+ loopAllChildren(props.children, (item, index, pos, newKey) => {
+ if (keys.indexOf(newKey) > -1) {
+ expandedPositionArr.push(pos);
+ }
+ });
+ }
+ const filterExpandedKeys = [];
+ loopAllChildren(props.children, (item, index, pos, newKey) => {
+ if (expandAll) {
+ filterExpandedKeys.push(newKey);
+ } else if (props.autoExpandParent) {
+ expandedPositionArr.forEach(p => {
+ if ((p.split('-').length > pos.split('-').length
+ && isInclude(pos.split('-'), p.split('-')) || pos === p)
+ && filterExpandedKeys.indexOf(newKey) === -1) {
+ filterExpandedKeys.push(newKey);
+ }
+ });
+ }
+ });
+ return filterExpandedKeys.length ? filterExpandedKeys : keys;
+ }
+
+ getDefaultExpandedKeys(props, willReceiveProps) {
+ let expandedKeys = willReceiveProps ? undefined :
+ this.getFilterExpandedKeys(props, 'defaultExpandedKeys',
+ props.defaultExpandedKeys.length ? false : props.defaultExpandAll);
+ if ('expandedKeys' in props) {
+ expandedKeys = (props.autoExpandParent ?
+ this.getFilterExpandedKeys(props, 'expandedKeys', false) :
+ props.expandedKeys) || [];
+ }
+ return expandedKeys;
+ }
+
+ getDefaultCheckedKeys(props, willReceiveProps) {
+ let checkedKeys = willReceiveProps ? undefined : props.defaultCheckedKeys;
+ if ('checkedKeys' in props) {
+ checkedKeys = props.checkedKeys || [];
+ if (props.checkStrictly) {
+ if (props.checkedKeys.checked) {
+ checkedKeys = props.checkedKeys.checked;
+ } else if (!Array.isArray(props.checkedKeys)) {
+ checkedKeys = [];
+ }
+ }
+ }
+ return checkedKeys;
+ }
+
+ getDefaultSelectedKeys(props, willReceiveProps) {
+ const getKeys = (keys) => {
+ if (props.multiple) {
+ return [...keys];
+ }
+ if (keys.length) {
+ return [keys[0]];
+ }
+ return keys;
+ };
+ let selectedKeys = willReceiveProps ? undefined : getKeys(props.defaultSelectedKeys);
+ if ('selectedKeys' in props) {
+ selectedKeys = getKeys(props.selectedKeys);
+ }
+ return selectedKeys;
+ }
+
+ getRawExpandedKeys() {
+ if (!this._rawExpandedKeys && ('expandedKeys' in this.props)) {
+ this._rawExpandedKeys = [...this.state.expandedKeys];
+ }
+ }
+
+ getOpenTransitionName() {
+ const props = this.props;
+ let transitionName = props.openTransitionName;
+ const animationName = props.openAnimation;
+ if (!transitionName && typeof animationName === 'string') {
+ transitionName = `${props.prefixCls}-open-${animationName}`;
+ }
+ return transitionName;
+ }
+
+ getDragNodes(treeNode) {
+ const dragNodesKeys = [];
+ const tPArr = treeNode.props.pos.split('-');
+ loopAllChildren(this.props.children, (item, index, pos, newKey) => {
+ const pArr = pos.split('-');
+ if (treeNode.props.pos === pos || tPArr.length < pArr.length && isInclude(tPArr, pArr)) {
+ dragNodesKeys.push(newKey);
+ }
+ });
+ return dragNodesKeys;
+ }
+
+ getExpandedKeys(treeNode, expand) {
+ const key = treeNode.props.eventKey;
+ const expandedKeys = this.state.expandedKeys;
+ const expandedIndex = expandedKeys.indexOf(key);
+ let exKeys;
+ if (expandedIndex > -1 && !expand) {
+ exKeys = [...expandedKeys];
+ exKeys.splice(expandedIndex, 1);
+ return exKeys;
+ }
+ if (expand && expandedKeys.indexOf(key) === -1) {
+ return expandedKeys.concat([key]);
+ }
+ }
+
+ filterTreeNode(treeNode) {
+ const filterTreeNode = this.props.filterTreeNode;
+ if (typeof filterTreeNode !== 'function' || treeNode.props.disabled) {
+ return false;
+ }
+ return filterTreeNode.call(this, treeNode);
+ }
+
+ renderTreeNode(child, index, level = 0) {
+ const pos = `${level}-${index}`;
+ const key = child.key || pos;
+ const state = this.state;
+ const props = this.props;
+
+ // prefer to child's own selectable property if passed
+ let selectable = props.selectable;
+ if (child.props.hasOwnProperty('selectable')) {
+ selectable = child.props.selectable;
+ }
+
+ const cloneProps = {
+ ref: `treeNode-${key}`,
+ root: this,
+ eventKey: key,
+ pos,
+ selectable,
+ loadData: props.loadData,
+ onMouseEnter: props.onMouseEnter,
+ onMouseLeave: props.onMouseLeave,
+ onRightClick: props.onRightClick,
+ prefixCls: props.prefixCls,
+ showLine: props.showLine,
+ showIcon: props.showIcon,
+ draggable: props.draggable,
+ dragOver: state.dragOverNodeKey === key && this.dropPosition === 0,
+ dragOverGapTop: state.dragOverNodeKey === key && this.dropPosition === -1,
+ dragOverGapBottom: state.dragOverNodeKey === key && this.dropPosition === 1,
+ _dropTrigger: this._dropTrigger,
+ expanded: state.expandedKeys.indexOf(key) !== -1,
+ selected: state.selectedKeys.indexOf(key) !== -1,
+ openTransitionName: this.getOpenTransitionName(),
+ openAnimation: props.openAnimation,
+ filterTreeNode: this.filterTreeNode.bind(this),
+ };
+ if (props.checkable) {
+ cloneProps.checkable = props.checkable;
+ if (props.checkStrictly) {
+ if (state.checkedKeys) {
+ cloneProps.checked = state.checkedKeys.indexOf(key) !== -1 || false;
+ }
+ if (props.checkedKeys.halfChecked) {
+ cloneProps.halfChecked = props.checkedKeys.halfChecked.indexOf(key) !== -1 || false;
+ } else {
+ cloneProps.halfChecked = false;
+ }
+ } else {
+ if (this.checkedKeys) {
+ cloneProps.checked = this.checkedKeys.indexOf(key) !== -1 || false;
+ }
+ cloneProps.halfChecked = this.halfCheckedKeys.indexOf(key) !== -1;
+ }
+ }
+ if (this.treeNodesStates && this.treeNodesStates[pos]) {
+ assign(cloneProps, this.treeNodesStates[pos].siblingPosition);
+ }
+ return React.cloneElement(child, cloneProps);
+ }
+
+ render() {
+ const props = this.props;
+ const domProps = {
+ className: classNames(props.className, props.prefixCls),
+ role: 'tree-node',
+ };
+ if (props.focusable) {
+ domProps.tabIndex = '0';
+ domProps.onKeyDown = this.onKeyDown;
+ }
+ const getTreeNodesStates = () => {
+ this.treeNodesStates = {};
+ loopAllChildren(props.children, (item, index, pos, keyOrPos, siblingPosition) => {
+ this.treeNodesStates[pos] = {
+ siblingPosition,
+ };
+ });
+ };
+ if (props.showLine && !props.checkable) {
+ getTreeNodesStates();
+ }
+ if (props.checkable && (this.checkedKeysChange || props.loadData)) {
+ if (props.checkStrictly) {
+ getTreeNodesStates();
+ } else if (props._treeNodesStates) {
+ this.treeNodesStates = props._treeNodesStates.treeNodesStates;
+ this.halfCheckedKeys = props._treeNodesStates.halfCheckedKeys;
+ this.checkedKeys = props._treeNodesStates.checkedKeys;
+ } else {
+ const checkedKeys = this.state.checkedKeys;
+ let checkKeys;
+ if (!props.loadData && this.checkKeys && this._checkedKeys &&
+ arraysEqual(this._checkedKeys, checkedKeys)) {
+ // if checkedKeys the same as _checkedKeys from onCheck, use _checkedKeys.
+ checkKeys = this.checkKeys;
+ } else {
+ const checkedPositions = [];
+ this.treeNodesStates = {};
+ loopAllChildren(props.children, (item, index, pos, keyOrPos, siblingPosition) => {
+ this.treeNodesStates[pos] = {
+ node: item,
+ key: keyOrPos,
+ checked: false,
+ halfChecked: false,
+ siblingPosition,
+ };
+ if (checkedKeys.indexOf(keyOrPos) !== -1) {
+ this.treeNodesStates[pos].checked = true;
+ checkedPositions.push(pos);
+ }
+ });
+ // if the parent node's key exists, it all children node will be checked
+ handleCheckState(this.treeNodesStates, filterParentPosition(checkedPositions), true);
+ checkKeys = getCheck(this.treeNodesStates);
+ }
+ this.halfCheckedKeys = checkKeys.halfCheckedKeys;
+ this.checkedKeys = checkKeys.checkedKeys;
+ }
+ }
+
+ return (
+
+ {React.Children.map(props.children, this.renderTreeNode, this)}
+
+ );
+ }
+}
+
+Tree.propTypes = {
+ prefixCls: PropTypes.string,
+ children: PropTypes.any,
+ showLine: PropTypes.bool,
+ showIcon: PropTypes.bool,
+ selectable: PropTypes.bool,
+ multiple: PropTypes.bool,
+ checkable: PropTypes.oneOfType([
+ PropTypes.bool,
+ PropTypes.node,
+ ]),
+ _treeNodesStates: PropTypes.object,
+ checkStrictly: PropTypes.bool,
+ draggable: PropTypes.bool,
+ autoExpandParent: PropTypes.bool,
+ defaultExpandAll: PropTypes.bool,
+ defaultExpandedKeys: PropTypes.arrayOf(PropTypes.string),
+ expandedKeys: PropTypes.arrayOf(PropTypes.string),
+ defaultCheckedKeys: PropTypes.arrayOf(PropTypes.string),
+ checkedKeys: PropTypes.oneOfType([
+ PropTypes.arrayOf(PropTypes.string),
+ PropTypes.object,
+ ]),
+ defaultSelectedKeys: PropTypes.arrayOf(PropTypes.string),
+ selectedKeys: PropTypes.arrayOf(PropTypes.string),
+ onExpand: PropTypes.func,
+ onCheck: PropTypes.func,
+ onSelect: PropTypes.func,
+ loadData: PropTypes.func,
+ onMouseEnter: PropTypes.func,
+ onMouseLeave: PropTypes.func,
+ onRightClick: PropTypes.func,
+ onDragStart: PropTypes.func,
+ onDragEnter: PropTypes.func,
+ onDragOver: PropTypes.func,
+ onDragLeave: PropTypes.func,
+ onDrop: PropTypes.func,
+ onDragEnd: PropTypes.func,
+ filterTreeNode: PropTypes.func,
+ openTransitionName: PropTypes.string,
+ openAnimation: PropTypes.oneOfType([PropTypes.string, PropTypes.object]),
+};
+
+Tree.defaultProps = {
+ prefixCls: 'rc-tree',
+ showLine: false,
+ showIcon: true,
+ selectable: true,
+ multiple: false,
+ checkable: false,
+ checkStrictly: false,
+ draggable: false,
+ autoExpandParent: true,
+ defaultExpandAll: false,
+ defaultExpandedKeys: [],
+ defaultCheckedKeys: [],
+ defaultSelectedKeys: [],
+ onExpand: noop,
+ onCheck: noop,
+ onSelect: noop,
+ onDragStart: noop,
+ onDragEnter: noop,
+ onDragOver: noop,
+ onDragLeave: noop,
+ onDrop: noop,
+ onDragEnd: noop,
+};
+
+export default Tree;
diff --git a/src copy/src/TreeNode.jsx b/src copy/src/TreeNode.jsx
new file mode 100644
index 0000000..aa140cf
--- /dev/null
+++ b/src copy/src/TreeNode.jsx
@@ -0,0 +1,383 @@
+import React, { PropTypes } from 'react';
+import assign from 'object-assign';
+import classNames from 'classnames';
+import Animate from 'rc-animate';
+import { browser } from './util';
+
+const browserUa = typeof window !== 'undefined' ? browser(window.navigator) : '';
+const ieOrEdge = /.*(IE|Edge).+/.test(browserUa);
+// const uaArray = browserUa.split(' ');
+// const gtIE8 = uaArray.length !== 2 || uaArray[0].indexOf('IE') === -1 || Number(uaArray[1]) > 8;
+
+const defaultTitle = '---';
+
+class TreeNode extends React.Component {
+ constructor(props) {
+ super(props);
+ [
+ 'onExpand',
+ 'onCheck',
+ 'onContextMenu',
+ 'onMouseEnter',
+ 'onMouseLeave',
+ 'onDragStart',
+ 'onDragEnter',
+ 'onDragOver',
+ 'onDragLeave',
+ 'onDrop',
+ 'onDragEnd',
+ ].forEach((m) => {
+ this[m] = this[m].bind(this);
+ });
+ this.state = {
+ dataLoading: false,
+ dragNodeHighlight: false,
+ };
+ }
+
+ componentDidMount() {
+ if (!this.props.root._treeNodeInstances) {
+ this.props.root._treeNodeInstances = [];
+ }
+ this.props.root._treeNodeInstances.push(this);
+ }
+ // shouldComponentUpdate(nextProps) {
+ // if (!nextProps.expanded) {
+ // return false;
+ // }
+ // return true;
+ // }
+
+ onCheck() {
+ this.props.root.onCheck(this);
+ }
+
+ onSelect() {
+ this.props.root.onSelect(this);
+ }
+
+ onMouseEnter(e) {
+ e.preventDefault();
+ this.props.root.onMouseEnter(e, this);
+ }
+
+ onMouseLeave(e) {
+ e.preventDefault();
+ this.props.root.onMouseLeave(e, this);
+ }
+
+ onContextMenu(e) {
+ e.preventDefault();
+ this.props.root.onContextMenu(e, this);
+ }
+
+ onDragStart(e) {
+ // console.log('dragstart', this.props.eventKey, e);
+ // e.preventDefault();
+ e.stopPropagation();
+ this.setState({
+ dragNodeHighlight: true,
+ });
+ this.props.root.onDragStart(e, this);
+ try {
+ // ie throw error
+ // firefox-need-it
+ e.dataTransfer.setData('text/plain', '');
+ } finally {
+ // empty
+ }
+ }
+
+ onDragEnter(e) {
+ e.preventDefault();
+ e.stopPropagation();
+ this.props.root.onDragEnter(e, this);
+ }
+
+ onDragOver(e) {
+ // todo disabled
+ e.preventDefault();
+ e.stopPropagation();
+ this.props.root.onDragOver(e, this);
+ return false;
+ }
+
+ onDragLeave(e) {
+ e.stopPropagation();
+ this.props.root.onDragLeave(e, this);
+ }
+
+ onDrop(e) {
+ e.preventDefault();
+ e.stopPropagation();
+ this.setState({
+ dragNodeHighlight: false,
+ });
+ this.props.root.onDrop(e, this);
+ }
+
+ onDragEnd(e) {
+ e.stopPropagation();
+ this.setState({
+ dragNodeHighlight: false,
+ });
+ this.props.root.onDragEnd(e, this);
+ }
+
+ onExpand() {
+ const callbackPromise = this.props.root.onExpand(this);
+ if (callbackPromise && typeof callbackPromise === 'object') {
+ const setLoading = (dataLoading) => {
+ this.setState({ dataLoading });
+ };
+ setLoading(true);
+ callbackPromise.then(() => {
+ setLoading(false);
+ }, () => {
+ setLoading(false);
+ });
+ }
+ }
+
+ // keyboard event support
+ onKeyDown(e) {
+ e.preventDefault();
+ }
+
+ renderSwitcher(props, expandedState) {
+ const prefixCls = props.prefixCls;
+ const switcherCls = {
+ [`${prefixCls}-switcher`]: true,
+ };
+ if (!props.showLine) {
+ switcherCls[`${prefixCls}-noline_${expandedState}`] = true;
+ } else if (props.pos === '0-0') {
+ switcherCls[`${prefixCls}-roots_${expandedState}`] = true;
+ } else {
+ switcherCls[`${prefixCls}-center_${expandedState}`] = !props.last;
+ switcherCls[`${prefixCls}-bottom_${expandedState}`] = props.last;
+ }
+ if (props.disabled) {
+ switcherCls[`${prefixCls}-switcher-disabled`] = true;
+ return ;
+ }
+ return ;
+ }
+
+ renderCheckbox(props) {
+ const prefixCls = props.prefixCls;
+ const checkboxCls = {
+ [`${prefixCls}-checkbox`]: true,
+ };
+ if (props.checked) {
+ checkboxCls[`${prefixCls}-checkbox-checked`] = true;
+ } else if (props.halfChecked) {
+ checkboxCls[`${prefixCls}-checkbox-indeterminate`] = true;
+ }
+ let customEle = null;
+ if (typeof props.checkable !== 'boolean') {
+ customEle = props.checkable;
+ }
+ if (props.disabled || props.disableCheckbox) {
+ checkboxCls[`${prefixCls}-checkbox-disabled`] = true;
+ return {customEle};
+ }
+ return (
+ {customEle});
+ }
+
+ renderChildren(props) {
+ const renderFirst = this.renderFirst;
+ this.renderFirst = 1;
+ let transitionAppear = true;
+ if (!renderFirst && props.expanded) {
+ transitionAppear = false;
+ }
+ const children = props.children;
+ let newChildren = children;
+ if (children &&
+ (children.type === TreeNode ||
+ Array.isArray(children) &&
+ children.every((item) => {
+ return item.type === TreeNode;
+ }))) {
+ const cls = {
+ [`${props.prefixCls}-child-tree`]: true,
+ [`${props.prefixCls}-child-tree-open`]: props.expanded,
+ };
+ if (props.showLine) {
+ cls[`${props.prefixCls}-line`] = !props.last;
+ }
+ const animProps = {};
+ if (props.openTransitionName) {
+ animProps.transitionName = props.openTransitionName;
+ } else if (typeof props.openAnimation === 'object') {
+ animProps.animation = assign({}, props.openAnimation);
+ if (!transitionAppear) {
+ delete animProps.animation.appear;
+ }
+ }
+ newChildren = (
+
+ {!props.expanded ? null :
+ {React.Children.map(children, (item, index) => {
+ return props.root.renderTreeNode(item, index, props.pos);
+ }, props.root)}
+
}
+
+ );
+ }
+ return newChildren;
+ }
+
+ render() {
+ const props = this.props;
+ const prefixCls = props.prefixCls;
+ const expandedState = props.expanded ? 'open' : 'close';
+ let iconState = expandedState;
+
+ let canRenderSwitcher = true;
+ const content = props.title;
+ let newChildren = this.renderChildren(props);
+ if (!newChildren || newChildren === props.children) {
+ // content = newChildren;
+ newChildren = null;
+ if (!props.loadData || props.isLeaf) {
+ canRenderSwitcher = false;
+ iconState = 'docu';
+ }
+ }
+ // For performance, does't render children into dom when `!props.expanded` (move to Animate)
+ // if (!props.expanded) {
+ // newChildren = null;
+ // }
+
+ const iconEleCls = {
+ [`${prefixCls}-iconEle`]: true,
+ [`${prefixCls}-icon_loading`]: this.state.dataLoading,
+ [`${prefixCls}-icon__${iconState}`]: true,
+ };
+
+ const selectHandle = () => {
+ const icon = (props.showIcon || props.loadData && this.state.dataLoading) ?
+ : null;
+ const title = {content};
+ const wrap = `${prefixCls}-node-content-wrapper`;
+ const domProps = {
+ className: `${wrap} ${wrap}-${iconState === expandedState ? iconState : 'normal'}`,
+ };
+ if (!props.disabled) {
+ if (props.selected || !props._dropTrigger && this.state.dragNodeHighlight) {
+ domProps.className += ` ${prefixCls}-node-selected`;
+ }
+ domProps.onClick = (e) => {
+ e.preventDefault();
+ if (props.selectable) {
+ this.onSelect();
+ }
+ // not fire check event
+ // if (props.checkable) {
+ // this.onCheck();
+ // }
+ };
+ if (props.onRightClick) {
+ domProps.onContextMenu = this.onContextMenu;
+ }
+ if (props.onMouseEnter) {
+ domProps.onMouseEnter = this.onMouseEnter;
+ }
+ if (props.onMouseLeave) {
+ domProps.onMouseLeave = this.onMouseLeave;
+ }
+ if (props.draggable) {
+ domProps.className += ' draggable';
+ if (ieOrEdge) {
+ // ie bug!
+ domProps.href = '#';
+ }
+ domProps.draggable = true;
+ domProps['aria-grabbed'] = true;
+ domProps.onDragStart = this.onDragStart;
+ }
+ }
+ return (
+
+ {icon}{title}
+
+ );
+ };
+
+ const liProps = {};
+ if (props.draggable) {
+ liProps.onDragEnter = this.onDragEnter;
+ liProps.onDragOver = this.onDragOver;
+ liProps.onDragLeave = this.onDragLeave;
+ liProps.onDrop = this.onDrop;
+ liProps.onDragEnd = this.onDragEnd;
+ }
+
+ let disabledCls = '';
+ let dragOverCls = '';
+ if (props.disabled) {
+ disabledCls = `${prefixCls}-treenode-disabled`;
+ } else if (props.dragOver) {
+ dragOverCls = 'drag-over';
+ } else if (props.dragOverGapTop) {
+ dragOverCls = 'drag-over-gap-top';
+ } else if (props.dragOverGapBottom) {
+ dragOverCls = 'drag-over-gap-bottom';
+ }
+
+ const filterCls = props.filterTreeNode(this) ? 'filter-node' : '';
+
+ const noopSwitcher = () => {
+ const cls = {
+ [`${prefixCls}-switcher`]: true,
+ [`${prefixCls}-switcher-noop`]: true,
+ };
+ if (props.showLine) {
+ cls[`${prefixCls}-center_docu`] = !props.last;
+ cls[`${prefixCls}-bottom_docu`] = props.last;
+ } else {
+ cls[`${prefixCls}-noline_docu`] = true;
+ }
+ return ;
+ };
+
+ return (
+
+ {canRenderSwitcher ? this.renderSwitcher(props, expandedState) : noopSwitcher()}
+ {props.checkable ? this.renderCheckbox(props) : null}
+ {selectHandle()}
+ {newChildren}
+
+ );
+ }
+}
+
+TreeNode.isTreeNode = 1;
+
+TreeNode.propTypes = {
+ prefixCls: PropTypes.string,
+ disabled: PropTypes.bool,
+ disableCheckbox: PropTypes.bool,
+ expanded: PropTypes.bool,
+ isLeaf: PropTypes.bool,
+ root: PropTypes.object,
+ onSelect: PropTypes.func,
+};
+
+TreeNode.defaultProps = {
+ title: defaultTitle,
+};
+
+export default TreeNode;
diff --git a/src copy/src/index.js b/src copy/src/index.js
new file mode 100644
index 0000000..a37c0d2
--- /dev/null
+++ b/src copy/src/index.js
@@ -0,0 +1,5 @@
+import Tree from './Tree';
+import TreeNode from './TreeNode';
+Tree.TreeNode = TreeNode;
+
+export default Tree;
diff --git a/src copy/src/util.js b/src copy/src/util.js
new file mode 100644
index 0000000..3d0d84a
--- /dev/null
+++ b/src copy/src/util.js
@@ -0,0 +1,286 @@
+/* eslint no-loop-func: 0*/
+
+import React from 'react';
+
+export function browser(navigator) {
+ let tem;
+ const ua = navigator.userAgent;
+ let M = ua.match(/(opera|chrome|safari|firefox|msie|trident(?=\/))\/?\s*(\d+)/i) || [];
+ if (/trident/i.test(M[1])) {
+ tem = /\brv[ :]+(\d+)/g.exec(ua) || [];
+ return `IE ${tem[1] || ''}`;
+ }
+ if (M[1] === 'Chrome') {
+ tem = ua.match(/\b(OPR|Edge)\/(\d+)/);
+ if (tem) return tem.slice(1).join(' ').replace('OPR', 'Opera');
+ }
+ M = M[2] ? [M[1], M[2]] : [navigator.appName, navigator.appVersion, '-?'];
+ tem = ua.match(/version\/(\d+)/i);
+ if (tem) {
+ M.splice(1, 1, tem[1]);
+ }
+ return M.join(' ');
+}
+
+// export function getOffset(el) {
+// const obj = el.getBoundingClientRect();
+// return {
+// left: obj.left + document.body.scrollLeft,
+// top: obj.top + document.body.scrollTop,
+// width: obj.width,
+// height: obj.height
+// };
+// }
+
+// // iscroll offset
+// offset = function (el) {
+// var left = -el.offsetLeft,
+// top = -el.offsetTop;
+
+// // jshint -W084
+// while (el = el.offsetParent) {
+// left -= el.offsetLeft;
+// top -= el.offsetTop;
+// }
+// // jshint +W084
+
+// return {
+// left: left,
+// top: top
+// };
+// }
+
+/* eslint-disable */
+export function getOffset(ele) {
+ let doc, win, docElem, rect;
+
+ if (!ele.getClientRects().length) {
+ return { top: 0, left: 0 };
+ }
+
+ rect = ele.getBoundingClientRect();
+
+ if (rect.width || rect.height) {
+ doc = ele.ownerDocument;
+ win = doc.defaultView;
+ docElem = doc.documentElement;
+
+ return {
+ top: rect.top + win.pageYOffset - docElem.clientTop,
+ left: rect.left + win.pageXOffset - docElem.clientLeft
+ };
+ }
+
+ return rect;
+}
+/* eslint-enable */
+
+function getChildrenlength(children) {
+ let len = 1;
+ if (Array.isArray(children)) {
+ len = children.length;
+ }
+ return len;
+}
+
+function getSiblingPosition(index, len, siblingPosition) {
+ if (len === 1) {
+ siblingPosition.first = true;
+ siblingPosition.last = true;
+ } else {
+ siblingPosition.first = index === 0;
+ siblingPosition.last = index === len - 1;
+ }
+ return siblingPosition;
+}
+
+export function loopAllChildren(childs, callback, parent) {
+ const loop = (children, level, _parent) => {
+ const len = getChildrenlength(children);
+ React.Children.forEach(children, (item, index) => {
+ const pos = `${level}-${index}`;
+ if (item.props.children && item.type && item.type.isTreeNode) {
+ loop(item.props.children, pos, { node: item, pos });
+ }
+ callback(item, index, pos, item.key || pos, getSiblingPosition(index, len, {}), _parent);
+ });
+ };
+ loop(childs, 0, parent);
+}
+
+export function isInclude(smallArray, bigArray) {
+ return smallArray.every((ii, i) => {
+ return ii === bigArray[i];
+ });
+}
+// console.log(isInclude(['0', '1'], ['0', '10', '1']));
+
+
+// arr.length === 628, use time: ~20ms
+export function filterParentPosition(arr) {
+ const levelObj = {};
+ arr.forEach((item) => {
+ const posLen = item.split('-').length;
+ if (!levelObj[posLen]) {
+ levelObj[posLen] = [];
+ }
+ levelObj[posLen].push(item);
+ });
+ const levelArr = Object.keys(levelObj).sort();
+ for (let i = 0; i < levelArr.length; i++) {
+ if (levelArr[i + 1]) {
+ levelObj[levelArr[i]].forEach(ii => {
+ for (let j = i + 1; j < levelArr.length; j++) {
+ levelObj[levelArr[j]].forEach((_i, index) => {
+ if (isInclude(ii.split('-'), _i.split('-'))) {
+ levelObj[levelArr[j]][index] = null;
+ }
+ });
+ levelObj[levelArr[j]] = levelObj[levelArr[j]].filter(p => p);
+ }
+ });
+ }
+ }
+ let nArr = [];
+ levelArr.forEach(i => {
+ nArr = nArr.concat(levelObj[i]);
+ });
+ return nArr;
+}
+// console.log(filterParentPosition(
+// ['0-2', '0-3-3', '0-10', '0-10-0', '0-0-1', '0-0', '0-1-1', '0-1']
+// ));
+
+
+function stripTail(str) {
+ const arr = str.match(/(.+)(-[^-]+)$/);
+ let st = '';
+ if (arr && arr.length === 3) {
+ st = arr[1];
+ }
+ return st;
+}
+function splitPosition(pos) {
+ return pos.split('-');
+}
+
+export function handleCheckState(obj, checkedPositionArr, checkIt) {
+ // console.log(stripTail('0-101-000'));
+ let objKeys = Object.keys(obj);
+ // let s = Date.now();
+ objKeys.forEach((i, index) => {
+ const iArr = splitPosition(i);
+ let saved = false;
+ checkedPositionArr.forEach((_pos) => {
+ // 设置子节点,全选或全不选
+ const _posArr = splitPosition(_pos);
+ if (iArr.length > _posArr.length && isInclude(_posArr, iArr)) {
+ obj[i].halfChecked = false;
+ obj[i].checked = checkIt;
+ objKeys[index] = null;
+ }
+ if (iArr[0] === _posArr[0] && iArr[1] === _posArr[1]) {
+ // 如果
+ saved = true;
+ }
+ });
+ if (!saved) {
+ objKeys[index] = null;
+ }
+ });
+ // TODO: 循环 2470000 次耗时约 1400 ms。 性能瓶颈!
+ // console.log(Date.now()-s, checkedPositionArr.length * objKeys.length);
+ objKeys = objKeys.filter(i => i); // filter non null;
+
+ for (let pIndex = 0; pIndex < checkedPositionArr.length; pIndex++) {
+ // 循环设置父节点的 选中 或 半选状态
+ const loop = (__pos) => {
+ const _posLen = splitPosition(__pos).length;
+ if (_posLen <= 2) { // e.g. '0-0', '0-1'
+ return;
+ }
+ let sibling = 0;
+ let siblingChecked = 0;
+ const parentPosition = stripTail(__pos);
+ objKeys.forEach((i /* , index*/) => {
+ const iArr = splitPosition(i);
+ if (iArr.length === _posLen && isInclude(splitPosition(parentPosition), iArr)) {
+ sibling++;
+ if (obj[i].checked) {
+ siblingChecked++;
+ const _i = checkedPositionArr.indexOf(i);
+ if (_i > -1) {
+ checkedPositionArr.splice(_i, 1);
+ if (_i <= pIndex) {
+ pIndex--;
+ }
+ }
+ } else if (obj[i].halfChecked) {
+ siblingChecked += 0.5;
+ }
+ // objKeys[index] = null;
+ }
+ });
+ // objKeys = objKeys.filter(i => i); // filter non null;
+ const parent = obj[parentPosition];
+ // sibling 不会等于0
+ // 全不选 - 全选 - 半选
+ if (siblingChecked === 0) {
+ parent.checked = false;
+ parent.halfChecked = false;
+ } else if (siblingChecked === sibling) {
+ parent.checked = true;
+ parent.halfChecked = false;
+ } else {
+ parent.halfChecked = true;
+ parent.checked = false;
+ }
+ loop(parentPosition);
+ };
+ loop(checkedPositionArr[pIndex], pIndex);
+ }
+ // console.log(Date.now()-s, objKeys.length, checkIt);
+}
+
+export function getCheck(treeNodesStates) {
+ const halfCheckedKeys = [];
+ const checkedKeys = [];
+ const checkedNodes = [];
+ const checkedNodesPositions = [];
+ Object.keys(treeNodesStates).forEach((item) => {
+ const itemObj = treeNodesStates[item];
+ if (itemObj.checked) {
+ checkedKeys.push(itemObj.key);
+ checkedNodes.push(itemObj.node);
+ checkedNodesPositions.push({ node: itemObj.node, pos: item });
+ } else if (itemObj.halfChecked) {
+ halfCheckedKeys.push(itemObj.key);
+ }
+ });
+ return {
+ halfCheckedKeys, checkedKeys, checkedNodes, checkedNodesPositions, treeNodesStates,
+ };
+}
+
+export function getStrictlyValue(checkedKeys, halfChecked) {
+ if (halfChecked) {
+ return { checked: checkedKeys, halfChecked };
+ }
+ return checkedKeys;
+}
+
+export function arraysEqual(a, b) {
+ if (a === b) return true;
+ if (a === null || typeof a === 'undefined' || b === null || typeof b === 'undefined') {
+ return false;
+ }
+ if (a.length !== b.length) return false;
+
+ // If you don't care about the order of the elements inside
+ // the array, you should sort both arrays here.
+
+ for (let i = 0; i < a.length; ++i) {
+ if (a[i] !== b[i]) return false;
+ }
+ return true;
+}
diff --git a/src copy/util.js b/src copy/util.js
new file mode 100644
index 0000000..3d0d84a
--- /dev/null
+++ b/src copy/util.js
@@ -0,0 +1,286 @@
+/* eslint no-loop-func: 0*/
+
+import React from 'react';
+
+export function browser(navigator) {
+ let tem;
+ const ua = navigator.userAgent;
+ let M = ua.match(/(opera|chrome|safari|firefox|msie|trident(?=\/))\/?\s*(\d+)/i) || [];
+ if (/trident/i.test(M[1])) {
+ tem = /\brv[ :]+(\d+)/g.exec(ua) || [];
+ return `IE ${tem[1] || ''}`;
+ }
+ if (M[1] === 'Chrome') {
+ tem = ua.match(/\b(OPR|Edge)\/(\d+)/);
+ if (tem) return tem.slice(1).join(' ').replace('OPR', 'Opera');
+ }
+ M = M[2] ? [M[1], M[2]] : [navigator.appName, navigator.appVersion, '-?'];
+ tem = ua.match(/version\/(\d+)/i);
+ if (tem) {
+ M.splice(1, 1, tem[1]);
+ }
+ return M.join(' ');
+}
+
+// export function getOffset(el) {
+// const obj = el.getBoundingClientRect();
+// return {
+// left: obj.left + document.body.scrollLeft,
+// top: obj.top + document.body.scrollTop,
+// width: obj.width,
+// height: obj.height
+// };
+// }
+
+// // iscroll offset
+// offset = function (el) {
+// var left = -el.offsetLeft,
+// top = -el.offsetTop;
+
+// // jshint -W084
+// while (el = el.offsetParent) {
+// left -= el.offsetLeft;
+// top -= el.offsetTop;
+// }
+// // jshint +W084
+
+// return {
+// left: left,
+// top: top
+// };
+// }
+
+/* eslint-disable */
+export function getOffset(ele) {
+ let doc, win, docElem, rect;
+
+ if (!ele.getClientRects().length) {
+ return { top: 0, left: 0 };
+ }
+
+ rect = ele.getBoundingClientRect();
+
+ if (rect.width || rect.height) {
+ doc = ele.ownerDocument;
+ win = doc.defaultView;
+ docElem = doc.documentElement;
+
+ return {
+ top: rect.top + win.pageYOffset - docElem.clientTop,
+ left: rect.left + win.pageXOffset - docElem.clientLeft
+ };
+ }
+
+ return rect;
+}
+/* eslint-enable */
+
+function getChildrenlength(children) {
+ let len = 1;
+ if (Array.isArray(children)) {
+ len = children.length;
+ }
+ return len;
+}
+
+function getSiblingPosition(index, len, siblingPosition) {
+ if (len === 1) {
+ siblingPosition.first = true;
+ siblingPosition.last = true;
+ } else {
+ siblingPosition.first = index === 0;
+ siblingPosition.last = index === len - 1;
+ }
+ return siblingPosition;
+}
+
+export function loopAllChildren(childs, callback, parent) {
+ const loop = (children, level, _parent) => {
+ const len = getChildrenlength(children);
+ React.Children.forEach(children, (item, index) => {
+ const pos = `${level}-${index}`;
+ if (item.props.children && item.type && item.type.isTreeNode) {
+ loop(item.props.children, pos, { node: item, pos });
+ }
+ callback(item, index, pos, item.key || pos, getSiblingPosition(index, len, {}), _parent);
+ });
+ };
+ loop(childs, 0, parent);
+}
+
+export function isInclude(smallArray, bigArray) {
+ return smallArray.every((ii, i) => {
+ return ii === bigArray[i];
+ });
+}
+// console.log(isInclude(['0', '1'], ['0', '10', '1']));
+
+
+// arr.length === 628, use time: ~20ms
+export function filterParentPosition(arr) {
+ const levelObj = {};
+ arr.forEach((item) => {
+ const posLen = item.split('-').length;
+ if (!levelObj[posLen]) {
+ levelObj[posLen] = [];
+ }
+ levelObj[posLen].push(item);
+ });
+ const levelArr = Object.keys(levelObj).sort();
+ for (let i = 0; i < levelArr.length; i++) {
+ if (levelArr[i + 1]) {
+ levelObj[levelArr[i]].forEach(ii => {
+ for (let j = i + 1; j < levelArr.length; j++) {
+ levelObj[levelArr[j]].forEach((_i, index) => {
+ if (isInclude(ii.split('-'), _i.split('-'))) {
+ levelObj[levelArr[j]][index] = null;
+ }
+ });
+ levelObj[levelArr[j]] = levelObj[levelArr[j]].filter(p => p);
+ }
+ });
+ }
+ }
+ let nArr = [];
+ levelArr.forEach(i => {
+ nArr = nArr.concat(levelObj[i]);
+ });
+ return nArr;
+}
+// console.log(filterParentPosition(
+// ['0-2', '0-3-3', '0-10', '0-10-0', '0-0-1', '0-0', '0-1-1', '0-1']
+// ));
+
+
+function stripTail(str) {
+ const arr = str.match(/(.+)(-[^-]+)$/);
+ let st = '';
+ if (arr && arr.length === 3) {
+ st = arr[1];
+ }
+ return st;
+}
+function splitPosition(pos) {
+ return pos.split('-');
+}
+
+export function handleCheckState(obj, checkedPositionArr, checkIt) {
+ // console.log(stripTail('0-101-000'));
+ let objKeys = Object.keys(obj);
+ // let s = Date.now();
+ objKeys.forEach((i, index) => {
+ const iArr = splitPosition(i);
+ let saved = false;
+ checkedPositionArr.forEach((_pos) => {
+ // 设置子节点,全选或全不选
+ const _posArr = splitPosition(_pos);
+ if (iArr.length > _posArr.length && isInclude(_posArr, iArr)) {
+ obj[i].halfChecked = false;
+ obj[i].checked = checkIt;
+ objKeys[index] = null;
+ }
+ if (iArr[0] === _posArr[0] && iArr[1] === _posArr[1]) {
+ // 如果
+ saved = true;
+ }
+ });
+ if (!saved) {
+ objKeys[index] = null;
+ }
+ });
+ // TODO: 循环 2470000 次耗时约 1400 ms。 性能瓶颈!
+ // console.log(Date.now()-s, checkedPositionArr.length * objKeys.length);
+ objKeys = objKeys.filter(i => i); // filter non null;
+
+ for (let pIndex = 0; pIndex < checkedPositionArr.length; pIndex++) {
+ // 循环设置父节点的 选中 或 半选状态
+ const loop = (__pos) => {
+ const _posLen = splitPosition(__pos).length;
+ if (_posLen <= 2) { // e.g. '0-0', '0-1'
+ return;
+ }
+ let sibling = 0;
+ let siblingChecked = 0;
+ const parentPosition = stripTail(__pos);
+ objKeys.forEach((i /* , index*/) => {
+ const iArr = splitPosition(i);
+ if (iArr.length === _posLen && isInclude(splitPosition(parentPosition), iArr)) {
+ sibling++;
+ if (obj[i].checked) {
+ siblingChecked++;
+ const _i = checkedPositionArr.indexOf(i);
+ if (_i > -1) {
+ checkedPositionArr.splice(_i, 1);
+ if (_i <= pIndex) {
+ pIndex--;
+ }
+ }
+ } else if (obj[i].halfChecked) {
+ siblingChecked += 0.5;
+ }
+ // objKeys[index] = null;
+ }
+ });
+ // objKeys = objKeys.filter(i => i); // filter non null;
+ const parent = obj[parentPosition];
+ // sibling 不会等于0
+ // 全不选 - 全选 - 半选
+ if (siblingChecked === 0) {
+ parent.checked = false;
+ parent.halfChecked = false;
+ } else if (siblingChecked === sibling) {
+ parent.checked = true;
+ parent.halfChecked = false;
+ } else {
+ parent.halfChecked = true;
+ parent.checked = false;
+ }
+ loop(parentPosition);
+ };
+ loop(checkedPositionArr[pIndex], pIndex);
+ }
+ // console.log(Date.now()-s, objKeys.length, checkIt);
+}
+
+export function getCheck(treeNodesStates) {
+ const halfCheckedKeys = [];
+ const checkedKeys = [];
+ const checkedNodes = [];
+ const checkedNodesPositions = [];
+ Object.keys(treeNodesStates).forEach((item) => {
+ const itemObj = treeNodesStates[item];
+ if (itemObj.checked) {
+ checkedKeys.push(itemObj.key);
+ checkedNodes.push(itemObj.node);
+ checkedNodesPositions.push({ node: itemObj.node, pos: item });
+ } else if (itemObj.halfChecked) {
+ halfCheckedKeys.push(itemObj.key);
+ }
+ });
+ return {
+ halfCheckedKeys, checkedKeys, checkedNodes, checkedNodesPositions, treeNodesStates,
+ };
+}
+
+export function getStrictlyValue(checkedKeys, halfChecked) {
+ if (halfChecked) {
+ return { checked: checkedKeys, halfChecked };
+ }
+ return checkedKeys;
+}
+
+export function arraysEqual(a, b) {
+ if (a === b) return true;
+ if (a === null || typeof a === 'undefined' || b === null || typeof b === 'undefined') {
+ return false;
+ }
+ if (a.length !== b.length) return false;
+
+ // If you don't care about the order of the elements inside
+ // the array, you should sort both arrays here.
+
+ for (let i = 0; i < a.length; ++i) {
+ if (a[i] !== b[i]) return false;
+ }
+ return true;
+}
diff --git a/src/Tree.js b/src/Tree.js
new file mode 100644
index 0000000..6b454cd
--- /dev/null
+++ b/src/Tree.js
@@ -0,0 +1,641 @@
+/* eslint no-console:0 */
+import React, { PropTypes } from 'react';
+import assign from 'object-assign';
+import classNames from 'classnames';
+import {
+ loopAllChildren, isInclude, getOffset,
+ filterParentPosition, handleCheckState, getCheck,
+ getStrictlyValue, arraysEqual,
+} from './util';
+
+function noop() {
+}
+
+class Tree extends React.Component {
+ constructor(props) {
+ super(props);
+ ['onKeyDown', 'onCheck'].forEach((m) => {
+ this[m] = this[m].bind(this);
+ });
+ this.contextmenuKeys = [];
+ this.checkedKeysChange = true;
+
+ this.state = {
+ expandedKeys: this.getDefaultExpandedKeys(props),
+ checkedKeys: this.getDefaultCheckedKeys(props),
+ selectedKeys: this.getDefaultSelectedKeys(props),
+ dragNodesKeys: '',
+ dragOverNodeKey: '',
+ dropNodeKey: '',
+ };
+ }
+
+ componentWillReceiveProps(nextProps) {
+ const expandedKeys = this.getDefaultExpandedKeys(nextProps, true);
+ const checkedKeys = this.getDefaultCheckedKeys(nextProps, true);
+ const selectedKeys = this.getDefaultSelectedKeys(nextProps, true);
+ const st = {};
+ if (expandedKeys) {
+ st.expandedKeys = expandedKeys;
+ }
+ if (checkedKeys) {
+ if (nextProps.checkedKeys === this.props.checkedKeys) {
+ this.checkedKeysChange = false;
+ } else {
+ this.checkedKeysChange = true;
+ }
+ st.checkedKeys = checkedKeys;
+ }
+ if (selectedKeys) {
+ st.selectedKeys = selectedKeys;
+ }
+ this.setState(st);
+ }
+
+ onDragStart(e, treeNode) {
+ this.dragNode = treeNode;
+ this.dragNodesKeys = this.getDragNodes(treeNode);
+ const st = {
+ dragNodesKeys: this.dragNodesKeys,
+ };
+ const expandedKeys = this.getExpandedKeys(treeNode, false);
+ if (expandedKeys) {
+ // Controlled expand, save and then reset
+ this.getRawExpandedKeys();
+ st.expandedKeys = expandedKeys;
+ }
+ this.setState(st);
+ this.props.onDragStart({
+ event: e,
+ node: treeNode,
+ });
+ this._dropTrigger = false;
+ }
+
+ onDragEnterGap(e, treeNode) {
+ const offsetTop = (0, getOffset)(treeNode.refs.selectHandle).top;
+ const offsetHeight = treeNode.refs.selectHandle.offsetHeight;
+ const pageY = e.pageY;
+ const gapHeight = 2;
+ if (pageY > offsetTop + offsetHeight - gapHeight) {
+ this.dropPosition = 1;
+ return 1;
+ }
+ if (pageY < offsetTop + gapHeight) {
+ this.dropPosition = -1;
+ return -1;
+ }
+ this.dropPosition = 0;
+ return 0;
+ }
+
+ onDragEnter(e, treeNode) {
+ const enterGap = this.onDragEnterGap(e, treeNode);
+ if (this.dragNode.props.eventKey === treeNode.props.eventKey && enterGap === 0) {
+ this.setState({
+ dragOverNodeKey: '',
+ });
+ return;
+ }
+ const st = {
+ dragOverNodeKey: treeNode.props.eventKey,
+ };
+ const expandedKeys = this.getExpandedKeys(treeNode, true);
+ if (expandedKeys) {
+ this.getRawExpandedKeys();
+ st.expandedKeys = expandedKeys;
+ }
+ this.setState(st);
+ this.props.onDragEnter({
+ event: e,
+ node: treeNode,
+ expandedKeys: expandedKeys && [...expandedKeys] || [...this.state.expandedKeys],
+ });
+ }
+
+ onDragOver(e, treeNode) {
+ this.props.onDragOver({ event: e, node: treeNode });
+ }
+
+ onDragLeave(e, treeNode) {
+ this.props.onDragLeave({ event: e, node: treeNode });
+ }
+
+ onDrop(e, treeNode) {
+ const key = treeNode.props.eventKey;
+ this.setState({
+ dragOverNodeKey: '',
+ dropNodeKey: key,
+ });
+ if (this.dragNodesKeys.indexOf(key) > -1) {
+ if (console.warn) {
+ console.warn('can not drop to dragNode(include it\'s children node)');
+ }
+ return false;
+ }
+
+ const posArr = treeNode.props.pos.split('-');
+ const res = {
+ event: e,
+ node: treeNode,
+ dragNode: this.dragNode,
+ dragNodesKeys: [...this.dragNodesKeys],
+ dropPosition: this.dropPosition + Number(posArr[posArr.length - 1]),
+ };
+ if (this.dropPosition !== 0) {
+ res.dropToGap = true;
+ }
+ if ('expandedKeys' in this.props) {
+ res.rawExpandedKeys = [...this._rawExpandedKeys] || [...this.state.expandedKeys];
+ }
+ this.props.onDrop(res);
+ this._dropTrigger = true;
+ }
+
+ onDragEnd(e, treeNode) {
+ this.setState({
+ dragOverNodeKey: '',
+ });
+ this.props.onDragEnd({ event: e, node: treeNode });
+ }
+
+ onExpand(treeNode) {
+ const expanded = !treeNode.props.expanded;
+ const controlled = 'expandedKeys' in this.props;
+ const expandedKeys = [...this.state.expandedKeys];
+ const index = expandedKeys.indexOf(treeNode.props.eventKey);
+ if (expanded && index === -1) {
+ expandedKeys.push(treeNode.props.eventKey);
+ } else if (!expanded && index > -1) {
+ expandedKeys.splice(index, 1);
+ }
+ if (!controlled) {
+ this.setState({ expandedKeys });
+ }
+ this.props.onExpand(expandedKeys, { node: treeNode, expanded });
+
+ // after data loaded, need set new expandedKeys
+ if (expanded && this.props.loadData) {
+ return this.props.loadData(treeNode).then(() => {
+ if (!controlled) {
+ this.setState({ expandedKeys });
+ }
+ });
+ }
+ }
+
+ onCheck(treeNode) {
+ let checked = !treeNode.props.checked;
+ if (treeNode.props.halfChecked) {
+ checked = true;
+ }
+ const key = treeNode.props.eventKey;
+ let checkedKeys = [...this.state.checkedKeys];
+ const index = checkedKeys.indexOf(key);
+
+ const newSt = {
+ event: 'check',
+ node: treeNode,
+ checked,
+ };
+
+ if (this.props.checkStrictly && ('checkedKeys' in this.props)) {
+ if (checked && index === -1) {
+ checkedKeys.push(key);
+ }
+ if (!checked && index > -1) {
+ checkedKeys.splice(index, 1);
+ }
+ newSt.checkedNodes = [];
+ loopAllChildren(this.props.children, (item, ind, pos, keyOrPos) => {
+ if (checkedKeys.indexOf(keyOrPos) !== -1) {
+ newSt.checkedNodes.push(item);
+ }
+ });
+ this.props.onCheck(getStrictlyValue(checkedKeys, this.props.checkedKeys.halfChecked), newSt);
+ } else {
+ if (checked && index === -1) {
+ this.treeNodesStates[treeNode.props.pos].checked = true;
+ const checkedPositions = [];
+ Object.keys(this.treeNodesStates).forEach(i => {
+ if (this.treeNodesStates[i].checked) {
+ checkedPositions.push(i);
+ }
+ });
+ handleCheckState(this.treeNodesStates, filterParentPosition(checkedPositions), true);
+ }
+ if (!checked) {
+ this.treeNodesStates[treeNode.props.pos].checked = false;
+ this.treeNodesStates[treeNode.props.pos].halfChecked = false;
+ handleCheckState(this.treeNodesStates, [treeNode.props.pos], false);
+ }
+ const checkKeys = getCheck(this.treeNodesStates);
+ newSt.checkedNodes = checkKeys.checkedNodes;
+ newSt.checkedNodesPositions = checkKeys.checkedNodesPositions;
+ newSt.halfCheckedKeys = checkKeys.halfCheckedKeys;
+ this.checkKeys = checkKeys;
+
+ this._checkedKeys = checkedKeys = checkKeys.checkedKeys;
+ if (!('checkedKeys' in this.props)) {
+ this.setState({
+ checkedKeys,
+ });
+ }
+ this.props.onCheck(checkedKeys, newSt);
+ }
+ }
+
+ onSelect(treeNode) {
+ const props = this.props;
+ const selectedKeys = [...this.state.selectedKeys];
+ const eventKey = treeNode.props.eventKey;
+ const index = selectedKeys.indexOf(eventKey);
+ let selected;
+ if (index !== -1) {
+ selected = false;
+ selectedKeys.splice(index, 1);
+ } else {
+ selected = true;
+ if (!props.multiple) {
+ selectedKeys.length = 0;
+ }
+ selectedKeys.push(eventKey);
+ }
+ const selectedNodes = [];
+ if (selectedKeys.length) {
+ loopAllChildren(this.props.children, (item) => {
+ if (selectedKeys.indexOf(item.key) !== -1) {
+ selectedNodes.push(item);
+ }
+ });
+ }
+ const newSt = {
+ event: 'select',
+ node: treeNode,
+ selected,
+ selectedNodes,
+ };
+ if (!('selectedKeys' in this.props)) {
+ this.setState({
+ selectedKeys,
+ });
+ }
+ props.onSelect(selectedKeys, newSt);
+ }
+
+ onMouseEnter(e, treeNode) {
+ this.props.onMouseEnter({ event: e, node: treeNode });
+ }
+
+ onMouseLeave(e, treeNode) {
+ this.props.onMouseLeave({ event: e, node: treeNode });
+ }
+
+ onContextMenu(e, treeNode) {
+ const selectedKeys = [...this.state.selectedKeys];
+ const eventKey = treeNode.props.eventKey;
+ if (this.contextmenuKeys.indexOf(eventKey) === -1) {
+ this.contextmenuKeys.push(eventKey);
+ }
+ this.contextmenuKeys.forEach((key) => {
+ const index = selectedKeys.indexOf(key);
+ if (index !== -1) {
+ selectedKeys.splice(index, 1);
+ }
+ });
+ if (selectedKeys.indexOf(eventKey) === -1) {
+ selectedKeys.push(eventKey);
+ }
+ this.setState({
+ selectedKeys,
+ });
+ this.props.onRightClick({ event: e, node: treeNode });
+ }
+
+ // all keyboard events callbacks run from here at first
+ onKeyDown(e) {
+ e.preventDefault();
+ }
+
+ getFilterExpandedKeys(props, expandKeyProp, expandAll) {
+ const keys = props[expandKeyProp];
+ if (!expandAll && !props.autoExpandParent) {
+ return keys || [];
+ }
+ const expandedPositionArr = [];
+ if (props.autoExpandParent) {
+ loopAllChildren(props.children, (item, index, pos, newKey) => {
+ if (keys.indexOf(newKey) > -1) {
+ expandedPositionArr.push(pos);
+ }
+ });
+ }
+ const filterExpandedKeys = [];
+ loopAllChildren(props.children, (item, index, pos, newKey) => {
+ if (expandAll) {
+ filterExpandedKeys.push(newKey);
+ } else if (props.autoExpandParent) {
+ expandedPositionArr.forEach(p => {
+ if ((p.split('-').length > pos.split('-').length
+ && isInclude(pos.split('-'), p.split('-')) || pos === p)
+ && filterExpandedKeys.indexOf(newKey) === -1) {
+ filterExpandedKeys.push(newKey);
+ }
+ });
+ }
+ });
+ return filterExpandedKeys.length ? filterExpandedKeys : keys;
+ }
+
+ getDefaultExpandedKeys(props, willReceiveProps) {
+ let expandedKeys = willReceiveProps ? undefined :
+ this.getFilterExpandedKeys(props, 'defaultExpandedKeys',
+ props.defaultExpandedKeys.length ? false : props.defaultExpandAll);
+ if ('expandedKeys' in props) {
+ expandedKeys = (props.autoExpandParent ?
+ this.getFilterExpandedKeys(props, 'expandedKeys', false) :
+ props.expandedKeys) || [];
+ }
+ return expandedKeys;
+ }
+
+ getDefaultCheckedKeys(props, willReceiveProps) {
+ let checkedKeys = willReceiveProps ? undefined : props.defaultCheckedKeys;
+ if ('checkedKeys' in props) {
+ checkedKeys = props.checkedKeys || [];
+ if (props.checkStrictly) {
+ if (props.checkedKeys.checked) {
+ checkedKeys = props.checkedKeys.checked;
+ } else if (!Array.isArray(props.checkedKeys)) {
+ checkedKeys = [];
+ }
+ }
+ }
+ return checkedKeys;
+ }
+
+ getDefaultSelectedKeys(props, willReceiveProps) {
+ const getKeys = (keys) => {
+ if (props.multiple) {
+ return [...keys];
+ }
+ if (keys.length) {
+ return [keys[0]];
+ }
+ return keys;
+ };
+ let selectedKeys = willReceiveProps ? undefined : getKeys(props.defaultSelectedKeys);
+ if ('selectedKeys' in props) {
+ selectedKeys = getKeys(props.selectedKeys);
+ }
+ return selectedKeys;
+ }
+
+ getRawExpandedKeys() {
+ if (!this._rawExpandedKeys && ('expandedKeys' in this.props)) {
+ this._rawExpandedKeys = [...this.state.expandedKeys];
+ }
+ }
+
+ getOpenTransitionName() {
+ const props = this.props;
+ let transitionName = props.openTransitionName;
+ const animationName = props.openAnimation;
+ if (!transitionName && typeof animationName === 'string') {
+ transitionName = `${props.prefixCls}-open-${animationName}`;
+ }
+ return transitionName;
+ }
+
+ getDragNodes(treeNode) {
+ const dragNodesKeys = [];
+ const tPArr = treeNode.props.pos.split('-');
+ loopAllChildren(this.props.children, (item, index, pos, newKey) => {
+ const pArr = pos.split('-');
+ if (treeNode.props.pos === pos || tPArr.length < pArr.length && isInclude(tPArr, pArr)) {
+ dragNodesKeys.push(newKey);
+ }
+ });
+ return dragNodesKeys;
+ }
+
+ getExpandedKeys(treeNode, expand) {
+ const key = treeNode.props.eventKey;
+ const expandedKeys = this.state.expandedKeys;
+ const expandedIndex = expandedKeys.indexOf(key);
+ let exKeys;
+ if (expandedIndex > -1 && !expand) {
+ exKeys = [...expandedKeys];
+ exKeys.splice(expandedIndex, 1);
+ return exKeys;
+ }
+ if (expand && expandedKeys.indexOf(key) === -1) {
+ return expandedKeys.concat([key]);
+ }
+ }
+
+ filterTreeNode(treeNode) {
+ const filterTreeNode = this.props.filterTreeNode;
+ if (typeof filterTreeNode !== 'function' || treeNode.props.disabled) {
+ return false;
+ }
+ return filterTreeNode.call(this, treeNode);
+ }
+
+ renderTreeNode(child, index, level = 0) {
+ const pos = `${level}-${index}`;
+ const key = child.key || pos;
+ const state = this.state;
+ const props = this.props;
+
+ // prefer to child's own selectable property if passed
+ let selectable = props.selectable;
+ if (child.props.hasOwnProperty('selectable')) {
+ selectable = child.props.selectable;
+ }
+
+ const cloneProps = {
+ ref: `treeNode-${key}`,
+ root: this,
+ eventKey: key,
+ pos,
+ selectable,
+ loadData: props.loadData,
+ onMouseEnter: props.onMouseEnter,
+ onMouseLeave: props.onMouseLeave,
+ onRightClick: props.onRightClick,
+ prefixCls: props.prefixCls,
+ showLine: props.showLine,
+ showIcon: props.showIcon,
+ draggable: props.draggable,
+ dragOver: state.dragOverNodeKey === key && this.dropPosition === 0,
+ dragOverGapTop: state.dragOverNodeKey === key && this.dropPosition === -1,
+ dragOverGapBottom: state.dragOverNodeKey === key && this.dropPosition === 1,
+ _dropTrigger: this._dropTrigger,
+ expanded: state.expandedKeys.indexOf(key) !== -1,
+ selected: state.selectedKeys.indexOf(key) !== -1,
+ openTransitionName: this.getOpenTransitionName(),
+ openAnimation: props.openAnimation,
+ filterTreeNode: this.filterTreeNode.bind(this),
+ };
+ if (props.checkable) {
+ cloneProps.checkable = props.checkable;
+ if (props.checkStrictly) {
+ if (state.checkedKeys) {
+ cloneProps.checked = state.checkedKeys.indexOf(key) !== -1 || false;
+ }
+ if (props.checkedKeys.halfChecked) {
+ cloneProps.halfChecked = props.checkedKeys.halfChecked.indexOf(key) !== -1 || false;
+ } else {
+ cloneProps.halfChecked = false;
+ }
+ } else {
+ if (this.checkedKeys) {
+ cloneProps.checked = this.checkedKeys.indexOf(key) !== -1 || false;
+ }
+ cloneProps.halfChecked = this.halfCheckedKeys.indexOf(key) !== -1;
+ }
+ }
+ if (this.treeNodesStates && this.treeNodesStates[pos]) {
+ assign(cloneProps, this.treeNodesStates[pos].siblingPosition);
+ }
+ return React.cloneElement(child, cloneProps);
+ }
+
+ render() {
+ const props = this.props;
+ const domProps = {
+ className: classNames(props.className, props.prefixCls),
+ role: 'tree-node',
+ };
+ if (props.focusable) {
+ domProps.tabIndex = '0';
+ domProps.onKeyDown = this.onKeyDown;
+ }
+ const getTreeNodesStates = () => {
+ this.treeNodesStates = {};
+ loopAllChildren(props.children, (item, index, pos, keyOrPos, siblingPosition) => {
+ this.treeNodesStates[pos] = {
+ siblingPosition,
+ };
+ });
+ };
+ if (props.showLine && !props.checkable) {
+ getTreeNodesStates();
+ }
+ if (props.checkable && (this.checkedKeysChange || props.loadData)) {
+ if (props.checkStrictly) {
+ getTreeNodesStates();
+ } else if (props._treeNodesStates) {
+ this.treeNodesStates = props._treeNodesStates.treeNodesStates;
+ this.halfCheckedKeys = props._treeNodesStates.halfCheckedKeys;
+ this.checkedKeys = props._treeNodesStates.checkedKeys;
+ } else {
+ const checkedKeys = this.state.checkedKeys;
+ let checkKeys;
+ if (!props.loadData && this.checkKeys && this._checkedKeys &&
+ arraysEqual(this._checkedKeys, checkedKeys)) {
+ // if checkedKeys the same as _checkedKeys from onCheck, use _checkedKeys.
+ checkKeys = this.checkKeys;
+ } else {
+ const checkedPositions = [];
+ this.treeNodesStates = {};
+ loopAllChildren(props.children, (item, index, pos, keyOrPos, siblingPosition) => {
+ this.treeNodesStates[pos] = {
+ node: item,
+ key: keyOrPos,
+ checked: false,
+ halfChecked: false,
+ siblingPosition,
+ };
+ if (checkedKeys.indexOf(keyOrPos) !== -1) {
+ this.treeNodesStates[pos].checked = true;
+ checkedPositions.push(pos);
+ }
+ });
+ // if the parent node's key exists, it all children node will be checked
+ handleCheckState(this.treeNodesStates, filterParentPosition(checkedPositions), true);
+ checkKeys = getCheck(this.treeNodesStates);
+ }
+ this.halfCheckedKeys = checkKeys.halfCheckedKeys;
+ this.checkedKeys = checkKeys.checkedKeys;
+ }
+ }
+
+ return (
+
+ {React.Children.map(props.children, this.renderTreeNode, this)}
+
+ );
+ }
+}
+
+Tree.propTypes = {
+ prefixCls: PropTypes.string,
+ children: PropTypes.any,
+ showLine: PropTypes.bool,
+ showIcon: PropTypes.bool,
+ selectable: PropTypes.bool,
+ multiple: PropTypes.bool,
+ checkable: PropTypes.oneOfType([
+ PropTypes.bool,
+ PropTypes.node,
+ ]),
+ _treeNodesStates: PropTypes.object,
+ checkStrictly: PropTypes.bool,
+ draggable: PropTypes.bool,
+ autoExpandParent: PropTypes.bool,
+ defaultExpandAll: PropTypes.bool,
+ defaultExpandedKeys: PropTypes.arrayOf(PropTypes.string),
+ expandedKeys: PropTypes.arrayOf(PropTypes.string),
+ defaultCheckedKeys: PropTypes.arrayOf(PropTypes.string),
+ checkedKeys: PropTypes.oneOfType([
+ PropTypes.arrayOf(PropTypes.string),
+ PropTypes.object,
+ ]),
+ defaultSelectedKeys: PropTypes.arrayOf(PropTypes.string),
+ selectedKeys: PropTypes.arrayOf(PropTypes.string),
+ onExpand: PropTypes.func,
+ onCheck: PropTypes.func,
+ onSelect: PropTypes.func,
+ loadData: PropTypes.func,
+ onMouseEnter: PropTypes.func,
+ onMouseLeave: PropTypes.func,
+ onRightClick: PropTypes.func,
+ onDragStart: PropTypes.func,
+ onDragEnter: PropTypes.func,
+ onDragOver: PropTypes.func,
+ onDragLeave: PropTypes.func,
+ onDrop: PropTypes.func,
+ onDragEnd: PropTypes.func,
+ filterTreeNode: PropTypes.func,
+ openTransitionName: PropTypes.string,
+ openAnimation: PropTypes.oneOfType([PropTypes.string, PropTypes.object]),
+};
+
+Tree.defaultProps = {
+ prefixCls: 'rc-tree',
+ showLine: false,
+ showIcon: true,
+ selectable: true,
+ multiple: false,
+ checkable: false,
+ checkStrictly: false,
+ draggable: false,
+ autoExpandParent: true,
+ defaultExpandAll: false,
+ defaultExpandedKeys: [],
+ defaultCheckedKeys: [],
+ defaultSelectedKeys: [],
+ onExpand: noop,
+ onCheck: noop,
+ onSelect: noop,
+ onDragStart: noop,
+ onDragEnter: noop,
+ onDragOver: noop,
+ onDragLeave: noop,
+ onDrop: noop,
+ onDragEnd: noop,
+};
+
+export default Tree;
diff --git a/src/Tree.scss b/src/Tree.scss
new file mode 100644
index 0000000..141d664
--- /dev/null
+++ b/src/Tree.scss
@@ -0,0 +1,320 @@
+@import "../node_modules/tinper-bee-core/scss/minxin-variables";
+@import "../node_modules/tinper-bee-core/scss/minxin-mixins";
+@import "../node_modules/bee-checkbox/src/Checkbox";
+
+
+//css 分割线
+.u-tree li span.u-tree-checkbox {
+ margin: 2px 4px 0 0;
+}
+.u-tree-checkbox {
+ white-space: nowrap;
+ cursor: pointer;
+ outline: none;
+ display: inline-block;
+ line-height: 1;
+ position: relative;
+ vertical-align: middle;
+}
+.u-tree-checkbox-checked .u-tree-checkbox-inner, .u-tree-checkbox-indeterminate .u-tree-checkbox-inner {
+ background-color: #108ee9;
+ border-color: #108ee9;
+}
+.u-tree-checkbox-inner {
+ position: relative;
+ top: 0;
+ left: 0;
+ display: inline-block;
+ width: 14px;
+ height: 14px;
+ border: 1px solid #d9d9d9;
+ border-radius: 3px;
+ background-color: #fff;
+ -webkit-transition: all .3s;
+ transition: all .3s;
+}
+.u-tree-checkbox-checked .u-tree-checkbox-inner, .u-tree-checkbox-indeterminate .u-tree-checkbox-inner {
+ background-color: #108ee9;
+ border-color: #108ee9;
+}
+u-tree-checkbox-disabled .u-tree-checkbox-inner {
+ border-color: #d9d9d9 !important;
+ background-color: #f3f3f3;
+}
+.u-tree-checkbox-indeterminate .u-tree-checkbox-inner:after {
+ content: ' ';
+ -webkit-transform: scale(1);
+ -ms-transform: scale(1);
+ transform: scale(1);
+ position: absolute;
+ left: 2px;
+ top: 5px;
+ width: 8px;
+ height: 1px;
+}
+.u-tree-checkbox-disabled .u-tree-checkbox-inner {
+ border-color: #d9d9d9 !important;
+ background-color: #f3f3f3;
+}
+.u-tree-checkbox-disabled.u-tree-checkbox-checked .u-tree-checkbox-inner:after {
+ -webkit-animation-name: none;
+ animation-name: none;
+ border-color: #ccc;
+}
+.u-tree-checkbox-disabled .u-tree-checkbox-inner:after {
+ -webkit-animation-name: none;
+ animation-name: none;
+ border-color: #f3f3f3;
+}
+.u-tree-checkbox-checked .u-tree-checkbox-inner:after {
+ -webkit-transform: rotate(45deg) scale(1);
+ -ms-transform: rotate(45deg) scale(1);
+ transform: rotate(45deg) scale(1);
+ position: absolute;
+ left: 4px;
+ top: 1px;
+ display: table;
+ width: 5px;
+ height: 8px;
+ border: 2px solid #fff;
+ border-top: 0;
+ border-left: 0;
+ content: ' ';
+ -webkit-transition: all 0.2s cubic-bezier(0.12, 0.4, 0.29, 1.46) 0.1s;
+ transition: all 0.2s cubic-bezier(0.12, 0.4, 0.29, 1.46) 0.1s;
+}
+.u-tree-checkbox-inner:after {
+ -webkit-transform: rotate(45deg) scale(0);
+ -ms-transform: rotate(45deg) scale(0);
+ transform: rotate(45deg) scale(0);
+ position: absolute;
+ left: 4px;
+ top: 1px;
+ display: table;
+ width: 5px;
+ height: 8px;
+ border: 2px solid #fff;
+ border-top: 0;
+ border-left: 0;
+ content: ' ';
+ -webkit-transition: all 0.1s cubic-bezier(0.71, -0.46, 0.88, 0.6);
+ transition: all 0.1s cubic-bezier(0.71, -0.46, 0.88, 0.6);
+}
+.u-tree {
+ margin: 0;
+ padding: 5px;
+ font-size: 12px;
+}
+.u-tree li {
+ padding: 0;
+ margin: 7px 0;
+ list-style: none;
+ white-space: nowrap;
+ outline: 0;
+}
+.u-tree li a[draggable],
+.u-tree li a[draggable="true"] {
+ -webkit-user-select: none;
+ -moz-user-select: none;
+ -ms-user-select: none;
+ user-select: none;
+ /* Required to make elements draggable in old WebKit */
+ -khtml-user-drag: element;
+ -webkit-user-drag: element;
+}
+.u-tree li.drag-over > a[draggable] {
+ background-color: #108ee9;
+ color: white;
+ opacity: 0.8;
+}
+.u-tree li.drag-over-gap-top > a[draggable] {
+ border-top: 2px #108ee9 solid;
+}
+.u-tree li.drag-over-gap-bottom > a[draggable] {
+ border-bottom: 2px #108ee9 solid;
+}
+.u-tree li.filter-node > a {
+ color: #f50 !important;
+ font-weight: bold!important;
+}
+.u-tree li ul {
+ margin: 0;
+ padding: 0 0 0 18px;
+}
+.u-tree li a {
+ display: inline-block;
+ padding: 1px 5px;
+ border-radius: 2px;
+ margin: 0;
+ cursor: pointer;
+ text-decoration: none;
+ vertical-align: top;
+ color: #666;
+ -webkit-transition: all 0.3s ease;
+ transition: all 0.3s ease;
+}
+.u-tree li a:hover {
+ background-color: #e7f4fd;
+}
+.u-tree li a.u-tree-node-selected {
+ background-color: #cfe8fb;
+}
+.u-tree li span.u-checkbox {
+ margin: 2px 4px 0 0;
+}
+.u-tree li span.u-tree-switcher,
+.u-tree li span.u-tree-iconEle {
+ margin: 0;
+ width: 16px;
+ height: 16px;
+ line-height: 16px;
+ display: inline-block;
+ vertical-align: middle;
+ border: 0 none;
+ cursor: pointer;
+ outline: none;
+}
+.u-tree li span.u-tree-icon_loading:after {
+ display: inline-block;
+ font-family: 'uf';
+ text-rendering: optimizeLegibility;
+ -webkit-font-smoothing: antialiased;
+ -moz-osx-font-smoothing: grayscale;
+ content: "\E6AE";
+ -webkit-animation: loadingCircle 1s infinite linear;
+ animation: loadingCircle 1s infinite linear;
+ color: #108ee9;
+}
+.u-tree li span.u-tree-switcher.u-tree-switcher-noop {
+ cursor: auto;
+}
+.u-tree li span.u-tree-switcher.u-tree-roots_open,
+.u-tree li span.u-tree-switcher.u-tree-center_open,
+.u-tree li span.u-tree-switcher.u-tree-bottom_open,
+.u-tree li span.u-tree-switcher.u-tree-noline_open {
+ position: relative;
+}
+.u-tree li span.u-tree-switcher.u-tree-roots_open:after,
+.u-tree li span.u-tree-switcher.u-tree-center_open:after,
+.u-tree li span.u-tree-switcher.u-tree-bottom_open:after,
+.u-tree li span.u-tree-switcher.u-tree-noline_open:after {
+ font-size: 18px;
+ font-size: 7px \9;
+ -webkit-transform: scale(0.58333333) rotate(0deg);
+ -ms-transform: scale(0.58333333) rotate(0deg);
+ transform: scale(0.58333333) rotate(0deg);
+ /* IE6-IE8 */
+ -ms-filter: "progid:DXImageTransform.Microsoft.Matrix(sizingMethod='auto expand', M11=1, M12=0, M21=0, M22=1)";
+ zoom: 1;
+ display: inline-block;
+ font-family: 'uf';
+ text-rendering: optimizeLegibility;
+ -webkit-font-smoothing: antialiased;
+ -moz-osx-font-smoothing: grayscale;
+ content: "\e639";
+ font-weight: bold;
+ position: absolute;
+ top: 0;
+ right: 4px;
+ color: #666;
+ -webkit-transition: -webkit-transform .3s ease;
+ transition: -webkit-transform .3s ease;
+ transition: transform .3s ease;
+ transition: transform .3s ease, -webkit-transform .3s ease;
+}
+:root .u-tree li span.u-tree-switcher.u-tree-roots_open:after,
+:root .u-tree li span.u-tree-switcher.u-tree-center_open:after,
+:root .u-tree li span.u-tree-switcher.u-tree-bottom_open:after,
+:root .u-tree li span.u-tree-switcher.u-tree-noline_open:after {
+ -webkit-filter: none;
+ filter: none;
+}
+:root .u-tree li span.u-tree-switcher.u-tree-roots_open:after,
+:root .u-tree li span.u-tree-switcher.u-tree-center_open:after,
+:root .u-tree li span.u-tree-switcher.u-tree-bottom_open:after,
+:root .u-tree li span.u-tree-switcher.u-tree-noline_open:after {
+ font-size: 18px;
+}
+.u-tree li span.u-tree-switcher.u-tree-roots_close,
+.u-tree li span.u-tree-switcher.u-tree-center_close,
+.u-tree li span.u-tree-switcher.u-tree-bottom_close,
+.u-tree li span.u-tree-switcher.u-tree-noline_close {
+ position: relative;
+ -ms-filter: "progid:DXImageTransform.Microsoft.BasicImage(rotation=3)";
+}
+.u-tree li span.u-tree-switcher.u-tree-roots_close:after,
+.u-tree li span.u-tree-switcher.u-tree-center_close:after,
+.u-tree li span.u-tree-switcher.u-tree-bottom_close:after,
+.u-tree li span.u-tree-switcher.u-tree-noline_close:after {
+ font-size: 18px;
+ font-size: 7px \9;
+ -webkit-transform: scale(0.58333333) rotate(0deg);
+ -ms-transform: scale(0.58333333) rotate(0deg);
+ transform: scale(0.58333333) rotate(0deg);
+ /* IE6-IE8 */
+ -ms-filter: "progid:DXImageTransform.Microsoft.Matrix(sizingMethod='auto expand', M11=1, M12=0, M21=0, M22=1)";
+ zoom: 1;
+ display: inline-block;
+ font-family: 'uf';
+ text-rendering: optimizeLegibility;
+ -webkit-font-smoothing: antialiased;
+ -moz-osx-font-smoothing: grayscale;
+ content: "\e639";
+ font-weight: bold;
+ position: absolute;
+ top: 0;
+ right: 4px;
+ color: #666;
+ -webkit-transition: -webkit-transform .3s ease;
+ transition: -webkit-transform .3s ease;
+ transition: transform .3s ease;
+ transition: transform .3s ease, -webkit-transform .3s ease;
+}
+:root .u-tree li span.u-tree-switcher.u-tree-roots_close:after,
+:root .u-tree li span.u-tree-switcher.u-tree-center_close:after,
+:root .u-tree li span.u-tree-switcher.u-tree-bottom_close:after,
+:root .u-tree li span.u-tree-switcher.u-tree-noline_close:after {
+ -webkit-filter: none;
+ filter: none;
+}
+:root .u-tree li span.u-tree-switcher.u-tree-roots_close:after,
+:root .u-tree li span.u-tree-switcher.u-tree-center_close:after,
+:root .u-tree li span.u-tree-switcher.u-tree-bottom_close:after,
+:root .u-tree li span.u-tree-switcher.u-tree-noline_close:after {
+ font-size: 18px;
+}
+.u-tree li span.u-tree-switcher.u-tree-roots_close:after,
+.u-tree li span.u-tree-switcher.u-tree-center_close:after,
+.u-tree li span.u-tree-switcher.u-tree-bottom_close:after,
+.u-tree li span.u-tree-switcher.u-tree-noline_close:after {
+ -webkit-transform: rotate(270deg) scale(0.6);
+ -ms-transform: rotate(270deg) scale(0.6);
+ transform: rotate(270deg) scale(0.6);
+}
+.u-tree-child-tree {
+ display: none;
+}
+.u-tree-child-tree-open {
+ display: block;
+}
+.u-tree-treenode-disabled > span,
+.u-tree-treenode-disabled > a,
+.u-tree-treenode-disabled > a span {
+ color: #ccc;
+ cursor: not-allowed;
+}
+.u-tree-icon__open {
+ margin-right: 2px;
+ vertical-align: top;
+}
+.u-tree-icon__close {
+ margin-right: 2px;
+ vertical-align: top;
+}
+.u-motion-collapse {
+ overflow: hidden;
+}
+.u-motion-collapse-active {
+ -webkit-transition: height 0.2s cubic-bezier(0.215, 0.61, 0.355, 1);
+ transition: height 0.2s cubic-bezier(0.215, 0.61, 0.355, 1);
+}
diff --git a/src/TreeNode.js b/src/TreeNode.js
new file mode 100644
index 0000000..aa140cf
--- /dev/null
+++ b/src/TreeNode.js
@@ -0,0 +1,383 @@
+import React, { PropTypes } from 'react';
+import assign from 'object-assign';
+import classNames from 'classnames';
+import Animate from 'rc-animate';
+import { browser } from './util';
+
+const browserUa = typeof window !== 'undefined' ? browser(window.navigator) : '';
+const ieOrEdge = /.*(IE|Edge).+/.test(browserUa);
+// const uaArray = browserUa.split(' ');
+// const gtIE8 = uaArray.length !== 2 || uaArray[0].indexOf('IE') === -1 || Number(uaArray[1]) > 8;
+
+const defaultTitle = '---';
+
+class TreeNode extends React.Component {
+ constructor(props) {
+ super(props);
+ [
+ 'onExpand',
+ 'onCheck',
+ 'onContextMenu',
+ 'onMouseEnter',
+ 'onMouseLeave',
+ 'onDragStart',
+ 'onDragEnter',
+ 'onDragOver',
+ 'onDragLeave',
+ 'onDrop',
+ 'onDragEnd',
+ ].forEach((m) => {
+ this[m] = this[m].bind(this);
+ });
+ this.state = {
+ dataLoading: false,
+ dragNodeHighlight: false,
+ };
+ }
+
+ componentDidMount() {
+ if (!this.props.root._treeNodeInstances) {
+ this.props.root._treeNodeInstances = [];
+ }
+ this.props.root._treeNodeInstances.push(this);
+ }
+ // shouldComponentUpdate(nextProps) {
+ // if (!nextProps.expanded) {
+ // return false;
+ // }
+ // return true;
+ // }
+
+ onCheck() {
+ this.props.root.onCheck(this);
+ }
+
+ onSelect() {
+ this.props.root.onSelect(this);
+ }
+
+ onMouseEnter(e) {
+ e.preventDefault();
+ this.props.root.onMouseEnter(e, this);
+ }
+
+ onMouseLeave(e) {
+ e.preventDefault();
+ this.props.root.onMouseLeave(e, this);
+ }
+
+ onContextMenu(e) {
+ e.preventDefault();
+ this.props.root.onContextMenu(e, this);
+ }
+
+ onDragStart(e) {
+ // console.log('dragstart', this.props.eventKey, e);
+ // e.preventDefault();
+ e.stopPropagation();
+ this.setState({
+ dragNodeHighlight: true,
+ });
+ this.props.root.onDragStart(e, this);
+ try {
+ // ie throw error
+ // firefox-need-it
+ e.dataTransfer.setData('text/plain', '');
+ } finally {
+ // empty
+ }
+ }
+
+ onDragEnter(e) {
+ e.preventDefault();
+ e.stopPropagation();
+ this.props.root.onDragEnter(e, this);
+ }
+
+ onDragOver(e) {
+ // todo disabled
+ e.preventDefault();
+ e.stopPropagation();
+ this.props.root.onDragOver(e, this);
+ return false;
+ }
+
+ onDragLeave(e) {
+ e.stopPropagation();
+ this.props.root.onDragLeave(e, this);
+ }
+
+ onDrop(e) {
+ e.preventDefault();
+ e.stopPropagation();
+ this.setState({
+ dragNodeHighlight: false,
+ });
+ this.props.root.onDrop(e, this);
+ }
+
+ onDragEnd(e) {
+ e.stopPropagation();
+ this.setState({
+ dragNodeHighlight: false,
+ });
+ this.props.root.onDragEnd(e, this);
+ }
+
+ onExpand() {
+ const callbackPromise = this.props.root.onExpand(this);
+ if (callbackPromise && typeof callbackPromise === 'object') {
+ const setLoading = (dataLoading) => {
+ this.setState({ dataLoading });
+ };
+ setLoading(true);
+ callbackPromise.then(() => {
+ setLoading(false);
+ }, () => {
+ setLoading(false);
+ });
+ }
+ }
+
+ // keyboard event support
+ onKeyDown(e) {
+ e.preventDefault();
+ }
+
+ renderSwitcher(props, expandedState) {
+ const prefixCls = props.prefixCls;
+ const switcherCls = {
+ [`${prefixCls}-switcher`]: true,
+ };
+ if (!props.showLine) {
+ switcherCls[`${prefixCls}-noline_${expandedState}`] = true;
+ } else if (props.pos === '0-0') {
+ switcherCls[`${prefixCls}-roots_${expandedState}`] = true;
+ } else {
+ switcherCls[`${prefixCls}-center_${expandedState}`] = !props.last;
+ switcherCls[`${prefixCls}-bottom_${expandedState}`] = props.last;
+ }
+ if (props.disabled) {
+ switcherCls[`${prefixCls}-switcher-disabled`] = true;
+ return ;
+ }
+ return ;
+ }
+
+ renderCheckbox(props) {
+ const prefixCls = props.prefixCls;
+ const checkboxCls = {
+ [`${prefixCls}-checkbox`]: true,
+ };
+ if (props.checked) {
+ checkboxCls[`${prefixCls}-checkbox-checked`] = true;
+ } else if (props.halfChecked) {
+ checkboxCls[`${prefixCls}-checkbox-indeterminate`] = true;
+ }
+ let customEle = null;
+ if (typeof props.checkable !== 'boolean') {
+ customEle = props.checkable;
+ }
+ if (props.disabled || props.disableCheckbox) {
+ checkboxCls[`${prefixCls}-checkbox-disabled`] = true;
+ return {customEle};
+ }
+ return (
+ {customEle});
+ }
+
+ renderChildren(props) {
+ const renderFirst = this.renderFirst;
+ this.renderFirst = 1;
+ let transitionAppear = true;
+ if (!renderFirst && props.expanded) {
+ transitionAppear = false;
+ }
+ const children = props.children;
+ let newChildren = children;
+ if (children &&
+ (children.type === TreeNode ||
+ Array.isArray(children) &&
+ children.every((item) => {
+ return item.type === TreeNode;
+ }))) {
+ const cls = {
+ [`${props.prefixCls}-child-tree`]: true,
+ [`${props.prefixCls}-child-tree-open`]: props.expanded,
+ };
+ if (props.showLine) {
+ cls[`${props.prefixCls}-line`] = !props.last;
+ }
+ const animProps = {};
+ if (props.openTransitionName) {
+ animProps.transitionName = props.openTransitionName;
+ } else if (typeof props.openAnimation === 'object') {
+ animProps.animation = assign({}, props.openAnimation);
+ if (!transitionAppear) {
+ delete animProps.animation.appear;
+ }
+ }
+ newChildren = (
+
+ {!props.expanded ? null :
+ {React.Children.map(children, (item, index) => {
+ return props.root.renderTreeNode(item, index, props.pos);
+ }, props.root)}
+
}
+
+ );
+ }
+ return newChildren;
+ }
+
+ render() {
+ const props = this.props;
+ const prefixCls = props.prefixCls;
+ const expandedState = props.expanded ? 'open' : 'close';
+ let iconState = expandedState;
+
+ let canRenderSwitcher = true;
+ const content = props.title;
+ let newChildren = this.renderChildren(props);
+ if (!newChildren || newChildren === props.children) {
+ // content = newChildren;
+ newChildren = null;
+ if (!props.loadData || props.isLeaf) {
+ canRenderSwitcher = false;
+ iconState = 'docu';
+ }
+ }
+ // For performance, does't render children into dom when `!props.expanded` (move to Animate)
+ // if (!props.expanded) {
+ // newChildren = null;
+ // }
+
+ const iconEleCls = {
+ [`${prefixCls}-iconEle`]: true,
+ [`${prefixCls}-icon_loading`]: this.state.dataLoading,
+ [`${prefixCls}-icon__${iconState}`]: true,
+ };
+
+ const selectHandle = () => {
+ const icon = (props.showIcon || props.loadData && this.state.dataLoading) ?
+ : null;
+ const title = {content};
+ const wrap = `${prefixCls}-node-content-wrapper`;
+ const domProps = {
+ className: `${wrap} ${wrap}-${iconState === expandedState ? iconState : 'normal'}`,
+ };
+ if (!props.disabled) {
+ if (props.selected || !props._dropTrigger && this.state.dragNodeHighlight) {
+ domProps.className += ` ${prefixCls}-node-selected`;
+ }
+ domProps.onClick = (e) => {
+ e.preventDefault();
+ if (props.selectable) {
+ this.onSelect();
+ }
+ // not fire check event
+ // if (props.checkable) {
+ // this.onCheck();
+ // }
+ };
+ if (props.onRightClick) {
+ domProps.onContextMenu = this.onContextMenu;
+ }
+ if (props.onMouseEnter) {
+ domProps.onMouseEnter = this.onMouseEnter;
+ }
+ if (props.onMouseLeave) {
+ domProps.onMouseLeave = this.onMouseLeave;
+ }
+ if (props.draggable) {
+ domProps.className += ' draggable';
+ if (ieOrEdge) {
+ // ie bug!
+ domProps.href = '#';
+ }
+ domProps.draggable = true;
+ domProps['aria-grabbed'] = true;
+ domProps.onDragStart = this.onDragStart;
+ }
+ }
+ return (
+
+ {icon}{title}
+
+ );
+ };
+
+ const liProps = {};
+ if (props.draggable) {
+ liProps.onDragEnter = this.onDragEnter;
+ liProps.onDragOver = this.onDragOver;
+ liProps.onDragLeave = this.onDragLeave;
+ liProps.onDrop = this.onDrop;
+ liProps.onDragEnd = this.onDragEnd;
+ }
+
+ let disabledCls = '';
+ let dragOverCls = '';
+ if (props.disabled) {
+ disabledCls = `${prefixCls}-treenode-disabled`;
+ } else if (props.dragOver) {
+ dragOverCls = 'drag-over';
+ } else if (props.dragOverGapTop) {
+ dragOverCls = 'drag-over-gap-top';
+ } else if (props.dragOverGapBottom) {
+ dragOverCls = 'drag-over-gap-bottom';
+ }
+
+ const filterCls = props.filterTreeNode(this) ? 'filter-node' : '';
+
+ const noopSwitcher = () => {
+ const cls = {
+ [`${prefixCls}-switcher`]: true,
+ [`${prefixCls}-switcher-noop`]: true,
+ };
+ if (props.showLine) {
+ cls[`${prefixCls}-center_docu`] = !props.last;
+ cls[`${prefixCls}-bottom_docu`] = props.last;
+ } else {
+ cls[`${prefixCls}-noline_docu`] = true;
+ }
+ return ;
+ };
+
+ return (
+
+ {canRenderSwitcher ? this.renderSwitcher(props, expandedState) : noopSwitcher()}
+ {props.checkable ? this.renderCheckbox(props) : null}
+ {selectHandle()}
+ {newChildren}
+
+ );
+ }
+}
+
+TreeNode.isTreeNode = 1;
+
+TreeNode.propTypes = {
+ prefixCls: PropTypes.string,
+ disabled: PropTypes.bool,
+ disableCheckbox: PropTypes.bool,
+ expanded: PropTypes.bool,
+ isLeaf: PropTypes.bool,
+ root: PropTypes.object,
+ onSelect: PropTypes.func,
+};
+
+TreeNode.defaultProps = {
+ title: defaultTitle,
+};
+
+export default TreeNode;
diff --git a/src/Treecopy.scss b/src/Treecopy.scss
new file mode 100644
index 0000000..4a2d36b
--- /dev/null
+++ b/src/Treecopy.scss
@@ -0,0 +1,363 @@
+
+$treePrefixCls: 'u-tree';
+.#{$treePrefixCls} {
+ margin: 0;
+ padding: 5px;
+ li {
+ padding: 0;
+ margin: 0;
+ list-style: none;
+ white-space: nowrap;
+ outline: 0;
+ .draggable {
+ color: #333;
+ -moz-user-select: none;
+ -khtml-user-select: none;
+ -webkit-user-select: none;
+ user-select: none;
+ /* Required to make elements draggable in old WebKit */
+ -khtml-user-drag: element;
+ -webkit-user-drag: element;
+ }
+ &.drag-over {
+ > .draggable {
+ background-color: #316ac5;
+ color: white;
+ border: 1px #316ac5 solid;
+ opacity: 0.8;
+ }
+ }
+ &.drag-over-gap-top {
+ > .draggable {
+ border-top: 2px blue solid;
+ }
+ }
+ &.drag-over-gap-bottom {
+ > .draggable {
+ border-bottom: 2px blue solid;
+ }
+ }
+ &.filter-node {
+ > .#{$treePrefixCls}-node-content-wrapper {
+ color: #a60000!important;
+ font-weight: bold!important;
+ }
+ }
+ ul {
+ margin: 0;
+ padding: 0 0 0 18px;
+ &.#{$treePrefixCls}-line {
+ background: url("https://t.alipayobjects.com/images/T13BtfXl0mXXXXXXXX.gif") 0 0 repeat-y;
+ }
+ }
+ .#{$treePrefixCls}-node-content-wrapper {
+ display: inline-block;
+ padding: 1px 3px 0 0;
+ margin: 0;
+ cursor: pointer;
+ height: 17px;
+ text-decoration: none;
+ vertical-align: top;
+ }
+ span {
+ &.#{$treePrefixCls}-switcher,
+ &.#{$treePrefixCls}-checkbox,
+ &.#{$treePrefixCls}-iconEle {
+ line-height: 16px;
+ margin-right: 2px;
+ width: 16px;
+ height: 16px;
+ display: inline-block;
+ vertical-align: middle;
+ border: 0 none;
+ cursor: pointer;
+ outline: none;
+ background-color: transparent;
+ background-repeat: no-repeat;
+ background-attachment: scroll;
+ background-image: url("https://t.alipayobjects.com/images/T1.ANfXhXtXXXXXXXX.png");
+ }
+ &.#{$treePrefixCls}-icon_loading {
+ margin-right: 2px;
+ vertical-align: top;
+ background: url(https://t.alipayobjects.com/images/rmsweb/T1YxhiXgJbXXXXXXXX.gif) no-repeat scroll 0 0 transparent;
+ }
+ &.#{$treePrefixCls}-switcher {
+ &.#{$treePrefixCls}-switcher-noop {
+ cursor: auto;
+ }
+ &.#{$treePrefixCls}-roots_open {
+ background-position: -93px -56px;
+ }
+ &.#{$treePrefixCls}-roots_close {
+ background-position: -75px -56px;
+ }
+ &.#{$treePrefixCls}-center_open {
+ background-position: -92px -18px;
+ }
+ &.#{$treePrefixCls}-center_close {
+ background-position: -74px -18px;
+ }
+ &.#{$treePrefixCls}-bottom_open {
+ background-position: -92px -36px;
+ }
+ &.#{$treePrefixCls}-bottom_close {
+ background-position: -74px -36px;
+ }
+ &.#{$treePrefixCls}-noline_open {
+ background-position: -92px -72px;
+ }
+ &.#{$treePrefixCls}-noline_close {
+ background-position: -74px -72px;
+ }
+ &.#{$treePrefixCls}-center_docu {
+ background-position: -56px -18px;
+ }
+ &.#{$treePrefixCls}-bottom_docu {
+ background-position: -56px -36px;
+ }
+ &.#{$treePrefixCls}-noline_docu {
+ background: none;
+ }
+ }
+ &.#{$treePrefixCls}-checkbox {
+ width: 13px;
+ height: 13px;
+ margin: 0 3px;
+ background-position: 0 0;
+ &-checked {
+ background-position: -14px 0;
+ }
+ &-indeterminate {
+ background-position: -14px -28px;
+ }
+ &-disabled {
+ background-position: 0 -56px;
+ }
+ &.#{$treePrefixCls}-checkbox-checked.#{$treePrefixCls}-checkbox-disabled {
+ background-position: -14px -56px;
+ }
+ &.#{$treePrefixCls}-checkbox-indeterminate.#{$treePrefixCls}-checkbox-disabled {
+ position: relative;
+ background: #ccc;
+ border-radius: 3px;
+ &::after {
+ content: ' ';
+ -webkit-transform: scale(1);
+ transform: scale(1);
+ position: absolute;
+ left: 3px;
+ top: 5px;
+ width: 5px;
+ height: 0;
+ border: 2px solid #fff;
+ border-top: 0;
+ border-left: 0;
+ }
+ }
+ }
+ }
+ }
+ &-child-tree {
+ display: none;
+ &-open {
+ display: block;
+ }
+ }
+ &-treenode-disabled {
+ >span,
+ >a,
+ >a span {
+ color: #ccc;
+ cursor: not-allowed;
+ }
+ }
+ &-node-selected {
+ background-color: #ffe6b0;
+ border: 1px #ffb951 solid;
+ opacity: 0.8;
+ }
+ &-icon__open {
+ margin-right: 2px;
+ background-position: -110px -16px;
+ vertical-align: top;
+ }
+ &-icon__close {
+ margin-right: 2px;
+ background-position: -110px 0;
+ vertical-align: top;
+ }
+ &-icon__docu {
+ margin-right: 2px;
+ background-position: -110px -32px;
+ vertical-align: top;
+ }
+}
+
+$tree-prefix-cls: "ant-tree";
+$primary-color: '#108ee9';
+$highlight-color: '#f50';
+$font-size-base: '12px';
+//.antCheckboxFn($checkbox-prefix-cls: "ant-tree-checkbox");
+
+.#{$tree-prefix-cls} {
+ margin: 0;
+ padding: 5px;
+ font-size: $font-size-base;
+ li {
+ padding: 0;
+ margin: 7px 0;
+ list-style: none;
+ white-space: nowrap;
+ outline: 0;
+ a[draggable],
+ a[draggable="true"] {
+ user-select: none;
+ /* Required to make elements draggable in old WebKit */
+ -khtml-user-drag: element;
+ -webkit-user-drag: element;
+ }
+ &.drag-over {
+ > a[draggable] {
+ background-color: $primary-color;
+ color: white;
+ opacity: 0.8;
+ }
+ }
+ &.drag-over-gap-top {
+ > a[draggable] {
+ border-top: 2px $primary-color solid;
+ }
+ }
+ &.drag-over-gap-bottom {
+ > a[draggable] {
+ border-bottom: 2px $primary-color solid;
+ }
+ }
+ &.filter-node {
+ > a {
+ color: $highlight-color!important;
+ font-weight: bold!important;
+ }
+ }
+ ul {
+ margin: 0;
+ padding: 0 0 0 18px;
+ }
+ a {
+ display: inline-block;
+ padding: 1px 5px;
+ border-radius: 2px;
+ margin: 0;
+ cursor: pointer;
+ text-decoration: none;
+ vertical-align: top;
+ color: $text-color;
+ transition: all 0.3s ease;
+ &:hover {
+ background-color: tint($primary-color, 90%);
+ }
+ &.#{$tree-prefix-cls}-node-selected {
+ background-color: tint($primary-color, 80%);
+ }
+ }
+ span {
+ &.#{$tree-prefix-cls}-checkbox {
+ margin: 2px 4px 0 0;
+ }
+ &.#{$tree-prefix-cls}-switcher,
+ &.#{$tree-prefix-cls}-iconEle {
+ margin: 0;
+ width: 16px;
+ height: 16px;
+ line-height: 16px;
+ display: inline-block;
+ vertical-align: middle;
+ border: 0 none;
+ cursor: pointer;
+ outline: none;
+ }
+ &.#{$tree-prefix-cls}-icon_loading {
+ &:after {
+ display: inline-block;
+ animation: loadingCircle 1s infinite linear;
+ color: $primary-color;
+ font-family: 'uf';
+ text-rendering: optimizeLegibility;
+ -webkit-font-smoothing: antialiased;
+ -moz-osx-font-smoothing: grayscale;
+ content: "\e6ae";
+ }
+ }
+ &.#{$tree-prefix-cls}-switcher {
+ &.#{$tree-prefix-cls}-switcher-noop {
+ cursor: auto;
+ }
+ &.#{$tree-prefix-cls}-roots_open,
+ &.#{$tree-prefix-cls}-center_open,
+ &.#{$tree-prefix-cls}-bottom_open,
+ &.#{$tree-prefix-cls}-noline_open {
+ //@include antTreeSwitcherIcon;
+ }
+ &.#{$tree-prefix-cls}-roots_close,
+ &.#{$tree-prefix-cls}-center_close,
+ &.#{$tree-prefix-cls}-bottom_close,
+ &.#{$tree-prefix-cls}-noline_close {
+ //@include antTreeSwitcherIcon;
+ -ms-filter: "progid:DXImageTransform.Microsoft.BasicImage(rotation=3)";
+ &:after {
+ transform: rotate(270deg) scale(0.6);
+ }
+ }
+ }
+ }
+ }
+ &-child-tree {
+ display: none;
+ &-open {
+ display: block;
+ }
+ }
+ &-treenode-disabled {
+ >span,
+ >a,
+ >a span {
+ color: #ccc;
+ cursor: not-allowed;
+ }
+ }
+ &-icon__open {
+ margin-right: 2px;
+ vertical-align: top;
+ }
+ &-icon__close {
+ margin-right: 2px;
+ vertical-align: top;
+ }
+}
+@mixin antTreeSwitcherIcon {
+ position: relative;
+ &:after {
+ font-size: 12px;
+ -webkit-transform: scale(0.58333333) rotate(0deg);
+ -ms-transform: scale(0.58333333) rotate(0deg);
+ transform: scale(0.58333333) rotate(0deg);
+ -ms-filter: "progid:DXImageTransform.Microsoft.Matrix(sizingMethod='auto expand', M11=1, M12=0, M21=0, M22=1)";
+ zoom: 1;
+ display: inline-block;
+ font-family: 'anticon';
+ text-rendering: optimizeLegibility;
+ -webkit-font-smoothing: antialiased;
+ -moz-osx-font-smoothing: grayscale;
+ content: "\E606";
+ font-weight: bold;
+ position: absolute;
+ top: 0;
+ right: 4px;
+ color: #666;
+ -webkit-transition: -webkit-transform .3s ease;
+ transition: -webkit-transform .3s ease;
+ transition: transform .3s ease;
+ transition: transform .3s ease, -webkit-transform .3s ease;
+ }
+}
\ No newline at end of file
diff --git a/src/index.js b/src/index.js
new file mode 100644
index 0000000..ee7c36a
--- /dev/null
+++ b/src/index.js
@@ -0,0 +1,108 @@
+import React,{PropTypes,Component} from 'react';
+import RcTree from './Tree';
+import TreeNode from './TreeNode';
+import animation from './openAnimation';
+
+const AntTreeNodeProps ={
+ disabled: PropTypes.bool,
+ disableCheckbox: PropTypes.bool,
+ title: PropTypes.oneOfType([
+ PropTypes.string,
+ PropTypes.node
+ ]),
+ key: PropTypes.string,
+ isLeaf: PropTypes.bool
+}
+
+export class AntTreeNode extends Component {
+ render() {
+ return ;
+ }
+}
+
+AntTreeNode.AntTreeNodeProps = AntTreeNodeProps;
+
+const TreeProps ={
+ showLine: PropTypes.bool,
+ className: PropTypes.string,
+ /** 是否支持多选 */
+ multiple: PropTypes.bool,
+ /** 是否自动展开父节点 */
+ autoExpandParent: PropTypes.bool,
+ /** checkable状态下节点选择完全受控(父子节点选中状态不再关联)*/
+ checkStrictly: PropTypes.bool,
+ /** 是否支持选中 */
+ checkable: PropTypes.bool,
+ /** 默认展开所有树节点 */
+ defaultExpandAll: PropTypes.bool,
+ /** 默认展开指定的树节点 */
+ defaultExpandedKeys: PropTypes.array,
+ /** (受控)展开指定的树节点 */
+ expandedKeys: PropTypes.array,
+ /** (受控)选中复选框的树节点 */
+ checkedKeys: PropTypes.oneOfType([
+ PropTypes.array,
+ PropTypes.object
+ ]),
+ /** 默认选中复选框的树节点 */
+ defaultCheckedKeys: PropTypes.array,
+ /** (受控)设置选中的树节点 */
+ selectedKeys: PropTypes.array,
+ /** 默认选中的树节点 */
+ defaultSelectedKeys: PropTypes.array,
+ /** 展开/收起节点时触发 */
+ onExpand: PropTypes.func,
+ /** 点击复选框触发 */
+ onCheck: PropTypes.func,
+ /** 点击树节点触发 */
+ onSelect: PropTypes.func,
+ /** filter some AntTreeNodes as you need. it should return true */
+ filterAntTreeNode: PropTypes.func,
+ /** 异步加载数据 */
+ loadData: PropTypes.func,
+ /** 响应右键点击 */
+ onRightClick: PropTypes.func,
+ /** 设置节点可拖拽(IE>8)*/
+ draggable: PropTypes.bool,
+ /** 开始拖拽时调用 */
+ onDragStart: PropTypes.func,
+ /** dragenter 触发时调用 */
+ onDragEnter: PropTypes.func,
+ /** dragover 触发时调用 */
+ onDragOver: PropTypes.func,
+ /** dragleave 触发时调用 */
+ onDragLeave: PropTypes.func,
+ /** drop 触发时调用 */
+ onDrop: PropTypes.func,
+ style: React.CSSProperties,
+ prefixCls: PropTypes.string,
+ filterTreeNode: PropTypes.func
+}
+
+const defaultProps = {
+ prefixCls: 'u-tree',
+ checkable: false,
+ showIcon: false,
+ openAnimation: animation,
+}
+
+class Tree extends Component{
+
+ render() {
+ const props = this.props;
+ let checkable = props.checkable;
+ return (
+ ) : checkable }
+ >
+ {this.props.children}
+
+ );
+ }
+}
+
+Tree.TreeNode = TreeNode;
+Tree.TreeProps = TreeProps;
+Tree.defaultProps = defaultProps;
+export default Tree;
diff --git a/src/openAnimation.js b/src/openAnimation.js
new file mode 100644
index 0000000..15f9aa5
--- /dev/null
+++ b/src/openAnimation.js
@@ -0,0 +1,36 @@
+import cssAnimation from 'css-animation';
+
+function animate(node, show, done) {
+ let height;
+ return cssAnimation(node, 'u-motion-collapse', {
+ start() {
+ if (!show) {
+ node.style.height = `${node.offsetHeight}px`;
+ } else {
+ height = node.offsetHeight;
+ node.style.height = 0;
+ }
+ },
+ active() {
+ node.style.height = `${show ? height : 0}px`;
+ },
+ end() {
+ node.style.height = '';
+ done();
+ },
+ });
+}
+
+const animation = {
+ enter(node, done) {
+ return animate(node, true, done);
+ },
+ leave(node, done) {
+ return animate(node, false, done);
+ },
+ appear(node, done) {
+ return animate(node, true, done);
+ },
+};
+
+export default animation;
diff --git a/src/util.js b/src/util.js
new file mode 100644
index 0000000..3d0d84a
--- /dev/null
+++ b/src/util.js
@@ -0,0 +1,286 @@
+/* eslint no-loop-func: 0*/
+
+import React from 'react';
+
+export function browser(navigator) {
+ let tem;
+ const ua = navigator.userAgent;
+ let M = ua.match(/(opera|chrome|safari|firefox|msie|trident(?=\/))\/?\s*(\d+)/i) || [];
+ if (/trident/i.test(M[1])) {
+ tem = /\brv[ :]+(\d+)/g.exec(ua) || [];
+ return `IE ${tem[1] || ''}`;
+ }
+ if (M[1] === 'Chrome') {
+ tem = ua.match(/\b(OPR|Edge)\/(\d+)/);
+ if (tem) return tem.slice(1).join(' ').replace('OPR', 'Opera');
+ }
+ M = M[2] ? [M[1], M[2]] : [navigator.appName, navigator.appVersion, '-?'];
+ tem = ua.match(/version\/(\d+)/i);
+ if (tem) {
+ M.splice(1, 1, tem[1]);
+ }
+ return M.join(' ');
+}
+
+// export function getOffset(el) {
+// const obj = el.getBoundingClientRect();
+// return {
+// left: obj.left + document.body.scrollLeft,
+// top: obj.top + document.body.scrollTop,
+// width: obj.width,
+// height: obj.height
+// };
+// }
+
+// // iscroll offset
+// offset = function (el) {
+// var left = -el.offsetLeft,
+// top = -el.offsetTop;
+
+// // jshint -W084
+// while (el = el.offsetParent) {
+// left -= el.offsetLeft;
+// top -= el.offsetTop;
+// }
+// // jshint +W084
+
+// return {
+// left: left,
+// top: top
+// };
+// }
+
+/* eslint-disable */
+export function getOffset(ele) {
+ let doc, win, docElem, rect;
+
+ if (!ele.getClientRects().length) {
+ return { top: 0, left: 0 };
+ }
+
+ rect = ele.getBoundingClientRect();
+
+ if (rect.width || rect.height) {
+ doc = ele.ownerDocument;
+ win = doc.defaultView;
+ docElem = doc.documentElement;
+
+ return {
+ top: rect.top + win.pageYOffset - docElem.clientTop,
+ left: rect.left + win.pageXOffset - docElem.clientLeft
+ };
+ }
+
+ return rect;
+}
+/* eslint-enable */
+
+function getChildrenlength(children) {
+ let len = 1;
+ if (Array.isArray(children)) {
+ len = children.length;
+ }
+ return len;
+}
+
+function getSiblingPosition(index, len, siblingPosition) {
+ if (len === 1) {
+ siblingPosition.first = true;
+ siblingPosition.last = true;
+ } else {
+ siblingPosition.first = index === 0;
+ siblingPosition.last = index === len - 1;
+ }
+ return siblingPosition;
+}
+
+export function loopAllChildren(childs, callback, parent) {
+ const loop = (children, level, _parent) => {
+ const len = getChildrenlength(children);
+ React.Children.forEach(children, (item, index) => {
+ const pos = `${level}-${index}`;
+ if (item.props.children && item.type && item.type.isTreeNode) {
+ loop(item.props.children, pos, { node: item, pos });
+ }
+ callback(item, index, pos, item.key || pos, getSiblingPosition(index, len, {}), _parent);
+ });
+ };
+ loop(childs, 0, parent);
+}
+
+export function isInclude(smallArray, bigArray) {
+ return smallArray.every((ii, i) => {
+ return ii === bigArray[i];
+ });
+}
+// console.log(isInclude(['0', '1'], ['0', '10', '1']));
+
+
+// arr.length === 628, use time: ~20ms
+export function filterParentPosition(arr) {
+ const levelObj = {};
+ arr.forEach((item) => {
+ const posLen = item.split('-').length;
+ if (!levelObj[posLen]) {
+ levelObj[posLen] = [];
+ }
+ levelObj[posLen].push(item);
+ });
+ const levelArr = Object.keys(levelObj).sort();
+ for (let i = 0; i < levelArr.length; i++) {
+ if (levelArr[i + 1]) {
+ levelObj[levelArr[i]].forEach(ii => {
+ for (let j = i + 1; j < levelArr.length; j++) {
+ levelObj[levelArr[j]].forEach((_i, index) => {
+ if (isInclude(ii.split('-'), _i.split('-'))) {
+ levelObj[levelArr[j]][index] = null;
+ }
+ });
+ levelObj[levelArr[j]] = levelObj[levelArr[j]].filter(p => p);
+ }
+ });
+ }
+ }
+ let nArr = [];
+ levelArr.forEach(i => {
+ nArr = nArr.concat(levelObj[i]);
+ });
+ return nArr;
+}
+// console.log(filterParentPosition(
+// ['0-2', '0-3-3', '0-10', '0-10-0', '0-0-1', '0-0', '0-1-1', '0-1']
+// ));
+
+
+function stripTail(str) {
+ const arr = str.match(/(.+)(-[^-]+)$/);
+ let st = '';
+ if (arr && arr.length === 3) {
+ st = arr[1];
+ }
+ return st;
+}
+function splitPosition(pos) {
+ return pos.split('-');
+}
+
+export function handleCheckState(obj, checkedPositionArr, checkIt) {
+ // console.log(stripTail('0-101-000'));
+ let objKeys = Object.keys(obj);
+ // let s = Date.now();
+ objKeys.forEach((i, index) => {
+ const iArr = splitPosition(i);
+ let saved = false;
+ checkedPositionArr.forEach((_pos) => {
+ // 设置子节点,全选或全不选
+ const _posArr = splitPosition(_pos);
+ if (iArr.length > _posArr.length && isInclude(_posArr, iArr)) {
+ obj[i].halfChecked = false;
+ obj[i].checked = checkIt;
+ objKeys[index] = null;
+ }
+ if (iArr[0] === _posArr[0] && iArr[1] === _posArr[1]) {
+ // 如果
+ saved = true;
+ }
+ });
+ if (!saved) {
+ objKeys[index] = null;
+ }
+ });
+ // TODO: 循环 2470000 次耗时约 1400 ms。 性能瓶颈!
+ // console.log(Date.now()-s, checkedPositionArr.length * objKeys.length);
+ objKeys = objKeys.filter(i => i); // filter non null;
+
+ for (let pIndex = 0; pIndex < checkedPositionArr.length; pIndex++) {
+ // 循环设置父节点的 选中 或 半选状态
+ const loop = (__pos) => {
+ const _posLen = splitPosition(__pos).length;
+ if (_posLen <= 2) { // e.g. '0-0', '0-1'
+ return;
+ }
+ let sibling = 0;
+ let siblingChecked = 0;
+ const parentPosition = stripTail(__pos);
+ objKeys.forEach((i /* , index*/) => {
+ const iArr = splitPosition(i);
+ if (iArr.length === _posLen && isInclude(splitPosition(parentPosition), iArr)) {
+ sibling++;
+ if (obj[i].checked) {
+ siblingChecked++;
+ const _i = checkedPositionArr.indexOf(i);
+ if (_i > -1) {
+ checkedPositionArr.splice(_i, 1);
+ if (_i <= pIndex) {
+ pIndex--;
+ }
+ }
+ } else if (obj[i].halfChecked) {
+ siblingChecked += 0.5;
+ }
+ // objKeys[index] = null;
+ }
+ });
+ // objKeys = objKeys.filter(i => i); // filter non null;
+ const parent = obj[parentPosition];
+ // sibling 不会等于0
+ // 全不选 - 全选 - 半选
+ if (siblingChecked === 0) {
+ parent.checked = false;
+ parent.halfChecked = false;
+ } else if (siblingChecked === sibling) {
+ parent.checked = true;
+ parent.halfChecked = false;
+ } else {
+ parent.halfChecked = true;
+ parent.checked = false;
+ }
+ loop(parentPosition);
+ };
+ loop(checkedPositionArr[pIndex], pIndex);
+ }
+ // console.log(Date.now()-s, objKeys.length, checkIt);
+}
+
+export function getCheck(treeNodesStates) {
+ const halfCheckedKeys = [];
+ const checkedKeys = [];
+ const checkedNodes = [];
+ const checkedNodesPositions = [];
+ Object.keys(treeNodesStates).forEach((item) => {
+ const itemObj = treeNodesStates[item];
+ if (itemObj.checked) {
+ checkedKeys.push(itemObj.key);
+ checkedNodes.push(itemObj.node);
+ checkedNodesPositions.push({ node: itemObj.node, pos: item });
+ } else if (itemObj.halfChecked) {
+ halfCheckedKeys.push(itemObj.key);
+ }
+ });
+ return {
+ halfCheckedKeys, checkedKeys, checkedNodes, checkedNodesPositions, treeNodesStates,
+ };
+}
+
+export function getStrictlyValue(checkedKeys, halfChecked) {
+ if (halfChecked) {
+ return { checked: checkedKeys, halfChecked };
+ }
+ return checkedKeys;
+}
+
+export function arraysEqual(a, b) {
+ if (a === b) return true;
+ if (a === null || typeof a === 'undefined' || b === null || typeof b === 'undefined') {
+ return false;
+ }
+ if (a.length !== b.length) return false;
+
+ // If you don't care about the order of the elements inside
+ // the array, you should sort both arrays here.
+
+ for (let i = 0; i < a.length; ++i) {
+ if (a[i] !== b[i]) return false;
+ }
+ return true;
+}
diff --git a/test/index.test.js b/test/index.test.js
new file mode 100644
index 0000000..93a09f9
--- /dev/null
+++ b/test/index.test.js
@@ -0,0 +1,4 @@
+import React from 'react';
+import {shallow, mount, render} from 'enzyme';
+import {expect} from 'chai';
+import Tree from '../src/index';
\ No newline at end of file