From 456c35361c27989b2a312dd994777b681da57a84 Mon Sep 17 00:00:00 2001 From: tangjinzhou <415800467@qq.com> Date: Sat, 18 Sep 2021 22:48:44 +0800 Subject: [PATCH] feat: notification support configprovider.config --- components/_util/{util.js => util.ts} | 12 ++ components/config-provider/index.tsx | 3 +- components/message/index.tsx | 3 +- components/modal/confirm.tsx | 13 +- .../notification/__tests__/index.test.js | 2 +- components/notification/index.tsx | 111 ++++++++---------- .../__tests__/__snapshots__/demo.test.js.snap | 8 +- components/vc-notification/Notification.jsx | 38 +++--- 8 files changed, 98 insertions(+), 92 deletions(-) rename components/_util/{util.js => util.ts} (88%) diff --git a/components/_util/util.js b/components/_util/util.ts similarity index 88% rename from components/_util/util.js rename to components/_util/util.ts index d1b647dbd..48a604753 100644 --- a/components/_util/util.js +++ b/components/_util/util.ts @@ -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>( + 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 }; diff --git a/components/config-provider/index.tsx b/components/config-provider/index.tsx index 7a97e23ff..03b93b28b 100644 --- a/components/config-provider/index.tsx +++ b/components/config-provider/index.tsx @@ -58,7 +58,7 @@ export const configConsumerProps = [ ]; export const defaultPrefixCls = 'ant'; -let globalPrefixCls = ref(); +const globalPrefixCls = ref(); type GlobalConfigProviderProps = { prefixCls?: MaybeRef; @@ -154,6 +154,7 @@ export type ConfigProviderProps = Partial { diff --git a/components/message/index.tsx b/components/message/index.tsx index b0ea95c4b..16506e20b 100644 --- a/components/message/index.tsx +++ b/components/message/index.tsx @@ -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 ? : ''; diff --git a/components/modal/confirm.tsx b/components/modal/confirm.tsx index 4112444ec..296065977 100644 --- a/components/modal/confirm.tsx +++ b/components/modal/confirm.tsx @@ -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 ( - ) : 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; diff --git a/components/notification/__tests__/index.test.js b/components/notification/__tests__/index.test.js index fefadc846..9e4c26064 100644 --- a/components/notification/__tests__/index.test.js +++ b/components/notification/__tests__/index.test.js @@ -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, diff --git a/components/notification/index.tsx b/components/notification/index.tsx index c6a8b712c..e2a800a80 100644 --- a/components/notification/index.tsx +++ b/components/notification/index.tsx @@ -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 = ( - {closeIcon || } + {renderHelper(closeIcon, {}, )} ); 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 = () => {icon}; - } else if (type) { - const Icon = typeToIcon[type]; - iconNode = () => ; - } - 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 = () => {renderHelper(icon)}; + } else if (type) { + const Icon = typeToIcon[type]; + iconNode = () => ; + } + return (
{iconNode && iconNode()}
{!description && iconNode ? ( ) : null} - {message} + {renderHelper(message)}
-
{description}
- {btn ? {btn} : null} +
{renderHelper(description)}
+ {btn ? {renderHelper(btn)} : null}
- ), - 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 = { diff --git a/components/tag/__tests__/__snapshots__/demo.test.js.snap b/components/tag/__tests__/__snapshots__/demo.test.js.snap index b324110a9..0a0cfc428 100644 --- a/components/tag/__tests__/__snapshots__/demo.test.js.snap +++ b/components/tag/__tests__/__snapshots__/demo.test.js.snap @@ -1,6 +1,6 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP -exports[`renders ./components/tag/demo/basic.vue correctly 1`] = `
Tag 1LinkTag 2Prevent Default
`; +exports[`renders ./components/tag/demo/basic.vue correctly 1`] = `
Tag 1LinkTag 2Prevent Default
`; exports[`renders ./components/tag/demo/checkable.vue correctly 1`] = `
Tag1Tag2Tag3
`; @@ -12,13 +12,13 @@ exports[`renders ./components/tag/demo/colorful.vue correctly 1`] = ` `; exports[`renders ./components/tag/demo/control.vue correctly 1`] = ` -UnremovableTag 2 -Tag 3Tag 3Tag 3Tag 3... New Tag +UnremovableTag 2 +Tag 3Tag 3Tag 3Tag 3... New Tag `; exports[`renders ./components/tag/demo/controlled.vue correctly 1`] = ` -Movies
`; diff --git a/components/vc-notification/Notification.jsx b/components/vc-notification/Notification.jsx index b138e41fc..591fd65fe 100644 --- a/components/vc-notification/Notification.jsx +++ b/components/vc-notification/Notification.jsx @@ -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 ( @@ -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); };