refactor: notification
parent
f92e75a1d9
commit
842dde0706
|
@ -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';
|
||||
|
|
|
@ -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)} />
|
||||
);
|
||||
|
|
|
@ -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' }),
|
||||
});
|
||||
};
|
||||
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -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,
|
||||
};
|
||||
},
|
||||
});
|
||||
|
|
|
@ -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 |
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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 |
|
||||
|
|
|
@ -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>();
|
||||
|
|
Loading…
Reference in New Issue