refactor: modal
parent
dfe462ec6f
commit
221b203cbb
|
@ -6,6 +6,7 @@ import type { LegacyButtonType } from '../button/buttonTypes';
|
|||
import { convertLegacyProps } from '../button/buttonTypes';
|
||||
import useDestroyed from './hooks/useDestroyed';
|
||||
import { objectType } from './type';
|
||||
import { findDOMNode } from './props-util';
|
||||
|
||||
const actionButtonProps = {
|
||||
type: {
|
||||
|
@ -38,7 +39,7 @@ export default defineComponent({
|
|||
const isDestroyed = useDestroyed();
|
||||
onMounted(() => {
|
||||
if (props.autofocus) {
|
||||
timeoutId = setTimeout(() => buttonRef.value.$el?.focus());
|
||||
timeoutId = setTimeout(() => findDOMNode(buttonRef.value)?.focus?.());
|
||||
}
|
||||
});
|
||||
onBeforeUnmount(() => {
|
||||
|
|
|
@ -1,10 +1,15 @@
|
|||
import CheckCircleFilled from '@ant-design/icons-vue/CheckCircleFilled';
|
||||
import CloseCircleFilled from '@ant-design/icons-vue/CloseCircleFilled';
|
||||
import ExclamationCircleFilled from '@ant-design/icons-vue/ExclamationCircleFilled';
|
||||
import InfoCircleFilled from '@ant-design/icons-vue/InfoCircleFilled';
|
||||
import classNames from '../_util/classNames';
|
||||
import type { ModalFuncProps } from './Modal';
|
||||
import type { ModalFuncProps, ModalLocale } from './Modal';
|
||||
import Dialog from './Modal';
|
||||
import ActionButton from '../_util/ActionButton';
|
||||
import { defineComponent } from 'vue';
|
||||
import { useLocaleReceiver } from '../locale-provider/LocaleReceiver';
|
||||
import { getTransitionName } from '../_util/transition';
|
||||
import warning from '../_util/warning';
|
||||
|
||||
interface ConfirmDialogProps extends ModalFuncProps {
|
||||
afterClose?: () => void;
|
||||
|
@ -12,6 +17,9 @@ interface ConfirmDialogProps extends ModalFuncProps {
|
|||
autoFocusButton?: null | 'ok' | 'cancel';
|
||||
rootPrefixCls: string;
|
||||
iconPrefixCls?: string;
|
||||
|
||||
/** @private Internal Usage. Do not override this */
|
||||
locale?: ModalLocale;
|
||||
}
|
||||
|
||||
function renderSomeContent(someContent: any) {
|
||||
|
@ -33,6 +41,7 @@ export default defineComponent<ConfirmDialogProps>({
|
|||
'zIndex',
|
||||
'afterClose',
|
||||
'visible',
|
||||
'open',
|
||||
'keyboard',
|
||||
'centered',
|
||||
'getContainer',
|
||||
|
@ -60,9 +69,19 @@ export default defineComponent<ConfirmDialogProps>({
|
|||
'modalRender',
|
||||
'focusTriggerAfterClose',
|
||||
'wrapClassName',
|
||||
'confirmPrefixCls',
|
||||
'footer',
|
||||
] as any,
|
||||
setup(props, { attrs }) {
|
||||
const [locale] = useLocaleReceiver('Modal');
|
||||
|
||||
if (process.env.NODE_ENV !== 'production') {
|
||||
warning(
|
||||
props.visible === undefined,
|
||||
'Modal',
|
||||
`\`visible\` is deprecated, please use \`open\` instead.`,
|
||||
);
|
||||
}
|
||||
return () => {
|
||||
const {
|
||||
icon,
|
||||
|
@ -72,18 +91,18 @@ export default defineComponent<ConfirmDialogProps>({
|
|||
closable = false,
|
||||
zIndex,
|
||||
afterClose,
|
||||
visible,
|
||||
keyboard,
|
||||
centered,
|
||||
getContainer,
|
||||
maskStyle,
|
||||
okButtonProps,
|
||||
cancelButtonProps,
|
||||
okCancel = true,
|
||||
okCancel,
|
||||
width = 416,
|
||||
mask = true,
|
||||
maskClosable = false,
|
||||
type,
|
||||
open,
|
||||
title,
|
||||
content,
|
||||
direction,
|
||||
|
@ -93,7 +112,31 @@ export default defineComponent<ConfirmDialogProps>({
|
|||
rootPrefixCls,
|
||||
bodyStyle,
|
||||
wrapClassName,
|
||||
footer,
|
||||
} = props;
|
||||
|
||||
// Icon
|
||||
let mergedIcon = 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 />;
|
||||
}
|
||||
}
|
||||
const okType = props.okType || 'primary';
|
||||
const prefixCls = props.prefixCls || 'ant-modal';
|
||||
const contentPrefixCls = `${prefixCls}-confirm`;
|
||||
|
@ -101,19 +144,20 @@ export default defineComponent<ConfirmDialogProps>({
|
|||
const okText =
|
||||
renderSomeContent(props.okText) ||
|
||||
(okCancel ? locale.value.okText : locale.value.justOkText);
|
||||
const cancelText = renderSomeContent(props.cancelText) || locale.value.cancelText;
|
||||
const mergedOkCancel = okCancel ?? type === 'confirm';
|
||||
const autoFocusButton =
|
||||
props.autoFocusButton === null ? false : props.autoFocusButton || 'ok';
|
||||
|
||||
const confirmPrefixCls = `${prefixCls}-confirm`;
|
||||
|
||||
const classString = classNames(
|
||||
contentPrefixCls,
|
||||
`${contentPrefixCls}-${type}`,
|
||||
`${prefixCls}-${type}`,
|
||||
{ [`${contentPrefixCls}-rtl`]: direction === 'rtl' },
|
||||
confirmPrefixCls,
|
||||
`${confirmPrefixCls}-${props.type}`,
|
||||
{ [`${confirmPrefixCls}-rtl`]: direction === 'rtl' },
|
||||
attrs.class,
|
||||
);
|
||||
|
||||
const cancelButton = okCancel && (
|
||||
const cancelButton = mergedOkCancel && (
|
||||
<ActionButton
|
||||
actionFn={onCancel}
|
||||
close={close}
|
||||
|
@ -121,7 +165,7 @@ export default defineComponent<ConfirmDialogProps>({
|
|||
buttonProps={cancelButtonProps}
|
||||
prefixCls={`${rootPrefixCls}-btn`}
|
||||
>
|
||||
{cancelText}
|
||||
{renderSomeContent(okCancel) || locale.value.cancelText}
|
||||
</ActionButton>
|
||||
);
|
||||
|
||||
|
@ -130,11 +174,11 @@ export default defineComponent<ConfirmDialogProps>({
|
|||
prefixCls={prefixCls}
|
||||
class={classString}
|
||||
wrapClassName={classNames(
|
||||
{ [`${contentPrefixCls}-centered`]: !!centered },
|
||||
{ [`${confirmPrefixCls}-centered`]: !!centered },
|
||||
wrapClassName,
|
||||
)}
|
||||
onCancel={e => close({ triggerCancel: true }, e)}
|
||||
visible={visible}
|
||||
onCancel={e => close?.({ triggerCancel: true }, e)}
|
||||
open={open}
|
||||
title=""
|
||||
footer=""
|
||||
transitionName={getTransitionName(rootPrefixCls, 'zoom', props.transitionName)}
|
||||
|
@ -157,25 +201,29 @@ export default defineComponent<ConfirmDialogProps>({
|
|||
>
|
||||
<div class={`${contentPrefixCls}-body-wrapper`}>
|
||||
<div class={`${contentPrefixCls}-body`}>
|
||||
{renderSomeContent(icon)}
|
||||
{renderSomeContent(mergedIcon)}
|
||||
{title === undefined ? null : (
|
||||
<span class={`${contentPrefixCls}-title`}>{renderSomeContent(title)}</span>
|
||||
)}
|
||||
<div class={`${contentPrefixCls}-content`}>{renderSomeContent(content)}</div>
|
||||
</div>
|
||||
<div class={`${contentPrefixCls}-btns`}>
|
||||
{cancelButton}
|
||||
<ActionButton
|
||||
type={okType}
|
||||
actionFn={onOk}
|
||||
close={close}
|
||||
autofocus={autoFocusButton === 'ok'}
|
||||
buttonProps={okButtonProps}
|
||||
prefixCls={`${rootPrefixCls}-btn`}
|
||||
>
|
||||
{okText}
|
||||
</ActionButton>
|
||||
</div>
|
||||
{footer !== undefined ? (
|
||||
renderSomeContent(footer)
|
||||
) : (
|
||||
<div class={`${contentPrefixCls}-btns`}>
|
||||
{cancelButton}
|
||||
<ActionButton
|
||||
type={okType}
|
||||
actionFn={onOk}
|
||||
close={close}
|
||||
autofocus={autoFocusButton === 'ok'}
|
||||
buttonProps={okButtonProps}
|
||||
prefixCls={`${rootPrefixCls}-btn`}
|
||||
>
|
||||
{okText}
|
||||
</ActionButton>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</Dialog>
|
||||
);
|
||||
|
|
|
@ -16,8 +16,12 @@ import { objectType } from '../_util/type';
|
|||
import { canUseDocElement } from '../_util/styleChecker';
|
||||
import useConfigInject from '../config-provider/hooks/useConfigInject';
|
||||
import { getTransitionName } from '../_util/transition';
|
||||
import warning from '../_util/warning';
|
||||
import useStyle from './style';
|
||||
|
||||
let mousePosition: { x: number; y: number } | null = null;
|
||||
type MousePosition = { x: number; y: number } | null;
|
||||
|
||||
let mousePosition: MousePosition;
|
||||
// ref: https://github.com/ant-design/ant-design/issues/15795
|
||||
const getClickPosition = (e: MouseEvent) => {
|
||||
mousePosition = {
|
||||
|
@ -37,7 +41,9 @@ if (canUseDocElement()) {
|
|||
|
||||
export const modalProps = () => ({
|
||||
prefixCls: String,
|
||||
/** @deprecated Please use `open` instead. */
|
||||
visible: { type: Boolean, default: undefined },
|
||||
open: { type: Boolean, default: undefined },
|
||||
confirmLoading: { type: Boolean, default: undefined },
|
||||
title: PropTypes.any,
|
||||
closable: { type: Boolean, default: undefined },
|
||||
|
@ -76,6 +82,7 @@ export const modalProps = () => ({
|
|||
wrapProps: Object,
|
||||
focusTriggerAfterClose: { type: Boolean, default: undefined },
|
||||
modalRender: Function as PropType<(arg: { originVNode: VueNode }) => VueNode>,
|
||||
mousePosition: objectType<MousePosition>(),
|
||||
});
|
||||
|
||||
export type ModalProps = Partial<ExtractPropTypes<ReturnType<typeof modalProps>>>;
|
||||
|
@ -83,8 +90,9 @@ export type ModalProps = Partial<ExtractPropTypes<ReturnType<typeof modalProps>>
|
|||
export interface ModalFuncProps {
|
||||
prefixCls?: string;
|
||||
class?: string;
|
||||
visible?: boolean;
|
||||
open?: boolean;
|
||||
title?: string | (() => VueNode) | VueNode;
|
||||
footer?: string | (() => VueNode) | VueNode;
|
||||
closable?: boolean;
|
||||
content?: string | (() => VueNode) | VueNode;
|
||||
// TODO: find out exact types
|
||||
|
@ -123,6 +131,9 @@ export interface ModalFuncProps {
|
|||
/** @deprecated please use `appContext` instead */
|
||||
parentContext?: any;
|
||||
appContext?: any;
|
||||
|
||||
/** @deprecated please use `open` instead */
|
||||
visible?: boolean;
|
||||
}
|
||||
|
||||
type getContainerFunc = () => HTMLElement;
|
||||
|
@ -149,7 +160,6 @@ export default defineComponent({
|
|||
transitionName: 'zoom',
|
||||
maskTransitionName: 'fade',
|
||||
confirmLoading: false,
|
||||
visible: false,
|
||||
okType: 'primary',
|
||||
}),
|
||||
setup(props, { emit, slots, attrs }) {
|
||||
|
@ -158,9 +168,15 @@ export default defineComponent({
|
|||
'modal',
|
||||
props,
|
||||
);
|
||||
|
||||
const [wrapSSR, hashId] = useStyle(prefixCls);
|
||||
warning(
|
||||
props.visible === undefined,
|
||||
'Modal',
|
||||
`\`visible\` will be removed in next major version, please use \`open\` instead.`,
|
||||
);
|
||||
const handleCancel = (e: MouseEvent) => {
|
||||
emit('update:visible', false);
|
||||
emit('update:open', false);
|
||||
emit('cancel', e);
|
||||
emit('change', false);
|
||||
};
|
||||
|
@ -196,6 +212,7 @@ export default defineComponent({
|
|||
const {
|
||||
prefixCls: customizePrefixCls,
|
||||
visible,
|
||||
open,
|
||||
wrapClassName,
|
||||
centered,
|
||||
getContainer,
|
||||
|
@ -208,15 +225,16 @@ export default defineComponent({
|
|||
[`${prefixCls.value}-centered`]: !!centered,
|
||||
[`${prefixCls.value}-wrap-rtl`]: direction.value === 'rtl',
|
||||
});
|
||||
return (
|
||||
return wrapSSR(
|
||||
<Dialog
|
||||
{...restProps}
|
||||
{...attrs}
|
||||
rootClassName={hashId.value}
|
||||
class={classNames(hashId.value, attrs.class)}
|
||||
getContainer={getContainer || getPopupContainer.value}
|
||||
prefixCls={prefixCls.value}
|
||||
wrapClassName={wrapClassNameExtended}
|
||||
visible={visible}
|
||||
mousePosition={mousePosition}
|
||||
visible={open ?? visible}
|
||||
onClose={handleCancel}
|
||||
focusTriggerAfterClose={focusTriggerAfterClose}
|
||||
transitionName={getTransitionName(rootPrefixCls.value, 'zoom', props.transitionName)}
|
||||
|
@ -225,6 +243,7 @@ export default defineComponent({
|
|||
'fade',
|
||||
props.maskTransitionName,
|
||||
)}
|
||||
mousePosition={restProps.mousePosition ?? mousePosition}
|
||||
v-slots={{
|
||||
...slots,
|
||||
footer: slots.footer || renderFooter,
|
||||
|
@ -236,7 +255,7 @@ export default defineComponent({
|
|||
);
|
||||
},
|
||||
}}
|
||||
></Dialog>
|
||||
></Dialog>,
|
||||
);
|
||||
};
|
||||
},
|
||||
|
|
|
@ -4,10 +4,8 @@ import type { ModalFuncProps } from './Modal';
|
|||
import { destroyFns } from './Modal';
|
||||
import ConfigProvider, { globalConfigForApi } from '../config-provider';
|
||||
import omit from '../_util/omit';
|
||||
import InfoCircleOutlined from '@ant-design/icons-vue/InfoCircleOutlined';
|
||||
import CheckCircleOutlined from '@ant-design/icons-vue/CheckCircleOutlined';
|
||||
import CloseCircleOutlined from '@ant-design/icons-vue/CloseCircleOutlined';
|
||||
import ExclamationCircleOutlined from '@ant-design/icons-vue/ExclamationCircleOutlined';
|
||||
|
||||
import { getConfirmLocale } from './locale';
|
||||
|
||||
type ConfigUpdate = ModalFuncProps | ((prevConfig: ModalFuncProps) => ModalFuncProps);
|
||||
|
||||
|
@ -21,7 +19,7 @@ const confirm = (config: ModalFuncProps) => {
|
|||
let currentConfig = {
|
||||
...omit(config, ['parentContext', 'appContext']),
|
||||
close,
|
||||
visible: true,
|
||||
open: true,
|
||||
} as any;
|
||||
let confirmDialogInstance = null;
|
||||
function destroy(...args: any[]) {
|
||||
|
@ -33,7 +31,7 @@ const confirm = (config: ModalFuncProps) => {
|
|||
}
|
||||
const triggerCancel = args.some(param => param && param.triggerCancel);
|
||||
if (config.onCancel && triggerCancel) {
|
||||
config.onCancel(...args);
|
||||
config.onCancel(() => {}, ...args.slice(1));
|
||||
}
|
||||
for (let i = 0; i < destroyFns.length; i++) {
|
||||
const fn = destroyFns[i];
|
||||
|
@ -47,7 +45,7 @@ const confirm = (config: ModalFuncProps) => {
|
|||
function close(this: typeof close, ...args: any[]) {
|
||||
currentConfig = {
|
||||
...currentConfig,
|
||||
visible: false,
|
||||
open: false,
|
||||
afterClose: () => {
|
||||
if (typeof config.afterClose === 'function') {
|
||||
config.afterClose();
|
||||
|
@ -55,6 +53,10 @@ const confirm = (config: ModalFuncProps) => {
|
|||
destroy.apply(this, args);
|
||||
},
|
||||
};
|
||||
// Legacy support
|
||||
if (currentConfig.visible) {
|
||||
delete currentConfig.visible;
|
||||
}
|
||||
update(currentConfig);
|
||||
}
|
||||
function update(configUpdate: ConfigUpdate) {
|
||||
|
@ -76,9 +78,18 @@ const confirm = (config: ModalFuncProps) => {
|
|||
const global = globalConfigForApi;
|
||||
const rootPrefixCls = global.prefixCls;
|
||||
const prefixCls = p.prefixCls || `${rootPrefixCls}-modal`;
|
||||
const iconPrefixCls = global.iconPrefixCls;
|
||||
const runtimeLocale = getConfirmLocale();
|
||||
return (
|
||||
<ConfigProvider {...(global as any)} prefixCls={rootPrefixCls}>
|
||||
<ConfirmDialog {...p} rootPrefixCls={rootPrefixCls} prefixCls={prefixCls}></ConfirmDialog>
|
||||
<ConfirmDialog
|
||||
{...p}
|
||||
rootPrefixCls={rootPrefixCls}
|
||||
prefixCls={prefixCls}
|
||||
iconPrefixCls={iconPrefixCls}
|
||||
locale={runtimeLocale}
|
||||
cancelText={p.cancelText || runtimeLocale.cancelText}
|
||||
></ConfirmDialog>
|
||||
</ConfigProvider>
|
||||
);
|
||||
};
|
||||
|
@ -101,8 +112,6 @@ export default confirm;
|
|||
|
||||
export function withWarn(props: ModalFuncProps): ModalFuncProps {
|
||||
return {
|
||||
icon: () => <ExclamationCircleOutlined />,
|
||||
okCancel: false,
|
||||
...props,
|
||||
type: 'warning',
|
||||
};
|
||||
|
@ -110,8 +119,6 @@ export function withWarn(props: ModalFuncProps): ModalFuncProps {
|
|||
|
||||
export function withInfo(props: ModalFuncProps): ModalFuncProps {
|
||||
return {
|
||||
icon: () => <InfoCircleOutlined />,
|
||||
okCancel: false,
|
||||
...props,
|
||||
type: 'info',
|
||||
};
|
||||
|
@ -119,8 +126,6 @@ export function withInfo(props: ModalFuncProps): ModalFuncProps {
|
|||
|
||||
export function withSuccess(props: ModalFuncProps): ModalFuncProps {
|
||||
return {
|
||||
icon: () => <CheckCircleOutlined />,
|
||||
okCancel: false,
|
||||
...props,
|
||||
type: 'success',
|
||||
};
|
||||
|
@ -128,8 +133,6 @@ export function withSuccess(props: ModalFuncProps): ModalFuncProps {
|
|||
|
||||
export function withError(props: ModalFuncProps): ModalFuncProps {
|
||||
return {
|
||||
icon: () => <CloseCircleOutlined />,
|
||||
okCancel: false,
|
||||
...props,
|
||||
type: 'error',
|
||||
};
|
||||
|
@ -137,8 +140,6 @@ export function withError(props: ModalFuncProps): ModalFuncProps {
|
|||
|
||||
export function withConfirm(props: ModalFuncProps): ModalFuncProps {
|
||||
return {
|
||||
icon: () => <ExclamationCircleOutlined />,
|
||||
okCancel: true,
|
||||
...props,
|
||||
type: 'confirm',
|
||||
};
|
||||
|
|
|
@ -23,12 +23,7 @@ For example, you can use this pattern when you submit a form.
|
|||
<template>
|
||||
<div>
|
||||
<a-button type="primary" @click="showModal">Open Modal with async logic</a-button>
|
||||
<a-modal
|
||||
v-model:visible="visible"
|
||||
title="Title"
|
||||
:confirm-loading="confirmLoading"
|
||||
@ok="handleOk"
|
||||
>
|
||||
<a-modal v-model:open="open" title="Title" :confirm-loading="confirmLoading" @ok="handleOk">
|
||||
<p>{{ modalText }}</p>
|
||||
</a-modal>
|
||||
</div>
|
||||
|
@ -38,24 +33,24 @@ import { ref, defineComponent } from 'vue';
|
|||
export default defineComponent({
|
||||
setup() {
|
||||
const modalText = ref<string>('Content of the modal');
|
||||
const visible = ref<boolean>(false);
|
||||
const open = ref<boolean>(false);
|
||||
const confirmLoading = ref<boolean>(false);
|
||||
|
||||
const showModal = () => {
|
||||
visible.value = true;
|
||||
open.value = true;
|
||||
};
|
||||
|
||||
const handleOk = () => {
|
||||
modalText.value = 'The modal will be closed after two seconds';
|
||||
confirmLoading.value = true;
|
||||
setTimeout(() => {
|
||||
visible.value = false;
|
||||
open.value = false;
|
||||
confirmLoading.value = false;
|
||||
}, 2000);
|
||||
};
|
||||
return {
|
||||
modalText,
|
||||
visible,
|
||||
open,
|
||||
confirmLoading,
|
||||
showModal,
|
||||
handleOk,
|
||||
|
|
|
@ -19,7 +19,7 @@ Basic modal.
|
|||
<template>
|
||||
<div>
|
||||
<a-button type="primary" @click="showModal">Open Modal</a-button>
|
||||
<a-modal v-model:visible="visible" title="Basic Modal" @ok="handleOk">
|
||||
<a-modal v-model:open="open" title="Basic Modal" @ok="handleOk">
|
||||
<p>Some contents...</p>
|
||||
<p>Some contents...</p>
|
||||
<p>Some contents...</p>
|
||||
|
@ -30,18 +30,18 @@ Basic modal.
|
|||
import { defineComponent, ref } from 'vue';
|
||||
export default defineComponent({
|
||||
setup() {
|
||||
const visible = ref<boolean>(false);
|
||||
const open = ref<boolean>(false);
|
||||
|
||||
const showModal = () => {
|
||||
visible.value = true;
|
||||
open.value = true;
|
||||
};
|
||||
|
||||
const handleOk = (e: MouseEvent) => {
|
||||
console.log(e);
|
||||
visible.value = false;
|
||||
open.value = false;
|
||||
};
|
||||
return {
|
||||
visible,
|
||||
open,
|
||||
showModal,
|
||||
handleOk,
|
||||
};
|
||||
|
|
|
@ -20,7 +20,7 @@ Passing `okButtonProps` and `cancelButtonProps` can customize the ok button and
|
|||
<div>
|
||||
<a-button type="primary" @click="showModal">Open Modal with customized button props</a-button>
|
||||
<a-modal
|
||||
v-model:visible="visible"
|
||||
v-model:open="open"
|
||||
title="Basic Modal"
|
||||
:ok-button-props="{ disabled: true }"
|
||||
:cancel-button-props="{ disabled: true }"
|
||||
|
@ -36,23 +36,23 @@ Passing `okButtonProps` and `cancelButtonProps` can customize the ok button and
|
|||
import { defineComponent, ref } from 'vue';
|
||||
export default defineComponent({
|
||||
setup() {
|
||||
const visible = ref<boolean>(false);
|
||||
const open = ref<boolean>(false);
|
||||
|
||||
const showModal = () => {
|
||||
visible.value = true;
|
||||
open.value = true;
|
||||
};
|
||||
|
||||
const handleOk = (e: MouseEvent) => {
|
||||
console.log(e);
|
||||
visible.value = false;
|
||||
open.value = false;
|
||||
};
|
||||
|
||||
const handleCancel = (e: MouseEvent) => {
|
||||
console.log(e);
|
||||
visible.value = false;
|
||||
open.value = false;
|
||||
};
|
||||
return {
|
||||
visible,
|
||||
open,
|
||||
showModal,
|
||||
handleOk,
|
||||
handleCancel,
|
||||
|
|
|
@ -23,7 +23,7 @@ You could set `footer` to `null` if you don't need default footer buttons.
|
|||
<template>
|
||||
<div>
|
||||
<a-button type="primary" @click="showModal">Open Modal with customized footer</a-button>
|
||||
<a-modal v-model:visible="visible" title="Title" @ok="handleOk">
|
||||
<a-modal v-model:open="open" title="Title" @ok="handleOk">
|
||||
<template #footer>
|
||||
<a-button key="back" @click="handleCancel">Return</a-button>
|
||||
<a-button key="submit" type="primary" :loading="loading" @click="handleOk">Submit</a-button>
|
||||
|
@ -41,26 +41,26 @@ import { defineComponent, ref } from 'vue';
|
|||
export default defineComponent({
|
||||
setup() {
|
||||
const loading = ref<boolean>(false);
|
||||
const visible = ref<boolean>(false);
|
||||
const open = ref<boolean>(false);
|
||||
|
||||
const showModal = () => {
|
||||
visible.value = true;
|
||||
open.value = true;
|
||||
};
|
||||
|
||||
const handleOk = () => {
|
||||
loading.value = true;
|
||||
setTimeout(() => {
|
||||
loading.value = false;
|
||||
visible.value = false;
|
||||
open.value = false;
|
||||
}, 2000);
|
||||
};
|
||||
|
||||
const handleCancel = () => {
|
||||
visible.value = false;
|
||||
open.value = false;
|
||||
};
|
||||
return {
|
||||
loading,
|
||||
visible,
|
||||
open,
|
||||
showModal,
|
||||
handleOk,
|
||||
handleCancel,
|
||||
|
|
|
@ -20,7 +20,7 @@ Full screen by custom style.
|
|||
<div>
|
||||
<a-button type="primary" @click="showModal">Open Modal</a-button>
|
||||
<a-modal
|
||||
v-model:visible="visible"
|
||||
v-model:open="open"
|
||||
title="Basic Modal"
|
||||
width="100%"
|
||||
wrap-class-name="full-modal"
|
||||
|
@ -36,18 +36,18 @@ Full screen by custom style.
|
|||
import { defineComponent, ref } from 'vue';
|
||||
export default defineComponent({
|
||||
setup() {
|
||||
const visible = ref<boolean>(false);
|
||||
const open = ref<boolean>(false);
|
||||
|
||||
const showModal = () => {
|
||||
visible.value = true;
|
||||
open.value = true;
|
||||
};
|
||||
|
||||
const handleOk = (e: MouseEvent) => {
|
||||
console.log(e);
|
||||
visible.value = false;
|
||||
open.value = false;
|
||||
};
|
||||
return {
|
||||
visible,
|
||||
open,
|
||||
showModal,
|
||||
handleOk,
|
||||
};
|
||||
|
|
|
@ -20,13 +20,7 @@ To customize the text of the buttons, you need to set `okText` and `cancelText`
|
|||
<div>
|
||||
<a-button type="primary" @click="showModal">Modal</a-button>
|
||||
<a-button @click="confirm">Confirm</a-button>
|
||||
<a-modal
|
||||
v-model:visible="visible"
|
||||
title="Modal"
|
||||
ok-text="确认"
|
||||
cancel-text="取消"
|
||||
@ok="hideModal"
|
||||
>
|
||||
<a-modal v-model:open="open" title="Modal" ok-text="确认" cancel-text="取消" @ok="hideModal">
|
||||
<p>Bla bla ...</p>
|
||||
<p>Bla bla ...</p>
|
||||
<p>Bla bla ...</p>
|
||||
|
@ -39,13 +33,13 @@ import { defineComponent, ref, createVNode } from 'vue';
|
|||
import { Modal } from 'ant-design-vue';
|
||||
export default defineComponent({
|
||||
setup() {
|
||||
const visible = ref<boolean>(false);
|
||||
const open = ref<boolean>(false);
|
||||
|
||||
const showModal = () => {
|
||||
visible.value = true;
|
||||
open.value = true;
|
||||
};
|
||||
const hideModal = () => {
|
||||
visible.value = false;
|
||||
open.value = false;
|
||||
};
|
||||
|
||||
const confirm = () => {
|
||||
|
@ -59,7 +53,7 @@ export default defineComponent({
|
|||
};
|
||||
|
||||
return {
|
||||
visible,
|
||||
open,
|
||||
showModal,
|
||||
hideModal,
|
||||
confirm,
|
||||
|
|
|
@ -18,12 +18,7 @@ Custom modal content render. use `vueuse` implements draggable.
|
|||
<template>
|
||||
<div>
|
||||
<a-button type="primary" @click="showModal">Open Modal</a-button>
|
||||
<a-modal
|
||||
ref="modalRef"
|
||||
v-model:visible="visible"
|
||||
:wrap-style="{ overflow: 'hidden' }"
|
||||
@ok="handleOk"
|
||||
>
|
||||
<a-modal ref="modalRef" v-model:open="open" :wrap-style="{ overflow: 'hidden' }" @ok="handleOk">
|
||||
<p>Some contents...</p>
|
||||
<p>Some contents...</p>
|
||||
<p>Some contents...</p>
|
||||
|
@ -43,15 +38,15 @@ import { defineComponent, ref, computed, CSSProperties, watch, watchEffect } fro
|
|||
import { useDraggable } from '@vueuse/core';
|
||||
export default defineComponent({
|
||||
setup() {
|
||||
const visible = ref<boolean>(false);
|
||||
const open = ref<boolean>(false);
|
||||
const modalTitleRef = ref<HTMLElement>(null);
|
||||
const showModal = () => {
|
||||
visible.value = true;
|
||||
open.value = true;
|
||||
};
|
||||
const { x, y, isDragging } = useDraggable(modalTitleRef);
|
||||
const handleOk = (e: MouseEvent) => {
|
||||
console.log(e);
|
||||
visible.value = false;
|
||||
open.value = false;
|
||||
};
|
||||
const startX = ref<number>(0);
|
||||
const startY = ref<number>(0);
|
||||
|
@ -98,7 +93,7 @@ export default defineComponent({
|
|||
};
|
||||
});
|
||||
return {
|
||||
visible,
|
||||
open,
|
||||
showModal,
|
||||
handleOk,
|
||||
modalTitleRef,
|
||||
|
|
|
@ -22,7 +22,7 @@ You can use `centered`,`style.top` or other styles to set position of modal dial
|
|||
Display a modal dialog at 20px to Top
|
||||
</a-button>
|
||||
<a-modal
|
||||
v-model:visible="modal1Visible"
|
||||
v-model:open="modal1Visible"
|
||||
title="20px to Top"
|
||||
style="top: 20px"
|
||||
@ok="setModal1Visible(false)"
|
||||
|
@ -37,7 +37,7 @@ You can use `centered`,`style.top` or other styles to set position of modal dial
|
|||
Vertically centered modal dialog
|
||||
</a-button>
|
||||
<a-modal
|
||||
v-model:visible="modal2Visible"
|
||||
v-model:open="modal2Visible"
|
||||
title="Vertically centered modal dialog"
|
||||
centered
|
||||
@ok="modal2Visible = false"
|
||||
|
@ -55,8 +55,8 @@ export default defineComponent({
|
|||
const modal1Visible = ref<boolean>(false);
|
||||
const modal2Visible = ref<boolean>(false);
|
||||
|
||||
const setModal1Visible = (visible: boolean) => {
|
||||
modal1Visible.value = visible;
|
||||
const setModal1Visible = (open: boolean) => {
|
||||
modal1Visible.value = open;
|
||||
};
|
||||
return {
|
||||
modal1Visible,
|
||||
|
|
|
@ -19,7 +19,7 @@ Use `width` to set the width of the modal dialog
|
|||
<template>
|
||||
<div>
|
||||
<a-button type="primary" @click="showModal">Open Modal of 1000px width</a-button>
|
||||
<a-modal v-model:visible="visible" width="1000px" title="Basic Modal" @ok="handleOk">
|
||||
<a-modal v-model:open="open" width="1000px" title="Basic Modal" @ok="handleOk">
|
||||
<p>Some contents...</p>
|
||||
<p>Some contents...</p>
|
||||
<p>Some contents...</p>
|
||||
|
@ -30,18 +30,18 @@ Use `width` to set the width of the modal dialog
|
|||
import { defineComponent, ref } from 'vue';
|
||||
export default defineComponent({
|
||||
setup() {
|
||||
const visible = ref<boolean>(false);
|
||||
const open = ref<boolean>(false);
|
||||
|
||||
const showModal = () => {
|
||||
visible.value = true;
|
||||
open.value = true;
|
||||
};
|
||||
|
||||
const handleOk = (e: MouseEvent) => {
|
||||
console.log(e);
|
||||
visible.value = false;
|
||||
open.value = false;
|
||||
};
|
||||
return {
|
||||
visible,
|
||||
open,
|
||||
showModal,
|
||||
handleOk,
|
||||
};
|
||||
|
|
|
@ -36,7 +36,7 @@ When requiring users to interact with the application, but without jumping to a
|
|||
| okText | Text of the OK button | string\|slot | `OK` | |
|
||||
| okType | Button `type` of the OK button | string | `primary` | |
|
||||
| title | The modal dialog's title | string\|slot | - | |
|
||||
| visible | Whether the modal dialog is visible or not | boolean | false | |
|
||||
| open(v-model) | Whether the modal dialog is visible or not | boolean | false | |
|
||||
| width | Width of the modal dialog | string\|number | 520 | |
|
||||
| wrapClassName | The class name of the container of the modal dialog | string | - | |
|
||||
| zIndex | The `z-index` of the Modal | number | 1000 | |
|
||||
|
@ -74,6 +74,7 @@ The items listed above are all functions, expecting a settings object as paramet
|
|||
| class | class of container | string | - | |
|
||||
| closable | Whether a close (x) button is visible on top right of the modal dialog or not | boolean | `false` | |
|
||||
| content | Content | string\|VNode \|function(h) | - | |
|
||||
| footer | Footer content, set as `footer: null` when you don't need default buttons | string\|VNode \|function(h) | - | 4.0.0 |
|
||||
| icon | custom icon (`Added in 1.14.0`) | VNode \|()=>VNode | - | |
|
||||
| keyboard | Whether support press esc to close | boolean | true | |
|
||||
| mask | Whether show mask or not. | boolean | true | |
|
||||
|
@ -116,7 +117,7 @@ router.beforeEach((to, from, next) => {
|
|||
|
||||
## FAQ
|
||||
|
||||
### Why can't the Modal method obtain global registered components, context, vuex, etc. and ConfigProvider `locale/prefixCls` configuration, and can't update data responsively?
|
||||
### Why can't the Modal method obtain global registered components, context, vuex, etc. and ConfigProvider `locale/prefixCls/theme` configuration, and can't update data responsively?
|
||||
|
||||
Call the Modal method directly, and the component will dynamically create a new Vue entity through `Vue.render`. Its context is not the same as the context where the current code is located, so the context information cannot be obtained.
|
||||
|
||||
|
|
|
@ -40,7 +40,7 @@ cover: https://gw.alipayobjects.com/zos/alicdn/3StSdUlSH/Modal.svg
|
|||
| okText | 确认按钮文字 | string\|slot | 确定 | |
|
||||
| okType | 确认按钮类型 | string | primary | |
|
||||
| title | 标题 | string\|slot | 无 | |
|
||||
| visible(v-model) | 对话框是否可见 | boolean | 无 | |
|
||||
| open(v-model) | 对话框是否可见 | boolean | 无 | |
|
||||
| width | 宽度 | string\|number | 520 | |
|
||||
| wrapClassName | 对话框外层容器的类名 | string | - | |
|
||||
| zIndex | 设置 Modal 的 `z-index` | number | 1000 | |
|
||||
|
@ -78,6 +78,7 @@ cover: https://gw.alipayobjects.com/zos/alicdn/3StSdUlSH/Modal.svg
|
|||
| class | 容器类名 | string | - | |
|
||||
| closable | 是否显示右上角的关闭按钮 | boolean | `false` | |
|
||||
| content | 内容 | string \|VNode \|function(h) | 无 | |
|
||||
| footer | 底部内容,当不需要默认底部按钮时,可以设为 `footer: null` | string \|VNode \|function(h) | - | 4.0.0 |
|
||||
| icon | 自定义图标(1.14.0 新增) | VNode \| ()=>VNode | - | |
|
||||
| keyboard | 是否支持键盘 esc 关闭 | boolean | true | |
|
||||
| mask | 是否展示遮罩 | boolean | true | |
|
||||
|
@ -120,7 +121,7 @@ router.beforeEach((to, from, next) => {
|
|||
|
||||
## FAQ
|
||||
|
||||
### 为什么 Modal 方法不能获取 全局注册组件、context、vuex 等内容和 ConfigProvider `locale/prefixCls` 配置, 以及不能响应式更新数据 ?
|
||||
### 为什么 Modal 方法不能获取 全局注册组件、context、vuex 等内容和 ConfigProvider `locale/prefixCls/theme` 配置, 以及不能响应式更新数据 ?
|
||||
|
||||
直接调用 Modal 方法,组件会通过 `Vue.render` 动态创建新的 Vue 实体。其 context 与当前代码所在 context 并不相同,因而无法获取 context 信息。
|
||||
|
||||
|
|
|
@ -1,5 +1,28 @@
|
|||
import defaultLocale from '../locale/en_US';
|
||||
|
||||
export interface ModalLocale {
|
||||
okText: string;
|
||||
cancelText: string;
|
||||
justOkText: string;
|
||||
}
|
||||
|
||||
let runtimeLocale: ModalLocale = {
|
||||
...(defaultLocale.Modal as ModalLocale),
|
||||
};
|
||||
|
||||
export function changeConfirmLocale(newLocale?: ModalLocale) {
|
||||
if (newLocale) {
|
||||
runtimeLocale = {
|
||||
...runtimeLocale,
|
||||
...newLocale,
|
||||
};
|
||||
} else {
|
||||
runtimeLocale = {
|
||||
...(defaultLocale.Modal as ModalLocale),
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
export function getConfirmLocale() {
|
||||
return runtimeLocale;
|
||||
}
|
||||
|
|
|
@ -1,74 +0,0 @@
|
|||
@import '../../style/mixins/index';
|
||||
|
||||
@confirm-prefix-cls: ~'@{ant-prefix}-modal-confirm';
|
||||
|
||||
.@{confirm-prefix-cls} {
|
||||
.@{ant-prefix}-modal-header {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.@{ant-prefix}-modal-body {
|
||||
padding: @modal-confirm-body-padding;
|
||||
}
|
||||
|
||||
&-body-wrapper {
|
||||
.clearfix();
|
||||
}
|
||||
|
||||
&-body {
|
||||
.@{confirm-prefix-cls}-title {
|
||||
display: block;
|
||||
// create BFC to avoid
|
||||
// https://user-images.githubusercontent.com/507615/37702510-ba844e06-2d2d-11e8-9b67-8e19be57f445.png
|
||||
overflow: hidden;
|
||||
color: @heading-color;
|
||||
font-weight: 500;
|
||||
font-size: @modal-confirm-title-font-size;
|
||||
line-height: 1.4;
|
||||
}
|
||||
|
||||
.@{confirm-prefix-cls}-content {
|
||||
margin-top: 8px;
|
||||
color: @text-color;
|
||||
font-size: @font-size-base;
|
||||
}
|
||||
|
||||
> .@{iconfont-css-prefix} {
|
||||
float: left;
|
||||
margin-right: 16px;
|
||||
font-size: 22px;
|
||||
|
||||
// `content` after `icon` should set marginLeft
|
||||
+ .@{confirm-prefix-cls}-title + .@{confirm-prefix-cls}-content {
|
||||
margin-left: 38px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.@{confirm-prefix-cls}-btns {
|
||||
float: right;
|
||||
margin-top: 24px;
|
||||
|
||||
.@{ant-prefix}-btn + .@{ant-prefix}-btn {
|
||||
margin-bottom: 0;
|
||||
margin-left: 8px;
|
||||
}
|
||||
}
|
||||
|
||||
&-error &-body > .@{iconfont-css-prefix} {
|
||||
color: @error-color;
|
||||
}
|
||||
|
||||
&-warning &-body > .@{iconfont-css-prefix},
|
||||
&-confirm &-body > .@{iconfont-css-prefix} {
|
||||
color: @warning-color;
|
||||
}
|
||||
|
||||
&-info &-body > .@{iconfont-css-prefix} {
|
||||
color: @info-color;
|
||||
}
|
||||
|
||||
&-success &-body > .@{iconfont-css-prefix} {
|
||||
color: @success-color;
|
||||
}
|
||||
}
|
|
@ -1,7 +0,0 @@
|
|||
@import '../../style/themes/index';
|
||||
@import '../../style/mixins/index';
|
||||
@import './modal';
|
||||
@import './confirm';
|
||||
@import './rtl';
|
||||
|
||||
.popover-customize-bg(@dialog-prefix-cls, @popover-background);
|
|
@ -0,0 +1,462 @@
|
|||
import { initFadeMotion, initZoomMotion } from '../../_style/motion';
|
||||
import type { AliasToken, FullToken, GenerateStyle } from '../../theme/internal';
|
||||
import { genComponentStyleHook, mergeToken } from '../../theme/internal';
|
||||
import type { TokenWithCommonCls } from '../../theme/util/genComponentStyleHook';
|
||||
import { clearFix, genFocusStyle, resetComponent } from '../../_style';
|
||||
import { CSSProperties } from 'vue';
|
||||
|
||||
/** Component only token. Which will handle additional calculation of alias token */
|
||||
export interface ComponentToken {
|
||||
// Component token here
|
||||
}
|
||||
|
||||
export interface ModalToken extends FullToken<'Modal'> {
|
||||
// Custom token here
|
||||
modalBodyPadding: number;
|
||||
modalHeaderBg: string;
|
||||
modalHeaderPadding: string;
|
||||
modalHeaderBorderWidth: number;
|
||||
modalHeaderBorderStyle: string;
|
||||
modalHeaderTitleLineHeight: number;
|
||||
modalHeaderTitleFontSize: number;
|
||||
modalHeaderBorderColorSplit: string;
|
||||
modalHeaderCloseSize: number;
|
||||
modalContentBg: string;
|
||||
modalHeadingColor: string;
|
||||
modalCloseColor: string;
|
||||
modalCloseBtnSize: number;
|
||||
modalFooterBg: string;
|
||||
modalFooterBorderColorSplit: string;
|
||||
modalFooterBorderStyle: string;
|
||||
modalFooterPaddingVertical: number;
|
||||
modalFooterPaddingHorizontal: number;
|
||||
modalFooterBorderWidth: number;
|
||||
modalConfirmTitleFontSize: number;
|
||||
modalIconHoverColor: string;
|
||||
modalConfirmIconSize: number;
|
||||
}
|
||||
|
||||
function box(position: CSSProperties['position']): CSSProperties {
|
||||
return {
|
||||
position,
|
||||
top: 0,
|
||||
insetInlineEnd: 0,
|
||||
bottom: 0,
|
||||
insetInlineStart: 0,
|
||||
};
|
||||
}
|
||||
|
||||
export const genModalMaskStyle: GenerateStyle<TokenWithCommonCls<AliasToken>> = token => {
|
||||
const { componentCls } = token;
|
||||
|
||||
return [
|
||||
{
|
||||
[`${componentCls}-root`]: {
|
||||
[`${componentCls}${token.antCls}-zoom-enter, ${componentCls}${token.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',
|
||||
},
|
||||
|
||||
[`${componentCls}-mask`]: {
|
||||
...box('fixed'),
|
||||
zIndex: token.zIndexPopupBase,
|
||||
height: '100%',
|
||||
backgroundColor: token.colorBgMask,
|
||||
|
||||
[`${componentCls}-hidden`]: {
|
||||
display: 'none',
|
||||
},
|
||||
},
|
||||
|
||||
[`${componentCls}-wrap`]: {
|
||||
...box('fixed'),
|
||||
overflow: 'auto',
|
||||
outline: 0,
|
||||
WebkitOverflowScrolling: 'touch',
|
||||
},
|
||||
},
|
||||
},
|
||||
{ [`${componentCls}-root`]: initFadeMotion(token) },
|
||||
];
|
||||
};
|
||||
|
||||
const genModalStyle: GenerateStyle<ModalToken> = token => {
|
||||
const { componentCls } = token;
|
||||
|
||||
return [
|
||||
// ======================== Root =========================
|
||||
{
|
||||
[`${componentCls}-root`]: {
|
||||
[`${componentCls}-wrap`]: {
|
||||
zIndex: token.zIndexPopupBase,
|
||||
position: 'fixed',
|
||||
inset: 0,
|
||||
overflow: 'auto',
|
||||
outline: 0,
|
||||
WebkitOverflowScrolling: 'touch',
|
||||
},
|
||||
[`${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})`]: {
|
||||
[componentCls]: {
|
||||
maxWidth: 'calc(100vw - 16px)',
|
||||
margin: `${token.marginXS} auto`,
|
||||
},
|
||||
[`${componentCls}-centered`]: {
|
||||
[componentCls]: {
|
||||
flex: 1,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
|
||||
// ======================== Modal ========================
|
||||
{
|
||||
[componentCls]: {
|
||||
...resetComponent(token),
|
||||
pointerEvents: 'none',
|
||||
position: 'relative',
|
||||
top: 100,
|
||||
width: 'auto',
|
||||
maxWidth: `calc(100vw - ${token.margin * 2}px)`,
|
||||
margin: '0 auto',
|
||||
paddingBottom: token.paddingLG,
|
||||
|
||||
[`${componentCls}-title`]: {
|
||||
margin: 0,
|
||||
color: token.modalHeadingColor,
|
||||
fontWeight: token.fontWeightStrong,
|
||||
fontSize: token.modalHeaderTitleFontSize,
|
||||
lineHeight: token.modalHeaderTitleLineHeight,
|
||||
wordWrap: 'break-word',
|
||||
},
|
||||
|
||||
[`${componentCls}-content`]: {
|
||||
position: 'relative',
|
||||
backgroundColor: token.modalContentBg,
|
||||
backgroundClip: 'padding-box',
|
||||
border: 0,
|
||||
borderRadius: token.borderRadiusLG,
|
||||
boxShadow: token.boxShadowSecondary,
|
||||
pointerEvents: 'auto',
|
||||
padding: `${token.paddingMD}px ${token.paddingContentHorizontalLG}px`,
|
||||
},
|
||||
|
||||
[`${componentCls}-close`]: {
|
||||
position: 'absolute',
|
||||
top: (token.modalHeaderCloseSize - token.modalCloseBtnSize) / 2,
|
||||
insetInlineEnd: (token.modalHeaderCloseSize - token.modalCloseBtnSize) / 2,
|
||||
zIndex: token.zIndexPopupBase + 10,
|
||||
padding: 0,
|
||||
color: token.modalCloseColor,
|
||||
fontWeight: token.fontWeightStrong,
|
||||
lineHeight: 1,
|
||||
textDecoration: 'none',
|
||||
background: 'transparent',
|
||||
borderRadius: token.borderRadiusSM,
|
||||
width: token.modalConfirmIconSize,
|
||||
height: token.modalConfirmIconSize,
|
||||
border: 0,
|
||||
outline: 0,
|
||||
cursor: 'pointer',
|
||||
transition: `color ${token.motionDurationMid}, background-color ${token.motionDurationMid}`,
|
||||
|
||||
'&-x': {
|
||||
display: 'block',
|
||||
fontSize: token.fontSizeLG,
|
||||
fontStyle: 'normal',
|
||||
lineHeight: `${token.modalCloseBtnSize}px`,
|
||||
textAlign: 'center',
|
||||
textTransform: 'none',
|
||||
textRendering: 'auto',
|
||||
},
|
||||
|
||||
'&:hover': {
|
||||
color: token.modalIconHoverColor,
|
||||
backgroundColor: token.wireframe ? 'transparent' : token.colorFillContent,
|
||||
textDecoration: 'none',
|
||||
},
|
||||
|
||||
'&:active': {
|
||||
backgroundColor: token.wireframe ? 'transparent' : token.colorFillContentHover,
|
||||
},
|
||||
|
||||
...genFocusStyle(token),
|
||||
},
|
||||
|
||||
[`${componentCls}-header`]: {
|
||||
color: token.colorText,
|
||||
background: token.modalHeaderBg,
|
||||
borderRadius: `${token.borderRadiusLG}px ${token.borderRadiusLG}px 0 0`,
|
||||
marginBottom: token.marginXS,
|
||||
},
|
||||
|
||||
[`${componentCls}-body`]: {
|
||||
fontSize: token.fontSize,
|
||||
lineHeight: token.lineHeight,
|
||||
wordWrap: 'break-word',
|
||||
},
|
||||
|
||||
[`${componentCls}-footer`]: {
|
||||
textAlign: 'end',
|
||||
background: token.modalFooterBg,
|
||||
marginTop: token.marginSM,
|
||||
|
||||
[`${token.antCls}-btn + ${token.antCls}-btn:not(${token.antCls}-dropdown-trigger)`]: {
|
||||
marginBottom: 0,
|
||||
marginInlineStart: token.marginXS,
|
||||
},
|
||||
},
|
||||
|
||||
[`${componentCls}-open`]: {
|
||||
overflow: 'hidden',
|
||||
},
|
||||
},
|
||||
},
|
||||
|
||||
// ======================== Pure =========================
|
||||
{
|
||||
[`${componentCls}-pure-panel`]: {
|
||||
top: 'auto',
|
||||
padding: 0,
|
||||
display: 'flex',
|
||||
flexDirection: 'column',
|
||||
|
||||
[`${componentCls}-content,
|
||||
${componentCls}-body,
|
||||
${componentCls}-confirm-body-wrapper`]: {
|
||||
display: 'flex',
|
||||
flexDirection: 'column',
|
||||
flex: 'auto',
|
||||
},
|
||||
|
||||
[`${componentCls}-confirm-body`]: {
|
||||
marginBottom: 'auto',
|
||||
},
|
||||
},
|
||||
},
|
||||
];
|
||||
};
|
||||
|
||||
const genModalConfirmStyle: GenerateStyle<ModalToken> = token => {
|
||||
const { componentCls } = token;
|
||||
const confirmComponentCls = `${componentCls}-confirm`;
|
||||
|
||||
return {
|
||||
[confirmComponentCls]: {
|
||||
'&-rtl': {
|
||||
direction: 'rtl',
|
||||
},
|
||||
[`${token.antCls}-modal-header`]: {
|
||||
display: 'none',
|
||||
},
|
||||
[`${confirmComponentCls}-body-wrapper`]: {
|
||||
...clearFix(),
|
||||
},
|
||||
[`${confirmComponentCls}-body`]: {
|
||||
display: 'flex',
|
||||
flexWrap: 'wrap',
|
||||
alignItems: 'center',
|
||||
|
||||
[`${confirmComponentCls}-title`]: {
|
||||
flex: '0 0 100%',
|
||||
display: 'block',
|
||||
// create BFC to avoid
|
||||
// https://user-images.githubusercontent.com/507615/37702510-ba844e06-2d2d-11e8-9b67-8e19be57f445.png
|
||||
overflow: 'hidden',
|
||||
color: token.colorTextHeading,
|
||||
fontWeight: token.fontWeightStrong,
|
||||
fontSize: token.modalHeaderTitleFontSize,
|
||||
lineHeight: token.modalHeaderTitleLineHeight,
|
||||
|
||||
[`+ ${confirmComponentCls}-content`]: {
|
||||
marginBlockStart: token.marginXS,
|
||||
flexBasis: '100%',
|
||||
maxWidth: `calc(100% - ${token.modalConfirmIconSize + token.marginSM}px)`,
|
||||
},
|
||||
},
|
||||
|
||||
[`${confirmComponentCls}-content`]: {
|
||||
color: token.colorText,
|
||||
fontSize: token.fontSize,
|
||||
},
|
||||
|
||||
[`> ${token.iconCls}`]: {
|
||||
flex: 'none',
|
||||
marginInlineEnd: token.marginSM,
|
||||
fontSize: token.modalConfirmIconSize,
|
||||
|
||||
[`+ ${confirmComponentCls}-title`]: {
|
||||
flex: 1,
|
||||
},
|
||||
|
||||
// `content` after `icon` should set marginLeft
|
||||
[`+ ${confirmComponentCls}-title + ${confirmComponentCls}-content`]: {
|
||||
marginInlineStart: token.modalConfirmIconSize + token.marginSM,
|
||||
},
|
||||
},
|
||||
},
|
||||
[`${confirmComponentCls}-btns`]: {
|
||||
textAlign: 'end',
|
||||
marginTop: token.marginSM,
|
||||
|
||||
[`${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,
|
||||
},
|
||||
|
||||
// https://github.com/ant-design/ant-design/issues/37329
|
||||
[`${componentCls}-zoom-leave ${componentCls}-btns`]: {
|
||||
pointerEvents: 'none',
|
||||
},
|
||||
};
|
||||
};
|
||||
|
||||
const genRTLStyle: GenerateStyle<ModalToken> = token => {
|
||||
const { componentCls } = token;
|
||||
return {
|
||||
[`${componentCls}-root`]: {
|
||||
[`${componentCls}-wrap-rtl`]: {
|
||||
direction: 'rtl',
|
||||
|
||||
[`${componentCls}-confirm-body`]: {
|
||||
direction: 'rtl',
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
||||
};
|
||||
|
||||
const genWireframeStyle: GenerateStyle<ModalToken> = token => {
|
||||
const { componentCls, antCls } = token;
|
||||
const confirmComponentCls = `${componentCls}-confirm`;
|
||||
|
||||
return {
|
||||
[componentCls]: {
|
||||
[`${componentCls}-content`]: {
|
||||
padding: 0,
|
||||
},
|
||||
|
||||
[`${componentCls}-header`]: {
|
||||
padding: token.modalHeaderPadding,
|
||||
borderBottom: `${token.modalHeaderBorderWidth}px ${token.modalHeaderBorderStyle} ${token.modalHeaderBorderColorSplit}`,
|
||||
marginBottom: 0,
|
||||
},
|
||||
|
||||
[`${componentCls}-body`]: {
|
||||
padding: token.modalBodyPadding,
|
||||
},
|
||||
|
||||
[`${componentCls}-footer`]: {
|
||||
padding: `${token.modalFooterPaddingVertical}px ${token.modalFooterPaddingHorizontal}px`,
|
||||
borderTop: `${token.modalFooterBorderWidth}px ${token.modalFooterBorderStyle} ${token.modalFooterBorderColorSplit}`,
|
||||
borderRadius: `0 0 ${token.borderRadiusLG}px ${token.borderRadiusLG}px`,
|
||||
marginTop: 0,
|
||||
},
|
||||
},
|
||||
|
||||
[confirmComponentCls]: {
|
||||
[`${antCls}-modal-body`]: {
|
||||
padding: `${token.padding * 2}px ${token.padding * 2}px ${token.paddingLG}px`,
|
||||
},
|
||||
[`${confirmComponentCls}-body`]: {
|
||||
[`> ${token.iconCls}`]: {
|
||||
marginInlineEnd: token.margin,
|
||||
|
||||
// `content` after `icon` should set marginLeft
|
||||
[`+ ${confirmComponentCls}-title + ${confirmComponentCls}-content`]: {
|
||||
marginInlineStart: token.modalConfirmIconSize + token.margin,
|
||||
},
|
||||
},
|
||||
},
|
||||
[`${confirmComponentCls}-btns`]: {
|
||||
marginTop: token.marginLG,
|
||||
},
|
||||
},
|
||||
};
|
||||
};
|
||||
|
||||
// ============================== Export ==============================
|
||||
export default genComponentStyleHook('Modal', token => {
|
||||
const headerPaddingVertical = token.padding;
|
||||
const headerFontSize = token.fontSizeHeading5;
|
||||
const headerLineHeight = token.lineHeightHeading5;
|
||||
|
||||
const modalToken = mergeToken<ModalToken>(token, {
|
||||
modalBodyPadding: token.paddingLG,
|
||||
modalHeaderBg: token.colorBgElevated,
|
||||
modalHeaderPadding: `${headerPaddingVertical}px ${token.paddingLG}px`,
|
||||
modalHeaderBorderWidth: token.lineWidth,
|
||||
modalHeaderBorderStyle: token.lineType,
|
||||
modalHeaderTitleLineHeight: headerLineHeight,
|
||||
modalHeaderTitleFontSize: headerFontSize,
|
||||
modalHeaderBorderColorSplit: token.colorSplit,
|
||||
modalHeaderCloseSize: headerLineHeight * headerFontSize + headerPaddingVertical * 2,
|
||||
modalContentBg: token.colorBgElevated,
|
||||
modalHeadingColor: token.colorTextHeading,
|
||||
modalCloseColor: token.colorTextDescription,
|
||||
modalFooterBg: 'transparent',
|
||||
modalFooterBorderColorSplit: token.colorSplit,
|
||||
modalFooterBorderStyle: token.lineType,
|
||||
modalFooterPaddingVertical: token.paddingXS,
|
||||
modalFooterPaddingHorizontal: token.padding,
|
||||
modalFooterBorderWidth: token.lineWidth,
|
||||
modalConfirmTitleFontSize: token.fontSizeLG,
|
||||
modalIconHoverColor: token.colorIconHover,
|
||||
modalConfirmIconSize: token.fontSize * token.lineHeight,
|
||||
modalCloseBtnSize: token.controlHeightLG * 0.55,
|
||||
});
|
||||
return [
|
||||
genModalStyle(modalToken),
|
||||
genModalConfirmStyle(modalToken),
|
||||
genRTLStyle(modalToken),
|
||||
genModalMaskStyle(modalToken),
|
||||
token.wireframe && genWireframeStyle(modalToken),
|
||||
initZoomMotion(modalToken, 'zoom'),
|
||||
];
|
||||
});
|
|
@ -1,5 +0,0 @@
|
|||
import '../../style/index.less';
|
||||
import './index.less';
|
||||
|
||||
// style dependencies
|
||||
import '../../button/style';
|
|
@ -1,136 +0,0 @@
|
|||
@dialog-prefix-cls: ~'@{ant-prefix}-modal';
|
||||
|
||||
.@{dialog-prefix-cls} {
|
||||
.reset-component();
|
||||
.modal-mask();
|
||||
|
||||
position: relative;
|
||||
top: 100px;
|
||||
width: auto;
|
||||
max-width: calc(100vw - 32px);
|
||||
margin: 0 auto;
|
||||
padding-bottom: 24px;
|
||||
|
||||
&-wrap {
|
||||
z-index: @zindex-modal;
|
||||
}
|
||||
|
||||
&-title {
|
||||
margin: 0;
|
||||
color: @modal-heading-color;
|
||||
font-weight: 500;
|
||||
font-size: @modal-header-title-font-size;
|
||||
line-height: @modal-header-title-line-height;
|
||||
word-wrap: break-word;
|
||||
}
|
||||
|
||||
&-content {
|
||||
position: relative;
|
||||
background-color: @modal-content-bg;
|
||||
background-clip: padding-box;
|
||||
border: 0;
|
||||
border-radius: @border-radius-base;
|
||||
box-shadow: @shadow-2;
|
||||
pointer-events: auto;
|
||||
}
|
||||
|
||||
&-close {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
right: 0;
|
||||
z-index: @zindex-popup-close;
|
||||
padding: 0;
|
||||
color: @modal-close-color;
|
||||
font-weight: 700;
|
||||
line-height: 1;
|
||||
text-decoration: none;
|
||||
background: transparent;
|
||||
border: 0;
|
||||
outline: 0;
|
||||
cursor: pointer;
|
||||
transition: color 0.3s;
|
||||
|
||||
&-x {
|
||||
display: block;
|
||||
width: @modal-header-close-size;
|
||||
height: @modal-header-close-size;
|
||||
font-size: @font-size-lg;
|
||||
font-style: normal;
|
||||
line-height: @modal-header-close-size;
|
||||
text-align: center;
|
||||
text-transform: none;
|
||||
text-rendering: auto;
|
||||
}
|
||||
|
||||
&:focus,
|
||||
&:hover {
|
||||
color: @icon-color-hover;
|
||||
text-decoration: none;
|
||||
}
|
||||
}
|
||||
|
||||
&-header {
|
||||
padding: @modal-header-padding;
|
||||
color: @text-color;
|
||||
background: @modal-header-bg;
|
||||
border-bottom: @modal-header-border-width @modal-header-border-style
|
||||
@modal-header-border-color-split;
|
||||
border-radius: @border-radius-base @border-radius-base 0 0;
|
||||
}
|
||||
|
||||
&-body {
|
||||
padding: @modal-body-padding;
|
||||
font-size: @font-size-base;
|
||||
line-height: @line-height-base;
|
||||
word-wrap: break-word;
|
||||
}
|
||||
|
||||
&-footer {
|
||||
padding: @modal-footer-padding-vertical @modal-footer-padding-horizontal;
|
||||
text-align: right;
|
||||
background: @modal-footer-bg;
|
||||
border-top: @modal-footer-border-width @modal-footer-border-style
|
||||
@modal-footer-border-color-split;
|
||||
border-radius: 0 0 @border-radius-base @border-radius-base;
|
||||
|
||||
.@{ant-prefix}-btn + .@{ant-prefix}-btn:not(.@{ant-prefix}-dropdown-trigger) {
|
||||
margin-bottom: 0;
|
||||
margin-left: 8px;
|
||||
}
|
||||
}
|
||||
|
||||
&-open {
|
||||
overflow: hidden;
|
||||
}
|
||||
}
|
||||
|
||||
.@{dialog-prefix-cls}-centered {
|
||||
text-align: center;
|
||||
|
||||
&::before {
|
||||
display: inline-block;
|
||||
width: 0;
|
||||
height: 100%;
|
||||
vertical-align: middle;
|
||||
content: '';
|
||||
}
|
||||
.@{dialog-prefix-cls} {
|
||||
top: 0;
|
||||
display: inline-block;
|
||||
padding-bottom: 0;
|
||||
text-align: left;
|
||||
vertical-align: middle;
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: @screen-sm-max) {
|
||||
.@{dialog-prefix-cls} {
|
||||
max-width: calc(100vw - 16px);
|
||||
margin: 8px auto;
|
||||
}
|
||||
.@{dialog-prefix-cls}-centered {
|
||||
.@{dialog-prefix-cls} {
|
||||
flex: 1;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,74 +0,0 @@
|
|||
@import '../../style/themes/index';
|
||||
@import '../../style/mixins/index';
|
||||
|
||||
@dialog-prefix-cls: ~'@{ant-prefix}-modal';
|
||||
@confirm-prefix-cls: ~'@{ant-prefix}-modal-confirm';
|
||||
@dialog-wrap-rtl-cls: ~'@{dialog-prefix-cls}-wrap-rtl';
|
||||
|
||||
.@{dialog-prefix-cls} {
|
||||
&-wrap {
|
||||
&-rtl {
|
||||
direction: rtl;
|
||||
}
|
||||
}
|
||||
|
||||
&-close {
|
||||
.@{dialog-wrap-rtl-cls} & {
|
||||
right: initial;
|
||||
left: 0;
|
||||
}
|
||||
}
|
||||
|
||||
&-footer {
|
||||
.@{dialog-wrap-rtl-cls} & {
|
||||
text-align: left;
|
||||
}
|
||||
.@{ant-prefix}-btn + .@{ant-prefix}-btn {
|
||||
.@{dialog-wrap-rtl-cls} & {
|
||||
margin-right: 8px;
|
||||
margin-left: 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&-confirm {
|
||||
&-body {
|
||||
.@{dialog-wrap-rtl-cls} & {
|
||||
direction: rtl;
|
||||
}
|
||||
> .@{iconfont-css-prefix} {
|
||||
.@{dialog-wrap-rtl-cls} & {
|
||||
float: right;
|
||||
margin-right: 0;
|
||||
margin-left: 16px;
|
||||
}
|
||||
+ .@{confirm-prefix-cls}-title + .@{confirm-prefix-cls}-content {
|
||||
.@{dialog-wrap-rtl-cls} & {
|
||||
margin-right: 38px;
|
||||
margin-left: 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&-btns {
|
||||
.@{dialog-wrap-rtl-cls} & {
|
||||
float: left;
|
||||
}
|
||||
.@{ant-prefix}-btn + .@{ant-prefix}-btn {
|
||||
.@{dialog-wrap-rtl-cls} & {
|
||||
margin-right: 8px;
|
||||
margin-left: 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.@{dialog-prefix-cls}-centered {
|
||||
.@{dialog-prefix-cls} {
|
||||
.@{dialog-wrap-rtl-cls}& {
|
||||
text-align: right;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -22,7 +22,7 @@ import type { ComponentToken as DividerComponentToken } from '../../divider/styl
|
|||
// import type { ComponentToken as MentionsComponentToken } from '../../mentions/style';
|
||||
// import type { ComponentToken as MenuComponentToken } from '../../menu/style';
|
||||
import type { ComponentToken as MessageComponentToken } from '../../message/style';
|
||||
// import type { ComponentToken as ModalComponentToken } from '../../modal/style';
|
||||
import type { ComponentToken as ModalComponentToken } from '../../modal/style';
|
||||
import type { ComponentToken as NotificationComponentToken } from '../../notification/style';
|
||||
// import type { ComponentToken as PopconfirmComponentToken } from '../../popconfirm/style';
|
||||
// import type { ComponentToken as PopoverComponentToken } from '../../popover/style';
|
||||
|
@ -103,7 +103,7 @@ export interface ComponentTokenMap {
|
|||
// Calendar?: CalendarComponentToken;
|
||||
// Steps?: StepsComponentToken;
|
||||
// Menu?: MenuComponentToken;
|
||||
// Modal?: ModalComponentToken;
|
||||
Modal?: ModalComponentToken;
|
||||
Message?: MessageComponentToken;
|
||||
// Upload?: UploadComponentToken;
|
||||
// Tooltip?: TooltipComponentToken;
|
||||
|
|
Loading…
Reference in New Issue