2023-06-16 15:36:28 +08:00
|
|
|
import type { ReactNode } from 'react';
|
|
|
|
import React from 'react';
|
2024-01-11 15:11:55 +08:00
|
|
|
import CloseOutlined from '@ant-design/icons/CloseOutlined';
|
2024-02-28 14:11:15 +08:00
|
|
|
import pickAttrs from 'rc-util/lib/pickAttrs';
|
2023-06-16 15:36:28 +08:00
|
|
|
|
2024-03-06 11:53:05 +08:00
|
|
|
export type ClosableType = boolean | ({ closeIcon?: React.ReactNode } & React.AriaAttributes);
|
|
|
|
|
2024-02-28 14:11:15 +08:00
|
|
|
export type UseClosableParams = {
|
2024-03-06 11:53:05 +08:00
|
|
|
closable?: ClosableType;
|
2024-02-28 14:11:15 +08:00
|
|
|
closeIcon?: ReactNode;
|
|
|
|
defaultClosable?: boolean;
|
|
|
|
defaultCloseIcon?: ReactNode;
|
|
|
|
customCloseIconRender?: (closeIcon: ReactNode) => ReactNode;
|
|
|
|
};
|
|
|
|
|
|
|
|
function useInnerClosable(
|
|
|
|
closable?: UseClosableParams['closable'],
|
|
|
|
closeIcon?: ReactNode,
|
|
|
|
defaultClosable?: boolean,
|
|
|
|
) {
|
2023-06-16 15:36:28 +08:00
|
|
|
if (typeof closable === 'boolean') {
|
|
|
|
return closable;
|
|
|
|
}
|
2024-02-28 14:11:15 +08:00
|
|
|
if (typeof closable === 'object') {
|
|
|
|
return true;
|
|
|
|
}
|
2023-06-16 15:36:28 +08:00
|
|
|
if (closeIcon === undefined) {
|
|
|
|
return !!defaultClosable;
|
|
|
|
}
|
|
|
|
return closeIcon !== false && closeIcon !== null;
|
|
|
|
}
|
|
|
|
|
2024-02-28 14:11:15 +08:00
|
|
|
function useClosable({
|
|
|
|
closable,
|
|
|
|
closeIcon,
|
|
|
|
customCloseIconRender,
|
|
|
|
defaultCloseIcon = <CloseOutlined />,
|
2023-06-16 15:36:28 +08:00
|
|
|
defaultClosable = false,
|
2024-02-28 14:11:15 +08:00
|
|
|
}: UseClosableParams): [closable: boolean, closeIcon: React.ReactNode | null] {
|
2023-06-16 15:36:28 +08:00
|
|
|
const mergedClosable = useInnerClosable(closable, closeIcon, defaultClosable);
|
2024-02-28 14:11:15 +08:00
|
|
|
|
2023-06-16 15:36:28 +08:00
|
|
|
if (!mergedClosable) {
|
|
|
|
return [false, null];
|
|
|
|
}
|
2024-02-28 14:11:15 +08:00
|
|
|
const { closeIcon: closableIcon, ...restProps } =
|
|
|
|
typeof closable === 'object'
|
|
|
|
? closable
|
|
|
|
: ({} as { closeIcon: React.ReactNode } & React.AriaAttributes);
|
|
|
|
// Priority: closable.closeIcon > closeIcon > defaultCloseIcon
|
|
|
|
const mergedCloseIcon: ReactNode = (() => {
|
|
|
|
if (typeof closable === 'object' && closableIcon !== undefined) {
|
|
|
|
return closableIcon;
|
|
|
|
}
|
|
|
|
return typeof closeIcon === 'boolean' || closeIcon === undefined || closeIcon === null
|
2023-06-16 15:36:28 +08:00
|
|
|
? defaultCloseIcon
|
|
|
|
: closeIcon;
|
2024-02-28 14:11:15 +08:00
|
|
|
})();
|
|
|
|
const ariaProps = pickAttrs(restProps, true);
|
|
|
|
|
|
|
|
const plainCloseIcon = customCloseIconRender
|
|
|
|
? customCloseIconRender(mergedCloseIcon)
|
|
|
|
: mergedCloseIcon;
|
|
|
|
|
|
|
|
const closeIconWithAria = React.isValidElement(plainCloseIcon) ? (
|
|
|
|
React.cloneElement(plainCloseIcon, ariaProps)
|
|
|
|
) : (
|
|
|
|
<span {...ariaProps}>{plainCloseIcon}</span>
|
|
|
|
);
|
|
|
|
|
|
|
|
return [true, closeIconWithAria];
|
2023-06-16 15:36:28 +08:00
|
|
|
}
|
2024-01-11 15:11:55 +08:00
|
|
|
|
|
|
|
export default useClosable;
|