feat: modal add closeIcon support

pull/1845/head
tangjinzhou 2020-02-22 13:22:24 +08:00
parent 078a3e4fff
commit e43f6905be
12 changed files with 113 additions and 90 deletions

View File

@ -1,5 +1,5 @@
module.exports = { module.exports = {
dev: { dev: {
componentName: 'message', // dev components componentName: 'modal', // dev components
}, },
}; };

View File

@ -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 });
}, },

View File

@ -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>

View File

@ -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>;
}, },
}; };

View File

@ -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', () => {

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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) {

View File

@ -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 | |
### 事件 ### 事件

View File

@ -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;

2
types/modal.d.ts vendored
View File

@ -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