refactor: notification

refactor-notification
tangjinzhou 2021-12-30 16:00:10 +08:00
parent f92e75a1d9
commit 842dde0706
9 changed files with 130 additions and 36 deletions

View File

@ -114,6 +114,7 @@ export { default as Modal } from './modal';
export type { StatisticProps } from './statistic';
export { default as Statistic, StatisticCountdown } from './statistic';
export type { NotificationPlacement } from './notification';
export { default as notification } from './notification';
export type { PageHeaderProps } from './page-header';

View File

@ -9,6 +9,8 @@ import type { TransformCellTextProps } from '../table/interface';
import LocaleReceiver from '../locale-provider/LocaleReceiver';
import type { RequiredMark } from '../form/Form';
import type { MaybeRef } from '../_util/type';
import message from '../message';
import notification from '../notification';
export type SizeType = 'small' | 'middle' | 'large' | undefined;
@ -248,6 +250,17 @@ const ConfigProvider = defineComponent({
);
};
watchEffect(() => {
if (props.direction) {
message.config({
rtl: props.direction === 'rtl',
});
notification.config({
rtl: props.direction === 'rtl',
});
}
});
return () => (
<LocaleReceiver children={(_, __, legacyLocale) => renderProvider(legacyLocale as Locale)} />
);

View File

@ -31,7 +31,7 @@ export default defineComponent({
message: 'Notification Title',
description:
'This is the content of the notification. This is the content of the notification. This is the content of the notification.',
icon: h(SmileOutlined, { style: 'color: #108ee9' }),
icon: () => h(SmileOutlined, { style: 'color: #108ee9' }),
});
};

View File

@ -46,6 +46,7 @@ import {
} from '@ant-design/icons-vue';
import { notification } from 'ant-design-vue';
import { defineComponent } from 'vue';
import type { NotificationPlacement } from 'ant-design-vue';
export default defineComponent({
components: {
RadiusUpleftOutlined,
@ -54,7 +55,7 @@ export default defineComponent({
RadiusBottomrightOutlined,
},
setup() {
const openNotification = (placement: string) => {
const openNotification = (placement: NotificationPlacement) => {
notification.open({
message: `Notification ${placement}`,
description:

View File

@ -1,27 +1,34 @@
<docs>
---
order: 7
title:
zh-CN: 更新消息内容
order: 7
title:
zh-CN: 更新消息内容
en-US: Update Message Content
---
## zh-CN
可以通过唯一的 key 来更新内容
可以通过唯一的 key 来更新内容, 或者通过响应式数据更新
## en-US
Update content with unique key.
Update content with unique key, or use reactive data.
</docs>
<template>
<a-button type="primary" @click="openNotification">Open the notification box</a-button>
<a-button type="primary" @click="openNotification">
Open the notification box (update by key)
</a-button>
<br />
<br />
<a-button type="primary" @click="openNotification2">
Open the notification box (update by reactive)
</a-button>
</template>
<script lang="ts">
import { notification } from 'ant-design-vue';
import { defineComponent } from 'vue';
import { defineComponent, ref } from 'vue';
const key = 'updatable';
export default defineComponent({
setup() {
@ -39,8 +46,22 @@ export default defineComponent({
});
}, 1000);
};
const message = ref('Notification Title');
const description = ref('description');
const openNotification2 = () => {
// content must use function
notification.open({
message: () => message.value,
description: () => description.value,
});
setTimeout(() => {
message.value = 'New Title';
description.value = 'New description.';
}, 1000);
};
return {
openNotification,
openNotification2,
};
},
});

View File

@ -31,30 +31,35 @@ The properties of config are as follows:
| Property | Description | Type | Default | Version |
| --- | --- | --- | --- | --- |
| bottom | Distance from the bottom of the viewport, when `placement` is `bottomRight` or `bottomLeft` (unit: pixels). | string | `24px` | |
| btn | Customized close button | VNode | - | |
| btn | Customized close button | VNode \| () => VNode | - | |
| class | Customized CSS class | string | - | |
| description | The content of notification box (required) | string\| VNode | - | |
| description | The content of notification box (required) | string\| VNode \| () => VNode | - | |
| duration | Time in seconds before Notification is closed. When set to 0 or null, it will never be closed automatically | number | 4.5 | |
| getContainer | Return the mount node for Notification | () => HTMLNode | () => document.body | |
| icon | Customized icon | VNode | - | |
| icon | Customized icon | VNode \| () => VNode | - | |
| key | The unique identifier of the Notification | string | - | |
| message | The title of notification box (required) | string\|VNode | - | |
| message | The title of notification box (required) | string\| VNode \| () => VNode | - | |
| placement | Position of Notification, can be one of `topLeft` `topRight` `bottomLeft` `bottomRight` | string | `topRight` | |
| style | Customized inline style | Object \| string | - | |
| onClose | Specify a function that will be called when the close button is clicked | Function | - | |
| onClick | Specify a function that will be called when the notification is clicked | Function | - | |
| top | Distance from the top of the viewport, when `placement` is `topRight` or `topLeft` (unit: pixels). | string | `24px` | |
| closeIcon | custom close icon | VNode | - | |
| closeIcon | custom close icon | VNode \| () => VNode | - | |
`notification` also provides a global `config()` method that can be used for specifying the default options. Once this method is used, all the notification boxes will take into account these globally defined options when displaying.
- `notification.config(options)`
> When you use `ConfigProvider` for global configuration, the system will automatically start RTL mode by default.(4.3.0+)
>
> When you want to use it alone, you can start the RTL mode through the following settings.
```js
notification.config({
placement: 'bottomRight',
bottom: '50px',
duration: 3,
rtl: true,
});
```
@ -64,5 +69,7 @@ notification.config({
| duration | Time in seconds before Notification is closed. When set to 0 or null, it will never be closed automatically | number | 4.5 | |
| getContainer | Return the mount node for Notification | () => HTMLNode | () => document.body | |
| placement | Position of Notification, can be one of `topLeft` `topRight` `bottomLeft` `bottomRight` | string | `topRight` | |
| rtl | Whether to enable RTL mode | boolean | false | |
| top | Distance from the top of the viewport, when `placement` is `topRight` or `topLeft` (unit: pixels). | string | `24px` | |
| closeIcon | custom close icon | VNode | - | |
| closeIcon | custom close icon | VNode \| () => VNode | - | |
| maxCount | Max Notification show, drop oldest if exceed limit | number | - | 3.0 |

View File

@ -8,6 +8,8 @@ import CloseOutlined from '@ant-design/icons-vue/CloseOutlined';
import type { VueNode } from '../_util/type';
import { renderHelper } from '../_util/util';
import { globalConfig } from '../config-provider';
import type { NotificationInstance as VCNotificationInstance } from '../vc-notification/Notification';
import classNames from '../_util/classNames';
export type NotificationPlacement = 'topLeft' | 'topRight' | 'bottomLeft' | 'bottomRight';
@ -21,9 +23,11 @@ export interface ConfigProps {
placement?: NotificationPlacement;
getContainer?: () => HTMLElement;
closeIcon?: VueNode | (() => VueNode);
rtl?: boolean;
maxCount?: number;
}
const notificationInstance: { [key: string]: any } = {};
const notificationInstance: { [key: string]: VCNotificationInstance } = {};
let defaultDuration = 4.5;
let defaultTop = '24px';
let defaultBottom = '24px';
@ -31,6 +35,8 @@ let defaultPrefixCls = '';
let defaultPlacement: NotificationPlacement = 'topRight';
let defaultGetContainer = () => document.body;
let defaultCloseIcon = null;
let rtl = false;
let maxCount: number;
function setNotificationConfig(options: ConfigProps) {
const { duration, placement, bottom, top, getContainer, closeIcon, prefixCls } = options;
@ -55,6 +61,12 @@ function setNotificationConfig(options: ConfigProps) {
if (closeIcon !== undefined) {
defaultCloseIcon = closeIcon;
}
if (options.rtl !== undefined) {
rtl = options.rtl;
}
if (options.maxCount !== undefined) {
maxCount = options.maxCount;
}
}
function getPlacementStyle(
@ -62,7 +74,7 @@ function getPlacementStyle(
top: string = defaultTop,
bottom: string = defaultBottom,
) {
let style;
let style: CSSProperties;
switch (placement) {
case 'topLeft':
style = {
@ -106,20 +118,28 @@ function getNotificationInstance(
closeIcon = defaultCloseIcon,
appContext,
}: NotificationArgsProps,
callback: (n: any) => void,
callback: (n: VCNotificationInstance) => void,
) {
const { getPrefixCls } = globalConfig();
const prefixCls = getPrefixCls('notification', customizePrefixCls || defaultPrefixCls);
const cacheKey = `${prefixCls}-${placement}`;
if (notificationInstance[cacheKey]) {
callback(notificationInstance[cacheKey]);
const cacheKey = `${prefixCls}-${placement}-${rtl}`;
const cacheInstance = notificationInstance[cacheKey];
if (cacheInstance) {
Promise.resolve(cacheInstance).then(instance => {
callback(instance);
});
return;
}
const notificationClass = classNames(`${prefixCls}-${placement}`, {
[`${prefixCls}-rtl`]: rtl === true,
});
Notification.newInstance(
{
name: 'notification',
prefixCls: customizePrefixCls || defaultPrefixCls,
class: `${prefixCls}-${placement}`,
class: notificationClass,
style: getPlacementStyle(placement, top, bottom),
appContext,
getContainer,
@ -131,6 +151,8 @@ function getNotificationInstance(
);
return closeIconToRender;
},
maxCount,
hasTransitionName: true,
},
(notification: any) => {
notificationInstance[cacheKey] = notification;
@ -206,25 +228,26 @@ function notice(args: NotificationArgsProps) {
});
}
const apiBase = {
const api: any = {
open: notice,
close(key: string) {
Object.keys(notificationInstance).forEach(cacheKey =>
notificationInstance[cacheKey].removeNotice(key),
Promise.resolve(notificationInstance[cacheKey]).then(instance => {
instance.removeNotice(key);
}),
);
},
config: setNotificationConfig,
destroy() {
Object.keys(notificationInstance).forEach(cacheKey => {
notificationInstance[cacheKey].destroy();
delete notificationInstance[cacheKey];
Promise.resolve(notificationInstance[cacheKey]).then(instance => {
instance.destroy();
});
delete notificationInstance[cacheKey]; // lgtm[js/missing-await]
});
},
};
type NotificationApi = typeof apiBase &
Record<IconType | 'warn', (args: Omit<NotificationArgsProps, 'type'>) => void>;
const api = apiBase as any as NotificationApi;
const iconTypes: IconType[] = ['success', 'info', 'warning', 'error'];
iconTypes.forEach(type => {
api[type] = args =>
@ -235,4 +258,24 @@ iconTypes.forEach(type => {
});
api.warn = api.warning;
export default api;
export interface NotificationInstance {
success(args: NotificationArgsProps): void;
error(args: NotificationArgsProps): void;
info(args: NotificationArgsProps): void;
warning(args: NotificationArgsProps): void;
open(args: NotificationArgsProps): void;
}
export interface NotificationApi extends NotificationInstance {
warn(args: NotificationArgsProps): void;
close(key: string): void;
config(options: ConfigProps): void;
destroy(): void;
}
/** @private test Only function. Not work on production */
export const getInstance = async (cacheKey: string) =>
process.env.NODE_ENV === 'test' ? notificationInstance[cacheKey] : null;
export default api as NotificationApi;

View File

@ -31,31 +31,36 @@ config 参数如下:
| 参数 | 说明 | 类型 | 默认值 | 版本 |
| --- | --- | --- | --- | --- |
| btn | 自定义关闭按钮 | VNode | - | |
| btn | 自定义关闭按钮 | VNode \| () => VNode | - | |
| bottom | 消息从底部弹出时,距离底部的位置,单位像素。 | string | `24px` | |
| class | 自定义 CSS class | string | - | |
| description | 通知提醒内容,必选 | string \|VNode | - | |
| description | 通知提醒内容,必选 | string \| VNode \| () => VNode | - | |
| duration | 默认 4.5 秒后自动关闭,配置为 null 则不自动关闭 | number | 4.5 | |
| getContainer | 配置渲染节点的输出位置 | () => HTMLNode | () => document.body | |
| icon | 自定义图标 | VNode | - | |
| icon | 自定义图标 | VNode \| () => VNode | - | |
| key | 当前通知唯一标志 | string | - | |
| message | 通知提醒标题,必选 | string \|VNode | - | |
| message | 通知提醒标题,必选 | string \| VNode \| () => VNode | - | |
| placement | 弹出位置,可选 `topLeft` `topRight` `bottomLeft` `bottomRight` | string | topRight | |
| style | 自定义内联样式 | Object \| string | - | |
| onClose | 点击默认关闭按钮时触发的回调函数 | Function | - | |
| onClick | 点击通知时触发的回调函数 | Function | - | |
| top | 消息从顶部弹出时,距离顶部的位置,单位像素。 | string | `24px` | |
| closeIcon | 自定义关闭图标 | VNode | - | |
| closeIcon | 自定义关闭图标 | VNode \| () => VNode | - | |
还提供了一个全局配置方法,在调用前提前配置,全局一次生效。
- `notification.config(options)`
> 当你使用 `ConfigProvider` 进行全局化配置时,系统会默认自动开启 RTL 模式。(3.0+)
>
> 当你想单独使用,可通过如下设置开启 RTL 模式。
```js
notification.config({
placement: 'bottomRight',
bottom: '50px',
duration: 3,
rtl: true,
});
```
@ -66,4 +71,6 @@ notification.config({
| getContainer | 配置渲染节点的输出位置 | () => HTMLNode | () => document.body | |
| placement | 弹出位置,可选 `topLeft` `topRight` `bottomLeft` `bottomRight` | string | topRight | |
| top | 消息从顶部弹出时,距离顶部的位置,单位像素。 | string | `24px` | |
| closeIcon | 自定义关闭图标 | VNode | - | |
| closeIcon | 自定义关闭图标 | VNode \| () => VNode | - | |
| rtl | 是否开启 RTL 模式 | boolean | false | 3.0 |
| maxCount | 最大显示数, 超过限制时,最早的消息会被自动关闭 | number | - | 3.0 |

View File

@ -65,6 +65,7 @@ type NotificationState = {
const Notification = defineComponent<NotificationProps>({
name: 'Notification',
inheritAttrs: false,
props: ['prefixCls', 'transitionName', 'animation', 'maxCount', 'closeIcon'] as any,
setup(props, { attrs, expose, slots }) {
const hookRefs = new Map<Key, HTMLDivElement>();