refactor: modal to ts

feat-dayjs^2
Amour1688 2020-10-19 23:09:36 +08:00
parent 889ae305df
commit 6b80eb72da
8 changed files with 193 additions and 115 deletions

View File

@ -1,3 +1,4 @@
import { ExtractPropTypes } from 'vue';
import { tuple } from '../_util/type'; import { tuple } from '../_util/type';
import PropTypes, { withUndefined } from '../_util/vue-types'; import PropTypes, { withUndefined } from '../_util/vue-types';
@ -10,7 +11,7 @@ export type ButtonSize = typeof ButtonSizes[number];
const ButtonHTMLTypes = tuple('submit', 'button', 'reset'); const ButtonHTMLTypes = tuple('submit', 'button', 'reset');
export type ButtonHTMLType = typeof ButtonHTMLTypes[number]; export type ButtonHTMLType = typeof ButtonHTMLTypes[number];
export default () => ({ const buttonProps = () => ({
prefixCls: PropTypes.string, prefixCls: PropTypes.string,
type: PropTypes.oneOf(ButtonTypes), type: PropTypes.oneOf(ButtonTypes),
htmlType: PropTypes.oneOf(ButtonHTMLTypes).def('button'), htmlType: PropTypes.oneOf(ButtonHTMLTypes).def('button'),
@ -26,3 +27,7 @@ export default () => ({
title: PropTypes.string, title: PropTypes.string,
onClick: PropTypes.func, onClick: PropTypes.func,
}); });
export type ButtonProps = ExtractPropTypes<ExtractPropTypes<ReturnType<typeof buttonProps>>>;
export default buttonProps;

View File

@ -26,7 +26,7 @@ export default defineComponent({
}, },
children: { children: {
type: Function as PropType< type: Function as PropType<
(locale: object, localeCode?: string, fullLocale?: object) => VNodeTypes (locale: any, localeCode?: string, fullLocale?: object) => VNodeTypes
>, >,
}, },
}, },

View File

@ -1,9 +1,11 @@
import { defineComponent, ExtractPropTypes } from 'vue';
import PropTypes from '../_util/vue-types'; import PropTypes from '../_util/vue-types';
import Button from '../button'; import Button from '../button';
import BaseMixin from '../_util/BaseMixin'; import BaseMixin from '../_util/BaseMixin';
import buttonTypes from '../button/buttonTypes'; import buttonTypes from '../button/buttonTypes';
import { getSlot, findDOMNode } from '../_util/props-util'; import { getSlot, findDOMNode } from '../_util/props-util';
const ButtonType = buttonTypes().type; const ButtonType = buttonTypes().type;
const ActionButtonProps = { const ActionButtonProps = {
type: ButtonType, type: ButtonType,
actionFn: PropTypes.func, actionFn: PropTypes.func,
@ -12,7 +14,9 @@ const ActionButtonProps = {
buttonProps: PropTypes.object, buttonProps: PropTypes.object,
}; };
export default { export type IActionButtonProps = ExtractPropTypes<typeof ActionButtonProps>;
export default defineComponent({
mixins: [BaseMixin], mixins: [BaseMixin],
props: ActionButtonProps, props: ActionButtonProps,
data() { data() {
@ -20,6 +24,11 @@ export default {
loading: false, loading: false,
}; };
}, },
setup() {
return {
timeoutId: undefined,
};
},
mounted() { mounted() {
if (this.autofocus) { if (this.autofocus) {
this.timeoutId = setTimeout(() => findDOMNode(this).focus()); this.timeoutId = setTimeout(() => findDOMNode(this).focus());
@ -74,4 +83,4 @@ export default {
}; };
return <Button {...props}>{getSlot(this)}</Button>; return <Button {...props}>{getSlot(this)}</Button>;
}, },
}; });

View File

@ -1,9 +1,16 @@
import classNames from '../_util/classNames'; import classNames from '../_util/classNames';
import Dialog from './Modal'; import Dialog, { ModalFuncProps } from './Modal';
import ActionButton from './ActionButton'; import ActionButton from './ActionButton';
import { getConfirmLocale } from './locale'; import { getConfirmLocale } from './locale';
import { FunctionalComponent } from 'vue';
const ConfirmDialog = (_, { attrs }) => { interface ConfirmDialogProps extends ModalFuncProps {
afterClose?: () => void;
close: (...args: any[]) => void;
autoFocusButton?: null | 'ok' | 'cancel';
}
const ConfirmDialog: FunctionalComponent<ConfirmDialogProps> = props => {
const { const {
icon, icon,
onCancel, onCancel,
@ -18,30 +25,30 @@ const ConfirmDialog = (_, { attrs }) => {
maskStyle, maskStyle,
okButtonProps, okButtonProps,
cancelButtonProps, cancelButtonProps,
closable = false, // closable = false,
} = attrs; } = props;
const okType = attrs.okType || 'primary'; const okType = props.okType || 'primary';
const prefixCls = attrs.prefixCls || 'ant-modal'; const prefixCls = props.prefixCls || 'ant-modal';
const contentPrefixCls = `${prefixCls}-confirm`; const contentPrefixCls = `${prefixCls}-confirm`;
// 默认为 true保持向下兼容 // 默认为 true保持向下兼容
const okCancel = 'okCancel' in attrs ? attrs.okCancel : true; const okCancel = 'okCancel' in props ? props.okCancel : true;
const width = attrs.width || 416; const width = props.width || 416;
const style = attrs.style || {}; const style = props.style || {};
const mask = attrs.mask === undefined ? true : attrs.mask; const mask = props.mask === undefined ? true : props.mask;
// 默认为 false保持旧版默认行为 // 默认为 false保持旧版默认行为
const maskClosable = attrs.maskClosable === undefined ? false : attrs.maskClosable; const maskClosable = props.maskClosable === undefined ? false : props.maskClosable;
const runtimeLocale = getConfirmLocale(); const runtimeLocale = getConfirmLocale();
const okText = attrs.okText || (okCancel ? runtimeLocale.okText : runtimeLocale.justOkText); const okText = props.okText || (okCancel ? runtimeLocale.okText : runtimeLocale.justOkText);
const cancelText = attrs.cancelText || runtimeLocale.cancelText; const cancelText = props.cancelText || runtimeLocale.cancelText;
const autoFocusButton = attrs.autoFocusButton === null ? false : attrs.autoFocusButton || 'ok'; const autoFocusButton = props.autoFocusButton === null ? false : props.autoFocusButton || 'ok';
const transitionName = attrs.transitionName || 'zoom'; const transitionName = props.transitionName || 'zoom';
const maskTransitionName = attrs.maskTransitionName || 'fade'; const maskTransitionName = props.maskTransitionName || 'fade';
const classString = classNames( const classString = classNames(
contentPrefixCls, contentPrefixCls,
`${contentPrefixCls}-${attrs.type}`, `${contentPrefixCls}-${props.type}`,
`${prefixCls}-${attrs.type}`, `${prefixCls}-${props.type}`,
attrs.class, props.class,
); );
const cancelButton = okCancel && ( const cancelButton = okCancel && (
@ -62,7 +69,6 @@ const ConfirmDialog = (_, { attrs }) => {
wrapClassName={classNames({ [`${contentPrefixCls}-centered`]: !!centered })} wrapClassName={classNames({ [`${contentPrefixCls}-centered`]: !!centered })}
onCancel={e => close({ triggerCancel: true }, e)} onCancel={e => close({ triggerCancel: true }, e)}
visible={visible} visible={visible}
closable={closable}
title="" title=""
transitionName={transitionName} transitionName={transitionName}
footer="" footer=""
@ -80,13 +86,11 @@ const ConfirmDialog = (_, { attrs }) => {
> >
<div class={`${contentPrefixCls}-body-wrapper`}> <div class={`${contentPrefixCls}-body-wrapper`}>
<div class={`${contentPrefixCls}-body`}> <div class={`${contentPrefixCls}-body`}>
{typeof icon === 'function' ? icon() : icon} {icon}
{attrs.title === undefined ? null : ( {props.title === undefined ? null : (
<span class={`${contentPrefixCls}-title`}>{attrs.title}</span> <span class={`${contentPrefixCls}-title`}>{props.title}</span>
)} )}
<div class={`${contentPrefixCls}-content`}> <div class={`${contentPrefixCls}-content`}>{props.content}</div>
{typeof attrs.content === 'function' ? attrs.content() : attrs.content}
</div>
</div> </div>
<div class={`${contentPrefixCls}-btns`}> <div class={`${contentPrefixCls}-btns`}>
{cancelButton} {cancelButton}
@ -104,5 +108,7 @@ const ConfirmDialog = (_, { attrs }) => {
</Dialog> </Dialog>
); );
}; };
ConfirmDialog.inheritAttrs = false; ConfirmDialog.inheritAttrs = false;
export default ConfirmDialog; export default ConfirmDialog;

View File

@ -1,4 +1,11 @@
import { inject } from 'vue'; import {
defineComponent,
ExtractPropTypes,
inject,
VNodeTypes,
CSSProperties,
PropType,
} from 'vue';
import classNames from '../_util/classNames'; import classNames from '../_util/classNames';
import Dialog from '../vc-dialog'; import Dialog from '../vc-dialog';
import PropTypes from '../_util/vue-types'; import PropTypes from '../_util/vue-types';
@ -6,15 +13,18 @@ import addEventListener from '../vc-util/Dom/addEventListener';
import { getConfirmLocale } from './locale'; import { getConfirmLocale } from './locale';
import CloseOutlined from '@ant-design/icons-vue/CloseOutlined'; import CloseOutlined from '@ant-design/icons-vue/CloseOutlined';
import Button from '../button'; import Button from '../button';
import buttonTypes from '../button/buttonTypes'; import buttonTypes, { ButtonType, ButtonProps } from '../button/buttonTypes';
const ButtonType = buttonTypes().type;
import LocaleReceiver from '../locale-provider/LocaleReceiver'; import LocaleReceiver from '../locale-provider/LocaleReceiver';
import { initDefaultProps, getComponent, getSlot } from '../_util/props-util'; import { getComponent, getSlot } from '../_util/props-util';
import initDefaultProps from '../_util/props-util/initDefaultProps';
import { defaultConfigProvider } from '../config-provider'; import { defaultConfigProvider } from '../config-provider';
let mousePosition = null; const ButtonProps = buttonTypes();
const ButtonType = ButtonProps.type;
let mousePosition: { x: number; y: number } | null = null;
// ref: https://github.com/ant-design/ant-design/issues/15795 // ref: https://github.com/ant-design/ant-design/issues/15795
const getClickPosition = e => { const getClickPosition = (e: MouseEvent) => {
mousePosition = { mousePosition = {
x: e.pageX, x: e.pageX,
y: e.pageY, y: e.pageY,
@ -31,68 +41,121 @@ if (typeof window !== 'undefined' && window.document && window.document.document
} }
function noop() {} function noop() {}
const modalProps = (defaultProps = {}) => {
const props = { const modalProps = {
prefixCls: PropTypes.string, prefixCls: PropTypes.string,
/** 对话框是否可见*/ /** 对话框是否可见*/
visible: PropTypes.looseBool, visible: PropTypes.looseBool,
/** 确定按钮 loading*/ /** 确定按钮 loading*/
confirmLoading: PropTypes.looseBool, confirmLoading: PropTypes.looseBool,
/** 标题*/ /** 标题*/
title: PropTypes.any, title: PropTypes.any,
/** 是否显示右上角的关闭按钮*/ /** 是否显示右上角的关闭按钮*/
closable: PropTypes.looseBool, closable: PropTypes.looseBool,
closeIcon: PropTypes.any, closeIcon: PropTypes.any,
/** 点击确定回调*/ /** 点击确定回调*/
// onOk: (e: React.MouseEvent<any>) => void, onOk: {
/** 点击模态框右上角叉、取消按钮、Props.maskClosable 值为 true 时的遮罩层或键盘按下 Esc 时的回调*/ type: Function as PropType<(e: MouseEvent) => void>,
// onCancel: (e: React.MouseEvent<any>) => void, },
afterClose: PropTypes.func.def(noop), /** 点击模态框右上角叉、取消按钮、Props.maskClosable 值为 true 时的遮罩层或键盘按下 Esc 时的回调*/
/** 垂直居中 */ onCancel: {
centered: PropTypes.looseBool, type: Function as PropType<(e: MouseEvent) => void>,
/** 宽度*/ },
width: PropTypes.oneOfType([PropTypes.string, PropTypes.number]), afterClose: PropTypes.func.def(noop),
/** 底部内容*/ /** 垂直居中 */
footer: PropTypes.any, centered: PropTypes.looseBool,
/** 确认按钮文字*/ /** 宽度*/
okText: PropTypes.any, width: PropTypes.oneOfType([PropTypes.string, PropTypes.number]),
/** 确认按钮类型*/ /** 底部内容*/
okType: ButtonType, footer: PropTypes.any,
/** 取消按钮文字*/ /** 确认按钮文字*/
cancelText: PropTypes.any, okText: PropTypes.any,
icon: PropTypes.any, /** 确认按钮类型*/
/** 点击蒙层是否允许关闭*/ okType: ButtonType,
maskClosable: PropTypes.looseBool, /** 取消按钮文字*/
/** 强制渲染 Modal*/ cancelText: PropTypes.any,
forceRender: PropTypes.looseBool, icon: PropTypes.any,
okButtonProps: PropTypes.object, /** 点击蒙层是否允许关闭*/
cancelButtonProps: PropTypes.object, maskClosable: PropTypes.looseBool,
destroyOnClose: PropTypes.looseBool, /** 强制渲染 Modal*/
wrapClassName: PropTypes.string, forceRender: PropTypes.looseBool,
maskTransitionName: PropTypes.string, okButtonProps: PropTypes.shape(ButtonProps),
transitionName: PropTypes.string, cancelButtonProps: PropTypes.shape(ButtonProps),
getContainer: PropTypes.func, destroyOnClose: PropTypes.looseBool,
zIndex: PropTypes.number, wrapClassName: PropTypes.string,
bodyStyle: PropTypes.object, maskTransitionName: PropTypes.string,
maskStyle: PropTypes.object, transitionName: PropTypes.string,
mask: PropTypes.looseBool, getContainer: PropTypes.func,
keyboard: PropTypes.looseBool, zIndex: PropTypes.number,
wrapProps: PropTypes.object, bodyStyle: PropTypes.style,
focusTriggerAfterClose: PropTypes.looseBool, maskStyle: PropTypes.style,
}; mask: PropTypes.looseBool,
return initDefaultProps(props, defaultProps); keyboard: PropTypes.looseBool,
wrapProps: PropTypes.object,
focusTriggerAfterClose: PropTypes.looseBool,
}; };
export type ModalProps = ExtractPropTypes<typeof modalProps>;
export interface ModalFuncProps {
prefixCls?: string;
class?: string;
visible?: boolean;
title?: VNodeTypes;
content?: VNodeTypes;
// TODO: find out exact types
onOk?: (...args: any[]) => any;
onCancel?: (...args: any[]) => any;
okButtonProps?: ButtonProps;
cancelButtonProps?: ButtonProps;
centered?: boolean;
width?: string | number;
okText?: VNodeTypes;
okType?: ButtonType;
cancelText?: VNodeTypes;
icon?: VNodeTypes;
/* Deprecated */
iconType?: string;
mask?: boolean;
maskClosable?: boolean;
zIndex?: number;
okCancel?: boolean;
style?: CSSProperties | string;
maskStyle?: CSSProperties;
type?: string;
keyboard?: boolean;
getContainer?: getContainerFunc;
autoFocusButton?: null | 'ok' | 'cancel';
transitionName?: string;
maskTransitionName?: string;
}
type getContainerFunc = () => HTMLElement;
export type ModalFunc = (
props: ModalFuncProps,
) => {
destroy: () => void;
update: (newConfig: ModalFuncProps) => void;
};
export interface ModalLocale {
okText: string;
cancelText: string;
justOkText: string;
}
export const destroyFns = []; export const destroyFns = [];
export default { export default defineComponent({
name: 'AModal', name: 'AModal',
inheritAttrs: false, inheritAttrs: false,
model: { model: {
prop: 'visible', prop: 'visible',
event: 'change', event: 'change',
}, },
props: modalProps({ emits: ['update:visible', 'cancel', 'change', 'ok'],
props: initDefaultProps(modalProps, {
width: 520, width: 520,
transitionName: 'zoom', transitionName: 'zoom',
maskTransitionName: 'fade', maskTransitionName: 'fade',
@ -115,23 +178,17 @@ export default {
configProvider: inject('configProvider', defaultConfigProvider), configProvider: inject('configProvider', defaultConfigProvider),
}; };
}, },
// static info: ModalFunc;
// static success: ModalFunc;
// static error: ModalFunc;
// static warn: ModalFunc;
// static warning: ModalFunc;
// static confirm: ModalFunc;
methods: { methods: {
handleCancel(e) { handleCancel(e: MouseEvent) {
this.$emit('update:visible', false); this.$emit('update:visible', false);
this.$emit('cancel', e); this.$emit('cancel', e);
this.$emit('change', false); this.$emit('change', false);
}, },
handleOk(e) { handleOk(e: MouseEvent) {
this.$emit('ok', e); this.$emit('ok', e);
}, },
renderFooter(locale) { renderFooter(locale: ModalLocale) {
const { okType, confirmLoading } = this; const { okType, confirmLoading } = this;
const cancelBtnProps = { onClick: this.handleCancel, ...(this.cancelButtonProps || {}) }; const cancelBtnProps = { onClick: this.handleCancel, ...(this.cancelButtonProps || {}) };
const okBtnProps = { const okBtnProps = {
@ -194,4 +251,4 @@ export default {
}; };
return <Dialog {...dialogProps}>{children}</Dialog>; return <Dialog {...dialogProps}>{children}</Dialog>;
}, },
}; });

View File

@ -1,17 +1,17 @@
import { createApp } from 'vue'; import { createApp } from 'vue';
import ConfirmDialog from './ConfirmDialog'; import ConfirmDialog from './ConfirmDialog';
import { destroyFns } from './Modal'; import { destroyFns, ModalFuncProps } from './Modal';
import Omit from 'omit.js'; import Omit from 'omit.js';
export default function confirm(config) { export default function confirm(config: ModalFuncProps) {
const div = document.createElement('div'); const div = document.createElement('div');
document.body.appendChild(div); document.body.appendChild(div);
let currentConfig = { ...Omit(config, ['parentContext']), close, visible: true }; let currentConfig = { ...Omit(config, ['parentContext']), close, visible: true };
let confirmDialogInstance = null; let confirmDialogInstance = null;
let confirmDialogProps = {}; let confirmDialogProps = {};
function close(...args) { function close(this: typeof close, ...args: any[]) {
currentConfig = { currentConfig = {
...currentConfig, ...currentConfig,
visible: false, visible: false,
@ -19,7 +19,7 @@ export default function confirm(config) {
}; };
update(currentConfig); update(currentConfig);
} }
function update(newConfig) { function update(newConfig: ModalFuncProps) {
currentConfig = { currentConfig = {
...currentConfig, ...currentConfig,
...newConfig, ...newConfig,
@ -27,7 +27,7 @@ export default function confirm(config) {
confirmDialogInstance && confirmDialogInstance &&
Object.assign(confirmDialogInstance, { confirmDialogProps: currentConfig }); Object.assign(confirmDialogInstance, { confirmDialogProps: currentConfig });
} }
function destroy(...args) { function destroy(...args: any[]) {
if (confirmDialogInstance && div.parentNode) { if (confirmDialogInstance && div.parentNode) {
confirmDialogInstance.vIf = false; // hack destroy confirmDialogInstance.vIf = false; // hack destroy
confirmDialogInstance = null; confirmDialogInstance = null;
@ -46,10 +46,10 @@ export default function confirm(config) {
} }
} }
function render(props) { function render(props: ModalFuncProps) {
confirmDialogProps = props; confirmDialogProps = props;
return createApp({ return createApp({
parent: config.parentContext, parent: (config as any).parentContext,
data() { data() {
return { confirmDialogProps, vIf: true }; return { confirmDialogProps, vIf: true };
}, },

View File

@ -1,14 +1,15 @@
import Modal, { destroyFns } from './Modal'; import { App } from 'vue';
import Modal, { destroyFns, ModalFuncProps } from './Modal';
import modalConfirm from './confirm'; import modalConfirm from './confirm';
import InfoCircleOutlined from '@ant-design/icons-vue/InfoCircleOutlined'; import InfoCircleOutlined from '@ant-design/icons-vue/InfoCircleOutlined';
import CheckCircleOutlined from '@ant-design/icons-vue/CheckCircleOutlined'; import CheckCircleOutlined from '@ant-design/icons-vue/CheckCircleOutlined';
import CloseCircleOutlined from '@ant-design/icons-vue/CloseCircleOutlined'; import CloseCircleOutlined from '@ant-design/icons-vue/CloseCircleOutlined';
import ExclamationCircleOutlined from '@ant-design/icons-vue/ExclamationCircleOutlined'; import ExclamationCircleOutlined from '@ant-design/icons-vue/ExclamationCircleOutlined';
// export { ActionButtonProps } from './ActionButton' export { IActionButtonProps as ActionButtonProps } from './ActionButton';
// export { ModalProps, ModalFuncProps } from './Modal' export { ModalProps, ModalFuncProps } from './Modal';
const info = function(props) { const info = function(props: ModalFuncProps) {
const config = { const config = {
type: 'info', type: 'info',
icon: <InfoCircleOutlined />, icon: <InfoCircleOutlined />,
@ -18,7 +19,7 @@ const info = function(props) {
return modalConfirm(config); return modalConfirm(config);
}; };
const success = function(props) { const success = function(props: ModalFuncProps) {
const config = { const config = {
type: 'success', type: 'success',
icon: <CheckCircleOutlined />, icon: <CheckCircleOutlined />,
@ -28,7 +29,7 @@ const success = function(props) {
return modalConfirm(config); return modalConfirm(config);
}; };
const error = function(props) { const error = function(props: ModalFuncProps) {
const config = { const config = {
type: 'error', type: 'error',
icon: <CloseCircleOutlined />, icon: <CloseCircleOutlined />,
@ -38,7 +39,7 @@ const error = function(props) {
return modalConfirm(config); return modalConfirm(config);
}; };
const warning = function(props) { const warning = function(props: ModalFuncProps) {
const config = { const config = {
type: 'warning', type: 'warning',
icon: <ExclamationCircleOutlined />, icon: <ExclamationCircleOutlined />,
@ -49,7 +50,7 @@ const warning = function(props) {
}; };
const warn = warning; const warn = warning;
const confirm = function confirmFn(props) { const confirm = function confirmFn(props: ModalFuncProps) {
const config = { const config = {
type: 'confirm', type: 'confirm',
okCancel: true, okCancel: true,
@ -74,7 +75,7 @@ Modal.destroyAll = function destroyAllFn() {
}; };
/* istanbul ignore next */ /* istanbul ignore next */
Modal.install = function(app) { Modal.install = function(app: App) {
app.component(Modal.name, Modal); app.component(Modal.name, Modal);
return app; return app;
}; };

View File

@ -10,7 +10,7 @@ let runtimeLocale = {
...defaultLocale.Modal, ...defaultLocale.Modal,
}; };
export function changeConfirmLocale(newLocale?: any) { export function changeConfirmLocale(newLocale?: ModalLocale) {
if (newLocale) { if (newLocale) {
runtimeLocale = { runtimeLocale = {
...runtimeLocale, ...runtimeLocale,