ant-design/components/modal/ConfirmDialog.tsx
lijianan a11d3b4b63
feat: CP support Modal closeIcon (#47226)
* feat: CP support Modal closeIcon

* fix: fix
2024-01-30 14:42:46 +08:00

293 lines
8.0 KiB
TypeScript
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import * as React from 'react';
import CheckCircleFilled from '@ant-design/icons/CheckCircleFilled';
import CloseCircleFilled from '@ant-design/icons/CloseCircleFilled';
import ExclamationCircleFilled from '@ant-design/icons/ExclamationCircleFilled';
import InfoCircleFilled from '@ant-design/icons/InfoCircleFilled';
import classNames from 'classnames';
import { CONTAINER_MAX_OFFSET } from '../_util/hooks/useZIndex';
import { getTransitionName } from '../_util/motion';
import { devUseWarning } from '../_util/warning';
import type { ThemeConfig } from '../config-provider';
import ConfigProvider from '../config-provider';
import { useLocale } from '../locale';
import useToken from '../theme/useToken';
import CancelBtn from './components/ConfirmCancelBtn';
import OkBtn from './components/ConfirmOkBtn';
import type { ModalContextProps } from './context';
import { ModalContextProvider } from './context';
import type { ModalFuncProps, ModalLocale } from './interface';
import Modal from './Modal';
import Confirm from './style/confirm';
export interface ConfirmDialogProps extends ModalFuncProps {
prefixCls: string;
afterClose?: () => void;
close?: (...args: any[]) => void;
/**
* `close` prop support `...args` that pass to the developer
* that we can not break this.
* Provider `onClose` for internal usage
*/
onConfirm?: (confirmed: boolean) => void;
autoFocusButton?: null | 'ok' | 'cancel';
rootPrefixCls?: string;
iconPrefixCls?: string;
/**
* Only passed by static method
*/
theme?: ThemeConfig;
/** @private Internal Usage. Do not override this */
locale?: ModalLocale;
/**
* Do not throw if is await mode
*/
isSilent?: () => boolean;
}
export function ConfirmContent(
props: ConfirmDialogProps & {
confirmPrefixCls: string;
},
) {
const {
prefixCls,
icon,
okText,
cancelText,
confirmPrefixCls,
type,
okCancel,
footer,
// Legacy for static function usage
locale: staticLocale,
...resetProps
} = props;
if (process.env.NODE_ENV !== 'production') {
const warning = devUseWarning('Modal');
warning(
!(typeof icon === 'string' && icon.length > 2),
'breaking',
`\`icon\` is using ReactNode instead of string naming in v4. Please check \`${icon}\` at https://ant.design/components/icon`,
);
}
// Icon
let mergedIcon: React.ReactNode = icon;
// 支持传入{ icon: null }来隐藏`Modal.confirm`默认的Icon
if (!icon && icon !== null) {
switch (type) {
case 'info':
mergedIcon = <InfoCircleFilled />;
break;
case 'success':
mergedIcon = <CheckCircleFilled />;
break;
case 'error':
mergedIcon = <CloseCircleFilled />;
break;
default:
mergedIcon = <ExclamationCircleFilled />;
}
}
// 默认为 true保持向下兼容
const mergedOkCancel = okCancel ?? type === 'confirm';
const autoFocusButton = props.autoFocusButton === null ? false : props.autoFocusButton || 'ok';
const [locale] = useLocale('Modal');
const mergedLocale = staticLocale || locale;
// ================== Locale Text ==================
const okTextLocale = okText || (mergedOkCancel ? mergedLocale?.okText : mergedLocale?.justOkText);
const cancelTextLocale = cancelText || mergedLocale?.cancelText;
// ================= Context Value =================
const btnCtxValue: ModalContextProps = {
autoFocusButton,
cancelTextLocale,
okTextLocale,
mergedOkCancel,
...resetProps,
};
const btnCtxValueMemo = React.useMemo(() => btnCtxValue, [...Object.values(btnCtxValue)]);
// ====================== Footer Origin Node ======================
const footerOriginNode = (
<>
<CancelBtn />
<OkBtn />
</>
);
const hasTitle = props.title !== undefined && props.title !== null;
const bodyCls = `${confirmPrefixCls}-body`;
return (
<div className={`${confirmPrefixCls}-body-wrapper`}>
<div
className={classNames(bodyCls, {
[`${bodyCls}-has-title`]: hasTitle,
})}
>
{mergedIcon}
<div className={`${confirmPrefixCls}-paragraph`}>
{hasTitle && <span className={`${confirmPrefixCls}-title`}>{props.title}</span>}
<div className={`${confirmPrefixCls}-content`}>{props.content}</div>
</div>
</div>
{footer === undefined || typeof footer === 'function' ? (
<ModalContextProvider value={btnCtxValueMemo}>
<div className={`${confirmPrefixCls}-btns`}>
{typeof footer === 'function'
? footer(footerOriginNode, {
OkBtn,
CancelBtn,
})
: footerOriginNode}
</div>
</ModalContextProvider>
) : (
footer
)}
<Confirm prefixCls={prefixCls} />
</div>
);
}
const ConfirmDialog: React.FC<ConfirmDialogProps> = (props) => {
const {
close,
zIndex,
afterClose,
open,
keyboard,
centered,
getContainer,
maskStyle,
direction,
prefixCls,
wrapClassName,
rootPrefixCls,
bodyStyle,
closable = false,
closeIcon,
modalRender,
focusTriggerAfterClose,
onConfirm,
styles,
} = props;
if (process.env.NODE_ENV !== 'production') {
const warning = devUseWarning('Modal');
[
['visible', 'open'],
['bodyStyle', 'styles.body'],
['maskStyle', 'styles.mask'],
].forEach(([deprecatedName, newName]) => {
warning.deprecated(!(deprecatedName in props), deprecatedName, newName);
});
}
const confirmPrefixCls = `${prefixCls}-confirm`;
const width = props.width || 416;
const style = props.style || {};
const mask = props.mask === undefined ? true : props.mask;
// 默认为 false保持旧版默认行为
const maskClosable = props.maskClosable === undefined ? false : props.maskClosable;
const classString = classNames(
confirmPrefixCls,
`${confirmPrefixCls}-${props.type}`,
{ [`${confirmPrefixCls}-rtl`]: direction === 'rtl' },
props.className,
);
// ========================= zIndex =========================
const [, token] = useToken();
const mergedZIndex = React.useMemo(() => {
if (zIndex !== undefined) {
return zIndex;
}
// Static always use max zIndex
return token.zIndexPopupBase + CONTAINER_MAX_OFFSET;
}, [zIndex, token]);
// ========================= Render =========================
return (
<Modal
prefixCls={prefixCls}
className={classString}
wrapClassName={classNames(
{ [`${confirmPrefixCls}-centered`]: !!props.centered },
wrapClassName,
)}
onCancel={() => {
close?.({ triggerCancel: true });
onConfirm?.(false);
}}
open={open}
title=""
footer={null}
transitionName={getTransitionName(rootPrefixCls || '', 'zoom', props.transitionName)}
maskTransitionName={getTransitionName(rootPrefixCls || '', 'fade', props.maskTransitionName)}
mask={mask}
maskClosable={maskClosable}
style={style}
styles={{ body: bodyStyle, mask: maskStyle, ...styles }}
width={width}
zIndex={mergedZIndex}
afterClose={afterClose}
keyboard={keyboard}
centered={centered}
getContainer={getContainer}
closable={closable}
closeIcon={closeIcon}
modalRender={modalRender}
focusTriggerAfterClose={focusTriggerAfterClose}
>
<ConfirmContent {...props} confirmPrefixCls={confirmPrefixCls} />
</Modal>
);
};
const ConfirmDialogWrapper: React.FC<ConfirmDialogProps> = (props) => {
const { rootPrefixCls, iconPrefixCls, direction, theme } = props;
return (
<ConfigProvider
prefixCls={rootPrefixCls}
iconPrefixCls={iconPrefixCls}
direction={direction}
theme={theme}
>
<ConfirmDialog {...props} />
</ConfigProvider>
);
};
if (process.env.NODE_ENV !== 'production') {
ConfirmDialog.displayName = 'ConfirmDialog';
ConfirmDialogWrapper.displayName = 'ConfirmDialogWrapper';
}
export default ConfirmDialogWrapper;