From 8bf14f40ac5eebb5dcae64724e27e8760ed6ae35 Mon Sep 17 00:00:00 2001 From: tangjinzhou <415800467@qq.com> Date: Sun, 22 Aug 2021 16:59:13 +0800 Subject: [PATCH] refactor: tree-select --- components/auto-complete/index.tsx | 4 +- components/pagination/MiniSelect.tsx | 4 +- components/select/index.tsx | 24 ++--- components/tree-select/index.tsx | 18 +++- components/tree-select/interface.tsx | 65 -------------- components/tree/Tree.tsx | 5 ++ components/vc-select/Select.tsx | 6 +- .../vc-select/Selector/SingleSelector.tsx | 13 ++- components/vc-select/generate.tsx | 15 ++-- components/vc-select/index.ts | 4 +- components/vc-tree-select/Context.tsx | 2 +- components/vc-tree-select/OptionList.tsx | 9 +- components/vc-tree-select/generate.tsx | 20 +++-- .../vc-tree-select/hooks/useTreeData.ts | 4 + components/vc-tree-select/interface.ts | 2 + components/vc-tree/Tree.tsx | 5 +- components/vc-tree/TreeNode.tsx | 8 +- examples/App.vue | 90 +++++++++++-------- 18 files changed, 139 insertions(+), 159 deletions(-) delete mode 100644 components/tree-select/interface.tsx diff --git a/components/auto-complete/index.tsx b/components/auto-complete/index.tsx index d9e43f753..53c171092 100644 --- a/components/auto-complete/index.tsx +++ b/components/auto-complete/index.tsx @@ -1,6 +1,6 @@ import type { App, Plugin, VNode, ExtractPropTypes } from 'vue'; import { defineComponent, inject, provide } from 'vue'; -import Select, { SelectProps } from '../select'; +import Select, { selectProps } from '../select'; import Input from '../input'; import PropTypes from '../_util/vue-types'; import { defaultConfigProvider } from '../config-provider'; @@ -15,7 +15,7 @@ function isSelectOptionOrSelectOptGroup(child: any): boolean { } const autoCompleteProps = { - ...SelectProps(), + ...selectProps(), dataSource: PropTypes.array, dropdownMenuStyle: PropTypes.style, optionLabelProp: PropTypes.string, diff --git a/components/pagination/MiniSelect.tsx b/components/pagination/MiniSelect.tsx index d02c7febf..d0888827e 100644 --- a/components/pagination/MiniSelect.tsx +++ b/components/pagination/MiniSelect.tsx @@ -1,10 +1,10 @@ import { defineComponent } from 'vue'; -import VcSelect, { SelectProps } from '../select'; +import VcSelect, { selectProps } from '../select'; import { getOptionProps, getSlot } from '../_util/props-util'; export default defineComponent({ inheritAttrs: false, - props: SelectProps(), + props: selectProps(), Option: VcSelect.Option, render() { const selectOptionsProps = getOptionProps(this); diff --git a/components/select/index.tsx b/components/select/index.tsx index 5cccdc8fd..0f60f71f2 100644 --- a/components/select/index.tsx +++ b/components/select/index.tsx @@ -1,8 +1,7 @@ import type { App, PropType, Plugin, ExtractPropTypes } from 'vue'; import { computed, defineComponent, ref } from 'vue'; -import omit from 'omit.js'; import classNames from '../_util/classNames'; -import type { SelectProps as RcSelectProps } from '../vc-select'; +import { selectProps as vcSelectProps } from '../vc-select'; import RcSelect, { Option, OptGroup, selectBaseProps } from '../vc-select'; import type { OptionProps as OptionPropsType } from '../vc-select/Option'; import getIcons from './utils/iconUtil'; @@ -10,6 +9,7 @@ import PropTypes from '../_util/vue-types'; import { tuple } from '../_util/type'; import useConfigInject from '../_util/hooks/useConfigInject'; import type { SizeType } from '../config-provider'; +import omit from '../_util/omit'; type RawValue = string | number; @@ -24,24 +24,8 @@ export interface LabeledValue { } export type SelectValue = RawValue | RawValue[] | LabeledValue | LabeledValue[] | undefined; -interface InternalSelectProps extends Omit, 'mode'> { - suffixIcon?: any; - itemIcon?: any; - size?: SizeType; - mode?: 'multiple' | 'tags' | 'SECRET_COMBOBOX_MODE_DO_NOT_USE'; - bordered?: boolean; -} - -interface SelectPropsTypes - extends Omit, 'inputIcon' | 'mode' | 'getInputElement' | 'backfill'> { - mode?: 'multiple' | 'tags'; -} -export type SelectProps = Partial>>; export const selectProps = () => ({ - ...(omit(selectBaseProps(), ['inputIcon', 'mode', 'getInputElement', 'backfill']) as Omit< - SelectPropsTypes, - 'inputIcon' | 'mode' | 'getInputElement' | 'backfill' | 'class' | 'style' - >), + ...omit(vcSelectProps(), ['inputIcon', 'mode', 'getInputElement', 'backfill']), value: { type: [Array, Object, String, Number] as PropType, }, @@ -58,6 +42,8 @@ export const selectProps = () => ({ choiceTransitionName: PropTypes.string.def(''), }); +export type SelectProps = Partial>>; + const Select = defineComponent({ name: 'ASelect', Option, diff --git a/components/tree-select/index.tsx b/components/tree-select/index.tsx index 9201f99f7..1b126c947 100644 --- a/components/tree-select/index.tsx +++ b/components/tree-select/index.tsx @@ -11,8 +11,6 @@ import VcTreeSelect, { import classNames from '../_util/classNames'; import initDefaultProps from '../_util/props-util/initDefaultProps'; import type { SizeType } from '../config-provider'; - -export { TreeData, TreeSelectProps } from './interface'; import LoadingOutlined from '@ant-design/icons-vue/LoadingOutlined'; import CaretDownOutlined from '@ant-design/icons-vue/CaretDownOutlined'; import type { DefaultValueType, FieldNames } from '../vc-tree-select/interface'; @@ -23,6 +21,7 @@ import devWarning from '../vc-util/devWarning'; import getIcons from '../select/utils/iconUtil'; import renderSwitcherIcon from '../tree/utils/iconUtil'; import type { AntTreeNodeProps } from '../tree/Tree'; +import { warning } from '../vc-util/warning'; const getTransitionName = (rootPrefixCls: string, motion: string, transitionName?: string) => { if (transitionName !== undefined) { @@ -53,6 +52,7 @@ export const treeSelectProps = { replaceFields: { type: Object as PropType }, }; export type TreeSelectProps = Partial>; + const TreeSelect = defineComponent({ TreeNode, SHOW_ALL, @@ -68,8 +68,19 @@ const TreeSelect = defineComponent({ listItemHeight: 26, bordered: true, }), - slots: ['placeholder', 'maxTagPlaceholder', 'treeIcon', 'switcherIcon', 'notFoundContent'], + slots: [ + 'title', + 'placeholder', + 'maxTagPlaceholder', + 'treeIcon', + 'switcherIcon', + 'notFoundContent', + ], setup(props, { attrs, slots, expose, emit }) { + warning( + !(props.treeData === undefined && slots.default), + '`children` of Tree is deprecated. Please use `treeData` instead.', + ); watchEffect(() => { devWarning( props.multiple !== false || !props.treeCheckable, @@ -212,6 +223,7 @@ const TreeSelect = defineComponent({ onSearch={handleSearch} onTreeExpand={handleTreeExpand} v-slots={{ + ...slots, treeCheckable: () => , }} children={slots.default?.()} diff --git a/components/tree-select/interface.tsx b/components/tree-select/interface.tsx deleted file mode 100644 index 49bd7a113..000000000 --- a/components/tree-select/interface.tsx +++ /dev/null @@ -1,65 +0,0 @@ -import PropTypes, { withUndefined } from '../_util/vue-types'; -import { SelectProps } from '../select'; -import { tuple } from '../_util/type'; - -export const TreeData = PropTypes.shape({ - key: PropTypes.string, - value: PropTypes.string, - label: PropTypes.VNodeChild, - slots: PropTypes.object, - children: PropTypes.array, -}).loose; - -export const TreeSelectProps = () => ({ - ...SelectProps(), - autofocus: PropTypes.looseBool, - dropdownStyle: PropTypes.object, - filterTreeNode: withUndefined(PropTypes.oneOfType([Function, Boolean])), - getPopupContainer: PropTypes.func, - labelInValue: PropTypes.looseBool, - loadData: PropTypes.func, - maxTagCount: PropTypes.number, - maxTagPlaceholder: PropTypes.VNodeChild, - value: PropTypes.oneOfType([ - PropTypes.string, - PropTypes.object, - PropTypes.array, - PropTypes.number, - ]), - defaultValue: PropTypes.oneOfType([ - PropTypes.string, - PropTypes.object, - PropTypes.array, - PropTypes.number, - ]), - multiple: PropTypes.looseBool, - notFoundContent: PropTypes.VNodeChild, - searchPlaceholder: PropTypes.string, - searchValue: PropTypes.string, - showCheckedStrategy: PropTypes.oneOf(tuple('SHOW_ALL', 'SHOW_PARENT', 'SHOW_CHILD')), - suffixIcon: PropTypes.VNodeChild, - treeCheckable: PropTypes.looseBool, - treeCheckStrictly: PropTypes.looseBool, - treeData: PropTypes.arrayOf(Object), - treeDataSimpleMode: withUndefined(PropTypes.oneOfType([PropTypes.looseBool, Object])), - - dropdownClassName: PropTypes.string, - dropdownMatchSelectWidth: PropTypes.looseBool, - treeDefaultExpandAll: PropTypes.looseBool, - treeExpandedKeys: PropTypes.array, - treeIcon: PropTypes.looseBool, - treeDefaultExpandedKeys: PropTypes.array, - treeNodeFilterProp: PropTypes.string, - treeNodeLabelProp: PropTypes.string, - replaceFields: PropTypes.object.def({}), - clearIcon: PropTypes.VNodeChild, - removeIcon: PropTypes.VNodeChild, - - onSelect: PropTypes.func, - onChange: PropTypes.func, - onSearch: PropTypes.func, - onTreeExpand: PropTypes.func, - 'onUpdate:treeExpandedKeys': PropTypes.func, - 'onUpdate:searchValue': PropTypes.func, - 'onUpdate:value': PropTypes.func, -}); diff --git a/components/tree/Tree.tsx b/components/tree/Tree.tsx index 09f38491a..b0e0ebde0 100644 --- a/components/tree/Tree.tsx +++ b/components/tree/Tree.tsx @@ -13,6 +13,7 @@ import useConfigInject from '../_util/hooks/useConfigInject'; import renderSwitcherIcon from './utils/iconUtil'; import dropIndicatorRender from './utils/dropIndicator'; import devWarning from '../vc-util/devWarning'; +import { warning } from '../vc-util/warning'; export interface AntdTreeNodeAttribute { eventKey: string; @@ -153,6 +154,10 @@ export default defineComponent({ ], TreeNode, setup(props, { attrs, expose, emit, slots }) { + warning( + !(props.treeData === undefined && slots.default), + '`children` of Tree is deprecated. Please use `treeData` instead.', + ); const { prefixCls, direction, virtual } = useConfigInject('tree', props); const treeRef = ref(); expose({ diff --git a/components/vc-select/Select.tsx b/components/vc-select/Select.tsx index b824fa411..2e81186eb 100644 --- a/components/vc-select/Select.tsx +++ b/components/vc-select/Select.tsx @@ -42,7 +42,7 @@ import { flattenOptions, fillOptionsWithMissingValue, } from './utils/valueUtil'; -import type { SelectProps } from './generate'; +import { selectBaseProps, SelectProps } from './generate'; import generateSelector from './generate'; import type { DefaultValueType } from './interface/generator'; import warningProps from './utils/warningPropsUtil'; @@ -68,6 +68,10 @@ export type ExportedSelectProps = T >; +export function selectProps() { + return selectBaseProps(); +} + const Select = defineComponent({ name: 'Select', inheritAttrs: false, diff --git a/components/vc-select/Selector/SingleSelector.tsx b/components/vc-select/Selector/SingleSelector.tsx index fdbff431a..b83dae774 100644 --- a/components/vc-select/Selector/SingleSelector.tsx +++ b/components/vc-select/Selector/SingleSelector.tsx @@ -2,8 +2,9 @@ import pickAttrs from '../../_util/pickAttrs'; import Input from './Input'; import type { InnerSelectorProps } from './interface'; import type { VNodeChild } from 'vue'; -import { computed, defineComponent, Fragment, ref, watch } from 'vue'; +import { computed, defineComponent, ref, watch } from 'vue'; import PropTypes from '../../_util/vue-types'; +import { useInjectTreeSelectContext } from 'ant-design-vue/es/vc-tree-select/Context'; interface SelectorProps extends InnerSelectorProps { inputElement: VNodeChild; @@ -50,6 +51,7 @@ const SingleSelector = defineComponent({ } return inputValue; }); + const treeSelectContext = useInjectTreeSelectContext(); watch( [combobox, () => props.activeValue], () => { @@ -94,6 +96,12 @@ const SingleSelector = defineComponent({ onInputCompositionEnd, } = props; const item = values[0]; + let slotTitle = null; + if (treeSelectContext.value.slots) { + slotTitle = + treeSelectContext.value.slots[item?.option?.data?.slots?.title] || + treeSelectContext.value.slots.title; + } return ( <> @@ -126,7 +134,8 @@ const SingleSelector = defineComponent({ {/* Display value */} {!combobox.value && item && !hasTextInput.value && ( - {item.label} + {/* {item.label} */} + {slotTitle?.(item.option?.data) || item.label} )} diff --git a/components/vc-select/generate.tsx b/components/vc-select/generate.tsx index 822ea5434..63a3fecc0 100644 --- a/components/vc-select/generate.tsx +++ b/components/vc-select/generate.tsx @@ -119,7 +119,7 @@ export function selectBaseProps() { defaultOpen: { type: Boolean, default: undefined }, listHeight: Number, listItemHeight: Number, - dropdownStyle: { type: Function as PropType }, + dropdownStyle: { type: Object as PropType }, dropdownClassName: String, dropdownMatchSelectWidth: { type: [Boolean, Number] as PropType, @@ -275,7 +275,7 @@ export default function generateSelector< slots: ['option'], inheritAttrs: false, props: selectBaseProps(), - setup(props) { + setup(props, { expose }) { const useInternalProps = computed( () => props.internalProps && props.internalProps.mark === INTERNAL_PROPS_MARK, ); @@ -455,10 +455,10 @@ export default function generateSelector< labelInValue: mergedLabelInValue.value, optionLabelProp: mergedOptionLabelProp.value, }); - return { ...displayValue, disabled: isValueDisabled(val, valueOptions), + option: valueOptions[0], }; }); @@ -947,10 +947,12 @@ export default function generateSelector< const blur = () => { selectorRef.value.blur(); }; - return { + expose({ focus, blur, - scrollTo: listRef.value?.scrollTo, + scrollTo: (...args: any[]) => listRef.value?.scrollTo(...args), + }); + return { tokenWithEnter, mockFocused, mergedId, @@ -1139,7 +1141,7 @@ export default function generateSelector< menuItemSelectedIcon={menuItemSelectedIcon} virtual={virtual !== false && dropdownMatchSelectWidth !== false} onMouseenter={onPopupMouseEnter} - v-slots={{ option: slots.option }} + v-slots={{ ...slots, option: slots.option }} /> ); @@ -1212,7 +1214,6 @@ export default function generateSelector< [`${prefixCls}-customize-input`]: customizeInputElement, [`${prefixCls}-show-search`]: mergedShowSearch, }); - return (
= Partial>>; -export { Option, OptGroup, selectBaseProps }; +export { Option, OptGroup, selectBaseProps, selectProps }; export default Select; diff --git a/components/vc-tree-select/Context.tsx b/components/vc-tree-select/Context.tsx index e8ea93dc8..a84a09e25 100644 --- a/components/vc-tree-select/Context.tsx +++ b/components/vc-tree-select/Context.tsx @@ -50,7 +50,7 @@ export const SelectContext = defineComponent({ }, }); -export const useInjectSelectContext = () => { +export const useInjectTreeSelectContext = () => { return inject( SelectContextKey, computed(() => ({} as ContextProps)), diff --git a/components/vc-tree-select/OptionList.tsx b/components/vc-tree-select/OptionList.tsx index ca3b25258..e8e32caec 100644 --- a/components/vc-tree-select/OptionList.tsx +++ b/components/vc-tree-select/OptionList.tsx @@ -1,5 +1,5 @@ import type { DataNode, TreeDataNode, Key } from './interface'; -import { useInjectSelectContext } from './Context'; +import { useInjectTreeSelectContext } from './Context'; import type { RefOptionListProps } from '../vc-select/OptionList'; import type { ScrollTo } from '../vc-virtual-list/List'; import { computed, defineComponent, nextTick, ref, watch } from 'vue'; @@ -36,7 +36,7 @@ export default defineComponent({ slots: ['notFoundContent', 'menuItemSelectedIcon'], expose: ['scrollTo', 'onKeydown', 'onKeyup'], setup(props, { slots, expose }) { - const context = useInjectSelectContext(); + const context = useInjectTreeSelectContext(); const treeRef = ref(); const memoOptions = useMemo( @@ -144,7 +144,7 @@ export default defineComponent({ activeKey.value = key; }; expose({ - scrollTo: treeRef.value?.scrollTo as ScrollTo, + scrollTo: (...args: any[]) => treeRef.value.scrollTo?.(...args), onKeydown: (event: KeyboardEvent) => { const { which } = event; switch (which) { @@ -217,7 +217,6 @@ export default defineComponent({ if (mergedExpandedKeys.value) { treeProps.expandedKeys = mergedExpandedKeys.value; } - return (
{activeEntity.value && open && ( @@ -255,7 +254,7 @@ export default defineComponent({ onExpand={onInternalExpand} onLoad={onTreeLoad} filterTreeNode={filterTreeNode} - v-slots={{ checkable: context.value.customCheckable }} + v-slots={{ ...slots, checkable: context.value.customCheckable }} />
); diff --git a/components/vc-tree-select/generate.tsx b/components/vc-tree-select/generate.tsx index bdaefc986..2194bf5db 100644 --- a/components/vc-tree-select/generate.tsx +++ b/components/vc-tree-select/generate.tsx @@ -91,7 +91,15 @@ export default function generate(config: { return defineComponent({ name: 'TreeSelect', props: treeSelectProps(), - slots: ['placeholder', 'maxTagPlaceholder', 'treeIcon', 'switcherIcon', 'notFoundContent'], + slots: [ + 'title', + 'placeholder', + 'maxTagPlaceholder', + 'treeIcon', + 'switcherIcon', + 'notFoundContent', + 'treeCheckable', + ], TreeNode, SHOW_ALL, SHOW_PARENT, @@ -105,7 +113,6 @@ export default function generate(config: { // ======================= 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) => { @@ -157,9 +164,9 @@ export default function generate(config: { const selectRef = ref(null); expose({ - scrollTo: selectRef.value.scrollTo, - focus: selectRef.value.focus, - blur: selectRef.value.blur, + 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, @@ -477,7 +484,7 @@ export default function generate(config: { treeNodeFilterProp, getEntityByKey, getEntityByValue, - customCheckable: slots.checkable, + customCheckable: slots.treeCheckable, slots, }; return ( @@ -496,6 +503,7 @@ export default function generate(config: { onSelect={null} onDeselect={null} onDropdownVisibleChange={onInternalDropdownVisibleChange} + v-slots={slots} /> ); diff --git a/components/vc-tree-select/hooks/useTreeData.ts b/components/vc-tree-select/hooks/useTreeData.ts index c6da55ec1..b0969db67 100644 --- a/components/vc-tree-select/hooks/useTreeData.ts +++ b/components/vc-tree-select/hooks/useTreeData.ts @@ -78,6 +78,10 @@ function formatTreeData( node, }; + if (node.slots) { + dataNode.slots = node.slots; + } + // Check `key` & `value` and warning user if (process.env.NODE_ENV !== 'production') { if ( diff --git a/components/vc-tree-select/interface.ts b/components/vc-tree-select/interface.ts index 2eccf640a..747795c4b 100644 --- a/components/vc-tree-select/interface.ts +++ b/components/vc-tree-select/interface.ts @@ -38,6 +38,8 @@ export interface InternalDataEntity { /** Origin DataNode */ node: DataNode; + + slots?: Record; // 兼容 V2 } export interface LegacyDataNode extends DataNode { diff --git a/components/vc-tree/Tree.tsx b/components/vc-tree/Tree.tsx index 7f20ba9b7..2553bcd5b 100644 --- a/components/vc-tree/Tree.tsx +++ b/components/vc-tree/Tree.tsx @@ -84,10 +84,6 @@ export default defineComponent({ // abstract-drag-over-node is the top node dragOverNodeKey: null, }); - warning( - !(props.treeData === undefined && props.children), - '`children` of Tree is deprecated. Please use `treeData` instead.', - ); const treeData = computed(() => { return props.treeData !== undefined ? props.treeData : convertTreeToData(props.children); }); @@ -953,6 +949,7 @@ export default defineComponent({ }; expose({ onNodeExpand, + scrollTo, }); onUnmounted(() => { window.removeEventListener('dragend', onWindowDragEnd); diff --git a/components/vc-tree/TreeNode.tsx b/components/vc-tree/TreeNode.tsx index 340d522c0..aa8cb0637 100644 --- a/components/vc-tree/TreeNode.tsx +++ b/components/vc-tree/TreeNode.tsx @@ -22,7 +22,9 @@ export default defineComponent({ setup(props, { attrs, slots, expose }) { warning( !('slots' in props.data), - 'treeData slots is deprecated, please use `v-slot:icon` or `v-slot:title`, `v-slot:switcherIcon` instead', + `treeData slots is deprecated, please use ${Object.keys(props.data.slots || {}).map( + key => '`v-slot:' + key + '` ', + )}instead`, ); const dragNodeHighlight = ref(false); const context = useInjectTreeContext(); @@ -346,7 +348,9 @@ export default defineComponent({ // Icon + Title const renderSelector = () => { const { - title = slots.title || context.value.slots?.[props.data?.slots?.title], + title = slots.title || + context.value.slots?.[props.data?.slots?.title] || + context.value.slots?.title, selected, icon = slots.icon, loading, diff --git a/examples/App.vue b/examples/App.vue index d9e64b8a7..b85bf8e8e 100644 --- a/examples/App.vue +++ b/examples/App.vue @@ -1,49 +1,63 @@