diff --git a/components/alert/index.tsx b/components/alert/index.tsx index eabe1d76e302..e9149cd14dab 100755 --- a/components/alert/index.tsx +++ b/components/alert/index.tsx @@ -1,5 +1,4 @@ import * as React from 'react'; -import * as ReactDOM from 'react-dom'; import CloseOutlined from '@ant-design/icons/CloseOutlined'; import CheckCircleOutlined from '@ant-design/icons/CheckCircleOutlined'; import ExclamationCircleOutlined from '@ant-design/icons/ExclamationCircleOutlined'; @@ -12,7 +11,7 @@ import CloseCircleFilled from '@ant-design/icons/CloseCircleFilled'; import Animate from 'rc-animate'; import classNames from 'classnames'; -import { ConfigConsumer, ConfigConsumerProps } from '../config-provider'; +import { ConfigContext } from '../config-provider'; import getDataOrAriaProps from '../_util/getDataOrAriaProps'; import ErrorBoundary from './ErrorBoundary'; import { replaceElement } from '../_util/reactNode'; @@ -48,11 +47,6 @@ export interface AlertProps { onClick?: React.MouseEventHandler; } -export interface AlertState { - closing: boolean; - closed: boolean; -} - const iconMapFilled = { success: CheckCircleFilled, info: InfoCircleFilled, @@ -67,66 +61,67 @@ const iconMapOutlined = { warning: ExclamationCircleOutlined, }; -export default class Alert extends React.Component { - static ErrorBoundary = ErrorBoundary; - - state = { - closing: false, - closed: false, - }; +interface AlertInterface extends React.FC { + ErrorBoundary: typeof ErrorBoundary; +} - handleClose = (e: React.MouseEvent) => { +const Alert: AlertInterface = ({ + description, + prefixCls: customizePrefixCls, + message, + banner, + className = '', + style, + onMouseEnter, + onMouseLeave, + onClick, + showIcon, + closable, + closeText, + ...props +}) => { + const [closing, setClosing] = React.useState(false); + const [closed, setClosed] = React.useState(false); + + const ref = React.useRef(); + const { getPrefixCls, direction } = React.useContext(ConfigContext); + const prefixCls = getPrefixCls('alert', customizePrefixCls); + + const handleClose = (e: React.MouseEvent) => { e.preventDefault(); - const dom = ReactDOM.findDOMNode(this) as HTMLElement; + const dom = ref.current as HTMLElement; dom.style.height = `${dom.offsetHeight}px`; // Magic code // 重复一次后才能正确设置 height dom.style.height = `${dom.offsetHeight}px`; - this.setState({ - closing: true, - }); - this.props.onClose?.(e); + setClosing(true); + props.onClose?.(e); }; - animationEnd = () => { - this.setState({ - closing: false, - closed: true, - }); - this.props.afterClose?.(); + const animationEnd = () => { + setClosing(false); + setClosed(true); + props.afterClose?.(); }; - getShowIcon() { - const { banner, showIcon } = this.props; - // banner 模式默认有 Icon - return banner && showIcon === undefined ? true : showIcon; - } - - getType() { - const { banner, type } = this.props; + const getType = () => { + const { type } = props; if (type !== undefined) { return type; } // banner 模式默认为警告 return banner ? 'warning' : 'info'; - } + }; - getClosable() { - const { closable, closeText } = this.props; - // closeable when closeText is assigned - return closeText ? true : closable; - } + // closeable when closeText is assigned + const isClosable = closeText ? true : closable; + const type = getType(); - getIconType() { - const { description } = this.props; + const renderIconNode = () => { + const { icon } = props; // use outline icon in alert with description - return (description ? iconMapOutlined : iconMapFilled)[this.getType()] || null; - } - - renderIconNode({ prefixCls }: { prefixCls: string }) { - const { icon } = this.props; - const iconType = this.getIconType(); + const iconType = (description ? iconMapOutlined : iconMapFilled)[type] || null; if (icon) { return replaceElement(icon, {icon}, () => ({ className: classNames(`${prefixCls}-icon`, { @@ -135,14 +130,13 @@ export default class Alert extends React.Component { })); } return React.createElement(iconType, { className: `${prefixCls}-icon` }); - } + }; - renderCloseIcon({ prefixCls }: { prefixCls: string }) { - const { closeText } = this.props; - return this.getClosable() ? ( + const renderCloseIcon = () => { + return isClosable ? ( ) : null; - } - - renderAlert = ({ getPrefixCls, direction }: ConfigConsumerProps) => { - const { - description, - prefixCls: customizePrefixCls, - message, - banner, - className = '', - style, - onMouseEnter, - onMouseLeave, - onClick, - } = this.props; - const { closing, closed } = this.state; - - const prefixCls = getPrefixCls('alert', customizePrefixCls); - - const isShowIcon = this.getShowIcon(); - const type = this.getType(); - const closable = this.getClosable(); - - const alertCls = classNames( - prefixCls, - `${prefixCls}-${type}`, - { - [`${prefixCls}-closing`]: closing, - [`${prefixCls}-with-description`]: !!description, - [`${prefixCls}-no-icon`]: !isShowIcon, - [`${prefixCls}-banner`]: !!banner, - [`${prefixCls}-closable`]: closable, - [`${prefixCls}-rtl`]: direction === 'rtl', - }, - className, - ); - - const closeIcon = this.renderCloseIcon({ prefixCls }); - - const dataOrAriaProps = getDataOrAriaProps(this.props); - - const iconNode = this.renderIconNode({ prefixCls }); - - return closed ? null : ( - -
- {isShowIcon ? iconNode : null} - {message} - {description} - {closeIcon} -
-
- ); }; - render() { - return {this.renderAlert}; - } -} + // banner 模式默认有 Icon + const isShowIcon = banner && showIcon === undefined ? true : showIcon; + + const alertCls = classNames( + prefixCls, + `${prefixCls}-${type}`, + { + [`${prefixCls}-closing`]: closing, + [`${prefixCls}-with-description`]: !!description, + [`${prefixCls}-no-icon`]: !isShowIcon, + [`${prefixCls}-banner`]: !!banner, + [`${prefixCls}-closable`]: isClosable, + [`${prefixCls}-rtl`]: direction === 'rtl', + }, + className, + ); + + const dataOrAriaProps = getDataOrAriaProps(props); + + return closed ? null : ( + +
+ {isShowIcon ? renderIconNode() : null} + {message} + {description} + {renderCloseIcon()} +
+
+ ); +}; + +Alert.ErrorBoundary = ErrorBoundary; + +export default Alert;