UNPKG

@douyinfe/semi-ui

Version:

A modern, comprehensive, flexible design system and UI library. Connect DesignOps & DevOps. Quickly build beautiful React apps. Maintained by Douyin-fe team.

764 lines 28.7 kB
import _isUndefined from "lodash/isUndefined"; import _pick from "lodash/pick"; import _isFunction from "lodash/isFunction"; import _get from "lodash/get"; import _isEqual from "lodash/isEqual"; import _isEmpty from "lodash/isEmpty"; var __rest = this && this.__rest || function (s, e) { var t = {}; for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p) && e.indexOf(p) < 0) t[p] = s[p]; if (s != null && typeof Object.getOwnPropertySymbols === "function") for (var i = 0, p = Object.getOwnPropertySymbols(s); i < p.length; i++) { if (e.indexOf(p[i]) < 0 && Object.prototype.propertyIsEnumerable.call(s, p[i])) t[p[i]] = s[p[i]]; } return t; }; import React from 'react'; import cls from 'classnames'; import PropTypes from 'prop-types'; import ConfigContext from '../configProvider/context'; import TreeFoundation from '@douyinfe/semi-foundation/lib/es/tree/foundation'; import { convertDataToEntities, flattenTreeData, calcExpandedKeysForValues, calcMotionKeys, convertJsonToData, findKeysForValues, calcCheckedKeys, calcExpandedKeys, filterTreeData, normalizeValue, updateKeys, calcDisabledKeys } from '@douyinfe/semi-foundation/lib/es/tree/treeUtil'; import { cssClasses, strings } from '@douyinfe/semi-foundation/lib/es/tree/constants'; import BaseComponent from '../_base/baseComponent'; import { cloneDeep } from './treeUtil'; import Input from '../input/index'; import { FixedSizeList as VirtualList } from 'react-window'; import AutoSizer from './autoSizer'; import TreeContext from './treeContext'; import TreeNode from './treeNode'; import NodeList from './nodeList'; import LocaleConsumer from '../locale/localeConsumer'; import '@douyinfe/semi-foundation/lib/es/tree/tree.css'; import { IconSearch } from '@douyinfe/semi-icons'; import CheckboxGroup from '../checkbox/checkboxGroup'; export * from './interface'; const prefixcls = cssClasses.PREFIX; const treeDataNodeShape = { key: PropTypes.string, value: PropTypes.oneOfType([PropTypes.string, PropTypes.number]), label: PropTypes.any, isLeaf: PropTypes.bool, children: PropTypes.array }; treeDataNodeShape.children = PropTypes.arrayOf(PropTypes.shape(treeDataNodeShape)); class Tree extends BaseComponent { constructor(props) { super(props); this.search = value => { this.foundation.handleInputChange(value); }; this.scrollTo = scrollData => { var _a; const { key, align = 'center' } = scrollData; const { flattenNodes } = this.state; if (key) { const index = flattenNodes === null || flattenNodes === void 0 ? void 0 : flattenNodes.findIndex(node => { return node.key === key; }); index >= 0 && ((_a = this.virtualizedListRef.current) === null || _a === void 0 ? void 0 : _a.scrollToItem(index, align)); } }; this.renderEmpty = () => { const { emptyContent } = this.props; if (emptyContent) { return /*#__PURE__*/React.createElement(TreeNode, { empty: true, emptyContent: this.props.emptyContent }); } else { return /*#__PURE__*/React.createElement(LocaleConsumer, { componentName: "Tree" }, locale => /*#__PURE__*/React.createElement(TreeNode, { empty: true, emptyContent: _get(locale, 'emptyText') })); } }; this.onNodeSelect = (e, treeNode) => { this.foundation.handleNodeSelect(e, treeNode); }; this.onNodeLoad = data => new Promise(resolve => { // We need to get the latest state of loading/loaded keys this.setState(_ref => { let { loadedKeys = new Set([]), loadingKeys = new Set([]) } = _ref; return this.foundation.handleNodeLoad(loadedKeys, loadingKeys, data, resolve); }); }); this.onNodeCheck = (e, treeNode) => { this.foundation.handleNodeSelect(e, treeNode); }; this.onNodeExpand = (e, treeNode) => { this.foundation.handleNodeExpand(e, treeNode); }; this.onNodeRightClick = (e, treeNode) => { this.foundation.handleNodeRightClick(e, treeNode); }; this.onNodeDoubleClick = (e, treeNode) => { this.foundation.handleNodeDoubleClick(e, treeNode); }; this.onNodeDragStart = (e, treeNode) => { this.foundation.handleNodeDragStart(e, treeNode); }; this.onNodeDragEnter = (e, treeNode) => { this.foundation.handleNodeDragEnter(e, treeNode, this.dragNode); }; this.onNodeDragOver = (e, treeNode) => { this.foundation.handleNodeDragOver(e, treeNode, this.dragNode); }; this.onNodeDragLeave = (e, treeNode) => { this.foundation.handleNodeDragLeave(e, treeNode); }; this.onNodeDragEnd = (e, treeNode) => { this.foundation.handleNodeDragEnd(e, treeNode); }; this.onNodeDrop = (e, treeNode) => { this.foundation.handleNodeDrop(e, treeNode, this.dragNode); }; this.getTreeNodeRequiredProps = () => { const { expandedKeys, selectedKeys, checkedKeys, halfCheckedKeys, keyEntities, filteredKeys } = this.state; return { expandedKeys: expandedKeys || new Set(), selectedKeys: selectedKeys || [], checkedKeys: checkedKeys || new Set(), halfCheckedKeys: halfCheckedKeys || new Set(), filteredKeys: filteredKeys || new Set(), keyEntities }; }; this.getTreeNodeKey = treeNode => { const { data } = treeNode; const { key } = data; return key; }; this.renderTreeNode = (treeNode, ind, style) => { const { data, key } = treeNode; const treeNodeProps = this.foundation.getTreeNodeProps(key); if (!treeNodeProps) { return null; } const { keyMaps, showLine, expandIcon } = this.props; const props = _pick(treeNode, ['key', 'label', 'disabled', 'isLeaf', 'icon', 'isEnd']); const children = data[_get(keyMaps, 'children', 'children')]; !_isUndefined(children) && (props.children = children); return /*#__PURE__*/React.createElement(TreeNode, Object.assign({}, treeNodeProps, data, props, { showLine: showLine, data: data, expandIcon: expandIcon, style: _isEmpty(style) ? {} : style })); }; this.itemKey = (index, data) => { // Find the item at the specified index. const item = data[index]; // Return a value that uniquely identifies this item. return item.key; }; this.option = _ref2 => { let { index, style, data } = _ref2; return this.renderTreeNode(data[index], index, style); }; this.state = { inputValue: '', keyEntities: {}, treeData: [], flattenNodes: [], selectedKeys: [], checkedKeys: new Set(), halfCheckedKeys: new Set(), realCheckedKeys: new Set([]), motionKeys: new Set([]), motionType: 'hide', expandedKeys: new Set(props.expandedKeys), filteredKeys: new Set(), filteredExpandedKeys: new Set(), filteredShownKeys: new Set(), prevProps: null, loadedKeys: new Set(), loadingKeys: new Set(), cachedFlattenNodes: undefined, cachedKeyValuePairs: {}, disabledKeys: new Set(), dragging: false, dragNodesKeys: new Set(), dragOverNodeKey: null, dropPosition: null }; this.inputRef = /*#__PURE__*/React.createRef(); this.optionsRef = /*#__PURE__*/React.createRef(); this.foundation = new TreeFoundation(this.adapter); this.dragNode = null; this.virtualizedListRef = /*#__PURE__*/React.createRef(); } /** * Process of getDerivedStateFromProps was inspired by rc-tree * https://github.com/react-component/tree */ static getDerivedStateFromProps(props, prevState) { var _a; const { prevProps } = prevState; const { keyMaps } = props; let treeData; let keyEntities = prevState.keyEntities || {}; let valueEntities = prevState.cachedKeyValuePairs || {}; const isSeaching = Boolean(props.filterTreeNode && prevState.inputValue && prevState.inputValue.length); const newState = { prevProps: props }; const isExpandControlled = 'expandedKeys' in props; // Accept a props field as a parameter to determine whether to update the field const needUpdate = name => { const firstInProps = !prevProps && name in props; const nameHasChange = prevProps && !_isEqual(prevProps[name], props[name]); return firstInProps || nameHasChange; }; // Determine whether treeData has changed const needUpdateData = () => { const firstInProps = !prevProps && 'treeData' in props; const treeDataHasChange = prevProps && prevProps.treeData !== props.treeData; return firstInProps || treeDataHasChange; }; const needUpdateTreeData = needUpdate('treeData'); const needUpdateSimpleJson = needUpdate('treeDataSimpleJson'); // Update the data of tree in state if (needUpdateTreeData || props.draggable && needUpdateData()) { treeData = props.treeData; newState.treeData = treeData; const entitiesMap = convertDataToEntities(treeData, keyMaps); newState.keyEntities = Object.assign({}, entitiesMap.keyEntities); keyEntities = newState.keyEntities; newState.cachedKeyValuePairs = Object.assign({}, entitiesMap.valueEntities); valueEntities = newState.cachedKeyValuePairs; } else if (needUpdateSimpleJson) { // Convert treeDataSimpleJson to treeData treeData = convertJsonToData(props.treeDataSimpleJson); newState.treeData = treeData; const entitiesMap = convertDataToEntities(treeData, keyMaps); newState.keyEntities = Object.assign({}, entitiesMap.keyEntities); keyEntities = newState.keyEntities; newState.cachedKeyValuePairs = Object.assign({}, entitiesMap.valueEntities); valueEntities = newState.cachedKeyValuePairs; } // If treeData keys changes, we won't show animation if (treeData && props.motion) { if (prevProps && props.motion) { newState.motionKeys = new Set([]); newState.motionType = null; } } const dataUpdated = needUpdateSimpleJson || needUpdateTreeData; const expandAllWhenDataChange = dataUpdated && props.expandAll; if (!isSeaching) { // Update expandedKeys if (needUpdate('expandedKeys') || prevProps && needUpdate('autoExpandParent')) { newState.expandedKeys = calcExpandedKeys(props.expandedKeys, keyEntities, props.autoExpandParent || !prevProps); // only show animation when treeData does not change if (prevProps && props.motion && !treeData) { const { motionKeys, motionType } = calcMotionKeys(prevState.expandedKeys, newState.expandedKeys, keyEntities); newState.motionKeys = new Set(motionKeys); newState.motionType = motionType; if (motionType === 'hide') { // cache flatten nodes: expandedKeys changed may not be triggered by interaction newState.cachedFlattenNodes = cloneDeep(prevState.flattenNodes); } } } else if (!prevProps && (props.defaultExpandAll || props.expandAll) || expandAllWhenDataChange) { newState.expandedKeys = new Set(Object.keys(keyEntities)); } else if (!prevProps && props.defaultExpandedKeys) { newState.expandedKeys = calcExpandedKeys(props.defaultExpandedKeys, keyEntities); } else if (!prevProps && props.defaultValue) { newState.expandedKeys = calcExpandedKeysForValues(props.defaultValue, keyEntities, props.multiple, valueEntities); } else if (!prevProps && props.value) { newState.expandedKeys = calcExpandedKeysForValues(props.value, keyEntities, props.multiple, valueEntities); } else if (!isExpandControlled && dataUpdated && props.value) { // 当 treeData 已经设置具体的值,并且设置了 props.loadData ,则认为 treeData 的更新是因为 loadData 导致的 // 如果是因为 loadData 导致 treeData改变, 此时在这里重新计算 key 会导致为未选中的展开项目被收起 // 所以此时不需要重新计算 expandedKeys,因为在点击展开按钮时候已经把被展开的项添加到 expandedKeys 中 // When treeData has a specific value and props.loadData is set, it is considered that the update of treeData is caused by loadData // If the treeData is changed because of loadData, recalculating the key here will cause the unselected expanded items to be collapsed // So there is no need to recalculate expandedKeys at this time, because the expanded item has been added to expandedKeys when the expand button is clicked if (!(prevState.treeData && ((_a = prevState.treeData) === null || _a === void 0 ? void 0 : _a.length) > 0 && props.loadData)) { newState.expandedKeys = calcExpandedKeysForValues(props.value, keyEntities, props.multiple, valueEntities); } } if (!newState.expandedKeys) { delete newState.expandedKeys; } // Update flattenNodes if (treeData || newState.expandedKeys) { const flattenNodes = flattenTreeData(treeData || prevState.treeData, newState.expandedKeys || prevState.expandedKeys, keyMaps); newState.flattenNodes = flattenNodes; } } else { let filteredState; // treeData changed while searching if (treeData) { // Get filter data filteredState = filterTreeData({ treeData, inputValue: prevState.inputValue, filterTreeNode: props.filterTreeNode, filterProps: props.treeNodeFilterProp, showFilteredOnly: props.showFilteredOnly, keyEntities: newState.keyEntities, prevExpandedKeys: [...prevState.filteredExpandedKeys], keyMaps: keyMaps }); newState.flattenNodes = filteredState.flattenNodes; newState.motionKeys = new Set([]); newState.filteredKeys = filteredState.filteredKeys; newState.filteredShownKeys = filteredState.filteredShownKeys; newState.filteredExpandedKeys = filteredState.filteredExpandedKeys; } // expandedKeys changed while searching if (props.expandedKeys) { newState.filteredExpandedKeys = calcExpandedKeys(props.expandedKeys, keyEntities, props.autoExpandParent || !prevProps); if (prevProps && props.motion) { const prevKeys = prevState ? prevState.filteredExpandedKeys : new Set([]); // only show animation when treeData does not change if (!treeData) { const motionResult = calcMotionKeys(prevKeys, newState.filteredExpandedKeys, keyEntities); let { motionKeys } = motionResult; const { motionType } = motionResult; if (props.showFilteredOnly) { motionKeys = motionKeys.filter(key => prevState.filteredShownKeys.has(key)); } if (motionType === 'hide') { // cache flatten nodes: expandedKeys changed may not be triggered by interaction newState.cachedFlattenNodes = cloneDeep(prevState.flattenNodes); } newState.motionKeys = new Set(motionKeys); newState.motionType = motionType; } } newState.flattenNodes = flattenTreeData(treeData || prevState.treeData, newState.filteredExpandedKeys || prevState.filteredExpandedKeys, keyMaps, props.showFilteredOnly && prevState.filteredShownKeys); } } // Handle single selection and multiple selection in controlled mode const withObject = props.onChangeWithObject; const isMultiple = props.multiple; if (!isMultiple) { // When getting single selection, the selected node if (needUpdate('value')) { newState.selectedKeys = findKeysForValues( // In both cases whether withObject is turned on, the value is standardized to string normalizeValue(props.value, withObject, keyMaps), valueEntities, isMultiple); } else if (!prevProps && props.defaultValue) { newState.selectedKeys = findKeysForValues(normalizeValue(props.defaultValue, withObject, keyMaps), valueEntities, isMultiple); } else if (treeData) { // If `treeData` changed, we also need check it if (props.value) { newState.selectedKeys = findKeysForValues(normalizeValue(props.value, withObject, keyMaps) || '', valueEntities, isMultiple); } } } else { let checkedKeyValues; // Get the selected node during multiple selection if (needUpdate('value')) { checkedKeyValues = findKeysForValues(normalizeValue(props.value, withObject, keyMaps), valueEntities, isMultiple); } else if (!prevProps && props.defaultValue) { checkedKeyValues = findKeysForValues(normalizeValue(props.defaultValue, withObject, keyMaps), valueEntities, isMultiple); } else if (treeData) { // If `treeData` changed, we also need check it if (props.value) { checkedKeyValues = findKeysForValues(normalizeValue(props.value, withObject, keyMaps) || [], valueEntities, isMultiple); } else { checkedKeyValues = updateKeys(props.checkRelation === 'related' ? prevState.checkedKeys : prevState.realCheckedKeys, keyEntities); } } if (checkedKeyValues) { if (props.checkRelation === 'unRelated') { newState.realCheckedKeys = new Set(checkedKeyValues); } else if (props.checkRelation === 'related') { const { checkedKeys, halfCheckedKeys } = calcCheckedKeys(checkedKeyValues, keyEntities); newState.checkedKeys = checkedKeys; newState.halfCheckedKeys = halfCheckedKeys; } } } // update loadedKeys if (needUpdate('loadedKeys')) { newState.loadedKeys = new Set(props.loadedKeys); } // update disableStrictly if (treeData && props.disableStrictly && props.checkRelation === 'related') { newState.disabledKeys = calcDisabledKeys(keyEntities, keyMaps); } return newState; } get adapter() { const filterAdapter = { updateInputValue: value => { this.setState({ inputValue: value }); }, focusInput: () => { const { preventScroll } = this.props; if (this.inputRef && this.inputRef.current) { this.inputRef.current.focus({ preventScroll }); } } }; return Object.assign(Object.assign(Object.assign({}, super.adapter), filterAdapter), { updateState: states => { this.setState(Object.assign({}, states)); }, notifyExpand: (expandedKeys, _ref3) => { let { expanded: bool, node } = _ref3; this.props.onExpand && this.props.onExpand([...expandedKeys], { expanded: bool, node }); if (bool && this.props.loadData) { this.onNodeLoad(node); } }, notifySelect: (selectKey, bool, node) => { this.props.onSelect && this.props.onSelect(selectKey, bool, node); }, notifyChange: value => { this.props.onChange && this.props.onChange(value); }, notifySearch: (input, filteredExpandedKeys) => { this.props.onSearch && this.props.onSearch(input, filteredExpandedKeys); }, notifyRightClick: (e, node) => { this.props.onContextMenu && this.props.onContextMenu(e, node); }, notifyDoubleClick: (e, node) => { this.props.onDoubleClick && this.props.onDoubleClick(e, node); }, cacheFlattenNodes: bool => { this.setState({ cachedFlattenNodes: bool ? cloneDeep(this.state.flattenNodes) : undefined }); }, setDragNode: treeNode => { this.dragNode = treeNode; } }); } renderInput() { const { searchClassName, searchStyle, searchRender, searchPlaceholder, showClear } = this.props; if (searchRender === false) { return null; } const inputcls = cls(`${prefixcls}-input`); const { inputValue } = this.state; const inputProps = { value: inputValue, className: inputcls, onChange: value => this.search(value), prefix: /*#__PURE__*/React.createElement(IconSearch, null), showClear, placeholder: searchPlaceholder }; const wrapperCls = cls(`${prefixcls}-search-wrapper`, searchClassName); return /*#__PURE__*/React.createElement("div", { className: wrapperCls, style: searchStyle }, /*#__PURE__*/React.createElement(LocaleConsumer, { componentName: "Tree" }, locale => { inputProps.placeholder = searchPlaceholder || _get(locale, 'searchPlaceholder'); if (_isFunction(searchRender)) { return searchRender(Object.assign({}, inputProps)); } return /*#__PURE__*/React.createElement(Input, Object.assign({ "aria-label": 'Filter Tree', ref: this.inputRef }, inputProps)); })); } renderNodeList() { const { flattenNodes, cachedFlattenNodes, motionKeys, motionType } = this.state; const { virtualize, motion } = this.props; const { direction } = this.context; if (_isEmpty(flattenNodes)) { return undefined; } if (!virtualize || _isEmpty(virtualize)) { return /*#__PURE__*/React.createElement(NodeList, { flattenNodes: flattenNodes, flattenList: cachedFlattenNodes, motionKeys: motion ? motionKeys : new Set([]), motionType: motionType, onMotionEnd: this.onMotionEnd, renderTreeNode: this.renderTreeNode }); } return /*#__PURE__*/React.createElement(AutoSizer, { defaultHeight: virtualize.height, defaultWidth: virtualize.width }, _ref4 => { let { height, width } = _ref4; return /*#__PURE__*/React.createElement(VirtualList, { ref: this.virtualizedListRef, itemCount: flattenNodes.length, itemSize: virtualize.itemSize, height: height, width: width, itemKey: this.itemKey, itemData: flattenNodes, className: `${prefixcls}-virtual-list`, style: { direction } }, this.option); }); } render() { const { keyEntities, motionKeys, motionType, inputValue, filteredKeys, dragOverNodeKey, dropPosition, checkedKeys, realCheckedKeys } = this.state; const _a = this.props, { blockNode, className, style, filterTreeNode, disabled, icon, directory, multiple, showFilteredOnly, showLine, motion, expandAction, loadData, renderLabel, draggable, renderFullLabel, labelEllipsis, virtualize, checkRelation } = _a, rest = __rest(_a, ["blockNode", "className", "style", "filterTreeNode", "disabled", "icon", "directory", "multiple", "showFilteredOnly", "showLine", "motion", "expandAction", "loadData", "renderLabel", "draggable", "renderFullLabel", "labelEllipsis", "virtualize", "checkRelation"]); const wrapperCls = cls(`${prefixcls}-wrapper`, className); const listCls = cls(`${prefixcls}-option-list`, { [`${prefixcls}-option-list-block`]: blockNode }); const searchNoRes = Boolean(inputValue) && !filteredKeys.size; const noData = _isEmpty(keyEntities) || showFilteredOnly && searchNoRes; const ariaAttr = { role: noData ? 'none' : 'tree' }; if (ariaAttr.role === 'tree') { ariaAttr['aria-multiselectable'] = multiple ? true : false; } return /*#__PURE__*/React.createElement(TreeContext.Provider, { value: { treeDisabled: disabled, treeIcon: icon, motion, motionKeys, motionType, filterTreeNode, keyEntities, onNodeClick: this.onNodeClick, onNodeExpand: this.onNodeExpand, onNodeSelect: this.onNodeSelect, onNodeCheck: this.onNodeCheck, onNodeRightClick: this.onNodeRightClick, onNodeDoubleClick: this.onNodeDoubleClick, renderTreeNode: this.renderTreeNode, onNodeDragStart: this.onNodeDragStart, onNodeDragEnter: this.onNodeDragEnter, onNodeDragOver: this.onNodeDragOver, onNodeDragLeave: this.onNodeDragLeave, onNodeDragEnd: this.onNodeDragEnd, onNodeDrop: this.onNodeDrop, expandAction, directory, multiple, showFilteredOnly, isSearching: Boolean(inputValue), loadData, onNodeLoad: this.onNodeLoad, renderLabel, draggable, renderFullLabel, dragOverNodeKey, dropPosition, labelEllipsis: typeof labelEllipsis === 'undefined' ? virtualize : labelEllipsis } }, /*#__PURE__*/React.createElement("div", Object.assign({ "aria-label": this.props['aria-label'], className: wrapperCls, style: style }, this.getDataAttr(rest)), filterTreeNode ? this.renderInput() : null, /*#__PURE__*/React.createElement("div", Object.assign({ className: listCls }, ariaAttr), noData ? this.renderEmpty() : multiple ? (/*#__PURE__*/React.createElement(CheckboxGroup, { value: Array.from(checkRelation === 'related' ? checkedKeys : realCheckedKeys) }, this.renderNodeList())) : this.renderNodeList()))); } } Tree.contextType = ConfigContext; Tree.propTypes = { autoMergeValue: PropTypes.bool, blockNode: PropTypes.bool, className: PropTypes.string, showClear: PropTypes.bool, defaultExpandAll: PropTypes.bool, defaultExpandedKeys: PropTypes.array, defaultValue: PropTypes.oneOfType([PropTypes.string, PropTypes.array]), directory: PropTypes.bool, disabled: PropTypes.bool, emptyContent: PropTypes.node, expandAll: PropTypes.bool, expandedKeys: PropTypes.array, filterTreeNode: PropTypes.oneOfType([PropTypes.func, PropTypes.bool]), icon: PropTypes.node, onChangeWithObject: PropTypes.bool, motion: PropTypes.bool, multiple: PropTypes.bool, onChange: PropTypes.func, onExpand: PropTypes.func, onSearch: PropTypes.func, onSelect: PropTypes.func, onContextMenu: PropTypes.func, onDoubleClick: PropTypes.func, searchClassName: PropTypes.string, searchPlaceholder: PropTypes.string, searchStyle: PropTypes.object, selectedKey: PropTypes.string, showFilteredOnly: PropTypes.bool, showLine: PropTypes.bool, style: PropTypes.object, treeData: PropTypes.arrayOf(PropTypes.shape(treeDataNodeShape)), keyMaps: PropTypes.object, treeDataSimpleJson: PropTypes.object, treeNodeFilterProp: PropTypes.string, value: PropTypes.oneOfType([PropTypes.string, PropTypes.number, PropTypes.array, PropTypes.object]), virtualize: PropTypes.object, autoExpandParent: PropTypes.bool, expandAction: PropTypes.oneOf(strings.EXPAND_ACTION), searchRender: PropTypes.oneOfType([PropTypes.func, PropTypes.bool]), renderLabel: PropTypes.func, renderFullLabel: PropTypes.func, leafOnly: PropTypes.bool, loadedKeys: PropTypes.array, loadData: PropTypes.func, onLoad: PropTypes.func, disableStrictly: PropTypes.bool, draggable: PropTypes.bool, autoExpandWhenDragEnter: PropTypes.bool, hideDraggingNode: PropTypes.bool, renderDraggingNode: PropTypes.func, onDragEnd: PropTypes.func, onDragEnter: PropTypes.func, onDragLeave: PropTypes.func, onDragOver: PropTypes.func, onDragStart: PropTypes.func, onDrop: PropTypes.func, labelEllipsis: PropTypes.bool, checkRelation: PropTypes.string, 'aria-label': PropTypes.string, preventScroll: PropTypes.bool }; Tree.defaultProps = { showClear: true, disabled: false, blockNode: true, multiple: false, filterTreeNode: false, autoExpandParent: false, treeNodeFilterProp: 'label', defaultExpandAll: false, expandAll: false, onChangeWithObject: false, motion: true, leafOnly: false, showFilteredOnly: false, showLine: false, expandAction: false, disableStrictly: false, draggable: false, autoExpandWhenDragEnter: true, checkRelation: 'related', autoMergeValue: true }; Tree.TreeNode = TreeNode; export default Tree;