181 lines
7.4 KiB
JavaScript
181 lines
7.4 KiB
JavaScript
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
|
|
};
|
|
}
|