// @flow strict import React, {useEffect, useReducer, useRef} from 'react'; // $FlowFixMe[untyped-import] import {createPortal} from 'react-dom'; import type {actionTypes, contentTypes, optionTypes} from 'src/types/toast'; import {useToastPortal} from '../../hooks'; import {classify} from '../../utils/classify'; import {toastManager} from './'; import css from './ToastContainer.module.css'; const ADD = 'ADD'; const REMOVE = 'REMOVE'; type stateTypes = { ...optionTypes, content?: contentTypes, }; type MapperValues = { ...optionTypes, content: contentTypes, }; type Actions = { type: actionTypes, data: stateTypes, }; const reducer = (state: stateTypes[], action: Actions) => { const {type, data} = action; if (type === ADD) { if ( /* $FlowIgnore */ state.filter((i) => i.uniqueCode && i.uniqueCode === data.uniqueCode) .length ) { return state; } return [...state, data]; } else if (type === REMOVE) { return state.filter((i) => i.id !== data.id); } return state; }; export const ToastContainer = (): null | React$Portal => { const [data, dispatch] = useReducer(reducer, []); const toastRef = useRef(null); const {loaded} = useToastPortal({toastRef}); const callback = ( actionType: actionTypes, content: contentTypes, options: optionTypes, ) => { if (actionType === ADD) { dispatch({type: ADD, data: {content, ...options, key: `${options.id}`}}); } if (actionType === REMOVE) { dispatch({type: REMOVE, data: {id: options.id}}); } if (options.autoClose) { window.setTimeout(() => { dispatch({type: REMOVE, data: {id: options.id}}); }, options.timeout); } }; useEffect(() => { toastManager.subscribe(callback); }, []); const positionMaintainer = () => { const mapper = {}; /* $FlowIgnore */ data.map(({position, ...rest}: optionTypes) => { if (position) { if (!mapper[position]) { mapper[position] = []; } mapper[position].push(rest); } }); return mapper; }; const markup = () => { const mapper = positionMaintainer(); return Object.keys(mapper).map((position) => { const content = mapper[position].map(({content, id}: MapperValues) => // $FlowFixMe React.cloneElement(content, { key: id, onClose: () => dispatch({type: REMOVE, data: {id}}), }), ); return (
{content}
); }); }; if (!toastRef.current) { return null; } return loaded ? createPortal(markup(), toastRef.current) : null; };