diff --git a/components/tree-select/index.en-US.md b/components/tree-select/index.en-US.md index 7332d25ad7..709df63e1a 100644 --- a/components/tree-select/index.en-US.md +++ b/components/tree-select/index.en-US.md @@ -12,7 +12,7 @@ | dropdownMatchSelectWidth | Determine whether the dropdown menu and the select input are the same width | boolean | true | | dropdownStyle | To set the style of the dropdown menu | object | - | | filterTreeNode | Whether to filter treeNodes by input value. The value of `treeNodeFilterProp` is used for filtering by default. | boolean\|Function(inputValue: string, treeNode: TreeNode) (should return boolean) | Function | -| getPopupContainer | To set the container of the dropdown menu. The default is to create a `div` element in `body`, you can reset it to the scrolling area and make a relative reposition. [example](https://codepen.io/afc163/pen/zEjNOy?editors=0010) | Function(triggerNode) | () => document.body | +| getPopupContainer | To set the container of the dropdown menu. The default is to create a `div` element in `body`, you can reset it to the scrolling area and make a relative reposition. | Function(triggerNode) | () => document.body | | labelInValue | whether to embed label in value, turn the format of value from `string` to `{value: string, label: VNode, halfChecked: string[]}` | boolean | false | | loadData | Load data asynchronously. | function(node) | - | | multiple | Support multiple or not, will be `true` when enable `treeCheckable`. | boolean | false | @@ -27,8 +27,8 @@ | treeData | Data of the treeNodes, manual construction work is no longer needed if this property has been set(ensure the Uniqueness of each value) | array<{ value, label, children, [disabled, disableCheckbox, selectable] }> | \[] | | treeDataSimpleMode | Enable simple mode of treeData.(treeData should like this: [{id:1, pId:0, value:'1', label:"test1",...},...], pId is parent node's id) | false\|Array<{ id: string, pId: string, rootPId: null }> | false | | treeDefaultExpandAll | Whether to expand all treeNodes by default | boolean | false | -| treeDefaultExpandedKeys | Default expanded treeNodes | string\[] | - | -| treeExpandedKeys | Set expanded keys | string\[] | - | +| treeDefaultExpandedKeys | Default expanded treeNodes | string\[] \| number\[] | - | +| treeExpandedKeys(.sync) | Set expanded keys | string\[] \| number\[] | - | | treeNodeFilterProp | Will be used for filtering if `filterTreeNode` returns true | string | 'value' | | treeNodeLabelProp | Will render as content of select | string | 'title' | | value(v-model) | To set the current selected treeNode(s). | string\|string\[] | - | @@ -58,7 +58,7 @@ | disableCheckbox | Disables the checkbox of the treeNode | boolean | false | | disabled | Disabled or not | boolean | false | | isLeaf | Leaf node or not | boolean | false | -| key | Required property, should be unique in the tree | string | - | +| key | Required property, should be unique in the tree | string \| number | - | | title | Content showed on the treeNodes | string\|slot | '---' | | value | Will be treated as `treeNodeFilterProp` by default, should be unique in the tree | string | - | | scopedSlots | When using treeNodes, you can use this property to configure the properties that support the slot, such as `scopedSlots: { title: 'XXX'}` | object | - | diff --git a/components/tree-select/index.zh-CN.md b/components/tree-select/index.zh-CN.md index bb252c21cd..4d879304d3 100644 --- a/components/tree-select/index.zh-CN.md +++ b/components/tree-select/index.zh-CN.md @@ -27,8 +27,8 @@ | treeData | treeNodes 数据,如果设置则不需要手动构造 TreeNode 节点(value 在整个树范围内唯一) | array<{value, label, children, [disabled, disableCheckbox, selectable]}> | \[] | | treeDataSimpleMode | 使用简单格式的 treeData,具体设置参考可设置的类型 (此时 treeData 应变为这样的数据结构: [{id:1, pId:0, value:'1', label:"test1",...},...], `pId` 是父节点的 id) | false\|Array<{ id: string, pId: string, rootPId: null }> | false | | treeDefaultExpandAll | 默认展开所有树节点 | boolean | false | -| treeDefaultExpandedKeys | 默认展开的树节点 | string\[] | - | -| treeExpandedKeys | 设置展开的树节点 | string\[] | - | +| treeDefaultExpandedKeys | 默认展开的树节点 | string\[] \| number\[] | - | +| treeExpandedKeys(.sync) | 设置展开的树节点 | string\[] \| number\[] | - | | treeNodeFilterProp | 输入项过滤对应的 treeNode 属性 | string | 'value' | | treeNodeLabelProp | 作为显示的 prop 设置 | string | 'title' | | value(v-model) | 指定当前选中的条目 | string/string\[] | - | @@ -59,7 +59,7 @@ | disableCheckbox | 禁掉 checkbox | boolean | false | | disabled | 是否禁用 | boolean | false | | isLeaf | 是否是叶子节点 | boolean | false | -| key | 此项必须设置(其值在整个树范围内唯一) | string | - | +| key | 此项必须设置(其值在整个树范围内唯一) | string \| number | - | | title | 树节点显示的内容 | string\|slot | '---' | | value | 默认根据此属性值进行筛选(其值在整个树范围内唯一) | string | - | | scopedSlots | 使用treeData时,可以通过该属性配置支持slot的属性,如 `scopedSlots: { title: 'XXX'}` | object | - | diff --git a/components/tree-select/interface.jsx b/components/tree-select/interface.jsx index 93bfc6870e..839dfc6c69 100644 --- a/components/tree-select/interface.jsx +++ b/components/tree-select/interface.jsx @@ -20,7 +20,7 @@ export const TreeSelectProps = () => ({ maxTagCount: PropTypes.number, maxTagPlaceholder: PropTypes.any, value: PropTypes.oneOfType([PropTypes.string, PropTypes.object, PropTypes.array]), - defaultValue: PropTypes.oneOfType([PropTypes.string, PropTypes.array]), + defaultValue: PropTypes.oneOfType([PropTypes.string, PropTypes.object, PropTypes.array]), multiple: PropTypes.bool, // onSelect: (value: any) => void, // onChange: (value: any, label: any) => void, @@ -36,8 +36,8 @@ export const TreeSelectProps = () => ({ dropdownClassName: PropTypes.string, dropdownMatchSelectWidth: PropTypes.bool, treeDefaultExpandAll: PropTypes.bool, - treeExpandedKeys: PropTypes.arrayOf(String), - treeDefaultExpandedKeys: PropTypes.arrayOf(String), + treeExpandedKeys: PropTypes.array, + treeDefaultExpandedKeys: PropTypes.array, treeNodeFilterProp: PropTypes.string, treeNodeLabelProp: PropTypes.string, }) diff --git a/components/tree/DirectoryTree.jsx b/components/tree/DirectoryTree.jsx index 37bd531807..32f9c3f546 100644 --- a/components/tree/DirectoryTree.jsx +++ b/components/tree/DirectoryTree.jsx @@ -144,6 +144,7 @@ export default { } newState._selectedKeys = newSelectedKeys + this.$emit('update:selectedKeys', newSelectedKeys) this.$emit('select', newSelectedKeys, event) this.setUncontrolledState(newState) @@ -188,7 +189,7 @@ export default { ref: 'tree', class: `${prefixCls}-directory`, on: { - ...this.$listeners, + ...omit(this.$listeners, ['update:selectedKeys']), select: this.onSelect, click: this.onClick, doubleclick: this.onDoubleClick, diff --git a/components/tree/Tree.jsx b/components/tree/Tree.jsx index 28bc9bde46..e83bfbe671 100644 --- a/components/tree/Tree.jsx +++ b/components/tree/Tree.jsx @@ -23,25 +23,25 @@ function TreeProps () { /** 默认展开对应树节点 */ defaultExpandParent: PropTypes.bool, /** 默认展开指定的树节点 */ - defaultExpandedKeys: PropTypes.arrayOf(String), + defaultExpandedKeys: PropTypes.array, /** (受控)展开指定的树节点 */ - expandedKeys: PropTypes.arrayOf(String), + expandedKeys: PropTypes.array, /** (受控)选中复选框的树节点 */ checkedKeys: PropTypes.oneOfType( [ - PropTypes.arrayOf(PropTypes.string), + PropTypes.array, PropTypes.shape({ - checked: PropTypes.arrayOf(String), - halfChecked: PropTypes.arrayOf(String), + checked: PropTypes.array, + halfChecked: PropTypes.array, }).loose, ] ), /** 默认选中复选框的树节点 */ - defaultCheckedKeys: PropTypes.arrayOf(String), + defaultCheckedKeys: PropTypes.array, /** (受控)设置选中的树节点 */ - selectedKeys: PropTypes.arrayOf(String), + selectedKeys: PropTypes.array, /** 默认选中的树节点 */ - defaultSelectedKeys: PropTypes.arrayOf(String), + defaultSelectedKeys: PropTypes.array, selectable: PropTypes.bool, /** 展开/收起节点时触发 */ // onExpand: (expandedKeys: string[], info: AntTreeNodeExpandedEvent) => void | PromiseLike, @@ -57,7 +57,7 @@ function TreeProps () { filterAntTreeNode: PropTypes.func, /** 异步加载数据 */ loadData: PropTypes.func, - loadedKeys: PropTypes.arrayOf(String), + loadedKeys: PropTypes.array, // onLoaded: (loadedKeys: string[], info: { event: 'load', node: AntTreeNode; }) => void, /** 响应右键点击 */ // onRightClick: (options: AntTreeNodeMouseEvent) => void, @@ -183,9 +183,7 @@ export default { __propsSymbol__: Symbol(), switcherIcon: this.renderSwitcherIcon, }, - on: { - ...this.$listeners, - }, + on: this.$listeners, ref: 'tree', class: !showIcon && `${prefixCls}-icon-hide`, } diff --git a/components/tree/index.en-US.md b/components/tree/index.en-US.md index 32af7c5865..7dee2b0443 100644 --- a/components/tree/index.en-US.md +++ b/components/tree/index.en-US.md @@ -8,21 +8,21 @@ | treeData | treeNode of tree, please use `treeNodes` before v1.1.4 | array | - | | autoExpandParent | Whether to automatically expand a parent treeNode | boolean | true | | checkable | Adds a `Checkbox` before the treeNodes | boolean | false | -| checkedKeys(v-model) | (Controlled) Specifies the keys of the checked treeNodes (PS: When this specifies the key of a treeNode which is also a parent treeNode, all the children treeNodes of will be checked; and vice versa, when it specifies the key of a treeNode which is a child treeNode, its parent treeNode will also be checked. When `checkable` and `checkStrictly` is true, its object has `checked` and `halfChecked` property. Regardless of whether the child or parent treeNode is checked, they won't impact each other. | string\[] \| {checked: string\[], halfChecked: string\[]} | \[] | +| checkedKeys(v-model) | (Controlled) Specifies the keys of the checked treeNodes (PS: When this specifies the key of a treeNode which is also a parent treeNode, all the children treeNodes of will be checked; and vice versa, when it specifies the key of a treeNode which is a child treeNode, its parent treeNode will also be checked. When `checkable` and `checkStrictly` is true, its object has `checked` and `halfChecked` property. Regardless of whether the child or parent treeNode is checked, they won't impact each other. | string\[] \| number\[] \| {checked: string\[] \| number\[], halfChecked: string\[] \| number\[]} | \[] | | checkStrictly | Check treeNode precisely; parent treeNode and children treeNodes are not associated | boolean | false | -| defaultCheckedKeys | Specifies the keys of the default checked treeNodes | string\[] | \[] | +| defaultCheckedKeys | Specifies the keys of the default checked treeNodes | string\[] \| number\[] | \[] | | defaultExpandAll | Whether to expand all treeNodes by default | boolean | false | -| defaultExpandedKeys | Specify the keys of the default expanded treeNodes | string\[] | \[] | +| defaultExpandedKeys | Specify the keys of the default expanded treeNodes | string\[] \| number\[] | \[] | | defaultExpandParent | auto expand parent treeNodes when init | bool | true | -| defaultSelectedKeys | Specifies the keys of the default selected treeNodes | string\[] | \[] | +| defaultSelectedKeys | Specifies the keys of the default selected treeNodes | string\[] \| number\[] | \[] | | disabled | whether disabled the tree | bool | false | | draggable | Specifies whether this Tree is draggable (IE > 8) | boolean | false | -| expandedKeys(.sync) | (Controlled) Specifies the keys of the expanded treeNodes | string\[] | \[] | +| expandedKeys(.sync) | (Controlled) Specifies the keys of the expanded treeNodes | string\[] \| number\[] | \[] | | filterTreeNode | Defines a function to filter (highlight) treeNodes. When the function returns `true`, the corresponding treeNode will be highlighted | function(node) | - | | loadData | Load data asynchronously | function(node) | - | -| loadedKeys | (Controlled) Set loaded tree nodes. Need work with `loadData` | string\[] | \[] | +| loadedKeys | (Controlled) Set loaded tree nodes. Need work with `loadData` | string\[] \| number\[] | \[] | | multiple | Allows selecting multiple treeNodes | boolean | false | -| selectedKeys(.sync) | (Controlled) Specifies the keys of the selected treeNodes | string\[] | - | +| selectedKeys(.sync) | (Controlled) Specifies the keys of the selected treeNodes | string\[] \| number\[] | - | | showIcon | Shows the icon before a TreeNode's title. There is no default style; you must set a custom style for it if set to `true` | boolean | false | | showLine | Shows a connecting line | boolean | false | @@ -54,7 +54,7 @@ One of the Tree `treeNode` prop for describing the tree's node, TreeNode has the | disabled | Disables the treeNode | boolean | false | | icon | customize icon. When you pass component, whose render will receive full TreeNode props as component props | slot\|slot-scope | - | | isLeaf | Determines if this is a leaf node(effective when `loadData` is specified) | boolean | false | -| key | Used with (default)ExpandedKeys / (default)CheckedKeys / (default)SelectedKeys. P.S.: It must be unique in all of treeNodes of the tree! | string | internal calculated position of treeNode | +| key | Used with (default)ExpandedKeys / (default)CheckedKeys / (default)SelectedKeys. P.S.: It must be unique in all of treeNodes of the tree! | string \| number | internal calculated position of treeNode | | selectable | Set whether the treeNode can be selected | boolean | true | | title | Title | string\|slot\|slot-scope | '---' | | slots | When using treeNodes, you can use this property to configure the properties that support the slot, such as `slots: { title: 'XXX'}` | object | - | diff --git a/components/tree/index.zh-CN.md b/components/tree/index.zh-CN.md index 76ef142e38..84a755070a 100644 --- a/components/tree/index.zh-CN.md +++ b/components/tree/index.zh-CN.md @@ -8,21 +8,21 @@ | treeData | 节点的配置描述,具体项见下表, 1.1.4之前的版本使用`treeNodes` | array | -- | | autoExpandParent | 是否自动展开父节点 | boolean | true | | checkable | 节点前添加 Checkbox 复选框 | boolean | false | -| checkedKeys(v-model) | (受控)选中复选框的树节点(注意:父子节点有关联,如果传入父节点key,则子节点自动选中;相应当子节点key都传入,父节点也自动选中。当设置`checkable`和`checkStrictly`,它是一个有`checked`和`halfChecked`属性的对象,并且父子节点的选中与否不再关联 | string\[] \| {checked: string\[], halfChecked: string\[]} | \[] | +| checkedKeys(v-model) | (受控)选中复选框的树节点(注意:父子节点有关联,如果传入父节点key,则子节点自动选中;相应当子节点key都传入,父节点也自动选中。当设置`checkable`和`checkStrictly`,它是一个有`checked`和`halfChecked`属性的对象,并且父子节点的选中与否不再关联 | string\[] \| number\[] \| {checked: string\[] \| number\[], halfChecked: string\[] \| number\[]} | \[] | | checkStrictly | checkable状态下节点选择完全受控(父子节点选中状态不再关联) | boolean | false | -| defaultCheckedKeys | 默认选中复选框的树节点 | string\[] | \[] | +| defaultCheckedKeys | 默认选中复选框的树节点 | string\[] \| number\[] | \[] | | defaultExpandAll | 默认展开所有树节点 | boolean | false | -| defaultExpandedKeys | 默认展开指定的树节点 | string\[] | \[] | +| defaultExpandedKeys | 默认展开指定的树节点 | string\[] \| number\[] | \[] | | defaultExpandParent | 默认展开父节点 | bool | true | -| defaultSelectedKeys | 默认选中的树节点 | string\[] | \[] | +| defaultSelectedKeys | 默认选中的树节点 | string\[] \| number\[] | \[] | | disabled | 将树禁用 | bool | false | | draggable | 设置节点可拖拽 | boolean | false | -| expandedKeys(.sync) | (受控)展开指定的树节点 | string\[] | \[] | +| expandedKeys(.sync) | (受控)展开指定的树节点 | string\[] \| number\[] | \[] | | filterTreeNode | 按需筛选树节点(高亮),返回true | function(node) | - | | loadData | 异步加载数据 | function(node) | - | -| loadedKeys | (受控)已经加载的节点,需要配合 `loadData` 使用 | string\[] | \[] | +| loadedKeys | (受控)已经加载的节点,需要配合 `loadData` 使用 | string\[] \| number\[] | \[] | | multiple | 支持点选多个节点(节点本身) | boolean | false | -| selectedKeys(.sync) | (受控)设置选中的树节点 | string\[] | - | +| selectedKeys(.sync) | (受控)设置选中的树节点 | string\[] \| number\[] | - | | showIcon | 是否展示 TreeNode title 前的图标,没有默认样式,如设置为 true,需要自行定义图标相关样式 | boolean | false | | showLine | 是否展示连接线 | boolean | false | @@ -55,7 +55,7 @@ | disabled | 禁掉响应 | boolean | false | | icon | 自定义图标。可接收组件,props 为当前节点 props | slot\|slot-scope | - | | isLeaf | 设置为叶子节点(设置了`loadData`时有效) | boolean | false | -| key | 被树的 (default)ExpandedKeys / (default)CheckedKeys / (default)SelectedKeys 属性所用。注意:整个树范围内的所有节点的 key 值不能重复! | string | 内部计算出的节点位置 | +| key | 被树的 (default)ExpandedKeys / (default)CheckedKeys / (default)SelectedKeys 属性所用。注意:整个树范围内的所有节点的 key 值不能重复! | string \| number | 内部计算出的节点位置 | | selectable | 设置节点是否可被选中 | boolean | true | | title | 标题 | string\|slot\|slot-scope | '---' | | slots | 使用treeNodes时,可以通过该属性配置支持slot的属性,如 `slots: { title: 'XXX'}` | object | - | diff --git a/components/tree/util.js b/components/tree/util.js index f5251f2030..c3a9f14979 100644 --- a/components/tree/util.js +++ b/components/tree/util.js @@ -24,7 +24,7 @@ function traverseNodesKey (rootChildren, callback) { export function getFullKeyList (children) { const { keyEntities } = convertTreeToEntities(children) - return Object.keys(keyEntities) + return [...keyEntities.keys()] } /** 计算选中范围,只考虑expanded情况以优化性能 */ diff --git a/components/vc-tree-select/src/Base/BasePopup.jsx b/components/vc-tree-select/src/Base/BasePopup.jsx index 376cab6cb1..3d8a397477 100644 --- a/components/vc-tree-select/src/Base/BasePopup.jsx +++ b/components/vc-tree-select/src/Base/BasePopup.jsx @@ -38,7 +38,7 @@ function getDerivedStateFromProps (nextProps, prevState) { filteredTreeNodes.length && filteredTreeNodes !== prevProps.filteredTreeNodes ) { - newState._expandedKeyList = Object.keys(keyEntities) + newState._expandedKeyList = [...keyEntities.keys()] } // Cache `expandedKeyList` when filter set @@ -56,7 +56,7 @@ function getDerivedStateFromProps (nextProps, prevState) { // Clean loadedKeys if key not exist in keyEntities anymore if (nextProps.loadData) { - newState._loadedKeys = loadedKeys.filter(key => key in keyEntities) + newState._loadedKeys = loadedKeys.filter(key => keyEntities.has(key)) } return newState @@ -70,7 +70,7 @@ const BasePopup = { valueList: PropTypes.array, searchHalfCheckedKeys: PropTypes.array, valueEntities: PropTypes.object, - keyEntities: PropTypes.object, + keyEntities: Map, treeIcon: PropTypes.bool, treeLine: PropTypes.bool, treeNodeFilterProp: PropTypes.string, @@ -114,7 +114,7 @@ const BasePopup = { // TODO: make `expandedKeyList` control let expandedKeyList = treeDefaultExpandedKeys if (treeDefaultExpandAll) { - expandedKeyList = Object.keys(keyEntities) + expandedKeyList = [...keyEntities.keys()] } const state = { @@ -140,6 +140,7 @@ const BasePopup = { this.__emit('treeExpanded') }) } + this.__emit('update:treeExpandedKeys', expandedKeyList) this.__emit('treeExpand', expandedKeyList) }, diff --git a/components/vc-tree-select/src/Select.jsx b/components/vc-tree-select/src/Select.jsx index 935d1d9de4..35c3397836 100644 --- a/components/vc-tree-select/src/Select.jsx +++ b/components/vc-tree-select/src/Select.jsx @@ -44,7 +44,7 @@ import { cleanEntity, } from './util' import SelectNode from './SelectNode' -import { initDefaultProps, getOptionProps, mergeProps, getPropsData } from '../../_util/props-util' +import { initDefaultProps, getOptionProps, mergeProps, getPropsData, filterEmpty } from '../../_util/props-util' function getWatch (keys = []) { const watch = {} keys.forEach(k => { @@ -162,7 +162,8 @@ const Select = { _missValueList: [], // Contains the value not in the tree _selectorValueList: [], // Used for multiple selector _valueEntities: {}, - _keyEntities: {}, + _posEntities: new Map(), + _keyEntities: new Map(), _searchValue: '', _prevProps: {}, _init: true, @@ -286,7 +287,7 @@ const Select = { // processState('children', (propValue) => { // treeNodes = Array.isArray(propValue) ? propValue : [propValue] // }) - treeNodes = this.$slots.default + treeNodes = filterEmpty(this.$slots.default) } // Convert `treeData` to entities @@ -354,7 +355,7 @@ const Select = { // Format value list again for internal usage newState._valueList = checkedKeys.map(key => ({ - value: (newState._keyEntities || prevState._keyEntities)[key].value, + value: (newState._keyEntities || prevState._keyEntities).get(key).value, })) } else { newState._valueList = filteredValueList @@ -621,7 +622,7 @@ const Select = { ).checkedKeys } newValueList = keyList.map(key => { - const props = getPropsData(keyEntities[key].node) + const props = getPropsData(keyEntities.get(key).node) return { value: props.value, label: props[treeNodeLabelProp], @@ -731,10 +732,10 @@ const Select = { ).checkedKeys } - checkedNodeList = keyList.map(key => keyEntities[key].node) + checkedNodeList = keyList.map(key => keyEntities.get(key).node) // Let's follow as not `treeCheckStrictly` format - extraInfo.allCheckedNodes = keyList.map(key => cleanEntity(keyEntities[key])) + extraInfo.allCheckedNodes = keyList.map(key => cleanEntity(keyEntities.get(key))) } else if (treeCheckStrictly) { extraInfo.allCheckedNodes = nodeEventInfo.checkedNodes } else { diff --git a/components/vc-tree-select/src/util.js b/components/vc-tree-select/src/util.js index 4ee1ded020..e7111e5086 100644 --- a/components/vc-tree-select/src/util.js +++ b/components/vc-tree-select/src/util.js @@ -336,13 +336,17 @@ export function formatSelectorValue (valueList, props, valueEntities) { * This will change the label to title value */ function processProps (props) { - const { title, label, key, value, class: cls, style, on = {}} = props + const { title, label, value, class: cls, style, on = {}} = props + let key = props.key + if (!key && (key === undefined || key === null)) { + key = value + } const p = { props: omit(props, ['on', 'key', 'class', 'className', 'style']), on, class: cls || props.className, style: style, - key: typeof key === 'number' ? String(key) : (key || value), + key, } // Warning user not to use deprecated label prop. if (label && !title) { diff --git a/components/vc-tree/src/Tree.jsx b/components/vc-tree/src/Tree.jsx index 43328ef7c6..9ae5c0e593 100644 --- a/components/vc-tree/src/Tree.jsx +++ b/components/vc-tree/src/Tree.jsx @@ -52,22 +52,22 @@ const Tree = { defaultExpandParent: PropTypes.bool, autoExpandParent: PropTypes.bool, defaultExpandAll: PropTypes.bool, - defaultExpandedKeys: PropTypes.arrayOf(PropTypes.string), - expandedKeys: PropTypes.arrayOf(PropTypes.string), - defaultCheckedKeys: PropTypes.arrayOf(PropTypes.string), + defaultExpandedKeys: PropTypes.array, + expandedKeys: PropTypes.array, + defaultCheckedKeys: PropTypes.array, checkedKeys: PropTypes.oneOfType([ - PropTypes.arrayOf(PropTypes.oneOfType([PropTypes.string, PropTypes.number])), + PropTypes.array, PropTypes.object, ]), - defaultSelectedKeys: PropTypes.arrayOf(PropTypes.string), - selectedKeys: PropTypes.arrayOf(PropTypes.string), + defaultSelectedKeys: PropTypes.array, + selectedKeys: PropTypes.array, // onClick: PropTypes.func, // onDoubleClick: PropTypes.func, // onExpand: PropTypes.func, // onCheck: PropTypes.func, // onSelect: PropTypes.func, loadData: PropTypes.func, - loadedKeys: PropTypes.arrayOf(PropTypes.string), + loadedKeys: PropTypes.array, // onMouseEnter: PropTypes.func, // onMouseLeave: PropTypes.func, // onRightClick: PropTypes.func, @@ -105,8 +105,8 @@ const Tree = { warning(this.$props.children, 'please children prop replace slots.default') this.needSyncKeys = {} const state = { - _posEntities: {}, - _keyEntities: {}, + _posEntities: new Map(), + _keyEntities: new Map(), _expandedKeys: [], _selectedKeys: [], _checkedKeys: [], @@ -177,7 +177,7 @@ const Tree = { newState._expandedKeys = (props.autoExpandParent || (!_prevProps && props.defaultExpandParent)) ? conductExpandParent(props.expandedKeys, keyEntities) : props.expandedKeys } else if (!_prevProps && props.defaultExpandAll) { - newState._expandedKeys = Object.keys(keyEntities) + newState._expandedKeys = [...keyEntities.keys()] } else if (!_prevProps && props.defaultExpandedKeys) { newState._expandedKeys = (props.autoExpandParent || props.defaultExpandParent) ? conductExpandParent(props.defaultExpandedKeys, keyEntities) : props.defaultExpandedKeys @@ -380,7 +380,7 @@ const Tree = { // [Legacy] Not found related usage in doc or upper libs const selectedNodes = selectedKeys.map(key => { - const entity = keyEntities[key] + const entity = keyEntities.get(key) if (!entity) return null return entity.node @@ -395,6 +395,7 @@ const Tree = { selectedNodes, nativeEvent: e, } + this.__emit('update:selectedKeys', selectedKeys) this.__emit('select', selectedKeys, eventObj) }, onNodeCheck (e, treeNode, checked) { @@ -417,7 +418,7 @@ const Tree = { checkedObj = { checked: checkedKeys, halfChecked: halfCheckedKeys } eventObj.checkedNodes = checkedKeys - .map(key => keyEntities[key]) + .map(key => keyEntities.get(key)) .filter(entity => entity) .map(entity => entity.node) @@ -435,7 +436,7 @@ const Tree = { eventObj.halfCheckedKeys = halfCheckedKeys checkedKeys.forEach((key) => { - const entity = keyEntities[key] + const entity = keyEntities.get(key) if (!entity) return const { node, pos } = entity @@ -516,6 +517,7 @@ const Tree = { expanded: targetExpanded, nativeEvent: e, }) + this.__emit('update:expandedKeys', expandedKeys) // Async Load data if (targetExpanded && loadData) { @@ -581,8 +583,11 @@ const Tree = { _dropPosition: dropPosition, } = this.$data const pos = getPosition(level, index) - const key = child.key || pos - if (!keyEntities[key]) { + let key = child.key + if (!key && (key === undefined || key === null)) { + key = pos + } + if (!keyEntities.get(key)) { warnOnlyTreeNode() return null } diff --git a/components/vc-tree/src/TreeNode.jsx b/components/vc-tree/src/TreeNode.jsx index b16fa1a3d6..7972d2a327 100644 --- a/components/vc-tree/src/TreeNode.jsx +++ b/components/vc-tree/src/TreeNode.jsx @@ -19,7 +19,7 @@ const TreeNode = { mixins: [BaseMixin], __ANT_TREE_NODE: true, props: initDefaultProps({ - eventKey: PropTypes.string, // Pass by parent `cloneElement` + eventKey: PropTypes.oneOfType([PropTypes.string, PropTypes.number]), // Pass by parent `cloneElement` prefixCls: PropTypes.string, // className: PropTypes.string, root: PropTypes.object, diff --git a/components/vc-tree/src/util.js b/components/vc-tree/src/util.js index 8b8463e306..d3ff123d9e 100644 --- a/components/vc-tree/src/util.js +++ b/components/vc-tree/src/util.js @@ -63,11 +63,15 @@ export function traverseTreeNodes (treeNodes, callback) { // Process node if is not root if (node) { + let key = node.key + if (!key && (key === undefined || key === null)) { + key = pos + } const data = { node, index, pos, - key: node.key || pos, + key, parentPos: parent.node ? parent.pos : null, } callback(data) @@ -178,8 +182,8 @@ export function convertDataToTree (h, treeData, processer) { * @param processTreeEntity User can customize the entity */ export function convertTreeToEntities (treeNodes, { initWrapper, processEntity, onProcessFinished } = {}) { - const posEntities = {} - const keyEntities = {} + const posEntities = new Map() + const keyEntities = new Map() let wrapper = { posEntities, keyEntities, @@ -193,11 +197,11 @@ export function convertTreeToEntities (treeNodes, { initWrapper, processEntity, const { node, index, pos, key, parentPos } = item const entity = { node, index, key, pos } - posEntities[pos] = entity - keyEntities[key] = entity + posEntities.set(pos, entity) + keyEntities.set(key, entity) // Fill children - entity.parent = posEntities[parentPos] + entity.parent = posEntities.get(parentPos) if (entity.parent) { entity.parent.children = entity.parent.children || [] entity.parent.children.push(entity) @@ -241,8 +245,8 @@ export function parseCheckedKeys (keys) { return null } - keyProps.checkedKeys = keyListToString(keyProps.checkedKeys) - keyProps.halfCheckedKeys = keyListToString(keyProps.halfCheckedKeys) + // keyProps.checkedKeys = keyListToString(keyProps.checkedKeys) + // keyProps.halfCheckedKeys = keyListToString(keyProps.halfCheckedKeys) return keyProps } @@ -257,22 +261,22 @@ export function parseCheckedKeys (keys) { * @returns {{checkedKeys: [], halfCheckedKeys: []}} */ export function conductCheck (keyList, isCheck, keyEntities, checkStatus = {}) { - const checkedKeys = {} - const halfCheckedKeys = {}; // Record the key has some child checked (include child half checked) + const checkedKeys = new Map() + const halfCheckedKeys = new Map(); // Record the key has some child checked (include child half checked) (checkStatus.checkedKeys || []).forEach((key) => { - checkedKeys[key] = true + checkedKeys.set(key, true) }); (checkStatus.halfCheckedKeys || []).forEach((key) => { - halfCheckedKeys[key] = true + halfCheckedKeys.set(key, true) }) // Conduct up function conductUp (key) { - if (checkedKeys[key] === isCheck) return + if (checkedKeys.get(key) === isCheck) return - const entity = keyEntities[key] + const entity = keyEntities.get(key) if (!entity) return const { children, parent, node } = entity @@ -286,8 +290,8 @@ export function conductCheck (keyList, isCheck, keyEntities, checkStatus = {}) { (children || []) .filter(child => !isCheckDisabled(child.node)) .forEach(({ key: childKey }) => { - const childChecked = checkedKeys[childKey] - const childHalfChecked = halfCheckedKeys[childKey] + const childChecked = checkedKeys.get(childKey) + const childHalfChecked = halfCheckedKeys.get(childKey) if (childChecked || childHalfChecked) someChildChecked = true if (!childChecked) everyChildChecked = false @@ -295,11 +299,11 @@ export function conductCheck (keyList, isCheck, keyEntities, checkStatus = {}) { // Update checked status if (isCheck) { - checkedKeys[key] = everyChildChecked + checkedKeys.set(key, everyChildChecked) } else { - checkedKeys[key] = false + checkedKeys.set(key, false) } - halfCheckedKeys[key] = someChildChecked + halfCheckedKeys.set(key, someChildChecked) if (parent) { conductUp(parent.key) @@ -308,16 +312,16 @@ export function conductCheck (keyList, isCheck, keyEntities, checkStatus = {}) { // Conduct down function conductDown (key) { - if (checkedKeys[key] === isCheck) return + if (checkedKeys.get(key) === isCheck) return - const entity = keyEntities[key] + const entity = keyEntities.get(key) if (!entity) return const { children, node } = entity if (isCheckDisabled(node)) return - checkedKeys[key] = isCheck; + checkedKeys.set(key, isCheck); (children || []).forEach((child) => { conductDown(child.key) @@ -325,14 +329,14 @@ export function conductCheck (keyList, isCheck, keyEntities, checkStatus = {}) { } function conduct (key) { - const entity = keyEntities[key] + const entity = keyEntities.get(key) if (!entity) { warning(false, `'${key}' does not exist in the tree.`) return } const { children, parent, node } = entity - checkedKeys[key] = isCheck + checkedKeys.set(key, isCheck) if (isCheckDisabled(node)) return; @@ -357,18 +361,18 @@ export function conductCheck (keyList, isCheck, keyEntities, checkStatus = {}) { const halfCheckedKeyList = [] // Fill checked list - Object.keys(checkedKeys).forEach((key) => { - if (checkedKeys[key]) { + for (const [key, value] of checkedKeys) { + if (value) { checkedKeyList.push(key) } - }) + } // Fill half checked list - Object.keys(halfCheckedKeys).forEach((key) => { - if (!checkedKeys[key] && halfCheckedKeys[key]) { + for (const [key, value] of halfCheckedKeys) { + if (!checkedKeys.get(key) && value) { halfCheckedKeyList.push(key) } - }) + } return { checkedKeys: checkedKeyList, @@ -382,15 +386,15 @@ export function conductCheck (keyList, isCheck, keyEntities, checkStatus = {}) { * @param keyEntities */ export function conductExpandParent (keyList, keyEntities) { - const expandedKeys = {} + const expandedKeys = new Map() function conductUp (key) { - if (expandedKeys[key]) return + if (expandedKeys.get(key)) return - const entity = keyEntities[key] + const entity = keyEntities.get(key) if (!entity) return - expandedKeys[key] = true + expandedKeys.set(key, true) const { parent, node } = entity @@ -405,7 +409,7 @@ export function conductExpandParent (keyList, keyEntities) { conductUp(key) }) - return Object.keys(expandedKeys) + return [...expandedKeys.keys()] } /**