From feffe70ebb72011ef311cb6e547b06b0ea139b99 Mon Sep 17 00:00:00 2001 From: tangjinzhou <415800467@qq.com> Date: Fri, 5 May 2023 11:55:42 +0800 Subject: [PATCH] refactor: useNotification #6527 --- components/message/interface.ts | 2 +- components/message/useMessage.tsx | 13 ++- components/notification/PurePanel.tsx | 93 +++++++++------- components/notification/index.tsx | 64 +---------- components/notification/interface.ts | 22 ++-- components/notification/useNotification.tsx | 102 +++++++++--------- components/vc-notification/Notification.tsx | 2 +- .../vc-notification/useNotification.tsx | 44 ++++---- 8 files changed, 148 insertions(+), 194 deletions(-) diff --git a/components/message/interface.ts b/components/message/interface.ts index 256e463ca..98d856e19 100644 --- a/components/message/interface.ts +++ b/components/message/interface.ts @@ -4,7 +4,7 @@ import type { Key, VueNode } from '../_util/type'; export type NoticeType = 'info' | 'success' | 'error' | 'warning' | 'loading'; export interface ConfigOptions { - top?: number; + top?: number | string; duration?: number; prefixCls?: string; getContainer?: () => HTMLElement; diff --git a/components/message/useMessage.tsx b/components/message/useMessage.tsx index 1f98d2df1..b81a07b3b 100644 --- a/components/message/useMessage.tsx +++ b/components/message/useMessage.tsx @@ -56,11 +56,14 @@ const Holder = defineComponent({ const [, hashId] = useStyle(prefixCls); // =============================== Style =============================== - const getStyles = () => ({ - left: '50%', - transform: 'translateX(-50%)', - top: `${top ?? DEFAULT_OFFSET}px`, - }); + const getStyles = () => { + const top = props.top ?? DEFAULT_OFFSET; + return { + left: '50%', + transform: 'translateX(-50%)', + top: typeof top === 'number' ? `${top}px` : top, + }; + }; const getClassName = () => classNames(hashId.value, props.rtl ? `${prefixCls.value}-rtl` : ''); // ============================== Motion =============================== diff --git a/components/notification/PurePanel.tsx b/components/notification/PurePanel.tsx index c014509f3..c2a10a52a 100644 --- a/components/notification/PurePanel.tsx +++ b/components/notification/PurePanel.tsx @@ -1,4 +1,4 @@ -import { computed } from 'vue'; +import { computed, defineComponent } from 'vue'; import useStyle from './style'; import useConfigInject from '../config-provider/hooks/useConfigInject'; import type { IconType } from './interface'; @@ -6,13 +6,12 @@ import Notice from '../vc-notification/Notice'; import classNames from '../_util/classNames'; import type { NoticeProps } from '../vc-notification/Notice'; import type { VueNode } from '../_util/type'; -import { - CheckCircleOutlined, - CloseCircleOutlined, - CloseOutlined, - ExclamationCircleOutlined, - InfoCircleOutlined, -} from '@ant-design/icons-vue'; +import LoadingOutlined from '@ant-design/icons-vue/LoadingOutlined'; +import ExclamationCircleFilled from '@ant-design/icons-vue/ExclamationCircleFilled'; +import CloseCircleFilled from '@ant-design/icons-vue/CloseCircleFilled'; +import CheckCircleFilled from '@ant-design/icons-vue/CheckCircleFilled'; +import InfoCircleFilled from '@ant-design/icons-vue/InfoCircleFilled'; +import CloseOutlined from '@ant-design/icons-vue/CloseOutlined'; import { renderHelper } from '../_util/util'; export function getCloseIcon(prefixCls: string, closeIcon?: VueNode) { @@ -34,11 +33,19 @@ export interface PureContentProps { type?: IconType; } +export const TypeIcon = { + info: , + success: , + error: , + warning: , + loading: , +}; + const typeToIcon = { - success: CheckCircleOutlined, - info: InfoCircleOutlined, - error: CloseCircleOutlined, - warning: ExclamationCircleOutlined, + success: CheckCircleFilled, + info: InfoCircleFilled, + error: CloseCircleFilled, + warning: ExclamationCircleFilled, }; export function PureContent({ @@ -74,36 +81,42 @@ export function PureContent({ export interface PurePanelProps extends Omit, - Omit { + Omit { prefixCls?: string; } /** @private Internal Component. Do not use in your production. */ -export default function PurePanel(props: PurePanelProps) { - const { getPrefixCls } = useConfigInject('notification', props); - const prefixCls = computed(() => props.prefixCls || getPrefixCls('notification')); - const noticePrefixCls = `${prefixCls.value}-notice`; +export default defineComponent({ + name: 'PurePanel', + inheritAttrs: false, + props: ['prefixCls', 'icon', 'type', 'message', 'description', 'btn', 'closeIcon'] as any, + setup(props) { + const { getPrefixCls } = useConfigInject('notification', props); + const prefixCls = computed(() => props.prefixCls || getPrefixCls('notification')); + const noticePrefixCls = computed(() => `${prefixCls.value}-notice`); - const [, hashId] = useStyle(prefixCls); - - return ( - - - - ); -} + const [, hashId] = useStyle(prefixCls); + return () => { + return ( + + + + ); + }; + }, +}); diff --git a/components/notification/index.tsx b/components/notification/index.tsx index 4179cc31e..3a639e15e 100644 --- a/components/notification/index.tsx +++ b/components/notification/index.tsx @@ -12,6 +12,7 @@ import type { NotificationInstance as VCNotificationInstance } from '../vc-notif import classNames from '../_util/classNames'; import useStyle from './style'; import useNotification from './useNotification'; +import { getPlacementStyle } from './util'; export type NotificationPlacement = | 'top' @@ -77,63 +78,6 @@ function setNotificationConfig(options: ConfigProps) { } } -function getPlacementStyle( - placement: NotificationPlacement, - top: string = defaultTop, - bottom: string = defaultBottom, -) { - let style: CSSProperties; - switch (placement) { - case 'top': - style = { - left: '50%', - transform: 'translateX(-50%)', - right: 'auto', - top, - bottom: 'auto', - }; - break; - case 'topLeft': - style = { - left: '0px', - top, - bottom: 'auto', - }; - break; - case 'topRight': - style = { - right: '0px', - top, - bottom: 'auto', - }; - break; - case 'bottom': - style = { - left: '50%', - transform: 'translateX(-50%)', - right: 'auto', - top: 'auto', - bottom, - }; - break; - case 'bottomLeft': - style = { - left: '0px', - top: 'auto', - bottom, - }; - break; - default: - style = { - right: '0px', - top: 'auto', - bottom, - }; - break; - } - return style; -} - function getNotificationInstance( { prefixCls: customizePrefixCls, @@ -167,7 +111,7 @@ function getNotificationInstance( prefixCls: customizePrefixCls || defaultPrefixCls, useStyle, class: notificationClass, - style: getPlacementStyle(placement, top, bottom), + style: getPlacementStyle(placement, top ?? defaultTop, bottom ?? defaultBottom), appContext, getContainer, closeIcon: ({ prefixCls }) => { @@ -210,8 +154,8 @@ export interface NotificationArgsProps { class?: string; readonly type?: IconType; onClick?: () => void; - top?: string; - bottom?: string; + top?: string | number; + bottom?: string | number; getContainer?: () => HTMLElement; closeIcon?: VueNode | (() => VueNode); appContext?: any; diff --git a/components/notification/interface.ts b/components/notification/interface.ts index a46d6245a..ddc291632 100644 --- a/components/notification/interface.ts +++ b/components/notification/interface.ts @@ -12,19 +12,19 @@ export type NotificationPlacement = export type IconType = 'success' | 'info' | 'error' | 'warning'; export interface ArgsProps { - message: VueNode; - description?: VueNode; - btn?: VueNode; + message: (() => VueNode) | VueNode; + description?: (() => VueNode) | VueNode; + btn?: (() => VueNode) | VueNode; key?: Key; onClose?: () => void; duration?: number | null; - icon?: VueNode; + icon?: (() => VueNode) | VueNode; placement?: NotificationPlacement; style?: CSSProperties; - className?: string; + class?: string; readonly type?: IconType; onClick?: () => void; - closeIcon?: VueNode; + closeIcon?: (() => VueNode) | VueNode; } type StaticFn = (args: ArgsProps) => void; @@ -39,20 +39,20 @@ export interface NotificationInstance { } export interface GlobalConfigProps { - top?: number; - bottom?: number; + top?: number | string; + bottom?: number | string; duration?: number; prefixCls?: string; getContainer?: () => HTMLElement; placement?: NotificationPlacement; - closeIcon?: VueNode; + closeIcon?: (() => VueNode) | VueNode; rtl?: boolean; maxCount?: number; } export interface NotificationConfig { - top?: number; - bottom?: number; + top?: number | string; + bottom?: number | string; prefixCls?: string; getContainer?: () => HTMLElement; placement?: NotificationPlacement; diff --git a/components/notification/useNotification.tsx b/components/notification/useNotification.tsx index 52923a35a..9e3dc5533 100644 --- a/components/notification/useNotification.tsx +++ b/components/notification/useNotification.tsx @@ -84,68 +84,64 @@ export function useInternalNotification( notificationConfig?: HolderProps, ): readonly [NotificationInstance, () => VNode] { const holderRef = shallowRef(null); - + const holderKey = Symbol('notificationHolderKey'); // ================================ API ================================ - const wrapAPI = computed(() => { - // Wrap with notification content + // Wrap with notification content - // >>> Open - const open = (config: ArgsProps) => { - if (!holderRef.value) { - return; - } - const { open: originOpen, prefixCls, hashId } = holderRef.value; - const noticePrefixCls = `${prefixCls}-notice`; + // >>> Open + const open = (config: ArgsProps) => { + if (!holderRef.value) { + return; + } + const { open: originOpen, prefixCls, hashId } = holderRef.value; + const noticePrefixCls = `${prefixCls}-notice`; - const { message, description, icon, type, btn, className, ...restConfig } = config; - return originOpen({ - placement: 'topRight', - ...restConfig, - content: ( - - ), - // @ts-ignore - class: classNames(type && `${noticePrefixCls}-${type}`, hashId, className), - }); - }; - - // >>> destroy - const destroy = (key?: Key) => { - if (key !== undefined) { - holderRef.value?.close(key); - } else { - holderRef.value?.destroy(); - } - }; - - const clone = { - open, - destroy, - } as NotificationInstance; - - const keys = ['success', 'info', 'warning', 'error'] as const; - keys.forEach(type => { - clone[type] = config => - open({ - ...config, - type, - }); + const { message, description, icon, type, btn, class: className, ...restConfig } = config; + return originOpen({ + placement: 'topRight', + ...restConfig, + content: () => ( + + ), + // @ts-ignore + class: classNames(type && `${noticePrefixCls}-${type}`, hashId, className), }); + }; - return clone; + // >>> destroy + const destroy = (key?: Key) => { + if (key !== undefined) { + holderRef.value?.close(key); + } else { + holderRef.value?.destroy(); + } + }; + + const wrapAPI = { + open, + destroy, + } as NotificationInstance; + + const keys = ['success', 'info', 'warning', 'error'] as const; + keys.forEach(type => { + wrapAPI[type] = config => + open({ + ...config, + type, + }); }); // ============================== Return =============================== return [ - wrapAPI.value, - () => , + wrapAPI, + () => , ] as const; } diff --git a/components/vc-notification/Notification.tsx b/components/vc-notification/Notification.tsx index 859c12b41..ff4bd02ff 100644 --- a/components/vc-notification/Notification.tsx +++ b/components/vc-notification/Notification.tsx @@ -29,7 +29,7 @@ export interface NoticeContent extends Omit VueNode) | VueNode; onClose?: () => void; style?: CSSProperties; class?: String; diff --git a/components/vc-notification/useNotification.tsx b/components/vc-notification/useNotification.tsx index 77da44b64..c3fdc34ca 100644 --- a/components/vc-notification/useNotification.tsx +++ b/components/vc-notification/useNotification.tsx @@ -1,5 +1,5 @@ import type { CSSProperties } from 'vue'; -import { shallowRef, watch, ref, computed } from 'vue'; +import { shallowRef, watch, computed } from 'vue'; import HookNotification, { getUuid } from './HookNotification'; import type { NotificationInstance, OpenConfig, Placement } from './Notification'; import type { CSSMotionProps } from '../_util/transition'; @@ -82,7 +82,7 @@ export default function useNotification(rootConfig: NotificationConfig = {}) { ...shareConfig } = rootConfig; - const notices = ref([]); + const notices = shallowRef([]); const notificationsRef = shallowRef(); const add = (originNotice: NoticeContent, holderCallback?: HolderReadyCallback) => { const key = originNotice.key || getUuid(); @@ -132,29 +132,27 @@ export default function useNotification(rootConfig: NotificationConfig = {}) { > )); - const taskQueue = ref([] as Task[]); + const taskQueue = shallowRef([] as Task[]); // ========================= Refs ========================= - const api = computed(() => { - return { - open: (config: OpenConfig) => { - const mergedConfig = mergeConfig(shareConfig, config); + const api = { + open: (config: OpenConfig) => { + const mergedConfig = mergeConfig(shareConfig, config); + //@ts-ignore + if (mergedConfig.key === null || mergedConfig.key === undefined) { //@ts-ignore - if (mergedConfig.key === null || mergedConfig.key === undefined) { - //@ts-ignore - mergedConfig.key = `vc-notification-${uniqueKey}`; - uniqueKey += 1; - } + mergedConfig.key = `vc-notification-${uniqueKey}`; + uniqueKey += 1; + } - taskQueue.value = [...taskQueue.value, { type: 'open', config: mergedConfig as any }]; - }, - close: key => { - taskQueue.value = [...taskQueue.value, { type: 'close', key }]; - }, - destroy: () => { - taskQueue.value = [...taskQueue.value, { type: 'destroy' }]; - }, - }; - }); + taskQueue.value = [...taskQueue.value, { type: 'open', config: mergedConfig as any }]; + }, + close: key => { + taskQueue.value = [...taskQueue.value, { type: 'close', key }]; + }, + destroy: () => { + taskQueue.value = [...taskQueue.value, { type: 'destroy' }]; + }, + }; // ======================== Effect ======================== watch(taskQueue, () => { @@ -180,5 +178,5 @@ export default function useNotification(rootConfig: NotificationConfig = {}) { }); // ======================== Return ======================== - return [api.value, () => contextHolder.value] as const; + return [api, () => contextHolder.value] as const; }