Files
ant-design/components/notification/PurePanel.tsx

243 lines
7.0 KiB
TypeScript

import * as React from 'react';
import CheckCircleFilled from '@ant-design/icons/CheckCircleFilled';
import CloseCircleFilled from '@ant-design/icons/CloseCircleFilled';
import CloseOutlined from '@ant-design/icons/CloseOutlined';
import ExclamationCircleFilled from '@ant-design/icons/ExclamationCircleFilled';
import InfoCircleFilled from '@ant-design/icons/InfoCircleFilled';
import LoadingOutlined from '@ant-design/icons/LoadingOutlined';
import { Notice } from '@rc-component/notification';
import type { NoticeProps } from '@rc-component/notification/lib/Notice';
import { clsx } from 'clsx';
import { pickClosable, useClosable, useMergeSemantic } from '../_util/hooks';
import type { SemanticClassNamesType, SemanticStylesType } from '../_util/hooks';
import { devUseWarning } from '../_util/warning';
import { ConfigContext } from '../config-provider';
import { useComponentConfig } from '../config-provider/context';
import useCSSVarCls from '../config-provider/hooks/useCSSVarCls';
import type {
IconType,
NotificationSemanticClassNames,
NotificationSemanticStyles,
} from './interface';
import useStyle from './style';
import PurePanelStyle from './style/pure-panel';
export type PurePanelClassNamesType = SemanticClassNamesType<
PurePanelProps,
NotificationSemanticClassNames
>;
export type PurePanelStylesType = SemanticStylesType<PurePanelProps, NotificationSemanticStyles>;
export const TypeIcon = {
info: <InfoCircleFilled />,
success: <CheckCircleFilled />,
error: <CloseCircleFilled />,
warning: <ExclamationCircleFilled />,
loading: <LoadingOutlined />,
};
export function getCloseIcon(prefixCls: string, closeIcon?: React.ReactNode): React.ReactNode {
if (closeIcon === null || closeIcon === false) {
return null;
}
return closeIcon || <CloseOutlined className={`${prefixCls}-close-icon`} />;
}
export interface PureContentProps {
prefixCls: string;
icon?: React.ReactNode;
/** @deprecated Please use `title` instead */
message?: React.ReactNode;
title?: React.ReactNode;
description?: React.ReactNode;
/** @deprecated Please use `actions` instead */
btn?: React.ReactNode;
actions?: React.ReactNode;
type?: IconType;
role?: 'alert' | 'status';
classNames: NotificationSemanticClassNames;
styles: NotificationSemanticStyles;
}
const typeToIcon = {
success: CheckCircleFilled,
info: InfoCircleFilled,
error: CloseCircleFilled,
warning: ExclamationCircleFilled,
};
export const PureContent: React.FC<PureContentProps> = (props) => {
const {
prefixCls,
icon,
type,
title,
description,
actions,
role = 'alert',
styles,
classNames: pureContentCls,
} = props;
let iconNode: React.ReactNode = null;
if (icon) {
iconNode = (
<span className={clsx(`${prefixCls}-icon`, pureContentCls.icon)} style={styles.icon}>
{icon}
</span>
);
} else if (type) {
iconNode = React.createElement(typeToIcon[type] || null, {
className: clsx(`${prefixCls}-icon`, pureContentCls.icon, `${prefixCls}-icon-${type}`),
style: styles.icon,
});
}
return (
<div className={clsx({ [`${prefixCls}-with-icon`]: iconNode })} role={role}>
{iconNode}
<div className={clsx(`${prefixCls}-title`, pureContentCls.title)} style={styles.title}>
{title}
</div>
{description && (
<div
className={clsx(`${prefixCls}-description`, pureContentCls.description)}
style={styles.description}
>
{description}
</div>
)}
{actions && (
<div
className={clsx(`${prefixCls}-actions`, pureContentCls.actions)}
style={styles.actions}
>
{actions}
</div>
)}
</div>
);
};
export interface PurePanelProps
extends Omit<NoticeProps, 'prefixCls' | 'eventKey' | 'classNames' | 'styles'>,
Omit<PureContentProps, 'prefixCls' | 'children' | 'classNames' | 'styles'> {
prefixCls?: string;
classNames?: PurePanelClassNamesType;
styles?: PurePanelStylesType;
closeIcon?: React.ReactNode;
}
/** @private Internal Component. Do not use in your production. */
const PurePanel: React.FC<PurePanelProps> = (props) => {
const {
prefixCls: staticPrefixCls,
icon,
type,
message,
title,
description,
btn,
actions,
closeIcon: _closeIcon,
className: notificationClassName,
style,
styles,
classNames: notificationClassNames,
closable,
...restProps
} = props;
const {
getPrefixCls,
className: contextClassName,
style: contextStyle,
classNames: contextClassNames,
styles: contextStyles,
} = useComponentConfig('notification');
const [mergedClassNames, mergedStyles] = useMergeSemantic<
PurePanelClassNamesType,
PurePanelStylesType,
PurePanelProps
>([contextClassNames, notificationClassNames], [contextStyles, styles], {
props,
});
const { notification: notificationContext } = React.useContext(ConfigContext);
const mergedActions = actions ?? btn;
if (process.env.NODE_ENV !== 'production') {
const warning = devUseWarning('Notification');
[
['btn', 'actions'],
['message', 'title'],
].forEach(([deprecatedName, newName]) => {
warning.deprecated(!(deprecatedName in props), deprecatedName, newName);
});
}
const mergedTitle = title ?? message;
const prefixCls = staticPrefixCls || getPrefixCls('notification');
const noticePrefixCls = `${prefixCls}-notice`;
const rootCls = useCSSVarCls(prefixCls);
const [hashId, cssVarCls] = useStyle(prefixCls, rootCls);
const [rawClosable, mergedCloseIcon, , ariaProps] = useClosable(
pickClosable(props),
pickClosable(notificationContext),
{
closable: true,
closeIcon: <CloseOutlined className={`${prefixCls}-close-icon`} />,
closeIconRender: (icon) => getCloseIcon(prefixCls, icon),
},
);
const mergedClosable = rawClosable
? {
onClose: closable && typeof closable === 'object' ? closable?.onClose : undefined,
closeIcon: mergedCloseIcon,
...ariaProps,
}
: false;
return (
<div
className={clsx(
`${noticePrefixCls}-pure-panel`,
hashId,
notificationClassName,
cssVarCls,
rootCls,
mergedClassNames.root,
)}
style={mergedStyles.root}
>
<PurePanelStyle prefixCls={prefixCls} />
<Notice
style={{ ...contextStyle, ...style }}
{...restProps}
prefixCls={prefixCls}
eventKey="pure"
duration={null}
closable={mergedClosable}
className={clsx(notificationClassName, contextClassName)}
content={
<PureContent
classNames={mergedClassNames as PureContentProps['classNames']}
styles={mergedStyles as PureContentProps['styles']}
prefixCls={noticePrefixCls}
icon={icon}
type={type}
title={mergedTitle}
description={description}
actions={mergedActions}
/>
}
/>
</div>
);
};
export default PurePanel;