@funstack/ui 0.1.0-alpha.1 → 0.1.0-alpha.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/accordion/accordion-context.mjs.map +1 -1
- package/dist/accordion/accordion-item.mjs +18 -4
- package/dist/accordion/accordion-item.mjs.map +1 -1
- package/dist/accordion/accordion-panel.mjs +2 -3
- package/dist/accordion/accordion-panel.mjs.map +1 -1
- package/dist/accordion/accordion-root.d.mts.map +1 -1
- package/dist/accordion/accordion-root.mjs +32 -14
- package/dist/accordion/accordion-root.mjs.map +1 -1
- package/dist/accordion/accordion-trigger.mjs +4 -5
- package/dist/accordion/accordion-trigger.mjs.map +1 -1
- package/dist/checkbox/checkbox.d.mts +5 -3
- package/dist/checkbox/checkbox.d.mts.map +1 -1
- package/dist/checkbox/checkbox.mjs +25 -9
- package/dist/checkbox/checkbox.mjs.map +1 -1
- package/dist/checkbox-group/checkbox-group-context.mjs.map +1 -1
- package/dist/checkbox-group/checkbox-group-item.d.mts +2 -1
- package/dist/checkbox-group/checkbox-group-item.d.mts.map +1 -1
- package/dist/checkbox-group/checkbox-group-item.mjs +3 -1
- package/dist/checkbox-group/checkbox-group-item.mjs.map +1 -1
- package/dist/checkbox-group/checkbox-group-root.d.mts +5 -2
- package/dist/checkbox-group/checkbox-group-root.d.mts.map +1 -1
- package/dist/checkbox-group/checkbox-group-root.mjs +24 -5
- package/dist/checkbox-group/checkbox-group-root.mjs.map +1 -1
- package/dist/combobox/combobox-clear.d.mts +34 -0
- package/dist/combobox/combobox-clear.d.mts.map +1 -0
- package/dist/combobox/combobox-clear.mjs +58 -0
- package/dist/combobox/combobox-clear.mjs.map +1 -0
- package/dist/combobox/combobox-context.mjs +23 -0
- package/dist/combobox/combobox-context.mjs.map +1 -0
- package/dist/combobox/combobox-group.d.mts +72 -0
- package/dist/combobox/combobox-group.d.mts.map +1 -0
- package/dist/combobox/combobox-group.mjs +104 -0
- package/dist/combobox/combobox-group.mjs.map +1 -0
- package/dist/combobox/combobox-input.d.mts +42 -0
- package/dist/combobox/combobox-input.d.mts.map +1 -0
- package/dist/combobox/combobox-input.mjs +102 -0
- package/dist/combobox/combobox-input.mjs.map +1 -0
- package/dist/combobox/combobox-label.d.mts +22 -0
- package/dist/combobox/combobox-label.d.mts.map +1 -0
- package/dist/combobox/combobox-label.mjs +32 -0
- package/dist/combobox/combobox-label.mjs.map +1 -0
- package/dist/combobox/combobox-list.d.mts +37 -0
- package/dist/combobox/combobox-list.d.mts.map +1 -0
- package/dist/combobox/combobox-list.mjs +70 -0
- package/dist/combobox/combobox-list.mjs.map +1 -0
- package/dist/combobox/combobox-option.d.mts +45 -0
- package/dist/combobox/combobox-option.d.mts.map +1 -0
- package/dist/combobox/combobox-option.mjs +88 -0
- package/dist/combobox/combobox-option.mjs.map +1 -0
- package/dist/combobox/combobox-popup.d.mts +49 -0
- package/dist/combobox/combobox-popup.d.mts.map +1 -0
- package/dist/combobox/combobox-popup.mjs +86 -0
- package/dist/combobox/combobox-popup.mjs.map +1 -0
- package/dist/combobox/combobox-root.d.mts +135 -0
- package/dist/combobox/combobox-root.d.mts.map +1 -0
- package/dist/combobox/combobox-root.mjs +523 -0
- package/dist/combobox/combobox-root.mjs.map +1 -0
- package/dist/combobox/combobox-trigger.d.mts +36 -0
- package/dist/combobox/combobox-trigger.d.mts.map +1 -0
- package/dist/combobox/combobox-trigger.mjs +92 -0
- package/dist/combobox/combobox-trigger.mjs.map +1 -0
- package/dist/combobox/index.d.mts +36 -0
- package/dist/combobox/index.d.mts.map +1 -0
- package/dist/combobox/index.mjs +36 -0
- package/dist/combobox/index.mjs.map +1 -0
- package/dist/dialog/dialog-close.d.mts +17 -10
- package/dist/dialog/dialog-close.d.mts.map +1 -1
- package/dist/dialog/dialog-close.mjs +31 -20
- package/dist/dialog/dialog-close.mjs.map +1 -1
- package/dist/dialog/dialog-context.mjs.map +1 -1
- package/dist/dialog/dialog-form.d.mts +12 -5
- package/dist/dialog/dialog-form.d.mts.map +1 -1
- package/dist/dialog/dialog-form.mjs +16 -28
- package/dist/dialog/dialog-form.mjs.map +1 -1
- package/dist/dialog/dialog-popup.d.mts +10 -4
- package/dist/dialog/dialog-popup.d.mts.map +1 -1
- package/dist/dialog/dialog-popup.mjs +106 -41
- package/dist/dialog/dialog-popup.mjs.map +1 -1
- package/dist/dialog/dialog-root.d.mts +14 -8
- package/dist/dialog/dialog-root.d.mts.map +1 -1
- package/dist/dialog/dialog-root.mjs +66 -11
- package/dist/dialog/dialog-root.mjs.map +1 -1
- package/dist/dialog/dialog-trigger.d.mts +3 -2
- package/dist/dialog/dialog-trigger.d.mts.map +1 -1
- package/dist/dialog/dialog-trigger.mjs.map +1 -1
- package/dist/dialog/index.d.mts +4 -2
- package/dist/dialog/index.d.mts.map +1 -1
- package/dist/dialog/index.mjs +4 -2
- package/dist/dialog/index.mjs.map +1 -1
- package/dist/field/field-control.d.mts +33 -0
- package/dist/field/field-control.d.mts.map +1 -0
- package/dist/field/field-control.mjs +68 -0
- package/dist/field/field-control.mjs.map +1 -0
- package/dist/field/field-description.d.mts +24 -0
- package/dist/field/field-description.d.mts.map +1 -0
- package/dist/field/field-description.mjs +34 -0
- package/dist/field/field-description.mjs.map +1 -0
- package/dist/field/field-error.d.mts +30 -0
- package/dist/field/field-error.d.mts.map +1 -0
- package/dist/field/field-error.mjs +42 -0
- package/dist/field/field-error.mjs.map +1 -0
- package/dist/field/field-label.d.mts +25 -0
- package/dist/field/field-label.d.mts.map +1 -0
- package/dist/field/field-label.mjs +46 -0
- package/dist/field/field-label.mjs.map +1 -0
- package/dist/field/field-root.d.mts +53 -0
- package/dist/field/field-root.d.mts.map +1 -0
- package/dist/field/field-root.mjs +317 -0
- package/dist/field/field-root.mjs.map +1 -0
- package/dist/field/index.d.mts +18 -0
- package/dist/field/index.d.mts.map +1 -0
- package/dist/field/index.mjs +17 -0
- package/dist/field/index.mjs.map +1 -0
- package/dist/form/form-root.d.mts +58 -0
- package/dist/form/form-root.d.mts.map +1 -0
- package/dist/form/form-root.mjs +146 -0
- package/dist/form/form-root.mjs.map +1 -0
- package/dist/form/form-submit.d.mts +31 -0
- package/dist/form/form-submit.d.mts.map +1 -0
- package/dist/form/form-submit.mjs +52 -0
- package/dist/form/form-submit.mjs.map +1 -0
- package/dist/form/index.d.mts +11 -0
- package/dist/form/index.d.mts.map +1 -0
- package/dist/form/index.mjs +11 -0
- package/dist/form/index.mjs.map +1 -0
- package/dist/index.d.mts +46 -2
- package/dist/index.mjs +8 -1
- package/dist/internal/content-focus-probe.mjs +42 -0
- package/dist/internal/content-focus-probe.mjs.map +1 -0
- package/dist/internal/field-context.d.mts +19 -0
- package/dist/internal/field-context.d.mts.map +1 -0
- package/dist/internal/field-context.mjs +14 -0
- package/dist/internal/field-context.mjs.map +1 -0
- package/dist/internal/focus-first-invalid.mjs +67 -0
- package/dist/internal/focus-first-invalid.mjs.map +1 -0
- package/dist/internal/form-status-watcher.mjs +31 -0
- package/dist/internal/form-status-watcher.mjs.map +1 -0
- package/dist/internal/modal-registry.mjs +39 -0
- package/dist/internal/modal-registry.mjs.map +1 -0
- package/dist/internal/platform-attributes.d.mts +12 -1
- package/dist/internal/platform-attributes.d.mts.map +1 -1
- package/dist/internal/types.d.mts +11 -1
- package/dist/internal/types.d.mts.map +1 -1
- package/dist/internal/use-controllable-action.mjs +35 -13
- package/dist/internal/use-controllable-action.mjs.map +1 -1
- package/dist/internal/use-field-control.mjs +107 -0
- package/dist/internal/use-field-control.mjs.map +1 -0
- package/dist/internal/use-form-owner-event.mjs +44 -0
- package/dist/internal/use-form-owner-event.mjs.map +1 -0
- package/dist/internal/use-form-reset.mjs +12 -31
- package/dist/internal/use-form-reset.mjs.map +1 -1
- package/dist/internal/use-hydrated-mount.mjs +21 -0
- package/dist/internal/use-hydrated-mount.mjs.map +1 -0
- package/dist/internal/use-latest.mjs +32 -0
- package/dist/internal/use-latest.mjs.map +1 -0
- package/dist/internal/use-modal-registration.mjs +47 -0
- package/dist/internal/use-modal-registration.mjs.map +1 -0
- package/dist/internal/use-native-group-reconciliation.mjs +6 -5
- package/dist/internal/use-native-group-reconciliation.mjs.map +1 -1
- package/dist/internal/use-native-select-value.mjs +184 -0
- package/dist/internal/use-native-select-value.mjs.map +1 -0
- package/dist/internal/use-popover-popup.mjs +8 -22
- package/dist/internal/use-popover-popup.mjs.map +1 -1
- package/dist/internal/use-popover-root.mjs +58 -23
- package/dist/internal/use-popover-root.mjs.map +1 -1
- package/dist/internal/validity.d.mts +24 -0
- package/dist/internal/validity.d.mts.map +1 -0
- package/dist/internal/validity.mjs +108 -0
- package/dist/internal/validity.mjs.map +1 -0
- package/dist/menu/menu-group.d.mts.map +1 -1
- package/dist/menu/menu-group.mjs +3 -2
- package/dist/menu/menu-group.mjs.map +1 -1
- package/dist/menu/menu-radio-group.mjs +9 -4
- package/dist/menu/menu-radio-group.mjs.map +1 -1
- package/dist/menu/menu-root.mjs.map +1 -1
- package/dist/radio-group/radio-group-root.d.mts +4 -2
- package/dist/radio-group/radio-group-root.d.mts.map +1 -1
- package/dist/radio-group/radio-group-root.mjs +34 -8
- package/dist/radio-group/radio-group-root.mjs.map +1 -1
- package/dist/select/index.d.mts +30 -0
- package/dist/select/index.d.mts.map +1 -0
- package/dist/select/index.mjs +30 -0
- package/dist/select/index.mjs.map +1 -0
- package/dist/select/select-context.mjs +13 -0
- package/dist/select/select-context.mjs.map +1 -0
- package/dist/select/select-dom.mjs +44 -0
- package/dist/select/select-dom.mjs.map +1 -0
- package/dist/select/select-group-label.d.mts +22 -0
- package/dist/select/select-group-label.d.mts.map +1 -0
- package/dist/select/select-group-label.mjs +39 -0
- package/dist/select/select-group-label.mjs.map +1 -0
- package/dist/select/select-group.d.mts +28 -0
- package/dist/select/select-group.d.mts.map +1 -0
- package/dist/select/select-group.mjs +47 -0
- package/dist/select/select-group.mjs.map +1 -0
- package/dist/select/select-option.d.mts +42 -0
- package/dist/select/select-option.d.mts.map +1 -0
- package/dist/select/select-option.mjs +65 -0
- package/dist/select/select-option.mjs.map +1 -0
- package/dist/select/select-root.d.mts +68 -0
- package/dist/select/select-root.d.mts.map +1 -0
- package/dist/select/select-root.mjs +114 -0
- package/dist/select/select-root.mjs.map +1 -0
- package/dist/select/select-separator.d.mts +20 -0
- package/dist/select/select-separator.d.mts.map +1 -0
- package/dist/select/select-separator.mjs +30 -0
- package/dist/select/select-separator.mjs.map +1 -0
- package/dist/select/select-trigger.d.mts +29 -0
- package/dist/select/select-trigger.d.mts.map +1 -0
- package/dist/select/select-trigger.mjs +48 -0
- package/dist/select/select-trigger.mjs.map +1 -0
- package/dist/select/select-value.d.mts +31 -0
- package/dist/select/select-value.d.mts.map +1 -0
- package/dist/select/select-value.mjs +101 -0
- package/dist/select/select-value.mjs.map +1 -0
- package/dist/slider/index.d.mts +32 -0
- package/dist/slider/index.d.mts.map +1 -0
- package/dist/slider/index.mjs +27 -0
- package/dist/slider/index.mjs.map +1 -0
- package/dist/slider/slider-arithmetic.mjs +120 -0
- package/dist/slider/slider-arithmetic.mjs.map +1 -0
- package/dist/slider/slider-context.mjs +13 -0
- package/dist/slider/slider-context.mjs.map +1 -0
- package/dist/slider/slider-indicator.d.mts +22 -0
- package/dist/slider/slider-indicator.d.mts.map +1 -0
- package/dist/slider/slider-indicator.mjs +36 -0
- package/dist/slider/slider-indicator.mjs.map +1 -0
- package/dist/slider/slider-label.d.mts +23 -0
- package/dist/slider/slider-label.d.mts.map +1 -0
- package/dist/slider/slider-label.mjs +44 -0
- package/dist/slider/slider-label.mjs.map +1 -0
- package/dist/slider/slider-root.d.mts +111 -0
- package/dist/slider/slider-root.d.mts.map +1 -0
- package/dist/slider/slider-root.mjs +582 -0
- package/dist/slider/slider-root.mjs.map +1 -0
- package/dist/slider/slider-thumb.d.mts +58 -0
- package/dist/slider/slider-thumb.d.mts.map +1 -0
- package/dist/slider/slider-thumb.mjs +145 -0
- package/dist/slider/slider-thumb.mjs.map +1 -0
- package/dist/slider/slider-track.d.mts +29 -0
- package/dist/slider/slider-track.d.mts.map +1 -0
- package/dist/slider/slider-track.mjs +54 -0
- package/dist/slider/slider-track.mjs.map +1 -0
- package/dist/slider/slider-value.d.mts +34 -0
- package/dist/slider/slider-value.d.mts.map +1 -0
- package/dist/slider/slider-value.mjs +38 -0
- package/dist/slider/slider-value.mjs.map +1 -0
- package/dist/switch/switch.d.mts +5 -3
- package/dist/switch/switch.d.mts.map +1 -1
- package/dist/switch/switch.mjs +24 -8
- package/dist/switch/switch.mjs.map +1 -1
- package/dist/tabs/tabs-root.mjs +13 -3
- package/dist/tabs/tabs-root.mjs.map +1 -1
- package/dist/toast/index.d.mts +25 -0
- package/dist/toast/index.d.mts.map +1 -0
- package/dist/toast/index.mjs +26 -0
- package/dist/toast/index.mjs.map +1 -0
- package/dist/toast/toast-action.d.mts +39 -0
- package/dist/toast/toast-action.d.mts.map +1 -0
- package/dist/toast/toast-action.mjs +64 -0
- package/dist/toast/toast-action.mjs.map +1 -0
- package/dist/toast/toast-close.d.mts +25 -0
- package/dist/toast/toast-close.d.mts.map +1 -0
- package/dist/toast/toast-close.mjs +46 -0
- package/dist/toast/toast-close.mjs.map +1 -0
- package/dist/toast/toast-context.mjs +19 -0
- package/dist/toast/toast-context.mjs.map +1 -0
- package/dist/toast/toast-description.d.mts +24 -0
- package/dist/toast/toast-description.d.mts.map +1 -0
- package/dist/toast/toast-description.mjs +35 -0
- package/dist/toast/toast-description.mjs.map +1 -0
- package/dist/toast/toast-root.d.mts +43 -0
- package/dist/toast/toast-root.d.mts.map +1 -0
- package/dist/toast/toast-root.mjs +204 -0
- package/dist/toast/toast-root.mjs.map +1 -0
- package/dist/toast/toast-title.d.mts +24 -0
- package/dist/toast/toast-title.d.mts.map +1 -0
- package/dist/toast/toast-title.mjs +33 -0
- package/dist/toast/toast-title.mjs.map +1 -0
- package/dist/toast/toast-viewport.d.mts +52 -0
- package/dist/toast/toast-viewport.d.mts.map +1 -0
- package/dist/toast/toast-viewport.mjs +621 -0
- package/dist/toast/toast-viewport.mjs.map +1 -0
- package/dist/toast/toast.d.mts +119 -0
- package/dist/toast/toast.d.mts.map +1 -0
- package/dist/toast/toast.mjs +153 -0
- package/dist/toast/toast.mjs.map +1 -0
- package/package.json +7 -1
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"toast-viewport.mjs","names":["defaultManager"],"sources":["../../src/toast/toast-viewport.tsx"],"sourcesContent":["\"use client\";\nimport {\n use,\n useCallback,\n useEffect,\n useLayoutEffect,\n useMemo,\n useRef,\n useState,\n useSyncExternalStore,\n useTransition,\n} from \"react\";\nimport type { ComponentPropsWithRef, CSSProperties, FocusEvent, ReactNode } from \"react\";\nimport { createPortal } from \"react-dom\";\nimport { DialogContext } from \"../dialog/dialog-context.js\";\nimport { createActionDetails } from \"../internal/action-details.js\";\nimport { dataAttr } from \"../internal/data-attr.js\";\nimport {\n getServerTopmostModal,\n getTopmostModal,\n subscribeModals,\n} from \"../internal/modal-registry.js\";\nimport type { ChangeReason, RenderProp, StateValue } from \"../internal/types.js\";\nimport { useDevWarning } from \"../internal/use-dev-warning.js\";\nimport { useLatest } from \"../internal/use-latest.js\";\nimport { useRender } from \"../internal/use-render.js\";\nimport type { DataAttributes } from \"../internal/use-render.js\";\nimport { toast as defaultManager, toastManagerInternals } from \"./toast.js\";\nimport type {\n RetainedToast,\n ToastCommand,\n ToastItem,\n ToastListener,\n ToastManager,\n ToastOptions,\n ToastPriority,\n} from \"./toast.js\";\nimport { ToastViewportContext } from \"./toast-context.js\";\nimport type { ToastViewportContextValue } from \"./toast-context.js\";\n\nexport interface ToastViewportState {\n /** Rendered toasts, chronological (oldest first), exiting ones included. */\n readonly toasts: readonly ToastItem[];\n /** Toasts waiting beyond `limit`. */\n readonly queued: number;\n /** A viewport-wide pause holds (pointer over the region, focus inside, hidden document, blurred window). */\n readonly paused: boolean;\n}\n\nexport interface ToastViewportProps extends Omit<\n ComponentPropsWithRef<\"section\">,\n | \"popover\"\n | \"className\"\n | \"style\"\n | \"children\"\n | \"onPointerEnter\"\n | \"onPointerLeave\"\n | \"onFocus\"\n | \"onBlur\"\n | \"onKeyDown\"\n> {\n /** The queue to render. Default: the module-level `toast`. */\n manager?: ToastManager;\n /** Toasts rendered at once; further ones wait in order (never dropped). Default `3`. */\n limit?: number;\n /** Default `duration` of a toast in milliseconds; `null` for persistent. Default `5000`. */\n duration?: number | null;\n /**\n * Keys that focus the newest toast: `event.code` values and modifier names (`\"altKey\"`,\n * `\"ctrlKey\"`, `\"metaKey\"`, `\"shiftKey\"`), all of which must match, with no other modifier\n * held. Default `[\"F8\"]`; `null` installs no listener.\n */\n hotkey?: readonly string[] | null;\n /**\n * The text `document.ariaNotify()` speaks for a toast (localisation); `null` announces nothing.\n * A toast's own `announce` wins. Default: the text of its Title and Description.\n */\n announce?: (toast: ToastItem) => string | null;\n /** Usually a function of the state mapping `state.toasts` to `Toast.Root`s. */\n children?: StateValue<ReactNode, ToastViewportState>;\n className?: StateValue<string | undefined, ToastViewportState>;\n style?: StateValue<CSSProperties | undefined, ToastViewportState>;\n /** Must render an element that can carry `popover` (a `<section>` by default). */\n render?: RenderProp<ComponentPropsWithRef<\"section\">, ToastViewportState>;\n}\n\nconst isDev = process.env.NODE_ENV !== \"production\";\n\nconst DEFAULT_HOTKEY: readonly string[] = [\"F8\"];\n\nconst MODIFIER_NAMES: Readonly<Record<string, string>> = {\n altKey: \"Alt\",\n ctrlKey: \"Ctrl\",\n metaKey: \"Meta\",\n shiftKey: \"Shift\",\n};\n\n/** `[\"altKey\", \"KeyT\"]` → `\"Alt+T\"`, `[\"F8\"]` → `\"F8\"`. */\nfunction hotkeyLabel(hotkey: readonly string[]): string {\n return hotkey\n .map((key) => MODIFIER_NAMES[key] ?? key.replace(/^Key(?=[A-Z]$)/, \"\").replace(/^Digit/, \"\"))\n .join(\"+\");\n}\n\n/** The region's built-in accessible name (conventions 11): `\"Notifications (F8)\"`. */\nfunction regionLabel(hotkey: readonly string[] | null): string {\n if (hotkey === null || hotkey.length === 0) return \"Notifications\";\n return `Notifications (${hotkeyLabel(hotkey)})`;\n}\n\n/** The listed keys and modifiers, and no unlisted modifier (Ctrl+F8 is not F8). */\nfunction matchesHotkey(hotkey: readonly string[], event: KeyboardEvent): boolean {\n const flags = event as unknown as Record<string, unknown>;\n for (const modifier of Object.keys(MODIFIER_NAMES)) {\n if ((flags[modifier] === true) !== hotkey.includes(modifier)) return false;\n }\n return hotkey.every((key) => key in MODIFIER_NAMES || event.code === key);\n}\n\n/** No store: `useSyncExternalStore` only tells the hydration render from a client render. */\nconst subscribeNothing = (): (() => void) => () => {};\nconst onClient = (): boolean => true;\nconst onServerOrHydrating = (): boolean => false;\n\n/**\n * The element the region is portalled into for its whole client life. The library moves this\n * element (between its inline anchor and the topmost modal dialog); React never sees the move, so\n * the toasts keep their state (an Undo in flight stays pending). `display: contents`: no box.\n */\nfunction createHost(): HTMLElement | null {\n if (typeof document === \"undefined\") return null;\n const host = document.createElement(\"div\");\n host.style.display = \"contents\";\n return host;\n}\n\n/** One queue entry: the toast's options and the flags its `ToastItem` is built from. */\ninterface Entry {\n readonly options: ToastOptions & { readonly id: string };\n readonly promisePending: boolean;\n readonly actionPending: boolean;\n readonly ending: boolean;\n /** The public item, rebuilt whenever any of the above changes (stable identity otherwise). */\n readonly item: ToastItem;\n}\n\nfunction makeEntry(\n options: ToastOptions & { readonly id: string },\n flags: { promisePending: boolean; actionPending: boolean; ending: boolean },\n): Entry {\n return {\n options,\n ...flags,\n item: {\n ...options,\n id: options.id,\n pending: flags.promisePending || flags.actionPending,\n ending: flags.ending,\n },\n };\n}\n\nfunction withFlags(\n entry: Entry,\n flags: Partial<{ promisePending: boolean; actionPending: boolean; ending: boolean }>,\n): Entry {\n return makeEntry(entry.options, {\n promisePending: flags.promisePending ?? entry.promisePending,\n actionPending: flags.actionPending ?? entry.actionPending,\n ending: flags.ending ?? entry.ending,\n });\n}\n\n/** Rendered entries (the first `limit` open ones plus every ending one) and the queued count. */\nfunction partition(\n entries: readonly Entry[],\n limit: number,\n): { visible: Entry[]; queued: Entry[] } {\n const visible: Entry[] = [];\n const queued: Entry[] = [];\n let open = 0;\n for (const entry of entries) {\n if (entry.ending) visible.push(entry);\n else if (open < limit) {\n visible.push(entry);\n open++;\n } else queued.push(entry);\n }\n return { visible, queued };\n}\n\n/** Plain text of a `ReactNode` that is text (queued toasts have no DOM to read yet). */\nfunction plainText(node: unknown): string {\n if (typeof node === \"string\") return node;\n if (typeof node === \"number\" || typeof node === \"bigint\") return String(node);\n if (Array.isArray(node)) return node.map(plainText).join(\"\");\n return \"\";\n}\n\n/** Document order. */\nfunction byDocumentPosition(a: Element, b: Element): number {\n if (a === b) return 0;\n return a.compareDocumentPosition(b) & Node.DOCUMENT_POSITION_FOLLOWING ? -1 : 1;\n}\n\ninterface Timer {\n /** The duration the timer was started with; a different effective duration restarts it. */\n readonly duration: number;\n remaining: number;\n startedAt: number;\n handle: ReturnType<typeof setTimeout> | null;\n}\n\ninterface PauseSources {\n hover: boolean;\n focus: boolean;\n hidden: boolean;\n blurred: boolean;\n}\n\nfunction anyPause(sources: PauseSources): boolean {\n return sources.hover || sources.focus || sources.hidden || sources.blurred;\n}\n\ntype DocumentWithAriaNotify = Document & {\n ariaNotify?: (announcement: string, options?: { priority?: ToastPriority }) => void;\n};\n\n/**\n * The toast region: a `<section popover=\"manual\">` named \"Notifications (F8)\", shown in the top\n * layer while it has toasts. Owns the queue (urgent state fed by `toast()`), the timers and their\n * pausing, the hotkey, focus return, announcements through `document.ariaNotify()`, and the move\n * into the topmost open modal `Dialog` (a region outside it would be inert).\n *\n * Owned keys (conventions 1.2): `popover`, `aria-label` (the consumer's own `aria-label` wins\n * verbatim), `data-paused`, `data-empty`, and the pointer / focus handlers.\n */\nexport function ToastViewport({\n manager = defaultManager,\n limit = 3,\n duration = 5000,\n hotkey = DEFAULT_HOTKEY,\n announce,\n children,\n className,\n style,\n render,\n ...rest\n}: ToastViewportProps) {\n // --- The queue: urgent state, written only from handlers, microtasks and effects -----------\n const [entries, setEntries] = useState<readonly Entry[]>([]);\n // The latest queue, written together with every `setEntries`: the dispatchers read and write\n // it synchronously so a burst of commands composes (and side effects stay out of updaters).\n const entriesRef = useRef<readonly Entry[]>(entries);\n const listener = useRef<ToastListener | null>(null);\n listener.current ??= {\n restore: (toasts: readonly RetainedToast[]) => handlers.current.restore(toasts),\n apply: (commands: readonly ToastCommand[]) => handlers.current.apply(commands),\n retain: () => handlers.current.retain(),\n };\n // A second Viewport on a taken manager renders nothing from its first render on (the effect\n // below confirms or clears it once the subscription is attempted).\n const [rejected, setRejected] = useState(() =>\n toastManagerInternals(manager).isTaken(listener.current!),\n );\n const [, startCloseTransition] = useTransition();\n\n const regionRef = useRef<HTMLElement | null>(null);\n const roots = useRef(new Map<string, HTMLElement>());\n const suspended = useRef(new Set<string>());\n const timers = useRef(new Map<string, Timer>());\n const pauses = useRef<PauseSources>({\n hover: false,\n focus: false,\n hidden: false,\n blurred: false,\n });\n const [paused, setPaused] = useState(false);\n /** Where focus goes back to once the last toast holding it leaves (design 7). */\n const returnTarget = useRef<HTMLElement | null>(null);\n const lastAnnounced = useRef(new Map<string, string | null>());\n /** Per closing toast: the animations already running before `data-ending` applied. */\n const exitBaselines = useRef(new Map<string, ReadonlySet<Animation>>());\n const announceBatch = useRef<{ text: string; priority: ToastPriority }[]>([]);\n const addedSinceEffect = useRef(false);\n const mounted = useRef(false);\n\n const latest = useLatest({ limit, duration, hotkey, announce });\n\n const container = useSyncExternalStore(subscribeModals, getTopmostModal, getServerTopmostModal);\n\n const write = (next: readonly Entry[]): void => {\n entriesRef.current = next;\n setEntries(next);\n };\n\n // --- Timers (design 6) ----------------------------------------------------------------------\n\n const effectiveDuration = (entry: Entry): number | null => {\n const own = entry.options.duration;\n if (own !== undefined) return own;\n return entry.options.action !== undefined ? null : latest.current.duration;\n };\n\n const syncTimers = (): void => {\n const map = timers.current;\n const { visible } = partition(entriesRef.current, latest.current.limit);\n const now = Date.now();\n const live = new Set<string>();\n const halted = !mounted.current || anyPause(pauses.current);\n for (const entry of visible) {\n if (entry.ending) continue;\n const id = entry.options.id;\n const ms = effectiveDuration(entry);\n if (ms === null) continue;\n live.add(id);\n let timer = map.get(id);\n if (timer === undefined || timer.duration !== ms) {\n if (timer?.handle != null) clearTimeout(timer.handle);\n timer = { duration: ms, remaining: ms, startedAt: 0, handle: null };\n map.set(id, timer);\n }\n const run = !halted && !entry.item.pending && !suspended.current.has(id);\n if (run && timer.handle === null) {\n const current = timer;\n current.startedAt = now;\n current.handle = setTimeout(\n () => {\n current.handle = null;\n current.remaining = 0;\n close([id], \"timeout\", undefined);\n },\n Math.max(0, current.remaining),\n );\n } else if (!run && timer.handle !== null) {\n clearTimeout(timer.handle);\n timer.handle = null;\n timer.remaining -= now - timer.startedAt;\n }\n }\n for (const [id, timer] of map) {\n if (live.has(id)) continue;\n if (timer.handle !== null) clearTimeout(timer.handle);\n map.delete(id);\n }\n };\n\n const setPause = (source: keyof PauseSources, value: boolean): void => {\n if (pauses.current[source] === value) return;\n pauses.current[source] = value;\n // Applied synchronously in the event, before any timer callback can run (design 14 item 9).\n syncTimers();\n setPaused(anyPause(pauses.current));\n };\n\n // --- Focus ----------------------------------------------------------------------------------\n\n /** Open toasts' Root elements in document order. */\n const sortedRoots = (exclude?: ReadonlySet<string>): { id: string; element: HTMLElement }[] => {\n const open = new Set(entriesRef.current.filter((e) => !e.ending).map((e) => e.options.id));\n const list: { id: string; element: HTMLElement }[] = [];\n for (const [id, element] of roots.current) {\n if (!open.has(id) || exclude?.has(id) || !element.isConnected) continue;\n list.push({ id, element });\n }\n return list.sort((a, b) => byDocumentPosition(a.element, b.element));\n };\n\n /**\n * Before toasts close: if focus is inside one of them, move it to the next toast, else the\n * previous one, else where it was before the region was entered, else the body (design 7).\n */\n const moveFocusOut = (closing: ReadonlySet<string>): void => {\n const doc = regionRef.current?.ownerDocument ?? document;\n const active = doc.activeElement;\n if (!(active instanceof HTMLElement)) return;\n const all = sortedRoots();\n const index = all.findIndex(({ element }) => element.contains(active));\n if (index === -1 || !closing.has(all[index]!.id)) return;\n const after = all.slice(index + 1).find(({ id }) => !closing.has(id));\n const before = all\n .slice(0, index)\n .reverse()\n .find(({ id }) => !closing.has(id));\n const target = after ?? before;\n if (target !== undefined) {\n target.element.focus();\n return;\n }\n const stored = returnTarget.current;\n if (stored !== null && stored.isConnected && !regionRef.current?.contains(stored)) {\n stored.focus();\n if (doc.activeElement === stored) return;\n }\n active.blur();\n };\n\n // --- Queue writes ---------------------------------------------------------------------------\n\n /**\n * Records the animations running in each closing toast before `data-ending` applies: the exit\n * waits only for the ones the ending state started, not for a running progress bar.\n */\n const snapshotExitBaselines = (ids: Iterable<string>): void => {\n for (const id of ids) {\n const element = roots.current.get(id);\n if (element === undefined || !element.isConnected) continue;\n if (typeof element.getAnimations !== \"function\") continue;\n exitBaselines.current.set(id, new Set(element.getAnimations({ subtree: true })));\n }\n };\n\n const clearTimer = (id: string): void => {\n const timer = timers.current.get(id);\n if (timer?.handle != null) clearTimeout(timer.handle);\n timers.current.delete(id);\n };\n\n const scheduleCloseActions = (\n closed: readonly Entry[],\n reason: ChangeReason,\n event: Event | undefined,\n ): void => {\n for (const entry of closed) {\n const closeAction = entry.options.closeAction;\n if (closeAction === undefined) continue;\n const details = createActionDetails(event, { id: entry.options.id, reason });\n // Not in the closing handler itself: the toast is gone first, then the action runs in the\n // Viewport's own transition (its rejection reaches the boundary above the Viewport).\n queueMicrotask(() => {\n if (!mounted.current) return;\n startCloseTransition(async () => {\n await closeAction(details);\n });\n });\n }\n };\n\n const close = (ids: readonly string[], reason: ChangeReason, event: Event | undefined): void => {\n const wanted = new Set(ids);\n const closing = entriesRef.current.filter((e) => !e.ending && wanted.has(e.options.id));\n if (closing.length === 0) return;\n const closingIds = new Set(closing.map((e) => e.options.id));\n moveFocusOut(closingIds);\n snapshotExitBaselines(closingIds);\n write(\n entriesRef.current.map((e) =>\n closingIds.has(e.options.id) ? withFlags(e, { ending: true }) : e,\n ),\n );\n syncTimers();\n scheduleCloseActions(closing, reason, event);\n };\n\n const drop = (id: string): void => {\n const next = entriesRef.current.filter((e) => !(e.ending && e.options.id === id));\n if (next.length === entriesRef.current.length) return;\n lastAnnounced.current.delete(id);\n exitBaselines.current.delete(id);\n write(next);\n };\n\n const apply = (commands: readonly ToastCommand[]): void => {\n const next = [...entriesRef.current];\n // Dismissals apply in order: each closes the toasts open at that point of the batch, never\n // one added after it in the same tick. Their side effects run after the loop.\n const dismissed: Entry[] = [];\n for (const command of commands) {\n if (command.kind === \"dismiss\") {\n for (let i = 0; i < next.length; i++) {\n const entry = next[i]!;\n if (entry.ending) continue;\n if (command.id !== undefined && entry.options.id !== command.id) continue;\n dismissed.push(entry);\n next[i] = withFlags(entry, { ending: true });\n }\n continue;\n }\n const index = next.findIndex((e) => e.options.id === command.id);\n const existing = index === -1 ? undefined : next[index]!;\n if (command.kind === \"add\") {\n const options = { ...command.options, id: command.id };\n if (existing !== undefined && !existing.ending) {\n // An explicit existing id is an update (\"one toast per key\").\n next[index] = makeEntry(\n { ...existing.options, ...options },\n {\n promisePending: command.pending || existing.promisePending,\n actionPending: existing.actionPending,\n ending: false,\n },\n );\n continue;\n }\n if (existing !== undefined) {\n // An ending toast replaced by a new one with its id (the keyed Root may be reused):\n // a fresh timer, a fresh announcement, no exit.\n next.splice(index, 1);\n clearTimer(command.id);\n lastAnnounced.current.delete(command.id);\n exitBaselines.current.delete(command.id);\n }\n next.push(\n makeEntry(options, {\n promisePending: command.pending,\n actionPending: false,\n ending: false,\n }),\n );\n addedSinceEffect.current = true;\n } else {\n if (existing === undefined) {\n if (isDev && !command.settle) {\n console.error(`toast.update: no toast with id \"${command.id}\"; the update is ignored.`);\n }\n continue;\n }\n if (existing.ending) continue;\n const options = command.settle\n ? { ...command.options, id: command.id }\n : { ...existing.options, ...command.options, id: command.id };\n next[index] = makeEntry(options, {\n promisePending: command.settle ? false : existing.promisePending,\n actionPending: existing.actionPending,\n ending: false,\n });\n }\n }\n if (dismissed.length === 0) {\n write(next);\n return;\n }\n // Toasts that leave the DOM: not one replaced by a later add with its id in this batch.\n const stays = new Set(next.filter((e) => !e.ending).map((e) => e.options.id));\n const leaving = new Set(dismissed.map((e) => e.options.id).filter((id) => !stays.has(id)));\n moveFocusOut(leaving);\n snapshotExitBaselines(leaving);\n write(next);\n syncTimers();\n scheduleCloseActions(dismissed, \"dismiss\", undefined);\n };\n\n const setActionPending = (id: string, pending: boolean): void => {\n const index = entriesRef.current.findIndex((e) => e.options.id === id);\n if (index === -1) return;\n const entry = entriesRef.current[index]!;\n if (entry.actionPending === pending) return;\n const next = [...entriesRef.current];\n next[index] = withFlags(entry, { actionPending: pending });\n write(next);\n };\n\n // --- Announcements (design 5) ---------------------------------------------------------------\n\n const flushAnnouncements = (): void => {\n const batch = announceBatch.current;\n announceBatch.current = [];\n if (batch.length === 0) return;\n const doc = (regionRef.current?.ownerDocument ?? document) as DocumentWithAriaNotify;\n if (typeof doc.ariaNotify !== \"function\") return;\n const priority = batch.some((a) => a.priority === \"high\") ? \"high\" : \"normal\";\n doc.ariaNotify(batch.map((a) => a.text).join(\" \"), { priority });\n };\n\n const queueAnnouncement = (id: string, text: string | null, priority: ToastPriority): void => {\n const map = lastAnnounced.current;\n if (map.has(id) && map.get(id) === text) return;\n map.set(id, text);\n if (text === null || text.trim() === \"\") return;\n if (announceBatch.current.length === 0) queueMicrotask(flushAnnouncements);\n announceBatch.current.push({ text, priority });\n };\n\n // The callbacks below read only refs, so one context value serves the Viewport's whole life.\n const restore = (toasts: readonly RetainedToast[]): void => {\n if (entriesRef.current.length > 0) return;\n // Already announced by the previous Viewport: not again on every remount.\n for (const t of toasts) {\n if (t.announced !== undefined) lastAnnounced.current.set(t.options.id, t.announced);\n }\n write(\n toasts.map((t) =>\n makeEntry(t.options, { promisePending: t.pending, actionPending: false, ending: false }),\n ),\n );\n addedSinceEffect.current = true;\n };\n\n const retain = (): readonly RetainedToast[] => {\n const announced = lastAnnounced.current;\n return entriesRef.current\n .filter((e) => !e.ending)\n .map((e) => ({\n options: e.options,\n pending: e.promisePending,\n announced: announced.has(e.options.id) ? announced.get(e.options.id) : undefined,\n }));\n };\n\n const handlers = useLatest({\n apply,\n restore,\n retain,\n close,\n drop,\n setActionPending,\n queueAnnouncement,\n setPause,\n syncTimers,\n });\n\n const context = useMemo<ToastViewportContextValue>(\n () => ({\n close: (ids, reason, event) => handlers.current.close(ids, reason, event),\n drop: (id) => handlers.current.drop(id),\n setActionPending: (id, pending) => handlers.current.setActionPending(id, pending),\n setSuspended: (id, value) => {\n if (value) suspended.current.add(id);\n else suspended.current.delete(id);\n handlers.current.syncTimers();\n },\n registerRoot: (id, element) => {\n if (element === null) roots.current.delete(id);\n else roots.current.set(id, element);\n },\n focusSibling: (id, direction) => {\n const all = sortedRoots();\n const index = all.findIndex((r) => r.id === id);\n if (index === -1) return;\n all[index + direction]?.element.focus();\n },\n announce: (id, text, priority) => handlers.current.queueAnnouncement(id, text, priority),\n exitBaseline: (id) => exitBaselines.current.get(id),\n announcer: () => latest.current.announce,\n }),\n // oxlint-disable-next-line react-hooks/exhaustive-deps -- refs only (`sortedRoots` reads refs)\n [handlers, latest],\n );\n\n // --- Subscription ---------------------------------------------------------------------------\n\n useEffect(() => {\n const unsubscribe = toastManagerInternals(manager).subscribe(listener.current!);\n if (unsubscribe === null) {\n setRejected(true);\n if (isDev) {\n console.error(\n \"Toast.Viewport: another Toast.Viewport already renders this manager; this one renders \" +\n \"nothing. Use one Viewport per manager, or `createToastManager()` for a second queue.\",\n );\n }\n return;\n }\n setRejected(false);\n return unsubscribe;\n }, [manager]);\n\n // --- Lifecycle, pause sources, hotkey --------------------------------------------------------\n\n useEffect(() => {\n if (rejected) return;\n mounted.current = true;\n const doc = regionRef.current?.ownerDocument ?? document;\n const view = doc.defaultView ?? window;\n pauses.current.hidden = doc.visibilityState === \"hidden\";\n const onVisibility = (): void =>\n handlers.current.setPause(\"hidden\", doc.visibilityState === \"hidden\");\n // The window's own focus and blur; an element's (which do not bubble) never reach here.\n const onWindowBlur = (event: Event): void => {\n if (!(event.target instanceof Node)) handlers.current.setPause(\"blurred\", true);\n };\n const onWindowFocus = (event: Event): void => {\n if (!(event.target instanceof Node)) handlers.current.setPause(\"blurred\", false);\n };\n doc.addEventListener(\"visibilitychange\", onVisibility);\n view.addEventListener(\"blur\", onWindowBlur);\n view.addEventListener(\"focus\", onWindowFocus);\n handlers.current.syncTimers();\n setPaused(anyPause(pauses.current));\n const map = timers.current;\n return () => {\n mounted.current = false;\n doc.removeEventListener(\"visibilitychange\", onVisibility);\n view.removeEventListener(\"blur\", onWindowBlur);\n view.removeEventListener(\"focus\", onWindowFocus);\n // Unmounting stops the timers; the queue itself is retained by the manager.\n for (const timer of map.values()) if (timer.handle !== null) clearTimeout(timer.handle);\n map.clear();\n };\n }, [handlers, rejected]);\n\n const hotkeyKey = hotkey === null ? null : hotkey.join(\"\\u0000\");\n useEffect(() => {\n if (hotkeyKey === null || rejected) return;\n const keys = hotkeyKey.split(\"\\u0000\");\n const doc = regionRef.current?.ownerDocument ?? document;\n const onKeyDown = (event: KeyboardEvent): void => {\n if (event.defaultPrevented || !matchesHotkey(keys, event)) return;\n const all = sortedRoots();\n const newest = all.at(-1);\n if (newest === undefined) return;\n event.preventDefault();\n const active = doc.activeElement;\n if (active instanceof HTMLElement && !regionRef.current?.contains(active)) {\n returnTarget.current = active;\n }\n newest.element.focus();\n };\n doc.addEventListener(\"keydown\", onKeyDown);\n return () => doc.removeEventListener(\"keydown\", onKeyDown);\n // oxlint-disable-next-line react-hooks/exhaustive-deps -- `sortedRoots` reads refs only\n }, [hotkeyKey, rejected]);\n\n // --- The portal host (design 4.3) ------------------------------------------------------------\n\n // The hydration render matches the server's inline region; every later render portals into\n // `host`, whose place the library changes without React noticing.\n const portalled = useSyncExternalStore(subscribeNothing, onClient, onServerOrHydrating);\n const [host] = useState(createHost);\n const anchorRef = useRef<HTMLDivElement | null>(null);\n\n useLayoutEffect(() => {\n if (host === null) return;\n return () => host.remove();\n }, [host]);\n\n // Before the region effect below: a move hides the popover (removal steps), which that effect\n // shows again in the same commit.\n useLayoutEffect(() => {\n if (host === null || !portalled) return;\n const target = container ?? anchorRef.current;\n if (target !== null && host.parentNode !== target) target.append(host);\n });\n\n // --- After every commit: region, timers, queued announcements, orphan exits ----------------\n\n useLayoutEffect(() => {\n const region = regionRef.current;\n const current = entriesRef.current;\n // Focus that left with a remount (a move into or out of a dialog) fires no `focusout`.\n if (\n pauses.current.focus &&\n (region === null || !region.contains(region.ownerDocument.activeElement))\n ) {\n returnTarget.current = null;\n setPause(\"focus\", false);\n }\n if (region !== null && region.isConnected && region.hasAttribute(\"popover\")) {\n let open = false;\n try {\n open = region.matches(\":popover-open\");\n } catch {\n open = false;\n }\n try {\n if (current.length > 0) {\n if (!open) region.showPopover();\n else if (addedSinceEffect.current) {\n // Re-raise to the top of the top layer, above a menu or popover opened since\n // (design 4.2). No style flush in between, so nothing is re-created.\n region.hidePopover();\n region.showPopover();\n }\n } else if (open) {\n region.hidePopover();\n }\n } catch {\n // A render element without popover support; nothing to show.\n }\n }\n addedSinceEffect.current = false;\n\n const { visible, queued } = partition(current, latest.current.limit);\n // Queued toasts are announced on add (design 5): from their plain-text fields, since they\n // have no DOM yet. Visible ones announce from their Root once their content committed.\n for (const entry of queued) {\n const id = entry.options.id;\n if (lastAnnounced.current.has(id)) continue;\n const resolver = entry.options.announce ?? latest.current.announce;\n const text = resolver\n ? resolver(entry.item)\n : [plainText(entry.options.title), plainText(entry.options.description)]\n .filter((t) => t !== \"\")\n .join(\" \");\n queueAnnouncement(id, text, entry.options.priority ?? \"normal\");\n }\n // An ending toast without a Root (the consumer did not render one) has nothing to animate.\n for (const entry of visible) {\n if (entry.ending && !roots.current.has(entry.options.id)) drop(entry.options.id);\n }\n syncTimers();\n });\n\n // --- Development checks ---------------------------------------------------------------------\n\n // A Viewport inside a library Dialog would close (and unmount) with it.\n const insideDialog = use(DialogContext) !== null;\n useDevWarning(\n insideDialog,\n \"Toast.Viewport: rendered inside a Dialog. The region would close with the dialog; render \" +\n \"the Viewport once near the root of the app (it moves into open modal Dialogs by itself).\",\n );\n\n // --- Render ---------------------------------------------------------------------------------\n\n const { visible, queued } = partition(entries, limit);\n const state: ToastViewportState = {\n toasts: visible.map((e) => e.item),\n queued: queued.length,\n paused,\n };\n\n const regionCallbackRef = useCallback((element: HTMLElement) => {\n regionRef.current = element;\n return () => {\n if (regionRef.current === element) regionRef.current = null;\n };\n }, []);\n\n // Touch lifts the pointer at once (`pointerleave` follows `pointerup`), so a tap never sticks.\n const onPointerEnter = (): void => setPause(\"hover\", true);\n const onPointerLeave = (): void => setPause(\"hover\", false);\n const onFocus = (event: FocusEvent<HTMLElement>): void => {\n const region = event.currentTarget;\n const from = event.relatedTarget;\n if (!(from instanceof Node) || !region.contains(from)) {\n returnTarget.current ??= from instanceof HTMLElement ? from : null;\n }\n setPause(\"focus\", true);\n };\n const onBlur = (event: FocusEvent<HTMLElement>): void => {\n const to = event.relatedTarget;\n if (to instanceof Node && event.currentTarget.contains(to)) return;\n returnTarget.current = null;\n setPause(\"focus\", false);\n };\n\n const libraryProps: ComponentPropsWithRef<\"section\"> & DataAttributes = {\n popover: \"manual\",\n \"aria-label\": rest[\"aria-label\"] ?? regionLabel(hotkey),\n \"data-paused\": dataAttr(paused),\n \"data-empty\": dataAttr(state.toasts.length === 0),\n onPointerEnter,\n onPointerLeave,\n onFocus,\n onBlur,\n };\n\n const region = useRender<ComponentPropsWithRef<\"section\">, ToastViewportState>({\n defaultElement: \"section\",\n render,\n state,\n className,\n style,\n children,\n props: rest,\n libraryProps,\n ref: regionCallbackRef,\n });\n\n if (rejected) return null;\n const content = <ToastViewportContext value={context}>{region}</ToastViewportContext>;\n if (!portalled || host === null) return content;\n return (\n <>\n <div ref={anchorRef} style={ANCHOR_STYLE} />\n {createPortal(content, host)}\n </>\n );\n}\n\nconst ANCHOR_STYLE: CSSProperties = { display: \"contents\" };\n\nToastViewport.displayName = \"ToastViewport\";\n"],"mappings":";;;;;;;;;;;;;;AAsFA,MAAM,QAAQ,QAAQ,IAAI,aAAa;AAEvC,MAAM,iBAAoC,CAAC,IAAI;AAE/C,MAAM,iBAAmD;CACvD,QAAQ;CACR,SAAS;CACT,SAAS;CACT,UAAU;AACZ;;AAGA,SAAS,YAAY,QAAmC;CACtD,OAAO,OACJ,KAAK,QAAQ,eAAe,QAAQ,IAAI,QAAQ,kBAAkB,EAAE,CAAC,CAAC,QAAQ,UAAU,EAAE,CAAC,CAAC,CAC5F,KAAK,GAAG;AACb;;AAGA,SAAS,YAAY,QAA0C;CAC7D,IAAI,WAAW,QAAQ,OAAO,WAAW,GAAG,OAAO;CACnD,OAAO,kBAAkB,YAAY,MAAM,EAAE;AAC/C;;AAGA,SAAS,cAAc,QAA2B,OAA+B;CAC/E,MAAM,QAAQ;CACd,KAAK,MAAM,YAAY,OAAO,KAAK,cAAc,GAC/C,IAAK,MAAM,cAAc,SAAU,OAAO,SAAS,QAAQ,GAAG,OAAO;CAEvE,OAAO,OAAO,OAAO,QAAQ,OAAO,kBAAkB,MAAM,SAAS,GAAG;AAC1E;;AAGA,MAAM,+BAA6C,CAAC;AACpD,MAAM,iBAA0B;AAChC,MAAM,4BAAqC;;;;;;AAO3C,SAAS,aAAiC;CACxC,IAAI,OAAO,aAAa,aAAa,OAAO;CAC5C,MAAM,OAAO,SAAS,cAAc,KAAK;CACzC,KAAK,MAAM,UAAU;CACrB,OAAO;AACT;AAYA,SAAS,UACP,SACA,OACO;CACP,OAAO;EACL;EACA,GAAG;EACH,MAAM;GACJ,GAAG;GACH,IAAI,QAAQ;GACZ,SAAS,MAAM,kBAAkB,MAAM;GACvC,QAAQ,MAAM;EAChB;CACF;AACF;AAEA,SAAS,UACP,OACA,OACO;CACP,OAAO,UAAU,MAAM,SAAS;EAC9B,gBAAgB,MAAM,kBAAkB,MAAM;EAC9C,eAAe,MAAM,iBAAiB,MAAM;EAC5C,QAAQ,MAAM,UAAU,MAAM;CAChC,CAAC;AACH;;AAGA,SAAS,UACP,SACA,OACuC;CACvC,MAAM,UAAmB,CAAC;CAC1B,MAAM,SAAkB,CAAC;CACzB,IAAI,OAAO;CACX,KAAK,MAAM,SAAS,SAClB,IAAI,MAAM,QAAQ,QAAQ,KAAK,KAAK;MAC/B,IAAI,OAAO,OAAO;EACrB,QAAQ,KAAK,KAAK;EAClB;CACF,OAAO,OAAO,KAAK,KAAK;CAE1B,OAAO;EAAE;EAAS;CAAO;AAC3B;;AAGA,SAAS,UAAU,MAAuB;CACxC,IAAI,OAAO,SAAS,UAAU,OAAO;CACrC,IAAI,OAAO,SAAS,YAAY,OAAO,SAAS,UAAU,OAAO,OAAO,IAAI;CAC5E,IAAI,MAAM,QAAQ,IAAI,GAAG,OAAO,KAAK,IAAI,SAAS,CAAC,CAAC,KAAK,EAAE;CAC3D,OAAO;AACT;;AAGA,SAAS,mBAAmB,GAAY,GAAoB;CAC1D,IAAI,MAAM,GAAG,OAAO;CACpB,OAAO,EAAE,wBAAwB,CAAC,IAAI,KAAK,8BAA8B,KAAK;AAChF;AAiBA,SAAS,SAAS,SAAgC;CAChD,OAAO,QAAQ,SAAS,QAAQ,SAAS,QAAQ,UAAU,QAAQ;AACrE;;;;;;;;;;AAeA,SAAgB,cAAc,EAC5B,UAAUA,OACV,QAAQ,GACR,WAAW,KACX,SAAS,gBACT,UACA,UACA,WACA,OACA,QACA,GAAG,QACkB;CAErB,MAAM,CAAC,SAAS,cAAc,SAA2B,CAAC,CAAC;CAG3D,MAAM,aAAa,OAAyB,OAAO;CACnD,MAAM,WAAW,OAA6B,IAAI;CAClD,SAAS,YAAY;EACnB,UAAU,WAAqC,SAAS,QAAQ,QAAQ,MAAM;EAC9E,QAAQ,aAAsC,SAAS,QAAQ,MAAM,QAAQ;EAC7E,cAAc,SAAS,QAAQ,OAAO;CACxC;CAGA,MAAM,CAAC,UAAU,eAAe,eAC9B,sBAAsB,OAAO,CAAC,CAAC,QAAQ,SAAS,OAAQ,CAC1D;CACA,MAAM,GAAG,wBAAwB,cAAc;CAE/C,MAAM,YAAY,OAA2B,IAAI;CACjD,MAAM,QAAQ,uBAAO,IAAI,IAAyB,CAAC;CACnD,MAAM,YAAY,uBAAO,IAAI,IAAY,CAAC;CAC1C,MAAM,SAAS,uBAAO,IAAI,IAAmB,CAAC;CAC9C,MAAM,SAAS,OAAqB;EAClC,OAAO;EACP,OAAO;EACP,QAAQ;EACR,SAAS;CACX,CAAC;CACD,MAAM,CAAC,QAAQ,aAAa,SAAS,KAAK;;CAE1C,MAAM,eAAe,OAA2B,IAAI;CACpD,MAAM,gBAAgB,uBAAO,IAAI,IAA2B,CAAC;;CAE7D,MAAM,gBAAgB,uBAAO,IAAI,IAAoC,CAAC;CACtE,MAAM,gBAAgB,OAAoD,CAAC,CAAC;CAC5E,MAAM,mBAAmB,OAAO,KAAK;CACrC,MAAM,UAAU,OAAO,KAAK;CAE5B,MAAM,SAAS,UAAU;EAAE;EAAO;EAAU;EAAQ;CAAS,CAAC;CAE9D,MAAM,YAAY,qBAAqB,iBAAiB,iBAAiB,qBAAqB;CAE9F,MAAM,SAAS,SAAiC;EAC9C,WAAW,UAAU;EACrB,WAAW,IAAI;CACjB;CAIA,MAAM,qBAAqB,UAAgC;EACzD,MAAM,MAAM,MAAM,QAAQ;EAC1B,IAAI,QAAQ,KAAA,GAAW,OAAO;EAC9B,OAAO,MAAM,QAAQ,WAAW,KAAA,IAAY,OAAO,OAAO,QAAQ;CACpE;CAEA,MAAM,mBAAyB;EAC7B,MAAM,MAAM,OAAO;EACnB,MAAM,EAAE,YAAY,UAAU,WAAW,SAAS,OAAO,QAAQ,KAAK;EACtE,MAAM,MAAM,KAAK,IAAI;EACrB,MAAM,uBAAO,IAAI,IAAY;EAC7B,MAAM,SAAS,CAAC,QAAQ,WAAW,SAAS,OAAO,OAAO;EAC1D,KAAK,MAAM,SAAS,SAAS;GAC3B,IAAI,MAAM,QAAQ;GAClB,MAAM,KAAK,MAAM,QAAQ;GACzB,MAAM,KAAK,kBAAkB,KAAK;GAClC,IAAI,OAAO,MAAM;GACjB,KAAK,IAAI,EAAE;GACX,IAAI,QAAQ,IAAI,IAAI,EAAE;GACtB,IAAI,UAAU,KAAA,KAAa,MAAM,aAAa,IAAI;IAChD,IAAI,OAAO,UAAU,MAAM,aAAa,MAAM,MAAM;IACpD,QAAQ;KAAE,UAAU;KAAI,WAAW;KAAI,WAAW;KAAG,QAAQ;IAAK;IAClE,IAAI,IAAI,IAAI,KAAK;GACnB;GACA,MAAM,MAAM,CAAC,UAAU,CAAC,MAAM,KAAK,WAAW,CAAC,UAAU,QAAQ,IAAI,EAAE;GACvE,IAAI,OAAO,MAAM,WAAW,MAAM;IAChC,MAAM,UAAU;IAChB,QAAQ,YAAY;IACpB,QAAQ,SAAS,iBACT;KACJ,QAAQ,SAAS;KACjB,QAAQ,YAAY;KACpB,MAAM,CAAC,EAAE,GAAG,WAAW,KAAA,CAAS;IAClC,GACA,KAAK,IAAI,GAAG,QAAQ,SAAS,CAC/B;GACF,OAAO,IAAI,CAAC,OAAO,MAAM,WAAW,MAAM;IACxC,aAAa,MAAM,MAAM;IACzB,MAAM,SAAS;IACf,MAAM,aAAa,MAAM,MAAM;GACjC;EACF;EACA,KAAK,MAAM,CAAC,IAAI,UAAU,KAAK;GAC7B,IAAI,KAAK,IAAI,EAAE,GAAG;GAClB,IAAI,MAAM,WAAW,MAAM,aAAa,MAAM,MAAM;GACpD,IAAI,OAAO,EAAE;EACf;CACF;CAEA,MAAM,YAAY,QAA4B,UAAyB;EACrE,IAAI,OAAO,QAAQ,YAAY,OAAO;EACtC,OAAO,QAAQ,UAAU;EAEzB,WAAW;EACX,UAAU,SAAS,OAAO,OAAO,CAAC;CACpC;;CAKA,MAAM,eAAe,YAA0E;EAC7F,MAAM,OAAO,IAAI,IAAI,WAAW,QAAQ,QAAQ,MAAM,CAAC,EAAE,MAAM,CAAC,CAAC,KAAK,MAAM,EAAE,QAAQ,EAAE,CAAC;EACzF,MAAM,OAA+C,CAAC;EACtD,KAAK,MAAM,CAAC,IAAI,YAAY,MAAM,SAAS;GACzC,IAAI,CAAC,KAAK,IAAI,EAAE,KAAK,SAAS,IAAI,EAAE,KAAK,CAAC,QAAQ,aAAa;GAC/D,KAAK,KAAK;IAAE;IAAI;GAAQ,CAAC;EAC3B;EACA,OAAO,KAAK,MAAM,GAAG,MAAM,mBAAmB,EAAE,SAAS,EAAE,OAAO,CAAC;CACrE;;;;;CAMA,MAAM,gBAAgB,YAAuC;EAC3D,MAAM,MAAM,UAAU,SAAS,iBAAiB;EAChD,MAAM,SAAS,IAAI;EACnB,IAAI,EAAE,kBAAkB,cAAc;EACtC,MAAM,MAAM,YAAY;EACxB,MAAM,QAAQ,IAAI,WAAW,EAAE,cAAc,QAAQ,SAAS,MAAM,CAAC;EACrE,IAAI,UAAU,MAAM,CAAC,QAAQ,IAAI,IAAI,MAAM,CAAE,EAAE,GAAG;EAClD,MAAM,QAAQ,IAAI,MAAM,QAAQ,CAAC,CAAC,CAAC,MAAM,EAAE,SAAS,CAAC,QAAQ,IAAI,EAAE,CAAC;EACpE,MAAM,SAAS,IACZ,MAAM,GAAG,KAAK,CAAC,CACf,QAAQ,CAAC,CACT,MAAM,EAAE,SAAS,CAAC,QAAQ,IAAI,EAAE,CAAC;EACpC,MAAM,SAAS,SAAS;EACxB,IAAI,WAAW,KAAA,GAAW;GACxB,OAAO,QAAQ,MAAM;GACrB;EACF;EACA,MAAM,SAAS,aAAa;EAC5B,IAAI,WAAW,QAAQ,OAAO,eAAe,CAAC,UAAU,SAAS,SAAS,MAAM,GAAG;GACjF,OAAO,MAAM;GACb,IAAI,IAAI,kBAAkB,QAAQ;EACpC;EACA,OAAO,KAAK;CACd;;;;;CAQA,MAAM,yBAAyB,QAAgC;EAC7D,KAAK,MAAM,MAAM,KAAK;GACpB,MAAM,UAAU,MAAM,QAAQ,IAAI,EAAE;GACpC,IAAI,YAAY,KAAA,KAAa,CAAC,QAAQ,aAAa;GACnD,IAAI,OAAO,QAAQ,kBAAkB,YAAY;GACjD,cAAc,QAAQ,IAAI,IAAI,IAAI,IAAI,QAAQ,cAAc,EAAE,SAAS,KAAK,CAAC,CAAC,CAAC;EACjF;CACF;CAEA,MAAM,cAAc,OAAqB;EACvC,MAAM,QAAQ,OAAO,QAAQ,IAAI,EAAE;EACnC,IAAI,OAAO,UAAU,MAAM,aAAa,MAAM,MAAM;EACpD,OAAO,QAAQ,OAAO,EAAE;CAC1B;CAEA,MAAM,wBACJ,QACA,QACA,UACS;EACT,KAAK,MAAM,SAAS,QAAQ;GAC1B,MAAM,cAAc,MAAM,QAAQ;GAClC,IAAI,gBAAgB,KAAA,GAAW;GAC/B,MAAM,UAAU,oBAAoB,OAAO;IAAE,IAAI,MAAM,QAAQ;IAAI;GAAO,CAAC;GAG3E,qBAAqB;IACnB,IAAI,CAAC,QAAQ,SAAS;IACtB,qBAAqB,YAAY;KAC/B,MAAM,YAAY,OAAO;IAC3B,CAAC;GACH,CAAC;EACH;CACF;CAEA,MAAM,SAAS,KAAwB,QAAsB,UAAmC;EAC9F,MAAM,SAAS,IAAI,IAAI,GAAG;EAC1B,MAAM,UAAU,WAAW,QAAQ,QAAQ,MAAM,CAAC,EAAE,UAAU,OAAO,IAAI,EAAE,QAAQ,EAAE,CAAC;EACtF,IAAI,QAAQ,WAAW,GAAG;EAC1B,MAAM,aAAa,IAAI,IAAI,QAAQ,KAAK,MAAM,EAAE,QAAQ,EAAE,CAAC;EAC3D,aAAa,UAAU;EACvB,sBAAsB,UAAU;EAChC,MACE,WAAW,QAAQ,KAAK,MACtB,WAAW,IAAI,EAAE,QAAQ,EAAE,IAAI,UAAU,GAAG,EAAE,QAAQ,KAAK,CAAC,IAAI,CAClE,CACF;EACA,WAAW;EACX,qBAAqB,SAAS,QAAQ,KAAK;CAC7C;CAEA,MAAM,QAAQ,OAAqB;EACjC,MAAM,OAAO,WAAW,QAAQ,QAAQ,MAAM,EAAE,EAAE,UAAU,EAAE,QAAQ,OAAO,GAAG;EAChF,IAAI,KAAK,WAAW,WAAW,QAAQ,QAAQ;EAC/C,cAAc,QAAQ,OAAO,EAAE;EAC/B,cAAc,QAAQ,OAAO,EAAE;EAC/B,MAAM,IAAI;CACZ;CAEA,MAAM,SAAS,aAA4C;EACzD,MAAM,OAAO,CAAC,GAAG,WAAW,OAAO;EAGnC,MAAM,YAAqB,CAAC;EAC5B,KAAK,MAAM,WAAW,UAAU;GAC9B,IAAI,QAAQ,SAAS,WAAW;IAC9B,KAAK,IAAI,IAAI,GAAG,IAAI,KAAK,QAAQ,KAAK;KACpC,MAAM,QAAQ,KAAK;KACnB,IAAI,MAAM,QAAQ;KAClB,IAAI,QAAQ,OAAO,KAAA,KAAa,MAAM,QAAQ,OAAO,QAAQ,IAAI;KACjE,UAAU,KAAK,KAAK;KACpB,KAAK,KAAK,UAAU,OAAO,EAAE,QAAQ,KAAK,CAAC;IAC7C;IACA;GACF;GACA,MAAM,QAAQ,KAAK,WAAW,MAAM,EAAE,QAAQ,OAAO,QAAQ,EAAE;GAC/D,MAAM,WAAW,UAAU,KAAK,KAAA,IAAY,KAAK;GACjD,IAAI,QAAQ,SAAS,OAAO;IAC1B,MAAM,UAAU;KAAE,GAAG,QAAQ;KAAS,IAAI,QAAQ;IAAG;IACrD,IAAI,aAAa,KAAA,KAAa,CAAC,SAAS,QAAQ;KAE9C,KAAK,SAAS,UACZ;MAAE,GAAG,SAAS;MAAS,GAAG;KAAQ,GAClC;MACE,gBAAgB,QAAQ,WAAW,SAAS;MAC5C,eAAe,SAAS;MACxB,QAAQ;KACV,CACF;KACA;IACF;IACA,IAAI,aAAa,KAAA,GAAW;KAG1B,KAAK,OAAO,OAAO,CAAC;KACpB,WAAW,QAAQ,EAAE;KACrB,cAAc,QAAQ,OAAO,QAAQ,EAAE;KACvC,cAAc,QAAQ,OAAO,QAAQ,EAAE;IACzC;IACA,KAAK,KACH,UAAU,SAAS;KACjB,gBAAgB,QAAQ;KACxB,eAAe;KACf,QAAQ;IACV,CAAC,CACH;IACA,iBAAiB,UAAU;GAC7B,OAAO;IACL,IAAI,aAAa,KAAA,GAAW;KAC1B,IAAI,SAAS,CAAC,QAAQ,QACpB,QAAQ,MAAM,mCAAmC,QAAQ,GAAG,0BAA0B;KAExF;IACF;IACA,IAAI,SAAS,QAAQ;IAIrB,KAAK,SAAS,UAHE,QAAQ,SACpB;KAAE,GAAG,QAAQ;KAAS,IAAI,QAAQ;IAAG,IACrC;KAAE,GAAG,SAAS;KAAS,GAAG,QAAQ;KAAS,IAAI,QAAQ;IAAG,GAC7B;KAC/B,gBAAgB,QAAQ,SAAS,QAAQ,SAAS;KAClD,eAAe,SAAS;KACxB,QAAQ;IACV,CAAC;GACH;EACF;EACA,IAAI,UAAU,WAAW,GAAG;GAC1B,MAAM,IAAI;GACV;EACF;EAEA,MAAM,QAAQ,IAAI,IAAI,KAAK,QAAQ,MAAM,CAAC,EAAE,MAAM,CAAC,CAAC,KAAK,MAAM,EAAE,QAAQ,EAAE,CAAC;EAC5E,MAAM,UAAU,IAAI,IAAI,UAAU,KAAK,MAAM,EAAE,QAAQ,EAAE,CAAC,CAAC,QAAQ,OAAO,CAAC,MAAM,IAAI,EAAE,CAAC,CAAC;EACzF,aAAa,OAAO;EACpB,sBAAsB,OAAO;EAC7B,MAAM,IAAI;EACV,WAAW;EACX,qBAAqB,WAAW,WAAW,KAAA,CAAS;CACtD;CAEA,MAAM,oBAAoB,IAAY,YAA2B;EAC/D,MAAM,QAAQ,WAAW,QAAQ,WAAW,MAAM,EAAE,QAAQ,OAAO,EAAE;EACrE,IAAI,UAAU,IAAI;EAClB,MAAM,QAAQ,WAAW,QAAQ;EACjC,IAAI,MAAM,kBAAkB,SAAS;EACrC,MAAM,OAAO,CAAC,GAAG,WAAW,OAAO;EACnC,KAAK,SAAS,UAAU,OAAO,EAAE,eAAe,QAAQ,CAAC;EACzD,MAAM,IAAI;CACZ;CAIA,MAAM,2BAAiC;EACrC,MAAM,QAAQ,cAAc;EAC5B,cAAc,UAAU,CAAC;EACzB,IAAI,MAAM,WAAW,GAAG;EACxB,MAAM,MAAO,UAAU,SAAS,iBAAiB;EACjD,IAAI,OAAO,IAAI,eAAe,YAAY;EAC1C,MAAM,WAAW,MAAM,MAAM,MAAM,EAAE,aAAa,MAAM,IAAI,SAAS;EACrE,IAAI,WAAW,MAAM,KAAK,MAAM,EAAE,IAAI,CAAC,CAAC,KAAK,GAAG,GAAG,EAAE,SAAS,CAAC;CACjE;CAEA,MAAM,qBAAqB,IAAY,MAAqB,aAAkC;EAC5F,MAAM,MAAM,cAAc;EAC1B,IAAI,IAAI,IAAI,EAAE,KAAK,IAAI,IAAI,EAAE,MAAM,MAAM;EACzC,IAAI,IAAI,IAAI,IAAI;EAChB,IAAI,SAAS,QAAQ,KAAK,KAAK,MAAM,IAAI;EACzC,IAAI,cAAc,QAAQ,WAAW,GAAG,eAAe,kBAAkB;EACzE,cAAc,QAAQ,KAAK;GAAE;GAAM;EAAS,CAAC;CAC/C;CAGA,MAAM,WAAW,WAA2C;EAC1D,IAAI,WAAW,QAAQ,SAAS,GAAG;EAEnC,KAAK,MAAM,KAAK,QACd,IAAI,EAAE,cAAc,KAAA,GAAW,cAAc,QAAQ,IAAI,EAAE,QAAQ,IAAI,EAAE,SAAS;EAEpF,MACE,OAAO,KAAK,MACV,UAAU,EAAE,SAAS;GAAE,gBAAgB,EAAE;GAAS,eAAe;GAAO,QAAQ;EAAM,CAAC,CACzF,CACF;EACA,iBAAiB,UAAU;CAC7B;CAEA,MAAM,eAAyC;EAC7C,MAAM,YAAY,cAAc;EAChC,OAAO,WAAW,QACf,QAAQ,MAAM,CAAC,EAAE,MAAM,CAAC,CACxB,KAAK,OAAO;GACX,SAAS,EAAE;GACX,SAAS,EAAE;GACX,WAAW,UAAU,IAAI,EAAE,QAAQ,EAAE,IAAI,UAAU,IAAI,EAAE,QAAQ,EAAE,IAAI,KAAA;EACzE,EAAE;CACN;CAEA,MAAM,WAAW,UAAU;EACzB;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;CACF,CAAC;CAED,MAAM,UAAU,eACP;EACL,QAAQ,KAAK,QAAQ,UAAU,SAAS,QAAQ,MAAM,KAAK,QAAQ,KAAK;EACxE,OAAO,OAAO,SAAS,QAAQ,KAAK,EAAE;EACtC,mBAAmB,IAAI,YAAY,SAAS,QAAQ,iBAAiB,IAAI,OAAO;EAChF,eAAe,IAAI,UAAU;GAC3B,IAAI,OAAO,UAAU,QAAQ,IAAI,EAAE;QAC9B,UAAU,QAAQ,OAAO,EAAE;GAChC,SAAS,QAAQ,WAAW;EAC9B;EACA,eAAe,IAAI,YAAY;GAC7B,IAAI,YAAY,MAAM,MAAM,QAAQ,OAAO,EAAE;QACxC,MAAM,QAAQ,IAAI,IAAI,OAAO;EACpC;EACA,eAAe,IAAI,cAAc;GAC/B,MAAM,MAAM,YAAY;GACxB,MAAM,QAAQ,IAAI,WAAW,MAAM,EAAE,OAAO,EAAE;GAC9C,IAAI,UAAU,IAAI;GAClB,IAAI,QAAQ,UAAU,EAAE,QAAQ,MAAM;EACxC;EACA,WAAW,IAAI,MAAM,aAAa,SAAS,QAAQ,kBAAkB,IAAI,MAAM,QAAQ;EACvF,eAAe,OAAO,cAAc,QAAQ,IAAI,EAAE;EAClD,iBAAiB,OAAO,QAAQ;CAClC,IAEA,CAAC,UAAU,MAAM,CACnB;CAIA,gBAAgB;EACd,MAAM,cAAc,sBAAsB,OAAO,CAAC,CAAC,UAAU,SAAS,OAAQ;EAC9E,IAAI,gBAAgB,MAAM;GACxB,YAAY,IAAI;GAChB,IAAI,OACF,QAAQ,MACN,4KAEF;GAEF;EACF;EACA,YAAY,KAAK;EACjB,OAAO;CACT,GAAG,CAAC,OAAO,CAAC;CAIZ,gBAAgB;EACd,IAAI,UAAU;EACd,QAAQ,UAAU;EAClB,MAAM,MAAM,UAAU,SAAS,iBAAiB;EAChD,MAAM,OAAO,IAAI,eAAe;EAChC,OAAO,QAAQ,SAAS,IAAI,oBAAoB;EAChD,MAAM,qBACJ,SAAS,QAAQ,SAAS,UAAU,IAAI,oBAAoB,QAAQ;EAEtE,MAAM,gBAAgB,UAAuB;GAC3C,IAAI,EAAE,MAAM,kBAAkB,OAAO,SAAS,QAAQ,SAAS,WAAW,IAAI;EAChF;EACA,MAAM,iBAAiB,UAAuB;GAC5C,IAAI,EAAE,MAAM,kBAAkB,OAAO,SAAS,QAAQ,SAAS,WAAW,KAAK;EACjF;EACA,IAAI,iBAAiB,oBAAoB,YAAY;EACrD,KAAK,iBAAiB,QAAQ,YAAY;EAC1C,KAAK,iBAAiB,SAAS,aAAa;EAC5C,SAAS,QAAQ,WAAW;EAC5B,UAAU,SAAS,OAAO,OAAO,CAAC;EAClC,MAAM,MAAM,OAAO;EACnB,aAAa;GACX,QAAQ,UAAU;GAClB,IAAI,oBAAoB,oBAAoB,YAAY;GACxD,KAAK,oBAAoB,QAAQ,YAAY;GAC7C,KAAK,oBAAoB,SAAS,aAAa;GAE/C,KAAK,MAAM,SAAS,IAAI,OAAO,GAAG,IAAI,MAAM,WAAW,MAAM,aAAa,MAAM,MAAM;GACtF,IAAI,MAAM;EACZ;CACF,GAAG,CAAC,UAAU,QAAQ,CAAC;CAEvB,MAAM,YAAY,WAAW,OAAO,OAAO,OAAO,KAAK,IAAQ;CAC/D,gBAAgB;EACd,IAAI,cAAc,QAAQ,UAAU;EACpC,MAAM,OAAO,UAAU,MAAM,IAAQ;EACrC,MAAM,MAAM,UAAU,SAAS,iBAAiB;EAChD,MAAM,aAAa,UAA+B;GAChD,IAAI,MAAM,oBAAoB,CAAC,cAAc,MAAM,KAAK,GAAG;GAE3D,MAAM,SADM,YACK,CAAC,CAAC,GAAG,EAAE;GACxB,IAAI,WAAW,KAAA,GAAW;GAC1B,MAAM,eAAe;GACrB,MAAM,SAAS,IAAI;GACnB,IAAI,kBAAkB,eAAe,CAAC,UAAU,SAAS,SAAS,MAAM,GACtE,aAAa,UAAU;GAEzB,OAAO,QAAQ,MAAM;EACvB;EACA,IAAI,iBAAiB,WAAW,SAAS;EACzC,aAAa,IAAI,oBAAoB,WAAW,SAAS;CAE3D,GAAG,CAAC,WAAW,QAAQ,CAAC;CAMxB,MAAM,YAAY,qBAAqB,kBAAkB,UAAU,mBAAmB;CACtF,MAAM,CAAC,QAAQ,SAAS,UAAU;CAClC,MAAM,YAAY,OAA8B,IAAI;CAEpD,sBAAsB;EACpB,IAAI,SAAS,MAAM;EACnB,aAAa,KAAK,OAAO;CAC3B,GAAG,CAAC,IAAI,CAAC;CAIT,sBAAsB;EACpB,IAAI,SAAS,QAAQ,CAAC,WAAW;EACjC,MAAM,SAAS,aAAa,UAAU;EACtC,IAAI,WAAW,QAAQ,KAAK,eAAe,QAAQ,OAAO,OAAO,IAAI;CACvE,CAAC;CAID,sBAAsB;EACpB,MAAM,SAAS,UAAU;EACzB,MAAM,UAAU,WAAW;EAE3B,IACE,OAAO,QAAQ,UACd,WAAW,QAAQ,CAAC,OAAO,SAAS,OAAO,cAAc,aAAa,IACvE;GACA,aAAa,UAAU;GACvB,SAAS,SAAS,KAAK;EACzB;EACA,IAAI,WAAW,QAAQ,OAAO,eAAe,OAAO,aAAa,SAAS,GAAG;GAC3E,IAAI,OAAO;GACX,IAAI;IACF,OAAO,OAAO,QAAQ,eAAe;GACvC,QAAQ;IACN,OAAO;GACT;GACA,IAAI;IACF,IAAI,QAAQ,SAAS,GAAG;KACtB,IAAI,CAAC,MAAM,OAAO,YAAY;UACzB,IAAI,iBAAiB,SAAS;MAGjC,OAAO,YAAY;MACnB,OAAO,YAAY;KACrB;IACF,OAAO,IAAI,MACT,OAAO,YAAY;GAEvB,QAAQ,CAER;EACF;EACA,iBAAiB,UAAU;EAE3B,MAAM,EAAE,SAAS,WAAW,UAAU,SAAS,OAAO,QAAQ,KAAK;EAGnE,KAAK,MAAM,SAAS,QAAQ;GAC1B,MAAM,KAAK,MAAM,QAAQ;GACzB,IAAI,cAAc,QAAQ,IAAI,EAAE,GAAG;GACnC,MAAM,WAAW,MAAM,QAAQ,YAAY,OAAO,QAAQ;GAC1D,MAAM,OAAO,WACT,SAAS,MAAM,IAAI,IACnB,CAAC,UAAU,MAAM,QAAQ,KAAK,GAAG,UAAU,MAAM,QAAQ,WAAW,CAAC,CAAC,CACnE,QAAQ,MAAM,MAAM,EAAE,CAAC,CACvB,KAAK,GAAG;GACf,kBAAkB,IAAI,MAAM,MAAM,QAAQ,YAAY,QAAQ;EAChE;EAEA,KAAK,MAAM,SAAS,SAClB,IAAI,MAAM,UAAU,CAAC,MAAM,QAAQ,IAAI,MAAM,QAAQ,EAAE,GAAG,KAAK,MAAM,QAAQ,EAAE;EAEjF,WAAW;CACb,CAAC;CAKD,MAAM,eAAe,IAAI,aAAa,MAAM;CAC5C,cACE,cACA,mLAEF;CAIA,MAAM,EAAE,SAAS,WAAW,UAAU,SAAS,KAAK;CACpD,MAAM,QAA4B;EAChC,QAAQ,QAAQ,KAAK,MAAM,EAAE,IAAI;EACjC,QAAQ,OAAO;EACf;CACF;CAEA,MAAM,oBAAoB,aAAa,YAAyB;EAC9D,UAAU,UAAU;EACpB,aAAa;GACX,IAAI,UAAU,YAAY,SAAS,UAAU,UAAU;EACzD;CACF,GAAG,CAAC,CAAC;CAGL,MAAM,uBAA6B,SAAS,SAAS,IAAI;CACzD,MAAM,uBAA6B,SAAS,SAAS,KAAK;CAC1D,MAAM,WAAW,UAAyC;EACxD,MAAM,SAAS,MAAM;EACrB,MAAM,OAAO,MAAM;EACnB,IAAI,EAAE,gBAAgB,SAAS,CAAC,OAAO,SAAS,IAAI,GAClD,aAAa,YAAY,gBAAgB,cAAc,OAAO;EAEhE,SAAS,SAAS,IAAI;CACxB;CACA,MAAM,UAAU,UAAyC;EACvD,MAAM,KAAK,MAAM;EACjB,IAAI,cAAc,QAAQ,MAAM,cAAc,SAAS,EAAE,GAAG;EAC5D,aAAa,UAAU;EACvB,SAAS,SAAS,KAAK;CACzB;CAEA,MAAM,eAAkE;EACtE,SAAS;EACT,cAAc,KAAK,iBAAiB,YAAY,MAAM;EACtD,eAAe,SAAS,MAAM;EAC9B,cAAc,SAAS,MAAM,OAAO,WAAW,CAAC;EAChD;EACA;EACA;EACA;CACF;CAEA,MAAM,SAAS,UAAgE;EAC7E,gBAAgB;EAChB;EACA;EACA;EACA;EACA;EACA,OAAO;EACP;EACA,KAAK;CACP,CAAC;CAED,IAAI,UAAU,OAAO;CACrB,MAAM,UAAU,oBAAC,sBAAD;EAAsB,OAAO;EAAU,UAAA;CAA6B,CAAA;CACpF,IAAI,CAAC,aAAa,SAAS,MAAM,OAAO;CACxC,OACE,qBAAA,UAAA,EAAA,UAAA,CACE,oBAAC,OAAD;EAAK,KAAK;EAAW,OAAO;CAAe,CAAA,GAC1C,aAAa,SAAS,IAAI,CAC3B,EAAA,CAAA;AAEN;AAEA,MAAM,eAA8B,EAAE,SAAS,WAAW;AAE1D,cAAc,cAAc"}
|
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
import { Action, ActionDetails, ChangeReason } from "../internal/types.mjs";
|
|
2
|
+
import { ReactNode } from "react";
|
|
3
|
+
//#region src/toast/toast.d.ts
|
|
4
|
+
/** Announcement priority and role of a toast. */
|
|
5
|
+
export type ToastPriority = "normal" | "high";
|
|
6
|
+
/** The `action` of a toast: what `Toast.Action` renders and runs by default. */
|
|
7
|
+
export interface ToastActionOptions {
|
|
8
|
+
/** The Action button's content. */
|
|
9
|
+
readonly label: ReactNode;
|
|
10
|
+
/**
|
|
11
|
+
* Runs in the Action part's own transition (`data-pending` on the Action and the Root, the
|
|
12
|
+
* timer paused). The toast closes with `reason: "activate"` when it resolves; a rejection
|
|
13
|
+
* keeps the toast and reaches the nearest Error Boundary above the Viewport. A caught error
|
|
14
|
+
* resolves the action and therefore closes the toast: rethrow after `toast.update` to keep it.
|
|
15
|
+
*/
|
|
16
|
+
readonly action: Action<[details: ToastActionDetails]>;
|
|
17
|
+
}
|
|
18
|
+
export interface ToastOptions {
|
|
19
|
+
/** Explicit id: an existing one updates that toast. Default: generated. */
|
|
20
|
+
readonly id?: string | undefined;
|
|
21
|
+
readonly title?: ReactNode;
|
|
22
|
+
readonly description?: ReactNode;
|
|
23
|
+
/** Free-form, rendered as `data-type` on the Root (`"success"`, `"error"`, ...). */
|
|
24
|
+
readonly type?: string | undefined;
|
|
25
|
+
/**
|
|
26
|
+
* Announcement priority and role: `"high"` announces with `ariaNotify` priority `"high"` and
|
|
27
|
+
* renders the Root as an `alertdialog`. Default `"normal"`.
|
|
28
|
+
*/
|
|
29
|
+
readonly priority?: ToastPriority | undefined;
|
|
30
|
+
/**
|
|
31
|
+
* Milliseconds before the toast closes with `reason: "timeout"`; `null` never. Default: the
|
|
32
|
+
* Viewport's `duration`, or `null` (persistent) when the toast has an `action`. A timed action
|
|
33
|
+
* toast must keep its action reachable elsewhere too (WCAG 2.2.1).
|
|
34
|
+
*/
|
|
35
|
+
readonly duration?: number | null | undefined;
|
|
36
|
+
readonly action?: ToastActionOptions | undefined;
|
|
37
|
+
/**
|
|
38
|
+
* Called once when the toast leaves, with the reason: `"activate"` (the Action part settled),
|
|
39
|
+
* `"dismiss"` (Close, Escape, `toast.dismiss()`, a promise toast with no message for its
|
|
40
|
+
* outcome) or `"timeout"`. Runs in the Viewport's transition; a rejection reaches the boundary
|
|
41
|
+
* above the Viewport. Not called when the Viewport unmounts.
|
|
42
|
+
*/
|
|
43
|
+
readonly closeAction?: Action<[details: ToastCloseDetails]> | undefined;
|
|
44
|
+
/**
|
|
45
|
+
* The text `document.ariaNotify()` speaks for this toast; `null` announces nothing. Default:
|
|
46
|
+
* the Viewport's `announce`, else the text of the toast's Title and Description.
|
|
47
|
+
*/
|
|
48
|
+
readonly announce?: ((toast: ToastItem) => string | null) | undefined;
|
|
49
|
+
/** Anything the Viewport's children want to render. */
|
|
50
|
+
readonly data?: unknown;
|
|
51
|
+
}
|
|
52
|
+
/** A toast in the queue, as the Viewport's `children(state)` receives it. */
|
|
53
|
+
export interface ToastItem extends ToastOptions {
|
|
54
|
+
readonly id: string;
|
|
55
|
+
/**
|
|
56
|
+
* The promise given to `toast.promise` is unsettled, or an action dispatched through this
|
|
57
|
+
* toast's Action part is in flight.
|
|
58
|
+
*/
|
|
59
|
+
readonly pending: boolean;
|
|
60
|
+
/** Closed; kept mounted (and `inert`) for its exit animation. */
|
|
61
|
+
readonly ending: boolean;
|
|
62
|
+
}
|
|
63
|
+
/** Details passed to a toast's Action. */
|
|
64
|
+
export interface ToastActionDetails extends ActionDetails {
|
|
65
|
+
/** The toast's id, so an inline action can `toast.update` or `toast.dismiss` its own toast. */
|
|
66
|
+
readonly id: string;
|
|
67
|
+
}
|
|
68
|
+
/** Details passed to a toast's `closeAction`. */
|
|
69
|
+
export interface ToastCloseDetails extends ActionDetails {
|
|
70
|
+
readonly id: string;
|
|
71
|
+
/** `"activate"`, `"dismiss"` or `"timeout"`. */
|
|
72
|
+
readonly reason: ChangeReason;
|
|
73
|
+
}
|
|
74
|
+
export interface ToastPromiseOptions<T> {
|
|
75
|
+
/** Shown at once, with `data-pending`; no timer until the promise settles. */
|
|
76
|
+
readonly pending: ToastOptions;
|
|
77
|
+
/**
|
|
78
|
+
* Replaces the toast when the promise fulfils; omitted: the toast is dismissed. A function that
|
|
79
|
+
* throws dismisses the toast, and the returned promise rejects with what it threw.
|
|
80
|
+
*/
|
|
81
|
+
readonly success?: ToastOptions | ((value: T) => ToastOptions) | undefined;
|
|
82
|
+
/**
|
|
83
|
+
* Replaces the toast when the promise rejects; the returned promise then resolves with
|
|
84
|
+
* `undefined` (the toast is the handler). Omitted: the toast is dismissed and the returned
|
|
85
|
+
* promise rejects with the same reason. A function that throws dismisses the toast, and the
|
|
86
|
+
* returned promise rejects with what it threw.
|
|
87
|
+
*/
|
|
88
|
+
readonly error?: ToastOptions | ((reason: unknown) => ToastOptions) | undefined;
|
|
89
|
+
readonly id?: string | undefined;
|
|
90
|
+
}
|
|
91
|
+
/** The queue's input: `toast(options)` and its methods. */
|
|
92
|
+
export interface ToastManager {
|
|
93
|
+
/** Adds a toast (or updates the one with `options.id`) and returns its id. */
|
|
94
|
+
(options: ToastOptions): string;
|
|
95
|
+
readonly add: (options: ToastOptions) => string;
|
|
96
|
+
/**
|
|
97
|
+
* Shows `options.pending` at once and swaps to `success` / `error` when `promise` settles.
|
|
98
|
+
* Returns a promise that settles with `promise`, except that a rejection resolves with
|
|
99
|
+
* `undefined` when `error` was given.
|
|
100
|
+
*/
|
|
101
|
+
readonly promise: <T>(promise: PromiseLike<T>, options: ToastPromiseOptions<T>) => Promise<T | undefined>;
|
|
102
|
+
/** Changes a toast in place (same position, same element). Unknown or closed ids are ignored. */
|
|
103
|
+
readonly update: (id: string, options: Partial<ToastOptions>) => void;
|
|
104
|
+
/** Closes one toast, or every toast when `id` is omitted, with `reason: "dismiss"`. */
|
|
105
|
+
readonly dismiss: (id?: string) => void;
|
|
106
|
+
}
|
|
107
|
+
/**
|
|
108
|
+
* Creates an isolated queue: `<Toast.Viewport manager={m}>` renders it. For a second React root,
|
|
109
|
+
* a micro-frontend, or a test that wants a clean queue. `toast` is the default manager.
|
|
110
|
+
*/
|
|
111
|
+
export declare function createToastManager(): ToastManager;
|
|
112
|
+
/**
|
|
113
|
+
* The default toast manager: `toast({ title: "Saved" })` from anywhere on the client (action
|
|
114
|
+
* bodies, stores, effects). `<Toast.Viewport>` without `manager` renders it. On the server every
|
|
115
|
+
* call is a no-op.
|
|
116
|
+
*/
|
|
117
|
+
export declare const toast: ToastManager;
|
|
118
|
+
//#endregion
|
|
119
|
+
//# sourceMappingURL=toast.d.mts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"toast.d.mts","names":[],"sources":["../../src/toast/toast.ts"],"mappings":";;;;YAOY;;iBAGK;;WAEN,OAAO;;;;;;;WAOP,QAAQ,QAAQ,SAAS;;iBAGnB;;WAEN;WACA,QAAQ;WACR,cAAc;;WAEd;;;;;WAKA,WAAW;;;;;;WAMX;WACA,SAAS;;;;;;;WAOT,cAAc,QAAQ,SAAS;;;;;WAK/B,aAAa,OAAO;;WAEpB;;;iBAIM,kBAAkB;WACxB;;;;;WAKA;;WAEA;;;iBAIM,2BAA2B;;WAEjC;;;iBAIM,0BAA0B;WAChC;;WAEA,QAAQ;;iBAGF,oBAAoB;;WAE1B,SAAS;;;;;WAKT,UAAU,iBAAiB,OAAO,MAAM;;;;;;;WAOxC,QAAQ,iBAAiB,oBAAoB;WAC7C;;;iBAIM;;GAEd,SAAS;WACD,MAAM,SAAS;;;;;;WAMf,UAAU,GACjB,SAAS,YAAY,IACrB,SAAS,oBAAoB,OAC1B,QAAQ;;WAEJ,SAAS,YAAY,SAAS,QAAQ;;WAEtC,UAAU;;;;;;wBAsGL,sBAAsB;;;;;;qBAqIzB,OAAO"}
|
|
@@ -0,0 +1,153 @@
|
|
|
1
|
+
//#region src/toast/toast.ts
|
|
2
|
+
const internals = /* @__PURE__ */ new WeakMap();
|
|
3
|
+
/** The command channel of `manager` (library-internal). */
|
|
4
|
+
function toastManagerInternals(manager) {
|
|
5
|
+
const value = internals.get(manager);
|
|
6
|
+
if (value === void 0) throw new TypeError("Toast.Viewport: `manager` must come from `createToastManager()`.");
|
|
7
|
+
return value;
|
|
8
|
+
}
|
|
9
|
+
const isDev = process.env.NODE_ENV !== "production";
|
|
10
|
+
let warnedServer = false;
|
|
11
|
+
/** On the server every method is a no-op, never buffered (design 13, product-owner amendment). */
|
|
12
|
+
function onServer() {
|
|
13
|
+
if (typeof document !== "undefined") return false;
|
|
14
|
+
if (isDev && !warnedServer) {
|
|
15
|
+
warnedServer = true;
|
|
16
|
+
console.error("toast(): called on the server. Toasts are client-only; the call does nothing. Return data from the Server Function and call `toast()` on the client.");
|
|
17
|
+
}
|
|
18
|
+
return true;
|
|
19
|
+
}
|
|
20
|
+
function resolveOptions(options, value) {
|
|
21
|
+
return typeof options === "function" ? options(value) : options;
|
|
22
|
+
}
|
|
23
|
+
/**
|
|
24
|
+
* Creates an isolated queue: `<Toast.Viewport manager={m}>` renders it. For a second React root,
|
|
25
|
+
* a micro-frontend, or a test that wants a clean queue. `toast` is the default manager.
|
|
26
|
+
*/
|
|
27
|
+
function createToastManager() {
|
|
28
|
+
let listener = null;
|
|
29
|
+
let retained = [];
|
|
30
|
+
/** Commands issued before any Viewport subscribed (client only). */
|
|
31
|
+
let buffer = [];
|
|
32
|
+
/** Commands of the current tick, flushed together from one microtask. */
|
|
33
|
+
let batch = [];
|
|
34
|
+
let scheduled = false;
|
|
35
|
+
let nextId = 0;
|
|
36
|
+
const flush = () => {
|
|
37
|
+
scheduled = false;
|
|
38
|
+
const commands = batch;
|
|
39
|
+
batch = [];
|
|
40
|
+
if (commands.length === 0) return;
|
|
41
|
+
if (listener !== null) listener.apply(commands);
|
|
42
|
+
else buffer.push(...commands);
|
|
43
|
+
};
|
|
44
|
+
const dispatch = (command) => {
|
|
45
|
+
batch.push(command);
|
|
46
|
+
if (scheduled) return;
|
|
47
|
+
scheduled = true;
|
|
48
|
+
queueMicrotask(flush);
|
|
49
|
+
};
|
|
50
|
+
const generateId = () => `fsui-toast-${++nextId}`;
|
|
51
|
+
const add = (options) => {
|
|
52
|
+
const id = options.id ?? generateId();
|
|
53
|
+
if (onServer()) return id;
|
|
54
|
+
dispatch({
|
|
55
|
+
kind: "add",
|
|
56
|
+
id,
|
|
57
|
+
options,
|
|
58
|
+
pending: false
|
|
59
|
+
});
|
|
60
|
+
return id;
|
|
61
|
+
};
|
|
62
|
+
const update = (id, options) => {
|
|
63
|
+
if (onServer()) return;
|
|
64
|
+
dispatch({
|
|
65
|
+
kind: "update",
|
|
66
|
+
id,
|
|
67
|
+
options,
|
|
68
|
+
settle: false
|
|
69
|
+
});
|
|
70
|
+
};
|
|
71
|
+
const dismiss = (id) => {
|
|
72
|
+
if (onServer()) return;
|
|
73
|
+
dispatch({
|
|
74
|
+
kind: "dismiss",
|
|
75
|
+
id
|
|
76
|
+
});
|
|
77
|
+
};
|
|
78
|
+
const promise = (input, options) => {
|
|
79
|
+
const { success, error } = options;
|
|
80
|
+
if (onServer()) return Promise.resolve(input).then((value) => value, (reason) => {
|
|
81
|
+
if (error !== void 0) return void 0;
|
|
82
|
+
throw reason;
|
|
83
|
+
});
|
|
84
|
+
const id = options.id ?? options.pending.id ?? generateId();
|
|
85
|
+
dispatch({
|
|
86
|
+
kind: "add",
|
|
87
|
+
id,
|
|
88
|
+
options: options.pending,
|
|
89
|
+
pending: true
|
|
90
|
+
});
|
|
91
|
+
const settle = (options, value) => {
|
|
92
|
+
let resolved;
|
|
93
|
+
try {
|
|
94
|
+
resolved = resolveOptions(options, value);
|
|
95
|
+
} catch (thrown) {
|
|
96
|
+
dismiss(id);
|
|
97
|
+
throw thrown;
|
|
98
|
+
}
|
|
99
|
+
dispatch({
|
|
100
|
+
kind: "update",
|
|
101
|
+
id,
|
|
102
|
+
options: resolved,
|
|
103
|
+
settle: true
|
|
104
|
+
});
|
|
105
|
+
};
|
|
106
|
+
return Promise.resolve(input).then((value) => {
|
|
107
|
+
if (success === void 0) dismiss(id);
|
|
108
|
+
else settle(success, value);
|
|
109
|
+
return value;
|
|
110
|
+
}, (reason) => {
|
|
111
|
+
if (error === void 0) {
|
|
112
|
+
dismiss(id);
|
|
113
|
+
throw reason;
|
|
114
|
+
}
|
|
115
|
+
settle(error, reason);
|
|
116
|
+
});
|
|
117
|
+
};
|
|
118
|
+
const manager = Object.assign((options) => add(options), {
|
|
119
|
+
add,
|
|
120
|
+
promise,
|
|
121
|
+
update,
|
|
122
|
+
dismiss
|
|
123
|
+
});
|
|
124
|
+
internals.set(manager, {
|
|
125
|
+
subscribe: (next) => {
|
|
126
|
+
if (listener !== null && listener !== next) return null;
|
|
127
|
+
listener = next;
|
|
128
|
+
const handedOver = retained;
|
|
129
|
+
retained = [];
|
|
130
|
+
if (handedOver.length > 0) next.restore(handedOver);
|
|
131
|
+
const pending = buffer;
|
|
132
|
+
buffer = [];
|
|
133
|
+
if (pending.length > 0) next.apply(pending);
|
|
134
|
+
return () => {
|
|
135
|
+
if (listener !== next) return;
|
|
136
|
+
retained = next.retain();
|
|
137
|
+
listener = null;
|
|
138
|
+
};
|
|
139
|
+
},
|
|
140
|
+
isTaken: (candidate) => listener !== null && listener !== candidate
|
|
141
|
+
});
|
|
142
|
+
return manager;
|
|
143
|
+
}
|
|
144
|
+
/**
|
|
145
|
+
* The default toast manager: `toast({ title: "Saved" })` from anywhere on the client (action
|
|
146
|
+
* bodies, stores, effects). `<Toast.Viewport>` without `manager` renders it. On the server every
|
|
147
|
+
* call is a no-op.
|
|
148
|
+
*/
|
|
149
|
+
const toast = createToastManager();
|
|
150
|
+
//#endregion
|
|
151
|
+
export { createToastManager, toast, toastManagerInternals };
|
|
152
|
+
|
|
153
|
+
//# sourceMappingURL=toast.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"toast.mjs","names":[],"sources":["../../src/toast/toast.ts"],"sourcesContent":["// No \"use client\" directive: the manager uses no React and no DOM API beyond `queueMicrotask`,\n// so a Server Component may import `toast` (for its type, or to pass nothing) without a client\n// reference (design/components/toast.md 13). The parts are client modules.\nimport type { ReactNode } from \"react\";\nimport type { Action, ActionDetails, ChangeReason } from \"../internal/types.js\";\n\n/** Announcement priority and role of a toast. */\nexport type ToastPriority = \"normal\" | \"high\";\n\n/** The `action` of a toast: what `Toast.Action` renders and runs by default. */\nexport interface ToastActionOptions {\n /** The Action button's content. */\n readonly label: ReactNode;\n /**\n * Runs in the Action part's own transition (`data-pending` on the Action and the Root, the\n * timer paused). The toast closes with `reason: \"activate\"` when it resolves; a rejection\n * keeps the toast and reaches the nearest Error Boundary above the Viewport. A caught error\n * resolves the action and therefore closes the toast: rethrow after `toast.update` to keep it.\n */\n readonly action: Action<[details: ToastActionDetails]>;\n}\n\nexport interface ToastOptions {\n /** Explicit id: an existing one updates that toast. Default: generated. */\n readonly id?: string | undefined;\n readonly title?: ReactNode;\n readonly description?: ReactNode;\n /** Free-form, rendered as `data-type` on the Root (`\"success\"`, `\"error\"`, ...). */\n readonly type?: string | undefined;\n /**\n * Announcement priority and role: `\"high\"` announces with `ariaNotify` priority `\"high\"` and\n * renders the Root as an `alertdialog`. Default `\"normal\"`.\n */\n readonly priority?: ToastPriority | undefined;\n /**\n * Milliseconds before the toast closes with `reason: \"timeout\"`; `null` never. Default: the\n * Viewport's `duration`, or `null` (persistent) when the toast has an `action`. A timed action\n * toast must keep its action reachable elsewhere too (WCAG 2.2.1).\n */\n readonly duration?: number | null | undefined;\n readonly action?: ToastActionOptions | undefined;\n /**\n * Called once when the toast leaves, with the reason: `\"activate\"` (the Action part settled),\n * `\"dismiss\"` (Close, Escape, `toast.dismiss()`, a promise toast with no message for its\n * outcome) or `\"timeout\"`. Runs in the Viewport's transition; a rejection reaches the boundary\n * above the Viewport. Not called when the Viewport unmounts.\n */\n readonly closeAction?: Action<[details: ToastCloseDetails]> | undefined;\n /**\n * The text `document.ariaNotify()` speaks for this toast; `null` announces nothing. Default:\n * the Viewport's `announce`, else the text of the toast's Title and Description.\n */\n readonly announce?: ((toast: ToastItem) => string | null) | undefined;\n /** Anything the Viewport's children want to render. */\n readonly data?: unknown;\n}\n\n/** A toast in the queue, as the Viewport's `children(state)` receives it. */\nexport interface ToastItem extends ToastOptions {\n readonly id: string;\n /**\n * The promise given to `toast.promise` is unsettled, or an action dispatched through this\n * toast's Action part is in flight.\n */\n readonly pending: boolean;\n /** Closed; kept mounted (and `inert`) for its exit animation. */\n readonly ending: boolean;\n}\n\n/** Details passed to a toast's Action. */\nexport interface ToastActionDetails extends ActionDetails {\n /** The toast's id, so an inline action can `toast.update` or `toast.dismiss` its own toast. */\n readonly id: string;\n}\n\n/** Details passed to a toast's `closeAction`. */\nexport interface ToastCloseDetails extends ActionDetails {\n readonly id: string;\n /** `\"activate\"`, `\"dismiss\"` or `\"timeout\"`. */\n readonly reason: ChangeReason;\n}\n\nexport interface ToastPromiseOptions<T> {\n /** Shown at once, with `data-pending`; no timer until the promise settles. */\n readonly pending: ToastOptions;\n /**\n * Replaces the toast when the promise fulfils; omitted: the toast is dismissed. A function that\n * throws dismisses the toast, and the returned promise rejects with what it threw.\n */\n readonly success?: ToastOptions | ((value: T) => ToastOptions) | undefined;\n /**\n * Replaces the toast when the promise rejects; the returned promise then resolves with\n * `undefined` (the toast is the handler). Omitted: the toast is dismissed and the returned\n * promise rejects with the same reason. A function that throws dismisses the toast, and the\n * returned promise rejects with what it threw.\n */\n readonly error?: ToastOptions | ((reason: unknown) => ToastOptions) | undefined;\n readonly id?: string | undefined;\n}\n\n/** The queue's input: `toast(options)` and its methods. */\nexport interface ToastManager {\n /** Adds a toast (or updates the one with `options.id`) and returns its id. */\n (options: ToastOptions): string;\n readonly add: (options: ToastOptions) => string;\n /**\n * Shows `options.pending` at once and swaps to `success` / `error` when `promise` settles.\n * Returns a promise that settles with `promise`, except that a rejection resolves with\n * `undefined` when `error` was given.\n */\n readonly promise: <T>(\n promise: PromiseLike<T>,\n options: ToastPromiseOptions<T>,\n ) => Promise<T | undefined>;\n /** Changes a toast in place (same position, same element). Unknown or closed ids are ignored. */\n readonly update: (id: string, options: Partial<ToastOptions>) => void;\n /** Closes one toast, or every toast when `id` is omitted, with `reason: \"dismiss\"`. */\n readonly dismiss: (id?: string) => void;\n}\n\n// --- Internal: the command channel between a manager and its Viewport --------------------------\n\n/** A queue mutation, applied by the subscribed Viewport (or buffered until one subscribes). */\nexport type ToastCommand =\n | {\n readonly kind: \"add\";\n readonly id: string;\n readonly options: ToastOptions;\n /** Added by `toast.promise`: pending until the promise settles. */\n readonly pending: boolean;\n }\n | {\n readonly kind: \"update\";\n readonly id: string;\n readonly options: Partial<ToastOptions>;\n /** A promise settle: the options replace the toast's (keeping its id), pending ends. */\n readonly settle: boolean;\n }\n | { readonly kind: \"dismiss\"; readonly id: string | undefined };\n\n/** What a Viewport hands its manager. */\nexport interface ToastListener {\n /**\n * Takes over the toasts a previous Viewport left behind (called on subscribe, before the\n * buffer is applied).\n */\n readonly restore: (toasts: readonly RetainedToast[]) => void;\n /** Applies a batch of commands, in dispatch order. */\n readonly apply: (commands: readonly ToastCommand[]) => void;\n /** The toasts to keep when this Viewport unsubscribes. */\n readonly retain: () => readonly RetainedToast[];\n}\n\n/** A retained queue entry: what a Viewport leaves behind when it unmounts (design 14 item 11). */\nexport interface RetainedToast {\n readonly options: ToastOptions & { readonly id: string };\n readonly pending: boolean;\n /**\n * The text last announced for it (`null`: silenced), so the next Viewport does not announce\n * it again; `undefined` when nothing was announced yet.\n */\n readonly announced?: string | null | undefined;\n}\n\nexport interface ToastManagerInternals {\n /**\n * Sets the listener, hands it the retained toasts and drains the buffer into it. Returns the\n * unsubscribe function, or `null` (changing nothing) when a different listener is subscribed:\n * the second-Viewport case.\n */\n readonly subscribe: (listener: ToastListener) => (() => void) | null;\n /** Whether a listener other than `listener` is subscribed (read during render). */\n readonly isTaken: (listener: ToastListener) => boolean;\n}\n\nconst internals = new WeakMap<ToastManager, ToastManagerInternals>();\n\n/** The command channel of `manager` (library-internal). */\nexport function toastManagerInternals(manager: ToastManager): ToastManagerInternals {\n const value = internals.get(manager);\n if (value === undefined) {\n throw new TypeError(\"Toast.Viewport: `manager` must come from `createToastManager()`.\");\n }\n return value;\n}\n\nconst isDev = process.env.NODE_ENV !== \"production\";\n\nlet warnedServer = false;\n\n/** On the server every method is a no-op, never buffered (design 13, product-owner amendment). */\nfunction onServer(): boolean {\n if (typeof document !== \"undefined\") return false;\n if (isDev && !warnedServer) {\n warnedServer = true;\n console.error(\n \"toast(): called on the server. Toasts are client-only; the call does nothing. Return data \" +\n \"from the Server Function and call `toast()` on the client.\",\n );\n }\n return true;\n}\n\n/** Resets the once-per-process server warning (tests only). */\nexport function resetToastServerWarning(): void {\n warnedServer = false;\n}\n\nfunction resolveOptions<V>(\n options: ToastOptions | ((value: V) => ToastOptions),\n value: V,\n): ToastOptions {\n return typeof options === \"function\" ? options(value) : options;\n}\n\n/**\n * Creates an isolated queue: `<Toast.Viewport manager={m}>` renders it. For a second React root,\n * a micro-frontend, or a test that wants a clean queue. `toast` is the default manager.\n */\nexport function createToastManager(): ToastManager {\n let listener: ToastListener | null = null;\n let retained: readonly RetainedToast[] = [];\n /** Commands issued before any Viewport subscribed (client only). */\n let buffer: ToastCommand[] = [];\n /** Commands of the current tick, flushed together from one microtask. */\n let batch: ToastCommand[] = [];\n let scheduled = false;\n let nextId = 0;\n\n const flush = (): void => {\n scheduled = false;\n const commands = batch;\n batch = [];\n if (commands.length === 0) return;\n if (listener !== null) listener.apply(commands);\n else buffer.push(...commands);\n };\n\n // A microtask, not a synchronous dispatch: a call made inside a `startTransition` scope (an\n // action's sync prefix) is no longer inside it when the microtask runs, so the queue update is\n // urgent and shows at once even while the action is pending (design 3.4).\n const dispatch = (command: ToastCommand): void => {\n batch.push(command);\n if (scheduled) return;\n scheduled = true;\n queueMicrotask(flush);\n };\n\n const generateId = (): string => `fsui-toast-${++nextId}`;\n\n const add = (options: ToastOptions): string => {\n const id = options.id ?? generateId();\n if (onServer()) return id;\n dispatch({ kind: \"add\", id, options, pending: false });\n return id;\n };\n\n const update = (id: string, options: Partial<ToastOptions>): void => {\n if (onServer()) return;\n dispatch({ kind: \"update\", id, options, settle: false });\n };\n\n const dismiss = (id?: string): void => {\n if (onServer()) return;\n dispatch({ kind: \"dismiss\", id });\n };\n\n const promise = <T>(\n input: PromiseLike<T>,\n options: ToastPromiseOptions<T>,\n ): Promise<T | undefined> => {\n const { success, error } = options;\n if (onServer()) {\n return Promise.resolve(input).then(\n (value) => value,\n (reason: unknown) => {\n if (error !== undefined) return undefined;\n throw reason;\n },\n );\n }\n const id = options.id ?? options.pending.id ?? generateId();\n dispatch({ kind: \"add\", id, options: options.pending, pending: true });\n // The returned promise is built from the same `then`, so it settles in the same tick as the\n // toast's update and an awaiting action prop is pending exactly as long as the toast.\n // A `success` / `error` function that throws dismisses the toast (it must not stay pending\n // forever) and the returned promise rejects with what it threw: the operation's outcome is\n // known, but its message is not, and the `error` message would misreport a fulfilment.\n const settle = <V>(options: ToastOptions | ((value: V) => ToastOptions), value: V): void => {\n let resolved: ToastOptions;\n try {\n resolved = resolveOptions(options, value);\n } catch (thrown) {\n dismiss(id);\n throw thrown;\n }\n dispatch({ kind: \"update\", id, options: resolved, settle: true });\n };\n return Promise.resolve(input).then(\n (value) => {\n if (success === undefined) dismiss(id);\n else settle(success, value);\n return value;\n },\n (reason: unknown) => {\n if (error === undefined) {\n dismiss(id);\n throw reason;\n }\n settle(error, reason);\n return undefined;\n },\n );\n };\n\n const manager = Object.assign((options: ToastOptions) => add(options), {\n add,\n promise,\n update,\n dismiss,\n }) as ToastManager;\n\n internals.set(manager, {\n subscribe: (next) => {\n if (listener !== null && listener !== next) return null;\n listener = next;\n const handedOver = retained;\n retained = [];\n if (handedOver.length > 0) next.restore(handedOver);\n // Commands issued before this Viewport subscribed, in order.\n const pending = buffer;\n buffer = [];\n if (pending.length > 0) next.apply(pending);\n return () => {\n if (listener !== next) return;\n // The queue survives the Viewport (a route-level Viewport remounting on navigation does\n // not lose an Undo); timers and `closeAction`s do not.\n retained = next.retain();\n listener = null;\n };\n },\n isTaken: (candidate) => listener !== null && listener !== candidate,\n });\n\n return manager;\n}\n\n/**\n * The default toast manager: `toast({ title: \"Saved\" })` from anywhere on the client (action\n * bodies, stores, effects). `<Toast.Viewport>` without `manager` renders it. On the server every\n * call is a no-op.\n */\nexport const toast: ToastManager = createToastManager();\n"],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@funstack/ui",
|
|
3
|
-
"version": "0.1.0-alpha.
|
|
3
|
+
"version": "0.1.0-alpha.3",
|
|
4
4
|
"description": "Headless UI library for React — the first Async React-native UI library",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"components",
|
|
@@ -32,12 +32,18 @@
|
|
|
32
32
|
"./accordion": "./dist/accordion/index.mjs",
|
|
33
33
|
"./checkbox": "./dist/checkbox/index.mjs",
|
|
34
34
|
"./checkbox-group": "./dist/checkbox-group/index.mjs",
|
|
35
|
+
"./combobox": "./dist/combobox/index.mjs",
|
|
35
36
|
"./dialog": "./dist/dialog/index.mjs",
|
|
37
|
+
"./field": "./dist/field/index.mjs",
|
|
38
|
+
"./form": "./dist/form/index.mjs",
|
|
36
39
|
"./menu": "./dist/menu/index.mjs",
|
|
37
40
|
"./popover": "./dist/popover/index.mjs",
|
|
38
41
|
"./radio-group": "./dist/radio-group/index.mjs",
|
|
42
|
+
"./select": "./dist/select/index.mjs",
|
|
43
|
+
"./slider": "./dist/slider/index.mjs",
|
|
39
44
|
"./switch": "./dist/switch/index.mjs",
|
|
40
45
|
"./tabs": "./dist/tabs/index.mjs",
|
|
46
|
+
"./toast": "./dist/toast/index.mjs",
|
|
41
47
|
"./toggle": "./dist/toggle/index.mjs",
|
|
42
48
|
"./tooltip": "./dist/tooltip/index.mjs",
|
|
43
49
|
"./package.json": "./package.json"
|