diff --git a/components/table/hooks/useSelection.tsx b/components/table/hooks/useSelection.tsx index af485fa15e..3bf6bd3f52 100644 --- a/components/table/hooks/useSelection.tsx +++ b/components/table/hooks/useSelection.tsx @@ -28,6 +28,7 @@ import type { ExpandType, GetPopupContainer, } from '../interface'; +import useMaxLevel from '../../vc-tree/useMaxLevel'; // TODO: warning if use ajax!!! @@ -120,7 +121,7 @@ export default function useSelection( const keyEntities = computed(() => mergedRowSelection.value.checkStrictly - ? { keyEntities: null } + ? null : convertDataToEntities(configRef.data.value as unknown as DataNode[], { externalGetKey: configRef.getRowKey.value as any, childrenPropName: configRef.childrenColumnName.value, @@ -155,7 +156,7 @@ export default function useSelection( }); return map; }); - + const { maxLevel, levelEntities } = useMaxLevel(keyEntities); const isCheckboxDisabled: GetCheckDisabled = (r: RecordType) => !!checkboxPropsMap.value.get(configRef.getRowKey.value(r))?.disabled; @@ -167,6 +168,8 @@ export default function useSelection( mergedSelectedKeys.value, true, keyEntities.value, + maxLevel.value, + levelEntities.value, isCheckboxDisabled as any, ); return [checkedKeys || [], halfCheckedKeys]; @@ -571,6 +574,8 @@ export default function useSelection( [...originCheckedKeys, key], true, keyEntities.value, + maxLevel.value, + levelEntities.value, isCheckboxDisabled as any, ); const { checkedKeys, halfCheckedKeys } = result; @@ -584,6 +589,8 @@ export default function useSelection( Array.from(tempKeySet), { checked: false, halfCheckedKeys }, keyEntities.value, + maxLevel.value, + levelEntities.value, isCheckboxDisabled as any, ).checkedKeys; } diff --git a/components/vc-cascader/Cascader.tsx b/components/vc-cascader/Cascader.tsx index a00e450d32..251a63cd98 100644 --- a/components/vc-cascader/Cascader.tsx +++ b/components/vc-cascader/Cascader.tsx @@ -21,6 +21,7 @@ import { useProvideCascader } from './context'; import OptionList from './OptionList'; import { BaseSelect } from '../vc-select'; import devWarning from '../vc-util/devWarning'; +import useMaxLevel from '../vc-tree/useMaxLevel'; export interface ShowSearchType { filter?: (inputValue: string, options: OptionType[], fieldNames: FieldNames) => boolean; @@ -259,6 +260,8 @@ export default defineComponent({ ref([]), ref([]), ]; + + const { maxLevel, levelEntities } = useMaxLevel(pathKeyEntities); watchEffect(() => { const [existValues, missingValues] = missingValuesInfo.value; @@ -274,7 +277,13 @@ export default defineComponent({ const keyPathValues = toPathKeys(existValues); const ketPathEntities = pathKeyEntities.value; - const { checkedKeys, halfCheckedKeys } = conductCheck(keyPathValues, true, ketPathEntities); + const { checkedKeys, halfCheckedKeys } = conductCheck( + keyPathValues, + true, + ketPathEntities, + maxLevel.value, + levelEntities.value, + ); // Convert key back to value cells [checkedValues.value, halfCheckedValues.value, missingCheckedValues.value] = [ @@ -356,9 +365,17 @@ export default defineComponent({ nextRawCheckedKeys, { checked: false, halfCheckedKeys: halfCheckedPathKeys }, pathKeyEntities.value, + maxLevel.value, + levelEntities.value, )); } else { - ({ checkedKeys } = conductCheck(nextRawCheckedKeys, true, pathKeyEntities.value)); + ({ checkedKeys } = conductCheck( + nextRawCheckedKeys, + true, + pathKeyEntities.value, + maxLevel.value, + levelEntities.value, + )); } // Roll up to parent level keys diff --git a/components/vc-select/OptionList.tsx b/components/vc-select/OptionList.tsx index 7cb1aa68d5..e7e8fb7630 100644 --- a/components/vc-select/OptionList.tsx +++ b/components/vc-select/OptionList.tsx @@ -5,7 +5,7 @@ import classNames from '../_util/classNames'; import pickAttrs from '../_util/pickAttrs'; import { isValidElement } from '../_util/props-util'; import createRef from '../_util/createRef'; -import { computed, defineComponent, nextTick, reactive, watch } from 'vue'; +import { computed, defineComponent, nextTick, reactive, toRaw, watch } from 'vue'; import List from '../vc-virtual-list'; import useMemo from '../_util/hooks/useMemo'; import { isPlatformMac } from './utils/platformUtil'; @@ -105,7 +105,9 @@ const OptionList = defineComponent({ () => { if (!baseProps.multiple && baseProps.open && props.rawValues.size === 1) { const value = Array.from(props.rawValues)[0]; - const index = memoFlattenOptions.value.findIndex(({ data }) => data.value === value); + const index = toRaw(memoFlattenOptions.value).findIndex( + ({ data }) => data.value === value, + ); if (index !== -1) { setActive(index); nextTick(() => { diff --git a/components/vc-select/hooks/useFilterOptions.ts b/components/vc-select/hooks/useFilterOptions.ts index 97720e3487..41f52cd4de 100644 --- a/components/vc-select/hooks/useFilterOptions.ts +++ b/components/vc-select/hooks/useFilterOptions.ts @@ -8,7 +8,7 @@ import type { } from '../Select'; import { injectPropsWithOption } from '../utils/valueUtil'; import type { Ref } from 'vue'; -import { computed } from 'vue'; +import { toRaw, computed } from 'vue'; function includes(test: any, search: string) { return toArray(test).join('').toUpperCase().includes(search); @@ -22,22 +22,24 @@ export default ( optionFilterProp?: Ref, ) => computed(() => { - if (!searchValue.value || filterOption.value === false) { + const searchValueVal = searchValue.value; + const optionFilterPropValue = optionFilterProp?.value; + const filterOptionValue = filterOption?.value; + if (!searchValueVal || filterOptionValue === false) { return options.value; } - const { options: fieldOptions, label: fieldLabel, value: fieldValue } = fieldNames.value; const filteredOptions: DefaultOptionType[] = []; - const customizeFilter = typeof filterOption.value === 'function'; + const customizeFilter = typeof filterOptionValue === 'function'; - const upperSearch = searchValue.value.toUpperCase(); + const upperSearch = searchValueVal.toUpperCase(); const filterFunc = customizeFilter - ? (filterOption.value as FilterFunc) + ? (filterOptionValue as FilterFunc) : (_: string, option: DefaultOptionType) => { // Use provided `optionFilterProp` - if (optionFilterProp.value) { - return includes(option[optionFilterProp.value], upperSearch); + if (optionFilterPropValue) { + return includes(option[optionFilterPropValue], upperSearch); } // Auto select `label` or `value` by option type @@ -53,17 +55,17 @@ export default ( ? opt => injectPropsWithOption(opt) : opt => opt; - options.value.forEach(item => { + toRaw(options.value).forEach(item => { // Group should check child options if (item[fieldOptions]) { // Check group first - const matchGroup = filterFunc(searchValue.value, wrapOption(item)); + const matchGroup = filterFunc(searchValueVal, wrapOption(item)); if (matchGroup) { filteredOptions.push(item); } else { // Check option const subOptions = item[fieldOptions].filter((subItem: DefaultOptionType) => - filterFunc(searchValue.value, wrapOption(subItem)), + filterFunc(searchValueVal, wrapOption(subItem)), ); if (subOptions.length) { filteredOptions.push({ @@ -76,10 +78,9 @@ export default ( return; } - if (filterFunc(searchValue.value, wrapOption(item))) { + if (filterFunc(searchValueVal, wrapOption(item))) { filteredOptions.push(item); } }); - return filteredOptions; }); diff --git a/components/vc-select/hooks/useOptions.ts b/components/vc-select/hooks/useOptions.ts index 8b0967ae77..727a70a4c2 100644 --- a/components/vc-select/hooks/useOptions.ts +++ b/components/vc-select/hooks/useOptions.ts @@ -1,5 +1,5 @@ import type { Ref } from 'vue'; -import { shallowRef, watchEffect } from 'vue'; +import { toRaw, shallowRef, watchEffect } from 'vue'; import type { FieldNames, RawValueType } from '../Select'; import { convertChildrenToData } from '../utils/legacyUtil'; @@ -16,7 +16,7 @@ export default function useOptions( const valueOptions = shallowRef(); const labelOptions = shallowRef(); watchEffect(() => { - let newOptions = options.value; + let newOptions = toRaw(options.value); const childrenAsData = !options.value; if (childrenAsData) { @@ -25,16 +25,16 @@ export default function useOptions( const newValueOptions = new Map(); const newLabelOptions = new Map(); - + const fieldNamesValue = fieldNames.value; function dig(optionList: OptionType[], isChildren = false) { // for loop to speed up collection speed for (let i = 0; i < optionList.length; i += 1) { const option = optionList[i]; - if (!option[fieldNames.value.options] || isChildren) { - newValueOptions.set(option[fieldNames.value.value], option); - newLabelOptions.set(option[fieldNames.value.label], option); + if (!option[fieldNamesValue.options] || isChildren) { + newValueOptions.set(option[fieldNamesValue.value], option); + newLabelOptions.set(option[fieldNamesValue.label], option); } else { - dig(option[fieldNames.value.options], true); + dig(option[fieldNamesValue.options], true); } } } diff --git a/components/vc-tree-select/OptionList.tsx b/components/vc-tree-select/OptionList.tsx index 832d6ce90f..86d66c7493 100644 --- a/components/vc-tree-select/OptionList.tsx +++ b/components/vc-tree-select/OptionList.tsx @@ -1,7 +1,7 @@ import type { TreeDataNode, Key } from './interface'; import type { RefOptionListProps } from '../vc-select/OptionList'; import type { ScrollTo } from '../vc-virtual-list/List'; -import { computed, defineComponent, nextTick, ref, shallowRef, watch } from 'vue'; +import { computed, defineComponent, nextTick, ref, shallowRef, toRaw, watch } from 'vue'; import useMemo from '../_util/hooks/useMemo'; import type { EventDataNode } from '../tree'; import KeyCode from '../_util/KeyCode'; @@ -89,7 +89,7 @@ export default defineComponent({ () => baseProps.searchValue, () => { if (baseProps.searchValue) { - searchExpandedKeys.value = getAllKeys(context.treeData, context.fieldNames); + searchExpandedKeys.value = getAllKeys(toRaw(context.treeData), toRaw(context.fieldNames)); } }, { @@ -98,7 +98,7 @@ export default defineComponent({ ); const mergedExpandedKeys = computed(() => { if (legacyContext.treeExpandedKeys) { - return [...legacyContext.treeExpandedKeys]; + return toRaw(legacyContext.treeExpandedKeys).slice(); } return baseProps.searchValue ? searchExpandedKeys.value : expandedKeys.value; }); diff --git a/components/vc-tree-select/TreeSelect.tsx b/components/vc-tree-select/TreeSelect.tsx index 2b30326853..801e528054 100644 --- a/components/vc-tree-select/TreeSelect.tsx +++ b/components/vc-tree-select/TreeSelect.tsx @@ -29,6 +29,7 @@ import type { VueNode } from '../_util/type'; import { conductCheck } from '../vc-tree/utils/conductUtil'; import { warning } from '../vc-util/warning'; import { toReactive } from '../_util/toReactive'; +import useMaxLevel from '../vc-tree/useMaxLevel'; export type OnInternalSelect = (value: RawValueType, info: { selected: boolean }) => void; @@ -364,13 +365,15 @@ export default defineComponent({ // const [mergedValues] = useCache(rawLabeledValues); const rawValues = computed(() => rawLabeledValues.value.map(item => item.value)); - + const { maxLevel, levelEntities } = useMaxLevel(keyEntities); // Convert value to key. Will fill missed keys for conduct check. const [rawCheckedValues, rawHalfCheckedValues] = useCheckedKeys( rawLabeledValues, rawHalfLabeledValues, treeConduction, keyEntities, + maxLevel, + levelEntities, ); // Convert rawCheckedKeys to check strategy related values @@ -504,7 +507,9 @@ export default defineComponent({ selectedKey: Key, { selected, source }: { selected: boolean; source: SelectSource }, ) => { - const entity = keyEntities.value[selectedKey]; + const keyEntitiesValue = toRaw(keyEntities.value); + const valueEntitiesValue = toRaw(valueEntities.value); + const entity = keyEntitiesValue[selectedKey]; const node = entity?.node; const selectedValue = node?.[mergedFieldNames.value.value] ?? selectedKey; @@ -521,24 +526,32 @@ export default defineComponent({ if (treeConduction.value) { // Should keep missing values const { missingRawValues, existRawValues } = splitRawValues(newRawValues); - const keyList = existRawValues.map(val => valueEntities.value.get(val).key); + const keyList = existRawValues.map(val => valueEntitiesValue.get(val).key); // Conduction by selected or not let checkedKeys: Key[]; if (selected) { - ({ checkedKeys } = conductCheck(keyList, true, keyEntities.value)); + ({ checkedKeys } = conductCheck( + keyList, + true, + keyEntitiesValue, + maxLevel.value, + levelEntities.value, + )); } else { ({ checkedKeys } = conductCheck( keyList, { checked: false, halfCheckedKeys: rawHalfCheckedValues.value }, - keyEntities.value, + keyEntitiesValue, + maxLevel.value, + levelEntities.value, )); } // Fill back of keys newRawValues = [ ...missingRawValues, - ...checkedKeys.map(key => keyEntities.value[key].node[mergedFieldNames.value.value]), + ...checkedKeys.map(key => keyEntitiesValue[key].node[mergedFieldNames.value.value]), ]; } triggerChange(newRawValues, { selected, triggerValue: selectedValue }, source || 'option'); diff --git a/components/vc-tree-select/hooks/useCache.ts b/components/vc-tree-select/hooks/useCache.ts index fa2814533e..326fd94d97 100644 --- a/components/vc-tree-select/hooks/useCache.ts +++ b/components/vc-tree-select/hooks/useCache.ts @@ -1,5 +1,5 @@ import type { Ref } from 'vue'; -import { computed, shallowRef } from 'vue'; +import { toRaw, computed, shallowRef } from 'vue'; import type { LabeledValueType, RawValueType } from '../TreeSelect'; /** @@ -15,7 +15,7 @@ export default (values: Ref): [Ref] => { const { valueLabels } = cacheRef.value; const valueLabelsCache = new Map(); - const filledValues = values.value.map(item => { + const filledValues = toRaw(values.value).map(item => { const { value } = item; const mergedLabel = item.label ?? valueLabels.get(value); diff --git a/components/vc-tree-select/hooks/useCheckedKeys.ts b/components/vc-tree-select/hooks/useCheckedKeys.ts index ad1eace6a5..4b9552ad6a 100644 --- a/components/vc-tree-select/hooks/useCheckedKeys.ts +++ b/components/vc-tree-select/hooks/useCheckedKeys.ts @@ -2,26 +2,36 @@ import type { Key } from '../../_util/type'; import type { DataEntity } from '../../vc-tree/interface'; import { conductCheck } from '../../vc-tree/utils/conductUtil'; import type { LabeledValueType, RawValueType } from '../TreeSelect'; -import type { Ref } from 'vue'; -import { shallowRef, watchEffect } from 'vue'; +import type { Ref, ShallowRef } from 'vue'; +import { toRaw, shallowRef, watchEffect } from 'vue'; export default ( rawLabeledValues: Ref, rawHalfCheckedValues: Ref, treeConduction: Ref, keyEntities: Ref>, + maxLevel: Ref, + levelEntities: ShallowRef>>, ) => { const newRawCheckedValues = shallowRef([]); const newRawHalfCheckedValues = shallowRef([]); watchEffect(() => { - let checkedKeys: RawValueType[] = rawLabeledValues.value.map(({ value }) => value); - let halfCheckedKeys: RawValueType[] = rawHalfCheckedValues.value.map(({ value }) => value); + let checkedKeys: RawValueType[] = toRaw(rawLabeledValues.value).map(({ value }) => value); + let halfCheckedKeys: RawValueType[] = toRaw(rawHalfCheckedValues.value).map( + ({ value }) => value, + ); const missingValues = checkedKeys.filter(key => !keyEntities.value[key]); if (treeConduction.value) { - ({ checkedKeys, halfCheckedKeys } = conductCheck(checkedKeys, true, keyEntities.value)); + ({ checkedKeys, halfCheckedKeys } = conductCheck( + checkedKeys, + true, + keyEntities.value, + maxLevel.value, + levelEntities.value, + )); } newRawCheckedValues.value = Array.from(new Set([...missingValues, ...checkedKeys])); newRawHalfCheckedValues.value = halfCheckedKeys; diff --git a/components/vc-tree-select/hooks/useDataEntities.ts b/components/vc-tree-select/hooks/useDataEntities.ts index 6756a9fb51..31be826a89 100644 --- a/components/vc-tree-select/hooks/useDataEntities.ts +++ b/components/vc-tree-select/hooks/useDataEntities.ts @@ -4,21 +4,22 @@ import type { FieldNames, RawValueType } from '../TreeSelect'; import { isNil } from '../utils/valueUtil'; import type { Ref } from 'vue'; -import { ref, watchEffect } from 'vue'; +import { toRaw, ref, watchEffect } from 'vue'; import { warning } from '../../vc-util/warning'; export default (treeData: Ref, fieldNames: Ref) => { const valueEntities = ref>(new Map()); const keyEntities = ref>({}); watchEffect(() => { - const collection = convertDataToEntities(treeData.value, { - fieldNames: fieldNames.value, + const fieldNamesValue = fieldNames.value; + const collection = convertDataToEntities(toRaw(treeData.value), { + fieldNames: fieldNamesValue, initWrapper: wrapper => ({ ...wrapper, valueEntities: new Map(), }), processEntity: (entity, wrapper: any) => { - const val = entity.node[fieldNames.value.value]; + const val = entity.node[fieldNamesValue.value]; // Check if exist same value if (process.env.NODE_ENV !== 'production') { diff --git a/components/vc-tree-select/hooks/useFilterTreeData.ts b/components/vc-tree-select/hooks/useFilterTreeData.ts index 5f19b10aaf..409b5a6438 100644 --- a/components/vc-tree-select/hooks/useFilterTreeData.ts +++ b/components/vc-tree-select/hooks/useFilterTreeData.ts @@ -1,5 +1,5 @@ import type { Ref } from 'vue'; -import { computed } from 'vue'; +import { toRaw, computed } from 'vue'; import type { DefaultOptionType, InternalFieldName, TreeSelectProps } from '../TreeSelect'; import { fillLegacyProps } from '../utils/legacyUtil'; @@ -21,7 +21,9 @@ export default ( ) => { return computed(() => { const { children: fieldChildren } = fieldNames.value; - if (!searchValue.value || filterTreeNode.value === false) { + const searchValueVal = searchValue.value; + const treeNodeFilterPropValue = treeNodeFilterProp?.value; + if (!searchValueVal || filterTreeNode.value === false) { return treeData.value; } @@ -29,33 +31,49 @@ export default ( if (typeof filterTreeNode.value === 'function') { filterOptionFunc = filterTreeNode.value; } else { - const upperStr = searchValue.value.toUpperCase(); + const upperStr = searchValueVal.toUpperCase(); filterOptionFunc = (_, dataNode) => { - const value = dataNode[treeNodeFilterProp.value]; + const value = dataNode[treeNodeFilterPropValue]; return String(value).toUpperCase().includes(upperStr); }; } function dig(list: DefaultOptionType[], keepAll = false) { - return list - .map(dataNode => { - const children = dataNode[fieldChildren]; - - const match = keepAll || filterOptionFunc(searchValue.value, fillLegacyProps(dataNode)); - const childList = dig(children || [], match); - - if (match || childList.length) { - return { - ...dataNode, - [fieldChildren]: childList, - }; - } - return null; - }) - .filter(node => node); + const res = []; + for (let index = 0, len = list.length; index < len; index++) { + const dataNode = list[index]; + const children = dataNode[fieldChildren]; + + const match = keepAll || filterOptionFunc(searchValueVal, fillLegacyProps(dataNode)); + const childList = dig(children || [], match); + + if (match || childList.length) { + res.push({ + ...dataNode, + [fieldChildren]: childList, + }); + } + } + return res; + // return list + // .map(dataNode => { + // const children = dataNode[fieldChildren]; + + // const match = keepAll || filterOptionFunc(searchValueVal, fillLegacyProps(dataNode)); + // const childList = dig(children || [], match); + + // if (match || childList.length) { + // return { + // ...dataNode, + // [fieldChildren]: childList, + // }; + // } + // return null; + // }) + // .filter(node => node); } - return dig(treeData.value); + return dig(toRaw(treeData.value)); }); }; diff --git a/components/vc-tree-select/hooks/useTreeData.ts b/components/vc-tree-select/hooks/useTreeData.ts index fe4ff36fcf..4fde7c8a43 100644 --- a/components/vc-tree-select/hooks/useTreeData.ts +++ b/components/vc-tree-select/hooks/useTreeData.ts @@ -1,5 +1,5 @@ import type { Ref } from 'vue'; -import { computed } from 'vue'; +import { toRaw, computed } from 'vue'; import type { DataNode, SimpleModeConfig } from '../interface'; import { convertChildrenToData } from '../utils/legacyUtil'; import type { DefaultOptionType } from '../TreeSelect'; @@ -51,17 +51,18 @@ export default function useTreeData( simpleMode: Ref, ): Ref { return computed(() => { + const simpleModeValue = simpleMode.value; if (treeData.value) { return simpleMode.value - ? parseSimpleTreeData(treeData.value, { + ? parseSimpleTreeData(toRaw(treeData.value), { id: 'id', pId: 'pId', rootPId: null, - ...(simpleMode.value !== true ? simpleMode.value : {}), + ...(simpleModeValue !== true ? simpleModeValue : {}), }) : treeData.value; } - return convertChildrenToData(children.value); + return convertChildrenToData(toRaw(children.value)); }); } diff --git a/components/vc-tree/Tree.tsx b/components/vc-tree/Tree.tsx index d27185c400..6247508ad0 100644 --- a/components/vc-tree/Tree.tsx +++ b/components/vc-tree/Tree.tsx @@ -32,6 +32,7 @@ import { watch, watchEffect, nextTick, + toRaw, } from 'vue'; import initDefaultProps from '../_util/props-util/initDefaultProps'; import type { CheckInfo, DraggableFn } from './props'; @@ -40,6 +41,7 @@ import { warning } from '../vc-util/warning'; import KeyCode from '../_util/KeyCode'; import classNames from '../_util/classNames'; import pickAttrs from '../_util/pickAttrs'; +import useMaxLevel from './useMaxLevel'; const MAX_RETRY_TIMES = 10; @@ -101,8 +103,12 @@ export default defineComponent({ // abstract-drag-over-node is the top node dragOverNodeKey: null, }); - const treeData = computed(() => { - return props.treeData !== undefined ? props.treeData : convertTreeToData(props.children); + const treeData = shallowRef([]); + watchEffect(() => { + treeData.value = + props.treeData !== undefined + ? toRaw(props.treeData) + : convertTreeToData(toRaw(props.children)); }); const keyEntities = shallowRef({}); @@ -137,7 +143,9 @@ export default defineComponent({ watchEffect(() => { if (treeData.value) { - const entitiesMap = convertDataToEntities(treeData.value, { fieldNames: fieldNames.value }); + const entitiesMap = convertDataToEntities(toRaw(treeData.value), { + fieldNames: fieldNames.value, + }); keyEntities.value = { [MOTION_KEY]: MotionEntity, ...entitiesMap.keyEntities, @@ -180,32 +188,37 @@ export default defineComponent({ ); // ================ flattenNodes ================= - const flattenNodes = computed(() => { - return flattenTreeData(treeData.value, expandedKeys.value, fieldNames.value); + const flattenNodes = shallowRef([]); + watchEffect(() => { + flattenNodes.value = flattenTreeData( + toRaw(treeData.value), + toRaw(expandedKeys.value), + fieldNames.value, + ); }); // ================ selectedKeys ================= watchEffect(() => { if (props.selectable) { if (props.selectedKeys !== undefined) { - selectedKeys.value = calcSelectedKeys(props.selectedKeys, props); + selectedKeys.value = calcSelectedKeys(toRaw(props.selectedKeys), props); } else if (!init && props.defaultSelectedKeys) { - selectedKeys.value = calcSelectedKeys(props.defaultSelectedKeys, props); + selectedKeys.value = calcSelectedKeys(toRaw(props.defaultSelectedKeys), props); } } }); - + const { maxLevel, levelEntities } = useMaxLevel(keyEntities); // ================= checkedKeys ================= watchEffect(() => { if (props.checkable) { let checkedKeyEntity; if (props.checkedKeys !== undefined) { - checkedKeyEntity = parseCheckedKeys(props.checkedKeys) || {}; + checkedKeyEntity = parseCheckedKeys(toRaw(props.checkedKeys)) || {}; } else if (!init && props.defaultCheckedKeys) { - checkedKeyEntity = parseCheckedKeys(props.defaultCheckedKeys) || {}; + checkedKeyEntity = parseCheckedKeys(toRaw(props.defaultCheckedKeys)) || {}; } else if (treeData.value) { // If `treeData` changed, we also need check it - checkedKeyEntity = parseCheckedKeys(props.checkedKeys) || { + checkedKeyEntity = parseCheckedKeys(toRaw(props.checkedKeys)) || { checkedKeys: checkedKeys.value, halfCheckedKeys: halfCheckedKeys.value, }; @@ -216,7 +229,13 @@ export default defineComponent({ checkedKeyEntity; if (!props.checkStrictly) { - const conductKeys = conductCheck(newCheckedKeys, true, keyEntities.value); + const conductKeys = conductCheck( + newCheckedKeys, + true, + keyEntities.value, + maxLevel.value, + levelEntities.value, + ); ({ checkedKeys: newCheckedKeys, halfCheckedKeys: newHalfCheckedKeys } = conductKeys); } @@ -399,7 +418,7 @@ export default defineComponent({ delayedDragEnterLogic[pos] = window.setTimeout(() => { if (dragState.draggingNodeKey === null) return; - let newExpandedKeys = [...expandedKeys.value]; + let newExpandedKeys = expandedKeys.value.slice(); const entity = keyEntities.value[node.eventKey]; if (entity && (entity.children || []).length) { @@ -549,7 +568,7 @@ export default defineComponent({ if (dropTargetKey === null) return; const abstractDropNodeProps = { - ...getTreeNodeProps(dropTargetKey, treeNodeRequiredProps.value), + ...getTreeNodeProps(dropTargetKey, toRaw(treeNodeRequiredProps.value)), active: activeItem.value?.key === dropTargetKey, data: keyEntities.value[dropTargetKey].node, }; @@ -646,7 +665,7 @@ export default defineComponent({ checked, nativeEvent: e, }; - + const keyEntitiesValue = keyEntities.value; if (checkStrictly) { const newCheckedKeys = checked ? arrAdd(checkedKeys.value, key) @@ -654,7 +673,6 @@ export default defineComponent({ const newHalfCheckedKeys = arrDel(halfCheckedKeys.value, key); checkedObj = { checked: newCheckedKeys, halfChecked: newHalfCheckedKeys }; - const keyEntitiesValue = keyEntities.value; eventObj.checkedNodes = newCheckedKeys .map(checkedKey => keyEntitiesValue[checkedKey]) .filter(entity => entity) @@ -668,7 +686,9 @@ export default defineComponent({ let { checkedKeys: newCheckedKeys, halfCheckedKeys: newHalfCheckedKeys } = conductCheck( [...checkedKeys.value, key], true, - keyEntities.value, + keyEntitiesValue, + maxLevel.value, + levelEntities.value, ); // If remove, we do it again to correction @@ -678,7 +698,9 @@ export default defineComponent({ ({ checkedKeys: newCheckedKeys, halfCheckedKeys: newHalfCheckedKeys } = conductCheck( Array.from(keySet), { checked: false, halfCheckedKeys: newHalfCheckedKeys }, - keyEntities.value, + keyEntitiesValue, + maxLevel.value, + levelEntities.value, )); } @@ -689,7 +711,7 @@ export default defineComponent({ eventObj.checkedNodesPositions = []; eventObj.halfCheckedKeys = newHalfCheckedKeys; newCheckedKeys.forEach(checkedKey => { - const entity = keyEntities.value[checkedKey]; + const entity = keyEntitiesValue[checkedKey]; if (!entity) return; const { node, pos } = entity; @@ -906,7 +928,6 @@ export default defineComponent({ const offsetActiveKey = (offset: number) => { let index = flattenNodes.value.findIndex(({ key }) => key === activeKey.value); - // Align with index if (index === -1 && offset < 0) { index = flattenNodes.value.length; diff --git a/components/vc-tree/useMaxLevel.ts b/components/vc-tree/useMaxLevel.ts new file mode 100644 index 0000000000..ca2942a012 --- /dev/null +++ b/components/vc-tree/useMaxLevel.ts @@ -0,0 +1,38 @@ +import type { ShallowRef } from 'vue'; +import { shallowRef, ref, watchEffect } from 'vue'; +import type { BasicDataNode, DataEntity, DataNode, Key } from './interface'; + +export default function useMaxLevel( + keyEntities: ShallowRef>>, +) { + const maxLevel = ref(0); + const levelEntities = shallowRef>>>(); + watchEffect(() => { + const newLevelEntities = new Map>>(); + let newMaxLevel = 0; + const keyEntitiesValue = keyEntities.value || {}; + // Convert entities by level for calculation + for (const key in keyEntitiesValue) { + if (Object.prototype.hasOwnProperty.call(keyEntitiesValue, key)) { + const entity = keyEntitiesValue[key]; + const { level } = entity; + + let levelSet: Set> = newLevelEntities.get(level); + if (!levelSet) { + levelSet = new Set(); + newLevelEntities.set(level, levelSet); + } + + levelSet.add(entity); + + newMaxLevel = Math.max(newMaxLevel, level); + } + } + maxLevel.value = newMaxLevel; + levelEntities.value = newLevelEntities; + }); + return { + maxLevel, + levelEntities, + }; +} diff --git a/components/vc-tree/utils/conductUtil.ts b/components/vc-tree/utils/conductUtil.ts index ed751be194..4e4389e384 100644 --- a/components/vc-tree/utils/conductUtil.ts +++ b/components/vc-tree/utils/conductUtil.ts @@ -186,6 +186,8 @@ export function conductCheck( keyList: Key[], checked: true | { checked: false; halfCheckedKeys: Key[] }, keyEntities: Record>, + maxLevel: number, + levelEntities: Map>>, getCheckDisabled?: GetCheckDisabled, ): ConductReturnType { const warningMissKeys: Key[] = []; @@ -208,24 +210,6 @@ export function conductCheck( return hasEntity; }), ); - const levelEntities = new Map>>(); - let maxLevel = 0; - - // Convert entities by level for calculation - Object.keys(keyEntities).forEach(key => { - const entity = keyEntities[key]; - const { level } = entity; - - let levelSet: Set> = levelEntities.get(level); - if (!levelSet) { - levelSet = new Set(); - levelEntities.set(level, levelSet); - } - - levelSet.add(entity); - - maxLevel = Math.max(maxLevel, level); - }); warning( !warningMissKeys.length,