@lobehub/ui
Version:
Lobe UI is an open-source UI component library for building AIGC web apps
1 lines • 14.7 kB
Source Map (JSON)
{"version":3,"file":"imperative.mjs","names":["ALL_POSITIONS: ToastPosition[]","globalState: ToastState","toastManagers: Record<ToastPosition, ReturnType<typeof BaseToast.createToastManager>>","BaseToast","activeToastIds: Record<ToastPosition, Set<string>>","toast: ToastAPI","ToastItem"],"sources":["../../src/Toast/imperative.tsx"],"sourcesContent":["'use client';\n\nimport { Toast as BaseToast } from '@base-ui/react/toast';\nimport { cx } from 'antd-style';\nimport { memo, useEffect, useState } from 'react';\nimport { createPortal } from 'react-dom';\n\nimport { LOBE_THEME_APP_ID } from '@/ThemeProvider';\nimport { useIsClient } from '@/hooks/useIsClient';\nimport { registerDevSingleton } from '@/utils/devSingleton';\n\nimport ToastItem from './Toast';\nimport { ToastContext } from './context';\nimport { viewportVariants } from './style';\nimport type {\n ToastAPI,\n ToastInstance,\n ToastOptions,\n ToastPosition,\n ToastPromiseOptions,\n ToastType,\n} from './type';\n\n// All possible positions\nconst ALL_POSITIONS: ToastPosition[] = [\n 'top',\n 'top-left',\n 'top-right',\n 'bottom',\n 'bottom-left',\n 'bottom-right',\n];\n\n// Global state management\ninterface ToastState {\n duration: number;\n limit: number;\n position: ToastPosition;\n swipeDirection: ('left' | 'right' | 'up' | 'down') | ('left' | 'right' | 'up' | 'down')[];\n}\n\nlet globalState: ToastState = {\n duration: 5000,\n limit: 5,\n position: 'bottom-right',\n swipeDirection: ['down', 'right'],\n};\n\n// Toast managers for each position\nconst toastManagers: Record<ToastPosition, ReturnType<typeof BaseToast.createToastManager>> = {\n 'bottom': BaseToast.createToastManager(),\n 'bottom-left': BaseToast.createToastManager(),\n 'bottom-right': BaseToast.createToastManager(),\n 'top': BaseToast.createToastManager(),\n 'top-left': BaseToast.createToastManager(),\n 'top-right': BaseToast.createToastManager(),\n};\n\nconst activeToastIds: Record<ToastPosition, Set<string>> = {\n 'bottom': new Set(),\n 'bottom-left': new Set(),\n 'bottom-right': new Set(),\n 'top': new Set(),\n 'top-left': new Set(),\n 'top-right': new Set(),\n};\n\nconst getManager = (position: ToastPosition) => toastManagers[position];\n\nconst normalizeOptions = (\n optionsOrMessage: Omit<ToastOptions, 'type'> | string,\n type: ToastType,\n): ToastOptions => {\n if (typeof optionsOrMessage === 'string') {\n return {\n description: optionsOrMessage,\n type,\n };\n }\n return {\n ...optionsOrMessage,\n type,\n };\n};\n\nconst createToastInstance = (id: string, position: ToastPosition): ToastInstance => ({\n close: () => getManager(position).close(id),\n id,\n update: (options) => {\n getManager(position).update(id, {\n data: options,\n description: options.description,\n title: options.title,\n });\n },\n});\n\nconst addToast = (options: ToastOptions): ToastInstance => {\n const position = options.placement ?? globalState.position;\n const manager = getManager(position);\n const onRemove = options.onRemove;\n const id = manager.add({\n data: options,\n description: options.description,\n onClose: options.onClose,\n onRemove: () => {\n activeToastIds[position].delete(id);\n onRemove?.();\n },\n timeout: options.duration ?? globalState.duration,\n title: options.title,\n });\n activeToastIds[position].add(id);\n return createToastInstance(id, position);\n};\n\nconst dismissToast = (id?: string) => {\n if (id) {\n // Try to close from all managers since we don't know which position the toast is in\n for (const [position, manager] of Object.entries(toastManagers)) {\n activeToastIds[position as ToastPosition].delete(id);\n manager.close(id);\n }\n } else {\n // Clear all toasts\n for (const [position, manager] of Object.entries(toastManagers)) {\n const ids = Array.from(activeToastIds[position as ToastPosition]);\n for (const toastId of ids) {\n manager.close(toastId);\n }\n activeToastIds[position as ToastPosition].clear();\n }\n }\n};\n\nconst createSuccessToast = (\n optionsOrMessage: Omit<ToastOptions, 'type'> | string,\n): ToastInstance => {\n return addToast(normalizeOptions(optionsOrMessage, 'success'));\n};\n\nconst createErrorToast = (optionsOrMessage: Omit<ToastOptions, 'type'> | string): ToastInstance => {\n return addToast(normalizeOptions(optionsOrMessage, 'error'));\n};\n\nconst createInfoToast = (optionsOrMessage: Omit<ToastOptions, 'type'> | string): ToastInstance => {\n return addToast(normalizeOptions(optionsOrMessage, 'info'));\n};\n\nconst createWarningToast = (\n optionsOrMessage: Omit<ToastOptions, 'type'> | string,\n): ToastInstance => {\n return addToast(normalizeOptions(optionsOrMessage, 'warning'));\n};\n\nconst createLoadingToast = (\n optionsOrMessage: Omit<ToastOptions, 'type'> | string,\n): ToastInstance => {\n const options = normalizeOptions(optionsOrMessage, 'loading');\n // Loading toasts don't auto-dismiss by default\n return addToast({ duration: 0, ...options });\n};\n\nasync function promiseToast<T>(promise: Promise<T>, options: ToastPromiseOptions<T>): Promise<T> {\n const loadingOptions =\n typeof options.loading === 'string'\n ? { description: options.loading }\n : (options.loading as ToastOptions);\n\n const loadingToast = addToast({\n closable: false,\n duration: 0,\n type: 'loading',\n ...loadingOptions,\n });\n\n try {\n const result = await promise;\n\n loadingToast.close();\n\n const successOptions = (() => {\n if (typeof options.success === 'string') {\n return { description: options.success };\n }\n if (typeof options.success === 'function') {\n return { description: options.success(result) };\n }\n return options.success as ToastOptions;\n })();\n\n addToast({ type: 'success', ...successOptions });\n\n return result;\n } catch (error) {\n loadingToast.close();\n\n const errorOptions = (() => {\n if (typeof options.error === 'string') {\n return { description: options.error };\n }\n if (typeof options.error === 'function') {\n return { description: options.error(error as Error) };\n }\n return options.error as ToastOptions;\n })();\n\n addToast({ type: 'error', ...errorOptions });\n\n throw error;\n }\n}\n\n// Base toast function\nconst baseToast = (options: ToastOptions): ToastInstance => {\n return addToast({ type: 'default', ...options });\n};\n\n// Toast API\nexport const toast: ToastAPI = Object.assign(baseToast, {\n dismiss: dismissToast,\n error: createErrorToast,\n info: createInfoToast,\n loading: createLoadingToast,\n promise: promiseToast,\n success: createSuccessToast,\n warning: createWarningToast,\n});\n\n// Toast List Component\nconst ToastList = memo(() => {\n const { toasts } = BaseToast.useToastManager();\n return toasts.map((t) => <ToastItem key={t.id} toast={t} />);\n});\n\nToastList.displayName = 'ToastList';\n\n// Toast Host Component\nconst TOAST_PORTAL_ATTR = 'data-lobe-ui-toast-portal';\nexport const TOAST_CONTAINER_ATTR = 'data-lobe-ui-toast-container';\n\nconst containerMap = new WeakMap<object, HTMLElement>();\n\nconst getOrCreateContainer = (root: HTMLElement | ShadowRoot): HTMLElement => {\n const resolvedRoot = (() => {\n if (typeof document === 'undefined') return root;\n if (typeof ShadowRoot !== 'undefined' && root instanceof ShadowRoot) return root;\n\n const isBody = root === document.body;\n if (!isBody) return root;\n\n const themeApp = document.querySelector<HTMLElement>(`#${LOBE_THEME_APP_ID}`);\n if (themeApp) return themeApp;\n\n const toastContainer = document.querySelector<HTMLElement>(`[${TOAST_CONTAINER_ATTR}=\"true\"]`);\n if (toastContainer) return toastContainer;\n\n return root;\n })();\n\n const cached = containerMap.get(resolvedRoot);\n if (cached && cached.isConnected) return cached;\n\n const el = document.createElement('div');\n el.setAttribute(TOAST_PORTAL_ATTR, 'true');\n resolvedRoot.append(el);\n containerMap.set(resolvedRoot, el);\n return el;\n};\n\nconst resolveRoot = (root?: HTMLElement | ShadowRoot | null): HTMLElement | ShadowRoot | null => {\n if (root) return root;\n return document.body;\n};\n\nexport interface ToastHostProps {\n className?: string;\n /**\n * Default duration for toasts\n * @default 5000\n */\n duration?: number;\n /**\n * Maximum number of toasts\n * @default 5\n */\n limit?: number;\n /**\n * Toast position\n * @default 'bottom-right'\n */\n position?: ToastPosition;\n /**\n * Root element for portal\n */\n root?: HTMLElement | ShadowRoot | null;\n /**\n * Swipe direction to dismiss\n * @default ['down', 'right']\n */\n swipeDirection?: ('left' | 'right' | 'up' | 'down') | ('left' | 'right' | 'up' | 'down')[];\n}\n\nexport const ToastHost = memo(\n ({\n root,\n className,\n duration = 5000,\n limit = 5,\n position = 'bottom-right',\n swipeDirection = ['down', 'right'],\n }: ToastHostProps) => {\n const isClient = useIsClient();\n const [container, setContainer] = useState<HTMLElement | null>(null);\n\n useEffect(() => {\n globalState = {\n duration,\n limit,\n position,\n swipeDirection,\n };\n }, [duration, limit, position, swipeDirection]);\n\n useEffect(() => {\n if (!isClient) return;\n\n const resolved = resolveRoot(root);\n if (resolved) {\n setContainer(getOrCreateContainer(resolved));\n }\n\n const scope = root ?? document.body;\n return registerDevSingleton('ToastHost', scope);\n }, [isClient, root]);\n\n if (!isClient || !container) return null;\n\n return createPortal(\n <>\n {ALL_POSITIONS.map((pos) => (\n <ToastContext.Provider key={pos} value={{ position: pos, swipeDirection }}>\n <BaseToast.Provider limit={limit} timeout={duration} toastManager={getManager(pos)}>\n <BaseToast.Portal container={container}>\n <BaseToast.Viewport className={cx(viewportVariants({ position: pos }), className)}>\n <ToastList />\n </BaseToast.Viewport>\n </BaseToast.Portal>\n </BaseToast.Provider>\n </ToastContext.Provider>\n ))}\n </>,\n container,\n );\n },\n);\n\nToastHost.displayName = 'ToastHost';\n\n// Hook to use toast manager\nexport const useToast = () => toast;\n"],"mappings":"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"}