import React, { useState, useImperativeHandle, useEffect, forwardRef, useRef, } from "react"; import classNames from "classnames"; import ReactDOM, { unmountComponentAtNode, createPortal } from "react-dom"; import { StyledProps, Omit } from "../_type"; import { useConfig } from "../_util/config-context"; import { Icon } from "../icon"; import { ConfigProvider } from "../configprovider"; import { useVisibleTransition } from "../_util/use-visible-transition"; import { callBoth } from "../_util/call-both"; import { noop } from "../_util/noop"; import { getRoot, MessageTransition, limit } from "./util"; import { useIsomorphicLayoutEffect } from "../_util/use-isomorphic-layout-effect"; export interface MessageProps extends StyledProps { /** * 类型 */ type: "success" | "warning" | "error" | "loading"; /** * 提示内容 */ content: React.ReactNode; /** * 自动关闭延时(单位毫秒) * * 设置为 0 时不自动关闭(`2.5.2` 支持) * * @default 3000 */ duration?: number; /** * 关闭时回调 */ onClose?: () => void; } interface MessageInstance { setVisible: React.Dispatch>; } const Message = forwardRef(function MessageShow( { type, content, duration = 3000, className, style = {}, onClose = noop, }: MessageProps, ref: React.Ref ) { const { classPrefix } = useConfig(); const [visible, setVisible] = useState(false); useImperativeHandle(ref, () => ({ setVisible })); const timerRef = useRef(null); // 渲染之后,马上显示 useEffect(() => { setVisible(true); return () => clearTimeout(timerRef.current); }, []); useIsomorphicLayoutEffect(() => { clearTimeout(timerRef.current); if (visible && duration !== 0) { timerRef.current = setTimeout(() => setVisible(false), duration); } }, [duration, visible]); const { shouldContentEnter, shouldContentRender, onContentExit, } = useVisibleTransition(visible); if (!shouldContentRender) { return null; } const message = (
clearTimeout(timerRef.current)} onMouseLeave={() => { if (duration !== 0) { timerRef.current = setTimeout(() => setVisible(false), duration); } }} > {content}
); return createPortal(message, getRoot(classPrefix)); }); Message.displayName = "Message"; interface MessageHandle { /** * 隐藏 */ hide: () => void; } export interface MessageOptions extends Omit {} let showMethod; /** * API 的方式显示通知 */ function show( type: MessageProps["type"] = "success", { onClose = noop, ...options }: MessageOptions = { content: "" } ): MessageHandle { if (typeof showMethod === "function") { return showMethod(type, options); } const el = document.createElement("div"); const instanceRef = React.createRef(); ReactDOM.render( { onClose(); unmountComponentAtNode(el); }} /> , el ); const hide = () => { if (instanceRef.current) { instanceRef.current.setVisible(false); } }; const handle = { hide, destroy: hide, }; limit(handle); return handle; } export const message = { success: (options: MessageOptions) => show("success", options), warning: (options: MessageOptions) => show("warning", options), error: (options: MessageOptions) => show("error", options), loading: (options: MessageOptions) => show("loading", options), }; // eslint-disable-next-line dot-notation message["setShowMethod"] = method => { showMethod = method; };