diff --git a/components/tree-select/index.tsx b/components/tree-select/index.tsx index f9604b546..e0730a7c7 100644 --- a/components/tree-select/index.tsx +++ b/components/tree-select/index.tsx @@ -70,7 +70,6 @@ export function treeSelectProps< export type TreeSelectProps = Partial>>; const TreeSelect = defineComponent({ - TreeNode, name: 'ATreeSelect', inheritAttrs: false, props: initDefaultProps(treeSelectProps(), { diff --git a/components/vc-tree-select/TreeSelect.tsx b/components/vc-tree-select/TreeSelect.tsx index 6612ab7d2..830043a5d 100644 --- a/components/vc-tree-select/TreeSelect.tsx +++ b/components/vc-tree-select/TreeSelect.tsx @@ -173,7 +173,7 @@ export function treeSelectProps< onDropdownVisibleChange: { type: Function as PropType<(open: boolean) => void> }, // >>> Tree - treeLine: { type: Boolean, default: undefined }, + treeLine: { type: [Boolean, Object], default: undefined }, treeIcon: PropTypes.any, showTreeIcon: { type: Boolean, default: undefined }, switcherIcon: PropTypes.any, diff --git a/components/vc-tree-select1/Context.tsx b/components/vc-tree-select1/Context.tsx deleted file mode 100644 index 95746f792..000000000 --- a/components/vc-tree-select1/Context.tsx +++ /dev/null @@ -1,68 +0,0 @@ -import type { - FlattenDataNode, - InternalDataEntity, - Key, - LegacyDataNode, - RawValueType, -} from './interface'; -import type { SkipType } from './hooks/useKeyValueMapping'; -import type { ComputedRef, InjectionKey, PropType } from 'vue'; -import { computed, defineComponent, inject, provide } from 'vue'; - -interface ContextProps { - checkable: boolean; - customCheckable: () => any; - checkedKeys: Key[]; - halfCheckedKeys: Key[]; - treeExpandedKeys: Key[]; - treeDefaultExpandedKeys: Key[]; - onTreeExpand: (keys: Key[]) => void; - treeDefaultExpandAll: boolean; - treeIcon: any; - showTreeIcon: boolean; - switcherIcon: any; - treeLine: boolean; - treeNodeFilterProp: string; - treeLoadedKeys: Key[]; - treeMotion: any; - loadData: (treeNode: LegacyDataNode) => Promise; - onTreeLoad: (loadedKeys: Key[]) => void; - - // Cache help content. These can be generated by parent component. - // Let's reuse this. - getEntityByKey: (key: Key, skipType?: SkipType, ignoreDisabledCheck?: boolean) => FlattenDataNode; - getEntityByValue: ( - value: RawValueType, - skipType?: SkipType, - ignoreDisabledCheck?: boolean, - ) => FlattenDataNode; - - slots: { - title?: (data: InternalDataEntity) => any; - titleRender?: (data: InternalDataEntity) => any; - [key: string]: ((...args: any[]) => any) | undefined; - }; -} - -const SelectContextKey: InjectionKey> = Symbol('SelectContextKey'); - -export const SelectContext = defineComponent({ - name: 'SelectContext', - props: { - value: { type: Object as PropType }, - }, - setup(props, { slots }) { - provide( - SelectContextKey, - computed(() => props.value), - ); - return () => slots.default?.(); - }, -}); - -export const useInjectTreeSelectContext = () => { - return inject( - SelectContextKey, - computed(() => ({} as ContextProps)), - ); -}; diff --git a/components/vc-tree-select1/OptionList.tsx b/components/vc-tree-select1/OptionList.tsx deleted file mode 100644 index 6d3ec67e4..000000000 --- a/components/vc-tree-select1/OptionList.tsx +++ /dev/null @@ -1,262 +0,0 @@ -import type { DataNode, TreeDataNode, Key } from './interface'; -import { useInjectTreeSelectContext } from './Context'; -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 { optionListProps } from './props'; -import useMemo from '../_util/hooks/useMemo'; -import type { EventDataNode } from '../tree'; -import KeyCode from '../_util/KeyCode'; -import Tree from '../vc-tree/Tree'; -import type { TreeProps } from '../vc-tree/props'; - -const HIDDEN_STYLE = { - width: 0, - height: 0, - display: 'flex', - overflow: 'hidden', - opacity: 0, - border: 0, - padding: 0, - margin: 0, -}; - -interface TreeEventInfo { - node: { key: Key }; - selected?: boolean; - checked?: boolean; -} - -type ReviseRefOptionListProps = Omit & { scrollTo: ScrollTo }; - -export default defineComponent({ - name: 'OptionList', - inheritAttrs: false, - props: optionListProps(), - slots: ['notFoundContent', 'menuItemSelectedIcon'], - setup(props, { slots, expose }) { - const context = useInjectTreeSelectContext(); - - const treeRef = ref(); - const memoOptions = useMemo( - () => props.options, - [() => props.open, () => props.options], - next => next[0], - ); - - const valueKeys = computed(() => { - const { checkedKeys, getEntityByValue } = context.value; - return checkedKeys.map(val => { - const entity = getEntityByValue(val); - return entity ? entity.key : null; - }); - }); - - const mergedCheckedKeys = computed(() => { - const { checkable, halfCheckedKeys } = context.value; - if (!checkable) { - return null; - } - - return { - checked: valueKeys.value, - halfChecked: halfCheckedKeys, - }; - }); - - watch( - () => props.open, - () => { - nextTick(() => { - if (props.open && !props.multiple && valueKeys.value.length) { - treeRef.value?.scrollTo({ key: valueKeys.value[0] }); - } - }); - }, - { immediate: true, flush: 'post' }, - ); - - // ========================== Search ========================== - const lowerSearchValue = computed(() => String(props.searchValue).toLowerCase()); - const filterTreeNode = (treeNode: EventDataNode) => { - if (!lowerSearchValue.value) { - return false; - } - return String(treeNode[context.value.treeNodeFilterProp]) - .toLowerCase() - .includes(lowerSearchValue.value); - }; - - // =========================== Keys =========================== - const expandedKeys = shallowRef(context.value.treeDefaultExpandedKeys); - const searchExpandedKeys = shallowRef(null); - - watch( - () => props.searchValue, - () => { - if (props.searchValue) { - searchExpandedKeys.value = props.flattenOptions.map(o => o.key); - } - }, - { - immediate: true, - }, - ); - const mergedExpandedKeys = computed(() => { - if (context.value.treeExpandedKeys) { - return [...context.value.treeExpandedKeys]; - } - return props.searchValue ? searchExpandedKeys.value : expandedKeys.value; - }); - - const onInternalExpand = (keys: Key[]) => { - expandedKeys.value = keys; - searchExpandedKeys.value = keys; - - context.value.onTreeExpand?.(keys); - }; - - // ========================== Events ========================== - const onListMouseDown = (event: MouseEvent) => { - event.preventDefault(); - }; - - const onInternalSelect = (_: Key[], { node: { key } }: TreeEventInfo) => { - const { getEntityByKey, checkable, checkedKeys } = context.value; - const entity = getEntityByKey(key, checkable ? 'checkbox' : 'select'); - if (entity !== null) { - props.onSelect?.(entity.data.value, { - selected: !checkedKeys.includes(entity.data.value), - }); - } - - if (!props.multiple) { - props.onToggleOpen?.(false); - } - }; - - // ========================= Keyboard ========================= - const activeKey = ref(null); - const activeEntity = computed(() => context.value.getEntityByKey(activeKey.value)); - - const setActiveKey = (key: Key) => { - activeKey.value = key; - }; - expose({ - scrollTo: (...args: any[]) => treeRef.value?.scrollTo?.(...args), - onKeydown: (event: KeyboardEvent) => { - const { which } = event; - switch (which) { - // >>> Arrow keys - case KeyCode.UP: - case KeyCode.DOWN: - case KeyCode.LEFT: - case KeyCode.RIGHT: - treeRef.value?.onKeydown(event); - break; - - // >>> Select item - case KeyCode.ENTER: { - const { selectable, value } = activeEntity.value?.data.node || {}; - if (selectable !== false) { - onInternalSelect(null, { - node: { key: activeKey.value }, - selected: !context.value.checkedKeys.includes(value), - }); - } - break; - } - - // >>> Close - case KeyCode.ESC: { - props.onToggleOpen(false); - } - } - }, - onKeyup: () => {}, - } as ReviseRefOptionListProps); - - return () => { - const { - prefixCls, - height, - itemHeight, - virtual, - multiple, - searchValue, - open, - notFoundContent = slots.notFoundContent?.(), - onMouseenter, - } = props; - const { - checkable, - treeDefaultExpandAll, - treeIcon, - showTreeIcon, - switcherIcon, - treeLine, - loadData, - treeLoadedKeys, - treeMotion, - onTreeLoad, - } = context.value; - // ========================== Render ========================== - if (memoOptions.value.length === 0) { - return ( -
- {notFoundContent} -
- ); - } - - const treeProps: Partial = {}; - if (treeLoadedKeys) { - treeProps.loadedKeys = treeLoadedKeys; - } - if (mergedExpandedKeys.value) { - treeProps.expandedKeys = mergedExpandedKeys.value; - } - return ( -
- {activeEntity.value && open && ( - - {activeEntity.value.data.value} - - )} - - -
- ); - }; - }, -}); diff --git a/components/vc-tree-select1/TreeNode.tsx b/components/vc-tree-select1/TreeNode.tsx deleted file mode 100644 index af6b752e0..000000000 --- a/components/vc-tree-select1/TreeNode.tsx +++ /dev/null @@ -1,15 +0,0 @@ -/* istanbul ignore file */ - -import type { FunctionalComponent } from 'vue'; -import type { DataNode, Key } from './interface'; - -export interface TreeNodeProps extends Omit { - value: Key; -} - -/** This is a placeholder, not real render in dom */ -const TreeNode: FunctionalComponent & { isTreeSelectNode: boolean } = () => null; -TreeNode.inheritAttrs = false; -TreeNode.displayName = 'ATreeSelectNode'; -TreeNode.isTreeSelectNode = true; -export default TreeNode; diff --git a/components/vc-tree-select1/TreeSelect.tsx b/components/vc-tree-select1/TreeSelect.tsx deleted file mode 100644 index 1f2d1684c..000000000 --- a/components/vc-tree-select1/TreeSelect.tsx +++ /dev/null @@ -1,6 +0,0 @@ -import generate from './generate'; -import OptionList from './OptionList'; - -const TreeSelect = generate({ prefixCls: 'vc-tree-select', optionList: OptionList as any }); - -export default TreeSelect; diff --git a/components/vc-tree-select1/generate.tsx b/components/vc-tree-select1/generate.tsx deleted file mode 100644 index 228dddc0f..000000000 --- a/components/vc-tree-select1/generate.tsx +++ /dev/null @@ -1,514 +0,0 @@ -import type { GenerateConfig } from '../vc-select/generate'; -import generateSelector from '../vc-select/generate'; -import TreeNode from './TreeNode'; -import type { - DefaultValueType, - DataNode, - LabelValueType, - RawValueType, - ChangeEventExtra, - SelectSource, - FlattenDataNode, -} from './interface'; -import { - flattenOptions, - filterOptions, - isValueDisabled, - findValueOption, - addValue, - removeValue, - getRawValueLabeled, - toArray, - fillFieldNames, -} from './utils/valueUtil'; -import warningProps from './utils/warningPropsUtil'; -import { SelectContext } from './Context'; -import useTreeData from './hooks/useTreeData'; -import useKeyValueMap from './hooks/useKeyValueMap'; -import useKeyValueMapping from './hooks/useKeyValueMapping'; -import { formatStrategyKeys, SHOW_ALL, SHOW_PARENT, SHOW_CHILD } from './utils/strategyUtil'; -import { fillAdditionalInfo } from './utils/legacyUtil'; -import useSelectValues from './hooks/useSelectValues'; -import type { TreeSelectProps } from './props'; -import { treeSelectProps } from './props'; -import { getLabeledValue } from '../vc-select/utils/valueUtil'; -import omit from '../_util/omit'; -import { computed, defineComponent, ref, shallowRef, toRef, watch, watchEffect } from 'vue'; -import { convertDataToEntities } from '../vc-tree/utils/treeUtil'; -import { conductCheck } from '../vc-tree/utils/conductUtil'; -import { warning } from '../vc-util/warning'; -import { INTERNAL_PROPS_MARK } from '../vc-select/interface/generator'; - -const OMIT_PROPS: (keyof TreeSelectProps)[] = [ - 'expandedKeys' as any, - 'treeData', - 'treeCheckable', - 'showCheckedStrategy', - 'searchPlaceholder', - 'treeLine', - 'treeIcon', - 'showTreeIcon', - 'switcherIcon', - 'treeNodeFilterProp', - 'filterTreeNode', - 'dropdownPopupAlign', - 'treeDefaultExpandAll', - 'treeCheckStrictly', - 'treeExpandedKeys', - 'treeLoadedKeys', - 'treeMotion', - 'onTreeExpand', - 'onTreeLoad', - 'labelRender', - 'loadData', - 'treeDataSimpleMode', - 'treeNodeLabelProp', - 'treeDefaultExpandedKeys', - 'bordered', -]; - -export default function generate(config: { - prefixCls: string; - optionList: GenerateConfig['components']['optionList']; -}) { - const { prefixCls, optionList } = config; - - const RefSelect = generateSelector({ - prefixCls, - components: { - optionList, - }, - // Not use generate since we will handle ourself - convertChildrenToData: () => null, - flattenOptions, - // Handle `optionLabelProp` in TreeSelect component - getLabeledValue: getLabeledValue as any, - filterOptions, - isValueDisabled, - findValueOption, - omitDOMProps: (props: TreeSelectProps) => omit(props, OMIT_PROPS), - }); - - return defineComponent({ - name: 'TreeSelect', - props: treeSelectProps(), - slots: [ - 'title', - 'placeholder', - 'maxTagPlaceholder', - 'treeIcon', - 'switcherIcon', - 'notFoundContent', - 'treeCheckable', - ], - TreeNode, - SHOW_ALL, - SHOW_PARENT, - SHOW_CHILD, - setup(props, { expose, slots, attrs }) { - const mergedCheckable = computed(() => props.treeCheckable || props.treeCheckStrictly); - const mergedMultiple = computed(() => props.multiple || mergedCheckable.value); - const treeConduction = computed(() => props.treeCheckable && !props.treeCheckStrictly); - const mergedLabelInValue = computed(() => props.treeCheckStrictly || props.labelInValue); - - // ======================= Tree Data ======================= - // FieldNames - const mergedFieldNames = computed(() => fillFieldNames(props.fieldNames, true)); - // Legacy both support `label` or `title` if not set. - // We have to fallback to function to handle this - const getTreeNodeTitle = (node: DataNode) => { - if (!props.treeData) { - return node.title; - } - - if (mergedFieldNames.value?.label) { - return node[mergedFieldNames.value.label]; - } - - return node.label || node.title; - }; - - const getTreeNodeLabelProp = (entity: FlattenDataNode) => { - const { labelRender, treeNodeLabelProp } = props; - const { node } = entity.data; - - if (labelRender) { - return labelRender(entity); - } - - if (treeNodeLabelProp) { - return node[treeNodeLabelProp]; - } - - return getTreeNodeTitle(node); - }; - - const mergedTreeData = useTreeData(toRef(props, 'treeData'), toRef(props, 'children'), { - getLabelProp: getTreeNodeTitle, - simpleMode: toRef(props, 'treeDataSimpleMode'), - fieldNames: mergedFieldNames, - }); - - const flattedOptions = computed(() => flattenOptions(mergedTreeData.value)); - const [cacheKeyMap, cacheValueMap] = useKeyValueMap(flattedOptions); - const [getEntityByKey, getEntityByValue] = useKeyValueMapping(cacheKeyMap, cacheValueMap); - - // Only generate keyEntities for check conduction when is `treeCheckable` - const conductKeyEntities = computed(() => { - if (treeConduction.value) { - return convertDataToEntities(mergedTreeData.value).keyEntities; - } - return null; - }); - - // ========================== Ref ========================== - const selectRef = ref(); - - expose({ - scrollTo: (...args: any[]) => selectRef.value.scrollTo?.(...args), - focus: () => selectRef.value.focus?.(), - blur: () => selectRef.value?.blur(), - - /** @private Internal usage. It's save to remove if `rc-cascader` not use it any longer */ - getEntityByValue, - }); - - const valueRef = ref( - props.value === undefined ? props.defaultValue : props.value, - ); - - watch( - () => props.value, - () => { - valueRef.value = props.value; - }, - ); - - /** Get `missingRawValues` which not exist in the tree yet */ - const splitRawValues = (newRawValues: RawValueType[]) => { - const missingRawValues = []; - const existRawValues = []; - - // Keep missing value in the cache - newRawValues.forEach(val => { - if (getEntityByValue(val)) { - existRawValues.push(val); - } else { - missingRawValues.push(val); - } - }); - - return { missingRawValues, existRawValues }; - }; - - const rawValues = shallowRef([]); - const rawHalfCheckedKeys = shallowRef([]); - - watchEffect(() => { - const valueHalfCheckedKeys: RawValueType[] = []; - const newRawValues: RawValueType[] = []; - - toArray(valueRef.value).forEach(item => { - if (item && typeof item === 'object' && 'value' in item) { - if (item.halfChecked && props.treeCheckStrictly) { - const entity = getEntityByValue(item.value); - valueHalfCheckedKeys.push(entity ? entity.key : item.value); - } else { - newRawValues.push(item.value); - } - } else { - newRawValues.push(item as RawValueType); - } - }); - - // We need do conduction of values - if (treeConduction.value) { - const { missingRawValues, existRawValues } = splitRawValues(newRawValues); - const keyList = existRawValues.map(val => getEntityByValue(val).key); - - const { checkedKeys, halfCheckedKeys } = conductCheck( - keyList, - true, - conductKeyEntities.value, - ); - rawValues.value = [ - ...missingRawValues, - ...checkedKeys.map(key => getEntityByKey(key).data.value), - ]; - rawHalfCheckedKeys.value = halfCheckedKeys; - } else { - [rawValues.value, rawHalfCheckedKeys.value] = [newRawValues, valueHalfCheckedKeys]; - } - }); - - const selectValues = useSelectValues(rawValues, { - treeConduction, - value: valueRef, - showCheckedStrategy: toRef(props, 'showCheckedStrategy'), - conductKeyEntities, - getEntityByValue, - getEntityByKey, - getLabelProp: getTreeNodeLabelProp, - }); - - const triggerChange = ( - newRawValues: RawValueType[], - extra: { triggerValue: RawValueType; selected: boolean }, - source: SelectSource, - ) => { - const { onChange, showCheckedStrategy, treeCheckStrictly } = props; - const preValue = valueRef.value; - valueRef.value = mergedMultiple.value ? newRawValues : newRawValues[0]; - if (onChange) { - let eventValues: RawValueType[] = newRawValues; - if (treeConduction.value && showCheckedStrategy !== 'SHOW_ALL') { - const keyList = newRawValues.map(val => { - const entity = getEntityByValue(val); - return entity ? entity.key : val; - }); - const formattedKeyList = formatStrategyKeys( - keyList, - showCheckedStrategy, - conductKeyEntities.value, - ); - - eventValues = formattedKeyList.map(key => { - const entity = getEntityByKey(key); - return entity ? entity.data.value : key; - }); - } - - const { triggerValue, selected } = extra || { - triggerValue: undefined, - selected: undefined, - }; - - let returnValues = mergedLabelInValue.value - ? getRawValueLabeled(eventValues, preValue, getEntityByValue, getTreeNodeLabelProp) - : eventValues; - - // We need fill half check back - if (treeCheckStrictly) { - const halfValues = rawHalfCheckedKeys.value - .map(key => { - const entity = getEntityByKey(key); - return entity ? entity.data.value : key; - }) - .filter(val => !eventValues.includes(val)); - - returnValues = [ - ...(returnValues as LabelValueType[]), - ...getRawValueLabeled(halfValues, preValue, getEntityByValue, getTreeNodeLabelProp), - ]; - } - - const additionalInfo = { - // [Legacy] Always return as array contains label & value - preValue: selectValues.value, - triggerValue, - } as ChangeEventExtra; - - // [Legacy] Fill legacy data if user query. - // This is expansive that we only fill when user query - // https://github.com/react-component/tree-select/blob/fe33eb7c27830c9ac70cd1fdb1ebbe7bc679c16a/src/Select.jsx - let showPosition = true; - if (treeCheckStrictly || (source === 'selection' && !selected)) { - showPosition = false; - } - - fillAdditionalInfo( - additionalInfo, - triggerValue, - newRawValues, - mergedTreeData.value, - showPosition, - ); - - if (mergedCheckable.value) { - additionalInfo.checked = selected; - } else { - additionalInfo.selected = selected; - } - - onChange( - mergedMultiple.value ? returnValues : returnValues[0], - mergedLabelInValue.value - ? null - : eventValues.map(val => { - const entity = getEntityByValue(val); - return entity ? entity.data.title : null; - }), - additionalInfo, - ); - } - }; - - const onInternalSelect = ( - selectValue: RawValueType, - option: DataNode, - source: SelectSource, - ) => { - const eventValue = mergedLabelInValue.value ? selectValue : selectValue; - - if (!mergedMultiple.value) { - // Single mode always set value - triggerChange([selectValue], { selected: true, triggerValue: selectValue }, source); - } else { - let newRawValues = addValue(rawValues.value, selectValue); - - // Add keys if tree conduction - if (treeConduction.value) { - // Should keep missing values - const { missingRawValues, existRawValues } = splitRawValues(newRawValues); - const keyList = existRawValues.map(val => getEntityByValue(val).key); - const { checkedKeys } = conductCheck(keyList, true, conductKeyEntities.value); - newRawValues = [ - ...missingRawValues, - ...checkedKeys.map(key => getEntityByKey(key).data.value), - ]; - } - - triggerChange(newRawValues, { selected: true, triggerValue: selectValue }, source); - } - - props.onSelect?.(eventValue, option); - }; - - const onInternalDeselect = ( - selectValue: RawValueType, - option: DataNode, - source: SelectSource, - ) => { - const eventValue = selectValue; - - let newRawValues = removeValue(rawValues.value, selectValue); - - // Remove keys if tree conduction - if (treeConduction.value) { - const { missingRawValues, existRawValues } = splitRawValues(newRawValues); - const keyList = existRawValues.map(val => getEntityByValue(val).key); - const { checkedKeys } = conductCheck( - keyList, - { checked: false, halfCheckedKeys: rawHalfCheckedKeys.value }, - conductKeyEntities.value, - ); - newRawValues = [ - ...missingRawValues, - ...checkedKeys.map(key => getEntityByKey(key).data.value), - ]; - } - - triggerChange(newRawValues, { selected: false, triggerValue: selectValue }, source); - - props.onDeselect?.(eventValue, option); - }; - - const onInternalClear = () => { - triggerChange([], null, 'clear'); - }; - - // ========================= Open ========================== - const onInternalDropdownVisibleChange = (open: boolean) => { - if (props.onDropdownVisibleChange) { - const legacyParam = {}; - - Object.defineProperty(legacyParam, 'documentClickClose', { - get() { - warning(false, 'Second param of `onDropdownVisibleChange` has been removed.'); - return false; - }, - }); - - (props.onDropdownVisibleChange as any)(open, legacyParam); - } - }; - - // ======================== Warning ======================== - if (process.env.NODE_ENV !== 'production') { - warningProps(props); - } - - return () => { - const { - treeNodeFilterProp, - dropdownPopupAlign, - filterTreeNode, - treeDefaultExpandAll, - treeExpandedKeys, - treeDefaultExpandedKeys, - onTreeExpand, - treeIcon, - treeMotion, - showTreeIcon, - switcherIcon, - treeLine, - loadData, - treeLoadedKeys, - onTreeLoad, - } = props; - // ======================== Render ========================= - // We pass some props into select props style - const selectProps = { - optionLabelProp: null, - optionFilterProp: treeNodeFilterProp, - dropdownAlign: dropdownPopupAlign, - internalProps: { - mark: INTERNAL_PROPS_MARK, - onClear: onInternalClear, - skipTriggerChange: true, - skipTriggerSelect: true, - onRawSelect: onInternalSelect, - onRawDeselect: onInternalDeselect, - }, - filterOption: filterTreeNode, - }; - - if (props.filterTreeNode === undefined) { - delete selectProps.filterOption; - } - const selectContext = { - checkable: mergedCheckable.value, - loadData, - treeLoadedKeys, - onTreeLoad, - checkedKeys: rawValues.value, - halfCheckedKeys: rawHalfCheckedKeys.value, - treeDefaultExpandAll, - treeExpandedKeys, - treeDefaultExpandedKeys, - onTreeExpand, - treeIcon, - treeMotion, - showTreeIcon, - switcherIcon, - treeLine, - treeNodeFilterProp, - getEntityByKey, - getEntityByValue, - customCheckable: slots.treeCheckable, - slots, - }; - return ( - - - - ); - }; - }, - }); -} diff --git a/components/vc-tree-select1/hooks/useKeyValueMap.ts b/components/vc-tree-select1/hooks/useKeyValueMap.ts deleted file mode 100644 index c0ee006ae..000000000 --- a/components/vc-tree-select1/hooks/useKeyValueMap.ts +++ /dev/null @@ -1,25 +0,0 @@ -import type { ComputedRef, Ref } from 'vue'; -import { shallowRef, watchEffect } from 'vue'; -import type { FlattenDataNode, Key, RawValueType } from '../interface'; - -/** - * Return cached Key Value map with DataNode. - * Only re-calculate when `flattenOptions` changed. - */ -export default function useKeyValueMap(flattenOptions: ComputedRef) { - const cacheKeyMap: Ref> = shallowRef(new Map()); - const cacheValueMap: Ref> = shallowRef(new Map()); - - watchEffect(() => { - const newCacheKeyMap = new Map(); - const newCacheValueMap = new Map(); - // Cache options by key - flattenOptions.value.forEach((dataNode: FlattenDataNode) => { - newCacheKeyMap.set(dataNode.key, dataNode); - newCacheValueMap.set(dataNode.data.value, dataNode); - }); - cacheKeyMap.value = newCacheKeyMap; - cacheValueMap.value = newCacheValueMap; - }); - return [cacheKeyMap, cacheValueMap]; -} diff --git a/components/vc-tree-select1/hooks/useKeyValueMapping.ts b/components/vc-tree-select1/hooks/useKeyValueMapping.ts deleted file mode 100644 index e385a035a..000000000 --- a/components/vc-tree-select1/hooks/useKeyValueMapping.ts +++ /dev/null @@ -1,58 +0,0 @@ -import type { Ref } from 'vue'; -import type { FlattenDataNode, Key, RawValueType } from '../interface'; - -export type SkipType = null | 'select' | 'checkbox'; - -export function isDisabled(dataNode: FlattenDataNode, skipType: SkipType): boolean { - if (!dataNode) { - return true; - } - - const { disabled, disableCheckbox } = dataNode.data.node; - - switch (skipType) { - case 'checkbox': - return disabled || disableCheckbox; - - default: - return disabled; - } -} - -export default function useKeyValueMapping( - cacheKeyMap: Ref>, - cacheValueMap: Ref>, -): [ - (key: Key, skipType?: SkipType, ignoreDisabledCheck?: boolean) => FlattenDataNode, - (value: RawValueType, skipType?: SkipType, ignoreDisabledCheck?: boolean) => FlattenDataNode, -] { - const getEntityByKey = ( - key: Key, - skipType: SkipType = 'select', - ignoreDisabledCheck?: boolean, - ) => { - const dataNode = cacheKeyMap.value.get(key); - - if (!ignoreDisabledCheck && isDisabled(dataNode, skipType)) { - return null; - } - - return dataNode; - }; - - const getEntityByValue = ( - value: RawValueType, - skipType: SkipType = 'select', - ignoreDisabledCheck?: boolean, - ) => { - const dataNode = cacheValueMap.value.get(value); - - if (!ignoreDisabledCheck && isDisabled(dataNode, skipType)) { - return null; - } - - return dataNode; - }; - - return [getEntityByKey, getEntityByValue]; -} diff --git a/components/vc-tree-select1/hooks/useSelectValues.ts b/components/vc-tree-select1/hooks/useSelectValues.ts deleted file mode 100644 index a8cc663fc..000000000 --- a/components/vc-tree-select1/hooks/useSelectValues.ts +++ /dev/null @@ -1,67 +0,0 @@ -import type { RawValueType, FlattenDataNode, Key, LabelValueType } from '../interface'; -import type { SkipType } from './useKeyValueMapping'; -import { getRawValueLabeled } from '../utils/valueUtil'; -import type { CheckedStrategy } from '../utils/strategyUtil'; -import { formatStrategyKeys } from '../utils/strategyUtil'; -import type { DefaultValueType } from '../../vc-select/interface/generator'; -import type { DataEntity } from '../../vc-tree/interface'; -import type { Ref } from 'vue'; -import { shallowRef, watchEffect } from 'vue'; - -interface Config { - treeConduction: Ref; - /** Current `value` of TreeSelect */ - value: Ref; - showCheckedStrategy: Ref; - conductKeyEntities: Ref>; - getEntityByKey: (key: Key, skipType?: SkipType, ignoreDisabledCheck?: boolean) => FlattenDataNode; - getEntityByValue: ( - value: RawValueType, - skipType?: SkipType, - ignoreDisabledCheck?: boolean, - ) => FlattenDataNode; - getLabelProp: (entity: FlattenDataNode) => any; -} - -/** Return */ -export default function useSelectValues( - rawValues: Ref, - { - value, - getEntityByValue, - getEntityByKey, - treeConduction, - showCheckedStrategy, - conductKeyEntities, - getLabelProp, - }: Config, -): Ref { - const rawValueLabeled = shallowRef([]); - watchEffect(() => { - let mergedRawValues = rawValues.value; - - if (treeConduction.value) { - const rawKeys = formatStrategyKeys( - rawValues.value.map(val => { - const entity = getEntityByValue(val); - return entity ? entity.key : val; - }), - showCheckedStrategy.value, - conductKeyEntities.value, - ); - - mergedRawValues = rawKeys.map(key => { - const entity = getEntityByKey(key); - return entity ? entity.data.value : key; - }); - } - - rawValueLabeled.value = getRawValueLabeled( - mergedRawValues, - value.value, - getEntityByValue, - getLabelProp, - ); - }); - return rawValueLabeled; -} diff --git a/components/vc-tree-select1/hooks/useTreeData.ts b/components/vc-tree-select1/hooks/useTreeData.ts deleted file mode 100644 index a274f3173..000000000 --- a/components/vc-tree-select1/hooks/useTreeData.ts +++ /dev/null @@ -1,155 +0,0 @@ -import { warning } from '../../vc-util/warning'; -import type { ComputedRef, Ref } from 'vue'; -import { computed } from 'vue'; -import type { - DataNode, - InternalDataEntity, - SimpleModeConfig, - RawValueType, - FieldNames, -} from '../interface'; -import { convertChildrenToData } from '../utils/legacyUtil'; - -const MAX_WARNING_TIMES = 10; - -function parseSimpleTreeData( - treeData: DataNode[], - { id, pId, rootPId }: SimpleModeConfig, -): DataNode[] { - const keyNodes = {}; - const rootNodeList = []; - - // Fill in the map - const nodeList = treeData.map(node => { - const clone = { ...node }; - const key = clone[id]; - keyNodes[key] = clone; - clone.key = clone.key || key; - return clone; - }); - - // Connect tree - nodeList.forEach(node => { - const parentKey = node[pId]; - const parent = keyNodes[parentKey]; - - // Fill parent - if (parent) { - parent.children = parent.children || []; - parent.children.push(node); - } - - // Fill root tree node - if (parentKey === rootPId || (!parent && rootPId === null)) { - rootNodeList.push(node); - } - }); - - return rootNodeList; -} - -/** - * Format `treeData` with `value` & `key` which is used for calculation - */ -function formatTreeData( - treeData: DataNode[], - getLabelProp: (node: DataNode) => any, - fieldNames: FieldNames, -): InternalDataEntity[] { - let warningTimes = 0; - const valueSet = new Set(); - - // Field names - const { value: fieldValue, children: fieldChildren } = fieldNames; - - function dig(dataNodes: DataNode[]) { - return (dataNodes || []).map(node => { - const { key, disableCheckbox, disabled, checkable, selectable, isLeaf } = node; - - const value = node[fieldValue]; - const mergedValue = fieldValue in node ? value : key; - - const dataNode: InternalDataEntity = { - disableCheckbox, - disabled, - key: key !== null && key !== undefined ? key : mergedValue, - value: mergedValue, - title: getLabelProp(node), - node, - selectable, - isLeaf, - dataRef: node, - checkable, - }; - - if (node.slots) { - dataNode.slots = node.slots; - } - - // Check `key` & `value` and warning user - if (process.env.NODE_ENV !== 'production') { - if ( - key !== null && - key !== undefined && - value !== undefined && - String(key) !== String(value) && - warningTimes < MAX_WARNING_TIMES - ) { - warningTimes += 1; - warning( - false, - `\`key\` or \`value\` with TreeNode must be the same or you can remove one of them. key: ${key}, value: ${value}.`, - ); - } - - warning(!valueSet.has(value), `Same \`value\` exist in the tree: ${value}`); - valueSet.add(value); - } - - if (fieldChildren in node) { - dataNode.children = dig(node[fieldChildren]); - } - - return dataNode; - }); - } - - return dig(treeData); -} - -/** - * Convert `treeData` or `children` into formatted `treeData`. - * Will not re-calculate if `treeData` or `children` not change. - */ -export default function useTreeData( - treeData: Ref, - children: Ref, - { - getLabelProp, - simpleMode, - fieldNames, - }: { - getLabelProp: (node: DataNode) => any; - simpleMode: Ref; - fieldNames: Ref; - }, -): ComputedRef { - return computed(() => { - if (treeData.value) { - return formatTreeData( - simpleMode.value - ? parseSimpleTreeData(treeData.value, { - id: 'id', - pId: 'pId', - rootPId: null, - ...(simpleMode.value !== true ? simpleMode.value : {}), - }) - : treeData.value, - getLabelProp, - fieldNames.value, - ); - } else { - return formatTreeData(convertChildrenToData(children.value), getLabelProp, fieldNames.value); - } - }); -} diff --git a/components/vc-tree-select1/index.tsx b/components/vc-tree-select1/index.tsx deleted file mode 100644 index d3e52682a..000000000 --- a/components/vc-tree-select1/index.tsx +++ /dev/null @@ -1,11 +0,0 @@ -// base rc-tree-select@4.6.1 -import TreeSelect from './TreeSelect'; -import TreeNode from './TreeNode'; -import { SHOW_ALL, SHOW_CHILD, SHOW_PARENT } from './utils/strategyUtil'; -import type { TreeSelectProps } from './props'; -import { treeSelectProps } from './props'; - -export { TreeNode, SHOW_ALL, SHOW_CHILD, SHOW_PARENT, treeSelectProps }; -export type { TreeSelectProps }; - -export default TreeSelect; diff --git a/components/vc-tree-select1/interface.ts b/components/vc-tree-select1/interface.ts deleted file mode 100644 index c39d58239..000000000 --- a/components/vc-tree-select1/interface.ts +++ /dev/null @@ -1,101 +0,0 @@ -export type SelectSource = 'option' | 'selection' | 'input' | 'clear'; - -export type Key = string | number; - -export type RawValueType = string | number; - -export interface LabelValueType { - key?: Key; - value?: RawValueType; - label?: any; - /** Only works on `treeCheckStrictly` */ - halfChecked?: boolean; -} - -export type DefaultValueType = RawValueType | RawValueType[] | LabelValueType | LabelValueType[]; - -export interface DataNode { - value?: RawValueType; - title?: any; - label?: any; - key?: Key; - disabled?: boolean; - disableCheckbox?: boolean; - checkable?: boolean; - selectable?: boolean; - children?: DataNode[]; - - /** Customize data info */ - [prop: string]: any; -} - -export interface InternalDataEntity { - key: Key; - value: RawValueType; - title?: any; - checkable: boolean; - disableCheckbox: boolean; - disabled: boolean; - selectable: boolean; - isLeaf: boolean; - children?: InternalDataEntity[]; - - /** Origin DataNode */ - node: DataNode; - - dataRef: DataNode; - - slots?: Record; // 兼容 V2 -} - -export interface LegacyDataNode extends DataNode { - props: any; -} - -export interface TreeDataNode extends DataNode { - key: Key; - children?: TreeDataNode[]; -} - -export interface FlattenDataNode { - data: InternalDataEntity; - key: Key; - value: RawValueType; - level: number; - parent?: FlattenDataNode; -} - -export interface SimpleModeConfig { - id?: Key; - pId?: Key; - rootPId?: Key; -} - -/** @deprecated This is only used for legacy compatible. Not works on new code. */ -export interface LegacyCheckedNode { - pos: string; - node: any; - children?: LegacyCheckedNode[]; -} - -export interface ChangeEventExtra { - /** @deprecated Please save prev value by control logic instead */ - preValue: LabelValueType[]; - triggerValue: RawValueType; - /** @deprecated Use `onSelect` or `onDeselect` instead. */ - selected?: boolean; - /** @deprecated Use `onSelect` or `onDeselect` instead. */ - checked?: boolean; - - // Not sure if exist user still use this. We have to keep but not recommend user to use - /** @deprecated This prop not work as react node anymore. */ - triggerNode: any; - /** @deprecated This prop not work as react node anymore. */ - allCheckedNodes: LegacyCheckedNode[]; -} - -export interface FieldNames { - value?: string; - label?: string; - children?: string; -} diff --git a/components/vc-tree-select1/props.ts b/components/vc-tree-select1/props.ts deleted file mode 100644 index 98b40c432..000000000 --- a/components/vc-tree-select1/props.ts +++ /dev/null @@ -1,140 +0,0 @@ -import type { ExtractPropTypes, PropType } from 'vue'; -import type { - DataNode, - ChangeEventExtra, - DefaultValueType, - FieldNames, - FlattenDataNode, - LabelValueType, - LegacyDataNode, - RawValueType, - SimpleModeConfig, -} from './interface'; -import { selectBaseProps } from '../vc-select'; -import type { FilterFunc } from '../vc-select/interface/generator'; -import omit from '../_util/omit'; -import type { Key } from '../_util/type'; -import PropTypes from '../_util/vue-types'; -import type { CheckedStrategy } from './utils/strategyUtil'; - -export function optionListProps() { - return { - prefixCls: String, - id: String, - options: { type: Array as PropType }, - flattenOptions: { type: Array as PropType }, - height: Number, - itemHeight: Number, - virtual: { type: Boolean, default: undefined }, - values: { type: Set as PropType> }, - multiple: { type: Boolean, default: undefined }, - open: { type: Boolean, default: undefined }, - defaultActiveFirstOption: { type: Boolean, default: undefined }, - notFoundContent: PropTypes.any, - menuItemSelectedIcon: PropTypes.any, - childrenAsData: { type: Boolean, default: undefined }, - searchValue: String, - - onSelect: { - type: Function as PropType<(value: RawValueType, option: { selected: boolean }) => void>, - }, - onToggleOpen: { type: Function as PropType<(open?: boolean) => void> }, - /** Tell Select that some value is now active to make accessibility work */ - onActiveValue: { type: Function as PropType<(value: RawValueType, index: number) => void> }, - onScroll: { type: Function as PropType<(e: UIEvent) => void> }, - - onMouseenter: { type: Function as PropType<() => void> }, - }; -} - -export function treeSelectProps() { - const selectProps = omit(selectBaseProps(), [ - 'onChange', - 'mode', - 'menuItemSelectedIcon', - 'dropdownAlign', - 'backfill', - 'getInputElement', - 'optionLabelProp', - 'tokenSeparators', - 'filterOption', - ]); - return { - ...selectProps, - - multiple: { type: Boolean, default: undefined }, - showArrow: { type: Boolean, default: undefined }, - showSearch: { type: Boolean, default: undefined }, - open: { type: Boolean, default: undefined }, - defaultOpen: { type: Boolean, default: undefined }, - value: { type: [String, Number, Object, Array] as PropType }, - defaultValue: { type: [String, Number, Object, Array] as PropType }, - disabled: { type: Boolean, default: undefined }, - - placeholder: PropTypes.any, - /** @deprecated Use `searchValue` instead */ - inputValue: String, - searchValue: String, - autoClearSearchValue: { type: Boolean, default: undefined }, - - maxTagPlaceholder: { type: Function as PropType<(omittedValues: LabelValueType[]) => any> }, - - fieldNames: { type: Object as PropType }, - loadData: { type: Function as PropType<(dataNode: LegacyDataNode) => Promise> }, - treeNodeFilterProp: String, - treeNodeLabelProp: String, - treeDataSimpleMode: { - type: [Boolean, Object] as PropType, - default: undefined, - }, - treeExpandedKeys: { type: Array as PropType }, - treeDefaultExpandedKeys: { type: Array as PropType }, - treeLoadedKeys: { type: Array as PropType }, - treeCheckable: { type: Boolean, default: undefined }, - treeCheckStrictly: { type: Boolean, default: undefined }, - showCheckedStrategy: { type: String as PropType }, - treeDefaultExpandAll: { type: Boolean, default: undefined }, - treeData: { type: Array as PropType }, - treeLine: { type: Boolean, default: undefined }, - treeIcon: PropTypes.any, - showTreeIcon: { type: Boolean, default: undefined }, - switcherIcon: PropTypes.any, - treeMotion: PropTypes.any, - children: Array, - - filterTreeNode: { - type: [Boolean, Function] as PropType>, - default: undefined, - }, - dropdownPopupAlign: PropTypes.any, - - // Event - onSearch: { type: Function as PropType<(value: string) => void> }, - onChange: { - type: Function as PropType< - (value: ValueType, labelList: any[], extra: ChangeEventExtra) => void - >, - }, - onTreeExpand: { type: Function as PropType<(expandedKeys: Key[]) => void> }, - onTreeLoad: { type: Function as PropType<(loadedKeys: Key[]) => void> }, - onDropdownVisibleChange: { type: Function as PropType<(open: boolean) => void> }, - - // Legacy - /** `searchPlaceholder` has been removed since search box has been merged into input box */ - searchPlaceholder: PropTypes.any, - - /** @private This is not standard API since we only used in `rc-cascader`. Do not use in your production */ - labelRender: { type: Function as PropType<(entity: FlattenDataNode) => any> }, - }; -} - -class Helper { - ReturnOptionListProps = optionListProps(); - ReturnTreeSelectProps = treeSelectProps(); -} - -export type OptionListProps = Partial['ReturnOptionListProps']>>; - -export type TreeSelectProps = Partial< - ExtractPropTypes['ReturnTreeSelectProps']> ->; diff --git a/components/vc-tree-select1/utils/legacyUtil.tsx b/components/vc-tree-select1/utils/legacyUtil.tsx deleted file mode 100644 index 8c2e358f3..000000000 --- a/components/vc-tree-select1/utils/legacyUtil.tsx +++ /dev/null @@ -1,181 +0,0 @@ -import { filterEmpty } from '../../_util/props-util'; -import { camelize } from 'vue'; -import { warning } from '../../vc-util/warning'; -import type { - DataNode, - LegacyDataNode, - ChangeEventExtra, - InternalDataEntity, - RawValueType, - LegacyCheckedNode, -} from '../interface'; -import TreeNode from '../TreeNode'; -import type { VueNode } from '../../_util/type'; - -function isTreeSelectNode(node: any) { - return node && node.type && (node.type as any).isTreeSelectNode; -} -export function convertChildrenToData(rootNodes: VueNode): DataNode[] { - function dig(treeNodes: any[] = []): DataNode[] { - return filterEmpty(treeNodes).map(treeNode => { - // Filter invalidate node - if (!isTreeSelectNode(treeNode)) { - warning(!treeNode, 'TreeSelect/TreeSelectNode can only accept TreeSelectNode as children.'); - return null; - } - const slots = (treeNode.children as any) || {}; - const key = treeNode.key as string | number; - const props: any = {}; - for (const [k, v] of Object.entries(treeNode.props)) { - props[camelize(k)] = v; - } - const { isLeaf, checkable, selectable, disabled, disableCheckbox } = props; - // 默认值为 undefined - const newProps = { - isLeaf: isLeaf || isLeaf === '' || undefined, - checkable: checkable || checkable === '' || undefined, - selectable: selectable || selectable === '' || undefined, - disabled: disabled || disabled === '' || undefined, - disableCheckbox: disableCheckbox || disableCheckbox === '' || undefined, - }; - const slotsProps = { ...props, ...newProps }; - const { - title = slots.title?.(slotsProps), - switcherIcon = slots.switcherIcon?.(slotsProps), - ...rest - } = props; - const children = slots.default?.(); - const dataNode: DataNode = { - ...rest, - title, - switcherIcon, - key, - isLeaf, - ...newProps, - }; - - const parsedChildren = dig(children); - if (parsedChildren.length) { - dataNode.children = parsedChildren; - } - - return dataNode; - }); - } - - return dig(rootNodes as any[]); -} - -export function fillLegacyProps(dataNode: DataNode): LegacyDataNode { - // Skip if not dataNode exist - if (!dataNode) { - return dataNode as LegacyDataNode; - } - - const cloneNode = { ...dataNode }; - - if (!('props' in cloneNode)) { - Object.defineProperty(cloneNode, 'props', { - get() { - warning( - false, - 'New `rc-tree-select` not support return node instance as argument anymore. Please consider to remove `props` access.', - ); - return cloneNode; - }, - }); - } - - return cloneNode as LegacyDataNode; -} - -export function fillAdditionalInfo( - extra: ChangeEventExtra, - triggerValue: RawValueType, - checkedValues: RawValueType[], - treeData: InternalDataEntity[], - showPosition: boolean, -) { - let triggerNode = null; - let nodeList: LegacyCheckedNode[] = null; - - function generateMap() { - function dig(list: InternalDataEntity[], level = '0', parentIncluded = false) { - return list - .map((dataNode, index) => { - const pos = `${level}-${index}`; - const included = checkedValues.includes(dataNode.value); - const children = dig(dataNode.children || [], pos, included); - const node = {children.map(child => child.node)}; - - // Link with trigger node - if (triggerValue === dataNode.value) { - triggerNode = node; - } - - if (included) { - const checkedNode: LegacyCheckedNode = { - pos, - node, - children, - }; - - if (!parentIncluded) { - nodeList.push(checkedNode); - } - - return checkedNode; - } - return null; - }) - .filter(node => node); - } - - if (!nodeList) { - nodeList = []; - - dig(treeData); - - // Sort to keep the checked node length - nodeList.sort( - ( - { - node: { - props: { value: val1 }, - }, - }, - { - node: { - props: { value: val2 }, - }, - }, - ) => { - const index1 = checkedValues.indexOf(val1); - const index2 = checkedValues.indexOf(val2); - return index1 - index2; - }, - ); - } - } - - Object.defineProperty(extra, 'triggerNode', { - get() { - warning(false, '`triggerNode` is deprecated. Please consider decoupling data with node.'); - generateMap(); - - return triggerNode; - }, - }); - Object.defineProperty(extra, 'allCheckedNodes', { - get() { - warning(false, '`allCheckedNodes` is deprecated. Please consider decoupling data with node.'); - generateMap(); - - if (showPosition) { - return nodeList; - } - - return nodeList.map(({ node }) => node); - }, - }); -} diff --git a/components/vc-tree-select1/utils/strategyUtil.ts b/components/vc-tree-select1/utils/strategyUtil.ts deleted file mode 100644 index ad47e768a..000000000 --- a/components/vc-tree-select1/utils/strategyUtil.ts +++ /dev/null @@ -1,46 +0,0 @@ -import type { DataEntity } from '../../vc-tree/interface'; -import type { RawValueType, Key, DataNode } from '../interface'; -import { isCheckDisabled } from './valueUtil'; - -export const SHOW_ALL = 'SHOW_ALL'; -export const SHOW_PARENT = 'SHOW_PARENT'; -export const SHOW_CHILD = 'SHOW_CHILD'; - -export type CheckedStrategy = typeof SHOW_ALL | typeof SHOW_PARENT | typeof SHOW_CHILD; - -export function formatStrategyKeys( - keys: Key[], - strategy: CheckedStrategy, - keyEntities: Record, -): RawValueType[] { - const keySet = new Set(keys); - - if (strategy === SHOW_CHILD) { - return keys.filter(key => { - const entity = keyEntities[key]; - - if ( - entity && - entity.children && - entity.children.every( - ({ node }) => isCheckDisabled(node) || keySet.has((node as DataNode).key), - ) - ) { - return false; - } - return true; - }); - } - if (strategy === SHOW_PARENT) { - return keys.filter(key => { - const entity = keyEntities[key]; - const parent = entity ? entity.parent : null; - - if (parent && !isCheckDisabled(parent.node) && keySet.has((parent.node as DataNode).key)) { - return false; - } - return true; - }); - } - return keys; -} diff --git a/components/vc-tree-select1/utils/valueUtil.ts b/components/vc-tree-select1/utils/valueUtil.ts deleted file mode 100644 index 33f71a74f..000000000 --- a/components/vc-tree-select1/utils/valueUtil.ts +++ /dev/null @@ -1,244 +0,0 @@ -import type { - FlattenDataNode, - Key, - RawValueType, - DataNode, - DefaultValueType, - LabelValueType, - LegacyDataNode, - FieldNames, - InternalDataEntity, -} from '../interface'; -import { fillLegacyProps } from './legacyUtil'; -import type { SkipType } from '../hooks/useKeyValueMapping'; -import type { FlattenNode } from '../../vc-tree/interface'; -import { flattenTreeData } from '../../vc-tree/utils/treeUtil'; -import type { FilterFunc } from '../../vc-select/interface/generator'; - -type CompatibleDataNode = Omit; - -export function toArray(value: T | T[]): T[] { - if (Array.isArray(value)) { - return value; - } - return value !== undefined ? [value] : []; -} - -/** - * Fill `fieldNames` with default field names. - * - * @param fieldNames passed props - * @param skipTitle Skip if no need fill `title`. This is useful since we have 2 name as same title level - * @returns - */ -export function fillFieldNames(fieldNames?: FieldNames, skipTitle = false) { - const { label, value, children } = fieldNames || {}; - - const filledNames: FieldNames = { - value: value || 'value', - children: children || 'children', - }; - - if (!skipTitle || label) { - filledNames.label = label || 'label'; - } - - return filledNames; -} - -export function findValueOption(values: RawValueType[], options: CompatibleDataNode[]): DataNode[] { - const optionMap: Map = new Map(); - - options.forEach(flattenItem => { - const { data, value } = flattenItem; - optionMap.set(value, data.node); - }); - - return values.map(val => fillLegacyProps(optionMap.get(val))); -} - -export function isValueDisabled(value: RawValueType, options: CompatibleDataNode[]): boolean { - const option = findValueOption([value], options)[0]; - if (option) { - return option.disabled; - } - - return false; -} - -export function isCheckDisabled(node: DataNode) { - return node.disabled || node.disableCheckbox || node.checkable === false; -} - -interface TreeDataNode extends InternalDataEntity { - key: Key; - children?: TreeDataNode[]; -} - -function getLevel({ parent }: FlattenNode): number { - let level = 0; - let current = parent; - - while (current) { - current = current.parent; - level += 1; - } - - return level; -} - -/** - * Before reuse `rc-tree` logic, we need to add key since TreeSelect use `value` instead of `key`. - */ -export function flattenOptions(options: any): FlattenDataNode[] { - const typedOptions = options as InternalDataEntity[]; - - // Add missing key - function fillKey(list: InternalDataEntity[]): TreeDataNode[] { - return (list || []).map(node => { - const { value, key, children } = node; - - const clone: TreeDataNode = { - ...node, - key: 'key' in node ? key : value, - }; - - if (children) { - clone.children = fillKey(children); - } - - return clone; - }); - } - - const flattenList = flattenTreeData(fillKey(typedOptions), true, null); - - const cacheMap = new Map(); - const flattenDateNodeList: (FlattenDataNode & { parentKey?: Key })[] = flattenList.map(option => { - const { data, key, value } = option as any as Omit & { - value: RawValueType; - data: InternalDataEntity; - }; - - const flattenNode = { - key, - value, - data, - level: getLevel(option), - parentKey: option.parent?.data.key, - }; - - cacheMap.set(key, flattenNode); - - return flattenNode; - }); - - // Fill parent - flattenDateNodeList.forEach(flattenNode => { - // eslint-disable-next-line no-param-reassign - flattenNode.parent = cacheMap.get(flattenNode.parentKey); - }); - - return flattenDateNodeList; -} - -function getDefaultFilterOption(optionFilterProp: string) { - return (searchValue: string, dataNode: LegacyDataNode) => { - const value = dataNode[optionFilterProp]; - - return String(value).toLowerCase().includes(String(searchValue).toLowerCase()); - }; -} - -/** Filter options and return a new options by the search text */ -export function filterOptions( - searchValue: string, - options: DataNode[], - { - optionFilterProp, - filterOption, - }: { - optionFilterProp: string; - filterOption: boolean | FilterFunc; - }, -): DataNode[] { - if (filterOption === false) { - return options; - } - - let filterOptionFunc: FilterFunc; - if (typeof filterOption === 'function') { - filterOptionFunc = filterOption; - } else { - filterOptionFunc = getDefaultFilterOption(optionFilterProp); - } - - function dig(list: DataNode[], keepAll = false) { - return list - .map(dataNode => { - const { children } = dataNode; - - const match = keepAll || filterOptionFunc(searchValue, fillLegacyProps(dataNode)); - const childList = dig(children || [], match); - - if (match || childList.length) { - return { - ...dataNode, - children: childList, - }; - } - return null; - }) - .filter(node => node); - } - - return dig(options); -} - -export function getRawValueLabeled( - values: RawValueType[], - prevValue: DefaultValueType, - getEntityByValue: ( - value: RawValueType, - skipType?: SkipType, - ignoreDisabledCheck?: boolean, - ) => FlattenDataNode, - getLabelProp: (entity: FlattenDataNode) => any, -): LabelValueType[] { - const valueMap = new Map(); - - toArray(prevValue).forEach(item => { - if (item && typeof item === 'object' && 'value' in item) { - valueMap.set(item.value, item); - } - }); - - return values.map(val => { - const item: LabelValueType = { value: val }; - const entity = getEntityByValue(val, 'select', true); - const label = entity ? getLabelProp(entity) : val; - - if (valueMap.has(val)) { - const labeledValue = valueMap.get(val); - item.label = 'label' in labeledValue ? labeledValue.label : label; - if ('halfChecked' in labeledValue) { - item.halfChecked = labeledValue.halfChecked; - } - } else { - item.label = label; - } - - return item; - }); -} - -export function addValue(rawValues: RawValueType[], value: RawValueType) { - const values = new Set(rawValues); - values.add(value); - return Array.from(values); -} -export function removeValue(rawValues: RawValueType[], value: RawValueType) { - const values = new Set(rawValues); - values.delete(value); - return Array.from(values); -} diff --git a/components/vc-tree-select1/utils/warningPropsUtil.ts b/components/vc-tree-select1/utils/warningPropsUtil.ts deleted file mode 100644 index 2b58b8936..000000000 --- a/components/vc-tree-select1/utils/warningPropsUtil.ts +++ /dev/null @@ -1,34 +0,0 @@ -import { warning } from '../../vc-util/warning'; -import { toArray } from './valueUtil'; - -function warningProps(props: any) { - const { searchPlaceholder, treeCheckStrictly, treeCheckable, labelInValue, value, multiple } = - props; - - warning( - !searchPlaceholder, - '`searchPlaceholder` has been removed, please use `placeholder` instead', - ); - - if (treeCheckStrictly && labelInValue === false) { - warning(false, '`treeCheckStrictly` will force set `labelInValue` to `true`.'); - } - - if (labelInValue || treeCheckStrictly) { - warning( - toArray(value).every(val => val && typeof val === 'object' && 'value' in val), - 'Invalid prop `value` supplied to `TreeSelect`. You should use { label: string, value: string | number } or [{ label: string, value: string | number }] instead.', - ); - } - - if (treeCheckStrictly || multiple || treeCheckable) { - warning( - !value || Array.isArray(value), - '`value` should be an array when `TreeSelect` is checkable or multiple.', - ); - } else { - warning(!Array.isArray(value), '`value` should not be array when `TreeSelect` is single mode.'); - } -} - -export default warningProps;