Compare commits

...

10 Commits

Author SHA1 Message Date
𝑾𝒖𝒙𝒉
a541d5aa4b fix 2025-10-16 15:41:33 +08:00
𝑾𝒖𝒙𝒉
a1a0cb64e6 chore: update 2025-10-16 13:48:09 +08:00
𝑾𝒖𝒙𝒉
856dabd295 chore: update 2025-10-16 13:35:53 +08:00
𝑾𝒖𝒙𝒉
7dfc686bb7 chore: update 2025-10-16 13:21:09 +08:00
𝑾𝒖𝒙𝒉
42ff8e0ed3 chore: update 2025-10-16 13:15:53 +08:00
𝑾𝒖𝒙𝒉
a076b23da2 chore: update 2025-10-16 12:38:28 +08:00
𝑾𝒖𝒙𝒉
3d70240760 chore: update 2025-10-16 11:30:41 +08:00
𝑾𝒖𝒙𝒉
2f53a841db chore: update 2025-10-16 11:13:37 +08:00
𝑾𝒖𝒙𝒉
2c04426dd7 chore: update 2025-10-16 11:05:08 +08:00
𝑾𝒖𝒙𝒉
4e1835e54f refactor: streamline notification and message configuration interfaces 2025-10-16 10:57:34 +08:00
10 changed files with 79 additions and 94 deletions

View File

@@ -44,7 +44,7 @@ export interface PurePanelProps
/** @private Internal Component. Do not use in your production. */
const PurePanel: React.FC<PurePanelProps> = (props) => {
const { prefixCls: staticPrefixCls, className, type, icon, content, ...restProps } = props;
const { getPrefixCls } = React.useContext(ConfigContext);
const { getPrefixCls, message } = React.useContext(ConfigContext);
const prefixCls = staticPrefixCls || getPrefixCls('message');
@@ -61,6 +61,7 @@ const PurePanel: React.FC<PurePanelProps> = (props) => {
`${prefixCls}-notice-pure-panel`,
cssVarCls,
rootCls,
message?.className,
)}
eventKey="pure"
duration={null}

View File

@@ -57,10 +57,13 @@ let taskQueue: Task[] = [];
let defaultGlobalConfig: ConfigOptions = {};
function getGlobalContext() {
const { getContainer, duration, rtl, maxCount, top } = defaultGlobalConfig;
const { getContainer, ...restConfig } = defaultGlobalConfig;
const mergedContainer = getContainer?.() || document.body;
return { getContainer: () => mergedContainer, duration, rtl, maxCount, top };
return {
...restConfig,
getContainer: () => mergedContainer,
};
}
interface GlobalHolderRef {

View File

@@ -1,28 +1,25 @@
import type * as React from 'react';
import type { NotificationConfig as RcNotificationConfig } from 'rc-notification';
export type NoticeType = 'info' | 'success' | 'error' | 'warning' | 'loading';
export interface ConfigOptions {
type SharedProps = Pick<
RcNotificationConfig,
'prefixCls' | 'maxCount' | 'duration' | 'getContainer' | 'pauseOnHover'
>;
export interface ConfigOptions extends SharedProps {
top?: string | number;
duration?: number;
prefixCls?: string;
getContainer?: () => HTMLElement;
transitionName?: string;
maxCount?: number;
rtl?: boolean;
}
export interface ArgsProps {
export interface ArgsProps extends SharedProps {
/**
* @descCN 消息通知的内容,接收组件或者字符串
* @descEN The content of the message notification, receiving component or string
*/
content: React.ReactNode;
/**
* @descCN 消息通知持续显示的时间
* @descEN How long the message notification remains displayed
*/
duration?: number;
/**
* @descCN 消息通知的类型,可以是 'info'、'success'、'error'、'warning' 或 'loading'
* @descEN The type of message notification, which can be 'info', 'success', 'error', 'warning' or 'loading'

View File

@@ -60,11 +60,11 @@ const Holder = React.forwardRef<HolderRef, HolderProps>((props, ref) => {
top,
prefixCls: staticPrefixCls,
getContainer: staticGetContainer,
maxCount,
duration = DEFAULT_DURATION,
rtl,
transitionName,
onAllRemoved,
...restProps
} = props;
const { getPrefixCls, getPopupContainer, message, direction } = React.useContext(ConfigContext);
@@ -91,6 +91,7 @@ const Holder = React.forwardRef<HolderRef, HolderProps>((props, ref) => {
// ============================== Origin ===============================
const [api, holder] = useRcNotification({
...restProps,
prefixCls,
style: getStyle,
className: getClassName,
@@ -99,7 +100,6 @@ const Holder = React.forwardRef<HolderRef, HolderProps>((props, ref) => {
closeIcon: mergedCloseIcon,
duration,
getContainer: () => staticGetContainer?.() || getPopupContainer?.() || document.body,
maxCount,
onAllRemoved,
renderNotifications,
});

View File

@@ -15,6 +15,7 @@ import useCSSVarCls from '../config-provider/hooks/useCSSVarCls';
import type { IconType } from './interface';
import useStyle from './style';
import PurePanelStyle from './style/pure-panel';
import { getCloseIconConfig } from './util';
export const TypeIcon = {
info: <InfoCircleFilled />,
@@ -87,12 +88,11 @@ const PurePanel: React.FC<PurePanelProps> = (props) => {
description,
btn,
actions,
closable = true,
closable,
closeIcon,
className: notificationClassName,
...restProps
} = props;
const { getPrefixCls } = React.useContext(ConfigContext);
const { getPrefixCls, notification } = React.useContext(ConfigContext);
const mergedActions = actions ?? btn;
if (process.env.NODE_ENV !== 'production') {
const warning = devUseWarning('Notification');
@@ -101,12 +101,21 @@ const PurePanel: React.FC<PurePanelProps> = (props) => {
const prefixCls = staticPrefixCls || getPrefixCls('notification');
const noticePrefixCls = `${prefixCls}-notice`;
const realCloseIcon = getCloseIcon(prefixCls, getCloseIconConfig(closeIcon, notification));
const rootCls = useCSSVarCls(prefixCls);
const [wrapCSSVar, hashId, cssVarCls] = useStyle(prefixCls, rootCls);
return wrapCSSVar(
<div
className={classNames(`${noticePrefixCls}-pure-panel`, hashId, className, cssVarCls, rootCls)}
className={classNames(
`${noticePrefixCls}-pure-panel`,
hashId,
className,
cssVarCls,
rootCls,
notification?.className,
)}
>
<PurePanelStyle prefixCls={prefixCls} />
<Notice
@@ -114,11 +123,8 @@ const PurePanel: React.FC<PurePanelProps> = (props) => {
prefixCls={prefixCls}
eventKey="pure"
duration={null}
closable={closable}
className={classNames({
notificationClassName,
})}
closeIcon={getCloseIcon(prefixCls, closeIcon)}
closable={closable ?? !!realCloseIcon}
closeIcon={realCloseIcon}
content={
<PureContent
prefixCls={noticePrefixCls}

View File

@@ -1,6 +1,6 @@
import notification, { actWrapper } from '..';
import { act, fireEvent } from '../../../tests/utils';
import type { ArgsProps, GlobalConfigProps } from '../interface';
import type { ArgsProps, NotificationConfig } from '../interface';
import { awaitPromise, triggerMotionEnd } from './util';
// TODO: Remove this. Mock for React 19
@@ -24,7 +24,7 @@ describe('Notification.placement', () => {
});
}
function config(args: Partial<GlobalConfigProps>) {
function config(args: Partial<NotificationConfig>) {
notification.config({
...args,
});

View File

@@ -21,7 +21,6 @@ const App: React.FC = () => {
{ length: Math.round(Math.random() * 5) + 1 },
() => 'This is the content of the notification.',
).join('\n')}`,
duration: null,
});
};

View File

@@ -3,7 +3,7 @@ import React, { useContext } from 'react';
import { AppConfigContext } from '../app/context';
import ConfigProvider, { ConfigContext, globalConfig, warnContext } from '../config-provider';
import { unstableSetRender } from '../config-provider/UnstableContext';
import type { ArgsProps, GlobalConfigProps, NotificationInstance } from './interface';
import type { ArgsProps, NotificationConfig, NotificationInstance } from './interface';
import PurePanel from './PurePanel';
import useNotification, { useInternalNotification } from './useNotification';
@@ -31,21 +31,15 @@ type Task =
let taskQueue: Task[] = [];
let defaultGlobalConfig: GlobalConfigProps = {};
let defaultGlobalConfig: NotificationConfig = {};
function getGlobalContext() {
const { getContainer, rtl, maxCount, top, bottom, showProgress, pauseOnHover } =
defaultGlobalConfig;
const { getContainer, ...restConfig } = defaultGlobalConfig;
const mergedContainer = getContainer?.() || document.body;
return {
...restConfig,
getContainer: () => mergedContainer,
rtl,
maxCount,
top,
bottom,
showProgress,
pauseOnHover,
};
}
@@ -56,7 +50,7 @@ interface GlobalHolderRef {
const GlobalHolder = React.forwardRef<
GlobalHolderRef,
{ notificationConfig: GlobalConfigProps; sync: () => void }
{ notificationConfig: NotificationConfig; sync: () => void }
>((props, ref) => {
const { notificationConfig, sync } = props;
@@ -93,7 +87,7 @@ const GlobalHolder = React.forwardRef<
const GlobalHolderWrapper = React.forwardRef<GlobalHolderRef, unknown>((_, ref) => {
const [notificationConfig, setNotificationConfig] =
React.useState<GlobalConfigProps>(getGlobalContext);
React.useState<NotificationConfig>(getGlobalContext);
const sync = () => {
setNotificationConfig(getGlobalContext);
@@ -183,7 +177,7 @@ const flushNotificationQueue = () => {
// == Export ==
// ==============================================================================
function setNotificationGlobalConfig(config: GlobalConfigProps) {
function setNotificationGlobalConfig(config: NotificationConfig) {
defaultGlobalConfig = {
...defaultGlobalConfig,
...config,
@@ -214,7 +208,7 @@ const destroy: BaseMethods['destroy'] = (key) => {
interface BaseMethods {
open: (config: ArgsProps) => void;
destroy: (key?: React.Key) => void;
config: (config: GlobalConfigProps) => void;
config: (config: NotificationConfig) => void;
useNotification: typeof useNotification;
/** @private Internal Component. Do not use in your production. */
_InternalPanelDoNotUseOrYouWillBeFired: typeof PurePanel;

View File

@@ -1,6 +1,5 @@
import type * as React from 'react';
import type { ClosableType } from '../_util/hooks/useClosable';
import type { NotificationConfig as RcNotificationConfig } from 'rc-notification';
interface DivProps extends React.HTMLProps<HTMLDivElement> {
'data-testid'?: string;
@@ -14,11 +13,25 @@ export const NotificationPlacements = [
'bottomLeft',
'bottomRight',
] as const;
export type NotificationPlacement = (typeof NotificationPlacements)[number];
export type IconType = 'success' | 'info' | 'error' | 'warning';
export interface ArgsProps {
type SharedProps = Pick<
RcNotificationConfig,
| 'prefixCls'
| 'getContainer'
| 'maxCount'
| 'stack'
| 'duration'
| 'showProgress'
| 'pauseOnHover'
| 'closeIcon'
| 'closable'
>;
export interface ArgsProps extends SharedProps {
message: React.ReactNode;
description?: React.ReactNode;
/** @deprecated Please use `actions` instead */
@@ -26,17 +39,15 @@ export interface ArgsProps {
actions?: React.ReactNode;
key?: React.Key;
onClose?: () => void;
duration?: number | null;
showProgress?: boolean;
pauseOnHover?: boolean;
icon?: React.ReactNode;
placement?: NotificationPlacement;
style?: React.CSSProperties;
className?: string;
readonly type?: IconType;
onClick?: () => void;
closeIcon?: React.ReactNode;
closable?: ClosableType;
/**
* @private It may be internal, uncertain, so it's better not to use it.
*/
props?: DivProps;
role?: 'alert' | 'status';
}
@@ -52,33 +63,16 @@ export interface NotificationInstance {
destroy: (key?: React.Key) => void;
}
export interface GlobalConfigProps {
// Prevent destructive updates, We do not internally use.
export type GlobalConfigProps = NotificationConfig;
export interface NotificationConfig extends SharedProps {
top?: number;
bottom?: number;
duration?: number;
showProgress?: boolean;
pauseOnHover?: boolean;
prefixCls?: string;
getContainer?: () => HTMLElement | ShadowRoot;
placement?: NotificationPlacement;
closeIcon?: React.ReactNode;
closable?: ClosableType;
rtl?: boolean;
maxCount?: number;
/**
* @private It may be internal, uncertain, so it's better not to use it.
*/
props?: DivProps;
}
export interface NotificationConfig {
top?: number;
bottom?: number;
prefixCls?: string;
getContainer?: () => HTMLElement | ShadowRoot;
placement?: NotificationPlacement;
maxCount?: number;
rtl?: boolean;
stack?: boolean | { threshold?: number };
duration?: number;
showProgress?: boolean;
pauseOnHover?: boolean;
closeIcon?: React.ReactNode;
}

View File

@@ -4,6 +4,7 @@ import classNames from 'classnames';
import { NotificationProvider, useNotification as useRcNotification } from 'rc-notification';
import type { NotificationAPI, NotificationConfig as RcNotificationConfig } from 'rc-notification';
import extendsObject from '../_util/extendsObject';
import { devUseWarning } from '../_util/warning';
import { ConfigContext } from '../config-provider';
import type { NotificationConfig as CPNotificationConfig } from '../config-provider/context';
@@ -17,7 +18,7 @@ import type {
} from './interface';
import { getCloseIcon, PureContent } from './PurePanel';
import useStyle from './style';
import { getMotion, getPlacementStyle, getCloseIconConfig } from './util';
import { getCloseIconConfig, getMotion, getPlacementStyle } from './util';
const DEFAULT_OFFSET = 24;
const DEFAULT_DURATION = 4.5;
@@ -60,13 +61,12 @@ const Holder = React.forwardRef<HolderRef, HolderProps>((props, ref) => {
bottom,
prefixCls: staticPrefixCls,
getContainer: staticGetContainer,
maxCount,
rtl,
onAllRemoved,
stack,
duration,
duration = DEFAULT_DURATION,
pauseOnHover = true,
showProgress,
closeIcon,
...restProps
} = props;
const { getPrefixCls, getPopupContainer, notification, direction } = useContext(ConfigContext);
const [, token] = useToken();
@@ -84,27 +84,17 @@ const Holder = React.forwardRef<HolderRef, HolderProps>((props, ref) => {
// ============================== Origin ===============================
const [api, holder] = useRcNotification({
...restProps,
prefixCls,
style: getStyle,
className: getClassName,
motion: getNotificationMotion,
closable: true,
closeIcon: getCloseIcon(prefixCls),
duration: duration ?? DEFAULT_DURATION,
closeIcon: getCloseIcon(prefixCls, closeIcon),
duration,
getContainer: () => staticGetContainer?.() || getPopupContainer?.() || document.body,
maxCount,
pauseOnHover,
showProgress,
onAllRemoved,
renderNotifications,
stack:
stack === false
? false
: {
threshold: typeof stack === 'object' ? stack?.threshold : undefined,
offset: 8,
gap: token.margin,
},
stack: stack === false ? false : extendsObject({ offset: 8, gap: token.margin }, stack),
});
// ================================ Ref ================================
@@ -155,7 +145,8 @@ export function useInternalNotification(
closeIcon,
closable,
...restConfig
} = config;
} = extendsObject(notificationConfig, config);
if (process.env.NODE_ENV !== 'production') {
warning.deprecated(!btn, 'btn', 'actions');
}