// @flow strict import * as React from 'react'; import { // $FlowFixMe[untyped-import] FloatingFocusManager, // $FlowFixMe[untyped-import] useFloating, } from '@floating-ui/react'; import classify from '../../utils/classify'; import {CloseIcon, Icon} from '../Icon'; import {SubTitleExtraSmall, TEXT_COLORS} from '../Text'; import typography from '../../styles/typography.module.css'; import css from './Toast.module.css'; type ClassNames = $ReadOnly<{wrapper?: string, time?: string}>; export const TOAST_SEMANTIC = Object.freeze({ success: 'success', information: 'information', warning: 'warning', danger: 'danger', primary: 'primary', }); export type ToastSemanticType = $Values; export type ToastProps = { classNames?: ClassNames, children?: React.Node, time?: string, semantic?: ToastSemanticType, onClose?: () => void, initialFocus?: number, customIcon?: React.Node, hideCloseIcon?: boolean, }; const ToastIcon = ({semantic}: {semantic: ToastSemanticType}) => { switch (semantic) { case 'success': return ( ); case TOAST_SEMANTIC.information: return ( ); case TOAST_SEMANTIC.warning: return ( ); case TOAST_SEMANTIC.danger: return ( ); default: return ( ); } }; export type ToastTitleProps = { children?: React.Node, className?: string, semantic?: ToastSemanticType, }; export const ToastTitle: React$AbstractComponent< ToastTitleProps, HTMLDivElement, > = React.forwardRef( ( {children, semantic = '', className, ...props}: ToastTitleProps, ref, ): React.Node => (
{children}
), ); ToastTitle.displayName = 'ToastTitle'; export type ToastBodyProps = { children?: React.Node, className?: string, }; export const ToastBody: React$AbstractComponent< ToastBodyProps, HTMLDivElement, > = React.forwardRef( ({children, className, ...props}: ToastBodyProps, ref): React.Node => (
{children}
), ); ToastBody.displayName = 'ToastBody'; export type ToastFooterProps = { children?: React.Node, onClose?: () => void, }; export const ToastFooter = ({ children, onClose, }: ToastFooterProps): React.Node => { const arrayChildren = React.Children.toArray(children); const footerActions = React.Children.map(children, (child, index) => { const isLast = index === arrayChildren.length - 1; if (React.isValidElement(child)) { const {onClick} = child.props; const buttonClickHandler = (e) => { onClose && onClose(); onClick && onClick(e); }; if (child?.type?.displayName === 'Button' && isLast) { return React.cloneElement(child, { size: 'small', type: 'primary', ...child.props, onClick: buttonClickHandler, }); } else if (child?.type?.displayName === 'Button') { return React.cloneElement(child, { size: 'small', type: 'tertiary', ...child.props, onClick: buttonClickHandler, }); } } return child; }); return React.Children.count(children) > 0 ? (
{footerActions}
) : null; }; ToastFooter.displayName = 'ToastFooter'; export const Toast = ({ classNames, children, time, semantic = TOAST_SEMANTIC.success, onClose, initialFocus = -1, customIcon, hideCloseIcon, }: ToastProps): React.Node => { const {refs, context} = useFloating({ open: true, }); const getComp = (comp: string) => { const childrenArray = React.Children.toArray(children); if (childrenArray.length) { const nodes: React.Node[] = []; for (const child of childrenArray) { if (child?.type?.displayName === comp) { nodes.push(React.cloneElement(child, {semantic})); } } return nodes.length > 1 ? nodes : nodes[0]; } return null; }; const footer = getComp('ToastFooter'); // $FlowFixMe const footerWithClose = footer ? React.cloneElement(footer, {onClose}) : null; return (
{customIcon || }
{getComp('ToastTitle')} {getComp('ToastBody')}
{time && ( {time} )}
{footerWithClose}
{!hideCloseIcon && ( )}
); };