diff --git a/components/_util/transition.tsx b/components/_util/transition.tsx index 1894aff8b..6caea7504 100644 --- a/components/_util/transition.tsx +++ b/components/_util/transition.tsx @@ -1,5 +1,5 @@ -import type { BaseTransitionProps, CSSProperties, Ref } from 'vue'; -import { getCurrentInstance, onUpdated } from 'vue'; +import { BaseTransitionProps, CSSProperties, onBeforeUpdate, Ref } from 'vue'; +import { getCurrentInstance } from 'vue'; import { defineComponent, nextTick, Transition as T, TransitionGroup as TG } from 'vue'; export const getTransitionProps = (transitionName: string, opt: object = {}) => { @@ -51,7 +51,7 @@ if (process.env.NODE_ENV === 'test') { inheritAttrs: false, setup(_props, { slots, attrs }) { const instance = getCurrentInstance(); - onUpdated(() => { + onBeforeUpdate(() => { const child = instance.subTree.children[0]; if (child && child.dirs && child.dirs[0]) { const value = child.dirs[0].value; diff --git a/components/auto-complete/InputElement.tsx b/components/auto-complete/InputElement.tsx deleted file mode 100644 index c3623cfec..000000000 --- a/components/auto-complete/InputElement.tsx +++ /dev/null @@ -1,9 +0,0 @@ -import { cloneElement } from '../_util/vnode'; -import { flattenChildren } from '../_util/props-util'; - -const InputElement = (_: any, { attrs, slots }) => { - const children = flattenChildren(slots.default?.())[0]; - return cloneElement(children, { ...attrs }); -}; -InputElement.inheritAttrs = false; -export default InputElement; diff --git a/components/auto-complete/index.tsx b/components/auto-complete/index.tsx index dac4d43db..d9e43f753 100644 --- a/components/auto-complete/index.tsx +++ b/components/auto-complete/index.tsx @@ -2,7 +2,6 @@ import type { App, Plugin, VNode, ExtractPropTypes } from 'vue'; import { defineComponent, inject, provide } from 'vue'; import Select, { SelectProps } from '../select'; import Input from '../input'; -import InputElement from './InputElement'; import PropTypes from '../_util/vue-types'; import { defaultConfigProvider } from '../config-provider'; import { getComponent, getOptionProps, isValidElement, getSlot } from '../_util/props-util'; @@ -72,7 +71,7 @@ const AutoComplete = defineComponent({ getInputElement() { const children = getSlot(this); const element = children.length ? children[0] : ; - return {element}; + return element; }, focus() { diff --git a/components/cascader/__tests__/__snapshots__/index.test.js.snap b/components/cascader/__tests__/__snapshots__/index.test.js.snap index 708351c16..c2a63580d 100644 --- a/components/cascader/__tests__/__snapshots__/index.test.js.snap +++ b/components/cascader/__tests__/__snapshots__/index.test.js.snap @@ -113,4 +113,33 @@ exports[`Cascader popup correctly with defaultValue 1`] = ` `; -exports[`Cascader support controlled mode 1`] = `Zhejiang / Hangzhou / West Lake`; +exports[`Cascader popup correctly with defaultValue 2`] = ` +
+ +
+
+
    + + +
+
    + +
+
    + +
+
+
+
+`; + +exports[`Cascader support controlled mode 1`] = `Zhejiang / Hangzhou / West Lake`; diff --git a/components/drawer/index.tsx b/components/drawer/index.tsx index cd4ababc7..bb53f4dab 100644 --- a/components/drawer/index.tsx +++ b/components/drawer/index.tsx @@ -123,7 +123,7 @@ const Drawer = defineComponent({ } if (!this.visible) { this.destroyClose = true; - this.$forceUpdate(); + (this as any).$forceUpdate(); } }, diff --git a/components/input/Input.tsx b/components/input/Input.tsx index df07a8bf1..46cb7ce0e 100644 --- a/components/input/Input.tsx +++ b/components/input/Input.tsx @@ -128,7 +128,7 @@ export default defineComponent({ if (!hasProp(this, 'value')) { this.stateValue = value; } else { - this.$forceUpdate(); + (this as any).$forceUpdate(); } nextTick(() => { callback && callback(); diff --git a/components/input/TextArea.tsx b/components/input/TextArea.tsx index 1fb195432..f654bc56d 100644 --- a/components/input/TextArea.tsx +++ b/components/input/TextArea.tsx @@ -55,7 +55,7 @@ export default defineComponent({ if (!hasProp(this, 'value')) { this.stateValue = value; } else { - this.$forceUpdate(); + (this as any).$forceUpdate(); } nextTick(() => { callback && callback(); diff --git a/components/menu/src/MenuItem.tsx b/components/menu/src/MenuItem.tsx index 49f7f7405..df483b93d 100644 --- a/components/menu/src/MenuItem.tsx +++ b/components/menu/src/MenuItem.tsx @@ -10,6 +10,7 @@ import type { MenuInfo } from './interface'; import KeyCode from '../../_util/KeyCode'; import useDirectionStyle from './hooks/useDirectionStyle'; import Overflow from '../../vc-overflow'; +import devWarning from '../../vc-util/devWarning'; let indexGuid = 0; const menuItemProps = { @@ -30,7 +31,15 @@ export default defineComponent({ slots: ['icon', 'title'], setup(props, { slots, emit, attrs }) { const instance = getCurrentInstance(); - const key = instance.vnode.key; + + const key = + typeof instance.vnode.key === 'symbol' ? String(instance.vnode.key) : instance.vnode.key; + devWarning( + typeof instance.vnode.key !== 'symbol', + 'MenuItem', + `MenuItem \`:key="${String(key)}"\` not support Symbol type`, + ); + const eventKey = `menu_item_${++indexGuid}_$$_${key}`; const { parentEventKeys, parentKeys } = useInjectKeyPath(); const { diff --git a/components/menu/src/SubMenu.tsx b/components/menu/src/SubMenu.tsx index 24fde493f..57fea3003 100644 --- a/components/menu/src/SubMenu.tsx +++ b/components/menu/src/SubMenu.tsx @@ -12,6 +12,8 @@ import InlineSubMenuList from './InlineSubMenuList'; import Transition, { getTransitionProps } from '../../_util/transition'; import { cloneElement } from '../../_util/vnode'; import Overflow from '../../vc-overflow'; +import devWarning from '../../vc-util/devWarning'; +import isValid from '../../_util/isValid'; let indexGuid = 0; @@ -39,14 +41,17 @@ export default defineComponent({ useProvideFirstLevel(false); const instance = getCurrentInstance(); - const key = - instance.vnode.key !== null ? instance.vnode.key : `sub_menu_${++indexGuid}_$$_not_set_key`; - + const vnodeKey = + typeof instance.vnode.key === 'symbol' ? String(instance.vnode.key) : instance.vnode.key; + devWarning( + typeof instance.vnode.key !== 'symbol', + 'SubMenu', + `SubMenu \`:key="${String(vnodeKey)}"\` not support Symbol type`, + ); + const key = isValid(vnodeKey) ? vnodeKey : `sub_menu_${++indexGuid}_$$_not_set_key`; const eventKey = props.eventKey ?? - (instance.vnode.key !== null - ? `sub_menu_${++indexGuid}_$$_${instance.vnode.key}` - : (key as string)); + (isValid(vnodeKey) ? `sub_menu_${++indexGuid}_$$_${vnodeKey}` : (key as string)); const { parentEventKeys, parentInfo, parentKeys } = useInjectKeyPath(); const keysPath = computed(() => [...parentKeys.value, key]); const eventKeysPath = computed(() => [...parentEventKeys.value, eventKey]); diff --git a/components/table/filterDropdown.tsx b/components/table/filterDropdown.tsx index 8f3b8ba27..12380eb11 100755 --- a/components/table/filterDropdown.tsx +++ b/components/table/filterDropdown.tsx @@ -98,7 +98,7 @@ export default defineComponent({ this.setVisible(false); // Call `setSelectedKeys` & `confirm` in the same time will make filter data not up to date // https://github.com/ant-design/ant-design/issues/12284 - this.$forceUpdate(); + (this as any).$forceUpdate(); nextTick(this.confirmFilter2); }, diff --git a/components/upload/UploadList.tsx b/components/upload/UploadList.tsx index 4137f876a..05f5fdc9b 100644 --- a/components/upload/UploadList.tsx +++ b/components/upload/UploadList.tsx @@ -64,7 +64,7 @@ export default defineComponent({ previewFile(file.originFileObj).then(previewDataUrl => { // Need append '' to avoid dead loop file.thumbUrl = previewDataUrl || ''; - this.$forceUpdate(); + (this as any).$forceUpdate(); }); } }); diff --git a/components/vc-select/OptionList.tsx b/components/vc-select/OptionList.tsx index fbeb9744b..a24735d76 100644 --- a/components/vc-select/OptionList.tsx +++ b/components/vc-select/OptionList.tsx @@ -5,7 +5,7 @@ import classNames from '../_util/classNames'; import pickAttrs from '../_util/pickAttrs'; import { isValidElement } from '../_util/props-util'; import createRef from '../_util/createRef'; -import type { VNodeChild } from 'vue'; +import type { PropType, VNodeChild } from 'vue'; import { computed, defineComponent, nextTick, reactive, watch } from 'vue'; import List from '../vc-virtual-list/List'; import type { @@ -61,7 +61,7 @@ const OptionListProps = { virtual: PropTypes.looseBool, onSelect: PropTypes.func, - onToggleOpen: PropTypes.func, + onToggleOpen: { type: Function as PropType<(open?: boolean) => void> }, /** Tell Select that some value is now active to make accessibility work */ onActiveValue: PropTypes.func, onScroll: PropTypes.func, diff --git a/components/vc-select/Selector/Input.tsx b/components/vc-select/Selector/Input.tsx index c8f72a71d..b29c6edf0 100644 --- a/components/vc-select/Selector/Input.tsx +++ b/components/vc-select/Selector/Input.tsx @@ -74,7 +74,7 @@ const Input = defineComponent) as VNode, [[antInput]]); + let inputNode: any = inputElement || withDirectives(() as VNode, [[antInput]]); const inputProps = inputNode.props || {}; const { diff --git a/components/vc-select/Selector/MultipleSelector.tsx b/components/vc-select/Selector/MultipleSelector.tsx index dc9805a97..950378854 100644 --- a/components/vc-select/Selector/MultipleSelector.tsx +++ b/components/vc-select/Selector/MultipleSelector.tsx @@ -7,9 +7,9 @@ import type { DisplayLabelValueType, } from '../interface/generator'; import type { RenderNode } from '../interface'; -import type { InnerSelectorProps } from '.'; +import type { InnerSelectorProps } from './interface'; import Input from './Input'; -import type { VNodeChild, Ref } from 'vue'; +import type { VNodeChild, Ref, PropType } from 'vue'; import { computed, defineComponent, onMounted, ref, watch } from 'vue'; import classNames from '../../_util/classNames'; import pickAttrs from '../../_util/pickAttrs'; @@ -17,24 +17,24 @@ import PropTypes from '../../_util/vue-types'; import type { VueNode } from '../../_util/type'; import Overflow from '../../vc-overflow'; -interface SelectorProps extends InnerSelectorProps { +type SelectorProps = InnerSelectorProps & { // Icon removeIcon?: RenderNode; // Tags maxTagCount?: number | 'responsive'; maxTagTextLength?: number; - maxTagPlaceholder?: VNodeChild; + maxTagPlaceholder?: VNodeChild | ((omittedValues: LabelValueType[]) => VNodeChild); tokenSeparators?: string[]; tagRender?: (props: CustomTagProps) => VNodeChild; - onToggleOpen: (open?: boolean) => void; + onToggleOpen: any; // Motion choiceTransitionName?: string; // Event onSelect: (value: RawValueType, option: { selected: boolean }) => void; -} +}; const props = { id: PropTypes.string, @@ -62,6 +62,7 @@ const props = { ), tagRender: PropTypes.func, + onToggleOpen: { type: Function as PropType<(open?: boolean) => void> }, onSelect: PropTypes.func, onInputChange: PropTypes.func, onInputPaste: PropTypes.func, @@ -78,6 +79,8 @@ const onPreventMouseDown = (event: MouseEvent) => { const SelectSelector = defineComponent({ name: 'MultipleSelectSelector', + inheritAttrs: false, + props: props as any, setup(props) { const measureRef = ref(); const inputWidth = ref(0); @@ -278,6 +281,5 @@ const SelectSelector = defineComponent({ }; }, }); -SelectSelector.inheritAttrs = false; -SelectSelector.props = props; + export default SelectSelector; diff --git a/components/vc-select/Selector/SingleSelector.tsx b/components/vc-select/Selector/SingleSelector.tsx index be58e784f..fdbff431a 100644 --- a/components/vc-select/Selector/SingleSelector.tsx +++ b/components/vc-select/Selector/SingleSelector.tsx @@ -1,6 +1,6 @@ import pickAttrs from '../../_util/pickAttrs'; import Input from './Input'; -import type { InnerSelectorProps } from '.'; +import type { InnerSelectorProps } from './interface'; import type { VNodeChild } from 'vue'; import { computed, defineComponent, Fragment, ref, watch } from 'vue'; import PropTypes from '../../_util/vue-types'; diff --git a/components/vc-select/Selector/index.tsx b/components/vc-select/Selector/index.tsx index d4430c35a..e6dd0f9a1 100644 --- a/components/vc-select/Selector/index.tsx +++ b/components/vc-select/Selector/index.tsx @@ -14,36 +14,12 @@ import SingleSelector from './SingleSelector'; import type { LabelValueType, RawValueType, CustomTagProps } from '../interface/generator'; import type { RenderNode, Mode } from '../interface'; import useLock from '../hooks/useLock'; -import type { VNodeChild } from 'vue'; +import type { VNodeChild, PropType } from 'vue'; import { defineComponent } from 'vue'; -import type { RefObject } from '../../_util/createRef'; import createRef from '../../_util/createRef'; import PropTypes from '../../_util/vue-types'; import type { VueNode } from '../../_util/type'; -export interface InnerSelectorProps { - prefixCls: string; - id: string; - mode: Mode; - inputRef: RefObject; - placeholder?: VNodeChild; - disabled?: boolean; - autofocus?: boolean; - autocomplete?: string; - values: LabelValueType[]; - showSearch?: boolean; - searchValue: string; - accessibilityIndex: number; - open: boolean; - tabindex?: number | string; - onInputKeyDown: EventHandlerNonNull; - onInputMouseDown: EventHandlerNonNull; - onInputChange: EventHandlerNonNull; - onInputPaste: EventHandlerNonNull; - onInputCompositionStart: EventHandlerNonNull; - onInputCompositionEnd: EventHandlerNonNull; -} - export interface SelectorProps { id: string; prefixCls: string; @@ -67,7 +43,7 @@ export interface SelectorProps { // Tags maxTagCount?: number | 'responsive'; maxTagTextLength?: number; - maxTagPlaceholder?: VNodeChild; + maxTagPlaceholder?: VNodeChild | ((omittedValues: LabelValueType[]) => VNodeChild); tagRender?: (props: CustomTagProps) => VNodeChild; /** Check if `tokenSeparators` contains `\n` or `\r\n` */ @@ -92,6 +68,52 @@ export interface SelectorProps { const Selector = defineComponent({ name: 'Selector', + inheritAttrs: false, + props: { + id: PropTypes.string, + prefixCls: PropTypes.string, + showSearch: PropTypes.looseBool, + open: PropTypes.looseBool, + /** Display in the Selector value, it's not same as `value` prop */ + values: PropTypes.array, + multiple: PropTypes.looseBool, + mode: PropTypes.string, + searchValue: PropTypes.string, + activeValue: PropTypes.string, + inputElement: PropTypes.any, + + autofocus: PropTypes.looseBool, + accessibilityIndex: PropTypes.number, + tabindex: PropTypes.oneOfType([PropTypes.number, PropTypes.string]), + disabled: PropTypes.looseBool, + placeholder: PropTypes.any, + removeIcon: PropTypes.any, + + // Tags + maxTagCount: PropTypes.oneOfType([PropTypes.number, PropTypes.string]), + maxTagTextLength: PropTypes.number, + maxTagPlaceholder: PropTypes.any, + tagRender: PropTypes.func, + + /** Check if `tokenSeparators` contains `\n` or `\r\n` */ + tokenWithEnter: PropTypes.looseBool, + + // Motion + choiceTransitionName: PropTypes.string, + + onToggleOpen: { type: Function as PropType<(open?: boolean) => void> }, + /** `onSearch` returns go next step boolean to check if need do toggle open */ + onSearch: PropTypes.func, + onSearchSubmit: PropTypes.func, + onSelect: PropTypes.func, + onInputKeyDown: PropTypes.func, + + /** + * @private get real dom for trigger align. + * This may be removed after React provides replacement of `findDOMNode` + */ + domRef: PropTypes.func, + } as any, setup(props) { const inputRef = createRef(); let compositionStatus = false; @@ -258,51 +280,4 @@ const Selector = defineComponent({ }, }); -Selector.inheritAttrs = false; -Selector.props = { - id: PropTypes.string, - prefixCls: PropTypes.string, - showSearch: PropTypes.looseBool, - open: PropTypes.looseBool, - /** Display in the Selector value, it's not same as `value` prop */ - values: PropTypes.array, - multiple: PropTypes.looseBool, - mode: PropTypes.string, - searchValue: PropTypes.string, - activeValue: PropTypes.string, - inputElement: PropTypes.any, - - autofocus: PropTypes.looseBool, - accessibilityIndex: PropTypes.number, - tabindex: PropTypes.oneOfType([PropTypes.number, PropTypes.string]), - disabled: PropTypes.looseBool, - placeholder: PropTypes.any, - removeIcon: PropTypes.any, - - // Tags - maxTagCount: PropTypes.oneOfType([PropTypes.number, PropTypes.string]), - maxTagTextLength: PropTypes.number, - maxTagPlaceholder: PropTypes.any, - tagRender: PropTypes.func, - - /** Check if `tokenSeparators` contains `\n` or `\r\n` */ - tokenWithEnter: PropTypes.looseBool, - - // Motion - choiceTransitionName: PropTypes.string, - - onToggleOpen: PropTypes.func, - /** `onSearch` returns go next step boolean to check if need do toggle open */ - onSearch: PropTypes.func, - onSearchSubmit: PropTypes.func, - onSelect: PropTypes.func, - onInputKeyDown: PropTypes.func, - - /** - * @private get real dom for trigger align. - * This may be removed after React provides replacement of `findDOMNode` - */ - domRef: PropTypes.func, -}; - export default Selector; diff --git a/components/vc-select/Selector/interface.ts b/components/vc-select/Selector/interface.ts new file mode 100644 index 000000000..2aa8b1e2c --- /dev/null +++ b/components/vc-select/Selector/interface.ts @@ -0,0 +1,27 @@ +import type { RefObject } from '../../_util/createRef'; +import type { VNodeChild } from 'vue'; +import type { Mode } from '../interface'; +import type { LabelValueType } from '../interface/generator'; + +export interface InnerSelectorProps { + prefixCls: string; + id: string; + mode: Mode; + inputRef: RefObject; + placeholder?: VNodeChild; + disabled?: boolean; + autofocus?: boolean; + autocomplete?: string; + values: LabelValueType[]; + showSearch?: boolean; + searchValue: string; + accessibilityIndex: number; + open: boolean; + tabindex?: number | string; + onInputKeyDown: EventHandlerNonNull; + onInputMouseDown: EventHandlerNonNull; + onInputChange: EventHandlerNonNull; + onInputPaste: EventHandlerNonNull; + onInputCompositionStart: EventHandlerNonNull; + onInputCompositionEnd: EventHandlerNonNull; +} diff --git a/components/vc-select/generate.tsx b/components/vc-select/generate.tsx index 09b2591ce..eb6571923 100644 --- a/components/vc-select/generate.tsx +++ b/components/vc-select/generate.tsx @@ -1057,7 +1057,7 @@ export default function generateSelector< methods: { // We need force update here since popup dom is render async onPopupMouseEnter() { - this.$forceUpdate(); + (this as any).$forceUpdate(); }, }, render() { diff --git a/v2-doc b/v2-doc index 157cce105..6b53258cc 160000 --- a/v2-doc +++ b/v2-doc @@ -1 +1 @@ -Subproject commit 157cce105e1f0a369658dfb29cc802ebc09d0d93 +Subproject commit 6b53258cc2b3709e070d340714e992760e660e67