import React, { useState, useRef, useEffect } from 'react'; import { storiesOf } from '@storybook/react'; // import { withKnobs, text, boolean } from '@storybook/addon-knobs'; import withPropsCombinations from 'react-storybook-addon-props-combinations'; import Tree from '../index'; import AutoSizer from '../autoSizer'; import { Icon, Toast, ButtonGroup, Button, Popover, Input } from '../../index'; import BigTree from './BigData'; import testData from './data'; import { isEmpty, isPlainObject, cloneDeep, isObject, cloneDeepWith, isArray, isEqual, debounce, omit, update, difference, } from 'lodash-es'; import {IconMapPin, IconMore, IconEdit } from '@douyinfe/semi-icons'; const TreeNode = Tree.TreeNode; const stories = storiesOf('Tree', module); const treeChildren = [ { label: '北京', value: 'beijing', key: 'beijing', }, { label: '上海', value: 'shanghai', key: 'shanghai', }, ]; const treeData1 = [ { label: '亚洲', value: 'yazhou', key: 'yazhou', children: [ { label: '中国', value: 'zhongguo', key: 'zhongguo', disabled: true, children: treeChildren, }, { label: '日本', value: 'riben', key: 'riben', children: [ { label: '东京', value: 'dongjing', key: 'dongjing', }, { label: '大阪', value: 'daban', key: 'daban', }, ], }, ], }, { label: '北美洲', value: 'beimeizhou', key: 'beimeizhou', children: [ { label: '美国', value: 'meiguo', key: 'meiguo', }, { label: '加拿大', value: 'jianada', key: 'jianada', }, ], }, ]; const treeDataWithoutValue = [ { label: '亚洲', key: 'yazhou', children: [ { label: '中国', key: 'zhongguo', disabled: true, children: treeChildren, }, { label: '日本', key: 'riben', children: [ { label: '东京', key: 'dongjing', }, { label: '大阪', key: 'daban', }, ], }, ], }, { label: '北美洲', key: 'beimeizhou', children: [ { label: '美国', key: 'meiguo', }, { label: '加拿大', key: 'jianada', }, ], }, ]; const treeDataWithIcon = [ { label: 'Asia', value: 'Asia', key: '0', icon: , children: [ { label: 'China', value: 'China', key: '0-0', icon: , }, { label: 'Japan', value: 'Japan', key: '0-1', icon: , }, ], }, ]; let opts = { content: 'Hi, Bytedance dance dance', duration: 3, }; const treeDataWithNode = [ { label: ( 亚洲 ), value: 'yazhou', key: 'yazhou', children: [ { label: '中国', value: 'zhongguo', key: 'zhongguo', children: [ { label: '北京', value: 'beijing', key: 'beijing', }, { label: '上海', value: 'shanghai', key: 'shanghai', }, ], }, { label: '日本', value: 'riben', key: 'riben', }, ], }, { label: '北美洲', value: 'beimeizhou', key: 'beimeizhou', children: [ { label: '美国', value: 'meiguo', key: 'meiguo', }, { label: '加拿大', value: 'jianada', key: 'jianada', }, ], }, ]; const treeJson = { Node1: { Child1: '0-0-1', Child2: '0-0-2', }, Node2: '0-1', }; const benchmarkSet = size => { console.time('benchmarkSet'); var set = new Set(); for (var i = 0; i < size; i++) set.add(i); for (var i = 0; i < size; i++) set.has(i); console.timeEnd('benchmarkSet'); }; const benchmarkArr = size => { console.time('benchmarkArr'); var arr = []; for (var i = 0; i < size; i++) arr.push(i); for (var i = 0; i < size; i++) arr.indexOf(i); console.timeEnd('benchmarkArr'); }; stories.add('bench mark', () => { const size = 100000; benchmarkSet(size); benchmarkArr(size); return
check console please
; }); stories.add('simple tree', () => ( console.log('expand', e, expanded, node)} // onSelect={(e, bool, node) => console.log('select', e, bool, node)} // onChange={e => console.log('change', e)} onRightClick={(e, node) => console.log(e.currentTarget, node)} onDoubleClick={(e, node) => console.log(e, node)} motion={true} /> )); stories.add('multiple tree', () => ( console.log('expand', e, expanded, node)} // onSelect={(e, bool) => console.log('select', e, bool)} onRightClick={(e, node) => console.log(e, node)} onDoubleClick={(e, node) => console.log(e, node)} onChange={e => console.log('change', e)} /> )); stories.add('searchable tree', () => ( )); stories.add('disabled tree', () => ( )); stories.add('default tree', () => (

console.log(e)} onSelect={e => console.log(e)} />
)); stories.add('expandAction', () => (

console.log(e.detail)} />
)); class Demo extends React.Component { constructor() { super(); this.state = { value: 'shanghai', }; } onChange(value) { this.setState({ value }); } render() { return ( this.onChange(e)} /> ); } } stories.add('controlled Component single', () => ); class Demo2 extends React.Component { constructor() { super(); this.state = { value: ['shanghai', 'beijing', 'zhongguo'], }; } onChange(value) { console.log(value); this.setState({ value }); } render() { console.log(this.state.value); return ( this.onChange(e)} /> ); } } stories.add('controlled Component multiple', () => ); stories.add('json tree', () => (
console.log('change', e)} onSelect={e => console.log('select', e)} />
console.log(e)} onSelect={e => console.log(e)} />
)); stories.add('icon tree', () => (

)); stories.add('directory tree', () => ); const button = ( ); const style = { display: 'flex', justifyContent: 'space-between', alignItems: 'center', }; const treeDataWithNode2 = [ { label: (
亚洲 {button}
), value: 'yazhou', key: 'yazhou', children: [ { label: (
中国 {button}
), value: 'zhongguo', key: 'zhongguo', children: [ { label: (
test {button}
), value: 'test', key: 'test', }, ], }, { label: (
日本 {button}
), value: 'riben', key: 'riben', }, ], }, ]; stories.add('tree label using node', () => (
)); const treeDataTest = [ { value: '一级标签1', label: '一级标签1', id: 1, key: '1', }, ]; class TagSideSheet2 extends React.Component { constructor() { super(); this.state = { tagList: [], visibles: false, }; this.onVisible = this.onVisible.bind(this); this.renderLabel = this.renderLabel.bind(this); this.transLabel = this.transLabel.bind(this); } componentDidMount() { let tagList = [...treeDataTest]; this.setState({ tagList, }); } onVisible(visibles) { this.setState({ visibles, }); } renderLabel(item) { const { visibles } = this.state; console.log('rendering label', visibles); return ( ); } transLabel(list) { // list = cloneDeep(list); list.forEach(item => { item.label = this.renderLabel(item); // item.key += Math.random().toString().slice(0, 5); }); return list; } render() { const { tagList = [] } = this.state; const transformedTags = this.transLabel(cloneDeep(tagList)); console.log('transformedTags', transformedTags, treeDataTest); return ; } } stories.add('tree label using popover', () => ); stories.add('defaultExpandKeys tree', () => ( <> )); stories.add('labelInValue tree', () => ( <> console.log(e)} /> console.log(e)} multiple /> console.log(e)} multiple /> )); class Test extends React.Component { constructor() { super(); this.state = { loading: false, }; } componentDidMount() { setTimeout(() => { console.log('set loading'); this.setState({ loading: true }); }, 5000); } render() { const treeDataWithNode2 = [ { label: (
亚洲 {button}
), value: 'yazhou', key: 'yazhou', children: [ { label: (
中国 {button}
), value: 'zhongguo', key: 'zhongguo', children: [ { label: (
test {button}
), value: 'test', key: 'test', }, ], }, { label: (
日本 {button}
), value: 'riben', key: 'riben', }, ], }, ]; return ; } } stories.add('setState after 5s', () => ); class DemoExpandedKeys extends React.Component { constructor() { super(); this.state = { expand: ['zhongguo', 'beimeizhou'], }; } onExpand(expand) { console.log(expand); this.setState({ expand }); } render() { return ( this.onExpand(e, rest)} /> ); } } class DemoExpandeKeysMulti extends React.Component { constructor() { super(); this.state = { expand: ['zhongguo'], }; } onExpand(expand) { console.log(expand); this.setState({ expand }); } render() { return ( this.onExpand(e)} /> ); } } stories.add('expandedKeys', () => ( <>
)); class DmExpandedKeys extends React.Component { constructor() { super(); this.state = { treeData: [ { key: '0', label: 'item-0', value: '0', }, ], }; this.add = this.add.bind(this); } add() { let itemLength = Math.floor(Math.random() * 5) + 1; let treeData = new Array(itemLength).fill(0).map((v, i) => { let length = Math.floor(Math.random() * 3); let children = new Array(length).fill(0).map((cv, ci) => { let child = { key: `${i}-${ci}`, label: `Leaf-${i}-${ci}`, value: `${i}-${ci}`, }; return child; }); let item = { key: `${i}`, label: `Item-${i}`, value: `${i}`, children, }; return item; }); this.setState({ treeData }); } render() { const { treeData } = this.state; return ( <>
); } } stories.add('dynamic expandKeys', () => ( <> )); class DmSelectedKeys extends React.Component { constructor() { super(); this.state = { treeData: [ { key: '0', label: 'item-0', value: '0', }, ], }; this.add = this.add.bind(this); } add() { let itemLength = Math.floor(Math.random() * 5) + 1; let treeData = new Array(itemLength).fill(0).map((v, i) => { let length = Math.floor(Math.random() * 3); let children = new Array(length).fill(0).map((cv, ci) => { let child = { key: `${i}-${ci}`, label: `Leaf-${i}-${ci}`, value: `${i}-${ci}`, }; return child; }); let item = { key: `${i}`, label: `Item-${i}`, value: `${i}`, children, }; return item; }); this.setState({ treeData }); } render() { const { treeData } = this.state; return ( <> console.log(e)} />
); } } // the demo will not work anymore as value does not exist when the tree mounts stories.add('dynamic selectedKey', () => ( <> )); stories.add('large amount of data', () => ( <> )); stories.add('autosizer', () => (
{({ height, width }) => (
{`width:${width}, height:${height}`}
)}
)); const MotionCustomLabelDemo = () => { const treeData = [ { label: '亚洲', value: 'Asia', key: '0', children: [ { label: '中国', value: 'China', key: '0-0', children: [ { label: '北京', value: 'Beijing', key: '0-0-0', }, { label: '上海', value: 'Shanghai', key: '0-0-1', }, ], }, ], }, { label: '北美洲', value: 'North America', key: '1', }, ]; const [hoverKey, setHoverKey] = useState(); const cusLabel = (list = []) => { const recusive = (list = []) => { if (!list.length) { return; } list.forEach(item => { const { type, label, key } = item; item.label = (
setHoverKey(key)}> {label} {hoverKey === key ? : null}
); recusive(item.children); }); }; recusive(list); return list; }; return ; }; stories.add('motion custom label', () => ); const AutoParentDemo = () => { const [expandedKeys, setExpandedKeys] = useState(['beimeizhou']); const [selectedKeys, setSelectedKeys] = useState(['beimeizhou']); const [autoExpandParent, setAutoExpandParent] = useState(true); const onExpand = expandedKeys => { console.log('onExpand', expandedKeys); // if not set autoExpandParent to false, if children expanded, parent can not collapse. // or, you can remove all expanded children keys. setExpandedKeys(expandedKeys); setAutoExpandParent(false); }; const onSelect = (selectedKeys, info) => { console.log('onSelect:', info); setSelectedKeys(selectedKeys); }; return (
); }; stories.add('autoExpandParent', () => ( <> )); const findDescendantKeys = node => { let res = [node.key]; const findChild = item => { if (!item) return; const { children } = item; if (children && children.length) { children.forEach(child => { res.push(child.key); findChild(child); }); } }; findChild(node); return res; }; class DyTreeWithExpandControlled extends React.Component { constructor() { super(); this.state = { treeData: [ { key: '0', label: 'item-0', value: '0', }, ], expandedKeys: [], autoExpandParent: false, inputValue: '', collapsedKeys: new Set([]), }; } componentDidUpdate(prevProps, prevState) { if ( !isEqual(prevState.treeData, this.state.treeData) || !isEqual(prevState.inputValue, this.state.inputValue) ) { const { treeData, inputValue, collapsedKeys } = this.state; let filteredKeys = []; const findFilteredKey = arr => { arr.forEach(item => { if (item.label.indexOf(inputValue) > -1) { filteredKeys.push(item.key); } if (item.children) { findFilteredKey(item.children); } }); }; findFilteredKey(treeData); const expanded = difference(filteredKeys, [...collapsedKeys]); this.setState({ expandedKeys: expanded, autoExpandParent: true, }); } } add = () => { let itemLength = Math.floor(Math.random() * 5) + 1; let treeData = new Array(itemLength).fill(0).map((v, i) => { let length = Math.floor(Math.random() * 3) + 1; let children = new Array(length).fill(0).map((cv, ci) => { let child = { key: `${i}-${ci}`, label: `Leaf-${i}-${ci}`, value: `${i}-${ci}`, }; return child; }); let item = { key: `${i}`, label: `Item-${i}`, value: `${i}`, children, }; return item; }); this.setState({ treeData }); }; search = val => { this.setState({ inputValue: val }); }; onExpand = (keys, { expanded, node }) => { let collapsed = this.state.collapsedKeys; let desKeys = findDescendantKeys(node); if (!expanded) { desKeys.forEach(key => collapsed.add(key)); } else { desKeys.forEach(key => collapsed.has(key) && collapsed.delete(key)); } this.setState({ expandedKeys: keys, autoExpandParent: false, collapsedKeys: collapsed, }); }; render() { const { treeData, expandedKeys, autoExpandParent } = this.state; return (

); } } stories.add('dynamic treeData with searchValue and controlled expand', () => ); const CusSearchRender = () => { const [inputValue, setInputValue] = useState(''); const ref = useRef(); const setValue = value => { setInputValue(value); ref.current.search(value); }; return ( ( setValue(value)} /> )} /> ); }; stories.add('CusSearchRender', () => ( <> )); const RefSearch = () => { const ref = useRef(); const treeData = [ { label: 'Asia', value: 'Asia', key: '0', children: [ { label: 'China', value: 'China', key: '0-0', children: [ { label: 'Beijing', value: 'Beijing', key: '0-0-0', }, { label: 'Shanghai', value: 'Shanghai', key: '0-0-1', }, ], }, { label: 'Japan', value: 'Japan', key: '0-1', children: [ { label: 'Osaka', value: 'Osaka', key: '0-1-0', }, ], }, ], }, { label: 'North America', value: 'North America', key: '1', children: [ { label: 'United States', value: 'United States', key: '1-0', }, { label: 'Canada', value: 'Canada', key: '1-1', }, ], }, ]; return (
ref.current.search(v)} /> null} />
); }; stories.add('RefSearch', () => ( <> )); const initTreeDate = [ { label: 'Expand to load', key: '0', }, { label: 'Expand to load', key: '1', }, { label: 'Tree Node', key: '2', isLeaf: true, }, ]; function updateTreeData(list, key, children) { return list.map(node => { if (node.key === key) { return { ...node, children }; } if (node.children) { return { ...node, children: updateTreeData(node.children, key, children) }; } return node; }); } const LoadingTreeDemo = () => { const [treeData, setTreeData] = useState(initTreeDate); function onLoadData({ key, children }) { return new Promise(resolve => { if (children) { resolve(); return; } setTimeout(() => { setTreeData(origin => updateTreeData(origin, key, [ { label: 'Child Node', key: `${key}-0`, }, { label: 'Child Node', key: `${key}-1`, }, ]) ); resolve(); }, 1000); }); } return ; }; stories.add('loading', () => ( <> )); const LoadingWithSearch = () => { const [treeData, setTreeData] = useState(initTreeDate); function onLoadData({ key, children }) { return new Promise(resolve => { if (children) { resolve(); return; } setTimeout(() => { setTreeData(origin => updateTreeData(origin, key, [ { label: 'Child Node', key: `${key}-0`, }, { label: 'Child Node', key: `${key}-1`, }, ]) ); resolve(); }, 1000); }); } return ; }; stories.add('Loading with search', () => ( <> )); const DisabledStrictly = () => { return ( <> disable shanghai(checked), China
disable shanghai(checked), beijing(checked) disable shanghai(checked)
disable shanghai disable China(checked) - Shanghai disable China ) } stories.add('disableStrictly', () => ); const ActionTree = () => { let initData = [{ label: 'Asia', value: 'Asia', key: 'asia', children: [{ label: 0, value: `${0}`, key: `${0}`, }, { label: 1, value: `${1}`, key: `${1}`, }, { label: 2, value: `${2}`, key: `${2}`, }, { label: 3, value: `${3}`, key: `${3}`, }, { label: 4, value: `${4}`, key: `${4}`, }] }]; const [data, setData] = useState(initData); const remove = key => { let ind = data[0].children.findIndex(item => item.key === key); if (ind >= 0) { let items = cloneDeep(data); items[0].children.splice(ind, 1); setData(items); } } return ( (
{label}
)} /> ); }; stories.add('Delete or Add Child Node', () => ); const MutipleHLTree = () => { const [selected, setSelected] = useState(new Set()); const [selectedThroughParent, setSelectedThroughParent] = useState(new Set()); const findDescendantKeys = (node) => { let res = [node.key]; const findChild = item => { if (!item) return; const { children } = item; if (children && children.length) { children.forEach(child => { res.push(child.key); findChild(child); }); } }; findChild(node); return res; } const handleSelect = (key, bool, node) => { setSelected(new Set([key])); const descendantKeys = findDescendantKeys(node); setSelectedThroughParent(new Set(descendantKeys)); } const renderLabel = ({ className, data, onClick, expandIcon }) => { const { label, icon, key } = data; const isLeaf = !(data.children && data.children.length); const style = { backgroundColor: selected.has(key) ? 'rgba(var(--semi-blue-0), 1)' : selectedThroughParent.has(key) ? 'rgba(var(--semi-blue-0), .5)' : 'transparent' } return (
  • {isLeaf ? null : expandIcon} {icon ? icon : null} {label}
  • ) } const treeStyle = { width: 260, height: 420, border: '1px solid var(--semi-color-border)' } return ( ) } stories.add('renderOuterLable', () => ) stories.add('tree without value props', () => ( console.log(args)} /> )); 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({ label: 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 DnDTree = () => { function generateData(x = 3, y = 2, z = 1, gData = []) { // x:每一级下的节点总数。y:每级节点里有y个节点、存在子节点。z:树的level层级数(0表示一级) function _loop(_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({ label: `${key}`, key: `${key}-key`, value: `${key}-value` }); if (i < y) { children.push(key); } } if (_level < 0) { return tns; } const __level = _level - 1; children.forEach((key, index) => { tns[index].children = []; return _loop(__level, key, tns[index].children); }); return null; } _loop(z); return gData; } const initialData = generateData(); const [treeData, setTreeData] = useState(initialData); // const [expandedKeys, setExpandedKeys] = useState(['zhongguo']); function onDragEnter(info) { console.log(info); // if in controlled expandedKeys mode // setExpandedKeys(info.expandedKeys) } function onDrop(info) { const { dropToGap, node, dragNode } = info; const dropKey = node.key; const dragKey = dragNode.key; const dropPos = node.pos.split('-'); const dropPosition = info.dropPosition - Number(dropPos[dropPos.length - 1]); const data = [...treeData]; const loop = (data, key, callback) => { data.forEach((item, ind, arr) => { if (item.key === key) return callback(item, ind, arr); if (item.children) return loop(item.children, key, callback); }) } let dragObj; loop(data, dragKey, (item, ind, arr) => { arr.splice(ind, 1); dragObj = item; }) if (!dropToGap) { // inset into the dropPosition loop(data, dropKey, (item, ind, arr) => { item.children = item.children || []; item.children.push(dragObj) }) } else if (dropPosition === 1 && node.children && node.expanded) { // has children && expanded and drop into the node bottom gap // insert to the top 这里我们添加在头部,可以是任意位置 loop(data, dropKey, item => { item.children = item.children || []; item.children.unshift(dragObj) }) } else { let dropNodeInd; let dropNodePosArr; loop(data, dropKey, (item, ind, arr) => { dropNodePosArr = arr; dropNodeInd = ind; }) if (dropPosition === -1) { // insert to top dropNodePosArr.splice(dropNodeInd, 0, dragObj) } else { // insert to bottom dropNodePosArr.splice(dropNodeInd + 1, 0, dragObj) } } setTreeData(data) } return ; }; stories.add('draggable Tree', () => ) const TestTree = () => { return ( ); }; stories.add('draggable', () => ); stories.add('renderFullLabel with draggable', () => { const [selected, setSelected] = useState(new Set()); const [selectedThroughParent, setSelectedThroughParent] = useState(new Set()); const defaultTreeData = [ { label: '黑色固定按钮', key: 'fix-btn-0' }, { label: '模块', key: 'module-0', children: [ { label: '可自由摆放的组件', key: 'free-compo-0', }, { label: '分栏容器', key: 'split-col-0', children: [ { label: '按钮组件', key: 'btn-0' }, { label: '按钮组件', key: 'btn-1' } ] }, ], }, { label: '模块', key: 'module-1', children: [ { label: '自定义组件', key: 'cus-0' } ] } ] const [treeData, setTreeData] = useState(defaultTreeData); const onDrop = (info) => { const { dropToGap, node, dragNode } = info; const dropKey = node.key; const dragKey = dragNode.key; const dropPos = node.pos.split('-'); const dropPosition = info.dropPosition - Number(dropPos[dropPos.length - 1]); const data = [...treeData]; const loop = (data, key, callback) => { data.forEach((item, ind, arr) => { if (item.key === key) return callback(item, ind, arr); if (item.children) return loop(item.children, key, callback); }) } let dragObj; loop(data, dragKey, (item, ind, arr) => { arr.splice(ind, 1); dragObj = item; }) if (!dropToGap) { loop(data, dropKey, (item, ind, arr) => { item.children = item.children || []; item.children.push(dragObj) }) } else if (dropPosition === 1 && node.children && node.expanded) { loop(data, dropKey, item => { item.children = item.children || []; item.children.unshift(dragObj) }) } else { let dropNodeInd; let dropNodePosArr; loop(data, dropKey, (item, ind, arr) => { dropNodePosArr = arr; dropNodeInd = ind; }) if (dropPosition === -1) { dropNodePosArr.splice(dropNodeInd, 0, dragObj) } else { dropNodePosArr.splice(dropNodeInd + 1, 0, dragObj) } } setTreeData(data) } const findDescendantKeys = (node) => { let res = [node.key]; const findChild = item => { if (!item) return; const { children } = item; if (children && children.length) { children.forEach(child => { res.push(child.key); findChild(child); }); } }; findChild(node); return res; } const handleSelect = (key, bool, node) => { setSelected(new Set([key])); const descendantKeys = findDescendantKeys(node); setSelectedThroughParent(new Set(descendantKeys)); } const renderLabel = ({ className, data, onClick, expandIcon }) => { const { label, icon, key } = data; const isLeaf = !(data.children && data.children.length); const style = { backgroundColor: selected.has(key) ? 'rgba(var(--semi-blue-0), 1)' : selectedThroughParent.has(key) ? 'rgba(var(--semi-blue-0), .5)' : 'transparent' } return (
  • {isLeaf ? : expandIcon} {icon ? icon : null} {label}
  • ) } const treeStyle = { height: 420, border: '1px solid var(--semi-color-border)' } return ; } );