UNPKG

@lobehub/ui

Version:

Lobe UI is an open-source UI component library for building AIGC web apps

240 lines (237 loc) 7.28 kB
'use client'; import { useIsClient } from "../hooks/useIsClient.mjs"; import { LOBE_THEME_APP_ID } from "../ThemeProvider/constants.mjs"; import { registerDevSingleton } from "../utils/devSingleton.mjs"; import { ToastContext } from "./context.mjs"; import { viewportVariants } from "./style.mjs"; import Toast_default from "./Toast.mjs"; import { memo, useEffect, useState } from "react"; import { Fragment as Fragment$1, jsx } from "react/jsx-runtime"; import { cx } from "antd-style"; import { createPortal } from "react-dom"; import { Toast } from "@base-ui/react/toast"; //#region src/Toast/imperative.tsx const ALL_POSITIONS = [ "top", "top-left", "top-right", "bottom", "bottom-left", "bottom-right" ]; let globalState = { duration: 5e3, limit: 5, position: "bottom-right", swipeDirection: ["down", "right"] }; const toastManagers = { "bottom": Toast.createToastManager(), "bottom-left": Toast.createToastManager(), "bottom-right": Toast.createToastManager(), "top": Toast.createToastManager(), "top-left": Toast.createToastManager(), "top-right": Toast.createToastManager() }; const activeToastIds = { "bottom": /* @__PURE__ */ new Set(), "bottom-left": /* @__PURE__ */ new Set(), "bottom-right": /* @__PURE__ */ new Set(), "top": /* @__PURE__ */ new Set(), "top-left": /* @__PURE__ */ new Set(), "top-right": /* @__PURE__ */ new Set() }; const getManager = (position) => toastManagers[position]; const normalizeOptions = (optionsOrMessage, type) => { if (typeof optionsOrMessage === "string") return { description: optionsOrMessage, type }; return { ...optionsOrMessage, type }; }; const createToastInstance = (id, position) => ({ close: () => getManager(position).close(id), id, update: (options) => { getManager(position).update(id, { data: options, description: options.description, title: options.title }); } }); const addToast = (options) => { const position = options.placement ?? globalState.position; const manager = getManager(position); const onRemove = options.onRemove; const id = manager.add({ data: options, description: options.description, onClose: options.onClose, onRemove: () => { activeToastIds[position].delete(id); onRemove?.(); }, timeout: options.duration ?? globalState.duration, title: options.title }); activeToastIds[position].add(id); return createToastInstance(id, position); }; const dismissToast = (id) => { if (id) for (const [position, manager] of Object.entries(toastManagers)) { activeToastIds[position].delete(id); manager.close(id); } else for (const [position, manager] of Object.entries(toastManagers)) { const ids = Array.from(activeToastIds[position]); for (const toastId of ids) manager.close(toastId); activeToastIds[position].clear(); } }; const createSuccessToast = (optionsOrMessage) => { return addToast(normalizeOptions(optionsOrMessage, "success")); }; const createErrorToast = (optionsOrMessage) => { return addToast(normalizeOptions(optionsOrMessage, "error")); }; const createInfoToast = (optionsOrMessage) => { return addToast(normalizeOptions(optionsOrMessage, "info")); }; const createWarningToast = (optionsOrMessage) => { return addToast(normalizeOptions(optionsOrMessage, "warning")); }; const createLoadingToast = (optionsOrMessage) => { return addToast({ duration: 0, ...normalizeOptions(optionsOrMessage, "loading") }); }; async function promiseToast(promise, options) { const loadingToast = addToast({ closable: false, duration: 0, type: "loading", ...typeof options.loading === "string" ? { description: options.loading } : options.loading }); try { const result = await promise; loadingToast.close(); addToast({ type: "success", ...(() => { if (typeof options.success === "string") return { description: options.success }; if (typeof options.success === "function") return { description: options.success(result) }; return options.success; })() }); return result; } catch (error) { loadingToast.close(); addToast({ type: "error", ...(() => { if (typeof options.error === "string") return { description: options.error }; if (typeof options.error === "function") return { description: options.error(error) }; return options.error; })() }); throw error; } } const baseToast = (options) => { return addToast({ type: "default", ...options }); }; const toast = Object.assign(baseToast, { dismiss: dismissToast, error: createErrorToast, info: createInfoToast, loading: createLoadingToast, promise: promiseToast, success: createSuccessToast, warning: createWarningToast }); const ToastList = memo(() => { const { toasts } = Toast.useToastManager(); return toasts.map((t) => /* @__PURE__ */ jsx(Toast_default, { toast: t }, t.id)); }); ToastList.displayName = "ToastList"; const TOAST_PORTAL_ATTR = "data-lobe-ui-toast-portal"; const TOAST_CONTAINER_ATTR = "data-lobe-ui-toast-container"; const containerMap = /* @__PURE__ */ new WeakMap(); const getOrCreateContainer = (root) => { const resolvedRoot = (() => { if (typeof document === "undefined") return root; if (typeof ShadowRoot !== "undefined" && root instanceof ShadowRoot) return root; if (!(root === document.body)) return root; const themeApp = document.querySelector(`#${LOBE_THEME_APP_ID}`); if (themeApp) return themeApp; const toastContainer = document.querySelector(`[${TOAST_CONTAINER_ATTR}="true"]`); if (toastContainer) return toastContainer; return root; })(); const cached = containerMap.get(resolvedRoot); if (cached && cached.isConnected) return cached; const el = document.createElement("div"); el.setAttribute(TOAST_PORTAL_ATTR, "true"); resolvedRoot.append(el); containerMap.set(resolvedRoot, el); return el; }; const resolveRoot = (root) => { if (root) return root; return document.body; }; const ToastHost = memo(({ root, className, duration = 5e3, limit = 5, position = "bottom-right", swipeDirection = ["down", "right"] }) => { const isClient = useIsClient(); const [container, setContainer] = useState(null); useEffect(() => { globalState = { duration, limit, position, swipeDirection }; }, [ duration, limit, position, swipeDirection ]); useEffect(() => { if (!isClient) return; const resolved = resolveRoot(root); if (resolved) setContainer(getOrCreateContainer(resolved)); return registerDevSingleton("ToastHost", root ?? document.body); }, [isClient, root]); if (!isClient || !container) return null; return createPortal(/* @__PURE__ */ jsx(Fragment$1, { children: ALL_POSITIONS.map((pos) => /* @__PURE__ */ jsx(ToastContext.Provider, { value: { position: pos, swipeDirection }, children: /* @__PURE__ */ jsx(Toast.Provider, { limit, timeout: duration, toastManager: getManager(pos), children: /* @__PURE__ */ jsx(Toast.Portal, { container, children: /* @__PURE__ */ jsx(Toast.Viewport, { className: cx(viewportVariants({ position: pos }), className), children: /* @__PURE__ */ jsx(ToastList, {}) }) }) }) }, pos)) }), container); }); ToastHost.displayName = "ToastHost"; const useToast = () => toast; //#endregion export { TOAST_CONTAINER_ATTR, ToastHost, toast, useToast }; //# sourceMappingURL=imperative.mjs.map