braid-design-system
Version:
Themeable design system for the SEEK Group
126 lines (125 loc) • 3.55 kB
JavaScript
import { jsx, jsxs } from "react/jsx-runtime";
import { useContext, createContext, Fragment, useCallback, useReducer, useState, useEffect } from "react";
import { BraidPortal } from "../BraidPortal/BraidPortal.mjs";
import { Toaster } from "./Toaster.mjs";
let toastCounter = 0;
const ToastControllerContext = createContext(null);
const QUEUE_TOAST = 0;
const REMOVE_TOAST = 1;
function reducer(state, action) {
switch (action.type) {
case QUEUE_TOAST: {
const { toast } = action;
const hasExistingToast = state.toasts.some(
(t) => t.dedupeKey === toast.dedupeKey
);
if (hasExistingToast) {
return {
toasts: state.toasts.map((t) => {
if (t.dedupeKey === toast.dedupeKey) {
return {
...t,
shouldRemove: true
};
}
return t;
}),
queuedToasts: {
...state.queuedToasts,
[toast.dedupeKey]: toast
}
};
}
return {
...state,
toasts: [...state.toasts, action.toast]
};
}
case REMOVE_TOAST: {
const toasts = state.toasts.filter(
({ dedupeKey }) => dedupeKey !== action.dedupeKey
);
const queuedToast = state.queuedToasts[action.dedupeKey];
if (queuedToast) {
return {
queuedToasts: {
...state.queuedToasts,
[action.dedupeKey]: void 0
},
toasts: [...toasts, queuedToast]
};
}
return {
...state,
toasts
};
}
}
}
const InternalToastProvider = ({ children }) => {
const [{ toasts }, dispatch] = useReducer(reducer, {
toasts: [],
queuedToasts: {}
});
const addToast = useCallback(
(toast) => dispatch({ type: QUEUE_TOAST, toast }),
[]
);
const removeToast = useCallback(
(dedupeKey) => dispatch({ type: REMOVE_TOAST, dedupeKey }),
[]
);
return /* @__PURE__ */ jsxs(ToastControllerContext.Provider, { value: addToast, children: [
children,
/* @__PURE__ */ jsx(ToastPortal, { children: /* @__PURE__ */ jsx(Toaster, { toasts, removeToast }) })
] });
};
const ToastProvider = ({ children }) => {
const currentContext = useContext(ToastControllerContext);
if (currentContext !== null) {
return /* @__PURE__ */ jsx(Fragment, { children });
}
return /* @__PURE__ */ jsx(InternalToastProvider, { children });
};
const ToastPortal = ({ children }) => {
const [toastElement, setElement] = useState(null);
useEffect(() => {
const toastContainerId = "braid-toast-container";
let element = document.getElementById(toastContainerId);
if (!element) {
element = document.createElement("div");
element.setAttribute("id", toastContainerId);
element.setAttribute("class", "");
document.body.appendChild(element);
}
setElement(element);
}, []);
if (!toastElement) {
return null;
}
return /* @__PURE__ */ jsx(BraidPortal, { container: toastElement, children });
};
const useToast = () => {
const addToast = useContext(ToastControllerContext);
if (addToast === null) {
throw new Error('No "ToastProvider" configured');
}
return useCallback(
(toast) => {
const toastKey = `${toastCounter++}`;
const { key, ...rest } = toast;
const dedupeKey = key ?? toastKey;
addToast({
...rest,
toastKey,
dedupeKey,
shouldRemove: false
});
},
[addToast]
);
};
export {
ToastProvider,
useToast
};