import React, { forwardRef, HTMLAttributes, useEffect, useState, useRef, ReactElement } from 'react' import PropTypes from 'prop-types' import classNames from 'classnames' import { CConditionalPortal } from '../conditional-portal' import type { CToastProps } from './CToast' export interface CToasterProps extends HTMLAttributes { /** * A string of all className you want applied to the base component. */ className?: string /** * Describes the placement of your component. * * @type 'top-start' | 'top' | 'top-end' | 'middle-start' | 'middle' | 'middle-end' | 'bottom-start' | 'bottom' | 'bottom-end' | string */ placement?: | 'top-start' | 'top-center' | 'top-end' | 'middle-start' | 'middle-center' | 'middle-end' | 'bottom-start' | 'bottom-center' | 'bottom-end' | string /** * Adds new `CToast` to `CToaster`. */ push?: ReactElement } export const CToaster = forwardRef( ({ children, className, placement, push, ...rest }, ref) => { const [toasts, setToasts] = useState[]>([]) const index = useRef(0) useEffect(() => { index.current++ if (push) { addToast(push) } }, [push]) const addToast = (push: ReactElement) => { setToasts((state) => [ ...state, React.cloneElement(push, { index: index.current, innerKey: index.current, onClose: (index: number) => setToasts((state) => state.filter((i) => i.props.index !== index)), }), ]) } return ( {toasts.length > 0 || children ? (
{children} {toasts.map((toast, index) => React.cloneElement(toast, { visible: true, key: index }))}
) : null}
) } ) CToaster.propTypes = { children: PropTypes.node, className: PropTypes.string, placement: PropTypes.oneOfType([ PropTypes.string, PropTypes.oneOf([ 'top-start', 'top-center', 'top-end', 'middle-start', 'middle-center', 'middle-end', 'bottom-start', 'bottom-center', 'bottom-end', ]), ]), push: PropTypes.any, } CToaster.displayName = 'CToaster'