diff --git a/CHANGELOG.en-US.md b/CHANGELOG.en-US.md
index b76a61caa..12cc1a41e 100644
--- a/CHANGELOG.en-US.md
+++ b/CHANGELOG.en-US.md
@@ -10,6 +10,30 @@
---
+## 4.2.5
+
+- 🐞 Fix Empty component memory leak problem
+- 🐞 Fix Image width & height property not working problem
+
+## 4.2.4
+
+- 🐞 Fix Wave memory leak problem
+
+## 4.2.3
+
+- 🌟 TourStep custom Button, support function children [#7628](https://github.com/vueComponent/ant-design-vue/pull/7628)
+- 🐞 Fix the problem that the input value is hidden in Select and Cascader search multi-select mode [#7640](https://github.com/vueComponent/ant-design-vue/issues/7640)
+
+## 4.2.2
+
+- 🐞 Fix TreeSelect placeholder slot invalid [#7545](https://github.com/vueComponent/ant-design-vue/issues/7545)
+- 🐞 Fix Tree slot responsive invalid issue [40ad45](https://github.com/vueComponent/ant-design-vue/commit/40ad45bc05b2bf9d0a2445d9f6ff365468ba90b7)
+- 🐞 Fix FloatButton target type error issue [#7576](https://github.com/vueComponent/ant-design-vue/issues/7576)
+- 🐞 Fix FormItem className error issue [#7582](https://github.com/vueComponent/ant-design-vue/issues/7582)
+- 🐞 Fix Input Cannot input problem under lazy [#7543](https://github.com/vueComponent/ant-design-vue/issues/7543)
+- 🐞 Fix the problem that placeholder is not hidden when inputting Chinese in Select [#7611](https://github.com/vueComponent/ant-design-vue/issues/7611)
+- 🐞 Fix the problem that the pop-up window flashes when clicking the preset option in DatePicker [#7550](https://github.com/vueComponent/ant-design-vue/issues/7550)
+
## 4.2.1
- 🐞 fix Input clear action error [#7523](https://github.com/vueComponent/ant-design-vue/issues/7523)
diff --git a/CHANGELOG.zh-CN.md b/CHANGELOG.zh-CN.md
index 36c43e269..d1e06a065 100644
--- a/CHANGELOG.zh-CN.md
+++ b/CHANGELOG.zh-CN.md
@@ -10,6 +10,30 @@
---
+## 4.2.5
+
+- 🐞 修复 Empty 组件内存泄漏问题
+- 🐞 修复 Image width & height 属性不生效问题
+
+## 4.2.4
+
+- 🐞 修复 Wave 内存泄漏问题
+
+## 4.2.3
+
+- 🌟 TourStep 自定义 Button,支持函数 children [#7628](https://github.com/vueComponent/ant-design-vue/pull/7628)
+- 🐞 修复 Select 和 Cascader 搜索多选模式下,输入值被隐藏问题 [#7640](https://github.com/vueComponent/ant-design-vue/issues/7640)
+
+## 4.2.2
+
+- 🐞 修复 TreeSelect placeholder 插槽无效 [#7545](https://github.com/vueComponent/ant-design-vue/issues/7545)
+- 🐞 修复 Tree 插槽响应式无效问题 [40ad45](https://github.com/vueComponent/ant-design-vue/commit/40ad45bc05b2bf9d0a2445d9f6ff365468ba90b7)
+- 🐞 修复 FloatButton target 类型错误问题 [#7576](https://github.com/vueComponent/ant-design-vue/issues/7576)
+- 🐞 修复 FormItem className 错误问题 [#7582](https://github.com/vueComponent/ant-design-vue/issues/7582)
+- 🐞 修复 Input lazy 下无法输入问题 [#7543](https://github.com/vueComponent/ant-design-vue/issues/7543)
+- 🐞 修复 Select 输入中文时,placeholder 未隐藏问题 [#7611](https://github.com/vueComponent/ant-design-vue/issues/7611)
+- 🐞 修复 DatePicker 点击预设选项时,弹窗闪动问题 [#7550](https://github.com/vueComponent/ant-design-vue/issues/7550)
+
## 4.2.1
- 🐞 修复 Input 清空操作才报错问题 [#7523](https://github.com/vueComponent/ant-design-vue/issues/7523)
diff --git a/README-zh_CN.md b/README-zh_CN.md
index 4f098de14..c67223107 100644
--- a/README-zh_CN.md
+++ b/README-zh_CN.md
@@ -10,7 +10,7 @@
-An enterprise-class UI components based on Ant Design and Vue 3.
+基于 Ant Design 和 Vue 3 的企业级 UI 组件库。
 [](https://codecov.io/gh/vueComponent/ant-design-vue) [](https://www.npmjs.org/package/ant-design-vue) [](http://www.npmtrends.com/ant-design-vue) [](#backers) [](#sponsors) [](https://marketplace.visualstudio.com/items?itemName=ant-design-vue.vscode-ant-design-vue-helper) [](https://github.com/actions-cool/issues-helper)
@@ -90,21 +90,21 @@ ant-design-vue 是 MIT 协议的开源项目。为了项目能够更好的持续
- [支付宝或微信](https://aliyuncdn.antdv.com/alipay-and-wechat.png)
- ETH: 0x30cc48515d8ae9fefa20ab87226ad7e8ab9c3bc2
-## Sponsors
+## 赞助商
-Become a sponsor and get your logo on our README on Github with a link to your site. [[Become a sponsor](https://opencollective.com/ant-design-vue#sponsor)]
+成为赞助商,并在 Github 上的自述文件上获得您的徽标,并链接到您的网站。 [[成为赞助商](https://opencollective.com/ant-design-vue#sponsor)]

-## Backers
+## 支持者
-Support us with a monthly donation and help us continue our activities. [[Become a backer](https://opencollective.com/ant-design-vue#backer)]
+每月捐款支持我们,帮助我们继续我们的活动。 [[成为支持者](https://opencollective.com/ant-design-vue#backer)]

## Patreon
-Support us with a monthly donation and help us continue our activities. [[Become a backer](https://www.patreon.com/tangjinzhou)]
+每月捐款支持我们,帮助我们继续我们的活动。 [[成为支持者](https://www.patreon.com/tangjinzhou)]

diff --git a/components/_util/BaseInput.tsx b/components/_util/BaseInput.tsx
index b01256715..e6bc9d708 100644
--- a/components/_util/BaseInput.tsx
+++ b/components/_util/BaseInput.tsx
@@ -1,6 +1,9 @@
import type { PropType } from 'vue';
-import { defineComponent, shallowRef, ref, watch } from 'vue';
+import { computed, defineComponent, shallowRef, ref, watch } from 'vue';
import PropTypes from './vue-types';
+import type { BaseInputInnerExpose } from './BaseInputInner';
+import BaseInputInner from './BaseInputInner';
+import { styleObjectToString } from '../vc-util/Dom/css';
export interface BaseInputExpose {
focus: () => void;
@@ -30,6 +33,8 @@ const BaseInput = defineComponent({
default: 'input',
},
size: PropTypes.string,
+ style: PropTypes.oneOfType([String, Object]),
+ class: PropTypes.string,
},
emits: [
'change',
@@ -40,9 +45,11 @@ const BaseInput = defineComponent({
'compositionstart',
'compositionend',
'keyup',
+ 'paste',
+ 'mousedown',
],
setup(props, { emit, attrs, expose }) {
- const inputRef = shallowRef(null);
+ const inputRef = shallowRef
(null);
const renderValue = ref();
const isComposing = ref(false);
watch(
@@ -68,6 +75,7 @@ const BaseInput = defineComponent({
const event = document.createEvent('HTMLEvents');
event.initEvent('input', true, true);
e.target.dispatchEvent(event);
+ handleChange(e);
};
const handleInput = (e: Event) => {
if (isComposing.value && props.lazy) {
@@ -114,19 +122,31 @@ const BaseInput = defineComponent({
expose({
focus,
blur,
- input: inputRef,
+ input: computed(() => inputRef.value?.input),
setSelectionRange,
select,
- getSelectionStart: () => inputRef.value?.selectionStart,
- getSelectionEnd: () => inputRef.value?.selectionEnd,
- getScrollTop: () => inputRef.value?.scrollTop,
+ getSelectionStart: () => inputRef.value?.getSelectionStart(),
+ getSelectionEnd: () => inputRef.value?.getSelectionEnd(),
+ getScrollTop: () => inputRef.value?.getScrollTop(),
+ });
+ const handleMousedown = (e: MouseEvent) => {
+ emit('mousedown', e);
+ };
+ const handlePaste = (e: ClipboardEvent) => {
+ emit('paste', e);
+ };
+ const styleString = computed(() => {
+ return props.style && typeof props.style !== 'string'
+ ? styleObjectToString(props.style)
+ : props.style;
});
return () => {
- const { tag: Tag, ...restProps } = props;
+ const { style, lazy, ...restProps } = props;
return (
-
);
};
diff --git a/components/_util/BaseInputInner.tsx b/components/_util/BaseInputInner.tsx
new file mode 100644
index 000000000..10423d7a4
--- /dev/null
+++ b/components/_util/BaseInputInner.tsx
@@ -0,0 +1,96 @@
+import type { PropType } from 'vue';
+import { defineComponent, shallowRef } from 'vue';
+import PropTypes from './vue-types';
+
+export interface BaseInputInnerExpose {
+ focus: () => void;
+ blur: () => void;
+ input: HTMLInputElement | HTMLTextAreaElement | null;
+ setSelectionRange: (
+ start: number,
+ end: number,
+ direction?: 'forward' | 'backward' | 'none',
+ ) => void;
+ select: () => void;
+ getSelectionStart: () => number | null;
+ getSelectionEnd: () => number | null;
+ getScrollTop: () => number | null;
+ setScrollTop: (scrollTop: number) => void;
+}
+const BaseInputInner = defineComponent({
+ compatConfig: { MODE: 3 },
+ // inheritAttrs: false,
+ props: {
+ disabled: PropTypes.looseBool,
+ type: PropTypes.string,
+ value: PropTypes.any,
+ tag: {
+ type: String as PropType<'input' | 'textarea'>,
+ default: 'input',
+ },
+ size: PropTypes.string,
+ onChange: Function as PropType<(e: Event) => void>,
+ onInput: Function as PropType<(e: Event) => void>,
+ onBlur: Function as PropType<(e: Event) => void>,
+ onFocus: Function as PropType<(e: Event) => void>,
+ onKeydown: Function as PropType<(e: Event) => void>,
+ onCompositionstart: Function as PropType<(e: Event) => void>,
+ onCompositionend: Function as PropType<(e: Event) => void>,
+ onKeyup: Function as PropType<(e: Event) => void>,
+ onPaste: Function as PropType<(e: Event) => void>,
+ onMousedown: Function as PropType<(e: Event) => void>,
+ },
+ emits: [
+ 'change',
+ 'input',
+ 'blur',
+ 'keydown',
+ 'focus',
+ 'compositionstart',
+ 'compositionend',
+ 'keyup',
+ 'paste',
+ 'mousedown',
+ ],
+ setup(props, { expose }) {
+ const inputRef = shallowRef(null);
+
+ const focus = () => {
+ if (inputRef.value) {
+ inputRef.value.focus();
+ }
+ };
+ const blur = () => {
+ if (inputRef.value) {
+ inputRef.value.blur();
+ }
+ };
+ const setSelectionRange = (
+ start: number,
+ end: number,
+ direction?: 'forward' | 'backward' | 'none',
+ ) => {
+ inputRef.value?.setSelectionRange(start, end, direction);
+ };
+
+ const select = () => {
+ inputRef.value?.select();
+ };
+ expose({
+ focus,
+ blur,
+ input: inputRef,
+ setSelectionRange,
+ select,
+ getSelectionStart: () => inputRef.value?.selectionStart,
+ getSelectionEnd: () => inputRef.value?.selectionEnd,
+ getScrollTop: () => inputRef.value?.scrollTop,
+ });
+ return () => {
+ const { tag: Tag, value, ...restProps } = props;
+ return ;
+ };
+ },
+});
+
+export default BaseInputInner;
diff --git a/components/_util/css-animation/Event.js b/components/_util/css-animation/Event.js
deleted file mode 100644
index cd5e87155..000000000
--- a/components/_util/css-animation/Event.js
+++ /dev/null
@@ -1,130 +0,0 @@
-const START_EVENT_NAME_MAP = {
- transitionstart: {
- transition: 'transitionstart',
- WebkitTransition: 'webkitTransitionStart',
- MozTransition: 'mozTransitionStart',
- OTransition: 'oTransitionStart',
- msTransition: 'MSTransitionStart',
- },
-
- animationstart: {
- animation: 'animationstart',
- WebkitAnimation: 'webkitAnimationStart',
- MozAnimation: 'mozAnimationStart',
- OAnimation: 'oAnimationStart',
- msAnimation: 'MSAnimationStart',
- },
-};
-
-const END_EVENT_NAME_MAP = {
- transitionend: {
- transition: 'transitionend',
- WebkitTransition: 'webkitTransitionEnd',
- MozTransition: 'mozTransitionEnd',
- OTransition: 'oTransitionEnd',
- msTransition: 'MSTransitionEnd',
- },
-
- animationend: {
- animation: 'animationend',
- WebkitAnimation: 'webkitAnimationEnd',
- MozAnimation: 'mozAnimationEnd',
- OAnimation: 'oAnimationEnd',
- msAnimation: 'MSAnimationEnd',
- },
-};
-
-const startEvents = [];
-const endEvents = [];
-
-function detectEvents() {
- const testEl = document.createElement('div');
- const style = testEl.style;
-
- if (!('AnimationEvent' in window)) {
- delete START_EVENT_NAME_MAP.animationstart.animation;
- delete END_EVENT_NAME_MAP.animationend.animation;
- }
-
- if (!('TransitionEvent' in window)) {
- delete START_EVENT_NAME_MAP.transitionstart.transition;
- delete END_EVENT_NAME_MAP.transitionend.transition;
- }
-
- function process(EVENT_NAME_MAP, events) {
- for (const baseEventName in EVENT_NAME_MAP) {
- if (EVENT_NAME_MAP.hasOwnProperty(baseEventName)) {
- const baseEvents = EVENT_NAME_MAP[baseEventName];
- for (const styleName in baseEvents) {
- if (styleName in style) {
- events.push(baseEvents[styleName]);
- break;
- }
- }
- }
- }
- }
-
- process(START_EVENT_NAME_MAP, startEvents);
- process(END_EVENT_NAME_MAP, endEvents);
-}
-
-if (typeof window !== 'undefined' && typeof document !== 'undefined') {
- detectEvents();
-}
-
-function addEventListener(node, eventName, eventListener) {
- node.addEventListener(eventName, eventListener, false);
-}
-
-function removeEventListener(node, eventName, eventListener) {
- node.removeEventListener(eventName, eventListener, false);
-}
-
-const TransitionEvents = {
- // Start events
- startEvents,
-
- addStartEventListener(node, eventListener) {
- if (startEvents.length === 0) {
- setTimeout(eventListener, 0);
- return;
- }
- startEvents.forEach(startEvent => {
- addEventListener(node, startEvent, eventListener);
- });
- },
-
- removeStartEventListener(node, eventListener) {
- if (startEvents.length === 0) {
- return;
- }
- startEvents.forEach(startEvent => {
- removeEventListener(node, startEvent, eventListener);
- });
- },
-
- // End events
- endEvents,
-
- addEndEventListener(node, eventListener) {
- if (endEvents.length === 0) {
- setTimeout(eventListener, 0);
- return;
- }
- endEvents.forEach(endEvent => {
- addEventListener(node, endEvent, eventListener);
- });
- },
-
- removeEndEventListener(node, eventListener) {
- if (endEvents.length === 0) {
- return;
- }
- endEvents.forEach(endEvent => {
- removeEventListener(node, endEvent, eventListener);
- });
- },
-};
-
-export default TransitionEvents;
diff --git a/components/_util/css-animation/index.js b/components/_util/css-animation/index.js
deleted file mode 100644
index 86e399dec..000000000
--- a/components/_util/css-animation/index.js
+++ /dev/null
@@ -1,186 +0,0 @@
-// https://github.com/yiminghe/css-animation 1.5.0
-
-import Event from './Event';
-import classes from '../component-classes';
-import { requestAnimationTimeout, cancelAnimationTimeout } from '../requestAnimationTimeout';
-import { inBrowser } from '../env';
-
-const isCssAnimationSupported = Event.endEvents.length !== 0;
-const capitalPrefixes = [
- 'Webkit',
- 'Moz',
- 'O',
- // ms is special .... !
- 'ms',
-];
-const prefixes = ['-webkit-', '-moz-', '-o-', 'ms-', ''];
-
-function getStyleProperty(node, name) {
- if (inBrowser) return '';
- // old ff need null, https://developer.mozilla.org/en-US/docs/Web/API/Window/getComputedStyle
- const style = window.getComputedStyle(node, null);
- let ret = '';
- for (let i = 0; i < prefixes.length; i++) {
- ret = style.getPropertyValue(prefixes[i] + name);
- if (ret) {
- break;
- }
- }
- return ret;
-}
-
-function fixBrowserByTimeout(node) {
- if (isCssAnimationSupported) {
- const transitionDelay = parseFloat(getStyleProperty(node, 'transition-delay')) || 0;
- const transitionDuration = parseFloat(getStyleProperty(node, 'transition-duration')) || 0;
- const animationDelay = parseFloat(getStyleProperty(node, 'animation-delay')) || 0;
- const animationDuration = parseFloat(getStyleProperty(node, 'animation-duration')) || 0;
- const time = Math.max(transitionDuration + transitionDelay, animationDuration + animationDelay);
- // sometimes, browser bug
- node.rcEndAnimTimeout = setTimeout(() => {
- node.rcEndAnimTimeout = null;
- if (node.rcEndListener) {
- node.rcEndListener();
- }
- }, time * 1000 + 200);
- }
-}
-
-function clearBrowserBugTimeout(node) {
- if (node.rcEndAnimTimeout) {
- clearTimeout(node.rcEndAnimTimeout);
- node.rcEndAnimTimeout = null;
- }
-}
-
-const cssAnimation = (node, transitionName, endCallback) => {
- const nameIsObj = typeof transitionName === 'object';
- const className = nameIsObj ? transitionName.name : transitionName;
- const activeClassName = nameIsObj ? transitionName.active : `${transitionName}-active`;
- let end = endCallback;
- let start;
- let active;
- const nodeClasses = classes(node);
-
- if (endCallback && Object.prototype.toString.call(endCallback) === '[object Object]') {
- end = endCallback.end;
- start = endCallback.start;
- active = endCallback.active;
- }
-
- if (node.rcEndListener) {
- node.rcEndListener();
- }
-
- node.rcEndListener = e => {
- if (e && e.target !== node) {
- return;
- }
-
- if (node.rcAnimTimeout) {
- cancelAnimationTimeout(node.rcAnimTimeout);
- node.rcAnimTimeout = null;
- }
-
- clearBrowserBugTimeout(node);
-
- nodeClasses.remove(className);
- nodeClasses.remove(activeClassName);
-
- Event.removeEndEventListener(node, node.rcEndListener);
- node.rcEndListener = null;
-
- // Usually this optional end is used for informing an owner of
- // a leave animation and telling it to remove the child.
- if (end) {
- end();
- }
- };
-
- Event.addEndEventListener(node, node.rcEndListener);
-
- if (start) {
- start();
- }
- nodeClasses.add(className);
-
- node.rcAnimTimeout = requestAnimationTimeout(() => {
- node.rcAnimTimeout = null;
-
- nodeClasses.add(className);
- nodeClasses.add(activeClassName);
-
- if (active) {
- requestAnimationTimeout(active, 0);
- }
- fixBrowserByTimeout(node);
- // 30ms for firefox
- }, 30);
-
- return {
- stop() {
- if (node.rcEndListener) {
- node.rcEndListener();
- }
- },
- };
-};
-
-cssAnimation.style = (node, style, callback) => {
- if (node.rcEndListener) {
- node.rcEndListener();
- }
-
- node.rcEndListener = e => {
- if (e && e.target !== node) {
- return;
- }
-
- if (node.rcAnimTimeout) {
- cancelAnimationTimeout(node.rcAnimTimeout);
- node.rcAnimTimeout = null;
- }
-
- clearBrowserBugTimeout(node);
-
- Event.removeEndEventListener(node, node.rcEndListener);
- node.rcEndListener = null;
-
- // Usually this optional callback is used for informing an owner of
- // a leave animation and telling it to remove the child.
- if (callback) {
- callback();
- }
- };
-
- Event.addEndEventListener(node, node.rcEndListener);
-
- node.rcAnimTimeout = requestAnimationTimeout(() => {
- for (const s in style) {
- if (style.hasOwnProperty(s)) {
- node.style[s] = style[s];
- }
- }
- node.rcAnimTimeout = null;
- fixBrowserByTimeout(node);
- }, 0);
-};
-
-cssAnimation.setTransition = (node, p, value) => {
- let property = p;
- let v = value;
- if (value === undefined) {
- v = property;
- property = '';
- }
- property = property || '';
- capitalPrefixes.forEach(prefix => {
- node.style[`${prefix}Transition${property}`] = v;
- });
-};
-
-cssAnimation.isCssAnimationSupported = isCssAnimationSupported;
-
-export { isCssAnimationSupported };
-
-export default cssAnimation;
diff --git a/components/_util/isCssAnimationSupported.js b/components/_util/isCssAnimationSupported.js
deleted file mode 100644
index 45d51bb35..000000000
--- a/components/_util/isCssAnimationSupported.js
+++ /dev/null
@@ -1,24 +0,0 @@
-let animation;
-
-function isCssAnimationSupported() {
- if (animation !== undefined) {
- return animation;
- }
- const domPrefixes = 'Webkit Moz O ms Khtml'.split(' ');
- const elm = document.createElement('div');
- if (elm.style.animationName !== undefined) {
- animation = true;
- }
- if (animation !== undefined) {
- for (let i = 0; i < domPrefixes.length; i++) {
- if (elm.style[`${domPrefixes[i]}AnimationName`] !== undefined) {
- animation = true;
- break;
- }
- }
- }
- animation = animation || false;
- return animation;
-}
-
-export default isCssAnimationSupported;
diff --git a/components/_util/transition.tsx b/components/_util/transition.tsx
index fd6ebc490..f4b69f31e 100644
--- a/components/_util/transition.tsx
+++ b/components/_util/transition.tsx
@@ -5,7 +5,7 @@ import type {
TransitionGroupProps,
TransitionProps,
} from 'vue';
-import { nextTick, Transition, TransitionGroup } from 'vue';
+import { nextTick } from 'vue';
import { tuple } from './type';
const SelectPlacements = tuple('bottomLeft', 'bottomRight', 'topLeft', 'topRight');
@@ -126,6 +126,4 @@ const getTransitionName = (rootPrefixCls: string, motion: string, transitionName
return `${rootPrefixCls}-${motion}`;
};
-export { Transition, TransitionGroup, collapseMotion, getTransitionName, getTransitionDirection };
-
-export default Transition;
+export { collapseMotion, getTransitionName, getTransitionDirection };
diff --git a/components/_util/wave/WaveEffect.tsx b/components/_util/wave/WaveEffect.tsx
index 1f3ce23d1..b59c8ff66 100644
--- a/components/_util/wave/WaveEffect.tsx
+++ b/components/_util/wave/WaveEffect.tsx
@@ -159,6 +159,12 @@ function showWaveEffect(node: HTMLElement, className: string) {
node?.insertBefore(holder, node?.firstChild);
render(, holder);
+ return () => {
+ render(null, holder);
+ if (holder.parentElement) {
+ holder.parentElement.removeChild(holder);
+ }
+ };
}
export default showWaveEffect;
diff --git a/components/_util/wave/index.tsx b/components/_util/wave/index.tsx
index 26ac1aff5..26dab40f9 100644
--- a/components/_util/wave/index.tsx
+++ b/components/_util/wave/index.tsx
@@ -33,13 +33,12 @@ export default defineComponent({
// =============================== Wave ===============================
const showWave = useWave(
- instance,
computed(() => classNames(prefixCls.value, hashId.value)),
wave,
);
let onClick: (e: MouseEvent) => void;
const clear = () => {
- const node = findDOMNode(instance);
+ const node = findDOMNode(instance) as HTMLElement;
node.removeEventListener('click', onClick, true);
};
onMounted(() => {
diff --git a/components/_util/wave/useWave.ts b/components/_util/wave/useWave.ts
index 84e2a6eff..f88c36168 100644
--- a/components/_util/wave/useWave.ts
+++ b/components/_util/wave/useWave.ts
@@ -1,21 +1,25 @@
-import type { ComponentInternalInstance, ComputedRef, Ref } from 'vue';
+import type { ComputedRef, Ref } from 'vue';
+import { onBeforeUnmount, getCurrentInstance } from 'vue';
import { findDOMNode } from '../props-util';
import showWaveEffect from './WaveEffect';
export default function useWave(
- instance: ComponentInternalInstance | null,
className: Ref,
wave?: ComputedRef<{ disabled?: boolean }>,
): VoidFunction {
+ const instance = getCurrentInstance();
+ let stopWave: () => void;
function showWave() {
const node = findDOMNode(instance);
-
+ stopWave?.();
if (wave?.value?.disabled || !node) {
return;
}
-
- showWaveEffect(node, className.value);
+ stopWave = showWaveEffect(node, className.value);
}
+ onBeforeUnmount(() => {
+ stopWave?.();
+ });
return showWave;
}
diff --git a/components/alert/index.tsx b/components/alert/index.tsx
index 63a61aa6e..f3dead2ba 100644
--- a/components/alert/index.tsx
+++ b/components/alert/index.tsx
@@ -1,5 +1,5 @@
import type { CSSProperties, ExtractPropTypes, PropType } from 'vue';
-import { computed, defineComponent, shallowRef } from 'vue';
+import { computed, defineComponent, shallowRef, Transition } from 'vue';
import CloseOutlined from '@ant-design/icons-vue/CloseOutlined';
import CheckCircleOutlined from '@ant-design/icons-vue/CheckCircleOutlined';
import ExclamationCircleOutlined from '@ant-design/icons-vue/ExclamationCircleOutlined';
@@ -11,7 +11,7 @@ import InfoCircleFilled from '@ant-design/icons-vue/InfoCircleFilled';
import CloseCircleFilled from '@ant-design/icons-vue/CloseCircleFilled';
import classNames from '../_util/classNames';
import PropTypes from '../_util/vue-types';
-import { getTransitionProps, Transition } from '../_util/transition';
+import { getTransitionProps } from '../_util/transition';
import { isValidElement } from '../_util/props-util';
import { tuple, withInstall } from '../_util/type';
import { cloneElement } from '../_util/vnode';
diff --git a/components/badge/Badge.tsx b/components/badge/Badge.tsx
index 30195d049..a0363732f 100644
--- a/components/badge/Badge.tsx
+++ b/components/badge/Badge.tsx
@@ -3,9 +3,9 @@ import ScrollNumber from './ScrollNumber';
import classNames from '../_util/classNames';
import { getPropsSlot, flattenChildren } from '../_util/props-util';
import { cloneElement } from '../_util/vnode';
-import { getTransitionProps, Transition } from '../_util/transition';
+import { getTransitionProps } from '../_util/transition';
import type { ExtractPropTypes, CSSProperties, PropType } from 'vue';
-import { defineComponent, computed, ref, watch } from 'vue';
+import { defineComponent, computed, ref, watch, Transition } from 'vue';
import Ribbon from './Ribbon';
import useConfigInject from '../config-provider/hooks/useConfigInject';
import isNumeric from '../_util/isNumeric';
diff --git a/components/button/LoadingIcon.tsx b/components/button/LoadingIcon.tsx
index 391527fd4..f865e0907 100644
--- a/components/button/LoadingIcon.tsx
+++ b/components/button/LoadingIcon.tsx
@@ -1,6 +1,5 @@
-import { defineComponent, nextTick } from 'vue';
+import { defineComponent, nextTick, Transition } from 'vue';
import LoadingOutlined from '@ant-design/icons-vue/LoadingOutlined';
-import Transition from '../_util/transition';
const getCollapsedWidth = (node: HTMLSpanElement) => {
if (node) {
node.style.width = '0px';
diff --git a/components/calendar/index.zh-CN.md b/components/calendar/index.zh-CN.md
index 3d0f68d1f..725f2d573 100644
--- a/components/calendar/index.zh-CN.md
+++ b/components/calendar/index.zh-CN.md
@@ -28,16 +28,16 @@ coverDark: https://mdn.alipayobjects.com/huamei_7uahnr/afts/img/A*-p-wQLik200AAA
| 参数 | 说明 | 类型 | 默认值 | 版本 |
| --- | --- | --- | --- | --- |
-| dateCellRender | 作用域插槽,用来自定义渲染日期单元格,返回内容会被追加到单元格, | v-slot:dateCellRender="{current: dayjs}" | 无 | |
-| dateFullCellRender | 作用域插槽,自定义渲染日期单元格,返回内容覆盖单元格 | v-slot:dateFullCellRender="{current: dayjs}" | 无 | |
-| disabledDate | 不可选择的日期 | (currentDate: dayjs) => boolean | 无 | |
+| dateCellRender | 作用域插槽,用来自定义渲染日期单元格,返回内容会被追加到单元格, | v-slot:dateCellRender="{current: dayjs}" | - | |
+| dateFullCellRender | 作用域插槽,自定义渲染日期单元格,返回内容覆盖单元格 | v-slot:dateFullCellRender="{current: dayjs}" | - | |
+| disabledDate | 不可选择的日期 | (currentDate: dayjs) => boolean | - | |
| fullscreen | 是否全屏显示 | boolean | true | |
| headerRender | 自定义头部内容 | v-slot:headerRender="{value: dayjs, type: string, onChange: f(), onTypeChange: f()}" | - | |
| locale | 国际化配置 | object | [默认配置](https://github.com/vueComponent/ant-design-vue/blob/main/components/date-picker/locale/example.json) | |
| mode | 初始模式,`month/year` | string | month | |
-| monthCellRender | 作用域插槽,自定义渲染月单元格,返回内容会被追加到单元格 | v-slot:monthCellRender="{current: dayjs}" | 无 | |
-| monthFullCellRender | 作用域插槽,自定义渲染月单元格,返回内容覆盖单元格 | v-slot:monthFullCellRender="{current: dayjs}" | 无 | |
-| validRange | 设置可以显示的日期 | \[[dayjs](https://day.js.org/), [dayjs](https://day.js.org/)] | 无 | |
+| monthCellRender | 作用域插槽,自定义渲染月单元格,返回内容会被追加到单元格 | v-slot:monthCellRender="{current: dayjs}" | - | |
+| monthFullCellRender | 作用域插槽,自定义渲染月单元格,返回内容覆盖单元格 | v-slot:monthFullCellRender="{current: dayjs}" | - | |
+| validRange | 设置可以显示的日期 | \[[dayjs](https://day.js.org/), [dayjs](https://day.js.org/)] | - | |
| value(v-model) | 展示日期 | [dayjs](https://day.js.org/) | 当前日期 | |
| valueFormat | 可选,绑定值的格式,对 value、defaultValue 起作用。不指定则绑定值为 dayjs 对象 | string,[具体格式](https://day.js.org/docs/zh-CN/display/format) | - | |
@@ -45,8 +45,8 @@ coverDark: https://mdn.alipayobjects.com/huamei_7uahnr/afts/img/A*-p-wQLik200AAA
| 事件名称 | 说明 | 回调参数 | |
| --- | --- | --- | --- | --- |
-| change | 日期变化时的回调, 面板变化有可能导致日期变化 | function(date: dayjs \| string) | 无 |
-| panelChange | 日期面板变化回调 | function(date: dayjs \| string, mode: string) | 无 |
+| change | 日期变化时的回调, 面板变化有可能导致日期变化 | function(date: dayjs \| string) | - |
+| panelChange | 日期面板变化回调 | function(date: dayjs \| string, mode: string) | - |
| select | 选择日期回调,包含来源信息 | function(date: Dayjs, info: { source: 'year' \| 'month' \| 'date' \| 'customize' }) | - | |
### 如何仅获取来自面板点击的日期?
diff --git a/components/collapse/CollapsePanel.tsx b/components/collapse/CollapsePanel.tsx
index 4ff984dd7..95af0ec64 100644
--- a/components/collapse/CollapsePanel.tsx
+++ b/components/collapse/CollapsePanel.tsx
@@ -2,8 +2,7 @@ import PanelContent from './PanelContent';
import { initDefaultProps } from '../_util/props-util';
import { collapsePanelProps } from './commonProps';
import type { ExtractPropTypes } from 'vue';
-import { defineComponent } from 'vue';
-import Transition from '../_util/transition';
+import { defineComponent, Transition } from 'vue';
import classNames from '../_util/classNames';
import devWarning from '../vc-util/devWarning';
import useConfigInject from '../config-provider/hooks/useConfigInject';
diff --git a/components/collapse/index.en-US.md b/components/collapse/index.en-US.md
index 4a80753b0..429a95e43 100644
--- a/components/collapse/index.en-US.md
+++ b/components/collapse/index.en-US.md
@@ -20,7 +20,7 @@ A content area which can be collapsed and expanded.
| Property | Description | Type | Default | Version |
| --- | --- | --- | --- | --- |
| accordion | If `true`, `Collapse` renders as `Accordion` | boolean | `false` | |
-| activeKey(v-model) | Key of the active panel | string\[] \| string
number\[] \| number | No default value. In `accordion` mode, it's the key of the first panel. | |
+| activeKey(v-model) | Key of the active panel | string\[] \| string
number\[] \| number | No default value. In [accordion mode](#components-collapse-demo-accordion), it's the key of the first panel. | |
| bordered | Toggles rendering of the border around the collapse block | boolean | `true` | |
| collapsible | Specify whether the panels of children be collapsible or the trigger area of collapsible | `header` \| `icon` \| `disabled` | - | 4.0 |
| destroyInactivePanel | Destroy Inactive Panel | boolean | `false` | |
diff --git a/components/collapse/index.zh-CN.md b/components/collapse/index.zh-CN.md
index 1c750c85f..ddae33295 100644
--- a/components/collapse/index.zh-CN.md
+++ b/components/collapse/index.zh-CN.md
@@ -20,8 +20,8 @@ coverDark: https://mdn.alipayobjects.com/huamei_7uahnr/afts/img/A*sir-TK0HkWcAAA
| 参数 | 说明 | 类型 | 默认值 | 版本 |
| --- | --- | --- | --- | --- |
-| accordion | 手风琴模式 | boolean | `false` | |
-| activeKey(v-model) | 当前激活 tab 面板的 key | string\[] \| string
number\[] \| number | 默认无,accordion 模式下默认第一个元素 | |
+| accordion | 手风琴模式,始终只有一个面板处在激活状态 | boolean | `false` | |
+| activeKey(v-model) | 当前激活 tab 面板的 key | string\[] \| string
number\[] \| number | 默认无,[手风琴模式](#components-collapse-demo-accordion)下默认第一个元素 | |
| bordered | 带边框风格的折叠面板 | boolean | `true` | |
| collapsible | 所有子面板是否可折叠或指定可折叠触发区域 | `header` \| `icon` \| `disabled` | - | 4.0 |
| destroyInactivePanel | 销毁折叠隐藏的面板 | boolean | `false` | |
@@ -42,6 +42,6 @@ coverDark: https://mdn.alipayobjects.com/huamei_7uahnr/afts/img/A*sir-TK0HkWcAAA
| collapsible | 是否可折叠或指定可折叠触发区域 | `header` \| `disabled` | - | 3.0 |
| extra | 自定义渲染每个面板右上角的内容 | VNode \| slot | - | 1.5.0 |
| forceRender | 被隐藏时是否渲染 DOM 结构 | boolean | false | |
-| header | 面板头内容 | string\|slot | 无 | |
-| key | 对应 activeKey | string \| number | 无 | |
+| header | 面板头内容 | string\|slot | - | |
+| key | 对应 activeKey | string \| number | - | |
| showArrow | 是否展示当前面板上的箭头 | boolean | `true` | |
diff --git a/components/config-provider/index.tsx b/components/config-provider/index.tsx
index 68198c2e7..223260119 100644
--- a/components/config-provider/index.tsx
+++ b/components/config-provider/index.tsx
@@ -1,4 +1,4 @@
-import type { App, Plugin, WatchStopHandle } from 'vue';
+import type { App, MaybeRef, Plugin, WatchStopHandle } from 'vue';
import { watch, computed, reactive, defineComponent, watchEffect } from 'vue';
import defaultRenderEmpty from './renderEmpty';
import type { RenderEmptyHandler } from './renderEmpty';
@@ -7,7 +7,6 @@ import LocaleProvider, { ANT_MARK } from '../locale-provider';
import LocaleReceiver from '../locale-provider/LocaleReceiver';
-import type { MaybeRef } from '../_util/type';
import message from '../message';
import notification from '../notification';
import { registerTheme } from './cssVariables';
diff --git a/components/date-picker/index.en-US.md b/components/date-picker/index.en-US.md
index dd071b05a..2b84c13cd 100644
--- a/components/date-picker/index.en-US.md
+++ b/components/date-picker/index.en-US.md
@@ -85,7 +85,7 @@ The following APIs are shared by DatePicker, RangePicker.
| disabled | Determine whether the DatePicker is disabled | boolean | false | |
| disabledDate | Specify the date that cannot be selected | (currentDate: dayjs) => boolean | - | |
| format | To set the date format, refer to [dayjs](https://day.js.org/). When an array is provided, all values are used for parsing and first value is used for formatting, support [Custom Format](#components-date-picker-demo-format) | [formatType](#formattype) | `YYYY-MM-DD` | |
-| dropdownClassName | To customize the className of the popup calendar | string | - | |
+| popupClassName | To customize the className of the popup calendar | string | - | |
| getPopupContainer | To set the container of the floating layer, while the default is to create a `div` element in `body` | function(trigger) | - | |
| inputReadOnly | Set the `readonly` attribute of the input tag (avoids virtual keyboard on touch devices) | boolean | false | |
| locale | Localization configuration | object | [default](https://github.com/vueComponent/ant-design-vue/blob/main/components/date-picker/locale/example.json) | |
diff --git a/components/date-picker/index.zh-CN.md b/components/date-picker/index.zh-CN.md
index 8aa5fdcbd..f878261bb 100644
--- a/components/date-picker/index.zh-CN.md
+++ b/components/date-picker/index.zh-CN.md
@@ -86,7 +86,7 @@ coverDark: https://mdn.alipayobjects.com/huamei_7uahnr/afts/img/A*3OpRQKcygo8AAA
| disabled | 禁用 | boolean | false | |
| disabledDate | 不可选择的日期 | (currentDate: dayjs) => boolean | - | |
| format | 设置日期格式,为数组时支持多格式匹配,展示以第一个为准。配置参考 [dayjs](https://day.js.org/docs/zh-CN/display/format),支持[自定义格式](#components-date-picker-demo-format) | [formatType](#formattype) | `YYYY-MM-DD` | |
-| dropdownClassName | 额外的弹出日历 className | string | - | |
+| popupClassName | 额外的弹出日历 className | string | - | |
| getPopupContainer | 定义浮层的容器,默认为 body 上新建 div | function(trigger) | - | |
| inputReadOnly | 设置输入框为只读(避免在移动设备上打开虚拟键盘) | boolean | false | |
| locale | 国际化配置 | object | [默认配置](https://github.com/vueComponent/ant-design-vue/blob/main/components/date-picker/locale/example.json) | - |
diff --git a/components/dropdown/index.zh-CN.md b/components/dropdown/index.zh-CN.md
index b66ae4048..1149da0a2 100644
--- a/components/dropdown/index.zh-CN.md
+++ b/components/dropdown/index.zh-CN.md
@@ -22,7 +22,7 @@ coverDark: https://mdn.alipayobjects.com/huamei_7uahnr/afts/img/A*5qm4S4Zgh2QAAA
| 参数 | 说明 | 类型 | 默认值 | |
| --- | --- | --- | --- | --- |
-| align | 该值将合并到 placement 的配置中,设置参考 [dom-align](https://github.com/yiminghe/dom-align) | Object | 无 | |
+| align | 该值将合并到 placement 的配置中,设置参考 [dom-align](https://github.com/yiminghe/dom-align) | Object | - | |
| arrow | 下拉框箭头是否显示 | boolean \| { pointAtCenter: boolean } | false | 3.3.0 |
| destroyPopupOnHide | 关闭后是否销毁 Dropdown | boolean | false | 3.0 |
| disabled | 菜单是否禁用 | boolean | - | |
diff --git a/components/empty/index.tsx b/components/empty/index.tsx
index 5d73982bc..ab778c9e6 100644
--- a/components/empty/index.tsx
+++ b/components/empty/index.tsx
@@ -1,4 +1,4 @@
-import { defineComponent } from 'vue';
+import { defineComponent, h } from 'vue';
import type { CSSProperties, ExtractPropTypes } from 'vue';
import classNames from '../_util/classNames';
import LocaleReceiver from '../locale-provider/LocaleReceiver';
@@ -11,9 +11,6 @@ import useConfigInject from '../config-provider/hooks/useConfigInject';
import useStyle from './style';
-const defaultEmptyImg = ;
-const simpleEmptyImg = ;
-
interface Locale {
description?: string;
}
@@ -40,13 +37,16 @@ const Empty = defineComponent({
return () => {
const prefixCls = prefixClsRef.value;
const {
- image = slots.image?.() || defaultEmptyImg,
+ image: mergedImage = slots.image?.() || h(DefaultEmptyImg),
description = slots.description?.() || undefined,
imageStyle,
class: className = '',
...restProps
} = { ...props, ...attrs };
-
+ const image =
+ typeof mergedImage === 'function' ? (mergedImage as () => VueNode)() : mergedImage;
+ const isNormal =
+ typeof image === 'object' && 'type' in image && (image.type as any).PRESENTED_IMAGE_SIMPLE;
return wrapSSR(
h(DefaultEmptyImg);
+Empty.PRESENTED_IMAGE_SIMPLE = () => h(SimpleEmptyImg);
export default withInstall(Empty);
diff --git a/components/float-button/BackTop.tsx b/components/float-button/BackTop.tsx
index 6baa6daad..722905975 100644
--- a/components/float-button/BackTop.tsx
+++ b/components/float-button/BackTop.tsx
@@ -1,5 +1,5 @@
import VerticalAlignTopOutlined from '@ant-design/icons-vue/VerticalAlignTopOutlined';
-import { getTransitionProps, Transition } from '../_util/transition';
+import { getTransitionProps } from '../_util/transition';
import {
defineComponent,
nextTick,
@@ -10,6 +10,7 @@ import {
ref,
watch,
onDeactivated,
+ Transition,
} from 'vue';
import FloatButton, { floatButtonPrefixCls } from './FloatButton';
import useConfigInject from '../config-provider/hooks/useConfigInject';
diff --git a/components/float-button/FloatButtonGroup.tsx b/components/float-button/FloatButtonGroup.tsx
index 743d60e5a..611646299 100644
--- a/components/float-button/FloatButtonGroup.tsx
+++ b/components/float-button/FloatButtonGroup.tsx
@@ -1,8 +1,8 @@
-import { defineComponent, ref, computed, watch, onBeforeUnmount } from 'vue';
+import { defineComponent, ref, computed, watch, onBeforeUnmount, Transition } from 'vue';
import CloseOutlined from '@ant-design/icons-vue/CloseOutlined';
import FileTextOutlined from '@ant-design/icons-vue/FileTextOutlined';
import classNames from '../_util/classNames';
-import { getTransitionProps, Transition } from '../_util/transition';
+import { getTransitionProps } from '../_util/transition';
import FloatButton, { floatButtonPrefixCls } from './FloatButton';
import useConfigInject from '../config-provider/hooks/useConfigInject';
import { useProvideFloatButtonGroupContext } from './context';
diff --git a/components/float-button/index.zh-CN.md b/components/float-button/index.zh-CN.md
index 589c65865..284cd6842 100644
--- a/components/float-button/index.zh-CN.md
+++ b/components/float-button/index.zh-CN.md
@@ -33,11 +33,11 @@ tag: New
| target | 相当于 a 标签的 target 属性,href 存在时生效 | string | - | |
| badge | 带徽标数字的悬浮按钮(不支持 status 以及相关属性) | [BadgeProps](/components/badge-cn#api) | - | |
-### common events
+### 共同的事件
-| 事件名称 | 说明 | 回调参数 | 版本 |
-| -------- | --------------------------------------- | ----------------- | ---- |
-| click | Set the handler to handle `click` event | `(event) => void` | - |
+| 事件名称 | 说明 | 回调参数 | 版本 |
+| -------- | ----------------------------- | ----------------- | ---- |
+| click | 设置处理 `click` 事件的处理器 | `(event) => void` | - |
### FloatButton.Group
@@ -47,7 +47,7 @@ tag: New
| trigger | 触发方式(有触发方式为菜单模式) | `click` \| `hover` | - | |
| open(v-model) | 受控展开 | boolean | - | |
-### FloatButton.Group Events
+### FloatButton.Group 事件
| 事件名称 | 说明 | 回调参数 | 版本 |
| ---------- | ---------------- | ----------------------- | ---- |
diff --git a/components/float-button/interface.ts b/components/float-button/interface.ts
index 6561910b8..9db3fba65 100644
--- a/components/float-button/interface.ts
+++ b/components/float-button/interface.ts
@@ -20,7 +20,7 @@ export const floatButtonProps = () => {
shape: stringType('circle'),
tooltip: PropTypes.any,
href: String,
- target: functionType<() => Window | HTMLElement | null>(),
+ target: String,
badge: objectType(),
onClick: functionType(),
};
diff --git a/components/form/FormItem.tsx b/components/form/FormItem.tsx
index df70f8e6f..6a0608de4 100644
--- a/components/form/FormItem.tsx
+++ b/components/form/FormItem.tsx
@@ -494,7 +494,7 @@ export default defineComponent({
>
(
diff --git a/components/grid/style/index.ts b/components/grid/style/index.ts
index 895b5b01c..d9df3e2fa 100644
--- a/components/grid/style/index.ts
+++ b/components/grid/style/index.ts
@@ -52,6 +52,10 @@ const genGridRowStyle: GenerateStyle = (token): CSSObject => {
justifyContent: 'space-around',
},
+ '&-space-evenly ': {
+ justifyContent: 'space-evenly',
+ },
+
// Align at the top
'&-top': {
alignItems: 'flex-start',
diff --git a/components/icon/index.en-US.md b/components/icon/index.en-US.md
index 758950f77..7d753e2c3 100644
--- a/components/icon/index.en-US.md
+++ b/components/icon/index.en-US.md
@@ -122,6 +122,8 @@ See [iconfont.cn documents](http://iconfont.cn/help/detail?spm=a313x.7781069.199
### Custom SVG Icon
+#### vue cli 3
+
You can import SVG icon as an vue component by using `vue cli 3` and [`vue-svg-loader`](https://www.npmjs.com/package/vue-svg-loader). `vue-svg-loader`'s `options` [reference](https://github.com/visualfanatic/vue-svg-loader).
```js
@@ -149,6 +151,84 @@ export default defineComponent({
});
```
+#### Rsbuild
+
+Rsbuild is a new generation of build tool, official website https://rsbuild.dev/
+Create your own `vue-svg-loader.js` file, which allows you to customize and beautify SVG, and then configure it in `rsbuild.config.ts`
+
+```js
+// vue-svg-loader.js
+/* eslint-disable */
+const { optimize } = require('svgo');
+const { version } = require('vue');
+const semverMajor = require('semver/functions/major');
+
+module.exports = async function (svg) {
+ const callback = this.async();
+
+ try {
+ ({ data: svg } = await optimize(svg, {
+ path: this.resourcePath,
+ js2svg: {
+ indent: 2,
+ pretty: true,
+ },
+ plugins: [
+ 'convertStyleToAttrs',
+ 'removeDoctype',
+ 'removeXMLProcInst',
+ 'removeComments',
+ 'removeMetadata',
+ 'removeTitle',
+ 'removeDesc',
+ 'removeStyleElement',
+ 'removeXMLNS',
+ 'removeXMLProcInst',
+ ],
+ }));
+ } catch (error) {
+ callback(error);
+ return;
+ }
+
+ if (semverMajor(version) === 2) {
+ svg = svg.replace('
diff --git a/components/input-number/style/index.tsx b/components/input-number/style/index.tsx
index d119e7d66..e363f0772 100644
--- a/components/input-number/style/index.tsx
+++ b/components/input-number/style/index.tsx
@@ -263,6 +263,10 @@ const genInputNumberStyles: GenerateStyle