@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.
Files changed (288) hide show
  1. package/dist/accordion/accordion-context.mjs.map +1 -1
  2. package/dist/accordion/accordion-item.mjs +18 -4
  3. package/dist/accordion/accordion-item.mjs.map +1 -1
  4. package/dist/accordion/accordion-panel.mjs +2 -3
  5. package/dist/accordion/accordion-panel.mjs.map +1 -1
  6. package/dist/accordion/accordion-root.d.mts.map +1 -1
  7. package/dist/accordion/accordion-root.mjs +32 -14
  8. package/dist/accordion/accordion-root.mjs.map +1 -1
  9. package/dist/accordion/accordion-trigger.mjs +4 -5
  10. package/dist/accordion/accordion-trigger.mjs.map +1 -1
  11. package/dist/checkbox/checkbox.d.mts +5 -3
  12. package/dist/checkbox/checkbox.d.mts.map +1 -1
  13. package/dist/checkbox/checkbox.mjs +25 -9
  14. package/dist/checkbox/checkbox.mjs.map +1 -1
  15. package/dist/checkbox-group/checkbox-group-context.mjs.map +1 -1
  16. package/dist/checkbox-group/checkbox-group-item.d.mts +2 -1
  17. package/dist/checkbox-group/checkbox-group-item.d.mts.map +1 -1
  18. package/dist/checkbox-group/checkbox-group-item.mjs +3 -1
  19. package/dist/checkbox-group/checkbox-group-item.mjs.map +1 -1
  20. package/dist/checkbox-group/checkbox-group-root.d.mts +5 -2
  21. package/dist/checkbox-group/checkbox-group-root.d.mts.map +1 -1
  22. package/dist/checkbox-group/checkbox-group-root.mjs +24 -5
  23. package/dist/checkbox-group/checkbox-group-root.mjs.map +1 -1
  24. package/dist/combobox/combobox-clear.d.mts +34 -0
  25. package/dist/combobox/combobox-clear.d.mts.map +1 -0
  26. package/dist/combobox/combobox-clear.mjs +58 -0
  27. package/dist/combobox/combobox-clear.mjs.map +1 -0
  28. package/dist/combobox/combobox-context.mjs +23 -0
  29. package/dist/combobox/combobox-context.mjs.map +1 -0
  30. package/dist/combobox/combobox-group.d.mts +72 -0
  31. package/dist/combobox/combobox-group.d.mts.map +1 -0
  32. package/dist/combobox/combobox-group.mjs +104 -0
  33. package/dist/combobox/combobox-group.mjs.map +1 -0
  34. package/dist/combobox/combobox-input.d.mts +42 -0
  35. package/dist/combobox/combobox-input.d.mts.map +1 -0
  36. package/dist/combobox/combobox-input.mjs +102 -0
  37. package/dist/combobox/combobox-input.mjs.map +1 -0
  38. package/dist/combobox/combobox-label.d.mts +22 -0
  39. package/dist/combobox/combobox-label.d.mts.map +1 -0
  40. package/dist/combobox/combobox-label.mjs +32 -0
  41. package/dist/combobox/combobox-label.mjs.map +1 -0
  42. package/dist/combobox/combobox-list.d.mts +37 -0
  43. package/dist/combobox/combobox-list.d.mts.map +1 -0
  44. package/dist/combobox/combobox-list.mjs +70 -0
  45. package/dist/combobox/combobox-list.mjs.map +1 -0
  46. package/dist/combobox/combobox-option.d.mts +45 -0
  47. package/dist/combobox/combobox-option.d.mts.map +1 -0
  48. package/dist/combobox/combobox-option.mjs +88 -0
  49. package/dist/combobox/combobox-option.mjs.map +1 -0
  50. package/dist/combobox/combobox-popup.d.mts +49 -0
  51. package/dist/combobox/combobox-popup.d.mts.map +1 -0
  52. package/dist/combobox/combobox-popup.mjs +86 -0
  53. package/dist/combobox/combobox-popup.mjs.map +1 -0
  54. package/dist/combobox/combobox-root.d.mts +135 -0
  55. package/dist/combobox/combobox-root.d.mts.map +1 -0
  56. package/dist/combobox/combobox-root.mjs +523 -0
  57. package/dist/combobox/combobox-root.mjs.map +1 -0
  58. package/dist/combobox/combobox-trigger.d.mts +36 -0
  59. package/dist/combobox/combobox-trigger.d.mts.map +1 -0
  60. package/dist/combobox/combobox-trigger.mjs +92 -0
  61. package/dist/combobox/combobox-trigger.mjs.map +1 -0
  62. package/dist/combobox/index.d.mts +36 -0
  63. package/dist/combobox/index.d.mts.map +1 -0
  64. package/dist/combobox/index.mjs +36 -0
  65. package/dist/combobox/index.mjs.map +1 -0
  66. package/dist/dialog/dialog-close.d.mts +17 -10
  67. package/dist/dialog/dialog-close.d.mts.map +1 -1
  68. package/dist/dialog/dialog-close.mjs +31 -20
  69. package/dist/dialog/dialog-close.mjs.map +1 -1
  70. package/dist/dialog/dialog-context.mjs.map +1 -1
  71. package/dist/dialog/dialog-form.d.mts +12 -5
  72. package/dist/dialog/dialog-form.d.mts.map +1 -1
  73. package/dist/dialog/dialog-form.mjs +16 -28
  74. package/dist/dialog/dialog-form.mjs.map +1 -1
  75. package/dist/dialog/dialog-popup.d.mts +10 -4
  76. package/dist/dialog/dialog-popup.d.mts.map +1 -1
  77. package/dist/dialog/dialog-popup.mjs +106 -41
  78. package/dist/dialog/dialog-popup.mjs.map +1 -1
  79. package/dist/dialog/dialog-root.d.mts +14 -8
  80. package/dist/dialog/dialog-root.d.mts.map +1 -1
  81. package/dist/dialog/dialog-root.mjs +66 -11
  82. package/dist/dialog/dialog-root.mjs.map +1 -1
  83. package/dist/dialog/dialog-trigger.d.mts +3 -2
  84. package/dist/dialog/dialog-trigger.d.mts.map +1 -1
  85. package/dist/dialog/dialog-trigger.mjs.map +1 -1
  86. package/dist/dialog/index.d.mts +4 -2
  87. package/dist/dialog/index.d.mts.map +1 -1
  88. package/dist/dialog/index.mjs +4 -2
  89. package/dist/dialog/index.mjs.map +1 -1
  90. package/dist/field/field-control.d.mts +33 -0
  91. package/dist/field/field-control.d.mts.map +1 -0
  92. package/dist/field/field-control.mjs +68 -0
  93. package/dist/field/field-control.mjs.map +1 -0
  94. package/dist/field/field-description.d.mts +24 -0
  95. package/dist/field/field-description.d.mts.map +1 -0
  96. package/dist/field/field-description.mjs +34 -0
  97. package/dist/field/field-description.mjs.map +1 -0
  98. package/dist/field/field-error.d.mts +30 -0
  99. package/dist/field/field-error.d.mts.map +1 -0
  100. package/dist/field/field-error.mjs +42 -0
  101. package/dist/field/field-error.mjs.map +1 -0
  102. package/dist/field/field-label.d.mts +25 -0
  103. package/dist/field/field-label.d.mts.map +1 -0
  104. package/dist/field/field-label.mjs +46 -0
  105. package/dist/field/field-label.mjs.map +1 -0
  106. package/dist/field/field-root.d.mts +53 -0
  107. package/dist/field/field-root.d.mts.map +1 -0
  108. package/dist/field/field-root.mjs +317 -0
  109. package/dist/field/field-root.mjs.map +1 -0
  110. package/dist/field/index.d.mts +18 -0
  111. package/dist/field/index.d.mts.map +1 -0
  112. package/dist/field/index.mjs +17 -0
  113. package/dist/field/index.mjs.map +1 -0
  114. package/dist/form/form-root.d.mts +58 -0
  115. package/dist/form/form-root.d.mts.map +1 -0
  116. package/dist/form/form-root.mjs +146 -0
  117. package/dist/form/form-root.mjs.map +1 -0
  118. package/dist/form/form-submit.d.mts +31 -0
  119. package/dist/form/form-submit.d.mts.map +1 -0
  120. package/dist/form/form-submit.mjs +52 -0
  121. package/dist/form/form-submit.mjs.map +1 -0
  122. package/dist/form/index.d.mts +11 -0
  123. package/dist/form/index.d.mts.map +1 -0
  124. package/dist/form/index.mjs +11 -0
  125. package/dist/form/index.mjs.map +1 -0
  126. package/dist/index.d.mts +46 -2
  127. package/dist/index.mjs +8 -1
  128. package/dist/internal/content-focus-probe.mjs +42 -0
  129. package/dist/internal/content-focus-probe.mjs.map +1 -0
  130. package/dist/internal/field-context.d.mts +19 -0
  131. package/dist/internal/field-context.d.mts.map +1 -0
  132. package/dist/internal/field-context.mjs +14 -0
  133. package/dist/internal/field-context.mjs.map +1 -0
  134. package/dist/internal/focus-first-invalid.mjs +67 -0
  135. package/dist/internal/focus-first-invalid.mjs.map +1 -0
  136. package/dist/internal/form-status-watcher.mjs +31 -0
  137. package/dist/internal/form-status-watcher.mjs.map +1 -0
  138. package/dist/internal/modal-registry.mjs +39 -0
  139. package/dist/internal/modal-registry.mjs.map +1 -0
  140. package/dist/internal/platform-attributes.d.mts +12 -1
  141. package/dist/internal/platform-attributes.d.mts.map +1 -1
  142. package/dist/internal/types.d.mts +11 -1
  143. package/dist/internal/types.d.mts.map +1 -1
  144. package/dist/internal/use-controllable-action.mjs +35 -13
  145. package/dist/internal/use-controllable-action.mjs.map +1 -1
  146. package/dist/internal/use-field-control.mjs +107 -0
  147. package/dist/internal/use-field-control.mjs.map +1 -0
  148. package/dist/internal/use-form-owner-event.mjs +44 -0
  149. package/dist/internal/use-form-owner-event.mjs.map +1 -0
  150. package/dist/internal/use-form-reset.mjs +12 -31
  151. package/dist/internal/use-form-reset.mjs.map +1 -1
  152. package/dist/internal/use-hydrated-mount.mjs +21 -0
  153. package/dist/internal/use-hydrated-mount.mjs.map +1 -0
  154. package/dist/internal/use-latest.mjs +32 -0
  155. package/dist/internal/use-latest.mjs.map +1 -0
  156. package/dist/internal/use-modal-registration.mjs +47 -0
  157. package/dist/internal/use-modal-registration.mjs.map +1 -0
  158. package/dist/internal/use-native-group-reconciliation.mjs +6 -5
  159. package/dist/internal/use-native-group-reconciliation.mjs.map +1 -1
  160. package/dist/internal/use-native-select-value.mjs +184 -0
  161. package/dist/internal/use-native-select-value.mjs.map +1 -0
  162. package/dist/internal/use-popover-popup.mjs +8 -22
  163. package/dist/internal/use-popover-popup.mjs.map +1 -1
  164. package/dist/internal/use-popover-root.mjs +58 -23
  165. package/dist/internal/use-popover-root.mjs.map +1 -1
  166. package/dist/internal/validity.d.mts +24 -0
  167. package/dist/internal/validity.d.mts.map +1 -0
  168. package/dist/internal/validity.mjs +108 -0
  169. package/dist/internal/validity.mjs.map +1 -0
  170. package/dist/menu/menu-group.d.mts.map +1 -1
  171. package/dist/menu/menu-group.mjs +3 -2
  172. package/dist/menu/menu-group.mjs.map +1 -1
  173. package/dist/menu/menu-radio-group.mjs +9 -4
  174. package/dist/menu/menu-radio-group.mjs.map +1 -1
  175. package/dist/menu/menu-root.mjs.map +1 -1
  176. package/dist/radio-group/radio-group-root.d.mts +4 -2
  177. package/dist/radio-group/radio-group-root.d.mts.map +1 -1
  178. package/dist/radio-group/radio-group-root.mjs +34 -8
  179. package/dist/radio-group/radio-group-root.mjs.map +1 -1
  180. package/dist/select/index.d.mts +30 -0
  181. package/dist/select/index.d.mts.map +1 -0
  182. package/dist/select/index.mjs +30 -0
  183. package/dist/select/index.mjs.map +1 -0
  184. package/dist/select/select-context.mjs +13 -0
  185. package/dist/select/select-context.mjs.map +1 -0
  186. package/dist/select/select-dom.mjs +44 -0
  187. package/dist/select/select-dom.mjs.map +1 -0
  188. package/dist/select/select-group-label.d.mts +22 -0
  189. package/dist/select/select-group-label.d.mts.map +1 -0
  190. package/dist/select/select-group-label.mjs +39 -0
  191. package/dist/select/select-group-label.mjs.map +1 -0
  192. package/dist/select/select-group.d.mts +28 -0
  193. package/dist/select/select-group.d.mts.map +1 -0
  194. package/dist/select/select-group.mjs +47 -0
  195. package/dist/select/select-group.mjs.map +1 -0
  196. package/dist/select/select-option.d.mts +42 -0
  197. package/dist/select/select-option.d.mts.map +1 -0
  198. package/dist/select/select-option.mjs +65 -0
  199. package/dist/select/select-option.mjs.map +1 -0
  200. package/dist/select/select-root.d.mts +68 -0
  201. package/dist/select/select-root.d.mts.map +1 -0
  202. package/dist/select/select-root.mjs +114 -0
  203. package/dist/select/select-root.mjs.map +1 -0
  204. package/dist/select/select-separator.d.mts +20 -0
  205. package/dist/select/select-separator.d.mts.map +1 -0
  206. package/dist/select/select-separator.mjs +30 -0
  207. package/dist/select/select-separator.mjs.map +1 -0
  208. package/dist/select/select-trigger.d.mts +29 -0
  209. package/dist/select/select-trigger.d.mts.map +1 -0
  210. package/dist/select/select-trigger.mjs +48 -0
  211. package/dist/select/select-trigger.mjs.map +1 -0
  212. package/dist/select/select-value.d.mts +31 -0
  213. package/dist/select/select-value.d.mts.map +1 -0
  214. package/dist/select/select-value.mjs +101 -0
  215. package/dist/select/select-value.mjs.map +1 -0
  216. package/dist/slider/index.d.mts +32 -0
  217. package/dist/slider/index.d.mts.map +1 -0
  218. package/dist/slider/index.mjs +27 -0
  219. package/dist/slider/index.mjs.map +1 -0
  220. package/dist/slider/slider-arithmetic.mjs +120 -0
  221. package/dist/slider/slider-arithmetic.mjs.map +1 -0
  222. package/dist/slider/slider-context.mjs +13 -0
  223. package/dist/slider/slider-context.mjs.map +1 -0
  224. package/dist/slider/slider-indicator.d.mts +22 -0
  225. package/dist/slider/slider-indicator.d.mts.map +1 -0
  226. package/dist/slider/slider-indicator.mjs +36 -0
  227. package/dist/slider/slider-indicator.mjs.map +1 -0
  228. package/dist/slider/slider-label.d.mts +23 -0
  229. package/dist/slider/slider-label.d.mts.map +1 -0
  230. package/dist/slider/slider-label.mjs +44 -0
  231. package/dist/slider/slider-label.mjs.map +1 -0
  232. package/dist/slider/slider-root.d.mts +111 -0
  233. package/dist/slider/slider-root.d.mts.map +1 -0
  234. package/dist/slider/slider-root.mjs +582 -0
  235. package/dist/slider/slider-root.mjs.map +1 -0
  236. package/dist/slider/slider-thumb.d.mts +58 -0
  237. package/dist/slider/slider-thumb.d.mts.map +1 -0
  238. package/dist/slider/slider-thumb.mjs +145 -0
  239. package/dist/slider/slider-thumb.mjs.map +1 -0
  240. package/dist/slider/slider-track.d.mts +29 -0
  241. package/dist/slider/slider-track.d.mts.map +1 -0
  242. package/dist/slider/slider-track.mjs +54 -0
  243. package/dist/slider/slider-track.mjs.map +1 -0
  244. package/dist/slider/slider-value.d.mts +34 -0
  245. package/dist/slider/slider-value.d.mts.map +1 -0
  246. package/dist/slider/slider-value.mjs +38 -0
  247. package/dist/slider/slider-value.mjs.map +1 -0
  248. package/dist/switch/switch.d.mts +5 -3
  249. package/dist/switch/switch.d.mts.map +1 -1
  250. package/dist/switch/switch.mjs +24 -8
  251. package/dist/switch/switch.mjs.map +1 -1
  252. package/dist/tabs/tabs-root.mjs +13 -3
  253. package/dist/tabs/tabs-root.mjs.map +1 -1
  254. package/dist/toast/index.d.mts +25 -0
  255. package/dist/toast/index.d.mts.map +1 -0
  256. package/dist/toast/index.mjs +26 -0
  257. package/dist/toast/index.mjs.map +1 -0
  258. package/dist/toast/toast-action.d.mts +39 -0
  259. package/dist/toast/toast-action.d.mts.map +1 -0
  260. package/dist/toast/toast-action.mjs +64 -0
  261. package/dist/toast/toast-action.mjs.map +1 -0
  262. package/dist/toast/toast-close.d.mts +25 -0
  263. package/dist/toast/toast-close.d.mts.map +1 -0
  264. package/dist/toast/toast-close.mjs +46 -0
  265. package/dist/toast/toast-close.mjs.map +1 -0
  266. package/dist/toast/toast-context.mjs +19 -0
  267. package/dist/toast/toast-context.mjs.map +1 -0
  268. package/dist/toast/toast-description.d.mts +24 -0
  269. package/dist/toast/toast-description.d.mts.map +1 -0
  270. package/dist/toast/toast-description.mjs +35 -0
  271. package/dist/toast/toast-description.mjs.map +1 -0
  272. package/dist/toast/toast-root.d.mts +43 -0
  273. package/dist/toast/toast-root.d.mts.map +1 -0
  274. package/dist/toast/toast-root.mjs +204 -0
  275. package/dist/toast/toast-root.mjs.map +1 -0
  276. package/dist/toast/toast-title.d.mts +24 -0
  277. package/dist/toast/toast-title.d.mts.map +1 -0
  278. package/dist/toast/toast-title.mjs +33 -0
  279. package/dist/toast/toast-title.mjs.map +1 -0
  280. package/dist/toast/toast-viewport.d.mts +52 -0
  281. package/dist/toast/toast-viewport.d.mts.map +1 -0
  282. package/dist/toast/toast-viewport.mjs +621 -0
  283. package/dist/toast/toast-viewport.mjs.map +1 -0
  284. package/dist/toast/toast.d.mts +119 -0
  285. package/dist/toast/toast.d.mts.map +1 -0
  286. package/dist/toast/toast.mjs +153 -0
  287. package/dist/toast/toast.mjs.map +1 -0
  288. package/package.json +7 -1
@@ -0,0 +1,621 @@
1
+ "use client";
2
+ import { createActionDetails } from "../internal/action-details.mjs";
3
+ import { dataAttr } from "../internal/data-attr.mjs";
4
+ import { useLatest } from "../internal/use-latest.mjs";
5
+ import { useRender } from "../internal/use-render.mjs";
6
+ import { useDevWarning } from "../internal/use-dev-warning.mjs";
7
+ import { DialogContext } from "../dialog/dialog-context.mjs";
8
+ import { getServerTopmostModal, getTopmostModal, subscribeModals } from "../internal/modal-registry.mjs";
9
+ import { ToastViewportContext } from "./toast-context.mjs";
10
+ import { toast, toastManagerInternals } from "./toast.mjs";
11
+ import { use, useCallback, useEffect, useLayoutEffect, useMemo, useRef, useState, useSyncExternalStore, useTransition } from "react";
12
+ import { Fragment, jsx, jsxs } from "react/jsx-runtime";
13
+ import { createPortal } from "react-dom";
14
+ //#region src/toast/toast-viewport.tsx
15
+ const isDev = process.env.NODE_ENV !== "production";
16
+ const DEFAULT_HOTKEY = ["F8"];
17
+ const MODIFIER_NAMES = {
18
+ altKey: "Alt",
19
+ ctrlKey: "Ctrl",
20
+ metaKey: "Meta",
21
+ shiftKey: "Shift"
22
+ };
23
+ /** `["altKey", "KeyT"]` → `"Alt+T"`, `["F8"]` → `"F8"`. */
24
+ function hotkeyLabel(hotkey) {
25
+ return hotkey.map((key) => MODIFIER_NAMES[key] ?? key.replace(/^Key(?=[A-Z]$)/, "").replace(/^Digit/, "")).join("+");
26
+ }
27
+ /** The region's built-in accessible name (conventions 11): `"Notifications (F8)"`. */
28
+ function regionLabel(hotkey) {
29
+ if (hotkey === null || hotkey.length === 0) return "Notifications";
30
+ return `Notifications (${hotkeyLabel(hotkey)})`;
31
+ }
32
+ /** The listed keys and modifiers, and no unlisted modifier (Ctrl+F8 is not F8). */
33
+ function matchesHotkey(hotkey, event) {
34
+ const flags = event;
35
+ for (const modifier of Object.keys(MODIFIER_NAMES)) if (flags[modifier] === true !== hotkey.includes(modifier)) return false;
36
+ return hotkey.every((key) => key in MODIFIER_NAMES || event.code === key);
37
+ }
38
+ /** No store: `useSyncExternalStore` only tells the hydration render from a client render. */
39
+ const subscribeNothing = () => () => {};
40
+ const onClient = () => true;
41
+ const onServerOrHydrating = () => false;
42
+ /**
43
+ * The element the region is portalled into for its whole client life. The library moves this
44
+ * element (between its inline anchor and the topmost modal dialog); React never sees the move, so
45
+ * the toasts keep their state (an Undo in flight stays pending). `display: contents`: no box.
46
+ */
47
+ function createHost() {
48
+ if (typeof document === "undefined") return null;
49
+ const host = document.createElement("div");
50
+ host.style.display = "contents";
51
+ return host;
52
+ }
53
+ function makeEntry(options, flags) {
54
+ return {
55
+ options,
56
+ ...flags,
57
+ item: {
58
+ ...options,
59
+ id: options.id,
60
+ pending: flags.promisePending || flags.actionPending,
61
+ ending: flags.ending
62
+ }
63
+ };
64
+ }
65
+ function withFlags(entry, flags) {
66
+ return makeEntry(entry.options, {
67
+ promisePending: flags.promisePending ?? entry.promisePending,
68
+ actionPending: flags.actionPending ?? entry.actionPending,
69
+ ending: flags.ending ?? entry.ending
70
+ });
71
+ }
72
+ /** Rendered entries (the first `limit` open ones plus every ending one) and the queued count. */
73
+ function partition(entries, limit) {
74
+ const visible = [];
75
+ const queued = [];
76
+ let open = 0;
77
+ for (const entry of entries) if (entry.ending) visible.push(entry);
78
+ else if (open < limit) {
79
+ visible.push(entry);
80
+ open++;
81
+ } else queued.push(entry);
82
+ return {
83
+ visible,
84
+ queued
85
+ };
86
+ }
87
+ /** Plain text of a `ReactNode` that is text (queued toasts have no DOM to read yet). */
88
+ function plainText(node) {
89
+ if (typeof node === "string") return node;
90
+ if (typeof node === "number" || typeof node === "bigint") return String(node);
91
+ if (Array.isArray(node)) return node.map(plainText).join("");
92
+ return "";
93
+ }
94
+ /** Document order. */
95
+ function byDocumentPosition(a, b) {
96
+ if (a === b) return 0;
97
+ return a.compareDocumentPosition(b) & Node.DOCUMENT_POSITION_FOLLOWING ? -1 : 1;
98
+ }
99
+ function anyPause(sources) {
100
+ return sources.hover || sources.focus || sources.hidden || sources.blurred;
101
+ }
102
+ /**
103
+ * The toast region: a `<section popover="manual">` named "Notifications (F8)", shown in the top
104
+ * layer while it has toasts. Owns the queue (urgent state fed by `toast()`), the timers and their
105
+ * pausing, the hotkey, focus return, announcements through `document.ariaNotify()`, and the move
106
+ * into the topmost open modal `Dialog` (a region outside it would be inert).
107
+ *
108
+ * Owned keys (conventions 1.2): `popover`, `aria-label` (the consumer's own `aria-label` wins
109
+ * verbatim), `data-paused`, `data-empty`, and the pointer / focus handlers.
110
+ */
111
+ function ToastViewport({ manager = toast, limit = 3, duration = 5e3, hotkey = DEFAULT_HOTKEY, announce, children, className, style, render, ...rest }) {
112
+ const [entries, setEntries] = useState([]);
113
+ const entriesRef = useRef(entries);
114
+ const listener = useRef(null);
115
+ listener.current ??= {
116
+ restore: (toasts) => handlers.current.restore(toasts),
117
+ apply: (commands) => handlers.current.apply(commands),
118
+ retain: () => handlers.current.retain()
119
+ };
120
+ const [rejected, setRejected] = useState(() => toastManagerInternals(manager).isTaken(listener.current));
121
+ const [, startCloseTransition] = useTransition();
122
+ const regionRef = useRef(null);
123
+ const roots = useRef(/* @__PURE__ */ new Map());
124
+ const suspended = useRef(/* @__PURE__ */ new Set());
125
+ const timers = useRef(/* @__PURE__ */ new Map());
126
+ const pauses = useRef({
127
+ hover: false,
128
+ focus: false,
129
+ hidden: false,
130
+ blurred: false
131
+ });
132
+ const [paused, setPaused] = useState(false);
133
+ /** Where focus goes back to once the last toast holding it leaves (design 7). */
134
+ const returnTarget = useRef(null);
135
+ const lastAnnounced = useRef(/* @__PURE__ */ new Map());
136
+ /** Per closing toast: the animations already running before `data-ending` applied. */
137
+ const exitBaselines = useRef(/* @__PURE__ */ new Map());
138
+ const announceBatch = useRef([]);
139
+ const addedSinceEffect = useRef(false);
140
+ const mounted = useRef(false);
141
+ const latest = useLatest({
142
+ limit,
143
+ duration,
144
+ hotkey,
145
+ announce
146
+ });
147
+ const container = useSyncExternalStore(subscribeModals, getTopmostModal, getServerTopmostModal);
148
+ const write = (next) => {
149
+ entriesRef.current = next;
150
+ setEntries(next);
151
+ };
152
+ const effectiveDuration = (entry) => {
153
+ const own = entry.options.duration;
154
+ if (own !== void 0) return own;
155
+ return entry.options.action !== void 0 ? null : latest.current.duration;
156
+ };
157
+ const syncTimers = () => {
158
+ const map = timers.current;
159
+ const { visible } = partition(entriesRef.current, latest.current.limit);
160
+ const now = Date.now();
161
+ const live = /* @__PURE__ */ new Set();
162
+ const halted = !mounted.current || anyPause(pauses.current);
163
+ for (const entry of visible) {
164
+ if (entry.ending) continue;
165
+ const id = entry.options.id;
166
+ const ms = effectiveDuration(entry);
167
+ if (ms === null) continue;
168
+ live.add(id);
169
+ let timer = map.get(id);
170
+ if (timer === void 0 || timer.duration !== ms) {
171
+ if (timer?.handle != null) clearTimeout(timer.handle);
172
+ timer = {
173
+ duration: ms,
174
+ remaining: ms,
175
+ startedAt: 0,
176
+ handle: null
177
+ };
178
+ map.set(id, timer);
179
+ }
180
+ const run = !halted && !entry.item.pending && !suspended.current.has(id);
181
+ if (run && timer.handle === null) {
182
+ const current = timer;
183
+ current.startedAt = now;
184
+ current.handle = setTimeout(() => {
185
+ current.handle = null;
186
+ current.remaining = 0;
187
+ close([id], "timeout", void 0);
188
+ }, Math.max(0, current.remaining));
189
+ } else if (!run && timer.handle !== null) {
190
+ clearTimeout(timer.handle);
191
+ timer.handle = null;
192
+ timer.remaining -= now - timer.startedAt;
193
+ }
194
+ }
195
+ for (const [id, timer] of map) {
196
+ if (live.has(id)) continue;
197
+ if (timer.handle !== null) clearTimeout(timer.handle);
198
+ map.delete(id);
199
+ }
200
+ };
201
+ const setPause = (source, value) => {
202
+ if (pauses.current[source] === value) return;
203
+ pauses.current[source] = value;
204
+ syncTimers();
205
+ setPaused(anyPause(pauses.current));
206
+ };
207
+ /** Open toasts' Root elements in document order. */
208
+ const sortedRoots = (exclude) => {
209
+ const open = new Set(entriesRef.current.filter((e) => !e.ending).map((e) => e.options.id));
210
+ const list = [];
211
+ for (const [id, element] of roots.current) {
212
+ if (!open.has(id) || exclude?.has(id) || !element.isConnected) continue;
213
+ list.push({
214
+ id,
215
+ element
216
+ });
217
+ }
218
+ return list.sort((a, b) => byDocumentPosition(a.element, b.element));
219
+ };
220
+ /**
221
+ * Before toasts close: if focus is inside one of them, move it to the next toast, else the
222
+ * previous one, else where it was before the region was entered, else the body (design 7).
223
+ */
224
+ const moveFocusOut = (closing) => {
225
+ const doc = regionRef.current?.ownerDocument ?? document;
226
+ const active = doc.activeElement;
227
+ if (!(active instanceof HTMLElement)) return;
228
+ const all = sortedRoots();
229
+ const index = all.findIndex(({ element }) => element.contains(active));
230
+ if (index === -1 || !closing.has(all[index].id)) return;
231
+ const after = all.slice(index + 1).find(({ id }) => !closing.has(id));
232
+ const before = all.slice(0, index).reverse().find(({ id }) => !closing.has(id));
233
+ const target = after ?? before;
234
+ if (target !== void 0) {
235
+ target.element.focus();
236
+ return;
237
+ }
238
+ const stored = returnTarget.current;
239
+ if (stored !== null && stored.isConnected && !regionRef.current?.contains(stored)) {
240
+ stored.focus();
241
+ if (doc.activeElement === stored) return;
242
+ }
243
+ active.blur();
244
+ };
245
+ /**
246
+ * Records the animations running in each closing toast before `data-ending` applies: the exit
247
+ * waits only for the ones the ending state started, not for a running progress bar.
248
+ */
249
+ const snapshotExitBaselines = (ids) => {
250
+ for (const id of ids) {
251
+ const element = roots.current.get(id);
252
+ if (element === void 0 || !element.isConnected) continue;
253
+ if (typeof element.getAnimations !== "function") continue;
254
+ exitBaselines.current.set(id, new Set(element.getAnimations({ subtree: true })));
255
+ }
256
+ };
257
+ const clearTimer = (id) => {
258
+ const timer = timers.current.get(id);
259
+ if (timer?.handle != null) clearTimeout(timer.handle);
260
+ timers.current.delete(id);
261
+ };
262
+ const scheduleCloseActions = (closed, reason, event) => {
263
+ for (const entry of closed) {
264
+ const closeAction = entry.options.closeAction;
265
+ if (closeAction === void 0) continue;
266
+ const details = createActionDetails(event, {
267
+ id: entry.options.id,
268
+ reason
269
+ });
270
+ queueMicrotask(() => {
271
+ if (!mounted.current) return;
272
+ startCloseTransition(async () => {
273
+ await closeAction(details);
274
+ });
275
+ });
276
+ }
277
+ };
278
+ const close = (ids, reason, event) => {
279
+ const wanted = new Set(ids);
280
+ const closing = entriesRef.current.filter((e) => !e.ending && wanted.has(e.options.id));
281
+ if (closing.length === 0) return;
282
+ const closingIds = new Set(closing.map((e) => e.options.id));
283
+ moveFocusOut(closingIds);
284
+ snapshotExitBaselines(closingIds);
285
+ write(entriesRef.current.map((e) => closingIds.has(e.options.id) ? withFlags(e, { ending: true }) : e));
286
+ syncTimers();
287
+ scheduleCloseActions(closing, reason, event);
288
+ };
289
+ const drop = (id) => {
290
+ const next = entriesRef.current.filter((e) => !(e.ending && e.options.id === id));
291
+ if (next.length === entriesRef.current.length) return;
292
+ lastAnnounced.current.delete(id);
293
+ exitBaselines.current.delete(id);
294
+ write(next);
295
+ };
296
+ const apply = (commands) => {
297
+ const next = [...entriesRef.current];
298
+ const dismissed = [];
299
+ for (const command of commands) {
300
+ if (command.kind === "dismiss") {
301
+ for (let i = 0; i < next.length; i++) {
302
+ const entry = next[i];
303
+ if (entry.ending) continue;
304
+ if (command.id !== void 0 && entry.options.id !== command.id) continue;
305
+ dismissed.push(entry);
306
+ next[i] = withFlags(entry, { ending: true });
307
+ }
308
+ continue;
309
+ }
310
+ const index = next.findIndex((e) => e.options.id === command.id);
311
+ const existing = index === -1 ? void 0 : next[index];
312
+ if (command.kind === "add") {
313
+ const options = {
314
+ ...command.options,
315
+ id: command.id
316
+ };
317
+ if (existing !== void 0 && !existing.ending) {
318
+ next[index] = makeEntry({
319
+ ...existing.options,
320
+ ...options
321
+ }, {
322
+ promisePending: command.pending || existing.promisePending,
323
+ actionPending: existing.actionPending,
324
+ ending: false
325
+ });
326
+ continue;
327
+ }
328
+ if (existing !== void 0) {
329
+ next.splice(index, 1);
330
+ clearTimer(command.id);
331
+ lastAnnounced.current.delete(command.id);
332
+ exitBaselines.current.delete(command.id);
333
+ }
334
+ next.push(makeEntry(options, {
335
+ promisePending: command.pending,
336
+ actionPending: false,
337
+ ending: false
338
+ }));
339
+ addedSinceEffect.current = true;
340
+ } else {
341
+ if (existing === void 0) {
342
+ if (isDev && !command.settle) console.error(`toast.update: no toast with id "${command.id}"; the update is ignored.`);
343
+ continue;
344
+ }
345
+ if (existing.ending) continue;
346
+ next[index] = makeEntry(command.settle ? {
347
+ ...command.options,
348
+ id: command.id
349
+ } : {
350
+ ...existing.options,
351
+ ...command.options,
352
+ id: command.id
353
+ }, {
354
+ promisePending: command.settle ? false : existing.promisePending,
355
+ actionPending: existing.actionPending,
356
+ ending: false
357
+ });
358
+ }
359
+ }
360
+ if (dismissed.length === 0) {
361
+ write(next);
362
+ return;
363
+ }
364
+ const stays = new Set(next.filter((e) => !e.ending).map((e) => e.options.id));
365
+ const leaving = new Set(dismissed.map((e) => e.options.id).filter((id) => !stays.has(id)));
366
+ moveFocusOut(leaving);
367
+ snapshotExitBaselines(leaving);
368
+ write(next);
369
+ syncTimers();
370
+ scheduleCloseActions(dismissed, "dismiss", void 0);
371
+ };
372
+ const setActionPending = (id, pending) => {
373
+ const index = entriesRef.current.findIndex((e) => e.options.id === id);
374
+ if (index === -1) return;
375
+ const entry = entriesRef.current[index];
376
+ if (entry.actionPending === pending) return;
377
+ const next = [...entriesRef.current];
378
+ next[index] = withFlags(entry, { actionPending: pending });
379
+ write(next);
380
+ };
381
+ const flushAnnouncements = () => {
382
+ const batch = announceBatch.current;
383
+ announceBatch.current = [];
384
+ if (batch.length === 0) return;
385
+ const doc = regionRef.current?.ownerDocument ?? document;
386
+ if (typeof doc.ariaNotify !== "function") return;
387
+ const priority = batch.some((a) => a.priority === "high") ? "high" : "normal";
388
+ doc.ariaNotify(batch.map((a) => a.text).join(" "), { priority });
389
+ };
390
+ const queueAnnouncement = (id, text, priority) => {
391
+ const map = lastAnnounced.current;
392
+ if (map.has(id) && map.get(id) === text) return;
393
+ map.set(id, text);
394
+ if (text === null || text.trim() === "") return;
395
+ if (announceBatch.current.length === 0) queueMicrotask(flushAnnouncements);
396
+ announceBatch.current.push({
397
+ text,
398
+ priority
399
+ });
400
+ };
401
+ const restore = (toasts) => {
402
+ if (entriesRef.current.length > 0) return;
403
+ for (const t of toasts) if (t.announced !== void 0) lastAnnounced.current.set(t.options.id, t.announced);
404
+ write(toasts.map((t) => makeEntry(t.options, {
405
+ promisePending: t.pending,
406
+ actionPending: false,
407
+ ending: false
408
+ })));
409
+ addedSinceEffect.current = true;
410
+ };
411
+ const retain = () => {
412
+ const announced = lastAnnounced.current;
413
+ return entriesRef.current.filter((e) => !e.ending).map((e) => ({
414
+ options: e.options,
415
+ pending: e.promisePending,
416
+ announced: announced.has(e.options.id) ? announced.get(e.options.id) : void 0
417
+ }));
418
+ };
419
+ const handlers = useLatest({
420
+ apply,
421
+ restore,
422
+ retain,
423
+ close,
424
+ drop,
425
+ setActionPending,
426
+ queueAnnouncement,
427
+ setPause,
428
+ syncTimers
429
+ });
430
+ const context = useMemo(() => ({
431
+ close: (ids, reason, event) => handlers.current.close(ids, reason, event),
432
+ drop: (id) => handlers.current.drop(id),
433
+ setActionPending: (id, pending) => handlers.current.setActionPending(id, pending),
434
+ setSuspended: (id, value) => {
435
+ if (value) suspended.current.add(id);
436
+ else suspended.current.delete(id);
437
+ handlers.current.syncTimers();
438
+ },
439
+ registerRoot: (id, element) => {
440
+ if (element === null) roots.current.delete(id);
441
+ else roots.current.set(id, element);
442
+ },
443
+ focusSibling: (id, direction) => {
444
+ const all = sortedRoots();
445
+ const index = all.findIndex((r) => r.id === id);
446
+ if (index === -1) return;
447
+ all[index + direction]?.element.focus();
448
+ },
449
+ announce: (id, text, priority) => handlers.current.queueAnnouncement(id, text, priority),
450
+ exitBaseline: (id) => exitBaselines.current.get(id),
451
+ announcer: () => latest.current.announce
452
+ }), [handlers, latest]);
453
+ useEffect(() => {
454
+ const unsubscribe = toastManagerInternals(manager).subscribe(listener.current);
455
+ if (unsubscribe === null) {
456
+ setRejected(true);
457
+ if (isDev) console.error("Toast.Viewport: another Toast.Viewport already renders this manager; this one renders nothing. Use one Viewport per manager, or `createToastManager()` for a second queue.");
458
+ return;
459
+ }
460
+ setRejected(false);
461
+ return unsubscribe;
462
+ }, [manager]);
463
+ useEffect(() => {
464
+ if (rejected) return;
465
+ mounted.current = true;
466
+ const doc = regionRef.current?.ownerDocument ?? document;
467
+ const view = doc.defaultView ?? window;
468
+ pauses.current.hidden = doc.visibilityState === "hidden";
469
+ const onVisibility = () => handlers.current.setPause("hidden", doc.visibilityState === "hidden");
470
+ const onWindowBlur = (event) => {
471
+ if (!(event.target instanceof Node)) handlers.current.setPause("blurred", true);
472
+ };
473
+ const onWindowFocus = (event) => {
474
+ if (!(event.target instanceof Node)) handlers.current.setPause("blurred", false);
475
+ };
476
+ doc.addEventListener("visibilitychange", onVisibility);
477
+ view.addEventListener("blur", onWindowBlur);
478
+ view.addEventListener("focus", onWindowFocus);
479
+ handlers.current.syncTimers();
480
+ setPaused(anyPause(pauses.current));
481
+ const map = timers.current;
482
+ return () => {
483
+ mounted.current = false;
484
+ doc.removeEventListener("visibilitychange", onVisibility);
485
+ view.removeEventListener("blur", onWindowBlur);
486
+ view.removeEventListener("focus", onWindowFocus);
487
+ for (const timer of map.values()) if (timer.handle !== null) clearTimeout(timer.handle);
488
+ map.clear();
489
+ };
490
+ }, [handlers, rejected]);
491
+ const hotkeyKey = hotkey === null ? null : hotkey.join("\0");
492
+ useEffect(() => {
493
+ if (hotkeyKey === null || rejected) return;
494
+ const keys = hotkeyKey.split("\0");
495
+ const doc = regionRef.current?.ownerDocument ?? document;
496
+ const onKeyDown = (event) => {
497
+ if (event.defaultPrevented || !matchesHotkey(keys, event)) return;
498
+ const newest = sortedRoots().at(-1);
499
+ if (newest === void 0) return;
500
+ event.preventDefault();
501
+ const active = doc.activeElement;
502
+ if (active instanceof HTMLElement && !regionRef.current?.contains(active)) returnTarget.current = active;
503
+ newest.element.focus();
504
+ };
505
+ doc.addEventListener("keydown", onKeyDown);
506
+ return () => doc.removeEventListener("keydown", onKeyDown);
507
+ }, [hotkeyKey, rejected]);
508
+ const portalled = useSyncExternalStore(subscribeNothing, onClient, onServerOrHydrating);
509
+ const [host] = useState(createHost);
510
+ const anchorRef = useRef(null);
511
+ useLayoutEffect(() => {
512
+ if (host === null) return;
513
+ return () => host.remove();
514
+ }, [host]);
515
+ useLayoutEffect(() => {
516
+ if (host === null || !portalled) return;
517
+ const target = container ?? anchorRef.current;
518
+ if (target !== null && host.parentNode !== target) target.append(host);
519
+ });
520
+ useLayoutEffect(() => {
521
+ const region = regionRef.current;
522
+ const current = entriesRef.current;
523
+ if (pauses.current.focus && (region === null || !region.contains(region.ownerDocument.activeElement))) {
524
+ returnTarget.current = null;
525
+ setPause("focus", false);
526
+ }
527
+ if (region !== null && region.isConnected && region.hasAttribute("popover")) {
528
+ let open = false;
529
+ try {
530
+ open = region.matches(":popover-open");
531
+ } catch {
532
+ open = false;
533
+ }
534
+ try {
535
+ if (current.length > 0) {
536
+ if (!open) region.showPopover();
537
+ else if (addedSinceEffect.current) {
538
+ region.hidePopover();
539
+ region.showPopover();
540
+ }
541
+ } else if (open) region.hidePopover();
542
+ } catch {}
543
+ }
544
+ addedSinceEffect.current = false;
545
+ const { visible, queued } = partition(current, latest.current.limit);
546
+ for (const entry of queued) {
547
+ const id = entry.options.id;
548
+ if (lastAnnounced.current.has(id)) continue;
549
+ const resolver = entry.options.announce ?? latest.current.announce;
550
+ const text = resolver ? resolver(entry.item) : [plainText(entry.options.title), plainText(entry.options.description)].filter((t) => t !== "").join(" ");
551
+ queueAnnouncement(id, text, entry.options.priority ?? "normal");
552
+ }
553
+ for (const entry of visible) if (entry.ending && !roots.current.has(entry.options.id)) drop(entry.options.id);
554
+ syncTimers();
555
+ });
556
+ const insideDialog = use(DialogContext) !== null;
557
+ useDevWarning(insideDialog, "Toast.Viewport: rendered inside a Dialog. The region would close with the dialog; render the Viewport once near the root of the app (it moves into open modal Dialogs by itself).");
558
+ const { visible, queued } = partition(entries, limit);
559
+ const state = {
560
+ toasts: visible.map((e) => e.item),
561
+ queued: queued.length,
562
+ paused
563
+ };
564
+ const regionCallbackRef = useCallback((element) => {
565
+ regionRef.current = element;
566
+ return () => {
567
+ if (regionRef.current === element) regionRef.current = null;
568
+ };
569
+ }, []);
570
+ const onPointerEnter = () => setPause("hover", true);
571
+ const onPointerLeave = () => setPause("hover", false);
572
+ const onFocus = (event) => {
573
+ const region = event.currentTarget;
574
+ const from = event.relatedTarget;
575
+ if (!(from instanceof Node) || !region.contains(from)) returnTarget.current ??= from instanceof HTMLElement ? from : null;
576
+ setPause("focus", true);
577
+ };
578
+ const onBlur = (event) => {
579
+ const to = event.relatedTarget;
580
+ if (to instanceof Node && event.currentTarget.contains(to)) return;
581
+ returnTarget.current = null;
582
+ setPause("focus", false);
583
+ };
584
+ const libraryProps = {
585
+ popover: "manual",
586
+ "aria-label": rest["aria-label"] ?? regionLabel(hotkey),
587
+ "data-paused": dataAttr(paused),
588
+ "data-empty": dataAttr(state.toasts.length === 0),
589
+ onPointerEnter,
590
+ onPointerLeave,
591
+ onFocus,
592
+ onBlur
593
+ };
594
+ const region = useRender({
595
+ defaultElement: "section",
596
+ render,
597
+ state,
598
+ className,
599
+ style,
600
+ children,
601
+ props: rest,
602
+ libraryProps,
603
+ ref: regionCallbackRef
604
+ });
605
+ if (rejected) return null;
606
+ const content = /* @__PURE__ */ jsx(ToastViewportContext, {
607
+ value: context,
608
+ children: region
609
+ });
610
+ if (!portalled || host === null) return content;
611
+ return /* @__PURE__ */ jsxs(Fragment, { children: [/* @__PURE__ */ jsx("div", {
612
+ ref: anchorRef,
613
+ style: ANCHOR_STYLE
614
+ }), createPortal(content, host)] });
615
+ }
616
+ const ANCHOR_STYLE = { display: "contents" };
617
+ ToastViewport.displayName = "ToastViewport";
618
+ //#endregion
619
+ export { ToastViewport };
620
+
621
+ //# sourceMappingURL=toast-viewport.mjs.map