|
1 | 1 | import type { ExtractPropTypes, PropType } from 'vue';
|
2 |
| -import { defineComponent } from 'vue'; |
3 |
| -import PropTypes from '../_util/vue-types'; |
| 2 | +import { onMounted, ref, defineComponent, onBeforeUnmount } from 'vue'; |
4 | 3 | import Button from '../button';
|
5 |
| -import BaseMixin from '../_util/BaseMixin'; |
| 4 | +import type { ButtonProps } from '../button'; |
6 | 5 | import type { LegacyButtonType } from '../button/buttonTypes';
|
7 | 6 | import { convertLegacyProps } from '../button/buttonTypes';
|
8 |
| -import { getSlot, findDOMNode } from '../_util/props-util'; |
9 | 7 |
|
10 |
| -const ActionButtonProps = { |
| 8 | +const actionButtonProps = { |
11 | 9 | type: {
|
12 | 10 | type: String as PropType<LegacyButtonType>,
|
13 | 11 | },
|
14 |
| - actionFn: PropTypes.func, |
15 |
| - closeModal: PropTypes.func, |
16 |
| - autofocus: PropTypes.looseBool, |
17 |
| - buttonProps: PropTypes.object, |
| 12 | + actionFn: Function as PropType<(...args: any[]) => any | PromiseLike<any>>, |
| 13 | + close: Function, |
| 14 | + autofocus: Boolean, |
| 15 | + prefixCls: String, |
| 16 | + buttonProps: Object as PropType<ButtonProps>, |
| 17 | + emitEvent: Boolean, |
| 18 | + quitOnNullishReturnValue: Boolean, |
18 | 19 | };
|
19 | 20 |
|
20 |
| -export type IActionButtonProps = ExtractPropTypes<typeof ActionButtonProps>; |
| 21 | +export type ActionButtonProps = ExtractPropTypes<typeof actionButtonProps>; |
| 22 | + |
| 23 | +function isThenable(thing?: PromiseLike<any>): boolean { |
| 24 | + return !!(thing && !!thing.then); |
| 25 | +} |
21 | 26 |
|
22 | 27 | export default defineComponent({
|
23 |
| - mixins: [BaseMixin], |
24 |
| - props: ActionButtonProps, |
25 |
| - setup() { |
26 |
| - return { |
27 |
| - timeoutId: undefined, |
28 |
| - }; |
29 |
| - }, |
30 |
| - data() { |
31 |
| - return { |
32 |
| - loading: false, |
| 28 | + name: 'ActionButton', |
| 29 | + props: actionButtonProps, |
| 30 | + setup(props, { slots }) { |
| 31 | + const clickedRef = ref<boolean>(false); |
| 32 | + const buttonRef = ref(); |
| 33 | + const loading = ref(false); |
| 34 | + let timeoutId: any; |
| 35 | + onMounted(() => { |
| 36 | + if (props.autofocus) { |
| 37 | + timeoutId = setTimeout(() => buttonRef.value.$el?.focus()); |
| 38 | + } |
| 39 | + }); |
| 40 | + onBeforeUnmount(() => { |
| 41 | + clearTimeout(timeoutId); |
| 42 | + }); |
| 43 | + |
| 44 | + const handlePromiseOnOk = (returnValueOfOnOk?: PromiseLike<any>) => { |
| 45 | + const { close } = props; |
| 46 | + if (!isThenable(returnValueOfOnOk)) { |
| 47 | + return; |
| 48 | + } |
| 49 | + loading.value = true; |
| 50 | + returnValueOfOnOk!.then( |
| 51 | + (...args: any[]) => { |
| 52 | + loading.value = false; |
| 53 | + close(...args); |
| 54 | + clickedRef.value = false; |
| 55 | + }, |
| 56 | + (e: Error) => { |
| 57 | + // Emit error when catch promise reject |
| 58 | + // eslint-disable-next-line no-console |
| 59 | + console.error(e); |
| 60 | + // See: https://github.com/ant-design/ant-design/issues/6183 |
| 61 | + loading.value = false; |
| 62 | + clickedRef.value = false; |
| 63 | + }, |
| 64 | + ); |
33 | 65 | };
|
34 |
| - }, |
35 |
| - mounted() { |
36 |
| - if (this.autofocus) { |
37 |
| - this.timeoutId = setTimeout(() => findDOMNode(this).focus()); |
38 |
| - } |
39 |
| - }, |
40 |
| - beforeUnmount() { |
41 |
| - clearTimeout(this.timeoutId); |
42 |
| - }, |
43 |
| - methods: { |
44 |
| - onClick() { |
45 |
| - const { actionFn, closeModal } = this; |
46 |
| - if (actionFn) { |
47 |
| - let ret: any; |
48 |
| - if (actionFn.length) { |
49 |
| - ret = actionFn(closeModal); |
50 |
| - } else { |
51 |
| - ret = actionFn(); |
52 |
| - if (!ret) { |
53 |
| - closeModal(); |
54 |
| - } |
55 |
| - } |
56 |
| - if (ret && ret.then) { |
57 |
| - this.setState({ loading: true }); |
58 |
| - ret.then( |
59 |
| - (...args: any[]) => { |
60 |
| - // It's unnecessary to set loading=false, for the Modal will be unmounted after close. |
61 |
| - // this.setState({ loading: false }); |
62 |
| - closeModal(...args); |
63 |
| - }, |
64 |
| - (e: Event) => { |
65 |
| - // Emit error when catch promise reject |
66 |
| - // eslint-disable-next-line no-console |
67 |
| - console.error(e); |
68 |
| - // See: https://github.com/ant-design/ant-design/issues/6183 |
69 |
| - this.setState({ loading: false }); |
70 |
| - }, |
71 |
| - ); |
| 66 | + |
| 67 | + const onClick = (e: MouseEvent) => { |
| 68 | + const { actionFn, close = () => {} } = props; |
| 69 | + if (clickedRef.value) { |
| 70 | + return; |
| 71 | + } |
| 72 | + clickedRef.value = true; |
| 73 | + if (!actionFn) { |
| 74 | + close(); |
| 75 | + return; |
| 76 | + } |
| 77 | + let returnValueOfOnOk; |
| 78 | + if (props.emitEvent) { |
| 79 | + returnValueOfOnOk = actionFn(e); |
| 80 | + if (props.quitOnNullishReturnValue && !isThenable(returnValueOfOnOk)) { |
| 81 | + clickedRef.value = false; |
| 82 | + close(e); |
| 83 | + return; |
72 | 84 | }
|
| 85 | + } else if (actionFn.length) { |
| 86 | + returnValueOfOnOk = actionFn(close); |
| 87 | + // https://github.com/ant-design/ant-design/issues/23358 |
| 88 | + clickedRef.value = false; |
73 | 89 | } else {
|
74 |
| - closeModal(); |
| 90 | + returnValueOfOnOk = actionFn(); |
| 91 | + if (!returnValueOfOnOk) { |
| 92 | + close(); |
| 93 | + return; |
| 94 | + } |
75 | 95 | }
|
76 |
| - }, |
77 |
| - }, |
78 |
| - |
79 |
| - render() { |
80 |
| - const { type, loading, buttonProps } = this; |
81 |
| - const props = { |
82 |
| - ...convertLegacyProps(type), |
83 |
| - onClick: this.onClick, |
84 |
| - loading, |
85 |
| - ...buttonProps, |
| 96 | + handlePromiseOnOk(returnValueOfOnOk); |
| 97 | + }; |
| 98 | + return () => { |
| 99 | + const { type, prefixCls, buttonProps } = props; |
| 100 | + return ( |
| 101 | + <Button |
| 102 | + {...convertLegacyProps(type)} |
| 103 | + onClick={onClick} |
| 104 | + loading={loading.value} |
| 105 | + prefixCls={prefixCls} |
| 106 | + {...buttonProps} |
| 107 | + ref={buttonRef} |
| 108 | + v-slots={slots} |
| 109 | + ></Button> |
| 110 | + ); |
86 | 111 | };
|
87 |
| - return <Button {...props}>{getSlot(this)}</Button>; |
88 | 112 | },
|
89 | 113 | });
|
0 commit comments