tree-select ui component for react
/* eslint react/no-multi-comp:0, no-console:0, no-alert: 0 */
import 'rc-tree-select/assets/index.css';
import React from 'react';
import ReactDOM from 'react-dom';
import 'rc-dialog/assets/index.css';
import Dialog from 'rc-dialog';
import TreeSelect, { TreeNode, SHOW_PARENT } from 'rc-tree-select';
import { gData } from './util';
import './demo.less';
function isLeaf(value) {
if (!value) {
return false;
}
let queues = [...gData];
while (queues.length) {
// BFS
const item = queues.shift();
if (item.value === value) {
if (!item.children) {
return true;
}
return false;
}
if (item.children) {
queues = queues.concat(item.children);
}
}
return false;
}
function findPath(value, data) {
const sel = [];
function loop(selected, children) {
for (let i = 0; i < children.length; i++) {
const item = children[i];
if (selected === item.value) {
sel.push(item);
return;
}
if (item.children) {
loop(selected, item.children, item);
if (sel.length) {
sel.push(item);
return;
}
}
}
}
loop(value, data);
return sel;
}
class Demo extends React.Component {
state = {
tsOpen: false,
visible: false,
searchValue: '0-0-0-label',
value: '0-0-0-value1',
// value: ['0-0-0-0-value', '0-0-0-1-value', '0-0-0-2-value'],
lv: { value: '0-0-0-value', label: 'spe label' },
multipleValue: [],
simpleSearchValue: 'test111',
simpleTreeData: [
{ key: 1, pId: 0, label: 'test1', value: 'test1' },
{ key: 121, pId: 0, label: 'test2', value: 'test2' },
{ key: 11, pId: 1, label: 'test11', value: 'test11' },
{ key: 12, pId: 1, label: 'test12', value: 'test12' },
{ key: 111, pId: 11, label: 'test111', value: 'test111' },
],
treeDataSimpleMode: {
id: 'key',
rootPId: 0,
},
};
onClick = () => {
this.setState({
visible: true,
});
};
onClose = () => {
this.setState({
visible: false,
});
};
onSearch = (value, ...args) => {
console.log('Do Search:', value, ...args);
this.setState({ searchValue: value });
};
onChange = (value, ...rest) => {
console.log('onChange', value, ...rest);
this.setState({ value });
};
onChangeChildren = (...args) => {
const { value: preValue } = this.state;
console.log('onChangeChildren', ...args);
const value = args[0];
const pre = value ? preValue : undefined;
this.setState({ value: isLeaf(value) ? value : pre });
};
onChangeLV = (value, ...args) => {
console.log('labelInValue', value, ...args);
if (!value) {
this.setState({ lv: undefined });
return;
}
const path = findPath(value.value, gData)
.map(i => i.label)
.reverse()
.join(' > ');
this.setState({ lv: { value: value.value, label: path } });
};
onMultipleChange = value => {
console.log('onMultipleChange', value);
this.setState({ multipleValue: value });
};
onSelect = (...args) => {
// use onChange instead
console.log(args);
};
onDropdownVisibleChange = (visible, info) => {
const { value } = this.state;
console.log(visible, value, info);
if (Array.isArray(value) && value.length > 1 && value.length < 3) {
window.alert('please select more than two item or less than one item.');
return false;
}
return true;
};
filterTreeNode = (input, child) => {
return String(child.props.title).indexOf(input) === 0;
};
render() {
const {
visible,
value,
searchValue,
tsOpen,
multipleValue,
lv,
simpleTreeData,
simpleSearchValue,
treeDataSimpleMode,
} = this.state;
return (
<div style={{ margin: 20 }}>
<h2>tree-select in dialog</h2>
<button type="button" className="btn btn-primary" onClick={this.onClick}>
show dialog
</button>
{visible ? (
<Dialog
visible={visible}
animation="zoom"
maskAnimation="fade"
onClose={this.onClose}
style={{ width: 600, height: 400, overflow: 'auto' }}
id="area"
>
<div style={{ height: 600, paddingTop: 100 }}>
<TreeSelect
getPopupContainer={triggerNode => triggerNode.parentNode}
style={{ width: 300 }}
transitionName="rc-tree-select-dropdown-slide-up"
choiceTransitionName="rc-tree-select-selection__choice-zoom"
dropdownStyle={{ maxHeight: 200, overflow: 'auto', zIndex: 1500 }}
placeholder={<i>请下拉选择</i>}
searchPlaceholder="please search"
showSearch
allowClear
treeLine
value={value}
treeData={gData}
treeNodeFilterProp="label"
filterTreeNode={false}
onSearch={this.onSearch}
onChange={this.onChange}
onSelect={this.onSelect}
/>
</div>
</Dialog>
) : null}
<h2>single select</h2>
<TreeSelect
style={{ width: 300 }}
transitionName="rc-tree-select-dropdown-slide-up"
choiceTransitionName="rc-tree-select-selection__choice-zoom"
dropdownStyle={{ maxHeight: 200, overflow: 'auto' }}
placeholder={<i>请下拉选择</i>}
searchPlaceholder="please search"
showSearch
allowClear
treeLine
searchValue={searchValue}
value={value}
treeData={gData}
treeNodeFilterProp="label"
filterTreeNode={false}
onSearch={this.onSearch}
open={tsOpen}
onChange={(val, ...args) => {
console.log('onChange', val, ...args);
if (val === '0-0-0-0-value') {
this.setState({ tsOpen: true });
} else {
this.setState({ tsOpen: false });
}
this.setState({ value: val });
}}
onDropdownVisibleChange={(v, info) => {
console.log('single onDropdownVisibleChange', v, info);
// document clicked
if (info.documentClickClose && value === '0-0-0-0-value') {
return false;
}
this.setState({
tsOpen: v,
});
return true;
}}
onSelect={this.onSelect}
/>
<h2>single select (just select children)</h2>
<TreeSelect
style={{ width: 300 }}
transitionName="rc-tree-select-dropdown-slide-up"
choiceTransitionName="rc-tree-select-selection__choice-zoom"
dropdownStyle={{ maxHeight: 200, overflow: 'auto' }}
placeholder={<i>请下拉选择</i>}
searchPlaceholder="please search"
showSearch
allowClear
treeLine
value={value}
treeData={gData}
treeNodeFilterProp="label"
filterTreeNode={false}
onChange={this.onChangeChildren}
/>
<h2>multiple select</h2>
<TreeSelect
style={{ width: 300 }}
transitionName="rc-tree-select-dropdown-slide-up"
choiceTransitionName="rc-tree-select-selection__choice-zoom"
dropdownStyle={{ maxHeight: 200, overflow: 'auto' }}
placeholder={<i>请下拉选择</i>}
searchPlaceholder="please search"
multiple
value={multipleValue}
treeData={gData}
treeNodeFilterProp="title"
onChange={this.onMultipleChange}
onSelect={this.onSelect}
allowClear
/>
<h2>check select</h2>
<TreeSelect
className="check-select"
transitionName="rc-tree-select-dropdown-slide-up"
choiceTransitionName="rc-tree-select-selection__choice-zoom"
dropdownStyle={{ height: 200, overflow: 'auto' }}
dropdownPopupAlign={{ overflow: { adjustY: 0, adjustX: 0 }, offset: [0, 2] }}
onDropdownVisibleChange={this.onDropdownVisibleChange}
placeholder={<i>请下拉选择</i>}
searchPlaceholder="please search"
treeLine
maxTagTextLength={10}
value={value}
autoClearSearchValue
treeData={gData}
treeNodeFilterProp="title"
treeCheckable
showCheckedStrategy={SHOW_PARENT}
onChange={this.onChange}
onSelect={this.onSelect}
maxTagCount={2}
maxTagPlaceholder={valueList => {
console.log('Max Tag Rest Value:', valueList);
return `${valueList.length} rest...`;
}}
/>
<h2>labelInValue & show path</h2>
<TreeSelect
style={{ width: 500 }}
transitionName="rc-tree-select-dropdown-slide-up"
choiceTransitionName="rc-tree-select-selection__choice-zoom"
dropdownStyle={{ maxHeight: 200, overflow: 'auto' }}
placeholder={<i>请下拉选择</i>}
searchPlaceholder="please search"
showSearch
allowClear
treeLine
value={lv}
labelInValue
treeData={gData}
treeNodeFilterProp="label"
filterTreeNode={false}
onChange={this.onChangeLV}
/>
<h2>use treeDataSimpleMode</h2>
<TreeSelect
style={{ width: 300 }}
dropdownStyle={{ maxHeight: 200, overflow: 'auto' }}
placeholder={<i>请下拉选择</i>}
searchPlaceholder="please search"
treeLine
maxTagTextLength={10}
searchValue={simpleSearchValue}
onSearch={val => {
this.setState({ simpleSearchValue: val });
}}
value={value}
treeData={simpleTreeData}
treeNodeFilterProp="title"
treeDataSimpleMode={treeDataSimpleMode}
treeCheckable
showCheckedStrategy={SHOW_PARENT}
onChange={this.onChange}
onSelect={this.onSelect}
/>
<h2>Testing in extreme conditions (Boundary conditions test) </h2>
<TreeSelect
style={{ width: 200 }}
dropdownStyle={{ maxHeight: 200, overflow: 'auto' }}
defaultValue="leaf1"
multiple
treeCheckable
showCheckedStrategy={SHOW_PARENT}
treeDefaultExpandAll
treeData={[
{ key: '', value: '', label: 'empty value', children: [] },
{
key: '0',
value: '0',
label: '0 label',
children: [
{ key: '00', value: '00', label: '00 label', children: [] },
{ key: '01', value: '01', label: '01 label', children: [] },
],
},
]}
onChange={(val, ...args) => console.log(val, ...args)}
/>
<h2>use TreeNode Component (not recommend)</h2>
<TreeSelect
style={{ width: 200 }}
dropdownStyle={{ maxHeight: 200, overflow: 'auto' }}
defaultValue="leaf1"
treeDefaultExpandAll
treeNodeFilterProp="title"
filterTreeNode={this.filterTreeNode}
onChange={(val, ...args) => console.log(val, ...args)}
>
<TreeNode value="" title="parent 1" key="">
<TreeNode value="parent 1-0" title="parent 1-0" key="0-1-0">
<TreeNode value="leaf1" title="my leaf" key="random" />
<TreeNode value="leaf2" title="your leaf" key="random1" disabled />
</TreeNode>
<TreeNode value="parent 1-1" title="parent 1-1" key="0-1-1">
<TreeNode
value="sss"
title={<span style={{ color: 'red' }}>sss</span>}
key="random3"
/>
<TreeNode value="same value1" title="same txtle" key="0-1-1-1">
<TreeNode
value="same value10"
title="same titlexd"
key="0-1-1-1-0"
style={{ color: 'red', background: 'green' }}
/>
</TreeNode>
</TreeNode>
</TreeNode>
<TreeNode value="same value2" title="same title" key="0-2">
<TreeNode value="2same value" title="2same title" key="0-2-0" />
</TreeNode>
<TreeNode value="same value3" title="same title" key="0-3" />
</TreeSelect>
</div>
);
}
}
ReactDOM.render(<Demo />, document.getElementById('__react-content'));