import * as React from 'react'; import { isHTMLElement, useEventCallback, useForceUpdate } from '@fluentui/react-utilities'; import { useFluent_unstable as useFluent } from '@fluentui/react-shared-contexts'; import { createToaster } from './vanilla'; import { EVENTS } from './constants'; export function useToaster(options = {}) { const forceUpdate = useForceUpdate(); const { toasterId: userToasterId, shortcuts } = options; // Currently the toaster options can never be changed at runtime const [toaster] = React.useState(()=>createToaster(options)); const { targetDocument } = useFluent(); const lastActiveElementRef = React.useRef(null); const isCorrectToaster = useEventCallback((toasterId)=>{ return toasterId === userToasterId; }); const isFocusShortcut = useEventCallback((e)=>{ if (shortcuts === null || shortcuts === void 0 ? void 0 : shortcuts.focus) { return shortcuts.focus(e); } }); const pauseAllToasts = React.useCallback(()=>{ toaster.visibleToasts.forEach((toastId)=>{ var _toast_imperativeRef_current; const toast = toaster.toasts.get(toastId); toast === null || toast === void 0 ? void 0 : (_toast_imperativeRef_current = toast.imperativeRef.current) === null || _toast_imperativeRef_current === void 0 ? void 0 : _toast_imperativeRef_current.pause(); }); }, [ toaster ]); const playAllToasts = React.useCallback(()=>{ toaster.visibleToasts.forEach((toastId)=>{ var _toast_imperativeRef_current; const toast = toaster.toasts.get(toastId); toast === null || toast === void 0 ? void 0 : (_toast_imperativeRef_current = toast.imperativeRef.current) === null || _toast_imperativeRef_current === void 0 ? void 0 : _toast_imperativeRef_current.play(); }); }, [ toaster ]); const getMostRecentVisibleToast = React.useCallback(()=>{ return Array.from(toaster.visibleToasts).reduce((cur, next)=>{ const toast = toaster.toasts.get(next); if (!toast) { return cur; } if (!cur) { return toast; } if (cur.order < (toast === null || toast === void 0 ? void 0 : toast.order)) { return toast; } return cur; }, undefined); }, [ toaster ]); const tryRestoreFocus = React.useCallback(()=>{ const mostRecentToast = getMostRecentVisibleToast(); if (mostRecentToast === null || mostRecentToast === void 0 ? void 0 : mostRecentToast.imperativeRef.current) { mostRecentToast.imperativeRef.current.focus(); } else { var _lastActiveElementRef_current; (_lastActiveElementRef_current = lastActiveElementRef.current) === null || _lastActiveElementRef_current === void 0 ? void 0 : _lastActiveElementRef_current.focus(); lastActiveElementRef.current = null; } }, [ getMostRecentVisibleToast ]); const closeAllToasts = React.useCallback(()=>{ toaster.visibleToasts.forEach((toastId)=>{ const toast = toaster.toasts.get(toastId); toast === null || toast === void 0 ? void 0 : toast.close(); }); tryRestoreFocus(); }, [ toaster, tryRestoreFocus ]); React.useEffect(()=>{ if (!targetDocument) { return; } const addToastListener = (eventType, callback)=>{ const listener = (e)=>{ if (!isCorrectToaster(e.detail.toasterId)) { return; } callback(e); forceUpdate(); }; targetDocument.addEventListener(eventType, listener); return ()=>targetDocument.removeEventListener(eventType, listener); }; const buildToast = (e)=>{ toaster.buildToast(e.detail, forceUpdate); }; const dismissToast = (e)=>{ toaster.dismissToast(e.detail.toastId); }; const updateToast = (e)=>{ toaster.updateToast(e.detail); }; const dismissAllToasts = (e)=>{ toaster.dismissAllToasts(); }; const pauseToast = (e)=>{ const toast = toaster.toasts.get(e.detail.toastId); if (toast) { var _toast_imperativeRef_current; (_toast_imperativeRef_current = toast.imperativeRef.current) === null || _toast_imperativeRef_current === void 0 ? void 0 : _toast_imperativeRef_current.pause(); } }; const playToast = (e)=>{ const toast = toaster.toasts.get(e.detail.toastId); if (toast) { var _toast_imperativeRef_current; (_toast_imperativeRef_current = toast.imperativeRef.current) === null || _toast_imperativeRef_current === void 0 ? void 0 : _toast_imperativeRef_current.play(); } }; const cleanupBuildListener = addToastListener(EVENTS.show, buildToast); const cleanupUpdateListener = addToastListener(EVENTS.update, updateToast); const cleanupDismissListener = addToastListener(EVENTS.dismiss, dismissToast); const cleanupDismissAllListener = addToastListener(EVENTS.dismissAll, dismissAllToasts); const cleanupPauseListener = addToastListener(EVENTS.pause, pauseToast); const cleanupPlayListener = addToastListener(EVENTS.play, playToast); const focusShortcutListener = (e)=>{ if (isFocusShortcut(e)) { pauseAllToasts(); const mostRecentToast = getMostRecentVisibleToast(); if (mostRecentToast) { var _mostRecentToast_imperativeRef_current; lastActiveElementRef.current = isHTMLElement(targetDocument.activeElement) ? targetDocument.activeElement : null; (_mostRecentToast_imperativeRef_current = mostRecentToast.imperativeRef.current) === null || _mostRecentToast_imperativeRef_current === void 0 ? void 0 : _mostRecentToast_imperativeRef_current.focus(); } } }; targetDocument.addEventListener('keydown', focusShortcutListener); return ()=>{ cleanupBuildListener(); cleanupDismissAllListener(); cleanupUpdateListener(); cleanupDismissListener(); cleanupPauseListener(); cleanupPlayListener(); targetDocument.removeEventListener('keydown', focusShortcutListener); }; }, [ toaster, forceUpdate, targetDocument, isCorrectToaster, pauseAllToasts, getMostRecentVisibleToast, isFocusShortcut ]); const toastsToRender = (()=>{ if (!toaster) { return new Map(); } const toRender = new Map(); const toasts = Array.from(toaster.toasts.values()); toasts.forEach((toast)=>{ const { position } = toast; toRender.has(position) || toRender.set(position, []); if (position.startsWith('bottom')) { toRender.get(position).push(toast); } else { toRender.get(position).unshift(toast); } }); return toRender; })(); return { isToastVisible: toaster.isToastVisible, toastsToRender, pauseAllToasts, playAllToasts, tryRestoreFocus, closeAllToasts }; }