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

View File

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

View File

@ -1,9 +1,16 @@
import classNames from '../_util/classNames';
import Dialog from './Modal';
import Dialog, { ModalFuncProps } from './Modal';
import ActionButton from './ActionButton';
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 {
icon,
onCancel,
@ -18,30 +25,30 @@ const ConfirmDialog = (_, { attrs }) => {
maskStyle,
okButtonProps,
cancelButtonProps,
closable = false,
} = attrs;
const okType = attrs.okType || 'primary';
const prefixCls = attrs.prefixCls || 'ant-modal';
// closable = false,
} = props;
const okType = props.okType || 'primary';
const prefixCls = props.prefixCls || 'ant-modal';
const contentPrefixCls = `${prefixCls}-confirm`;
// 默认为 true保持向下兼容
const okCancel = 'okCancel' in attrs ? attrs.okCancel : true;
const width = attrs.width || 416;
const style = attrs.style || {};
const mask = attrs.mask === undefined ? true : attrs.mask;
const okCancel = 'okCancel' in props ? props.okCancel : true;
const width = props.width || 416;
const style = props.style || {};
const mask = props.mask === undefined ? true : props.mask;
// 默认为 false保持旧版默认行为
const maskClosable = attrs.maskClosable === undefined ? false : attrs.maskClosable;
const maskClosable = props.maskClosable === undefined ? false : props.maskClosable;
const runtimeLocale = getConfirmLocale();
const okText = attrs.okText || (okCancel ? runtimeLocale.okText : runtimeLocale.justOkText);
const cancelText = attrs.cancelText || runtimeLocale.cancelText;
const autoFocusButton = attrs.autoFocusButton === null ? false : attrs.autoFocusButton || 'ok';
const transitionName = attrs.transitionName || 'zoom';
const maskTransitionName = attrs.maskTransitionName || 'fade';
const okText = props.okText || (okCancel ? runtimeLocale.okText : runtimeLocale.justOkText);
const cancelText = props.cancelText || runtimeLocale.cancelText;
const autoFocusButton = props.autoFocusButton === null ? false : props.autoFocusButton || 'ok';
const transitionName = props.transitionName || 'zoom';
const maskTransitionName = props.maskTransitionName || 'fade';
const classString = classNames(
contentPrefixCls,
`${contentPrefixCls}-${attrs.type}`,
`${prefixCls}-${attrs.type}`,
attrs.class,
`${contentPrefixCls}-${props.type}`,
`${prefixCls}-${props.type}`,
props.class,
);
const cancelButton = okCancel && (
@ -62,7 +69,6 @@ const ConfirmDialog = (_, { attrs }) => {
wrapClassName={classNames({ [`${contentPrefixCls}-centered`]: !!centered })}
onCancel={e => close({ triggerCancel: true }, e)}
visible={visible}
closable={closable}
title=""
transitionName={transitionName}
footer=""
@ -80,13 +86,11 @@ const ConfirmDialog = (_, { attrs }) => {
>
<div class={`${contentPrefixCls}-body-wrapper`}>
<div class={`${contentPrefixCls}-body`}>
{typeof icon === 'function' ? icon() : icon}
{attrs.title === undefined ? null : (
<span class={`${contentPrefixCls}-title`}>{attrs.title}</span>
{icon}
{props.title === undefined ? null : (
<span class={`${contentPrefixCls}-title`}>{props.title}</span>
)}
<div class={`${contentPrefixCls}-content`}>
{typeof attrs.content === 'function' ? attrs.content() : attrs.content}
</div>
<div class={`${contentPrefixCls}-content`}>{props.content}</div>
</div>
<div class={`${contentPrefixCls}-btns`}>
{cancelButton}
@ -104,5 +108,7 @@ const ConfirmDialog = (_, { attrs }) => {
</Dialog>
);
};
ConfirmDialog.inheritAttrs = false;
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 Dialog from '../vc-dialog';
import PropTypes from '../_util/vue-types';
@ -6,15 +13,18 @@ import addEventListener from '../vc-util/Dom/addEventListener';
import { getConfirmLocale } from './locale';
import CloseOutlined from '@ant-design/icons-vue/CloseOutlined';
import Button from '../button';
import buttonTypes from '../button/buttonTypes';
const ButtonType = buttonTypes().type;
import buttonTypes, { ButtonType, ButtonProps } from '../button/buttonTypes';
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';
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
const getClickPosition = e => {
const getClickPosition = (e: MouseEvent) => {
mousePosition = {
x: e.pageX,
y: e.pageY,
@ -31,68 +41,121 @@ if (typeof window !== 'undefined' && window.document && window.document.document
}
function noop() {}
const modalProps = (defaultProps = {}) => {
const props = {
prefixCls: PropTypes.string,
/** 对话框是否可见*/
visible: PropTypes.looseBool,
/** 确定按钮 loading*/
confirmLoading: PropTypes.looseBool,
/** 标题*/
title: PropTypes.any,
/** 是否显示右上角的关闭按钮*/
closable: PropTypes.looseBool,
closeIcon: PropTypes.any,
/** 点击确定回调*/
// onOk: (e: React.MouseEvent<any>) => void,
/** 点击模态框右上角叉、取消按钮、Props.maskClosable 值为 true 时的遮罩层或键盘按下 Esc 时的回调*/
// onCancel: (e: React.MouseEvent<any>) => void,
afterClose: PropTypes.func.def(noop),
/** 垂直居中 */
centered: PropTypes.looseBool,
/** 宽度*/
width: PropTypes.oneOfType([PropTypes.string, PropTypes.number]),
/** 底部内容*/
footer: PropTypes.any,
/** 确认按钮文字*/
okText: PropTypes.any,
/** 确认按钮类型*/
okType: ButtonType,
/** 取消按钮文字*/
cancelText: PropTypes.any,
icon: PropTypes.any,
/** 点击蒙层是否允许关闭*/
maskClosable: PropTypes.looseBool,
/** 强制渲染 Modal*/
forceRender: PropTypes.looseBool,
okButtonProps: PropTypes.object,
cancelButtonProps: PropTypes.object,
destroyOnClose: PropTypes.looseBool,
wrapClassName: PropTypes.string,
maskTransitionName: PropTypes.string,
transitionName: PropTypes.string,
getContainer: PropTypes.func,
zIndex: PropTypes.number,
bodyStyle: PropTypes.object,
maskStyle: PropTypes.object,
mask: PropTypes.looseBool,
keyboard: PropTypes.looseBool,
wrapProps: PropTypes.object,
focusTriggerAfterClose: PropTypes.looseBool,
};
return initDefaultProps(props, defaultProps);
const modalProps = {
prefixCls: PropTypes.string,
/** 对话框是否可见*/
visible: PropTypes.looseBool,
/** 确定按钮 loading*/
confirmLoading: PropTypes.looseBool,
/** 标题*/
title: PropTypes.any,
/** 是否显示右上角的关闭按钮*/
closable: PropTypes.looseBool,
closeIcon: PropTypes.any,
/** 点击确定回调*/
onOk: {
type: Function as PropType<(e: MouseEvent) => void>,
},
/** 点击模态框右上角叉、取消按钮、Props.maskClosable 值为 true 时的遮罩层或键盘按下 Esc 时的回调*/
onCancel: {
type: Function as PropType<(e: MouseEvent) => void>,
},
afterClose: PropTypes.func.def(noop),
/** 垂直居中 */
centered: PropTypes.looseBool,
/** 宽度*/
width: PropTypes.oneOfType([PropTypes.string, PropTypes.number]),
/** 底部内容*/
footer: PropTypes.any,
/** 确认按钮文字*/
okText: PropTypes.any,
/** 确认按钮类型*/
okType: ButtonType,
/** 取消按钮文字*/
cancelText: PropTypes.any,
icon: PropTypes.any,
/** 点击蒙层是否允许关闭*/
maskClosable: PropTypes.looseBool,
/** 强制渲染 Modal*/
forceRender: PropTypes.looseBool,
okButtonProps: PropTypes.shape(ButtonProps),
cancelButtonProps: PropTypes.shape(ButtonProps),
destroyOnClose: PropTypes.looseBool,
wrapClassName: PropTypes.string,
maskTransitionName: PropTypes.string,
transitionName: PropTypes.string,
getContainer: PropTypes.func,
zIndex: PropTypes.number,
bodyStyle: PropTypes.style,
maskStyle: PropTypes.style,
mask: PropTypes.looseBool,
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 default {
export default defineComponent({
name: 'AModal',
inheritAttrs: false,
model: {
prop: 'visible',
event: 'change',
},
props: modalProps({
emits: ['update:visible', 'cancel', 'change', 'ok'],
props: initDefaultProps(modalProps, {
width: 520,
transitionName: 'zoom',
maskTransitionName: 'fade',
@ -115,23 +178,17 @@ export default {
configProvider: inject('configProvider', defaultConfigProvider),
};
},
// static info: ModalFunc;
// static success: ModalFunc;
// static error: ModalFunc;
// static warn: ModalFunc;
// static warning: ModalFunc;
// static confirm: ModalFunc;
methods: {
handleCancel(e) {
handleCancel(e: MouseEvent) {
this.$emit('update:visible', false);
this.$emit('cancel', e);
this.$emit('change', false);
},
handleOk(e) {
handleOk(e: MouseEvent) {
this.$emit('ok', e);
},
renderFooter(locale) {
renderFooter(locale: ModalLocale) {
const { okType, confirmLoading } = this;
const cancelBtnProps = { onClick: this.handleCancel, ...(this.cancelButtonProps || {}) };
const okBtnProps = {
@ -194,4 +251,4 @@ export default {
};
return <Dialog {...dialogProps}>{children}</Dialog>;
},
};
});

View File

@ -1,17 +1,17 @@
import { createApp } from 'vue';
import ConfirmDialog from './ConfirmDialog';
import { destroyFns } from './Modal';
import { destroyFns, ModalFuncProps } from './Modal';
import Omit from 'omit.js';
export default function confirm(config) {
export default function confirm(config: ModalFuncProps) {
const div = document.createElement('div');
document.body.appendChild(div);
let currentConfig = { ...Omit(config, ['parentContext']), close, visible: true };
let confirmDialogInstance = null;
let confirmDialogProps = {};
function close(...args) {
function close(this: typeof close, ...args: any[]) {
currentConfig = {
...currentConfig,
visible: false,
@ -19,7 +19,7 @@ export default function confirm(config) {
};
update(currentConfig);
}
function update(newConfig) {
function update(newConfig: ModalFuncProps) {
currentConfig = {
...currentConfig,
...newConfig,
@ -27,7 +27,7 @@ export default function confirm(config) {
confirmDialogInstance &&
Object.assign(confirmDialogInstance, { confirmDialogProps: currentConfig });
}
function destroy(...args) {
function destroy(...args: any[]) {
if (confirmDialogInstance && div.parentNode) {
confirmDialogInstance.vIf = false; // hack destroy
confirmDialogInstance = null;
@ -46,10 +46,10 @@ export default function confirm(config) {
}
}
function render(props) {
function render(props: ModalFuncProps) {
confirmDialogProps = props;
return createApp({
parent: config.parentContext,
parent: (config as any).parentContext,
data() {
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 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';
// export { ActionButtonProps } from './ActionButton'
// export { ModalProps, ModalFuncProps } from './Modal'
export { IActionButtonProps as ActionButtonProps } from './ActionButton';
export { ModalProps, ModalFuncProps } from './Modal';
const info = function(props) {
const info = function(props: ModalFuncProps) {
const config = {
type: 'info',
icon: <InfoCircleOutlined />,
@ -18,7 +19,7 @@ const info = function(props) {
return modalConfirm(config);
};
const success = function(props) {
const success = function(props: ModalFuncProps) {
const config = {
type: 'success',
icon: <CheckCircleOutlined />,
@ -28,7 +29,7 @@ const success = function(props) {
return modalConfirm(config);
};
const error = function(props) {
const error = function(props: ModalFuncProps) {
const config = {
type: 'error',
icon: <CloseCircleOutlined />,
@ -38,7 +39,7 @@ const error = function(props) {
return modalConfirm(config);
};
const warning = function(props) {
const warning = function(props: ModalFuncProps) {
const config = {
type: 'warning',
icon: <ExclamationCircleOutlined />,
@ -49,7 +50,7 @@ const warning = function(props) {
};
const warn = warning;
const confirm = function confirmFn(props) {
const confirm = function confirmFn(props: ModalFuncProps) {
const config = {
type: 'confirm',
okCancel: true,
@ -74,7 +75,7 @@ Modal.destroyAll = function destroyAllFn() {
};
/* istanbul ignore next */
Modal.install = function(app) {
Modal.install = function(app: App) {
app.component(Modal.name, Modal);
return app;
};

View File

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