1
This commit is contained in:
+32
@@ -0,0 +1,32 @@
|
||||
import * as React from 'react';
|
||||
import type { ThemeConfig } from '../config-provider';
|
||||
import type { ModalFuncProps, ModalLocale } from './interface';
|
||||
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 declare const ConfirmContent: React.FC<ConfirmDialogProps & {
|
||||
confirmPrefixCls: string;
|
||||
}>;
|
||||
declare const ConfirmDialogWrapper: React.FC<ConfirmDialogProps>;
|
||||
export default ConfirmDialogWrapper;
|
||||
+228
@@ -0,0 +1,228 @@
|
||||
"use client";
|
||||
|
||||
import * as React from 'react';
|
||||
import CheckCircleFilled from "@ant-design/icons/es/icons/CheckCircleFilled";
|
||||
import CloseCircleFilled from "@ant-design/icons/es/icons/CloseCircleFilled";
|
||||
import ExclamationCircleFilled from "@ant-design/icons/es/icons/ExclamationCircleFilled";
|
||||
import InfoCircleFilled from "@ant-design/icons/es/icons/InfoCircleFilled";
|
||||
import { clsx } from 'clsx';
|
||||
import { CONTAINER_MAX_OFFSET, normalizeMaskConfig } from '../_util/hooks';
|
||||
import { isNonNullable, isPlainObject } from '../_util/is';
|
||||
import { getTransitionName } from '../_util/motion';
|
||||
import { devUseWarning } from '../_util/warning';
|
||||
import ConfigProvider from '../config-provider';
|
||||
import { useComponentConfig } from '../config-provider/context';
|
||||
import { useLocale } from '../locale';
|
||||
import useToken from '../theme/useToken';
|
||||
import CancelBtn from './components/ConfirmCancelBtn';
|
||||
import OkBtn from './components/ConfirmOkBtn';
|
||||
import { ModalContextProvider } from './context';
|
||||
import Modal from './Modal';
|
||||
import Confirm from './style/confirm';
|
||||
export const ConfirmContent = props => {
|
||||
const {
|
||||
prefixCls,
|
||||
icon,
|
||||
okText,
|
||||
cancelText,
|
||||
confirmPrefixCls,
|
||||
type,
|
||||
okCancel,
|
||||
footer,
|
||||
// Legacy for static function usage
|
||||
locale: staticLocale,
|
||||
autoFocusButton,
|
||||
focusable,
|
||||
...restProps
|
||||
} = props;
|
||||
if (process.env.NODE_ENV !== 'production') {
|
||||
const warning = devUseWarning('Modal');
|
||||
process.env.NODE_ENV !== "production" ? 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`) : void 0;
|
||||
}
|
||||
// Icon
|
||||
let mergedIcon = icon;
|
||||
// 支持传入{ icon: null }来隐藏`Modal.confirm`默认的Icon
|
||||
if (!icon && icon !== null) {
|
||||
switch (type) {
|
||||
case 'info':
|
||||
mergedIcon = /*#__PURE__*/React.createElement(InfoCircleFilled, null);
|
||||
break;
|
||||
case 'success':
|
||||
mergedIcon = /*#__PURE__*/React.createElement(CheckCircleFilled, null);
|
||||
break;
|
||||
case 'error':
|
||||
mergedIcon = /*#__PURE__*/React.createElement(CloseCircleFilled, null);
|
||||
break;
|
||||
default:
|
||||
mergedIcon = /*#__PURE__*/React.createElement(ExclamationCircleFilled, null);
|
||||
}
|
||||
}
|
||||
// 默认为 true,保持向下兼容
|
||||
const mergedOkCancel = okCancel ?? type === 'confirm';
|
||||
const mergedAutoFocusButton = React.useMemo(() => {
|
||||
const base = focusable?.autoFocusButton || autoFocusButton;
|
||||
return base || base === null ? base : 'ok';
|
||||
}, [autoFocusButton, focusable?.autoFocusButton]);
|
||||
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 {
|
||||
closable
|
||||
} = restProps;
|
||||
const {
|
||||
onClose
|
||||
} = isPlainObject(closable) ? closable : {};
|
||||
const memoizedValue = React.useMemo(() => {
|
||||
return {
|
||||
autoFocusButton: mergedAutoFocusButton,
|
||||
cancelTextLocale,
|
||||
okTextLocale,
|
||||
mergedOkCancel,
|
||||
onClose,
|
||||
...restProps
|
||||
};
|
||||
}, [mergedAutoFocusButton, cancelTextLocale, okTextLocale, mergedOkCancel, onClose, restProps]);
|
||||
// ====================== Footer Origin Node ======================
|
||||
const footerOriginNode = /*#__PURE__*/React.createElement(React.Fragment, null, /*#__PURE__*/React.createElement(CancelBtn, null), /*#__PURE__*/React.createElement(OkBtn, null));
|
||||
const hasTitle = isNonNullable(props.title) && props.title !== '';
|
||||
const hasIcon = isNonNullable(mergedIcon);
|
||||
const bodyCls = `${confirmPrefixCls}-body`;
|
||||
return /*#__PURE__*/React.createElement("div", {
|
||||
className: `${confirmPrefixCls}-body-wrapper`
|
||||
}, /*#__PURE__*/React.createElement("div", {
|
||||
className: clsx(bodyCls, {
|
||||
[`${bodyCls}-has-title`]: hasTitle,
|
||||
[`${bodyCls}-no-icon`]: !hasIcon
|
||||
})
|
||||
}, mergedIcon, /*#__PURE__*/React.createElement("div", {
|
||||
className: `${confirmPrefixCls}-paragraph`
|
||||
}, hasTitle && /*#__PURE__*/React.createElement("span", {
|
||||
className: `${confirmPrefixCls}-title`
|
||||
}, props.title), /*#__PURE__*/React.createElement("div", {
|
||||
className: `${confirmPrefixCls}-content`
|
||||
}, props.content))), footer === undefined || typeof footer === 'function' ? (/*#__PURE__*/React.createElement(ModalContextProvider, {
|
||||
value: memoizedValue
|
||||
}, /*#__PURE__*/React.createElement("div", {
|
||||
className: `${confirmPrefixCls}-btns`
|
||||
}, typeof footer === 'function' ? footer(footerOriginNode, {
|
||||
OkBtn,
|
||||
CancelBtn
|
||||
}) : footerOriginNode))) : footer, /*#__PURE__*/React.createElement(Confirm, {
|
||||
prefixCls: prefixCls
|
||||
}));
|
||||
};
|
||||
const ConfirmDialog = props => {
|
||||
const {
|
||||
close,
|
||||
zIndex,
|
||||
maskStyle,
|
||||
direction,
|
||||
prefixCls,
|
||||
wrapClassName,
|
||||
rootPrefixCls,
|
||||
bodyStyle,
|
||||
closable = false,
|
||||
onConfirm,
|
||||
styles,
|
||||
title,
|
||||
mask,
|
||||
maskClosable,
|
||||
okButtonProps,
|
||||
cancelButtonProps
|
||||
} = props;
|
||||
const {
|
||||
cancelButtonProps: contextCancelButtonProps,
|
||||
okButtonProps: contextOkButtonProps
|
||||
} = useComponentConfig('modal');
|
||||
if (process.env.NODE_ENV !== 'production') {
|
||||
const warning = devUseWarning('Modal');
|
||||
[['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 classString = clsx(confirmPrefixCls, `${confirmPrefixCls}-${props.type}`, {
|
||||
[`${confirmPrefixCls}-rtl`]: direction === 'rtl'
|
||||
}, props.className);
|
||||
// ========================== Mask ==========================
|
||||
// 默认为 false,保持旧版默认行为
|
||||
const mergedMask = React.useMemo(() => {
|
||||
const nextMaskConfig = normalizeMaskConfig(mask, maskClosable);
|
||||
nextMaskConfig.closable ?? (nextMaskConfig.closable = false);
|
||||
return nextMaskConfig;
|
||||
}, [mask, maskClosable]);
|
||||
// ========================= 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 /*#__PURE__*/React.createElement(Modal, {
|
||||
...props,
|
||||
className: classString,
|
||||
wrapClassName: clsx({
|
||||
[`${confirmPrefixCls}-centered`]: !!props.centered
|
||||
}, wrapClassName),
|
||||
onCancel: () => {
|
||||
close?.({
|
||||
triggerCancel: true
|
||||
});
|
||||
onConfirm?.(false);
|
||||
},
|
||||
title: title,
|
||||
footer: null,
|
||||
transitionName: getTransitionName(rootPrefixCls || '', 'zoom', props.transitionName),
|
||||
maskTransitionName: getTransitionName(rootPrefixCls || '', 'fade', props.maskTransitionName),
|
||||
mask: mergedMask,
|
||||
style: style,
|
||||
styles: {
|
||||
body: bodyStyle,
|
||||
mask: maskStyle,
|
||||
...styles
|
||||
},
|
||||
width: width,
|
||||
zIndex: mergedZIndex,
|
||||
closable: closable
|
||||
}, /*#__PURE__*/React.createElement(ConfirmContent, {
|
||||
...props,
|
||||
confirmPrefixCls: confirmPrefixCls,
|
||||
okButtonProps: {
|
||||
...contextOkButtonProps,
|
||||
...okButtonProps
|
||||
},
|
||||
cancelButtonProps: {
|
||||
...contextCancelButtonProps,
|
||||
...cancelButtonProps
|
||||
}
|
||||
}));
|
||||
};
|
||||
const ConfirmDialogWrapper = props => {
|
||||
const {
|
||||
rootPrefixCls,
|
||||
iconPrefixCls,
|
||||
direction,
|
||||
theme
|
||||
} = props;
|
||||
return /*#__PURE__*/React.createElement(ConfigProvider, {
|
||||
prefixCls: rootPrefixCls,
|
||||
iconPrefixCls: iconPrefixCls,
|
||||
direction: direction,
|
||||
theme: theme
|
||||
}, /*#__PURE__*/React.createElement(ConfirmDialog, {
|
||||
...props
|
||||
}));
|
||||
};
|
||||
if (process.env.NODE_ENV !== 'production') {
|
||||
ConfirmDialog.displayName = 'ConfirmDialog';
|
||||
ConfirmDialogWrapper.displayName = 'ConfirmDialogWrapper';
|
||||
}
|
||||
export default ConfirmDialogWrapper;
|
||||
+4
@@ -0,0 +1,4 @@
|
||||
import * as React from 'react';
|
||||
import type { ModalProps } from './interface';
|
||||
declare const Modal: React.FC<ModalProps>;
|
||||
export default Modal;
|
||||
+248
@@ -0,0 +1,248 @@
|
||||
"use client";
|
||||
|
||||
import * as React from 'react';
|
||||
import CloseOutlined from "@ant-design/icons/es/icons/CloseOutlined";
|
||||
import Dialog from '@rc-component/dialog';
|
||||
import { composeRef } from "@rc-component/util/es/ref";
|
||||
import { clsx } from 'clsx';
|
||||
import ContextIsolator from '../_util/ContextIsolator';
|
||||
import { pickClosable, useClosable, useMergedMask, useMergeSemantic, useZIndex } from '../_util/hooks';
|
||||
import { isNonNullable, isNumber, isPlainObject } from '../_util/is';
|
||||
import { getTransitionName } from '../_util/motion';
|
||||
import { canUseDocElement } from '../_util/styleChecker';
|
||||
import { devUseWarning } from '../_util/warning';
|
||||
import ZIndexContext from '../_util/zindexContext';
|
||||
import { ConfigContext } from '../config-provider';
|
||||
import { useComponentConfig } from '../config-provider/context';
|
||||
import useCSSVarCls from '../config-provider/hooks/useCSSVarCls';
|
||||
import useFocusable from '../drawer/useFocusable';
|
||||
import Skeleton from '../skeleton';
|
||||
import { usePanelRef } from '../watermark/context';
|
||||
import { Footer, renderCloseIcon } from './shared';
|
||||
import useStyle from './style';
|
||||
let mousePosition;
|
||||
// ref: https://github.com/ant-design/ant-design/issues/15795
|
||||
const getClickPosition = e => {
|
||||
mousePosition = {
|
||||
x: e.pageX,
|
||||
y: e.pageY
|
||||
};
|
||||
// 100ms 内发生过点击事件,则从点击位置动画展示
|
||||
// 否则直接 zoom 展示
|
||||
// 这样可以兼容非点击方式展开
|
||||
setTimeout(() => {
|
||||
mousePosition = null;
|
||||
}, 100);
|
||||
};
|
||||
// 只有点击事件支持从鼠标位置动画展开
|
||||
if (canUseDocElement()) {
|
||||
document.documentElement.addEventListener('click', getClickPosition, true);
|
||||
}
|
||||
const Modal = props => {
|
||||
const {
|
||||
prefixCls: customizePrefixCls,
|
||||
className,
|
||||
rootClassName,
|
||||
open,
|
||||
wrapClassName,
|
||||
centered,
|
||||
getContainer,
|
||||
style,
|
||||
width = 520,
|
||||
footer,
|
||||
classNames,
|
||||
styles,
|
||||
children,
|
||||
loading,
|
||||
confirmLoading,
|
||||
zIndex: customizeZIndex,
|
||||
mousePosition: customizeMousePosition,
|
||||
onOk,
|
||||
onCancel,
|
||||
okButtonProps,
|
||||
cancelButtonProps,
|
||||
destroyOnHidden,
|
||||
destroyOnClose,
|
||||
panelRef = null,
|
||||
closable,
|
||||
mask: modalMask,
|
||||
modalRender,
|
||||
maskClosable,
|
||||
// Focusable
|
||||
focusTriggerAfterClose,
|
||||
focusable,
|
||||
...restProps
|
||||
} = props;
|
||||
const {
|
||||
getPopupContainer: getContextPopupContainer,
|
||||
getPrefixCls,
|
||||
direction,
|
||||
className: contextClassName,
|
||||
style: contextStyle,
|
||||
classNames: contextClassNames,
|
||||
styles: contextStyles,
|
||||
centered: contextCentered,
|
||||
cancelButtonProps: contextCancelButtonProps,
|
||||
okButtonProps: contextOkButtonProps,
|
||||
mask: contextMask
|
||||
} = useComponentConfig('modal');
|
||||
const {
|
||||
modal: modalContext
|
||||
} = React.useContext(ConfigContext);
|
||||
const [closableAfterClose, onClose] = React.useMemo(() => {
|
||||
if (typeof closable === 'boolean') {
|
||||
return [undefined, undefined];
|
||||
}
|
||||
return [closable?.afterClose, closable?.onClose];
|
||||
}, [closable]);
|
||||
const prefixCls = getPrefixCls('modal', customizePrefixCls);
|
||||
const rootPrefixCls = getPrefixCls();
|
||||
// ============================ Mask ============================
|
||||
const [mergedMask, maskBlurClassName, mergeMaskClosable] = useMergedMask(modalMask, contextMask, prefixCls, maskClosable);
|
||||
// ========================== Focusable =========================
|
||||
const mergedFocusable = useFocusable(focusable, mergedMask, focusTriggerAfterClose);
|
||||
// ============================ Open ============================
|
||||
const handleCancel = e => {
|
||||
if (confirmLoading) {
|
||||
return;
|
||||
}
|
||||
onCancel?.(e);
|
||||
onClose?.();
|
||||
};
|
||||
const handleOk = e => {
|
||||
onOk?.(e);
|
||||
onClose?.();
|
||||
};
|
||||
if (process.env.NODE_ENV !== 'production') {
|
||||
const warning = devUseWarning('Modal');
|
||||
[['bodyStyle', 'styles.body'], ['maskStyle', 'styles.mask'], ['destroyOnClose', 'destroyOnHidden'], ['autoFocusButton', 'focusable.autoFocusButton'], ['focusTriggerAfterClose', 'focusable.focusTriggerAfterClose'], ['maskClosable', 'mask.closable']].forEach(([deprecatedName, newName]) => {
|
||||
warning.deprecated(!(deprecatedName in props), deprecatedName, newName);
|
||||
});
|
||||
}
|
||||
// Style
|
||||
const rootCls = useCSSVarCls(prefixCls);
|
||||
const [hashId, cssVarCls] = useStyle(prefixCls, rootCls);
|
||||
const wrapClassNameExtended = clsx(wrapClassName, {
|
||||
[`${prefixCls}-centered`]: centered ?? contextCentered,
|
||||
[`${prefixCls}-wrap-rtl`]: direction === 'rtl'
|
||||
});
|
||||
const dialogFooter = footer !== null && !loading ? (/*#__PURE__*/React.createElement(Footer, {
|
||||
...props,
|
||||
okButtonProps: {
|
||||
...contextOkButtonProps,
|
||||
...okButtonProps
|
||||
},
|
||||
onOk: handleOk,
|
||||
cancelButtonProps: {
|
||||
...contextCancelButtonProps,
|
||||
...cancelButtonProps
|
||||
},
|
||||
onCancel: handleCancel
|
||||
})) : null;
|
||||
const [rawClosable, mergedCloseIcon, closeBtnIsDisabled, ariaProps] = useClosable(pickClosable(props), pickClosable(modalContext), {
|
||||
closable: true,
|
||||
closeIcon: /*#__PURE__*/React.createElement(CloseOutlined, {
|
||||
className: `${prefixCls}-close-icon`
|
||||
}),
|
||||
closeIconRender: icon => renderCloseIcon(prefixCls, icon)
|
||||
});
|
||||
const mergedClosable = rawClosable ? {
|
||||
disabled: closeBtnIsDisabled,
|
||||
closeIcon: mergedCloseIcon,
|
||||
afterClose: closableAfterClose,
|
||||
...ariaProps
|
||||
} : false;
|
||||
// ============================ modalRender ============================
|
||||
const mergedModalRender = modalRender ? node => /*#__PURE__*/React.createElement("div", {
|
||||
className: `${prefixCls}-render`
|
||||
}, modalRender(node)) : undefined;
|
||||
// ============================ Refs ============================
|
||||
// Select `ant-modal-container` by `panelRef`
|
||||
const panelClassName = `.${prefixCls}-${modalRender ? 'render' : 'container'}`;
|
||||
const innerPanelRef = usePanelRef(panelClassName);
|
||||
const mergedPanelRef = composeRef(panelRef, innerPanelRef);
|
||||
// ============================ zIndex ============================
|
||||
const [zIndex, contextZIndex] = useZIndex('Modal', customizeZIndex);
|
||||
const mergedProps = {
|
||||
...props,
|
||||
width,
|
||||
panelRef,
|
||||
focusTriggerAfterClose: mergedFocusable.focusTriggerAfterClose,
|
||||
focusable: mergedFocusable,
|
||||
mask: mergedMask,
|
||||
maskClosable: mergeMaskClosable,
|
||||
zIndex
|
||||
};
|
||||
const [mergedClassNames, mergedStyles] = useMergeSemantic([contextClassNames, classNames, maskBlurClassName], [contextStyles, styles], {
|
||||
props: mergedProps
|
||||
});
|
||||
// =========================== Width ============================
|
||||
const [numWidth, responsiveWidth] = React.useMemo(() => {
|
||||
if (isPlainObject(width)) {
|
||||
return [undefined, width];
|
||||
}
|
||||
return [width, undefined];
|
||||
}, [width]);
|
||||
const responsiveWidthVars = React.useMemo(() => {
|
||||
const vars = {};
|
||||
if (responsiveWidth) {
|
||||
Object.keys(responsiveWidth).forEach(breakpoint => {
|
||||
const breakpointWidth = responsiveWidth[breakpoint];
|
||||
if (isNonNullable(breakpointWidth)) {
|
||||
vars[`--${prefixCls}-${breakpoint}-width`] = isNumber(breakpointWidth) ? `${breakpointWidth}px` : breakpointWidth;
|
||||
}
|
||||
});
|
||||
}
|
||||
return vars;
|
||||
}, [prefixCls, responsiveWidth]);
|
||||
// =========================== Render ===========================
|
||||
return /*#__PURE__*/React.createElement(ContextIsolator, {
|
||||
form: true,
|
||||
space: true
|
||||
}, /*#__PURE__*/React.createElement(ZIndexContext.Provider, {
|
||||
value: contextZIndex
|
||||
}, /*#__PURE__*/React.createElement(Dialog, {
|
||||
width: numWidth,
|
||||
...restProps,
|
||||
zIndex: zIndex,
|
||||
getContainer: getContainer === undefined ? getContextPopupContainer : getContainer,
|
||||
prefixCls: prefixCls,
|
||||
rootClassName: clsx(hashId, rootClassName, cssVarCls, rootCls, mergedClassNames.root),
|
||||
rootStyle: mergedStyles.root,
|
||||
footer: dialogFooter,
|
||||
visible: open,
|
||||
mousePosition: customizeMousePosition ?? mousePosition,
|
||||
onClose: handleCancel,
|
||||
closable: mergedClosable,
|
||||
closeIcon: mergedCloseIcon,
|
||||
transitionName: getTransitionName(rootPrefixCls, 'zoom', props.transitionName),
|
||||
maskTransitionName: getTransitionName(rootPrefixCls, 'fade', props.maskTransitionName),
|
||||
mask: mergedMask,
|
||||
maskClosable: mergeMaskClosable,
|
||||
className: clsx(hashId, className, contextClassName),
|
||||
style: {
|
||||
...contextStyle,
|
||||
...style,
|
||||
...responsiveWidthVars
|
||||
},
|
||||
classNames: {
|
||||
...mergedClassNames,
|
||||
wrapper: clsx(mergedClassNames.wrapper, wrapClassNameExtended)
|
||||
},
|
||||
styles: mergedStyles,
|
||||
panelRef: mergedPanelRef,
|
||||
destroyOnHidden: destroyOnHidden ?? destroyOnClose,
|
||||
modalRender: mergedModalRender,
|
||||
// Focusable
|
||||
focusTriggerAfterClose: mergedFocusable.focusTriggerAfterClose,
|
||||
focusTrap: mergedFocusable.trap
|
||||
}, loading ? (/*#__PURE__*/React.createElement(Skeleton, {
|
||||
active: true,
|
||||
title: false,
|
||||
paragraph: {
|
||||
rows: 4
|
||||
},
|
||||
className: `${prefixCls}-body-skeleton`
|
||||
})) : children)));
|
||||
};
|
||||
export default Modal;
|
||||
+11
@@ -0,0 +1,11 @@
|
||||
import * as React from 'react';
|
||||
import type { PanelProps } from '@rc-component/dialog/lib/Dialog/Content/Panel';
|
||||
import type { ModalClassNamesType, ModalFuncProps, ModalStylesType } from './interface';
|
||||
export interface PurePanelProps extends Omit<PanelProps, 'prefixCls' | 'footer' | 'classNames' | 'styles'>, Pick<ModalFuncProps, 'type' | 'footer'> {
|
||||
prefixCls?: string;
|
||||
style?: React.CSSProperties;
|
||||
classNames?: ModalClassNamesType;
|
||||
styles?: ModalStylesType;
|
||||
}
|
||||
declare const _default: (props: PurePanelProps) => React.JSX.Element;
|
||||
export default _default;
|
||||
+85
@@ -0,0 +1,85 @@
|
||||
"use client";
|
||||
|
||||
import * as React from 'react';
|
||||
import { Panel } from '@rc-component/dialog';
|
||||
import { clsx } from 'clsx';
|
||||
import { useMergeSemantic } from '../_util/hooks';
|
||||
import { withPureRenderTheme } from '../_util/PurePanel';
|
||||
import { ConfigContext } from '../config-provider';
|
||||
import { useComponentConfig } from '../config-provider/context';
|
||||
import useCSSVarCls from '../config-provider/hooks/useCSSVarCls';
|
||||
import { ConfirmContent } from './ConfirmDialog';
|
||||
import { Footer, renderCloseIcon } from './shared';
|
||||
import useStyle from './style';
|
||||
const PurePanel = props => {
|
||||
const {
|
||||
prefixCls: customizePrefixCls,
|
||||
className,
|
||||
closeIcon,
|
||||
closable,
|
||||
type,
|
||||
title,
|
||||
children,
|
||||
footer,
|
||||
classNames,
|
||||
styles,
|
||||
...restProps
|
||||
} = props;
|
||||
const {
|
||||
getPrefixCls
|
||||
} = React.useContext(ConfigContext);
|
||||
const {
|
||||
className: contextClassName,
|
||||
style: contextStyle,
|
||||
classNames: contextClassNames,
|
||||
styles: contextStyles
|
||||
} = useComponentConfig('modal');
|
||||
const rootPrefixCls = getPrefixCls();
|
||||
const prefixCls = customizePrefixCls || getPrefixCls('modal');
|
||||
const rootCls = useCSSVarCls(rootPrefixCls);
|
||||
const [hashId, cssVarCls] = useStyle(prefixCls, rootCls);
|
||||
const [mergedClassNames, mergedStyles] = useMergeSemantic([contextClassNames, classNames], [contextStyles, styles], {
|
||||
props
|
||||
});
|
||||
const confirmPrefixCls = `${prefixCls}-confirm`;
|
||||
// Choose target props by confirm mark
|
||||
let additionalProps = {};
|
||||
if (type) {
|
||||
additionalProps = {
|
||||
closable: closable ?? false,
|
||||
title: '',
|
||||
footer: '',
|
||||
children: (/*#__PURE__*/React.createElement(ConfirmContent, {
|
||||
...props,
|
||||
prefixCls: prefixCls,
|
||||
confirmPrefixCls: confirmPrefixCls,
|
||||
rootPrefixCls: rootPrefixCls,
|
||||
content: children
|
||||
}))
|
||||
};
|
||||
} else {
|
||||
additionalProps = {
|
||||
closable: closable ?? true,
|
||||
title,
|
||||
footer: footer !== null && /*#__PURE__*/React.createElement(Footer, {
|
||||
...props
|
||||
}),
|
||||
children
|
||||
};
|
||||
}
|
||||
return /*#__PURE__*/React.createElement(Panel, {
|
||||
prefixCls: prefixCls,
|
||||
className: clsx(hashId, `${prefixCls}-pure-panel`, type && confirmPrefixCls, type && `${confirmPrefixCls}-${type}`, className, contextClassName, cssVarCls, rootCls, mergedClassNames.root),
|
||||
style: {
|
||||
...contextStyle,
|
||||
...mergedStyles.root
|
||||
},
|
||||
...restProps,
|
||||
closeIcon: renderCloseIcon(prefixCls, closeIcon),
|
||||
closable: closable,
|
||||
classNames: mergedClassNames,
|
||||
styles: mergedStyles,
|
||||
...additionalProps
|
||||
});
|
||||
};
|
||||
export default withPureRenderTheme(PurePanel);
|
||||
+11
@@ -0,0 +1,11 @@
|
||||
import type { FC } from 'react';
|
||||
import React from 'react';
|
||||
import type { ConfirmDialogProps } from '../ConfirmDialog';
|
||||
export interface ConfirmCancelBtnProps extends Pick<ConfirmDialogProps, 'cancelButtonProps' | 'isSilent' | 'rootPrefixCls' | 'close' | 'onConfirm' | 'onCancel'> {
|
||||
autoFocusButton?: false | 'ok' | 'cancel' | null;
|
||||
cancelTextLocale?: React.ReactNode;
|
||||
mergedOkCancel?: boolean;
|
||||
onClose?: () => void;
|
||||
}
|
||||
declare const ConfirmCancelBtn: FC;
|
||||
export default ConfirmCancelBtn;
|
||||
+32
@@ -0,0 +1,32 @@
|
||||
"use client";
|
||||
|
||||
import React, { useContext } from 'react';
|
||||
import ActionButton from '../../_util/ActionButton';
|
||||
import { ModalContext } from '../context';
|
||||
const ConfirmCancelBtn = () => {
|
||||
const {
|
||||
autoFocusButton,
|
||||
cancelButtonProps,
|
||||
cancelTextLocale,
|
||||
isSilent,
|
||||
mergedOkCancel,
|
||||
rootPrefixCls,
|
||||
close,
|
||||
onCancel,
|
||||
onConfirm,
|
||||
onClose
|
||||
} = useContext(ModalContext);
|
||||
return mergedOkCancel ? (/*#__PURE__*/React.createElement(ActionButton, {
|
||||
isSilent: isSilent,
|
||||
actionFn: onCancel,
|
||||
close: (...args) => {
|
||||
close?.(...args);
|
||||
onConfirm?.(false);
|
||||
onClose?.();
|
||||
},
|
||||
autoFocus: autoFocusButton === 'cancel',
|
||||
buttonProps: cancelButtonProps,
|
||||
prefixCls: `${rootPrefixCls}-btn`
|
||||
}, cancelTextLocale)) : null;
|
||||
};
|
||||
export default ConfirmCancelBtn;
|
||||
+10
@@ -0,0 +1,10 @@
|
||||
import type { FC } from 'react';
|
||||
import React from 'react';
|
||||
import type { ConfirmDialogProps } from '../ConfirmDialog';
|
||||
export interface ConfirmOkBtnProps extends Pick<ConfirmDialogProps, 'close' | 'isSilent' | 'okType' | 'okButtonProps' | 'rootPrefixCls' | 'onConfirm' | 'onOk'> {
|
||||
autoFocusButton?: false | 'ok' | 'cancel' | null;
|
||||
okTextLocale?: React.ReactNode;
|
||||
onClose?: () => void;
|
||||
}
|
||||
declare const ConfirmOkBtn: FC;
|
||||
export default ConfirmOkBtn;
|
||||
+33
@@ -0,0 +1,33 @@
|
||||
"use client";
|
||||
|
||||
import React, { useContext } from 'react';
|
||||
import ActionButton from '../../_util/ActionButton';
|
||||
import { ModalContext } from '../context';
|
||||
const ConfirmOkBtn = () => {
|
||||
const {
|
||||
autoFocusButton,
|
||||
close,
|
||||
isSilent,
|
||||
okButtonProps,
|
||||
rootPrefixCls,
|
||||
okTextLocale,
|
||||
okType,
|
||||
onConfirm,
|
||||
onOk,
|
||||
onClose
|
||||
} = useContext(ModalContext);
|
||||
return /*#__PURE__*/React.createElement(ActionButton, {
|
||||
isSilent: isSilent,
|
||||
type: okType || 'primary',
|
||||
actionFn: onOk,
|
||||
close: (...args) => {
|
||||
close?.(...args);
|
||||
onConfirm?.(true);
|
||||
onClose?.();
|
||||
},
|
||||
autoFocus: autoFocusButton === 'ok',
|
||||
buttonProps: okButtonProps,
|
||||
prefixCls: `${rootPrefixCls}-btn`
|
||||
}, okTextLocale);
|
||||
};
|
||||
export default ConfirmOkBtn;
|
||||
+8
@@ -0,0 +1,8 @@
|
||||
import type { FC } from 'react';
|
||||
import React from 'react';
|
||||
import type { ModalProps } from '../interface';
|
||||
export interface NormalCancelBtnProps extends Pick<ModalProps, 'cancelButtonProps' | 'onCancel'> {
|
||||
cancelTextLocale?: React.ReactNode;
|
||||
}
|
||||
declare const NormalCancelBtn: FC;
|
||||
export default NormalCancelBtn;
|
||||
+17
@@ -0,0 +1,17 @@
|
||||
"use client";
|
||||
|
||||
import React, { useContext } from 'react';
|
||||
import Button from '../../button/Button';
|
||||
import { ModalContext } from '../context';
|
||||
const NormalCancelBtn = () => {
|
||||
const {
|
||||
cancelButtonProps,
|
||||
cancelTextLocale,
|
||||
onCancel
|
||||
} = useContext(ModalContext);
|
||||
return /*#__PURE__*/React.createElement(Button, {
|
||||
onClick: onCancel,
|
||||
...cancelButtonProps
|
||||
}, cancelTextLocale);
|
||||
};
|
||||
export default NormalCancelBtn;
|
||||
+8
@@ -0,0 +1,8 @@
|
||||
import type { FC } from 'react';
|
||||
import React from 'react';
|
||||
import type { ModalProps } from '../interface';
|
||||
export interface NormalOkBtnProps extends Pick<ModalProps, 'confirmLoading' | 'okType' | 'okButtonProps' | 'onOk'> {
|
||||
okTextLocale?: React.ReactNode;
|
||||
}
|
||||
declare const NormalOkBtn: FC;
|
||||
export default NormalOkBtn;
|
||||
+22
@@ -0,0 +1,22 @@
|
||||
"use client";
|
||||
|
||||
import React, { useContext } from 'react';
|
||||
import Button from '../../button/Button';
|
||||
import { convertLegacyProps } from '../../button/buttonHelpers';
|
||||
import { ModalContext } from '../context';
|
||||
const NormalOkBtn = () => {
|
||||
const {
|
||||
confirmLoading,
|
||||
okButtonProps,
|
||||
okType,
|
||||
okTextLocale,
|
||||
onOk
|
||||
} = useContext(ModalContext);
|
||||
return /*#__PURE__*/React.createElement(Button, {
|
||||
...convertLegacyProps(okType),
|
||||
loading: confirmLoading,
|
||||
onClick: onOk,
|
||||
...okButtonProps
|
||||
}, okTextLocale);
|
||||
};
|
||||
export default NormalOkBtn;
|
||||
+27
@@ -0,0 +1,27 @@
|
||||
import type { ModalFuncProps } from './interface';
|
||||
export type ConfigUpdate = ModalFuncProps | ((prevConfig: ModalFuncProps) => ModalFuncProps);
|
||||
export type ModalFunc = (props: ModalFuncProps) => {
|
||||
destroy: () => void;
|
||||
update: (configUpdate: ConfigUpdate) => void;
|
||||
};
|
||||
export type ModalStaticFunctions = {
|
||||
info: ModalFunc;
|
||||
success: ModalFunc;
|
||||
error: ModalFunc;
|
||||
warning: ModalFunc;
|
||||
confirm: ModalFunc;
|
||||
/** @deprecated Please use `warning` instead */
|
||||
warn: ModalFunc;
|
||||
};
|
||||
export default function confirm(config: ModalFuncProps): {
|
||||
destroy: (...args: any[]) => void;
|
||||
update: (configUpdate: ConfigUpdate) => void;
|
||||
};
|
||||
export declare function withWarn(props: ModalFuncProps): ModalFuncProps;
|
||||
export declare function withInfo(props: ModalFuncProps): ModalFuncProps;
|
||||
export declare function withSuccess(props: ModalFuncProps): ModalFuncProps;
|
||||
export declare function withError(props: ModalFuncProps): ModalFuncProps;
|
||||
export declare function withConfirm(props: ModalFuncProps): ModalFuncProps;
|
||||
export declare function modalGlobalConfig({ rootPrefixCls }: {
|
||||
rootPrefixCls: string;
|
||||
}): void;
|
||||
+159
@@ -0,0 +1,159 @@
|
||||
"use client";
|
||||
|
||||
import React, { useContext } from 'react';
|
||||
import { render, unmount } from "@rc-component/util/es/React/render";
|
||||
import warning from '../_util/warning';
|
||||
import ConfigProvider, { ConfigContext, globalConfig, warnContext } from '../config-provider';
|
||||
import ConfirmDialog from './ConfirmDialog';
|
||||
import destroyFns from './destroyFns';
|
||||
import { getConfirmLocale } from './locale';
|
||||
let defaultRootPrefixCls = '';
|
||||
function getRootPrefixCls() {
|
||||
return defaultRootPrefixCls;
|
||||
}
|
||||
const ConfirmDialogWrapper = props => {
|
||||
const {
|
||||
prefixCls: customizePrefixCls,
|
||||
getContainer,
|
||||
direction
|
||||
} = props;
|
||||
const runtimeLocale = getConfirmLocale();
|
||||
const config = useContext(ConfigContext);
|
||||
const rootPrefixCls = getRootPrefixCls() || config.getPrefixCls();
|
||||
// because Modal.config set rootPrefixCls, which is different from other components
|
||||
const prefixCls = customizePrefixCls || `${rootPrefixCls}-modal`;
|
||||
let mergedGetContainer = getContainer;
|
||||
if (mergedGetContainer === false) {
|
||||
mergedGetContainer = undefined;
|
||||
if (process.env.NODE_ENV !== 'production') {
|
||||
process.env.NODE_ENV !== "production" ? warning(false, 'Modal', 'Static method not support `getContainer` to be `false` since it do not have context env.') : void 0;
|
||||
}
|
||||
}
|
||||
return /*#__PURE__*/React.createElement(ConfirmDialog, {
|
||||
...props,
|
||||
rootPrefixCls: rootPrefixCls,
|
||||
prefixCls: prefixCls,
|
||||
iconPrefixCls: config.iconPrefixCls,
|
||||
theme: config.theme,
|
||||
direction: direction ?? config.direction,
|
||||
locale: config.locale?.Modal ?? runtimeLocale,
|
||||
getContainer: mergedGetContainer
|
||||
});
|
||||
};
|
||||
export default function confirm(config) {
|
||||
const global = globalConfig();
|
||||
if (process.env.NODE_ENV !== 'production' && !global.holderRender) {
|
||||
warnContext('Modal');
|
||||
}
|
||||
const container = document.createDocumentFragment();
|
||||
let currentConfig = {
|
||||
...config,
|
||||
close,
|
||||
open: true
|
||||
};
|
||||
let timeoutId;
|
||||
function destroy(...args) {
|
||||
const triggerCancel = args.some(param => param?.triggerCancel);
|
||||
if (triggerCancel) {
|
||||
config.onCancel?.(() => {}, ...args.slice(1));
|
||||
}
|
||||
for (let i = 0; i < destroyFns.length; i++) {
|
||||
const fn = destroyFns[i];
|
||||
if (fn === close) {
|
||||
destroyFns.splice(i, 1);
|
||||
break;
|
||||
}
|
||||
}
|
||||
unmount(container).then(() => {
|
||||
// Do nothing
|
||||
});
|
||||
}
|
||||
const scheduleRender = props => {
|
||||
clearTimeout(timeoutId);
|
||||
/**
|
||||
* https://github.com/ant-design/ant-design/issues/23623
|
||||
*
|
||||
* Sync render blocks React event. Let's make this async.
|
||||
*/
|
||||
timeoutId = setTimeout(() => {
|
||||
const rootPrefixCls = global.getPrefixCls(undefined, getRootPrefixCls());
|
||||
const iconPrefixCls = global.getIconPrefixCls();
|
||||
const theme = global.getTheme();
|
||||
const dom = /*#__PURE__*/React.createElement(ConfirmDialogWrapper, {
|
||||
...props
|
||||
});
|
||||
render(/*#__PURE__*/React.createElement(ConfigProvider, {
|
||||
prefixCls: rootPrefixCls,
|
||||
iconPrefixCls: iconPrefixCls,
|
||||
theme: theme
|
||||
}, typeof global.holderRender === 'function' ? global.holderRender(dom) : dom), container);
|
||||
});
|
||||
};
|
||||
function close(...args) {
|
||||
currentConfig = {
|
||||
...currentConfig,
|
||||
open: false,
|
||||
afterClose: () => {
|
||||
if (typeof config.afterClose === 'function') {
|
||||
config.afterClose();
|
||||
}
|
||||
// @ts-ignore
|
||||
destroy.apply(this, args);
|
||||
}
|
||||
};
|
||||
scheduleRender(currentConfig);
|
||||
}
|
||||
function update(configUpdate) {
|
||||
if (typeof configUpdate === 'function') {
|
||||
currentConfig = configUpdate(currentConfig);
|
||||
} else {
|
||||
currentConfig = {
|
||||
...currentConfig,
|
||||
...configUpdate
|
||||
};
|
||||
}
|
||||
scheduleRender(currentConfig);
|
||||
}
|
||||
scheduleRender(currentConfig);
|
||||
destroyFns.push(close);
|
||||
return {
|
||||
destroy: close,
|
||||
update
|
||||
};
|
||||
}
|
||||
export function withWarn(props) {
|
||||
return {
|
||||
...props,
|
||||
type: 'warning'
|
||||
};
|
||||
}
|
||||
export function withInfo(props) {
|
||||
return {
|
||||
...props,
|
||||
type: 'info'
|
||||
};
|
||||
}
|
||||
export function withSuccess(props) {
|
||||
return {
|
||||
...props,
|
||||
type: 'success'
|
||||
};
|
||||
}
|
||||
export function withError(props) {
|
||||
return {
|
||||
...props,
|
||||
type: 'error'
|
||||
};
|
||||
}
|
||||
export function withConfirm(props) {
|
||||
return {
|
||||
...props,
|
||||
type: 'confirm'
|
||||
};
|
||||
}
|
||||
export function modalGlobalConfig({
|
||||
rootPrefixCls
|
||||
}) {
|
||||
process.env.NODE_ENV !== "production" ? warning(false, 'Modal', 'Modal.config is deprecated. Please use ConfigProvider.config instead.') : void 0;
|
||||
defaultRootPrefixCls = rootPrefixCls;
|
||||
}
|
||||
+8
@@ -0,0 +1,8 @@
|
||||
import React from 'react';
|
||||
import type { ConfirmCancelBtnProps } from './components/ConfirmCancelBtn';
|
||||
import type { ConfirmOkBtnProps } from './components/ConfirmOkBtn';
|
||||
import type { NormalCancelBtnProps } from './components/NormalCancelBtn';
|
||||
import type { NormalOkBtnProps } from './components/NormalOkBtn';
|
||||
export type ModalContextProps = NormalCancelBtnProps & NormalOkBtnProps & ConfirmOkBtnProps & ConfirmCancelBtnProps;
|
||||
export declare const ModalContext: React.Context<ModalContextProps>;
|
||||
export declare const ModalContextProvider: React.Provider<ModalContextProps>;
|
||||
+5
@@ -0,0 +1,5 @@
|
||||
import React from 'react';
|
||||
export const ModalContext = /*#__PURE__*/React.createContext({});
|
||||
export const {
|
||||
Provider: ModalContextProvider
|
||||
} = ModalContext;
|
||||
+2
@@ -0,0 +1,2 @@
|
||||
declare const destroyFns: Array<() => void>;
|
||||
export default destroyFns;
|
||||
+2
@@ -0,0 +1,2 @@
|
||||
const destroyFns = [];
|
||||
export default destroyFns;
|
||||
+15
@@ -0,0 +1,15 @@
|
||||
import type { ModalStaticFunctions } from './confirm';
|
||||
import { modalGlobalConfig } from './confirm';
|
||||
import OriginModal from './Modal';
|
||||
import PurePanel from './PurePanel';
|
||||
import useModal from './useModal';
|
||||
export type { ModalFuncProps, ModalLocale, ModalProps, ModalSemanticClassNames, ModalSemanticName, ModalSemanticStyles, } from './interface';
|
||||
type ModalType = typeof OriginModal & ModalStaticFunctions & {
|
||||
useModal: typeof useModal;
|
||||
destroyAll: () => void;
|
||||
config: typeof modalGlobalConfig;
|
||||
/** @private Internal Component. Do not use in your production. */
|
||||
_InternalPanelDoNotUseOrYouWillBeFired: typeof PurePanel;
|
||||
};
|
||||
declare const Modal: ModalType;
|
||||
export default Modal;
|
||||
+40
@@ -0,0 +1,40 @@
|
||||
"use client";
|
||||
|
||||
import confirm, { modalGlobalConfig, withConfirm, withError, withInfo, withSuccess, withWarn } from './confirm';
|
||||
import destroyFns from './destroyFns';
|
||||
import OriginModal from './Modal';
|
||||
import PurePanel from './PurePanel';
|
||||
import useModal from './useModal';
|
||||
function modalWarn(props) {
|
||||
return confirm(withWarn(props));
|
||||
}
|
||||
const Modal = OriginModal;
|
||||
Modal.useModal = useModal;
|
||||
Modal.info = function infoFn(props) {
|
||||
return confirm(withInfo(props));
|
||||
};
|
||||
Modal.success = function successFn(props) {
|
||||
return confirm(withSuccess(props));
|
||||
};
|
||||
Modal.error = function errorFn(props) {
|
||||
return confirm(withError(props));
|
||||
};
|
||||
Modal.warning = modalWarn;
|
||||
Modal.warn = modalWarn;
|
||||
Modal.confirm = function confirmFn(props) {
|
||||
return confirm(withConfirm(props));
|
||||
};
|
||||
Modal.destroyAll = function destroyAllFn() {
|
||||
while (destroyFns.length) {
|
||||
const close = destroyFns.pop();
|
||||
if (close) {
|
||||
close();
|
||||
}
|
||||
}
|
||||
};
|
||||
Modal.config = modalGlobalConfig;
|
||||
Modal._InternalPanelDoNotUseOrYouWillBeFired = PurePanel;
|
||||
if (process.env.NODE_ENV !== 'production') {
|
||||
Modal.displayName = 'Modal';
|
||||
}
|
||||
export default Modal;
|
||||
+167
@@ -0,0 +1,167 @@
|
||||
import type React from 'react';
|
||||
import type { DialogProps } from '@rc-component/dialog';
|
||||
import type { ClosableType, MaskType, SemanticClassNamesType, SemanticStylesType } from '../_util/hooks';
|
||||
import type { Breakpoint } from '../_util/responsiveObserver';
|
||||
import type { ButtonProps, LegacyButtonType } from '../button/Button';
|
||||
import type { DirectionType } from '../config-provider';
|
||||
import type { FocusableConfig, OmitFocusType } from '../drawer/useFocusable';
|
||||
export type ModalSemanticName = keyof ModalSemanticClassNames & keyof ModalSemanticStyles;
|
||||
export type ModalSemanticClassNames = {
|
||||
root?: string;
|
||||
header?: string;
|
||||
body?: string;
|
||||
footer?: string;
|
||||
container?: string;
|
||||
title?: string;
|
||||
wrapper?: string;
|
||||
mask?: string;
|
||||
};
|
||||
export type ModalSemanticStyles = {
|
||||
root?: React.CSSProperties;
|
||||
header?: React.CSSProperties;
|
||||
body?: React.CSSProperties;
|
||||
footer?: React.CSSProperties;
|
||||
container?: React.CSSProperties;
|
||||
title?: React.CSSProperties;
|
||||
wrapper?: React.CSSProperties;
|
||||
mask?: React.CSSProperties;
|
||||
};
|
||||
export type ModalClassNamesType = SemanticClassNamesType<ModalProps, ModalSemanticClassNames>;
|
||||
export type ModalStylesType = SemanticStylesType<ModalProps, ModalSemanticStyles>;
|
||||
interface ModalCommonProps extends Omit<DialogProps, 'footer' | 'width' | 'onClose' | 'animation' | 'maskAnimation' | 'transitionName' | 'maskTransitionName' | 'mask' | 'classNames' | 'styles' | OmitFocusType> {
|
||||
footer?: React.ReactNode | ((originNode: React.ReactNode, extra: {
|
||||
OkBtn: React.FC;
|
||||
CancelBtn: React.FC;
|
||||
}) => React.ReactNode);
|
||||
closable?: boolean | (Exclude<ClosableType, boolean> & {
|
||||
onClose?: () => void;
|
||||
afterClose?: () => void;
|
||||
});
|
||||
classNames?: ModalClassNamesType;
|
||||
styles?: ModalStylesType;
|
||||
}
|
||||
export interface ModalProps extends ModalCommonProps {
|
||||
/** Whether the modal dialog is visible or not */
|
||||
open?: boolean;
|
||||
/** Whether to apply loading visual effect for OK button or not */
|
||||
confirmLoading?: boolean;
|
||||
/** The modal dialog's title */
|
||||
title?: React.ReactNode;
|
||||
/** Specify a function that will be called when a user clicks the OK button */
|
||||
onOk?: (e: React.MouseEvent<HTMLButtonElement>) => void;
|
||||
/** Specify a function that will be called when a user clicks mask, close button on top right or Cancel button, or presses Esc key */
|
||||
onCancel?: (e: React.MouseEvent<HTMLButtonElement> | React.KeyboardEvent<HTMLElement>) => void;
|
||||
afterClose?: () => void;
|
||||
/** Callback when the animation ends when Modal is turned on and off */
|
||||
afterOpenChange?: (open: boolean) => void;
|
||||
/** Centered Modal */
|
||||
centered?: boolean;
|
||||
/** Width of the modal dialog */
|
||||
width?: string | number | Partial<Record<Breakpoint, string | number>>;
|
||||
/** Text of the OK button */
|
||||
okText?: React.ReactNode;
|
||||
/** Button `type` of the OK button */
|
||||
okType?: LegacyButtonType;
|
||||
/** Text of the Cancel button */
|
||||
cancelText?: React.ReactNode;
|
||||
/**
|
||||
* @deprecated Please use `mask.closable` instead
|
||||
* @description Whether to close the modal dialog when the mask (area outside the modal) is clicked
|
||||
*/
|
||||
maskClosable?: boolean;
|
||||
/** Force render Modal */
|
||||
forceRender?: boolean;
|
||||
okButtonProps?: ButtonProps;
|
||||
cancelButtonProps?: ButtonProps;
|
||||
/** @deprecated Please use `destroyOnHidden` instead */
|
||||
destroyOnClose?: boolean;
|
||||
/**
|
||||
* @since 5.25.0
|
||||
*/
|
||||
destroyOnHidden?: boolean;
|
||||
style?: React.CSSProperties;
|
||||
wrapClassName?: string;
|
||||
maskTransitionName?: string;
|
||||
transitionName?: string;
|
||||
className?: string;
|
||||
rootClassName?: string;
|
||||
rootStyle?: React.CSSProperties;
|
||||
getContainer?: string | HTMLElement | getContainerFunc | false;
|
||||
zIndex?: number;
|
||||
/** @deprecated Please use `styles.body` instead */
|
||||
bodyStyle?: React.CSSProperties;
|
||||
/** @deprecated Please use `styles.mask` instead */
|
||||
maskStyle?: React.CSSProperties;
|
||||
mask?: MaskType;
|
||||
keyboard?: boolean;
|
||||
wrapProps?: any;
|
||||
prefixCls?: string;
|
||||
closeIcon?: React.ReactNode;
|
||||
modalRender?: (node: React.ReactNode) => React.ReactNode;
|
||||
children?: React.ReactNode;
|
||||
mousePosition?: MousePosition;
|
||||
/**
|
||||
* @since 5.18.0
|
||||
*/
|
||||
loading?: boolean;
|
||||
/** @deprecated Please use `focusable.focusTriggerAfterClose` instead */
|
||||
focusTriggerAfterClose?: boolean;
|
||||
focusable?: FocusableConfig;
|
||||
}
|
||||
type getContainerFunc = () => HTMLElement;
|
||||
export interface ModalFuncProps extends ModalCommonProps {
|
||||
prefixCls?: string;
|
||||
className?: string;
|
||||
rootClassName?: string;
|
||||
open?: boolean;
|
||||
title?: React.ReactNode;
|
||||
content?: React.ReactNode;
|
||||
onOk?: (...args: any[]) => any;
|
||||
onCancel?: (...args: any[]) => any;
|
||||
afterClose?: () => void;
|
||||
okButtonProps?: ButtonProps;
|
||||
cancelButtonProps?: ButtonProps;
|
||||
centered?: boolean;
|
||||
width?: string | number;
|
||||
okText?: React.ReactNode;
|
||||
okType?: LegacyButtonType;
|
||||
cancelText?: React.ReactNode;
|
||||
icon?: React.ReactNode;
|
||||
/** @deprecated Please use `mask.closable` instead */
|
||||
maskClosable?: boolean;
|
||||
mask?: MaskType;
|
||||
zIndex?: number;
|
||||
okCancel?: boolean;
|
||||
style?: React.CSSProperties;
|
||||
wrapClassName?: string;
|
||||
/** @deprecated Please use `styles.mask` instead */
|
||||
maskStyle?: React.CSSProperties;
|
||||
type?: 'info' | 'success' | 'error' | 'warn' | 'warning' | 'confirm';
|
||||
keyboard?: boolean;
|
||||
getContainer?: string | HTMLElement | getContainerFunc | false;
|
||||
transitionName?: string;
|
||||
maskTransitionName?: string;
|
||||
direction?: DirectionType;
|
||||
/** @deprecated Please use `styles.body` instead */
|
||||
bodyStyle?: React.CSSProperties;
|
||||
closeIcon?: React.ReactNode;
|
||||
footer?: ModalProps['footer'];
|
||||
modalRender?: (node: React.ReactNode) => React.ReactNode;
|
||||
/** @deprecated Please use `focusable.focusTriggerAfterClose` instead */
|
||||
focusTriggerAfterClose?: boolean;
|
||||
/** @deprecated Please use `focusable.autoFocusButton` instead */
|
||||
autoFocusButton?: null | 'ok' | 'cancel';
|
||||
focusable?: FocusableConfig & {
|
||||
autoFocusButton?: null | 'ok' | 'cancel';
|
||||
};
|
||||
}
|
||||
export interface ModalLocale {
|
||||
okText: string;
|
||||
cancelText: string;
|
||||
justOkText: string;
|
||||
}
|
||||
export type MousePosition = {
|
||||
x: number;
|
||||
y: number;
|
||||
} | null;
|
||||
export {};
|
||||
+1
@@ -0,0 +1 @@
|
||||
export {};
|
||||
+3
@@ -0,0 +1,3 @@
|
||||
import type { ModalLocale } from './interface';
|
||||
export declare function changeConfirmLocale(newLocale?: ModalLocale): (() => void) | undefined;
|
||||
export declare function getConfirmLocale(): ModalLocale;
|
||||
+28
@@ -0,0 +1,28 @@
|
||||
import defaultLocale from '../locale/en_US';
|
||||
let runtimeLocale = {
|
||||
...defaultLocale.Modal
|
||||
};
|
||||
let localeList = [];
|
||||
const generateLocale = () => localeList.reduce((merged, locale) => ({
|
||||
...merged,
|
||||
...locale
|
||||
}), defaultLocale.Modal);
|
||||
export function changeConfirmLocale(newLocale) {
|
||||
if (newLocale) {
|
||||
const cloneLocale = {
|
||||
...newLocale
|
||||
};
|
||||
localeList.push(cloneLocale);
|
||||
runtimeLocale = generateLocale();
|
||||
return () => {
|
||||
localeList = localeList.filter(locale => locale !== cloneLocale);
|
||||
runtimeLocale = generateLocale();
|
||||
};
|
||||
}
|
||||
runtimeLocale = {
|
||||
...defaultLocale.Modal
|
||||
};
|
||||
}
|
||||
export function getConfirmLocale() {
|
||||
return runtimeLocale;
|
||||
}
|
||||
+9
@@ -0,0 +1,9 @@
|
||||
import React from 'react';
|
||||
import type { ModalProps } from './interface';
|
||||
export declare function renderCloseIcon(prefixCls: string, closeIcon?: React.ReactNode): React.JSX.Element;
|
||||
interface FooterProps {
|
||||
onOk?: React.MouseEventHandler<HTMLButtonElement | HTMLAnchorElement>;
|
||||
onCancel?: ModalProps['onCancel'];
|
||||
}
|
||||
export declare const Footer: React.FC<FooterProps & Pick<ModalProps, 'footer' | 'okText' | 'okType' | 'cancelText' | 'confirmLoading' | 'okButtonProps' | 'cancelButtonProps'>>;
|
||||
export {};
|
||||
+64
@@ -0,0 +1,64 @@
|
||||
"use client";
|
||||
|
||||
import React from 'react';
|
||||
import CloseOutlined from "@ant-design/icons/es/icons/CloseOutlined";
|
||||
import { DisabledContextProvider } from '../config-provider/DisabledContext';
|
||||
import { useLocale } from '../locale';
|
||||
import NormalCancelBtn from './components/NormalCancelBtn';
|
||||
import NormalOkBtn from './components/NormalOkBtn';
|
||||
import { ModalContextProvider } from './context';
|
||||
import { getConfirmLocale } from './locale';
|
||||
export function renderCloseIcon(prefixCls, closeIcon) {
|
||||
return /*#__PURE__*/React.createElement("span", {
|
||||
className: `${prefixCls}-close-x`
|
||||
}, closeIcon || /*#__PURE__*/React.createElement(CloseOutlined, {
|
||||
className: `${prefixCls}-close-icon`
|
||||
}));
|
||||
}
|
||||
export const Footer = props => {
|
||||
const {
|
||||
okText,
|
||||
okType = 'primary',
|
||||
cancelText,
|
||||
confirmLoading,
|
||||
onOk,
|
||||
onCancel,
|
||||
okButtonProps,
|
||||
cancelButtonProps,
|
||||
footer
|
||||
} = props;
|
||||
const [locale] = useLocale('Modal', getConfirmLocale());
|
||||
// ================== Locale Text ==================
|
||||
const okTextLocale = okText || locale?.okText;
|
||||
const cancelTextLocale = cancelText || locale?.cancelText;
|
||||
const memoizedValue = React.useMemo(() => {
|
||||
return {
|
||||
confirmLoading,
|
||||
okButtonProps,
|
||||
cancelButtonProps,
|
||||
okTextLocale,
|
||||
cancelTextLocale,
|
||||
okType,
|
||||
onOk,
|
||||
onCancel
|
||||
};
|
||||
}, [confirmLoading, okButtonProps, cancelButtonProps, okTextLocale, cancelTextLocale, okType, onOk, onCancel]);
|
||||
let footerNode;
|
||||
if (typeof footer === 'function' || typeof footer === 'undefined') {
|
||||
footerNode = /*#__PURE__*/React.createElement(React.Fragment, null, /*#__PURE__*/React.createElement(NormalCancelBtn, null), /*#__PURE__*/React.createElement(NormalOkBtn, null));
|
||||
if (typeof footer === 'function') {
|
||||
footerNode = footer(footerNode, {
|
||||
OkBtn: NormalOkBtn,
|
||||
CancelBtn: NormalCancelBtn
|
||||
});
|
||||
}
|
||||
footerNode = /*#__PURE__*/React.createElement(ModalContextProvider, {
|
||||
value: memoizedValue
|
||||
}, footerNode);
|
||||
} else {
|
||||
footerNode = footer;
|
||||
}
|
||||
return /*#__PURE__*/React.createElement(DisabledContextProvider, {
|
||||
disabled: false
|
||||
}, footerNode);
|
||||
};
|
||||
+2
@@ -0,0 +1,2 @@
|
||||
declare const _default: import("react").FunctionComponent<import("@ant-design/cssinjs-utils/lib/util/genStyleUtils").SubStyleComponentProps>;
|
||||
export default _default;
|
||||
+106
@@ -0,0 +1,106 @@
|
||||
import { unit } from '@ant-design/cssinjs';
|
||||
import { prepareComponentToken, prepareToken } from '.';
|
||||
import { clearFix } from '../../style';
|
||||
import { genSubStyleComponent } from '../../theme/internal';
|
||||
// ============================= Confirm ==============================
|
||||
const genModalConfirmStyle = token => {
|
||||
const {
|
||||
componentCls,
|
||||
titleFontSize,
|
||||
titleLineHeight,
|
||||
modalConfirmIconSize,
|
||||
fontSize,
|
||||
lineHeight,
|
||||
modalTitleHeight,
|
||||
fontHeight,
|
||||
confirmBodyPadding
|
||||
} = token;
|
||||
const confirmComponentCls = `${componentCls}-confirm`;
|
||||
return {
|
||||
[confirmComponentCls]: {
|
||||
'&-rtl': {
|
||||
direction: 'rtl'
|
||||
},
|
||||
[`${token.antCls}-modal-header`]: {
|
||||
display: 'none'
|
||||
},
|
||||
[`${confirmComponentCls}-body-wrapper`]: {
|
||||
...clearFix()
|
||||
},
|
||||
[`&${componentCls} ${componentCls}-body`]: {
|
||||
padding: confirmBodyPadding
|
||||
},
|
||||
// ====================== Body ======================
|
||||
[`${confirmComponentCls}-body`]: {
|
||||
display: 'flex',
|
||||
flexWrap: 'nowrap',
|
||||
alignItems: 'start',
|
||||
[`> ${token.iconCls}`]: {
|
||||
flex: 'none',
|
||||
fontSize: modalConfirmIconSize,
|
||||
marginInlineEnd: token.confirmIconMarginInlineEnd,
|
||||
marginTop: token.calc(token.calc(fontHeight).sub(modalConfirmIconSize).equal()).div(2).equal()
|
||||
},
|
||||
[`&-has-title > ${token.iconCls}`]: {
|
||||
marginTop: token.calc(token.calc(modalTitleHeight).sub(modalConfirmIconSize).equal()).div(2).equal()
|
||||
}
|
||||
},
|
||||
[`${confirmComponentCls}-paragraph`]: {
|
||||
display: 'flex',
|
||||
flexDirection: 'column',
|
||||
flex: 'auto',
|
||||
rowGap: token.marginXS,
|
||||
// https://github.com/ant-design/ant-design/issues/51912
|
||||
maxWidth: `calc(100% - ${unit(token.marginSM)})`
|
||||
},
|
||||
[`${confirmComponentCls}-body-no-icon ${confirmComponentCls}-paragraph`]: {
|
||||
maxWidth: '100%'
|
||||
},
|
||||
// https://github.com/ant-design/ant-design/issues/48159
|
||||
[`${token.iconCls} + ${confirmComponentCls}-paragraph`]: {
|
||||
maxWidth: `calc(100% - ${unit(token.calc(token.modalConfirmIconSize).add(token.marginSM).equal())})`
|
||||
},
|
||||
[`${confirmComponentCls}-title`]: {
|
||||
color: token.colorTextHeading,
|
||||
fontWeight: token.fontWeightStrong,
|
||||
fontSize: titleFontSize,
|
||||
lineHeight: titleLineHeight
|
||||
},
|
||||
[`${confirmComponentCls}-container`]: {
|
||||
color: token.colorText,
|
||||
fontSize,
|
||||
lineHeight
|
||||
},
|
||||
// ===================== Footer =====================
|
||||
[`${confirmComponentCls}-btns`]: {
|
||||
textAlign: 'end',
|
||||
marginTop: token.confirmBtnsMarginTop,
|
||||
[`${token.antCls}-btn + ${token.antCls}-btn`]: {
|
||||
marginBottom: 0,
|
||||
marginInlineStart: token.marginXS
|
||||
}
|
||||
}
|
||||
},
|
||||
[`${confirmComponentCls}-error ${confirmComponentCls}-body > ${token.iconCls}`]: {
|
||||
color: token.colorError
|
||||
},
|
||||
[`${confirmComponentCls}-warning ${confirmComponentCls}-body > ${token.iconCls},
|
||||
${confirmComponentCls}-confirm ${confirmComponentCls}-body > ${token.iconCls}`]: {
|
||||
color: token.colorWarning
|
||||
},
|
||||
[`${confirmComponentCls}-info ${confirmComponentCls}-body > ${token.iconCls}`]: {
|
||||
color: token.colorInfo
|
||||
},
|
||||
[`${confirmComponentCls}-success ${confirmComponentCls}-body > ${token.iconCls}`]: {
|
||||
color: token.colorSuccess
|
||||
}
|
||||
};
|
||||
};
|
||||
// ============================== Export ==============================
|
||||
export default genSubStyleComponent(['Modal', 'confirm'], token => {
|
||||
const modalToken = prepareToken(token);
|
||||
return genModalConfirmStyle(modalToken);
|
||||
}, prepareComponentToken, {
|
||||
// confirm is weak than modal since no conflict here
|
||||
order: -1000
|
||||
});
|
||||
+110
@@ -0,0 +1,110 @@
|
||||
import type { AliasToken, FullToken, GenerateStyle, GenStyleFn, GlobalToken, TokenWithCommonCls } from '../../theme/internal';
|
||||
/** Component only token. Which will handle additional calculation of alias token */
|
||||
export interface ComponentToken {
|
||||
/**
|
||||
* @desc 顶部背景色
|
||||
* @descEN Background color of header
|
||||
*/
|
||||
headerBg: string;
|
||||
/**
|
||||
* @desc 标题行高
|
||||
* @descEN Line height of title
|
||||
*/
|
||||
titleLineHeight: number | string;
|
||||
/**
|
||||
* @desc 标题字体大小
|
||||
* @descEN Font size of title
|
||||
*/
|
||||
titleFontSize: number;
|
||||
/**
|
||||
* @desc 标题字体颜色
|
||||
* @descEN Font color of title
|
||||
*/
|
||||
titleColor: string;
|
||||
/**
|
||||
* @desc 内容区域背景色
|
||||
* @descEN Background color of content
|
||||
*/
|
||||
contentBg: string;
|
||||
/**
|
||||
* @desc 底部区域背景色
|
||||
* @descEN Background color of footer
|
||||
*/
|
||||
footerBg: string;
|
||||
}
|
||||
/**
|
||||
* @desc Modal 组件的 Token
|
||||
* @descEN Token for Modal component
|
||||
*/
|
||||
export interface ModalToken extends FullToken<'Modal'> {
|
||||
/**
|
||||
* @desc 模态框头部高度
|
||||
* @descEN Height of modal header
|
||||
*/
|
||||
modalHeaderHeight: number | string;
|
||||
/**
|
||||
* @desc 模态框底部边框颜色
|
||||
* @descEN Border color of modal footer
|
||||
*/
|
||||
modalFooterBorderColorSplit: string;
|
||||
/**
|
||||
* @desc 模态框底部边框样式
|
||||
* @descEN Border style of modal footer
|
||||
*/
|
||||
modalFooterBorderStyle: string;
|
||||
/**
|
||||
* @desc 模态框底部边框宽度
|
||||
* @descEN Border width of modal footer
|
||||
*/
|
||||
modalFooterBorderWidth: number | string;
|
||||
/**
|
||||
* @desc 模态框关闭图标颜色
|
||||
* @descEN Color of modal close icon
|
||||
*/
|
||||
modalCloseIconColor: string;
|
||||
/**
|
||||
* @desc 模态框关闭图标悬停颜色
|
||||
* @descEN Hover color of modal close icon
|
||||
*/
|
||||
modalCloseIconHoverColor: string;
|
||||
/**
|
||||
* @desc 模态框关闭按钮尺寸
|
||||
* @descEN Size of modal close button
|
||||
*/
|
||||
modalCloseBtnSize: number | string;
|
||||
/**
|
||||
* @desc 模态框确认图标尺寸
|
||||
* @descEN Size of modal confirm icon
|
||||
*/
|
||||
modalConfirmIconSize: number | string;
|
||||
/**
|
||||
* @desc 模态框标题高度
|
||||
* @descEN Height of modal title
|
||||
*/
|
||||
modalTitleHeight: number | string;
|
||||
}
|
||||
export declare const genModalMaskStyle: GenerateStyle<TokenWithCommonCls<AliasToken>>;
|
||||
export declare const prepareToken: (token: Parameters<GenStyleFn<"Modal">>[0]) => ModalToken;
|
||||
export declare const prepareComponentToken: (token: GlobalToken) => {
|
||||
footerBg: string;
|
||||
headerBg: string;
|
||||
titleLineHeight: number;
|
||||
titleFontSize: number;
|
||||
contentBg: string;
|
||||
titleColor: string;
|
||||
contentPadding: string | number;
|
||||
headerPadding: string | number;
|
||||
headerBorderBottom: string;
|
||||
headerMarginBottom: number;
|
||||
bodyPadding: number;
|
||||
footerPadding: string | number;
|
||||
footerBorderTop: string;
|
||||
footerBorderRadius: string | number;
|
||||
footerMarginTop: number;
|
||||
confirmBodyPadding: string | number;
|
||||
confirmIconMarginInlineEnd: number;
|
||||
confirmBtnsMarginTop: number;
|
||||
mask: boolean;
|
||||
};
|
||||
declare const _default: (prefixCls: string, rootCls?: string) => readonly [string, string];
|
||||
export default _default;
|
||||
+320
@@ -0,0 +1,320 @@
|
||||
import _toConsumableArray from "@babel/runtime/helpers/esm/toConsumableArray";
|
||||
import { unit } from '@ant-design/cssinjs';
|
||||
import { getMediaSize } from '../../grid/style';
|
||||
import { genFocusOutline, genFocusStyle, resetComponent } from '../../style';
|
||||
import { initFadeMotion, initZoomMotion } from '../../style/motion';
|
||||
import { genStyleHooks, mergeToken } from '../../theme/internal';
|
||||
function box(position) {
|
||||
return {
|
||||
position,
|
||||
inset: 0
|
||||
};
|
||||
}
|
||||
export const genModalMaskStyle = token => {
|
||||
const {
|
||||
componentCls,
|
||||
antCls
|
||||
} = token;
|
||||
return [{
|
||||
[`${componentCls}-root`]: {
|
||||
[`${componentCls}${antCls}-zoom-enter, ${componentCls}${antCls}-zoom-appear`]: {
|
||||
// reset scale avoid mousePosition bug
|
||||
transform: 'none',
|
||||
opacity: 0,
|
||||
animationDuration: token.motionDurationSlow,
|
||||
// https://github.com/ant-design/ant-design/issues/11777
|
||||
userSelect: 'none'
|
||||
},
|
||||
// https://github.com/ant-design/ant-design/issues/37329
|
||||
// https://github.com/ant-design/ant-design/issues/40272
|
||||
[`${componentCls}${antCls}-zoom-leave ${componentCls}-container`]: {
|
||||
pointerEvents: 'none'
|
||||
},
|
||||
[`${componentCls}-mask`]: {
|
||||
...box('fixed'),
|
||||
zIndex: token.zIndexPopupBase,
|
||||
height: '100%',
|
||||
backgroundColor: token.colorBgMask,
|
||||
pointerEvents: 'none',
|
||||
[`&${componentCls}-mask-blur`]: {
|
||||
backdropFilter: 'blur(4px)'
|
||||
},
|
||||
[`${componentCls}-hidden`]: {
|
||||
display: 'none'
|
||||
}
|
||||
},
|
||||
[`${componentCls}-wrap`]: {
|
||||
...box('fixed'),
|
||||
zIndex: token.zIndexPopupBase,
|
||||
overflow: 'auto',
|
||||
outline: 0,
|
||||
WebkitOverflowScrolling: 'touch'
|
||||
}
|
||||
}
|
||||
}, {
|
||||
[`${componentCls}-root`]: initFadeMotion(token)
|
||||
}];
|
||||
};
|
||||
const genModalStyle = token => {
|
||||
const {
|
||||
componentCls,
|
||||
motionDurationMid
|
||||
} = token;
|
||||
return [
|
||||
// ======================== Root =========================
|
||||
{
|
||||
[`${componentCls}-root`]: {
|
||||
[`${componentCls}-wrap-rtl`]: {
|
||||
direction: 'rtl'
|
||||
},
|
||||
[`${componentCls}-centered`]: {
|
||||
textAlign: 'center',
|
||||
'&::before': {
|
||||
display: 'inline-block',
|
||||
width: 0,
|
||||
height: '100%',
|
||||
verticalAlign: 'middle',
|
||||
content: '""'
|
||||
},
|
||||
[componentCls]: {
|
||||
top: 0,
|
||||
display: 'inline-block',
|
||||
paddingBottom: 0,
|
||||
textAlign: 'start',
|
||||
verticalAlign: 'middle'
|
||||
}
|
||||
},
|
||||
[`@media (max-width: ${token.screenSMMax}px)`]: {
|
||||
[componentCls]: {
|
||||
maxWidth: 'calc(100vw - 16px)',
|
||||
margin: `${unit(token.marginXS)} auto`
|
||||
},
|
||||
[`${componentCls}-centered`]: {
|
||||
[componentCls]: {
|
||||
flex: 1
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
// ======================== Modal ========================
|
||||
{
|
||||
[componentCls]: {
|
||||
...resetComponent(token),
|
||||
pointerEvents: 'none',
|
||||
position: 'relative',
|
||||
top: 100,
|
||||
width: 'auto',
|
||||
maxWidth: `calc(100vw - ${unit(token.calc(token.margin).mul(2).equal())})`,
|
||||
margin: '0 auto',
|
||||
'&:focus-visible': {
|
||||
borderRadius: token.borderRadiusLG,
|
||||
...genFocusOutline(token)
|
||||
},
|
||||
[`${componentCls}-title`]: {
|
||||
margin: 0,
|
||||
color: token.titleColor,
|
||||
fontWeight: token.fontWeightStrong,
|
||||
fontSize: token.titleFontSize,
|
||||
lineHeight: token.titleLineHeight,
|
||||
wordWrap: 'break-word'
|
||||
},
|
||||
[`${componentCls}-container`]: {
|
||||
position: 'relative',
|
||||
backgroundColor: token.contentBg,
|
||||
backgroundClip: 'padding-box',
|
||||
border: 0,
|
||||
borderRadius: token.borderRadiusLG,
|
||||
boxShadow: token.boxShadow,
|
||||
pointerEvents: 'auto',
|
||||
padding: token.contentPadding
|
||||
},
|
||||
[`${componentCls}-close`]: {
|
||||
position: 'absolute',
|
||||
top: token.calc(token.modalHeaderHeight).sub(token.modalCloseBtnSize).div(2).equal(),
|
||||
insetInlineEnd: token.calc(token.modalHeaderHeight).sub(token.modalCloseBtnSize).div(2).equal(),
|
||||
zIndex: token.calc(token.zIndexPopupBase).add(10).equal(),
|
||||
padding: 0,
|
||||
color: token.modalCloseIconColor,
|
||||
fontWeight: token.fontWeightStrong,
|
||||
lineHeight: 1,
|
||||
textDecoration: 'none',
|
||||
background: 'transparent',
|
||||
borderRadius: token.borderRadiusSM,
|
||||
width: token.modalCloseBtnSize,
|
||||
height: token.modalCloseBtnSize,
|
||||
border: 0,
|
||||
outline: 0,
|
||||
cursor: 'pointer',
|
||||
transition: ['color', 'background-color'].map(prop => `${prop} ${motionDurationMid}`).join(', '),
|
||||
'&-x': {
|
||||
display: 'flex',
|
||||
fontSize: token.fontSizeLG,
|
||||
fontStyle: 'normal',
|
||||
lineHeight: unit(token.modalCloseBtnSize),
|
||||
justifyContent: 'center',
|
||||
textTransform: 'none',
|
||||
textRendering: 'auto'
|
||||
},
|
||||
'&:disabled': {
|
||||
pointerEvents: 'none'
|
||||
},
|
||||
'&:hover': {
|
||||
color: token.modalCloseIconHoverColor,
|
||||
backgroundColor: token.colorBgTextHover,
|
||||
textDecoration: 'none'
|
||||
},
|
||||
'&:active': {
|
||||
backgroundColor: token.colorBgTextActive
|
||||
},
|
||||
...genFocusStyle(token)
|
||||
},
|
||||
[`${componentCls}-header`]: {
|
||||
color: token.colorText,
|
||||
background: token.headerBg,
|
||||
borderRadius: `${unit(token.borderRadiusLG)} ${unit(token.borderRadiusLG)} 0 0`,
|
||||
marginBottom: token.headerMarginBottom,
|
||||
padding: token.headerPadding,
|
||||
borderBottom: token.headerBorderBottom
|
||||
},
|
||||
[`${componentCls}-body`]: {
|
||||
fontSize: token.fontSize,
|
||||
lineHeight: token.lineHeight,
|
||||
wordWrap: 'break-word',
|
||||
padding: token.bodyPadding,
|
||||
[`${componentCls}-body-skeleton`]: {
|
||||
width: '100%',
|
||||
height: '100%',
|
||||
display: 'flex',
|
||||
justifyContent: 'center',
|
||||
alignItems: 'center',
|
||||
margin: `${unit(token.margin)} auto`
|
||||
}
|
||||
},
|
||||
[`${componentCls}-footer`]: {
|
||||
textAlign: 'end',
|
||||
background: token.footerBg,
|
||||
marginTop: token.footerMarginTop,
|
||||
padding: token.footerPadding,
|
||||
borderTop: token.footerBorderTop,
|
||||
borderRadius: token.footerBorderRadius,
|
||||
[`> ${token.antCls}-btn + ${token.antCls}-btn`]: {
|
||||
marginInlineStart: token.marginXS
|
||||
}
|
||||
},
|
||||
[`${componentCls}-open`]: {
|
||||
overflow: 'hidden'
|
||||
}
|
||||
}
|
||||
},
|
||||
// ======================== Pure =========================
|
||||
{
|
||||
[`${componentCls}-pure-panel`]: {
|
||||
top: 'auto',
|
||||
padding: 0,
|
||||
display: 'flex',
|
||||
flexDirection: 'column',
|
||||
[`${componentCls}-container,
|
||||
${componentCls}-body,
|
||||
${componentCls}-confirm-body-wrapper`]: {
|
||||
display: 'flex',
|
||||
flexDirection: 'column',
|
||||
flex: 'auto'
|
||||
},
|
||||
[`${componentCls}-confirm-body`]: {
|
||||
marginBottom: 'auto'
|
||||
}
|
||||
}
|
||||
}];
|
||||
};
|
||||
const genRTLStyle = token => {
|
||||
const {
|
||||
componentCls
|
||||
} = token;
|
||||
return {
|
||||
[`${componentCls}-root`]: {
|
||||
[`${componentCls}-wrap-rtl`]: {
|
||||
direction: 'rtl',
|
||||
[`${componentCls}-confirm-body`]: {
|
||||
direction: 'rtl'
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
};
|
||||
const genResponsiveWidthStyle = token => {
|
||||
const {
|
||||
componentCls
|
||||
} = token;
|
||||
const oriGridMediaSizesMap = getMediaSize(token);
|
||||
const gridMediaSizesMap = {
|
||||
...oriGridMediaSizesMap
|
||||
};
|
||||
delete gridMediaSizesMap.xs;
|
||||
const cssVarPrefix = `--${componentCls.replace('.', '')}-`;
|
||||
const responsiveStyles = Object.keys(gridMediaSizesMap).map(key => ({
|
||||
[`@media (min-width: ${unit(gridMediaSizesMap[key])})`]: {
|
||||
width: `var(${cssVarPrefix}${key}-width)`
|
||||
}
|
||||
}));
|
||||
return {
|
||||
[`${componentCls}-root`]: {
|
||||
[componentCls]: [].concat(_toConsumableArray(Object.keys(oriGridMediaSizesMap).map((currentKey, index) => {
|
||||
const previousKey = Object.keys(oriGridMediaSizesMap)[index - 1];
|
||||
return previousKey ? {
|
||||
[`${cssVarPrefix}${currentKey}-width`]: `var(${cssVarPrefix}${previousKey}-width)`
|
||||
} : null;
|
||||
})), [{
|
||||
width: `var(${cssVarPrefix}xs-width)`
|
||||
}], _toConsumableArray(responsiveStyles))
|
||||
}
|
||||
};
|
||||
};
|
||||
// ============================== Export ==============================
|
||||
export const prepareToken = token => {
|
||||
const headerPaddingVertical = token.padding;
|
||||
const headerFontSize = token.fontSizeHeading5;
|
||||
const headerLineHeight = token.lineHeightHeading5;
|
||||
const modalToken = mergeToken(token, {
|
||||
modalHeaderHeight: token.calc(token.calc(headerLineHeight).mul(headerFontSize).equal()).add(token.calc(headerPaddingVertical).mul(2).equal()).equal(),
|
||||
modalFooterBorderColorSplit: token.colorSplit,
|
||||
modalFooterBorderStyle: token.lineType,
|
||||
modalFooterBorderWidth: token.lineWidth,
|
||||
modalCloseIconColor: token.colorIcon,
|
||||
modalCloseIconHoverColor: token.colorIconHover,
|
||||
modalCloseBtnSize: token.controlHeight,
|
||||
modalConfirmIconSize: token.fontHeight,
|
||||
modalTitleHeight: token.calc(token.titleFontSize).mul(token.titleLineHeight).equal()
|
||||
});
|
||||
return modalToken;
|
||||
};
|
||||
export const prepareComponentToken = token => ({
|
||||
footerBg: 'transparent',
|
||||
headerBg: 'transparent',
|
||||
titleLineHeight: token.lineHeightHeading5,
|
||||
titleFontSize: token.fontSizeHeading5,
|
||||
contentBg: token.colorBgElevated,
|
||||
titleColor: token.colorTextHeading,
|
||||
// internal
|
||||
contentPadding: token.wireframe ? 0 : `${unit(token.paddingMD)} ${unit(token.paddingContentHorizontalLG)}`,
|
||||
headerPadding: token.wireframe ? `${unit(token.padding)} ${unit(token.paddingLG)}` : 0,
|
||||
headerBorderBottom: token.wireframe ? `${unit(token.lineWidth)} ${token.lineType} ${token.colorSplit}` : 'none',
|
||||
headerMarginBottom: token.wireframe ? 0 : token.marginXS,
|
||||
bodyPadding: token.wireframe ? token.paddingLG : 0,
|
||||
footerPadding: token.wireframe ? `${unit(token.paddingXS)} ${unit(token.padding)}` : 0,
|
||||
footerBorderTop: token.wireframe ? `${unit(token.lineWidth)} ${token.lineType} ${token.colorSplit}` : 'none',
|
||||
footerBorderRadius: token.wireframe ? `0 0 ${unit(token.borderRadiusLG)} ${unit(token.borderRadiusLG)}` : 0,
|
||||
footerMarginTop: token.wireframe ? 0 : token.marginSM,
|
||||
confirmBodyPadding: token.wireframe ? `${unit(token.padding * 2)} ${unit(token.padding * 2)} ${unit(token.paddingLG)}` : 0,
|
||||
confirmIconMarginInlineEnd: token.wireframe ? token.margin : token.marginSM,
|
||||
confirmBtnsMarginTop: token.wireframe ? token.marginLG : token.marginSM,
|
||||
mask: true
|
||||
});
|
||||
export default genStyleHooks('Modal', token => {
|
||||
const modalToken = prepareToken(token);
|
||||
return [genModalStyle(modalToken), genRTLStyle(modalToken), genModalMaskStyle(modalToken), initZoomMotion(modalToken, 'zoom'), genResponsiveWidthStyle(modalToken)];
|
||||
}, prepareComponentToken, {
|
||||
unitless: {
|
||||
titleLineHeight: true
|
||||
}
|
||||
});
|
||||
+18
@@ -0,0 +1,18 @@
|
||||
import * as React from 'react';
|
||||
import type { ConfigUpdate } from '../confirm';
|
||||
import type { ModalFuncProps } from '../interface';
|
||||
export interface HookModalProps {
|
||||
afterClose: () => void;
|
||||
config: ModalFuncProps;
|
||||
onConfirm?: (confirmed: boolean) => void;
|
||||
/**
|
||||
* Do not throw if is await mode
|
||||
*/
|
||||
isSilent?: () => boolean;
|
||||
}
|
||||
export interface HookModalRef {
|
||||
destroy: () => void;
|
||||
update: (config: ConfigUpdate) => void;
|
||||
}
|
||||
declare const HookModal: React.ForwardRefExoticComponent<HookModalProps & React.RefAttributes<HookModalRef>>;
|
||||
export default HookModal;
|
||||
+60
@@ -0,0 +1,60 @@
|
||||
"use client";
|
||||
|
||||
import * as React from 'react';
|
||||
import { ConfigContext } from '../../config-provider';
|
||||
import defaultLocale from '../../locale/en_US';
|
||||
import useLocale from '../../locale/useLocale';
|
||||
import ConfirmDialog from '../ConfirmDialog';
|
||||
const HookModal = /*#__PURE__*/React.forwardRef((props, ref) => {
|
||||
const {
|
||||
afterClose: hookAfterClose,
|
||||
config,
|
||||
...restProps
|
||||
} = props;
|
||||
const [open, setOpen] = React.useState(true);
|
||||
const [innerConfig, setInnerConfig] = React.useState(config);
|
||||
const {
|
||||
direction,
|
||||
getPrefixCls
|
||||
} = React.useContext(ConfigContext);
|
||||
const prefixCls = getPrefixCls('modal');
|
||||
const rootPrefixCls = getPrefixCls();
|
||||
const afterClose = () => {
|
||||
hookAfterClose();
|
||||
innerConfig.afterClose?.();
|
||||
};
|
||||
const close = (...args) => {
|
||||
setOpen(false);
|
||||
const triggerCancel = args.some(param => param?.triggerCancel);
|
||||
if (triggerCancel) {
|
||||
innerConfig.onCancel?.(() => {}, ...args.slice(1));
|
||||
}
|
||||
};
|
||||
React.useImperativeHandle(ref, () => ({
|
||||
destroy: close,
|
||||
update: newConfig => {
|
||||
setInnerConfig(originConfig => {
|
||||
const nextConfig = typeof newConfig === 'function' ? newConfig(originConfig) : newConfig;
|
||||
return {
|
||||
...originConfig,
|
||||
...nextConfig
|
||||
};
|
||||
});
|
||||
}
|
||||
}));
|
||||
const mergedOkCancel = innerConfig.okCancel ?? innerConfig.type === 'confirm';
|
||||
const [contextLocale] = useLocale('Modal', defaultLocale.Modal);
|
||||
return /*#__PURE__*/React.createElement(ConfirmDialog, {
|
||||
prefixCls: prefixCls,
|
||||
rootPrefixCls: rootPrefixCls,
|
||||
...innerConfig,
|
||||
close: close,
|
||||
open: open,
|
||||
afterClose: afterClose,
|
||||
okText: innerConfig.okText || (mergedOkCancel ? contextLocale?.okText : contextLocale?.justOkText),
|
||||
direction: innerConfig.direction || direction,
|
||||
cancelText: innerConfig.cancelText || contextLocale?.cancelText,
|
||||
...restProps
|
||||
});
|
||||
});
|
||||
export default HookModal;
|
||||
+8
@@ -0,0 +1,8 @@
|
||||
import * as React from 'react';
|
||||
import type { ModalFunc, ModalStaticFunctions } from '../confirm';
|
||||
export type ModalFuncWithPromise = (...args: Parameters<ModalFunc>) => ReturnType<ModalFunc> & {
|
||||
then: <T>(resolve: (confirmed: boolean) => T, reject: VoidFunction) => Promise<T>;
|
||||
};
|
||||
export type HookAPI = Omit<Record<keyof ModalStaticFunctions, ModalFuncWithPromise>, 'warn'>;
|
||||
declare function useModal(): readonly [instance: HookAPI, contextHolder: React.ReactElement];
|
||||
export default useModal;
|
||||
+97
@@ -0,0 +1,97 @@
|
||||
"use client";
|
||||
|
||||
import _toConsumableArray from "@babel/runtime/helpers/esm/toConsumableArray";
|
||||
import * as React from 'react';
|
||||
import { usePatchElement } from '../../_util/hooks';
|
||||
import { withConfirm, withError, withInfo, withSuccess, withWarn } from '../confirm';
|
||||
import destroyFns from '../destroyFns';
|
||||
import HookModal from './HookModal';
|
||||
let uuid = 0;
|
||||
const ElementsHolder = /*#__PURE__*/React.memo(/*#__PURE__*/React.forwardRef((_props, ref) => {
|
||||
const [elements, patchElement] = usePatchElement();
|
||||
React.useImperativeHandle(ref, () => ({
|
||||
patchElement
|
||||
}), [patchElement]);
|
||||
return /*#__PURE__*/React.createElement(React.Fragment, null, elements);
|
||||
}));
|
||||
function useModal() {
|
||||
const holderRef = React.useRef(null);
|
||||
// ========================== Effect ==========================
|
||||
const [actionQueue, setActionQueue] = React.useState([]);
|
||||
React.useEffect(() => {
|
||||
if (actionQueue.length) {
|
||||
const cloneQueue = _toConsumableArray(actionQueue);
|
||||
cloneQueue.forEach(action => {
|
||||
action();
|
||||
});
|
||||
setActionQueue([]);
|
||||
}
|
||||
}, [actionQueue]);
|
||||
// =========================== Hook ===========================
|
||||
const getConfirmFunc = React.useCallback(withFunc => function hookConfirm(config) {
|
||||
uuid += 1;
|
||||
const modalRef = /*#__PURE__*/React.createRef();
|
||||
// Proxy to promise with `onClose`
|
||||
let resolvePromise;
|
||||
const promise = new Promise(resolve => {
|
||||
resolvePromise = resolve;
|
||||
});
|
||||
let silent = false;
|
||||
let closeFunc;
|
||||
const modal = /*#__PURE__*/React.createElement(HookModal, {
|
||||
key: `modal-${uuid}`,
|
||||
config: withFunc(config),
|
||||
ref: modalRef,
|
||||
afterClose: () => {
|
||||
closeFunc?.();
|
||||
},
|
||||
isSilent: () => silent,
|
||||
onConfirm: confirmed => {
|
||||
resolvePromise(confirmed);
|
||||
}
|
||||
});
|
||||
closeFunc = holderRef.current?.patchElement(modal);
|
||||
if (closeFunc) {
|
||||
destroyFns.push(closeFunc);
|
||||
}
|
||||
const instance = {
|
||||
destroy: () => {
|
||||
function destroyAction() {
|
||||
modalRef.current?.destroy();
|
||||
}
|
||||
if (modalRef.current) {
|
||||
destroyAction();
|
||||
} else {
|
||||
setActionQueue(prev => [].concat(_toConsumableArray(prev), [destroyAction]));
|
||||
}
|
||||
},
|
||||
update: newConfig => {
|
||||
function updateAction() {
|
||||
modalRef.current?.update(newConfig);
|
||||
}
|
||||
if (modalRef.current) {
|
||||
updateAction();
|
||||
} else {
|
||||
setActionQueue(prev => [].concat(_toConsumableArray(prev), [updateAction]));
|
||||
}
|
||||
},
|
||||
then: resolve => {
|
||||
silent = true;
|
||||
return promise.then(resolve);
|
||||
}
|
||||
};
|
||||
return instance;
|
||||
}, []);
|
||||
const fns = React.useMemo(() => ({
|
||||
info: getConfirmFunc(withInfo),
|
||||
success: getConfirmFunc(withSuccess),
|
||||
error: getConfirmFunc(withError),
|
||||
warning: getConfirmFunc(withWarn),
|
||||
confirm: getConfirmFunc(withConfirm)
|
||||
}), [getConfirmFunc]);
|
||||
return [fns, /*#__PURE__*/React.createElement(ElementsHolder, {
|
||||
key: "modal-holder",
|
||||
ref: holderRef
|
||||
})];
|
||||
}
|
||||
export default useModal;
|
||||
Reference in New Issue
Block a user