refactor: tree-select
parent
6982052e74
commit
314461848c
|
@ -0,0 +1,63 @@
|
||||||
|
/**
|
||||||
|
* BaseSelect provide some parsed data into context.
|
||||||
|
* You can use this hooks to get them.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import type { InjectionKey } from 'vue';
|
||||||
|
import { inject, provide } from 'vue';
|
||||||
|
import type { DataEntity, IconType } from '../vc-tree/interface';
|
||||||
|
import type { Key, LegacyDataNode, RawValueType } from './interface';
|
||||||
|
|
||||||
|
interface LegacyContextProps {
|
||||||
|
checkable: boolean;
|
||||||
|
checkedKeys: Key[];
|
||||||
|
customCheckable: () => any;
|
||||||
|
halfCheckedKeys: Key[];
|
||||||
|
treeExpandedKeys: Key[];
|
||||||
|
treeDefaultExpandedKeys: Key[];
|
||||||
|
onTreeExpand: (keys: Key[]) => void;
|
||||||
|
treeDefaultExpandAll: boolean;
|
||||||
|
treeIcon: IconType;
|
||||||
|
showTreeIcon: boolean;
|
||||||
|
switcherIcon: IconType;
|
||||||
|
treeLine: boolean;
|
||||||
|
treeNodeFilterProp: string;
|
||||||
|
treeLoadedKeys: Key[];
|
||||||
|
treeMotion: any;
|
||||||
|
loadData: (treeNode: LegacyDataNode) => Promise<unknown>;
|
||||||
|
onTreeLoad: (loadedKeys: Key[]) => void;
|
||||||
|
|
||||||
|
keyEntities: Record<RawValueType, DataEntity<any>>;
|
||||||
|
|
||||||
|
// slots: {
|
||||||
|
// title?: (data: InternalDataEntity) => any;
|
||||||
|
// titleRender?: (data: InternalDataEntity) => any;
|
||||||
|
// [key: string]: ((...args: any[]) => any) | undefined;
|
||||||
|
// };
|
||||||
|
}
|
||||||
|
|
||||||
|
const TreeSelectLegacyContextPropsKey: InjectionKey<LegacyContextProps> = Symbol(
|
||||||
|
'TreeSelectLegacyContextPropsKey',
|
||||||
|
);
|
||||||
|
|
||||||
|
// export const LegacySelectContext = defineComponent({
|
||||||
|
// name: 'SelectContext',
|
||||||
|
// props: {
|
||||||
|
// value: { type: Object as PropType<LegacyContextProps> },
|
||||||
|
// },
|
||||||
|
// setup(props, { slots }) {
|
||||||
|
// provide(
|
||||||
|
// TreeSelectLegacyContextPropsKey,
|
||||||
|
// computed(() => props.value),
|
||||||
|
// );
|
||||||
|
// return () => slots.default?.();
|
||||||
|
// },
|
||||||
|
// });
|
||||||
|
|
||||||
|
export function useProvideLegacySelectContext(props: LegacyContextProps) {
|
||||||
|
return provide(TreeSelectLegacyContextPropsKey, props);
|
||||||
|
}
|
||||||
|
|
||||||
|
export default function useInjectLegacySelectContext() {
|
||||||
|
return inject(TreeSelectLegacyContextPropsKey, {} as LegacyContextProps);
|
||||||
|
}
|
|
@ -1,14 +1,16 @@
|
||||||
import type { DataNode, TreeDataNode, Key } from './interface';
|
import type { TreeDataNode, Key } from './interface';
|
||||||
import { useInjectTreeSelectContext } from './Context';
|
|
||||||
import type { RefOptionListProps } from '../vc-select/OptionList';
|
import type { RefOptionListProps } from '../vc-select/OptionList';
|
||||||
import type { ScrollTo } from '../vc-virtual-list/List';
|
import type { ScrollTo } from '../vc-virtual-list/List';
|
||||||
import { computed, defineComponent, nextTick, ref, shallowRef, watch } from 'vue';
|
import { computed, defineComponent, nextTick, ref, shallowRef, watch } from 'vue';
|
||||||
import { optionListProps } from './props';
|
|
||||||
import useMemo from '../_util/hooks/useMemo';
|
import useMemo from '../_util/hooks/useMemo';
|
||||||
import type { EventDataNode } from '../tree';
|
import type { EventDataNode } from '../tree';
|
||||||
import KeyCode from '../_util/KeyCode';
|
import KeyCode from '../_util/KeyCode';
|
||||||
import Tree from '../vc-tree/Tree';
|
import Tree from '../vc-tree/Tree';
|
||||||
import type { TreeProps } from '../vc-tree/props';
|
import type { TreeProps } from '../vc-tree/props';
|
||||||
|
import { getAllKeys, isCheckDisabled } from './utils/valueUtil';
|
||||||
|
import { useBaseProps } from '../vc-select';
|
||||||
|
import useInjectLegacySelectContext from './LegacyContext';
|
||||||
|
import useInjectSelectContext from './TreeSelectContext';
|
||||||
|
|
||||||
const HIDDEN_STYLE = {
|
const HIDDEN_STYLE = {
|
||||||
width: 0,
|
width: 0,
|
||||||
|
@ -32,44 +34,36 @@ type ReviseRefOptionListProps = Omit<RefOptionListProps, 'scrollTo'> & { scrollT
|
||||||
export default defineComponent({
|
export default defineComponent({
|
||||||
name: 'OptionList',
|
name: 'OptionList',
|
||||||
inheritAttrs: false,
|
inheritAttrs: false,
|
||||||
props: optionListProps<DataNode>(),
|
|
||||||
slots: ['notFoundContent', 'menuItemSelectedIcon'],
|
slots: ['notFoundContent', 'menuItemSelectedIcon'],
|
||||||
setup(props, { slots, expose }) {
|
setup(_, { slots, expose }) {
|
||||||
const context = useInjectTreeSelectContext();
|
const baseProps = useBaseProps();
|
||||||
|
const legacyContext = useInjectLegacySelectContext();
|
||||||
|
const context = useInjectSelectContext();
|
||||||
const treeRef = ref();
|
const treeRef = ref();
|
||||||
const memoOptions = useMemo(
|
const memoTreeData = useMemo(
|
||||||
() => props.options,
|
() => context.treeData,
|
||||||
[() => props.open, () => props.options],
|
[() => baseProps.open, () => context.treeData],
|
||||||
next => next[0],
|
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 mergedCheckedKeys = computed(() => {
|
||||||
const { checkable, halfCheckedKeys } = context.value;
|
const { checkable, halfCheckedKeys, checkedKeys } = legacyContext;
|
||||||
if (!checkable) {
|
if (!checkable) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
checked: valueKeys.value,
|
checked: checkedKeys,
|
||||||
halfChecked: halfCheckedKeys,
|
halfChecked: halfCheckedKeys,
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
|
||||||
watch(
|
watch(
|
||||||
() => props.open,
|
() => baseProps.open,
|
||||||
() => {
|
() => {
|
||||||
nextTick(() => {
|
nextTick(() => {
|
||||||
if (props.open && !props.multiple && valueKeys.value.length) {
|
if (baseProps.open && !baseProps.multiple && legacyContext.checkedKeys.length) {
|
||||||
treeRef.value?.scrollTo({ key: valueKeys.value[0] });
|
treeRef.value?.scrollTo({ key: legacyContext.checkedKeys[0] });
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
@ -77,25 +71,25 @@ export default defineComponent({
|
||||||
);
|
);
|
||||||
|
|
||||||
// ========================== Search ==========================
|
// ========================== Search ==========================
|
||||||
const lowerSearchValue = computed(() => String(props.searchValue).toLowerCase());
|
const lowerSearchValue = computed(() => String(baseProps.searchValue).toLowerCase());
|
||||||
const filterTreeNode = (treeNode: EventDataNode) => {
|
const filterTreeNode = (treeNode: EventDataNode) => {
|
||||||
if (!lowerSearchValue.value) {
|
if (!lowerSearchValue.value) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
return String(treeNode[context.value.treeNodeFilterProp])
|
return String(treeNode[legacyContext.treeNodeFilterProp])
|
||||||
.toLowerCase()
|
.toLowerCase()
|
||||||
.includes(lowerSearchValue.value);
|
.includes(lowerSearchValue.value);
|
||||||
};
|
};
|
||||||
|
|
||||||
// =========================== Keys ===========================
|
// =========================== Keys ===========================
|
||||||
const expandedKeys = shallowRef<Key[]>(context.value.treeDefaultExpandedKeys);
|
const expandedKeys = shallowRef<Key[]>(legacyContext.treeDefaultExpandedKeys);
|
||||||
const searchExpandedKeys = shallowRef<Key[]>(null);
|
const searchExpandedKeys = shallowRef<Key[]>(null);
|
||||||
|
|
||||||
watch(
|
watch(
|
||||||
() => props.searchValue,
|
() => baseProps.searchValue,
|
||||||
() => {
|
() => {
|
||||||
if (props.searchValue) {
|
if (baseProps.searchValue) {
|
||||||
searchExpandedKeys.value = props.flattenOptions.map(o => o.key);
|
searchExpandedKeys.value = getAllKeys(context.treeData, context.fieldNames);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
@ -103,17 +97,17 @@ export default defineComponent({
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
const mergedExpandedKeys = computed(() => {
|
const mergedExpandedKeys = computed(() => {
|
||||||
if (context.value.treeExpandedKeys) {
|
if (legacyContext.treeExpandedKeys) {
|
||||||
return [...context.value.treeExpandedKeys];
|
return [...legacyContext.treeExpandedKeys];
|
||||||
}
|
}
|
||||||
return props.searchValue ? searchExpandedKeys.value : expandedKeys.value;
|
return baseProps.searchValue ? searchExpandedKeys.value : expandedKeys.value;
|
||||||
});
|
});
|
||||||
|
|
||||||
const onInternalExpand = (keys: Key[]) => {
|
const onInternalExpand = (keys: Key[]) => {
|
||||||
expandedKeys.value = keys;
|
expandedKeys.value = keys;
|
||||||
searchExpandedKeys.value = keys;
|
searchExpandedKeys.value = keys;
|
||||||
|
|
||||||
context.value.onTreeExpand?.(keys);
|
legacyContext.onTreeExpand?.(keys);
|
||||||
};
|
};
|
||||||
|
|
||||||
// ========================== Events ==========================
|
// ========================== Events ==========================
|
||||||
|
@ -121,23 +115,23 @@ export default defineComponent({
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
};
|
};
|
||||||
|
|
||||||
const onInternalSelect = (_: Key[], { node: { key } }: TreeEventInfo) => {
|
const onInternalSelect = (_: Key[], { node }: TreeEventInfo) => {
|
||||||
const { getEntityByKey, checkable, checkedKeys } = context.value;
|
const { checkable, checkedKeys } = legacyContext;
|
||||||
const entity = getEntityByKey(key, checkable ? 'checkbox' : 'select');
|
if (checkable && isCheckDisabled(node)) {
|
||||||
if (entity !== null) {
|
return;
|
||||||
props.onSelect?.(entity.data.value, {
|
|
||||||
selected: !checkedKeys.includes(entity.data.value),
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
context.onSelect?.(node.key, {
|
||||||
|
selected: !checkedKeys.includes(node.key),
|
||||||
|
});
|
||||||
|
|
||||||
if (!props.multiple) {
|
if (!baseProps.multiple) {
|
||||||
props.onToggleOpen?.(false);
|
baseProps.toggleOpen?.(false);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
// ========================= Keyboard =========================
|
// ========================= Keyboard =========================
|
||||||
const activeKey = ref<Key>(null);
|
const activeKey = ref<Key>(null);
|
||||||
const activeEntity = computed(() => context.value.getEntityByKey(activeKey.value));
|
const activeEntity = computed(() => legacyContext.keyEntities[activeKey.value]);
|
||||||
|
|
||||||
const setActiveKey = (key: Key) => {
|
const setActiveKey = (key: Key) => {
|
||||||
activeKey.value = key;
|
activeKey.value = key;
|
||||||
|
@ -157,11 +151,11 @@ export default defineComponent({
|
||||||
|
|
||||||
// >>> Select item
|
// >>> Select item
|
||||||
case KeyCode.ENTER: {
|
case KeyCode.ENTER: {
|
||||||
const { selectable, value } = activeEntity.value?.data.node || {};
|
const { selectable, value } = activeEntity.value?.node || {};
|
||||||
if (selectable !== false) {
|
if (selectable !== false) {
|
||||||
onInternalSelect(null, {
|
onInternalSelect(null, {
|
||||||
node: { key: activeKey.value },
|
node: { key: activeKey.value },
|
||||||
selected: !context.value.checkedKeys.includes(value),
|
selected: !legacyContext.checkedKeys.includes(value),
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
@ -169,7 +163,7 @@ export default defineComponent({
|
||||||
|
|
||||||
// >>> Close
|
// >>> Close
|
||||||
case KeyCode.ESC: {
|
case KeyCode.ESC: {
|
||||||
props.onToggleOpen(false);
|
baseProps.toggleOpen(false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
@ -179,15 +173,12 @@ export default defineComponent({
|
||||||
return () => {
|
return () => {
|
||||||
const {
|
const {
|
||||||
prefixCls,
|
prefixCls,
|
||||||
height,
|
|
||||||
itemHeight,
|
|
||||||
virtual,
|
|
||||||
multiple,
|
multiple,
|
||||||
searchValue,
|
searchValue,
|
||||||
open,
|
open,
|
||||||
notFoundContent = slots.notFoundContent?.(),
|
notFoundContent = slots.notFoundContent?.(),
|
||||||
onMouseenter,
|
} = baseProps;
|
||||||
} = props;
|
const { listHeight, listItemHeight, virtual } = context;
|
||||||
const {
|
const {
|
||||||
checkable,
|
checkable,
|
||||||
treeDefaultExpandAll,
|
treeDefaultExpandAll,
|
||||||
|
@ -199,9 +190,10 @@ export default defineComponent({
|
||||||
treeLoadedKeys,
|
treeLoadedKeys,
|
||||||
treeMotion,
|
treeMotion,
|
||||||
onTreeLoad,
|
onTreeLoad,
|
||||||
} = context.value;
|
checkedKeys,
|
||||||
|
} = legacyContext;
|
||||||
// ========================== Render ==========================
|
// ========================== Render ==========================
|
||||||
if (memoOptions.value.length === 0) {
|
if (memoTreeData.value.length === 0) {
|
||||||
return (
|
return (
|
||||||
<div role="listbox" class={`${prefixCls}-empty`} onMousedown={onListMouseDown}>
|
<div role="listbox" class={`${prefixCls}-empty`} onMousedown={onListMouseDown}>
|
||||||
{notFoundContent}
|
{notFoundContent}
|
||||||
|
@ -217,10 +209,10 @@ export default defineComponent({
|
||||||
treeProps.expandedKeys = mergedExpandedKeys.value;
|
treeProps.expandedKeys = mergedExpandedKeys.value;
|
||||||
}
|
}
|
||||||
return (
|
return (
|
||||||
<div onMousedown={onListMouseDown} onMouseenter={onMouseenter}>
|
<div onMousedown={onListMouseDown}>
|
||||||
{activeEntity.value && open && (
|
{activeEntity.value && open && (
|
||||||
<span style={HIDDEN_STYLE} aria-live="assertive">
|
<span style={HIDDEN_STYLE} aria-live="assertive">
|
||||||
{activeEntity.value.data.value}
|
{activeEntity.value.node.value}
|
||||||
</span>
|
</span>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
|
@ -228,9 +220,9 @@ export default defineComponent({
|
||||||
ref={treeRef}
|
ref={treeRef}
|
||||||
focusable={false}
|
focusable={false}
|
||||||
prefixCls={`${prefixCls}-tree`}
|
prefixCls={`${prefixCls}-tree`}
|
||||||
treeData={memoOptions.value as TreeDataNode[]}
|
treeData={memoTreeData.value as TreeDataNode[]}
|
||||||
height={height}
|
height={listHeight}
|
||||||
itemHeight={itemHeight}
|
itemHeight={listItemHeight}
|
||||||
virtual={virtual}
|
virtual={virtual}
|
||||||
multiple={multiple}
|
multiple={multiple}
|
||||||
icon={treeIcon}
|
icon={treeIcon}
|
||||||
|
@ -243,7 +235,7 @@ export default defineComponent({
|
||||||
checkable={checkable}
|
checkable={checkable}
|
||||||
checkStrictly
|
checkStrictly
|
||||||
checkedKeys={mergedCheckedKeys.value}
|
checkedKeys={mergedCheckedKeys.value}
|
||||||
selectedKeys={!checkable ? valueKeys.value : []}
|
selectedKeys={!checkable ? checkedKeys : []}
|
||||||
defaultExpandAll={treeDefaultExpandAll}
|
defaultExpandAll={treeDefaultExpandAll}
|
||||||
{...treeProps}
|
{...treeProps}
|
||||||
// Proxy event out
|
// Proxy event out
|
||||||
|
@ -253,7 +245,7 @@ export default defineComponent({
|
||||||
onExpand={onInternalExpand}
|
onExpand={onInternalExpand}
|
||||||
onLoad={onTreeLoad}
|
onLoad={onTreeLoad}
|
||||||
filterTreeNode={filterTreeNode}
|
filterTreeNode={filterTreeNode}
|
||||||
v-slots={{ ...slots, checkable: context.value.customCheckable }}
|
v-slots={{ ...slots, checkable: legacyContext.customCheckable }}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|
|
@ -1,6 +1,210 @@
|
||||||
import generate from './generate';
|
|
||||||
import OptionList from './OptionList';
|
import OptionList from './OptionList';
|
||||||
|
import TreeNode from './TreeNode';
|
||||||
|
import { formatStrategyValues, SHOW_ALL, SHOW_PARENT, SHOW_CHILD } from './utils/strategyUtil';
|
||||||
|
import type { CheckedStrategy } from './utils/strategyUtil';
|
||||||
|
import TreeSelectContext from './TreeSelectContext';
|
||||||
|
import type { TreeSelectContextProps } from './TreeSelectContext';
|
||||||
|
import LegacyContext from './LegacyContext';
|
||||||
|
import useTreeData from './hooks/useTreeData';
|
||||||
|
import { toArray, fillFieldNames, isNil } from './utils/valueUtil';
|
||||||
|
import useCache from './hooks/useCache';
|
||||||
|
import useRefFunc from './hooks/useRefFunc';
|
||||||
|
import useDataEntities from './hooks/useDataEntities';
|
||||||
|
import { fillAdditionalInfo, fillLegacyProps } from './utils/legacyUtil';
|
||||||
|
import useCheckedKeys from './hooks/useCheckedKeys';
|
||||||
|
import useFilterTreeData from './hooks/useFilterTreeData';
|
||||||
|
import warningProps from './utils/warningPropsUtil';
|
||||||
|
import type { Key } from './interface';
|
||||||
|
import { baseSelectPropsWithoutPrivate } from '../vc-select/BaseSelect';
|
||||||
|
import { defineComponent } from 'vue';
|
||||||
|
import type { ExtractPropTypes, PropType } from 'vue';
|
||||||
|
import omit from '../_util/omit';
|
||||||
|
import PropTypes from '../_util/vue-types';
|
||||||
|
import type { SelectProps } from '../vc-select';
|
||||||
|
|
||||||
const TreeSelect = generate({ prefixCls: 'vc-tree-select', optionList: OptionList as any });
|
export type OnInternalSelect = (value: RawValueType, info: { selected: boolean }) => void;
|
||||||
|
|
||||||
export default TreeSelect;
|
export type RawValueType = string | number;
|
||||||
|
|
||||||
|
export interface LabeledValueType {
|
||||||
|
key?: Key;
|
||||||
|
value?: RawValueType;
|
||||||
|
label?: any;
|
||||||
|
/** Only works on `treeCheckStrictly` */
|
||||||
|
halfChecked?: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
export type SelectSource = 'option' | 'selection' | 'input' | 'clear';
|
||||||
|
|
||||||
|
export type DraftValueType = RawValueType | LabeledValueType | (RawValueType | LabeledValueType)[];
|
||||||
|
|
||||||
|
/** @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: LabeledValueType[];
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface InternalFieldName extends Omit<FieldNames, 'label'> {
|
||||||
|
_title: string[];
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface SimpleModeConfig {
|
||||||
|
id?: Key;
|
||||||
|
pId?: Key;
|
||||||
|
rootPId?: Key;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface BaseOptionType {
|
||||||
|
disabled?: boolean;
|
||||||
|
checkable?: boolean;
|
||||||
|
disableCheckbox?: boolean;
|
||||||
|
children?: BaseOptionType[];
|
||||||
|
[name: string]: any;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface DefaultOptionType extends BaseOptionType {
|
||||||
|
value?: RawValueType;
|
||||||
|
title?: any;
|
||||||
|
label?: any;
|
||||||
|
key?: Key;
|
||||||
|
children?: DefaultOptionType[];
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface LegacyDataNode extends DefaultOptionType {
|
||||||
|
props: any;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function treeSelectProps<
|
||||||
|
ValueType = any,
|
||||||
|
OptionType extends BaseOptionType = DefaultOptionType,
|
||||||
|
>() {
|
||||||
|
return {
|
||||||
|
...omit(baseSelectPropsWithoutPrivate(), ['mode']),
|
||||||
|
|
||||||
|
prefixCls: String,
|
||||||
|
id: String,
|
||||||
|
value: { type: [String, Number, Object, Array] as PropType<ValueType> },
|
||||||
|
defaultValue: { type: [String, Number, Object, Array] as PropType<ValueType> },
|
||||||
|
onChange: {
|
||||||
|
type: Function as PropType<
|
||||||
|
(value: ValueType, labelList: any[], extra: ChangeEventExtra) => void
|
||||||
|
>,
|
||||||
|
},
|
||||||
|
searchValue: String,
|
||||||
|
/** @deprecated Use `searchValue` instead */
|
||||||
|
inputValue: String,
|
||||||
|
onSearch: { type: Function as PropType<(value: string) => void> },
|
||||||
|
autoClearSearchValue: { type: Boolean, default: undefined },
|
||||||
|
|
||||||
|
filterTreeNode: {
|
||||||
|
type: [Boolean, Function] as PropType<
|
||||||
|
boolean | ((inputValue: string, treeNode: DefaultOptionType) => boolean)
|
||||||
|
>,
|
||||||
|
default: undefined,
|
||||||
|
},
|
||||||
|
treeNodeFilterProp: String,
|
||||||
|
|
||||||
|
// >>> Select
|
||||||
|
onSelect: Function as PropType<SelectProps['onSelect']>,
|
||||||
|
onDeselect: Function as PropType<SelectProps['onDeselect']>,
|
||||||
|
|
||||||
|
showCheckedStrategy: { type: String as PropType<CheckedStrategy> },
|
||||||
|
treeNodeLabelProp: String,
|
||||||
|
|
||||||
|
fieldNames: { type: Object as PropType<FieldNames> },
|
||||||
|
|
||||||
|
// >>> Mode
|
||||||
|
multiple: { type: Boolean, default: undefined },
|
||||||
|
treeCheckable: { type: Boolean, default: undefined },
|
||||||
|
treeCheckStrictly: { type: Boolean, default: undefined },
|
||||||
|
labelInValue: { type: Boolean, default: undefined },
|
||||||
|
|
||||||
|
// >>> Data
|
||||||
|
treeData: { type: Array as PropType<OptionType[]> },
|
||||||
|
treeDataSimpleMode: {
|
||||||
|
type: [Boolean, Object] as PropType<boolean | SimpleModeConfig>,
|
||||||
|
default: undefined,
|
||||||
|
},
|
||||||
|
treeLoadedKeys: { type: Array as PropType<Key[]> },
|
||||||
|
onTreeLoad: { type: Function as PropType<(loadedKeys: Key[]) => void> },
|
||||||
|
|
||||||
|
// >>> Options
|
||||||
|
virtual: { type: Boolean, default: undefined },
|
||||||
|
listHeight: Number,
|
||||||
|
listItemHeight: Number,
|
||||||
|
onDropdownVisibleChange: { type: Function as PropType<(open: boolean) => void> },
|
||||||
|
|
||||||
|
// >>> Tree
|
||||||
|
treeLine: { type: Boolean, default: undefined },
|
||||||
|
treeIcon: PropTypes.any,
|
||||||
|
showTreeIcon: { type: Boolean, default: undefined },
|
||||||
|
switcherIcon: PropTypes.any,
|
||||||
|
treeMotion: PropTypes.any,
|
||||||
|
|
||||||
|
// showArrow: { type: Boolean, default: undefined },
|
||||||
|
// showSearch: { type: Boolean, default: undefined },
|
||||||
|
// open: { type: Boolean, default: undefined },
|
||||||
|
// defaultOpen: { type: Boolean, default: undefined },
|
||||||
|
|
||||||
|
// disabled: { type: Boolean, default: undefined },
|
||||||
|
|
||||||
|
// placeholder: PropTypes.any,
|
||||||
|
|
||||||
|
// maxTagPlaceholder: { type: Function as PropType<(omittedValues: LabelValueType[]) => any> },
|
||||||
|
|
||||||
|
// loadData: { type: Function as PropType<(dataNode: LegacyDataNode) => Promise<unknown>> },
|
||||||
|
|
||||||
|
// treeExpandedKeys: { type: Array as PropType<Key[]> },
|
||||||
|
// treeDefaultExpandedKeys: { type: Array as PropType<Key[]> },
|
||||||
|
|
||||||
|
// treeDefaultExpandAll: { type: Boolean, default: undefined },
|
||||||
|
|
||||||
|
// children: Array,
|
||||||
|
|
||||||
|
// dropdownPopupAlign: PropTypes.any,
|
||||||
|
|
||||||
|
// // Event
|
||||||
|
|
||||||
|
// onTreeExpand: { type: Function as PropType<(expandedKeys: Key[]) => void> },
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export type TreeSelectProps = Partial<ExtractPropTypes<ReturnType<typeof treeSelectProps>>>;
|
||||||
|
|
||||||
|
function isRawValue(value: RawValueType | LabeledValueType): value is RawValueType {
|
||||||
|
return !value || typeof value !== 'object';
|
||||||
|
}
|
||||||
|
|
||||||
|
export default defineComponent({
|
||||||
|
name: 'TreeSelect',
|
||||||
|
inheritAttrs: false,
|
||||||
|
props: treeSelectProps(),
|
||||||
|
setup() {
|
||||||
|
return () => {
|
||||||
|
return null;
|
||||||
|
};
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
|
@ -0,0 +1,23 @@
|
||||||
|
import type { InjectionKey } from 'vue';
|
||||||
|
import { provide, inject } from 'vue';
|
||||||
|
import type { DefaultOptionType, InternalFieldName, OnInternalSelect } from './TreeSelect';
|
||||||
|
|
||||||
|
export interface TreeSelectContextProps {
|
||||||
|
virtual?: boolean;
|
||||||
|
listHeight: number;
|
||||||
|
listItemHeight: number;
|
||||||
|
treeData: DefaultOptionType[];
|
||||||
|
fieldNames: InternalFieldName;
|
||||||
|
onSelect: OnInternalSelect;
|
||||||
|
}
|
||||||
|
|
||||||
|
const TreeSelectContextPropsKey: InjectionKey<TreeSelectContextProps> = Symbol(
|
||||||
|
'TreeSelectContextPropsKey',
|
||||||
|
);
|
||||||
|
|
||||||
|
export function useProvideSelectContext(props: TreeSelectContextProps) {
|
||||||
|
return provide(TreeSelectContextPropsKey, props);
|
||||||
|
}
|
||||||
|
export default function useInjectSelectContext() {
|
||||||
|
return inject(TreeSelectContextPropsKey, {} as TreeSelectContextProps);
|
||||||
|
}
|
|
@ -1,4 +1,4 @@
|
||||||
// base rc-tree-select@4.6.1
|
// base rc-tree-select@5.0.0-alpha.4
|
||||||
import TreeSelect from './TreeSelect';
|
import TreeSelect from './TreeSelect';
|
||||||
import TreeNode from './TreeNode';
|
import TreeNode from './TreeNode';
|
||||||
import { SHOW_ALL, SHOW_CHILD, SHOW_PARENT } from './utils/strategyUtil';
|
import { SHOW_ALL, SHOW_CHILD, SHOW_PARENT } from './utils/strategyUtil';
|
||||||
|
|
|
@ -1,16 +1,10 @@
|
||||||
import { filterEmpty } from '../../_util/props-util';
|
import { filterEmpty } from '../../_util/props-util';
|
||||||
import { camelize } from 'vue';
|
import { camelize } from 'vue';
|
||||||
import { warning } from '../../vc-util/warning';
|
import { warning } from '../../vc-util/warning';
|
||||||
import type {
|
import type { DataNode, ChangeEventExtra, RawValueType, LegacyCheckedNode } from '../interface';
|
||||||
DataNode,
|
|
||||||
LegacyDataNode,
|
|
||||||
ChangeEventExtra,
|
|
||||||
InternalDataEntity,
|
|
||||||
RawValueType,
|
|
||||||
LegacyCheckedNode,
|
|
||||||
} from '../interface';
|
|
||||||
import TreeNode from '../TreeNode';
|
import TreeNode from '../TreeNode';
|
||||||
import type { VueNode } from '../../_util/type';
|
import type { VueNode } from '../../_util/type';
|
||||||
|
import type { DefaultOptionType, FieldNames } from '../TreeSelect';
|
||||||
|
|
||||||
function isTreeSelectNode(node: any) {
|
function isTreeSelectNode(node: any) {
|
||||||
return node && node.type && (node.type as any).isTreeSelectNode;
|
return node && node.type && (node.type as any).isTreeSelectNode;
|
||||||
|
@ -66,10 +60,10 @@ export function convertChildrenToData(rootNodes: VueNode[]): DataNode[] {
|
||||||
return dig(rootNodes as any[]);
|
return dig(rootNodes as any[]);
|
||||||
}
|
}
|
||||||
|
|
||||||
export function fillLegacyProps(dataNode: DataNode): LegacyDataNode {
|
export function fillLegacyProps(dataNode: DataNode): any {
|
||||||
// Skip if not dataNode exist
|
// Skip if not dataNode exist
|
||||||
if (!dataNode) {
|
if (!dataNode) {
|
||||||
return dataNode as LegacyDataNode;
|
return dataNode;
|
||||||
}
|
}
|
||||||
|
|
||||||
const cloneNode = { ...dataNode };
|
const cloneNode = { ...dataNode };
|
||||||
|
@ -79,37 +73,43 @@ export function fillLegacyProps(dataNode: DataNode): LegacyDataNode {
|
||||||
get() {
|
get() {
|
||||||
warning(
|
warning(
|
||||||
false,
|
false,
|
||||||
'New `rc-tree-select` not support return node instance as argument anymore. Please consider to remove `props` access.',
|
'New `vc-tree-select` not support return node instance as argument anymore. Please consider to remove `props` access.',
|
||||||
);
|
);
|
||||||
return cloneNode;
|
return cloneNode;
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
return cloneNode as LegacyDataNode;
|
return cloneNode;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function fillAdditionalInfo(
|
export function fillAdditionalInfo(
|
||||||
extra: ChangeEventExtra,
|
extra: ChangeEventExtra,
|
||||||
triggerValue: RawValueType,
|
triggerValue: RawValueType,
|
||||||
checkedValues: RawValueType[],
|
checkedValues: RawValueType[],
|
||||||
treeData: InternalDataEntity[],
|
treeData: DefaultOptionType[],
|
||||||
showPosition: boolean,
|
showPosition: boolean,
|
||||||
|
fieldNames: FieldNames,
|
||||||
) {
|
) {
|
||||||
let triggerNode = null;
|
let triggerNode = null;
|
||||||
let nodeList: LegacyCheckedNode[] = null;
|
let nodeList: LegacyCheckedNode[] = null;
|
||||||
|
|
||||||
function generateMap() {
|
function generateMap() {
|
||||||
function dig(list: InternalDataEntity[], level = '0', parentIncluded = false) {
|
function dig(list: DefaultOptionType[], level = '0', parentIncluded = false) {
|
||||||
return list
|
return list
|
||||||
.map((dataNode, index) => {
|
.map((option, index) => {
|
||||||
const pos = `${level}-${index}`;
|
const pos = `${level}-${index}`;
|
||||||
const included = checkedValues.includes(dataNode.value);
|
const value = option[fieldNames.value];
|
||||||
const children = dig(dataNode.children || [], pos, included);
|
const included = checkedValues.includes(value);
|
||||||
const node = <TreeNode {...dataNode}>{children.map(child => child.node)}</TreeNode>;
|
const children = dig(option[fieldNames.children] || [], pos, included);
|
||||||
|
const node = (
|
||||||
|
<TreeNode {...(option as Required<DefaultOptionType>)}>
|
||||||
|
{children.map(child => child.node)}
|
||||||
|
</TreeNode>
|
||||||
|
);
|
||||||
|
|
||||||
// Link with trigger node
|
// Link with trigger node
|
||||||
if (triggerValue === dataNode.value) {
|
if (triggerValue === value) {
|
||||||
triggerNode = node;
|
triggerNode = node;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
import type { DataEntity } from '../../vc-tree/interface';
|
import type { DataEntity } from '../../vc-tree/interface';
|
||||||
import type { RawValueType, Key, DataNode } from '../interface';
|
import type { InternalFieldName } from '../TreeSelect';
|
||||||
|
import type { RawValueType, Key } from '../interface';
|
||||||
import { isCheckDisabled } from './valueUtil';
|
import { isCheckDisabled } from './valueUtil';
|
||||||
|
|
||||||
export const SHOW_ALL = 'SHOW_ALL';
|
export const SHOW_ALL = 'SHOW_ALL';
|
||||||
|
@ -8,22 +9,23 @@ export const SHOW_CHILD = 'SHOW_CHILD';
|
||||||
|
|
||||||
export type CheckedStrategy = typeof SHOW_ALL | typeof SHOW_PARENT | typeof SHOW_CHILD;
|
export type CheckedStrategy = typeof SHOW_ALL | typeof SHOW_PARENT | typeof SHOW_CHILD;
|
||||||
|
|
||||||
export function formatStrategyKeys(
|
export function formatStrategyValues(
|
||||||
keys: Key[],
|
values: Key[],
|
||||||
strategy: CheckedStrategy,
|
strategy: CheckedStrategy,
|
||||||
keyEntities: Record<Key, DataEntity>,
|
keyEntities: Record<Key, DataEntity>,
|
||||||
|
fieldNames: InternalFieldName,
|
||||||
): RawValueType[] {
|
): RawValueType[] {
|
||||||
const keySet = new Set(keys);
|
const valueSet = new Set(values);
|
||||||
|
|
||||||
if (strategy === SHOW_CHILD) {
|
if (strategy === SHOW_CHILD) {
|
||||||
return keys.filter(key => {
|
return values.filter(key => {
|
||||||
const entity = keyEntities[key];
|
const entity = keyEntities[key];
|
||||||
|
|
||||||
if (
|
if (
|
||||||
entity &&
|
entity &&
|
||||||
entity.children &&
|
entity.children &&
|
||||||
entity.children.every(
|
entity.children.every(
|
||||||
({ node }) => isCheckDisabled(node) || keySet.has((node as DataNode).key),
|
({ node }) => isCheckDisabled(node) || valueSet.has(node[fieldNames.value]),
|
||||||
)
|
)
|
||||||
) {
|
) {
|
||||||
return false;
|
return false;
|
||||||
|
@ -32,15 +34,15 @@ export function formatStrategyKeys(
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
if (strategy === SHOW_PARENT) {
|
if (strategy === SHOW_PARENT) {
|
||||||
return keys.filter(key => {
|
return values.filter(key => {
|
||||||
const entity = keyEntities[key];
|
const entity = keyEntities[key];
|
||||||
const parent = entity ? entity.parent : null;
|
const parent = entity ? entity.parent : null;
|
||||||
|
|
||||||
if (parent && !isCheckDisabled(parent.node) && keySet.has((parent.node as DataNode).key)) {
|
if (parent && !isCheckDisabled(parent.node) && valueSet.has(parent.node.key)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
return keys;
|
return values;
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,21 +1,5 @@
|
||||||
import type {
|
import type { Key, DataNode, FieldNames } from '../interface';
|
||||||
FlattenDataNode,
|
import type { DefaultOptionType, InternalFieldName } from '../TreeSelect';
|
||||||
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<FlattenDataNode, 'level'>;
|
|
||||||
|
|
||||||
export function toArray<T>(value: T | T[]): T[] {
|
export function toArray<T>(value: T | T[]): T[] {
|
||||||
if (Array.isArray(value)) {
|
if (Array.isArray(value)) {
|
||||||
|
@ -24,221 +8,43 @@ export function toArray<T>(value: T | T[]): T[] {
|
||||||
return value !== undefined ? [value] : [];
|
return value !== undefined ? [value] : [];
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
export function fillFieldNames(fieldNames?: FieldNames) {
|
||||||
* 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 { label, value, children } = fieldNames || {};
|
||||||
|
|
||||||
const filledNames: FieldNames = {
|
const mergedValue = value || 'value';
|
||||||
value: value || 'value',
|
|
||||||
|
return {
|
||||||
|
_title: label ? [label] : ['title', 'label'],
|
||||||
|
value: mergedValue,
|
||||||
|
key: mergedValue,
|
||||||
children: children || 'children',
|
children: children || 'children',
|
||||||
};
|
};
|
||||||
|
|
||||||
if (!skipTitle || label) {
|
|
||||||
filledNames.label = label || 'label';
|
|
||||||
}
|
|
||||||
|
|
||||||
return filledNames;
|
|
||||||
}
|
|
||||||
|
|
||||||
export function findValueOption(values: RawValueType[], options: CompatibleDataNode[]): DataNode[] {
|
|
||||||
const optionMap: Map<RawValueType, DataNode> = 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) {
|
export function isCheckDisabled(node: DataNode) {
|
||||||
return node.disabled || node.disableCheckbox || node.checkable === false;
|
return node.disabled || node.disableCheckbox || node.checkable === false;
|
||||||
}
|
}
|
||||||
|
|
||||||
interface TreeDataNode extends InternalDataEntity {
|
/** Loop fetch all the keys exist in the tree */
|
||||||
key: Key;
|
export function getAllKeys(treeData: DefaultOptionType[], fieldNames: InternalFieldName) {
|
||||||
children?: TreeDataNode[];
|
const keys: Key[] = [];
|
||||||
}
|
|
||||||
|
|
||||||
function getLevel({ parent }: FlattenNode): number {
|
function dig(list: DefaultOptionType[]) {
|
||||||
let level = 0;
|
list.forEach(item => {
|
||||||
let current = parent;
|
keys.push(item[fieldNames.value]);
|
||||||
|
|
||||||
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,
|
|
||||||
};
|
|
||||||
|
|
||||||
|
const children = item[fieldNames.children];
|
||||||
if (children) {
|
if (children) {
|
||||||
clone.children = fillKey(children);
|
dig(children);
|
||||||
}
|
}
|
||||||
|
|
||||||
return clone;
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
const flattenList = flattenTreeData(fillKey(typedOptions), true, null);
|
dig(treeData);
|
||||||
|
|
||||||
const cacheMap = new Map<Key, FlattenDataNode>();
|
return keys;
|
||||||
const flattenDateNodeList: (FlattenDataNode & { parentKey?: Key })[] = flattenList.map(option => {
|
|
||||||
const { data, key, value } = option as any as Omit<FlattenNode, 'data'> & {
|
|
||||||
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) {
|
export function isNil(val: any) {
|
||||||
return (searchValue: string, dataNode: LegacyDataNode) => {
|
return val === null || val === undefined;
|
||||||
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<LegacyDataNode>;
|
|
||||||
},
|
|
||||||
): DataNode[] {
|
|
||||||
if (filterOption === false) {
|
|
||||||
return options;
|
|
||||||
}
|
|
||||||
|
|
||||||
let filterOptionFunc: FilterFunc<LegacyDataNode>;
|
|
||||||
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<RawValueType, LabelValueType>();
|
|
||||||
|
|
||||||
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);
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,7 +1,8 @@
|
||||||
import { warning } from '../../vc-util/warning';
|
import { warning } from '../../vc-util/warning';
|
||||||
|
import type { TreeSelectProps } from '../TreeSelect';
|
||||||
import { toArray } from './valueUtil';
|
import { toArray } from './valueUtil';
|
||||||
|
|
||||||
function warningProps(props: any) {
|
function warningProps(props: TreeSelectProps & { searchPlaceholder?: string }) {
|
||||||
const { searchPlaceholder, treeCheckStrictly, treeCheckable, labelInValue, value, multiple } =
|
const { searchPlaceholder, treeCheckStrictly, treeCheckable, labelInValue, value, multiple } =
|
||||||
props;
|
props;
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue