diff --git a/components/button/button-group.tsx b/components/button/button-group.tsx
index f6a897d6d..66494438d 100644
--- a/components/button/button-group.tsx
+++ b/components/button/button-group.tsx
@@ -1,4 +1,4 @@
-import { defineComponent } from 'vue';
+import { computed, defineComponent } from 'vue';
import { flattenChildren } from '../_util/props-util';
import PropTypes from '../_util/vue-types';
import useConfigInject from '../_util/hooks/useConfigInject';
@@ -21,10 +21,8 @@ export default defineComponent({
props: buttonGroupProps,
setup(props, { slots }) {
const { prefixCls, direction } = useConfigInject('btn-group', props);
-
- return () => {
+ const classes = computed(() => {
const { size } = props;
-
// large => lg
// small => sm
let sizeCls = '';
@@ -38,12 +36,14 @@ export default defineComponent({
default:
break;
}
- const classes = {
+ return {
[`${prefixCls.value}`]: true,
[`${prefixCls.value}-${sizeCls}`]: sizeCls,
[`${prefixCls.value}-rtl`]: direction.value === 'rtl',
};
- return
{flattenChildren(slots.default?.())}
;
+ });
+ return () => {
+ return {flattenChildren(slots.default?.())}
;
};
},
});
diff --git a/components/button/button.tsx b/components/button/button.tsx
index 7c39538eb..8929d7ace 100644
--- a/components/button/button.tsx
+++ b/components/button/button.tsx
@@ -4,6 +4,7 @@ import {
onBeforeUnmount,
onMounted,
onUpdated,
+ Ref,
ref,
Text,
watch,
@@ -19,6 +20,8 @@ import devWarning from '../vc-util/devWarning';
import type { ButtonType } from './buttonTypes';
import type { VNode } from 'vue';
+type Loading = boolean | number;
+
const rxTwoCNChar = /^[\u4e00-\u9fa5]{2}$/;
const isTwoCNChar = rxTwoCNChar.test.bind(rxTwoCNChar);
const props = buttonTypes();
@@ -38,18 +41,41 @@ export default defineComponent({
const { prefixCls, autoInsertSpaceInButton, direction } = useConfigInject('btn', props);
const buttonNodeRef = ref(null);
- const delayTimeout = ref(undefined);
- const iconCom = ref(null);
- const children = ref([]);
+ const delayTimeoutRef = ref(undefined);
+ let isNeedInserted = false;
- const sLoading = ref(props.loading);
+ const innerLoading: Ref = ref(false);
const hasTwoCNChar = ref(false);
const autoInsertSpace = computed(() => autoInsertSpaceInButton.value !== false);
- const getClasses = () => {
- const { type, shape, size, ghost, block, danger } = props;
+ // =============== Update Loading ===============
+ const loadingOrDelay = computed(() =>
+ typeof props.loading === 'object' && props.loading.delay
+ ? props.loading.delay || true
+ : !!props.loading,
+ );
+ watch(
+ loadingOrDelay,
+ (val) => {
+ clearTimeout(delayTimeoutRef.value);
+ if (typeof loadingOrDelay.value === 'number') {
+ delayTimeoutRef.value = window.setTimeout(() => {
+ innerLoading.value = val;
+ }, loadingOrDelay.value);
+ } else {
+ innerLoading.value = val;
+ }
+ },
+ {
+ immediate: true,
+ },
+ );
+
+ const classes = computed(() => {
+ const { type, shape, size, ghost, block, danger } = props;
+ const pre = prefixCls.value;
// large => lg
// small => sm
let sizeCls = '';
@@ -63,22 +89,20 @@ export default defineComponent({
default:
break;
}
- const iconType = sLoading.value ? 'loading' : iconCom.value;
return {
[attrs.class as string]: attrs.class,
- [`${prefixCls.value}`]: true,
- [`${prefixCls.value}-${type}`]: type,
- [`${prefixCls.value}-${shape}`]: shape,
- [`${prefixCls.value}-${sizeCls}`]: sizeCls,
- [`${prefixCls.value}-icon-only`]: children.value.length === 0 && !!iconType,
- [`${prefixCls.value}-loading`]: sLoading.value,
- [`${prefixCls.value}-background-ghost`]: ghost && !isUnborderedButtonType(type),
- [`${prefixCls.value}-two-chinese-chars`]: hasTwoCNChar.value && autoInsertSpace.value,
- [`${prefixCls.value}-block`]: block,
- [`${prefixCls.value}-dangerous`]: !!danger,
- [`${prefixCls.value}-rtl`]: direction.value === 'rtl',
+ [`${pre}`]: true,
+ [`${pre}-${type}`]: type,
+ [`${pre}-${shape}`]: shape,
+ [`${pre}-${sizeCls}`]: sizeCls,
+ [`${pre}-loading`]: innerLoading.value,
+ [`${pre}-background-ghost`]: ghost && !isUnborderedButtonType(type),
+ [`${pre}-two-chinese-chars`]: hasTwoCNChar.value && autoInsertSpace.value,
+ [`${pre}-block`]: block,
+ [`${pre}-dangerous`]: !!danger,
+ [`${pre}-rtl`]: direction.value === 'rtl',
};
- };
+ });
const fixTwoCNChar = () => {
// Fix for HOC usage like
@@ -88,7 +112,7 @@ export default defineComponent({
}
const buttonText = node.textContent;
- if (isNeedInserted() && isTwoCNChar(buttonText)) {
+ if (isNeedInserted && isTwoCNChar(buttonText)) {
if (!hasTwoCNChar.value) {
hasTwoCNChar.value = true;
}
@@ -98,7 +122,7 @@ export default defineComponent({
};
const handleClick = (event: Event) => {
// https://github.com/ant-design/ant-design/issues/30207
- if (sLoading.value || props.disabled) {
+ if (innerLoading.value || props.disabled) {
event.preventDefault();
return;
}
@@ -117,9 +141,6 @@ export default defineComponent({
return child;
};
- const isNeedInserted = () =>
- children.value.length === 1 && !iconCom.value && !isUnborderedButtonType(props.type);
-
watchEffect(() => {
devWarning(
!(props.ghost && isUnborderedButtonType(props.type)),
@@ -128,50 +149,45 @@ export default defineComponent({
);
});
- watch(
- () => props.loading,
- (val, preVal) => {
- if (preVal && typeof preVal !== 'boolean') {
- clearTimeout(delayTimeout.value);
- }
- if (val && typeof val !== 'boolean' && val.delay) {
- delayTimeout.value = setTimeout(() => {
- sLoading.value = !!val;
- }, val.delay);
- } else {
- sLoading.value = !!val;
- }
- },
- {
- immediate: true,
- },
- );
-
onMounted(fixTwoCNChar);
onUpdated(fixTwoCNChar);
onBeforeUnmount(() => {
- delayTimeout.value && clearTimeout(delayTimeout.value);
+ delayTimeoutRef.value && clearTimeout(delayTimeoutRef.value);
});
return () => {
- iconCom.value = getPropsSlot(slots, props, 'icon');
- children.value = flattenChildren(getPropsSlot(slots, props));
+ const children = flattenChildren(getPropsSlot(slots, props));
+
+ const icon = getPropsSlot(slots, props, 'icon');
+
+ isNeedInserted = children.length === 1 && !icon && !isUnborderedButtonType(props.type);
const { type, htmlType, disabled, href, title, target } = props;
- const classes = getClasses();
+ const iconType = innerLoading.value ? 'loading' : icon;
const buttonProps = {
...attrs,
title,
disabled,
- class: classes,
+ class: [
+ classes.value,
+ attrs.class,
+ { [`${prefixCls.value}-icon-only`]: children.length === 0 && !!iconType },
+ ],
onClick: handleClick,
};
- const iconNode = sLoading.value ? : iconCom.value;
- const kids = children.value.map((child) =>
- insertSpace(child, isNeedInserted() && autoInsertSpace.value),
+ const iconNode = innerLoading.value ? (
+
+
+
+ ) : (
+ icon
+ );
+
+ const kids = children.map((child) =>
+ insertSpace(child, isNeedInserted && autoInsertSpace.value),
);
if (href !== undefined) {
diff --git a/components/button/buttonTypes.ts b/components/button/buttonTypes.ts
index e71a0b696..87b1f498d 100644
--- a/components/button/buttonTypes.ts
+++ b/components/button/buttonTypes.ts
@@ -6,7 +6,7 @@ import type { SizeType } from '../config-provider';
const ButtonTypes = tuple('default', 'primary', 'ghost', 'dashed', 'link', 'text');
export type ButtonType = typeof ButtonTypes[number];
-const ButtonShapes = tuple('circle', 'circle-outline', 'round');
+const ButtonShapes = tuple('circle', 'round');
export type ButtonShape = typeof ButtonShapes[number];
const ButtonHTMLTypes = tuple('submit', 'button', 'reset');
diff --git a/components/button/index.ts b/components/button/index.ts
index 4b9364911..b5136a9e2 100644
--- a/components/button/index.ts
+++ b/components/button/index.ts
@@ -17,6 +17,8 @@ Button.install = function (app: App) {
return app;
};
+export { ButtonGroup };
+
export default Button as typeof Button &
Plugin & {
readonly Group: typeof ButtonGroup;
diff --git a/components/button/style/index.less b/components/button/style/index.less
index 97fbeb2ed..2ea7e01a3 100644
--- a/components/button/style/index.less
+++ b/components/button/style/index.less
@@ -155,38 +155,24 @@
}
}
- &&-loading:not(&-circle):not(&-circle-outline):not(&-icon-only) {
- padding-left: 29px;
- .@{iconfont-css-prefix}:not(:last-child) {
- margin-left: -14px;
- }
- }
+ & > &-loading-icon {
+ transition: all 0.3s @ease-in-out;
- &-sm&-loading:not(&-circle):not(&-circle-outline):not(&-icon-only) {
- padding-left: 24px;
.@{iconfont-css-prefix} {
- margin-left: -17px;
+ padding-right: @padding-xs;
+ animation: none;
+ // for smooth button padding transition
+ svg {
+ animation: loadingCircle 1s infinite linear;
+ }
}
- }
- // & > &-loading-icon {
- // transition: all 0.3s @ease-in-out;
-
- // .@{iconfont-css-prefix} {
- // padding-right: @padding-xs;
- // animation: none;
- // // for smooth button padding transition
- // svg {
- // animation: loadingCircle 1s infinite linear;
- // }
- // }
-
- // &:only-child {
- // .@{iconfont-css-prefix} {
- // padding-right: 0;
- // }
- // }
- // }
+ &:only-child {
+ .@{iconfont-css-prefix} {
+ padding-right: 0;
+ }
+ }
+ }
&-group {
.btn-group(@btn-prefix-cls);
diff --git a/components/modal/Modal.tsx b/components/modal/Modal.tsx
index c0995dac0..66d06a74a 100644
--- a/components/modal/Modal.tsx
+++ b/components/modal/Modal.tsx
@@ -136,7 +136,9 @@ export interface ModalFuncProps {
type getContainerFunc = () => HTMLElement;
-export type ModalFunc = (props: ModalFuncProps) => {
+export type ModalFunc = (
+ props: ModalFuncProps,
+) => {
destroy: () => void;
update: (newConfig: ModalFuncProps) => void;
};
diff --git a/components/popconfirm/index.tsx b/components/popconfirm/index.tsx
index 5521f9038..f4d739f3c 100644
--- a/components/popconfirm/index.tsx
+++ b/components/popconfirm/index.tsx
@@ -139,7 +139,7 @@ const Popconfirm = defineComponent({
this.renderOverlay(prefixCls, popconfirmLocale)}
+ children={popconfirmLocale => this.renderOverlay(prefixCls, popconfirmLocale)}
/>
);
const tooltipProps = {
diff --git a/v2-doc b/v2-doc
index 4c2982755..b6ab0fec2 160000
--- a/v2-doc
+++ b/v2-doc
@@ -1 +1 @@
-Subproject commit 4c298275518d5790a58d26f2ed9b83ee5ba1dba4
+Subproject commit b6ab0fec2cfa378bab8dfe6c8ef6b6a8664b970e