bee-tree
This commit is contained in:
commit
d53c451d4d
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
||||||
|
```
|
|
@ -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
|
||||||
|
```
|
|
@ -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( <Tree/> )}}
|
||||||
|
export default Demo;
|
|
@ -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;
|
||||||
|
}
|
|
@ -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;
|
||||||
|
}
|
|
@ -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 (
|
||||||
|
<Tree className="myCls" showLine checkable
|
||||||
|
defaultExpandedKeys={this.state.defaultExpandedKeys}
|
||||||
|
defaultSelectedKeys={this.state.defaultSelectedKeys}
|
||||||
|
defaultCheckedKeys={this.state.defaultCheckedKeys}
|
||||||
|
onSelect={this.onSelect} onCheck={this.onCheck}
|
||||||
|
>
|
||||||
|
<TreeNode title="parent 1" key="0-0">
|
||||||
|
<TreeNode title="parent 1-0" key="0-0-0" disabled>
|
||||||
|
<TreeNode title="leaf" key="0-0-0-0" disableCheckbox />
|
||||||
|
<TreeNode title="leaf" key="0-0-0-1" />
|
||||||
|
</TreeNode>
|
||||||
|
<TreeNode title="parent 1-1" key="0-0-1">
|
||||||
|
<TreeNode title={<span style={{ color: '#08c' }}>sss</span>} key="0-0-1-0" />
|
||||||
|
</TreeNode>
|
||||||
|
</TreeNode>
|
||||||
|
</Tree>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Demo1.defaultProps = defaultProps;
|
|
@ -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 (
|
||||||
|
<TreeNode key={item.key} title={item.key} disableCheckbox={item.key === '0-0-0'}>
|
||||||
|
{loop(item.children)}
|
||||||
|
</TreeNode>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
return <TreeNode key={item.key} title={item.key} />;
|
||||||
|
});
|
||||||
|
return (
|
||||||
|
<Tree
|
||||||
|
checkable
|
||||||
|
onExpand={this.onExpand} expandedKeys={this.state.expandedKeys}
|
||||||
|
autoExpandParent={this.state.autoExpandParent}
|
||||||
|
onCheck={this.onCheck} checkedKeys={this.state.checkedKeys}
|
||||||
|
onSelect={this.onSelect} selectedKeys={this.state.selectedKeys}
|
||||||
|
>
|
||||||
|
{loop(gData)}
|
||||||
|
</Tree>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
};
|
|
@ -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 <TreeNode key={item.key} title={item.key}>{loop(item.children)}</TreeNode>;
|
||||||
|
}
|
||||||
|
return <TreeNode key={item.key} title={item.key} />;
|
||||||
|
});
|
||||||
|
return (
|
||||||
|
<Tree
|
||||||
|
defaultExpandedKeys={this.state.expandedKeys}
|
||||||
|
draggable
|
||||||
|
onDragEnter={this.onDragEnter}
|
||||||
|
onDrop={this.onDrop}
|
||||||
|
>
|
||||||
|
{loop(this.state.gData)}
|
||||||
|
</Tree>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
};
|
|
@ -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 ? (
|
||||||
|
<span>
|
||||||
|
{beforeStr}
|
||||||
|
<span className="ant-tree-searchable-filter">{searchValue}</span>
|
||||||
|
{afterStr}
|
||||||
|
</span>
|
||||||
|
) : <span>{item.key}</span>;
|
||||||
|
if (item.children) {
|
||||||
|
return (
|
||||||
|
<TreeNode key={item.key} title={title}>
|
||||||
|
{loop(item.children)}
|
||||||
|
</TreeNode>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
return <TreeNode key={item.key} title={title} />;
|
||||||
|
});
|
||||||
|
return (
|
||||||
|
<div>
|
||||||
|
<FormControl
|
||||||
|
style={{ width: 200 }}
|
||||||
|
placeholder="Search"
|
||||||
|
onChange={this.onChange}
|
||||||
|
/>
|
||||||
|
<Tree
|
||||||
|
onExpand={this.onExpand}
|
||||||
|
expandedKeys={expandedKeys}
|
||||||
|
autoExpandParent={autoExpandParent}
|
||||||
|
>
|
||||||
|
{loop(gData)}
|
||||||
|
</Tree>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
|
@ -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 <TreeNode title={item.name} key={item.key}>{loop(item.children)}</TreeNode>;
|
||||||
|
}
|
||||||
|
return <TreeNode title={item.name} key={item.key} isLeaf={item.isLeaf} disabled={item.key === '0-0-0'} />;
|
||||||
|
});
|
||||||
|
const treeNodes = loop(this.state.treeData);
|
||||||
|
return (
|
||||||
|
<Tree onSelect={this.onSelect} loadData={this.onLoadData}>
|
||||||
|
{treeNodes}
|
||||||
|
</Tree>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
};
|
|
@ -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 = <i className="uf uf-arrow-down"></i>;
|
||||||
|
|
||||||
|
const CARETUP = <i className="uf uf-arrow-up"></i>;
|
||||||
|
|
||||||
|
|
||||||
|
{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 = (
|
||||||
|
<Button shape="block" onClick={ this.handleClick }>
|
||||||
|
{ caret }
|
||||||
|
{ text }
|
||||||
|
</Button>
|
||||||
|
);
|
||||||
|
const header = (
|
||||||
|
<Row>
|
||||||
|
<Col md={11}>
|
||||||
|
{ example }
|
||||||
|
</Col>
|
||||||
|
<Col md={1}>
|
||||||
|
<Button shape="icon" onClick={ this.handleClick }>
|
||||||
|
{ caret }
|
||||||
|
</Button>
|
||||||
|
</Col>
|
||||||
|
</Row>
|
||||||
|
);
|
||||||
|
return (
|
||||||
|
<Col md={12} >
|
||||||
|
<h3>{ title }</h3>
|
||||||
|
<p>{ desc }</p>
|
||||||
|
<Panel collapsible headerContent expanded={ this.state.open } colors='bordered' header={ header } footer={footer} footerStyle = {{padding: 0}}>
|
||||||
|
<pre><code className="hljs javascript">{ code }</code></pre>
|
||||||
|
</Panel>
|
||||||
|
</Col>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class DemoGroup extends Component {
|
||||||
|
constructor(props){
|
||||||
|
super(props)
|
||||||
|
}
|
||||||
|
render () {
|
||||||
|
return (
|
||||||
|
<Row>
|
||||||
|
{DemoArray.map((child,index) => {
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Demo example= {child.example} title= {child.title} code= {child.code} desc= {child.desc} key= {index}/>
|
||||||
|
)
|
||||||
|
|
||||||
|
})}
|
||||||
|
</Row>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ReactDOM.render(<DemoGroup/>, document.getElementById('tinperBeeDemo'));
|
File diff suppressed because one or more lines are too long
|
@ -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
|
|
@ -0,0 +1,5 @@
|
||||||
|
## Tree
|
||||||
|
## Code display
|
||||||
|
## API
|
||||||
|
|Property|Description|Type|Default|
|
||||||
|
|:---|:-----|:----|:------|
|
|
@ -0,0 +1,22 @@
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<meta http-equiv="Content-Type" content="text/html; charset=utf-8"/>
|
||||||
|
<title>tinper-bee demo</title>
|
||||||
|
<link rel="stylesheet" href="./dist/demo.css">
|
||||||
|
<link rel="stylesheet" href="./demo/atom-one-dark.css">
|
||||||
|
</head>
|
||||||
|
<body style="background: #eceff1">
|
||||||
|
<div id="tinperBeeDemo"></div>
|
||||||
|
<script src="//cdn.jsdelivr.net/highlight.js/9.8.0/highlight.min.js"></script>
|
||||||
|
<script>
|
||||||
|
hljs.initHighlightingOnLoad();
|
||||||
|
</script>
|
||||||
|
<script src="./node_modules/console-polyfill/index.js"></script>
|
||||||
|
<script src="./node_modules/es5-shim/es5-shim.min.js"></script>
|
||||||
|
<script src="./node_modules/es5-shim/es5-sham.min.js"></script>
|
||||||
|
<script src="./node_modules/react/dist/react-with-addons.js"></script>
|
||||||
|
<script src="./node_modules/react-dom/dist/react-dom.js"></script>
|
||||||
|
<script src="./dist/demo.js"></script>
|
||||||
|
</body>
|
||||||
|
</html>
|
|
@ -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"
|
||||||
|
}
|
||||||
|
}
|
|
@ -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<AntTreeNodeProps, {}> {
|
||||||
|
render() {
|
||||||
|
return <AntTreeNode {...this.props} />;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface AntTreeNodeEvent {
|
||||||
|
event: 'check' | 'select';
|
||||||
|
node: AntTreeNode;
|
||||||
|
checked?: boolean;
|
||||||
|
checkedNodes?: Array<AntTreeNode>;
|
||||||
|
selected?: boolean;
|
||||||
|
selectedNodes?: Array<AntTreeNode>;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface AntTreeNodeMouseEvent {
|
||||||
|
node: AntTreeNode;
|
||||||
|
event: React.MouseEventHandler<any>;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface TreeProps {
|
||||||
|
showLine?: boolean;
|
||||||
|
className?: string;
|
||||||
|
/** 是否支持多选 */
|
||||||
|
multiple?: boolean;
|
||||||
|
/** 是否自动展开父节点 */
|
||||||
|
autoExpandParent?: boolean;
|
||||||
|
/** checkable状态下节点选择完全受控(父子节点选中状态不再关联)*/
|
||||||
|
checkStrictly?: boolean;
|
||||||
|
/** 是否支持选中 */
|
||||||
|
checkable?: boolean;
|
||||||
|
/** 默认展开所有树节点 */
|
||||||
|
defaultExpandAll?: boolean;
|
||||||
|
/** 默认展开指定的树节点 */
|
||||||
|
defaultExpandedKeys?: Array<string>;
|
||||||
|
/** (受控)展开指定的树节点 */
|
||||||
|
expandedKeys?: Array<string>;
|
||||||
|
/** (受控)选中复选框的树节点 */
|
||||||
|
checkedKeys?: Array<string> | { checked: Array<string>, halfChecked: Array<string> };
|
||||||
|
/** 默认选中复选框的树节点 */
|
||||||
|
defaultCheckedKeys?: Array<string>;
|
||||||
|
/** (受控)设置选中的树节点 */
|
||||||
|
selectedKeys?: Array<string>;
|
||||||
|
/** 默认选中的树节点 */
|
||||||
|
defaultSelectedKeys?: Array<string>;
|
||||||
|
/** 展开/收起节点时触发 */
|
||||||
|
onExpand?: (expandedKeys: Array<string>, info: { node: AntTreeNode, expanded: boolean }) => void | PromiseLike<any>;
|
||||||
|
/** 点击复选框触发 */
|
||||||
|
onCheck?: (checkedKeys: Array<string>, e: AntTreeNodeEvent) => void;
|
||||||
|
/** 点击树节点触发 */
|
||||||
|
onSelect?: (selectedKeys: Array<string>, e: AntTreeNodeEvent) => void;
|
||||||
|
/** filter some AntTreeNodes as you need. it should return true */
|
||||||
|
filterAntTreeNode?: (node: AntTreeNode) => boolean;
|
||||||
|
/** 异步加载数据 */
|
||||||
|
loadData?: (node: AntTreeNode) => PromiseLike<any>;
|
||||||
|
/** 响应右键点击 */
|
||||||
|
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<TreeProps, any> {
|
||||||
|
static TreeNode = TreeNode;
|
||||||
|
|
||||||
|
static defaultProps = {
|
||||||
|
prefixCls: 'ant-tree',
|
||||||
|
checkable: false,
|
||||||
|
showIcon: false,
|
||||||
|
openAnimation: animation,
|
||||||
|
};
|
||||||
|
|
||||||
|
render() {
|
||||||
|
const props = this.props;
|
||||||
|
let checkable = props.checkable;
|
||||||
|
return (
|
||||||
|
<RcTree
|
||||||
|
{...props}
|
||||||
|
checkable={checkable ? (<span className={`${props.prefixCls}-checkbox-inner`} />) : checkable }
|
||||||
|
>
|
||||||
|
{this.props.children}
|
||||||
|
</RcTree>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
|
@ -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;
|
||||||
|
}
|
|
@ -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 <span className={classNames(switcherCls)}></span>;
|
||||||
|
}
|
||||||
|
return <span className={classNames(switcherCls)} onClick={this.onExpand}></span>;
|
||||||
|
}
|
||||||
|
|
||||||
|
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 <span ref="checkbox" className={classNames(checkboxCls)}>{customEle}</span>;
|
||||||
|
}
|
||||||
|
return (
|
||||||
|
<span ref="checkbox"
|
||||||
|
className={classNames(checkboxCls) }
|
||||||
|
onClick={this.onCheck}
|
||||||
|
>{customEle}</span>
|
||||||
|
|
||||||
|
);
|
||||||
|
/*return (
|
||||||
|
<Checkbox className={classNames(checkboxCls) } checked={props.checked} halfChecked={props.halfChecked} disabled={props.disabled || props.disableCheckbox} onClick={this.onCheck}></Checkbox>
|
||||||
|
)*/
|
||||||
|
}
|
||||||
|
|
||||||
|
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 = (
|
||||||
|
<Animate {...animProps}
|
||||||
|
showProp="data-expanded"
|
||||||
|
transitionAppear={transitionAppear}
|
||||||
|
component=""
|
||||||
|
>
|
||||||
|
{!props.expanded ? null : <ul className={classNames(cls)} data-expanded={props.expanded}>
|
||||||
|
{React.Children.map(children, (item, index) => {
|
||||||
|
return props.root.renderTreeNode(item, index, props.pos);
|
||||||
|
}, props.root)}
|
||||||
|
</ul>}
|
||||||
|
</Animate>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
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) ?
|
||||||
|
<span className={classNames(iconEleCls)}></span> : null;
|
||||||
|
const title = <span className={`${prefixCls}-title`}>{content}</span>;
|
||||||
|
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 (
|
||||||
|
<a ref="selectHandle" title={typeof content === 'string' ? content : ''} {...domProps}>
|
||||||
|
{icon}{title}
|
||||||
|
</a>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
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 <span className={classNames(cls)}></span>;
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<li {...liProps} ref="li"
|
||||||
|
className={classNames(props.className, disabledCls, dragOverCls, filterCls) }
|
||||||
|
>
|
||||||
|
{canRenderSwitcher ? this.renderSwitcher(props, expandedState) : noopSwitcher()}
|
||||||
|
{props.checkable ? this.renderCheckbox(props) : null}
|
||||||
|
{selectHandle()}
|
||||||
|
{newChildren}
|
||||||
|
</li>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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;
|
|
@ -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;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,2 @@
|
||||||
|
import Tree from './Tree';
|
||||||
|
export default Tree;
|
|
@ -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;
|
|
@ -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 (
|
||||||
|
<ul {...domProps} unselectable ref="tree">
|
||||||
|
{React.Children.map(props.children, this.renderTreeNode, this)}
|
||||||
|
</ul>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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;
|
|
@ -0,0 +1,5 @@
|
||||||
|
import Tree from './rcTree';
|
||||||
|
import TreeNode from './TreeNode';
|
||||||
|
Tree.TreeNode = TreeNode;
|
||||||
|
|
||||||
|
export default Tree;
|
|
@ -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 (
|
||||||
|
<ul {...domProps} unselectable ref="tree">
|
||||||
|
{React.Children.map(props.children, this.renderTreeNode, this)}
|
||||||
|
</ul>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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;
|
|
@ -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 <span className={classNames(switcherCls)}></span>;
|
||||||
|
}
|
||||||
|
return <span className={classNames(switcherCls)} onClick={this.onExpand}></span>;
|
||||||
|
}
|
||||||
|
|
||||||
|
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 <span ref="checkbox" className={classNames(checkboxCls)}>{customEle}</span>;
|
||||||
|
}
|
||||||
|
return (
|
||||||
|
<span ref="checkbox"
|
||||||
|
className={classNames(checkboxCls) }
|
||||||
|
onClick={this.onCheck}
|
||||||
|
>{customEle}</span>);
|
||||||
|
}
|
||||||
|
|
||||||
|
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 = (
|
||||||
|
<Animate {...animProps}
|
||||||
|
showProp="data-expanded"
|
||||||
|
transitionAppear={transitionAppear}
|
||||||
|
component=""
|
||||||
|
>
|
||||||
|
{!props.expanded ? null : <ul className={classNames(cls)} data-expanded={props.expanded}>
|
||||||
|
{React.Children.map(children, (item, index) => {
|
||||||
|
return props.root.renderTreeNode(item, index, props.pos);
|
||||||
|
}, props.root)}
|
||||||
|
</ul>}
|
||||||
|
</Animate>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
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) ?
|
||||||
|
<span className={classNames(iconEleCls)}></span> : null;
|
||||||
|
const title = <span className={`${prefixCls}-title`}>{content}</span>;
|
||||||
|
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 (
|
||||||
|
<a ref="selectHandle" title={typeof content === 'string' ? content : ''} {...domProps}>
|
||||||
|
{icon}{title}
|
||||||
|
</a>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
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 <span className={classNames(cls)}></span>;
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<li {...liProps} ref="li"
|
||||||
|
className={classNames(props.className, disabledCls, dragOverCls, filterCls) }
|
||||||
|
>
|
||||||
|
{canRenderSwitcher ? this.renderSwitcher(props, expandedState) : noopSwitcher()}
|
||||||
|
{props.checkable ? this.renderCheckbox(props) : null}
|
||||||
|
{selectHandle()}
|
||||||
|
{newChildren}
|
||||||
|
</li>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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;
|
|
@ -0,0 +1,5 @@
|
||||||
|
import Tree from './Tree';
|
||||||
|
import TreeNode from './TreeNode';
|
||||||
|
Tree.TreeNode = TreeNode;
|
||||||
|
|
||||||
|
export default Tree;
|
|
@ -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;
|
||||||
|
}
|
|
@ -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;
|
||||||
|
}
|
|
@ -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 (
|
||||||
|
<ul {...domProps} unselectable ref="tree">
|
||||||
|
{React.Children.map(props.children, this.renderTreeNode, this)}
|
||||||
|
</ul>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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;
|
|
@ -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);
|
||||||
|
}
|
|
@ -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 <span className={classNames(switcherCls)}></span>;
|
||||||
|
}
|
||||||
|
return <span className={classNames(switcherCls)} onClick={this.onExpand}></span>;
|
||||||
|
}
|
||||||
|
|
||||||
|
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 <span ref="checkbox" className={classNames(checkboxCls)}>{customEle}</span>;
|
||||||
|
}
|
||||||
|
return (
|
||||||
|
<span ref="checkbox"
|
||||||
|
className={classNames(checkboxCls) }
|
||||||
|
onClick={this.onCheck}
|
||||||
|
>{customEle}</span>);
|
||||||
|
}
|
||||||
|
|
||||||
|
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 = (
|
||||||
|
<Animate {...animProps}
|
||||||
|
showProp="data-expanded"
|
||||||
|
transitionAppear={transitionAppear}
|
||||||
|
component=""
|
||||||
|
>
|
||||||
|
{!props.expanded ? null : <ul className={classNames(cls)} data-expanded={props.expanded}>
|
||||||
|
{React.Children.map(children, (item, index) => {
|
||||||
|
return props.root.renderTreeNode(item, index, props.pos);
|
||||||
|
}, props.root)}
|
||||||
|
</ul>}
|
||||||
|
</Animate>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
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) ?
|
||||||
|
<span className={classNames(iconEleCls)}></span> : null;
|
||||||
|
const title = <span className={`${prefixCls}-title`}>{content}</span>;
|
||||||
|
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 (
|
||||||
|
<a ref="selectHandle" title={typeof content === 'string' ? content : ''} {...domProps}>
|
||||||
|
{icon}{title}
|
||||||
|
</a>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
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 <span className={classNames(cls)}></span>;
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<li {...liProps} ref="li"
|
||||||
|
className={classNames(props.className, disabledCls, dragOverCls, filterCls) }
|
||||||
|
>
|
||||||
|
{canRenderSwitcher ? this.renderSwitcher(props, expandedState) : noopSwitcher()}
|
||||||
|
{props.checkable ? this.renderCheckbox(props) : null}
|
||||||
|
{selectHandle()}
|
||||||
|
{newChildren}
|
||||||
|
</li>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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;
|
|
@ -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;
|
||||||
|
}
|
||||||
|
}
|
|
@ -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 {...this.props} />;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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 (
|
||||||
|
<RcTree
|
||||||
|
{...props}
|
||||||
|
checkable={checkable ? (<span className={`${props.prefixCls}-checkbox-inner`} />) : checkable }
|
||||||
|
>
|
||||||
|
{this.props.children}
|
||||||
|
</RcTree>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Tree.TreeNode = TreeNode;
|
||||||
|
Tree.TreeProps = TreeProps;
|
||||||
|
Tree.defaultProps = defaultProps;
|
||||||
|
export default Tree;
|
|
@ -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;
|
|
@ -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;
|
||||||
|
}
|
|
@ -0,0 +1,4 @@
|
||||||
|
import React from 'react';
|
||||||
|
import {shallow, mount, render} from 'enzyme';
|
||||||
|
import {expect} from 'chai';
|
||||||
|
import Tree from '../src/index';
|
Loading…
Reference in New Issue