feat: notification support configprovider.config

pull/4675/head
tangjinzhou 2021-09-18 22:48:44 +08:00
parent 322158d2be
commit 456c35361c
8 changed files with 98 additions and 92 deletions

View File

@ -1,3 +1,4 @@
import type { VueNode } from './type';
export const isFunction = val => typeof val === 'function';
export const isArray = Array.isArray;
@ -67,4 +68,15 @@ export function toPx(val) {
return val;
}
export function renderHelper<T = Record<string, any>>(
v: VueNode | ((arg0: T) => VueNode),
props: T = {} as T,
defaultV?: any,
) {
if (typeof v === 'function') {
return v(props);
}
return v ?? defaultV;
}
export { isOn, cacheStringFunction, camelize, hyphenate, capitalize, resolvePropValue };

View File

@ -58,7 +58,7 @@ export const configConsumerProps = [
];
export const defaultPrefixCls = 'ant';
let globalPrefixCls = ref<string>();
const globalPrefixCls = ref<string>();
type GlobalConfigProviderProps = {
prefixCls?: MaybeRef<ConfigProviderProps['prefixCls']>;
@ -154,6 +154,7 @@ export type ConfigProviderProps = Partial<ExtractPropTypes<typeof configProvider
const ConfigProvider = defineComponent({
name: 'AConfigProvider',
inheritAttrs: false,
props: configProviderProps,
setup(props, { slots }) {
const getPrefixCls = (suffixCls?: string, customizePrefixCls?: string) => {

View File

@ -23,12 +23,14 @@ function getMessageInstance(args: MessageArgsProps, callback: (i: any) => void)
}
Notification.newInstance(
{
appContext: args.appContext,
prefixCls: args.prefixCls || localPrefixCls,
rootPrefixCls: args.rootPrefixCls,
transitionName,
style: { top: defaultTop }, //
getContainer,
maxCount,
name: 'message',
},
(instance: any) => {
if (messageInstance) {
@ -92,7 +94,6 @@ function notice(args: MessageArgsProps): MessageType {
duration,
style: args.style || {},
class: args.class,
appContext: args.appContext,
content: ({ prefixCls }) => {
const Icon = iconMap[args.type];
const iconNode = Icon ? <Icon /> : '';

View File

@ -6,7 +6,7 @@ import { destroyFns } from './Modal';
import Omit from 'omit.js';
import ConfigProvider, { globalConfig } from '../config-provider';
let defaultRootPrefixCls = '';
const defaultRootPrefixCls = '';
function getRootPrefixCls() {
return defaultRootPrefixCls;
@ -42,7 +42,8 @@ const confirm = (config: ModalFuncProps) => {
}
function destroy(...args: any[]) {
if (confirmDialogInstance && div.parentNode) {
Object.assign(confirmDialogInstance.component.props, { vIf: false }); // hack destroy
// destroy
vueRender(null, div);
confirmDialogInstance.component.update();
confirmDialogInstance = null;
div.parentNode.removeChild(div);
@ -59,18 +60,18 @@ const confirm = (config: ModalFuncProps) => {
}
}
}
const Wrapper = (p: ModalFuncProps & { vIf: boolean }) => {
const Wrapper = (p: ModalFuncProps) => {
const { getPrefixCls } = globalConfig();
const rootPrefixCls = getPrefixCls(undefined, getRootPrefixCls());
const prefixCls = p.prefixCls || `${rootPrefixCls}-modal`;
return p.vIf ? (
return (
<ConfigProvider prefixCls={rootPrefixCls}>
<ConfirmDialog {...p} prefixCls={prefixCls}></ConfirmDialog>
</ConfigProvider>
) : null;
);
};
function render(props: ModalFuncProps) {
const vm = createVNode(Wrapper, { ...props, vIf: true });
const vm = createVNode(Wrapper, { ...props });
vm.appContext = config.parentContext || config.appContext || vm.appContext;
vueRender(vm, div);
return vm;

View File

@ -11,7 +11,7 @@ describe('notification', () => {
notification.destroy();
});
it('should be able to hide manually', async () => {
fit('should be able to hide manually', async () => {
notification.open({
message: 'Notification Title',
duration: 0,

View File

@ -1,10 +1,13 @@
import type { VNodeTypes, CSSProperties } from 'vue';
import type { CSSProperties } from 'vue';
import Notification from '../vc-notification';
import CheckCircleOutlined from '@ant-design/icons-vue/CheckCircleOutlined';
import InfoCircleOutlined from '@ant-design/icons-vue/InfoCircleOutlined';
import CloseCircleOutlined from '@ant-design/icons-vue/CloseCircleOutlined';
import ExclamationCircleOutlined from '@ant-design/icons-vue/ExclamationCircleOutlined';
import CloseOutlined from '@ant-design/icons-vue/CloseOutlined';
import type { VueNode } from '../_util/type';
import { renderHelper } from '../_util/util';
import { globalConfig } from '../config-provider';
export type NotificationPlacement = 'topLeft' | 'topRight' | 'bottomLeft' | 'bottomRight';
@ -14,21 +17,26 @@ export interface ConfigProps {
top?: string | number;
bottom?: string | number;
duration?: number;
prefixCls?: string;
placement?: NotificationPlacement;
getContainer?: () => HTMLElement;
closeIcon?: VNodeTypes;
closeIcon?: VueNode | (() => VueNode);
}
const notificationInstance: { [key: string]: any } = {};
let defaultDuration = 4.5;
let defaultTop = '24px';
let defaultBottom = '24px';
let defaultPrefixCls = '';
let defaultPlacement: NotificationPlacement = 'topRight';
let defaultGetContainer = () => document.body;
let defaultCloseIcon = null;
function setNotificationConfig(options: ConfigProps) {
const { duration, placement, bottom, top, getContainer, closeIcon } = options;
const { duration, placement, bottom, top, getContainer, closeIcon, prefixCls } = options;
if (prefixCls !== undefined) {
defaultPrefixCls = prefixCls;
}
if (duration !== undefined) {
defaultDuration = duration;
}
@ -88,26 +96,20 @@ function getPlacementStyle(
return style;
}
type NotificationInstanceProps = {
prefixCls: string;
placement?: NotificationPlacement;
getContainer?: () => HTMLElement;
top?: string;
bottom?: string;
closeIcon?: VNodeTypes;
};
function getNotificationInstance(
{
prefixCls,
prefixCls: customizePrefixCls,
placement = defaultPlacement,
getContainer = defaultGetContainer,
top,
bottom,
closeIcon = defaultCloseIcon,
}: NotificationInstanceProps,
appContext,
}: NotificationArgsProps,
callback: (n: any) => void,
) {
const { getPrefixCls } = globalConfig();
const prefixCls = getPrefixCls('notification', customizePrefixCls || defaultPrefixCls);
const cacheKey = `${prefixCls}-${placement}`;
if (notificationInstance[cacheKey]) {
callback(notificationInstance[cacheKey]);
@ -115,14 +117,16 @@ function getNotificationInstance(
}
Notification.newInstance(
{
prefixCls,
name: 'notification',
prefixCls: customizePrefixCls || defaultPrefixCls,
class: `${prefixCls}-${placement}`,
style: getPlacementStyle(placement, top, bottom),
appContext,
getContainer,
closeIcon: () => {
closeIcon: ({ prefixCls }) => {
const closeIconToRender = (
<span class={`${prefixCls}-close-x`}>
{closeIcon || <CloseOutlined class={`${prefixCls}-close-icon`} />}
{renderHelper(closeIcon, {}, <CloseOutlined class={`${prefixCls}-close-icon`} />)}
</span>
);
return closeIconToRender;
@ -143,13 +147,13 @@ const typeToIcon = {
};
export interface NotificationArgsProps {
message: VNodeTypes;
description?: VNodeTypes;
btn?: VNodeTypes;
message: VueNode | (() => VueNode);
description?: VueNode | (() => VueNode);
btn?: VueNode | (() => VueNode);
key?: string;
onClose?: () => void;
duration?: number | null;
icon?: VNodeTypes;
icon?: VueNode | (() => VueNode);
placement?: NotificationPlacement;
style?: CSSProperties;
prefixCls?: string;
@ -159,57 +163,46 @@ export interface NotificationArgsProps {
top?: string;
bottom?: string;
getContainer?: () => HTMLElement;
closeIcon?: VNodeTypes;
closeIcon?: VueNode | (() => VueNode);
appContext?: any;
}
function notice(args: NotificationArgsProps) {
const { icon, type, description, message, btn } = args;
const outerPrefixCls = args.prefixCls || 'ant-notification';
const prefixCls = `${outerPrefixCls}-notice`;
const duration = args.duration === undefined ? defaultDuration : args.duration;
let iconNode = null;
if (icon) {
iconNode = () => <span class={`${prefixCls}-icon`}>{icon}</span>;
} else if (type) {
const Icon = typeToIcon[type];
iconNode = () => <Icon class={`${prefixCls}-icon ${prefixCls}-icon-${type}`} />;
}
const { placement, top, bottom, getContainer, closeIcon } = args;
getNotificationInstance(
{
prefixCls: outerPrefixCls,
placement,
top,
bottom,
getContainer,
closeIcon,
},
notification => {
notification.notice({
content: () => (
getNotificationInstance(args, notification => {
notification.notice({
content: ({ prefixCls }) => {
let iconNode = null;
if (icon) {
iconNode = () => <span class={`${prefixCls}-icon`}>{renderHelper(icon)}</span>;
} else if (type) {
const Icon = typeToIcon[type];
iconNode = () => <Icon class={`${prefixCls}-icon ${prefixCls}-icon-${type}`} />;
}
return (
<div class={iconNode ? `${prefixCls}-with-icon` : ''}>
{iconNode && iconNode()}
<div class={`${prefixCls}-message`}>
{!description && iconNode ? (
<span class={`${prefixCls}-message-single-line-auto-margin`} />
) : null}
{message}
{renderHelper(message)}
</div>
<div class={`${prefixCls}-description`}>{description}</div>
{btn ? <span class={`${prefixCls}-btn`}>{btn}</span> : null}
<div class={`${prefixCls}-description`}>{renderHelper(description)}</div>
{btn ? <span class={`${prefixCls}-btn`}>{renderHelper(btn)}</span> : null}
</div>
),
duration,
closable: true,
onClose: args.onClose,
onClick: args.onClick,
key: args.key,
style: args.style || {},
class: args.class,
});
},
);
);
},
duration,
closable: true,
onClose: args.onClose,
onClick: args.onClick,
key: args.key,
style: args.style || {},
class: args.class,
});
});
}
const apiBase = {

View File

@ -1,6 +1,6 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`renders ./components/tag/demo/basic.vue correctly 1`] = `<div><span class="ant-tag">Tag 1<!----></span><span class="ant-tag"><a href="https://github.com/vueComponent/ant-design">Link</a><!----></span><span class="ant-tag">Tag 2<span tabindex="-1" role="img" aria-label="close" class="anticon anticon-close [object Object]-close-icon"><svg focusable="false" class="" data-icon="close" width="1em" height="1em" fill="currentColor" aria-hidden="true" viewBox="64 64 896 896"><path d="M563.8 512l262.5-312.9c4.4-5.2.7-13.1-6.1-13.1h-79.8c-4.7 0-9.2 2.1-12.3 5.7L511.6 449.8 295.1 191.7c-3-3.6-7.5-5.7-12.3-5.7H203c-6.8 0-10.5 7.9-6.1 13.1L459.4 512 196.9 824.9A7.95 7.95 0 00203 838h79.8c4.7 0 9.2-2.1 12.3-5.7l216.5-258.1 216.5 258.1c3 3.6 7.5 5.7 12.3 5.7h79.8c6.8 0 10.5-7.9 6.1-13.1L563.8 512z"></path></svg></span></span><span class="ant-tag">Prevent Default<span tabindex="-1" role="img" aria-label="close" class="anticon anticon-close [object Object]-close-icon"><svg focusable="false" class="" data-icon="close" width="1em" height="1em" fill="currentColor" aria-hidden="true" viewBox="64 64 896 896"><path d="M563.8 512l262.5-312.9c4.4-5.2.7-13.1-6.1-13.1h-79.8c-4.7 0-9.2 2.1-12.3 5.7L511.6 449.8 295.1 191.7c-3-3.6-7.5-5.7-12.3-5.7H203c-6.8 0-10.5 7.9-6.1 13.1L459.4 512 196.9 824.9A7.95 7.95 0 00203 838h79.8c4.7 0 9.2-2.1 12.3-5.7l216.5-258.1 216.5 258.1c3 3.6 7.5 5.7 12.3 5.7h79.8c6.8 0 10.5-7.9 6.1-13.1L563.8 512z"></path></svg></span></span></div>`;
exports[`renders ./components/tag/demo/basic.vue correctly 1`] = `<div><span class="ant-tag">Tag 1<!----></span><span class="ant-tag"><a href="https://github.com/vueComponent/ant-design">Link</a><!----></span><span class="ant-tag">Tag 2<span tabindex="-1" role="img" aria-label="close" class="anticon anticon-close ant-tag-close-icon"><svg focusable="false" class="" data-icon="close" width="1em" height="1em" fill="currentColor" aria-hidden="true" viewBox="64 64 896 896"><path d="M563.8 512l262.5-312.9c4.4-5.2.7-13.1-6.1-13.1h-79.8c-4.7 0-9.2 2.1-12.3 5.7L511.6 449.8 295.1 191.7c-3-3.6-7.5-5.7-12.3-5.7H203c-6.8 0-10.5 7.9-6.1 13.1L459.4 512 196.9 824.9A7.95 7.95 0 00203 838h79.8c4.7 0 9.2-2.1 12.3-5.7l216.5-258.1 216.5 258.1c3 3.6 7.5 5.7 12.3 5.7h79.8c6.8 0 10.5-7.9 6.1-13.1L563.8 512z"></path></svg></span></span><span class="ant-tag">Prevent Default<span tabindex="-1" role="img" aria-label="close" class="anticon anticon-close ant-tag-close-icon"><svg focusable="false" class="" data-icon="close" width="1em" height="1em" fill="currentColor" aria-hidden="true" viewBox="64 64 896 896"><path d="M563.8 512l262.5-312.9c4.4-5.2.7-13.1-6.1-13.1h-79.8c-4.7 0-9.2 2.1-12.3 5.7L511.6 449.8 295.1 191.7c-3-3.6-7.5-5.7-12.3-5.7H203c-6.8 0-10.5 7.9-6.1 13.1L459.4 512 196.9 824.9A7.95 7.95 0 00203 838h79.8c4.7 0 9.2-2.1 12.3-5.7l216.5-258.1 216.5 258.1c3 3.6 7.5 5.7 12.3 5.7h79.8c6.8 0 10.5-7.9 6.1-13.1L563.8 512z"></path></svg></span></span></div>`;
exports[`renders ./components/tag/demo/checkable.vue correctly 1`] = `<div><span class="ant-tag ant-tag-checkable">Tag1</span><span class="ant-tag ant-tag-checkable">Tag2</span><span class="ant-tag ant-tag-checkable">Tag3</span></div>`;
@ -12,13 +12,13 @@ exports[`renders ./components/tag/demo/colorful.vue correctly 1`] = `
`;
exports[`renders ./components/tag/demo/control.vue correctly 1`] = `
<span class="ant-tag">Unremovable<!----></span><span class="ant-tag">Tag 2<span tabindex="-1" role="img" aria-label="close" class="anticon anticon-close [object Object]-close-icon"><svg focusable="false" class="" data-icon="close" width="1em" height="1em" fill="currentColor" aria-hidden="true" viewBox="64 64 896 896"><path d="M563.8 512l262.5-312.9c4.4-5.2.7-13.1-6.1-13.1h-79.8c-4.7 0-9.2 2.1-12.3 5.7L511.6 449.8 295.1 191.7c-3-3.6-7.5-5.7-12.3-5.7H203c-6.8 0-10.5 7.9-6.1 13.1L459.4 512 196.9 824.9A7.95 7.95 0 00203 838h79.8c4.7 0 9.2-2.1 12.3-5.7l216.5-258.1 216.5 258.1c3 3.6 7.5 5.7 12.3 5.7h79.8c6.8 0 10.5-7.9 6.1-13.1L563.8 512z"></path></svg></span></span>
<!----><span class="ant-tag">Tag 3Tag 3Tag 3Tag 3...<span tabindex="-1" role="img" aria-label="close" class="anticon anticon-close [object Object]-close-icon"><svg focusable="false" class="" data-icon="close" width="1em" height="1em" fill="currentColor" aria-hidden="true" viewBox="64 64 896 896"><path d="M563.8 512l262.5-312.9c4.4-5.2.7-13.1-6.1-13.1h-79.8c-4.7 0-9.2 2.1-12.3 5.7L511.6 449.8 295.1 191.7c-3-3.6-7.5-5.7-12.3-5.7H203c-6.8 0-10.5 7.9-6.1 13.1L459.4 512 196.9 824.9A7.95 7.95 0 00203 838h79.8c4.7 0 9.2-2.1 12.3-5.7l216.5-258.1 216.5 258.1c3 3.6 7.5 5.7 12.3 5.7h79.8c6.8 0 10.5-7.9 6.1-13.1L563.8 512z"></path></svg></span></span><span class="ant-tag" style="background: rgb(255, 255, 255); border-style: dashed;"><span role="img" aria-label="plus" class="anticon anticon-plus"><svg focusable="false" class="" data-icon="plus" width="1em" height="1em" fill="currentColor" aria-hidden="true" viewBox="64 64 896 896"><defs><style></style></defs><path d="M482 152h60q8 0 8 8v704q0 8-8 8h-60q-8 0-8-8V160q0-8 8-8z"></path><path d="M176 474h672q8 0 8 8v60q0 8-8 8H176q-8 0-8-8v-60q0-8 8-8z"></path></svg></span> New Tag
<span class="ant-tag">Unremovable<!----></span><span class="ant-tag">Tag 2<span tabindex="-1" role="img" aria-label="close" class="anticon anticon-close ant-tag-close-icon"><svg focusable="false" class="" data-icon="close" width="1em" height="1em" fill="currentColor" aria-hidden="true" viewBox="64 64 896 896"><path d="M563.8 512l262.5-312.9c4.4-5.2.7-13.1-6.1-13.1h-79.8c-4.7 0-9.2 2.1-12.3 5.7L511.6 449.8 295.1 191.7c-3-3.6-7.5-5.7-12.3-5.7H203c-6.8 0-10.5 7.9-6.1 13.1L459.4 512 196.9 824.9A7.95 7.95 0 00203 838h79.8c4.7 0 9.2-2.1 12.3-5.7l216.5-258.1 216.5 258.1c3 3.6 7.5 5.7 12.3 5.7h79.8c6.8 0 10.5-7.9 6.1-13.1L563.8 512z"></path></svg></span></span>
<!----><span class="ant-tag">Tag 3Tag 3Tag 3Tag 3...<span tabindex="-1" role="img" aria-label="close" class="anticon anticon-close ant-tag-close-icon"><svg focusable="false" class="" data-icon="close" width="1em" height="1em" fill="currentColor" aria-hidden="true" viewBox="64 64 896 896"><path d="M563.8 512l262.5-312.9c4.4-5.2.7-13.1-6.1-13.1h-79.8c-4.7 0-9.2 2.1-12.3 5.7L511.6 449.8 295.1 191.7c-3-3.6-7.5-5.7-12.3-5.7H203c-6.8 0-10.5 7.9-6.1 13.1L459.4 512 196.9 824.9A7.95 7.95 0 00203 838h79.8c4.7 0 9.2-2.1 12.3-5.7l216.5-258.1 216.5 258.1c3 3.6 7.5 5.7 12.3 5.7h79.8c6.8 0 10.5-7.9 6.1-13.1L563.8 512z"></path></svg></span></span><span class="ant-tag" style="background: rgb(255, 255, 255); border-style: dashed;"><span role="img" aria-label="plus" class="anticon anticon-plus"><svg focusable="false" class="" data-icon="plus" width="1em" height="1em" fill="currentColor" aria-hidden="true" viewBox="64 64 896 896"><defs><style></style></defs><path d="M482 152h60q8 0 8 8v704q0 8-8 8h-60q-8 0-8-8V160q0-8 8-8z"></path><path d="M176 474h672q8 0 8 8v60q0 8-8 8H176q-8 0-8-8v-60q0-8 8-8z"></path></svg></span> New Tag
<!----></span>
`;
exports[`renders ./components/tag/demo/controlled.vue correctly 1`] = `
<span class="ant-tag ant-tag-hidden">Movies<span tabindex="-1" role="img" aria-label="close" class="anticon anticon-close [object Object]-close-icon"><svg focusable="false" class="" data-icon="close" width="1em" height="1em" fill="currentColor" aria-hidden="true" viewBox="64 64 896 896"><path d="M563.8 512l262.5-312.9c4.4-5.2.7-13.1-6.1-13.1h-79.8c-4.7 0-9.2 2.1-12.3 5.7L511.6 449.8 295.1 191.7c-3-3.6-7.5-5.7-12.3-5.7H203c-6.8 0-10.5 7.9-6.1 13.1L459.4 512 196.9 824.9A7.95 7.95 0 00203 838h79.8c4.7 0 9.2-2.1 12.3-5.7l216.5-258.1 216.5 258.1c3 3.6 7.5 5.7 12.3 5.7h79.8c6.8 0 10.5-7.9 6.1-13.1L563.8 512z"></path></svg></span></span><br><button class="ant-btn ant-btn-sm" type="button">
<span class="ant-tag ant-tag-hidden">Movies<span tabindex="-1" role="img" aria-label="close" class="anticon anticon-close ant-tag-close-icon"><svg focusable="false" class="" data-icon="close" width="1em" height="1em" fill="currentColor" aria-hidden="true" viewBox="64 64 896 896"><path d="M563.8 512l262.5-312.9c4.4-5.2.7-13.1-6.1-13.1h-79.8c-4.7 0-9.2 2.1-12.3 5.7L511.6 449.8 295.1 191.7c-3-3.6-7.5-5.7-12.3-5.7H203c-6.8 0-10.5 7.9-6.1 13.1L459.4 512 196.9 824.9A7.95 7.95 0 00203 838h79.8c4.7 0 9.2-2.1 12.3-5.7l216.5-258.1 216.5 258.1c3 3.6 7.5 5.7 12.3 5.7h79.8c6.8 0 10.5-7.9 6.1-13.1L563.8 512z"></path></svg></span></span><br><button class="ant-btn ant-btn-sm" type="button">
<!----><span>Toggle</span>
</button>
`;

View File

@ -1,4 +1,4 @@
import { defineComponent, createVNode, render as vueRender, onMounted, ref, nextTick } from 'vue';
import { defineComponent, createVNode, render as vueRender, onMounted, ref } from 'vue';
import PropTypes from '../_util/vue-types';
import { getComponent } from '../_util/props-util';
import BaseMixin from '../_util/BaseMixin';
@ -88,7 +88,7 @@ const Notification = defineComponent({
duration,
closable,
update,
closeIcon: getComponent(this, 'closeIcon'),
closeIcon: getComponent(this, 'closeIcon', { prefixCls }),
onClose: close,
onClick: notice.onClick || noop,
style,
@ -124,6 +124,7 @@ const Notification = defineComponent({
Notification.newInstance = function newNotificationInstance(properties, callback) {
const {
name = 'notification',
getContainer,
appContext,
prefixCls: customizePrefixCls,
@ -137,31 +138,28 @@ Notification.newInstance = function newNotificationInstance(properties, callback
} else {
document.body.appendChild(div);
}
let vm = null;
const Wrapper = defineComponent({
setup(_props, { attrs }) {
const notiRef = ref();
onMounted(() => {
nextTick(() => {
callback({
notice(noticeProps) {
notiRef.value?.add(noticeProps);
},
removeNotice(key) {
notiRef.value?.remove(key);
},
destroy() {
vm?.unmount(div);
if (div.parentNode) {
div.parentNode.removeChild(div);
}
},
});
callback({
notice(noticeProps) {
notiRef.value?.add(noticeProps);
},
removeNotice(key) {
notiRef.value?.remove(key);
},
destroy() {
vueRender(null, div);
if (div.parentNode) {
div.parentNode.removeChild(div);
}
},
});
});
return () => {
const { getPrefixCls, getRootPrefixCls } = globalConfig();
const prefixCls = getPrefixCls('message', customizePrefixCls);
const prefixCls = getPrefixCls(name, customizePrefixCls);
const rootPrefixCls = getRootPrefixCls(customRootPrefixCls, prefixCls);
return (
<ConfigProvider prefixCls={rootPrefixCls}>
@ -172,7 +170,7 @@ Notification.newInstance = function newNotificationInstance(properties, callback
},
});
vm = createVNode(Wrapper, props);
const vm = createVNode(Wrapper, props);
vm.appContext = appContext || vm.appContext;
vueRender(vm, div);
};