diff --git a/antdv-demo b/antdv-demo
index 955716e4e..79d49c0ff 160000
--- a/antdv-demo
+++ b/antdv-demo
@@ -1 +1 @@
-Subproject commit 955716e4e9533bc628c651d6ba6c8d1eb9b21a9d
+Subproject commit 79d49c0ff31a4f505ccd5bc3ad238c08f9925212
diff --git a/components/_util/pickAttrs.js b/components/_util/pickAttrs.js
new file mode 100644
index 000000000..30a241893
--- /dev/null
+++ b/components/_util/pickAttrs.js
@@ -0,0 +1,69 @@
+const attributes = `accept acceptCharset accessKey action allowFullScreen allowTransparency
+ alt async autoComplete autoFocus autoPlay capture cellPadding cellSpacing challenge
+ charSet checked classID className colSpan cols content contentEditable contextMenu
+ controls coords crossOrigin data dateTime default defer dir disabled download draggable
+ encType form formAction formEncType formMethod formNoValidate formTarget frameBorder
+ headers height hidden high href hrefLang htmlFor httpEquiv icon id inputMode integrity
+ is keyParams keyType kind label lang list loop low manifest marginHeight marginWidth max maxLength media
+ mediaGroup method min minLength multiple muted name noValidate nonce open
+ optimum pattern placeholder poster preload radioGroup readOnly rel required
+ reversed role rowSpan rows sandbox scope scoped scrolling seamless selected
+ shape size sizes span spellCheck src srcDoc srcLang srcSet start step style
+ summary tabIndex target title type useMap value width wmode wrap`;
+
+const eventsName = `onCopy onCut onPaste onCompositionEnd onCompositionStart onCompositionUpdate onKeyDown
+ onKeyPress onKeyUp onFocus onBlur onChange onInput onSubmit onClick onContextMenu onDoubleClick
+ onDrag onDragEnd onDragEnter onDragExit onDragLeave onDragOver onDragStart onDrop onMouseDown
+ onMouseEnter onMouseLeave onMouseMove onMouseOut onMouseOver onMouseUp onSelect onTouchCancel
+ onTouchEnd onTouchMove onTouchStart onScroll onWheel onAbort onCanPlay onCanPlayThrough
+ onDurationChange onEmptied onEncrypted onEnded onError onLoadedData onLoadedMetadata
+ onLoadStart onPause onPlay onPlaying onProgress onRateChange onSeeked onSeeking onStalled onSuspend onTimeUpdate onVolumeChange onWaiting onLoad onError`;
+
+const propList = `${attributes} ${eventsName}`.split(/[\s\n]+/);
+
+/* eslint-enable max-len */
+const ariaPrefix = 'aria-';
+const dataPrefix = 'data-';
+
+function match(key, prefix) {
+ return key.indexOf(prefix) === 0;
+}
+
+/**
+ * Picker props from exist props with filter
+ * @param props Passed props
+ * @param ariaOnly boolean | { aria?: boolean; data?: boolean; attr?: boolean; } filter config
+ */
+export default function pickAttrs(props, ariaOnly = false) {
+ let mergedConfig;
+ if (ariaOnly === false) {
+ mergedConfig = {
+ aria: true,
+ data: true,
+ attr: true,
+ };
+ } else if (ariaOnly === true) {
+ mergedConfig = {
+ aria: true,
+ };
+ } else {
+ mergedConfig = {
+ ...ariaOnly,
+ };
+ }
+
+ const attrs = {};
+ Object.keys(props).forEach(key => {
+ if (
+ // Aria
+ (mergedConfig.aria && (key === 'role' || match(key, ariaPrefix))) ||
+ // Data
+ (mergedConfig.data && match(key, dataPrefix)) ||
+ // Attr
+ (mergedConfig.attr && propList.includes(key))
+ ) {
+ attrs[key] = props[key];
+ }
+ });
+ return attrs;
+}
diff --git a/components/vc-select2/OptGroup.jsx b/components/vc-select2/OptGroup.jsx
new file mode 100644
index 000000000..1293870d5
--- /dev/null
+++ b/components/vc-select2/OptGroup.jsx
@@ -0,0 +1,11 @@
+import PropTypes from '../_util/vue-types';
+export default {
+ props: {
+ value: PropTypes.oneOfType([PropTypes.string, PropTypes.number]),
+ label: PropTypes.oneOfType([PropTypes.string, PropTypes.number]),
+ },
+ isSelectOptGroup: true,
+ render() {
+ return null;
+ },
+};
diff --git a/components/vc-select2/Option.jsx b/components/vc-select2/Option.jsx
new file mode 100644
index 000000000..6704e4c42
--- /dev/null
+++ b/components/vc-select2/Option.jsx
@@ -0,0 +1,14 @@
+import PropTypes from '../_util/vue-types';
+
+export default {
+ props: {
+ value: PropTypes.oneOfType([PropTypes.string, PropTypes.number]),
+ label: PropTypes.oneOfType([PropTypes.string, PropTypes.number]),
+ disabled: PropTypes.bool,
+ title: PropTypes.oneOfType([PropTypes.string, PropTypes.number]),
+ },
+ isSelectOption: true,
+ render() {
+ return null;
+ },
+};
diff --git a/components/vc-select2/OptionList.jsx b/components/vc-select2/OptionList.jsx
new file mode 100644
index 000000000..d526affc3
--- /dev/null
+++ b/components/vc-select2/OptionList.jsx
@@ -0,0 +1,348 @@
+import TransBtn from './TransBtn';
+import PropTypes from '../_util/vue-types';
+import KeyCode from '../_util/KeyCode';
+import classNames from '../_util/classNames';
+import pickAttrs from '../_util/pickAttrs';
+import { isValidElement } from '../_util/props-util';
+import createRef from '../_util/createRef';
+import { computed, reactive, watch } from 'vue';
+import List from '../vc-virtual-list/List';
+
+const OptionListProps = {
+ prefixCls: PropTypes.string,
+ id: PropTypes.string,
+ options: PropTypes.array,
+ flattenOptions: PropTypes.array,
+ height: PropTypes.number,
+ itemHeight: PropTypes.number,
+ values: PropTypes.any,
+ multiple: PropTypes.bool,
+ open: PropTypes.bool,
+ defaultActiveFirstOption: PropTypes.bool,
+ notFoundContent: PropTypes.any,
+ menuItemSelectedIcon: PropTypes.any,
+ childrenAsData: PropTypes.bool,
+ searchValue: PropTypes.string,
+ virtual: PropTypes.bool,
+
+ onSelect: PropTypes.func,
+ onToggleOpen: PropTypes.func,
+ /** Tell Select that some value is now active to make accessibility work */
+ onActiveValue: PropTypes.func,
+ onScroll: PropTypes.func,
+
+ /** Tell Select that mouse enter the popup to force re-render */
+ onMouseenter: PropTypes.func,
+};
+
+/**
+ * Using virtual list of option display.
+ * Will fallback to dom if use customize render.
+ */
+const OptionList = {
+ props: OptionListProps,
+ name: 'OptionList',
+ inheritAttrs: false,
+ setup(props) {
+ const itemPrefixCls = computed(() => `${props.prefixCls}-item`);
+
+ // =========================== List ===========================
+ const listRef = createRef();
+
+ const onListMouseDown = event => {
+ event.preventDefault();
+ };
+
+ const scrollIntoView = index => {
+ if (listRef.current) {
+ listRef.current.scrollTo({ index });
+ }
+ };
+
+ // ========================== Active ==========================
+ const getEnabledActiveIndex = (index, offset = 1) => {
+ const len = props.flattenOptions.length;
+
+ for (let i = 0; i < len; i += 1) {
+ const current = (index + i * offset + len) % len;
+
+ const { group, data } = props.flattenOptions[current];
+ if (!group && !data.disabled) {
+ return current;
+ }
+ }
+
+ return -1;
+ };
+ const state = reactive({
+ activeIndex: getEnabledActiveIndex(0),
+ });
+
+ const setActive = (index, fromKeyboard = false) => {
+ state.activeIndex = index;
+ const info = { source: fromKeyboard ? 'keyboard' : 'mouse' };
+
+ // Trigger active event
+ const flattenItem = props.flattenOptions[index];
+ if (!flattenItem) {
+ props.onActiveValue(null, -1, info);
+ return;
+ }
+
+ props.onActiveValue(flattenItem.data.value, index, info);
+ };
+
+ // Auto active first item when list length or searchValue changed
+
+ watch([props.flattenOptions.length, props.searchValue], () => {
+ setActive(props.defaultActiveFirstOption !== false ? getEnabledActiveIndex(0) : -1);
+ });
+ // Auto scroll to item position in single mode
+ watch(props.open, () => {
+ /**
+ * React will skip `onChange` when component update.
+ * `setActive` function will call root accessibility state update which makes re-render.
+ * So we need to delay to let Input component trigger onChange first.
+ */
+ const timeoutId = setTimeout(() => {
+ if (!props.multiple && props.open && props.values.size === 1) {
+ const value = Array.from(props.values)[0];
+ const index = props.flattenOptions.findIndex(({ data }) => data.value === value);
+ setActive(index);
+ scrollIntoView(index);
+ }
+ });
+
+ return () => clearTimeout(timeoutId);
+ });
+
+ // ========================== Values ==========================
+ const onSelectValue = value => {
+ if (value !== undefined) {
+ props.onSelect(value, { selected: !props.values.has(value) });
+ }
+
+ // Single mode should always close by select
+ if (!props.multiple) {
+ props.onToggleOpen(false);
+ }
+ };
+
+ function renderItem(index) {
+ const item = props.flattenOptions[index];
+ if (!item) return null;
+
+ const itemData = item.data || {};
+ const { value, label, children } = itemData;
+ const attrs = pickAttrs(itemData, true);
+ const mergedLabel = props.childrenAsData ? children : label;
+ return item ? (
+
+ {value}
+
+ ) : null;
+ }
+ return {
+ renderItem,
+ listRef,
+ state,
+ onListMouseDown,
+ itemPrefixCls,
+ setActive,
+ onSelectValue,
+ onKeydown: event => {
+ const { which } = event;
+ switch (which) {
+ // >>> Arrow keys
+ case KeyCode.UP:
+ case KeyCode.DOWN: {
+ let offset = 0;
+ if (which === KeyCode.UP) {
+ offset = -1;
+ } else if (which === KeyCode.DOWN) {
+ offset = 1;
+ }
+
+ if (offset !== 0) {
+ const nextActiveIndex = getEnabledActiveIndex(state.activeIndex + offset, offset);
+ scrollIntoView(nextActiveIndex);
+ setActive(nextActiveIndex, true);
+ }
+
+ break;
+ }
+
+ // >>> Select
+ case KeyCode.ENTER: {
+ // value
+ const item = props.flattenOptions[state.activeIndex];
+ if (item && !item.data.disabled) {
+ onSelectValue(item.data.value);
+ } else {
+ onSelectValue(undefined);
+ }
+
+ if (props.open) {
+ event.preventDefault();
+ }
+
+ break;
+ }
+
+ // >>> Close
+ case KeyCode.ESC: {
+ props.onToggleOpen(false);
+ }
+ }
+ },
+ onKeyup: () => {},
+
+ scrollTo: index => {
+ scrollIntoView(index);
+ },
+ };
+ },
+ render() {
+ const { renderItem, listRef, onListMouseDown, itemPrefixCls, setActive, onSelectValue } = this;
+ const {
+ id,
+ childrenAsData,
+ values,
+ height,
+ itemHeight,
+ flattenOptions,
+ menuItemSelectedIcon,
+ notFoundContent,
+ virtual,
+ onScroll,
+ onMouseenter,
+ } = this.$props;
+ const { activeIndex } = this.state;
+ // ========================== Render ==========================
+ if (flattenOptions.length === 0) {
+ return (
+
+ {notFoundContent}
+
+ );
+ }
+ return (
+ <>
+
+ {renderItem(activeIndex - 1)}
+ {renderItem(activeIndex)}
+ {renderItem(activeIndex + 1)}
+
+
+ {({ group, groupOption, data }, itemIndex) => {
+ const { label, key } = data;
+
+ // Group
+ if (group) {
+ return (
+
+ {label !== undefined ? label : key}
+
+ );
+ }
+
+ const {
+ disabled,
+ value,
+ title,
+ children,
+ style,
+ class: cls,
+ className,
+ ...otherProps
+ } = data;
+
+ // Option
+ const selected = values.has(value);
+
+ const optionPrefixCls = `${itemPrefixCls}-option`;
+ const optionClassName = classNames(itemPrefixCls, optionPrefixCls, cls, className, {
+ [`${optionPrefixCls}-grouped`]: groupOption,
+ [`${optionPrefixCls}-active`]: activeIndex === itemIndex && !disabled,
+ [`${optionPrefixCls}-disabled`]: disabled,
+ [`${optionPrefixCls}-selected`]: selected,
+ });
+
+ const mergedLabel = childrenAsData ? children : label;
+
+ const iconVisible =
+ !menuItemSelectedIcon || typeof menuItemSelectedIcon === 'function' || selected;
+
+ const content = mergedLabel || value;
+ // https://github.com/ant-design/ant-design/issues/26717
+ let optionTitle =
+ typeof content === 'string' || typeof content === 'number'
+ ? content.toString()
+ : undefined;
+ if (title !== undefined) {
+ optionTitle = title;
+ }
+
+ return (
+ {
+ if (activeIndex === itemIndex || disabled) {
+ return;
+ }
+ setActive(itemIndex);
+ }}
+ onClick={() => {
+ if (!disabled) {
+ onSelectValue(value);
+ }
+ }}
+ style={style}
+ >
+
{content}
+ {isValidElement(menuItemSelectedIcon) || selected}
+ {iconVisible && (
+
+ {selected ? '✓' : null}
+
+ )}
+
+ );
+ }}
+
+ >
+ );
+ },
+};
+
+export default OptionList;
diff --git a/components/vc-select2/SelectTrigger.jsx b/components/vc-select2/SelectTrigger.jsx
new file mode 100644
index 000000000..863d3abde
--- /dev/null
+++ b/components/vc-select2/SelectTrigger.jsx
@@ -0,0 +1,144 @@
+import Trigger from '../vc-trigger';
+import PropTypes from '../_util/vue-types';
+import { getSlot } from '../_util/props-util';
+import classNames from '../_util/classNames';
+import createRef from '../_util/createRef';
+
+const getBuiltInPlacements = dropdownMatchSelectWidth => {
+ // Enable horizontal overflow auto-adjustment when a custom dropdown width is provided
+ const adjustX = typeof dropdownMatchSelectWidth !== 'number' ? 0 : 1;
+
+ return {
+ bottomLeft: {
+ points: ['tl', 'bl'],
+ offset: [0, 4],
+ overflow: {
+ adjustX,
+ adjustY: 1,
+ },
+ },
+ bottomRight: {
+ points: ['tr', 'br'],
+ offset: [0, 4],
+ overflow: {
+ adjustX,
+ adjustY: 1,
+ },
+ },
+ topLeft: {
+ points: ['bl', 'tl'],
+ offset: [0, -4],
+ overflow: {
+ adjustX,
+ adjustY: 1,
+ },
+ },
+ topRight: {
+ points: ['br', 'tr'],
+ offset: [0, -4],
+ overflow: {
+ adjustX,
+ adjustY: 1,
+ },
+ },
+ };
+};
+
+export default {
+ name: 'SelectTrigger',
+ inheritAttrs: false,
+ props: {
+ // onPopupFocus: PropTypes.func,
+ // onPopupScroll: PropTypes.func,
+ dropdownAlign: PropTypes.object,
+ visible: PropTypes.bool,
+ disabled: PropTypes.bool,
+ dropdownClassName: PropTypes.string,
+ dropdownStyle: PropTypes.object,
+ empty: PropTypes.bool,
+ prefixCls: PropTypes.string,
+ popupClassName: PropTypes.string,
+ // children: PropTypes.any,
+ animation: PropTypes.string,
+ transitionName: PropTypes.string,
+ getPopupContainer: PropTypes.func,
+ dropdownRender: PropTypes.func,
+ containerWidth: PropTypes.number,
+ dropdownMatchSelectWidth: PropTypes.oneOfType([Number, Boolean]).def(true),
+ popupElement: PropTypes.any,
+ direction: PropTypes.string,
+ getTriggerDOMNode: PropTypes.func,
+ },
+
+ created() {
+ this.popupRef = createRef();
+ },
+
+ methods: {
+ getDropdownTransitionName() {
+ const props = this.$props;
+ let transitionName = props.transitionName;
+ if (!transitionName && props.animation) {
+ transitionName = `${this.getDropdownPrefixCls()}-${props.animation}`;
+ }
+ return transitionName;
+ },
+ getPopupElement() {
+ return this.popupRef.current;
+ },
+ },
+
+ render() {
+ const { empty, ...props } = { ...this.$props, ...this.$attrs };
+ const {
+ visible,
+ dropdownAlign,
+ prefixCls,
+ popupElement,
+ dropdownClassName,
+ dropdownStyle,
+ dropdownMatchSelectWidth,
+ containerWidth,
+ dropdownRender,
+ } = props;
+ const dropdownPrefixCls = `${prefixCls}-dropdown`;
+
+ let popupNode = popupElement;
+ if (dropdownRender) {
+ popupNode = dropdownRender({ menuNode: popupElement, props });
+ }
+
+ const builtInPlacements = getBuiltInPlacements(dropdownMatchSelectWidth);
+ const popupStyle = { minWidth: containerWidth, ...dropdownStyle };
+
+ if (typeof dropdownMatchSelectWidth === 'number') {
+ popupStyle.width = `${dropdownMatchSelectWidth}px`;
+ } else if (dropdownMatchSelectWidth) {
+ popupStyle.width = `${containerWidth}px`;
+ }
+ return (
+ {popupNode}}
+ popupAlign={dropdownAlign}
+ popupVisible={visible}
+ getPopupContainer={props.getPopupContainer}
+ popupClassName={classNames(dropdownClassName, {
+ [`${dropdownPrefixCls}-empty`]: empty,
+ })}
+ popupStyle={popupStyle}
+ getTriggerDOMNode={this.getTriggerDOMNode}
+ >
+ {getSlot(this)[0]}
+
+ );
+ },
+};
diff --git a/components/vc-select2/TransBtn.jsx b/components/vc-select2/TransBtn.jsx
new file mode 100644
index 000000000..47bfd4740
--- /dev/null
+++ b/components/vc-select2/TransBtn.jsx
@@ -0,0 +1,41 @@
+const TransBtn = (
+ _,
+ { attrs: { class: className, customizeIcon, customizeIconProps, onMousedown, onClick }, slots },
+) => {
+ let icon;
+
+ if (typeof customizeIcon === 'function') {
+ icon = customizeIcon(customizeIconProps);
+ } else {
+ icon = customizeIcon;
+ }
+
+ return (
+ {
+ event.preventDefault();
+ if (onMousedown) {
+ onMousedown(event);
+ }
+ }}
+ style={{
+ userSelect: 'none',
+ WebkitUserSelect: 'none',
+ }}
+ unselectable="on"
+ onClick={onClick}
+ aria-hidden
+ >
+ {icon !== undefined ? (
+ icon
+ ) : (
+ `${cls}-icon`)}>{slots?.default()}
+ )}
+
+ );
+};
+
+TransBtn.inheritAttrs = false;
+
+export default TransBtn;
diff --git a/components/vc-select2/index.js b/components/vc-select2/index.js
new file mode 100644
index 000000000..cfa3695ae
--- /dev/null
+++ b/components/vc-select2/index.js
@@ -0,0 +1,9 @@
+// based on vc-select 9.2.2
+import Select from './Select';
+import Option from './Option';
+import { SelectPropTypes } from './PropTypes';
+import OptGroup from './OptGroup';
+Select.Option = Option;
+Select.OptGroup = OptGroup;
+export { Select, Option, OptGroup, SelectPropTypes };
+export default Select;
diff --git a/components/vc-virtual-list/List.jsx b/components/vc-virtual-list/List.jsx
index 86058816c..95a666db1 100644
--- a/components/vc-virtual-list/List.jsx
+++ b/components/vc-virtual-list/List.jsx
@@ -282,7 +282,6 @@ const List = {
};
},
render() {
- const { style, class: className } = this.$attrs;
const {
prefixCls = 'rc-virtual-list',
height,
@@ -295,8 +294,10 @@ const List = {
component: Component = 'div',
onScroll,
children,
+ style,
+ class: className,
...restProps
- } = this.$props;
+ } = { ...this.$props, ...this.$attrs };
const mergedClassName = classNames(prefixCls, className);
const { scrollTop, mergedData } = this.state;
const { scrollHeight, offset, start, end } = this.calRes;