diff --git a/components/_util/ActionButton.tsx b/components/_util/ActionButton.tsx index 19b1365ae..65cfd8e12 100644 --- a/components/_util/ActionButton.tsx +++ b/components/_util/ActionButton.tsx @@ -5,6 +5,7 @@ import type { ButtonProps } from '../button'; import type { LegacyButtonType } from '../button/buttonTypes'; import { convertLegacyProps } from '../button/buttonTypes'; import useDestroyed from './hooks/useDestroyed'; +import { objectType } from './type'; const actionButtonProps = { type: { @@ -14,15 +15,15 @@ const actionButtonProps = { close: Function, autofocus: Boolean, prefixCls: String, - buttonProps: Object as PropType, + buttonProps: objectType(), emitEvent: Boolean, quitOnNullishReturnValue: Boolean, }; export type ActionButtonProps = ExtractPropTypes; -function isThenable(thing?: PromiseLike): boolean { - return !!(thing && !!thing.then); +function isThenable(thing?: PromiseLike): boolean { + return !!(thing && thing.then); } export default defineComponent({ @@ -44,8 +45,11 @@ export default defineComponent({ clearTimeout(timeoutId); }); + const onInternalClose = (...args: any[]) => { + props.close?.(...args); + }; + const handlePromiseOnOk = (returnValueOfOnOk?: PromiseLike) => { - const { close } = props; if (!isThenable(returnValueOfOnOk)) { return; } @@ -55,48 +59,46 @@ export default defineComponent({ if (!isDestroyed.value) { loading.value = false; } - close(...args); + onInternalClose(...args); clickedRef.value = false; }, (e: Error) => { - // Emit error when catch promise reject - // eslint-disable-next-line no-console - console.error(e); // See: https://github.com/ant-design/ant-design/issues/6183 if (!isDestroyed.value) { loading.value = false; } clickedRef.value = false; + return Promise.reject(e); }, ); }; const onClick = (e: MouseEvent) => { - const { actionFn, close = () => {} } = props; + const { actionFn } = props; if (clickedRef.value) { return; } clickedRef.value = true; if (!actionFn) { - close(); + onInternalClose(); return; } - let returnValueOfOnOk; + let returnValueOfOnOk: PromiseLike; if (props.emitEvent) { returnValueOfOnOk = actionFn(e); if (props.quitOnNullishReturnValue && !isThenable(returnValueOfOnOk)) { clickedRef.value = false; - close(e); + onInternalClose(e); return; } } else if (actionFn.length) { - returnValueOfOnOk = actionFn(close); + returnValueOfOnOk = actionFn(props.close); // https://github.com/ant-design/ant-design/issues/23358 clickedRef.value = false; } else { returnValueOfOnOk = actionFn(); if (!returnValueOfOnOk) { - close(); + onInternalClose(); return; } } diff --git a/components/_util/getScroll.ts b/components/_util/getScroll.ts index 70b50141d..2889b43ed 100644 --- a/components/_util/getScroll.ts +++ b/components/_util/getScroll.ts @@ -1,4 +1,4 @@ -export function isWindow(obj: any) { +export function isWindow(obj: any): obj is Window { return obj !== null && obj !== undefined && obj === obj.window; } @@ -12,16 +12,22 @@ export default function getScroll( const method = top ? 'scrollTop' : 'scrollLeft'; let result = 0; if (isWindow(target)) { - result = (target as Window)[top ? 'pageYOffset' : 'pageXOffset']; + result = target[top ? 'pageYOffset' : 'pageXOffset']; } else if (target instanceof Document) { result = target.documentElement[method]; + } else if (target instanceof HTMLElement) { + result = target[method]; } else if (target) { - result = (target as HTMLElement)[method]; + // According to the type inference, the `target` is `never` type. + // Since we configured the loose mode type checking, and supports mocking the target with such shape below:: + // `{ documentElement: { scrollLeft: 200, scrollTop: 400 } }`, + // the program may falls into this branch. + // Check the corresponding tests for details. Don't sure what is the real scenario this happens. + result = target[method]; } + if (target && !isWindow(target) && typeof result !== 'number') { - result = ((target as HTMLElement).ownerDocument || (target as Document)).documentElement?.[ - method - ]; + result = ((target.ownerDocument ?? target) as any).documentElement?.[method]; } return result; } diff --git a/components/_util/scrollTo.ts b/components/_util/scrollTo.ts index f41c1b649..c9dbb8910 100644 --- a/components/_util/scrollTo.ts +++ b/components/_util/scrollTo.ts @@ -1,6 +1,6 @@ import raf from './raf'; -import getScroll, { isWindow } from './getScroll'; import { easeInOutCubic } from './easings'; +import getScroll, { isWindow } from './getScroll'; interface ScrollToOptions { /** Scroll container, default as window */ @@ -23,8 +23,8 @@ export default function scrollTo(y: number, options: ScrollToOptions = {}) { const nextScrollTop = easeInOutCubic(time > duration ? duration : time, scrollTop, y, duration); if (isWindow(container)) { (container as Window).scrollTo(window.pageXOffset, nextScrollTop); - } else if (container instanceof HTMLDocument || container.constructor.name === 'HTMLDocument') { - (container as HTMLDocument).documentElement.scrollTop = nextScrollTop; + } else if (container instanceof Document || container.constructor.name === 'HTMLDocument') { + (container as Document).documentElement.scrollTop = nextScrollTop; } else { (container as HTMLElement).scrollTop = nextScrollTop; } diff --git a/components/_util/statusUtils.tsx b/components/_util/statusUtils.tsx index b61614f7d..491d256fb 100644 --- a/components/_util/statusUtils.tsx +++ b/components/_util/statusUtils.tsx @@ -1,8 +1,8 @@ import type { ValidateStatus } from '../form/FormItem'; import classNames from './classNames'; -import { tuple } from './type'; -const InputStatuses = tuple('warning', 'error', ''); +const InputStatuses = ['warning', 'error', ''] as const; + export type InputStatus = typeof InputStatuses[number]; export function getStatusClassNames( diff --git a/components/_util/throttleByAnimationFrame.ts b/components/_util/throttleByAnimationFrame.ts index 45bae734b..ce469a3f0 100644 --- a/components/_util/throttleByAnimationFrame.ts +++ b/components/_util/throttleByAnimationFrame.ts @@ -1,6 +1,10 @@ import raf from './raf'; -export default function throttleByAnimationFrame(fn: (...args: T) => void) { +type throttledFn = (...args: any[]) => void; + +type throttledCancelFn = { cancel: () => void }; + +function throttleByAnimationFrame(fn: (...args: T) => void) { let requestId: number | null; const later = (args: T) => () => { @@ -8,10 +12,7 @@ export default function throttleByAnimationFrame(fn: (...ar fn(...args); }; - const throttled: { - (...args: T): void; - cancel: () => void; - } = (...args: T) => { + const throttled: throttledFn & throttledCancelFn = (...args: T) => { if (requestId == null) { requestId = raf(later(args)); } @@ -25,29 +26,4 @@ export default function throttleByAnimationFrame(fn: (...ar return throttled; } -export function throttleByAnimationFrameDecorator() { - // eslint-disable-next-line func-names - return function (target: any, key: string, descriptor: any) { - const fn = descriptor.value; - let definingProperty = false; - return { - configurable: true, - get() { - // eslint-disable-next-line no-prototype-builtins - if (definingProperty || this === target.prototype || this.hasOwnProperty(key)) { - return fn; - } - - const boundFn = throttleByAnimationFrame(fn.bind(this)); - definingProperty = true; - Object.defineProperty(this, key, { - value: boundFn, - configurable: true, - writable: true, - }); - definingProperty = false; - return boundFn; - }, - }; - }; -} +export default throttleByAnimationFrame; diff --git a/components/_util/warning.js b/components/_util/warning.js deleted file mode 100644 index 59681e40a..000000000 --- a/components/_util/warning.js +++ /dev/null @@ -1,7 +0,0 @@ -import warning, { resetWarned } from '../vc-util/warning'; - -export { resetWarned }; - -export default (valid, component, message = '') => { - warning(valid, `[antdv: ${component}] ${message}`); -}; diff --git a/components/_util/warning.ts b/components/_util/warning.ts new file mode 100644 index 000000000..73387fd81 --- /dev/null +++ b/components/_util/warning.ts @@ -0,0 +1,21 @@ +import vcWarning, { resetWarned } from '../vc-util/warning'; + +export { resetWarned }; +export function noop() {} + +type Warning = (valid: boolean, component: string, message?: string) => void; + +// eslint-disable-next-line import/no-mutable-exports +let warning: Warning = noop; +if (process.env.NODE_ENV !== 'production') { + warning = (valid, component, message) => { + vcWarning(valid, `[antd: ${component}] ${message}`); + + // StrictMode will inject console which will not throw warning in React 17. + if (process.env.NODE_ENV === 'test') { + resetWarned(); + } + }; +} + +export default warning;