feat: modal add closeIcon support
parent
078a3e4fff
commit
e43f6905be
|
@ -1,5 +1,5 @@
|
||||||
module.exports = {
|
module.exports = {
|
||||||
dev: {
|
dev: {
|
||||||
componentName: 'message', // dev components
|
componentName: 'modal', // dev components
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
|
@ -48,7 +48,10 @@ export default {
|
||||||
// this.setState({ loading: false });
|
// this.setState({ loading: false });
|
||||||
closeModal(...args);
|
closeModal(...args);
|
||||||
},
|
},
|
||||||
() => {
|
e => {
|
||||||
|
// Emit error when catch promise reject
|
||||||
|
// eslint-disable-next-line no-console
|
||||||
|
console.error(e);
|
||||||
// See: https://github.com/ant-design/ant-design/issues/6183
|
// See: https://github.com/ant-design/ant-design/issues/6183
|
||||||
this.setState({ loading: false });
|
this.setState({ loading: false });
|
||||||
},
|
},
|
||||||
|
|
|
@ -27,6 +27,7 @@ export default {
|
||||||
} = props;
|
} = props;
|
||||||
warning(
|
warning(
|
||||||
!('iconType' in props),
|
!('iconType' in props),
|
||||||
|
'Modal',
|
||||||
`The property 'iconType' is deprecated. Use the property 'icon' instead.`,
|
`The property 'iconType' is deprecated. Use the property 'icon' instead.`,
|
||||||
);
|
);
|
||||||
const icon = props.icon ? props.icon : iconType;
|
const icon = props.icon ? props.icon : iconType;
|
||||||
|
@ -92,9 +93,9 @@ export default {
|
||||||
<div class={`${contentPrefixCls}-body-wrapper`}>
|
<div class={`${contentPrefixCls}-body-wrapper`}>
|
||||||
<div class={`${contentPrefixCls}-body`}>
|
<div class={`${contentPrefixCls}-body`}>
|
||||||
{iconNode}
|
{iconNode}
|
||||||
<span class={`${contentPrefixCls}-title`}>
|
{props.title === undefined ? null : (
|
||||||
{typeof props.title === 'function' ? props.title(h) : props.title}
|
<span class={`${contentPrefixCls}-title`}>{props.title}</span>
|
||||||
</span>
|
)}
|
||||||
<div class={`${contentPrefixCls}-content`}>
|
<div class={`${contentPrefixCls}-content`}>
|
||||||
{typeof props.content === 'function' ? props.content(h) : props.content}
|
{typeof props.content === 'function' ? props.content(h) : props.content}
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -19,7 +19,23 @@ import {
|
||||||
import { ConfigConsumerProps } from '../config-provider';
|
import { ConfigConsumerProps } from '../config-provider';
|
||||||
|
|
||||||
let mousePosition = null;
|
let mousePosition = null;
|
||||||
let mousePositionEventBinded = false;
|
// 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 (typeof window !== 'undefined' && window.document && window.document.documentElement) {
|
||||||
|
addEventListener(document.documentElement, 'click', getClickPosition);
|
||||||
|
}
|
||||||
|
|
||||||
function noop() {}
|
function noop() {}
|
||||||
const modalProps = (defaultProps = {}) => {
|
const modalProps = (defaultProps = {}) => {
|
||||||
const props = {
|
const props = {
|
||||||
|
@ -32,6 +48,7 @@ const modalProps = (defaultProps = {}) => {
|
||||||
title: PropTypes.any,
|
title: PropTypes.any,
|
||||||
/** 是否显示右上角的关闭按钮*/
|
/** 是否显示右上角的关闭按钮*/
|
||||||
closable: PropTypes.bool,
|
closable: PropTypes.bool,
|
||||||
|
closeIcon: PropTypes.any,
|
||||||
/** 点击确定回调*/
|
/** 点击确定回调*/
|
||||||
// onOk: (e: React.MouseEvent<any>) => void,
|
// onOk: (e: React.MouseEvent<any>) => void,
|
||||||
/** 点击模态框右上角叉、取消按钮、Props.maskClosable 值为 true 时的遮罩层或键盘按下 Esc 时的回调*/
|
/** 点击模态框右上角叉、取消按钮、Props.maskClosable 值为 true 时的遮罩层或键盘按下 Esc 时的回调*/
|
||||||
|
@ -88,31 +105,23 @@ export default {
|
||||||
confirmLoading: false,
|
confirmLoading: false,
|
||||||
visible: false,
|
visible: false,
|
||||||
okType: 'primary',
|
okType: 'primary',
|
||||||
// okButtonDisabled: false,
|
|
||||||
// cancelButtonDisabled: false,
|
|
||||||
}),
|
}),
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
sVisible: !!this.visible,
|
||||||
|
};
|
||||||
|
},
|
||||||
|
watch: {
|
||||||
|
visible(val) {
|
||||||
|
// 点击位置动画 getClickPosition 在 render 后执行,需要延迟触发render
|
||||||
|
setTimeout(() => {
|
||||||
|
this.sVisible = val;
|
||||||
|
});
|
||||||
|
},
|
||||||
|
},
|
||||||
inject: {
|
inject: {
|
||||||
configProvider: { default: () => ConfigConsumerProps },
|
configProvider: { default: () => ConfigConsumerProps },
|
||||||
},
|
},
|
||||||
mounted() {
|
|
||||||
if (mousePositionEventBinded) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
// 只有点击事件支持从鼠标位置动画展开
|
|
||||||
addEventListener(document.documentElement, 'click', e => {
|
|
||||||
mousePosition = {
|
|
||||||
x: e.pageX,
|
|
||||||
y: e.pageY,
|
|
||||||
};
|
|
||||||
// 100ms 内发生过点击事件,则从点击位置动画展示
|
|
||||||
// 否则直接 zoom 展示
|
|
||||||
// 这样可以兼容非点击方式展开
|
|
||||||
setTimeout(() => {
|
|
||||||
mousePosition = null;
|
|
||||||
}, 100);
|
|
||||||
});
|
|
||||||
mousePositionEventBinded = true;
|
|
||||||
},
|
|
||||||
// static info: ModalFunc;
|
// static info: ModalFunc;
|
||||||
// static success: ModalFunc;
|
// static success: ModalFunc;
|
||||||
// static error: ModalFunc;
|
// static error: ModalFunc;
|
||||||
|
@ -158,14 +167,16 @@ export default {
|
||||||
render() {
|
render() {
|
||||||
const {
|
const {
|
||||||
prefixCls: customizePrefixCls,
|
prefixCls: customizePrefixCls,
|
||||||
visible,
|
sVisible: visible,
|
||||||
wrapClassName,
|
wrapClassName,
|
||||||
centered,
|
centered,
|
||||||
|
getContainer,
|
||||||
$slots,
|
$slots,
|
||||||
|
$scopedSlots,
|
||||||
$attrs,
|
$attrs,
|
||||||
} = this;
|
} = this;
|
||||||
|
const children = $scopedSlots.default ? $scopedSlots.default() : $slots.default;
|
||||||
const getPrefixCls = this.configProvider.getPrefixCls;
|
const { getPrefixCls, getPopupContainer: getContextPopupContainer } = this.configProvider;
|
||||||
const prefixCls = getPrefixCls('modal', customizePrefixCls);
|
const prefixCls = getPrefixCls('modal', customizePrefixCls);
|
||||||
|
|
||||||
const defaultFooter = (
|
const defaultFooter = (
|
||||||
|
@ -175,9 +186,10 @@ export default {
|
||||||
scopedSlots={{ default: this.renderFooter }}
|
scopedSlots={{ default: this.renderFooter }}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
const closeIcon = (
|
const closeIcon = getComponentFromProp(this, 'closeIcon');
|
||||||
|
const closeIconToRender = (
|
||||||
<span class={`${prefixCls}-close-x`}>
|
<span class={`${prefixCls}-close-x`}>
|
||||||
<Icon class={`${prefixCls}-close-icon`} type={'close'} />
|
{closeIcon || <Icon class={`${prefixCls}-close-icon`} type={'close'} />}
|
||||||
</span>
|
</span>
|
||||||
);
|
);
|
||||||
const footer = getComponentFromProp(this, 'footer');
|
const footer = getComponentFromProp(this, 'footer');
|
||||||
|
@ -185,13 +197,14 @@ export default {
|
||||||
const dialogProps = {
|
const dialogProps = {
|
||||||
props: {
|
props: {
|
||||||
...this.$props,
|
...this.$props,
|
||||||
|
getContainer: getContainer === undefined ? getContextPopupContainer : getContainer,
|
||||||
prefixCls,
|
prefixCls,
|
||||||
wrapClassName: classNames({ [`${prefixCls}-centered`]: !!centered }, wrapClassName),
|
wrapClassName: classNames({ [`${prefixCls}-centered`]: !!centered }, wrapClassName),
|
||||||
title,
|
title,
|
||||||
footer: footer === undefined ? defaultFooter : footer,
|
footer: footer === undefined ? defaultFooter : footer,
|
||||||
visible: visible,
|
visible: visible,
|
||||||
mousePosition,
|
mousePosition,
|
||||||
closeIcon,
|
closeIcon: closeIconToRender,
|
||||||
},
|
},
|
||||||
on: {
|
on: {
|
||||||
...getListeners(this),
|
...getListeners(this),
|
||||||
|
@ -201,6 +214,6 @@ export default {
|
||||||
style: getStyle(this),
|
style: getStyle(this),
|
||||||
attrs: $attrs,
|
attrs: $attrs,
|
||||||
};
|
};
|
||||||
return <Dialog {...dialogProps}>{$slots.default}</Dialog>;
|
return <Dialog {...dialogProps}>{children}</Dialog>;
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
import { mount } from '@vue/test-utils';
|
import { mount } from '@vue/test-utils';
|
||||||
import Vue from 'vue';
|
import Vue from 'vue';
|
||||||
import Modal from '..';
|
import Modal from '..';
|
||||||
|
import mountTest from '../../../tests/shared/mountTest';
|
||||||
|
|
||||||
const ModalTester = {
|
const ModalTester = {
|
||||||
props: ['footer', 'visible'],
|
props: ['footer', 'visible'],
|
||||||
|
@ -26,6 +27,7 @@ const ModalTester = {
|
||||||
};
|
};
|
||||||
|
|
||||||
describe('Modal', () => {
|
describe('Modal', () => {
|
||||||
|
mountTest(Modal);
|
||||||
it('render correctly', done => {
|
it('render correctly', done => {
|
||||||
const wrapper = mount({
|
const wrapper = mount({
|
||||||
render() {
|
render() {
|
||||||
|
@ -38,10 +40,10 @@ describe('Modal', () => {
|
||||||
sync: false,
|
sync: false,
|
||||||
});
|
});
|
||||||
wrapper1.setProps({ visible: true });
|
wrapper1.setProps({ visible: true });
|
||||||
Vue.nextTick(() => {
|
setTimeout(() => {
|
||||||
expect(wrapper1.html()).toMatchSnapshot();
|
expect(wrapper1.html()).toMatchSnapshot();
|
||||||
done();
|
done();
|
||||||
});
|
}, 10);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('render without footer', () => {
|
it('render without footer', () => {
|
||||||
|
|
|
@ -5,7 +5,7 @@
|
||||||
|
|
||||||
<us>
|
<us>
|
||||||
#### Confirmation modal dialog
|
#### Confirmation modal dialog
|
||||||
To use `confirm()` to popup a confirmation modal dialog.
|
To use `confirm()` to show a confirmation modal dialog.
|
||||||
</us>
|
</us>
|
||||||
|
|
||||||
```tpl
|
```tpl
|
||||||
|
|
|
@ -5,7 +5,7 @@
|
||||||
|
|
||||||
<us>
|
<us>
|
||||||
#### Manual to update destroy
|
#### Manual to update destroy
|
||||||
Manually updateing and destroying a modal from `Modal.method`.
|
Manually updating and destroying a modal from `Modal.method`.
|
||||||
</us>
|
</us>
|
||||||
|
|
||||||
```tpl
|
```tpl
|
||||||
|
|
|
@ -1,29 +1,30 @@
|
||||||
## API
|
## API
|
||||||
|
|
||||||
| Property | Description | Type | Default |
|
| Property | Description | Type | Default | Version |
|
||||||
| --- | --- | --- | --- |
|
| --- | --- | --- | --- | --- |
|
||||||
| afterClose | Specify a function that will be called when modal is closed completely. | function | - |
|
| afterClose | Specify a function that will be called when modal is closed completely. | function | - | |
|
||||||
| bodyStyle | Body style for modal body element. Such as height, padding etc. | object | {} |
|
| bodyStyle | Body style for modal body element. Such as height, padding etc. | object | {} | |
|
||||||
| cancelText | Text of the Cancel button | string\|slot | `Cancel` |
|
| cancelText | Text of the Cancel button | string\|slot | `Cancel` | |
|
||||||
| centered | Centered Modal | Boolean | `false` |
|
| centered | Centered Modal | Boolean | `false` | |
|
||||||
| closable | Whether a close (x) button is visible on top right of the modal dialog or not | boolean | true |
|
| closable | Whether a close (x) button is visible on top right of the modal dialog or not | boolean | true | |
|
||||||
| confirmLoading | Whether to apply loading visual effect for OK button or not | boolean | false |
|
| closeIcon | custom close icon | VNode \| slot | - | 1.5.0 |
|
||||||
| destroyOnClose | Whether to unmount child components on onClose | boolean | false |
|
| confirmLoading | Whether to apply loading visual effect for OK button or not | boolean | false | |
|
||||||
| footer | Footer content, set as `:footer="null"` when you don't need default buttons | string\|slot | OK and Cancel buttons |
|
| destroyOnClose | Whether to unmount child components on onClose | boolean | false | |
|
||||||
| forceRender | Force render Modal | boolean | false |
|
| footer | Footer content, set as `:footer="null"` when you don't need default buttons | string\|slot | OK and Cancel buttons | |
|
||||||
| getContainer | Return the mount node for Modal | (instance): HTMLElement | () => document.body |
|
| forceRender | Force render Modal | boolean | false | |
|
||||||
| mask | Whether show mask or not. | Boolean | true |
|
| getContainer | Return the mount node for Modal | (instance): HTMLElement | () => document.body | |
|
||||||
| maskClosable | Whether to close the modal dialog when the mask (area outside the modal) is clicked | boolean | true |
|
| mask | Whether show mask or not. | Boolean | true | |
|
||||||
| maskStyle | Style for modal's mask element. | object | {} |
|
| maskClosable | Whether to close the modal dialog when the mask (area outside the modal) is clicked | boolean | true | |
|
||||||
| okText | Text of the OK button | string\|slot | `OK` |
|
| maskStyle | Style for modal's mask element. | object | {} | |
|
||||||
| okType | Button `type` of the OK button | string | `primary` |
|
| okText | Text of the OK button | string\|slot | `OK` | |
|
||||||
| okButtonProps | The ok button props, follow jsx [rules](https://github.com/vuejs/babel-plugin-transform-vue-jsx#difference-from-react-jsx) | {props: [ButtonProps](/components/button/#API), on: {}} | - |
|
| okType | Button `type` of the OK button | string | `primary` | |
|
||||||
| cancelButtonProps | The cancel button props, follow jsx [rules](https://github.com/vuejs/babel-plugin-transform-vue-jsx#difference-from-react-jsx) | {props: [ButtonProps](/components/button/#API), on: {}} | - |
|
| okButtonProps | The ok button props, follow jsx [rules](https://github.com/vuejs/babel-plugin-transform-vue-jsx#difference-from-react-jsx) | {props: [ButtonProps](/components/button/#API), on: {}} | - | |
|
||||||
| title | The modal dialog's title | string\|slot | - |
|
| cancelButtonProps | The cancel button props, follow jsx [rules](https://github.com/vuejs/babel-plugin-transform-vue-jsx#difference-from-react-jsx) | {props: [ButtonProps](/components/button/#API), on: {}} | - | |
|
||||||
| visible | Whether the modal dialog is visible or not | boolean | false |
|
| title | The modal dialog's title | string\|slot | - | |
|
||||||
| width | Width of the modal dialog | string\|number | 520 |
|
| visible | Whether the modal dialog is visible or not | boolean | false | |
|
||||||
| wrapClassName | The class name of the container of the modal dialog | string | - |
|
| width | Width of the modal dialog | string\|number | 520 | |
|
||||||
| zIndex | The `z-index` of the Modal | Number | 1000 |
|
| wrapClassName | The class name of the container of the modal dialog | string | - | |
|
||||||
|
| zIndex | The `z-index` of the Modal | Number | 1000 | |
|
||||||
|
|
||||||
### events
|
### events
|
||||||
|
|
||||||
|
|
|
@ -55,7 +55,7 @@ const warning = function(props) {
|
||||||
};
|
};
|
||||||
const warn = warning;
|
const warn = warning;
|
||||||
|
|
||||||
const confirm = function(props) {
|
const confirm = function confirmFn(props) {
|
||||||
const config = {
|
const config = {
|
||||||
type: 'confirm',
|
type: 'confirm',
|
||||||
okCancel: true,
|
okCancel: true,
|
||||||
|
@ -70,7 +70,7 @@ Modal.warning = warning;
|
||||||
Modal.warn = warn;
|
Modal.warn = warn;
|
||||||
Modal.confirm = confirm;
|
Modal.confirm = confirm;
|
||||||
|
|
||||||
Modal.destroyAll = function() {
|
Modal.destroyAll = function destroyAllFn() {
|
||||||
while (destroyFns.length) {
|
while (destroyFns.length) {
|
||||||
const close = destroyFns.pop();
|
const close = destroyFns.pop();
|
||||||
if (close) {
|
if (close) {
|
||||||
|
|
|
@ -1,30 +1,31 @@
|
||||||
## API
|
## API
|
||||||
|
|
||||||
| 参数 | 说明 | 类型 | 默认值 |
|
| 参数 | 说明 | 类型 | 默认值 | 版本 |
|
||||||
| --- | --- | --- | --- |
|
| --- | --- | --- | --- | --- |
|
||||||
| afterClose | Modal 完全关闭后的回调 | function | 无 |
|
| afterClose | Modal 完全关闭后的回调 | function | 无 | |
|
||||||
| bodyStyle | Modal body 样式 | object | {} |
|
| bodyStyle | Modal body 样式 | object | {} | |
|
||||||
| cancelText | 取消按钮文字 | string\| slot | 取消 |
|
| cancelText | 取消按钮文字 | string\| slot | 取消 | |
|
||||||
| centered | 垂直居中展示 Modal | Boolean | `false` |
|
| centered | 垂直居中展示 Modal | Boolean | `false` | |
|
||||||
| closable | 是否显示右上角的关闭按钮 | boolean | true |
|
| closable | 是否显示右上角的关闭按钮 | boolean | true | |
|
||||||
| confirmLoading | 确定按钮 loading | boolean | 无 |
|
| closeIcon | 自定义关闭图标 | VNode \| slot | - | 1.5.0 |
|
||||||
| destroyOnClose | 关闭时销毁 Modal 里的子元素 | boolean | false |
|
| confirmLoading | 确定按钮 loading | boolean | 无 | |
|
||||||
| footer | 底部内容,当不需要默认底部按钮时,可以设为 `:footer="null"` | string\|slot | 确定取消按钮 |
|
| destroyOnClose | 关闭时销毁 Modal 里的子元素 | boolean | false | |
|
||||||
| forceRender | 强制渲染 Modal | boolean | false |
|
| footer | 底部内容,当不需要默认底部按钮时,可以设为 `:footer="null"` | string\|slot | 确定取消按钮 | |
|
||||||
| getContainer | 指定 Modal 挂载的 HTML 节点 | (instance): HTMLElement | () => document.body |
|
| forceRender | 强制渲染 Modal | boolean | false | |
|
||||||
| keyboard | 是否支持键盘 esc 关闭 | boolean | true |
|
| getContainer | 指定 Modal 挂载的 HTML 节点 | (instance): HTMLElement | () => document.body | |
|
||||||
| mask | 是否展示遮罩 | Boolean | true |
|
| keyboard | 是否支持键盘 esc 关闭 | boolean | true | |
|
||||||
| maskClosable | 点击蒙层是否允许关闭 | boolean | true |
|
| mask | 是否展示遮罩 | Boolean | true | |
|
||||||
| maskStyle | 遮罩样式 | object | {} |
|
| maskClosable | 点击蒙层是否允许关闭 | boolean | true | |
|
||||||
| okText | 确认按钮文字 | string\|slot | 确定 |
|
| maskStyle | 遮罩样式 | object | {} | |
|
||||||
| okType | 确认按钮类型 | string | primary |
|
| okText | 确认按钮文字 | string\|slot | 确定 | |
|
||||||
| okButtonProps | ok 按钮 props, 遵循 jsx[规范](https://github.com/vuejs/babel-plugin-transform-vue-jsx#difference-from-react-jsx) | {props: [ButtonProps](/components/button/#API), on: {}} | - |
|
| okType | 确认按钮类型 | string | primary | |
|
||||||
| cancelButtonProps | cancel 按钮 props, 遵循 jsx[规范](https://github.com/vuejs/babel-plugin-transform-vue-jsx#difference-from-react-jsx) | {props: [ButtonProps](/components/button/#API), on: {}} | - |
|
| okButtonProps | ok 按钮 props, 遵循 jsx[规范](https://github.com/vuejs/babel-plugin-transform-vue-jsx#difference-from-react-jsx) | {props: [ButtonProps](/components/button/#API), on: {}} | - | |
|
||||||
| title | 标题 | string\|slot | 无 |
|
| cancelButtonProps | cancel 按钮 props, 遵循 jsx[规范](https://github.com/vuejs/babel-plugin-transform-vue-jsx#difference-from-react-jsx) | {props: [ButtonProps](/components/button/#API), on: {}} | - | |
|
||||||
| visible(v-model) | 对话框是否可见 | boolean | 无 |
|
| title | 标题 | string\|slot | 无 | |
|
||||||
| width | 宽度 | string\|number | 520 |
|
| visible(v-model) | 对话框是否可见 | boolean | 无 | |
|
||||||
| wrapClassName | 对话框外层容器的类名 | string | - |
|
| width | 宽度 | string\|number | 520 | |
|
||||||
| zIndex | 设置 Modal 的 `z-index` | Number | 1000 |
|
| wrapClassName | 对话框外层容器的类名 | string | - | |
|
||||||
|
| zIndex | 设置 Modal 的 `z-index` | Number | 1000 | |
|
||||||
|
|
||||||
### 事件
|
### 事件
|
||||||
|
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
import defaultLocale from '../locale-provider/default';
|
import defaultLocale from '../locale/default';
|
||||||
|
|
||||||
// export interface ModalLocale {
|
// export interface ModalLocale {
|
||||||
// okText: string;
|
// okText: string;
|
||||||
|
|
|
@ -179,6 +179,8 @@ export declare class Modal extends AntdComponent {
|
||||||
*/
|
*/
|
||||||
closable: boolean;
|
closable: boolean;
|
||||||
|
|
||||||
|
closeIcon: any;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Whether to apply loading visual effect for OK button or not
|
* Whether to apply loading visual effect for OK button or not
|
||||||
* @default false
|
* @default false
|
||||||
|
|
Loading…
Reference in New Issue