@funstack/ui 0.1.0-alpha.0 → 0.1.0-alpha.2

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 (242) hide show
  1. package/README.md +1 -1
  2. package/dist/checkbox-group/checkbox-group-context.mjs +24 -0
  3. package/dist/checkbox-group/checkbox-group-context.mjs.map +1 -0
  4. package/dist/checkbox-group/checkbox-group-item.d.mts +55 -0
  5. package/dist/checkbox-group/checkbox-group-item.d.mts.map +1 -0
  6. package/dist/checkbox-group/checkbox-group-item.mjs +90 -0
  7. package/dist/checkbox-group/checkbox-group-item.mjs.map +1 -0
  8. package/dist/checkbox-group/checkbox-group-label.d.mts +21 -0
  9. package/dist/checkbox-group/checkbox-group-label.d.mts.map +1 -0
  10. package/dist/checkbox-group/checkbox-group-label.mjs +30 -0
  11. package/dist/checkbox-group/checkbox-group-label.mjs.map +1 -0
  12. package/dist/checkbox-group/checkbox-group-parent.d.mts +68 -0
  13. package/dist/checkbox-group/checkbox-group-parent.d.mts.map +1 -0
  14. package/dist/checkbox-group/checkbox-group-parent.mjs +145 -0
  15. package/dist/checkbox-group/checkbox-group-parent.mjs.map +1 -0
  16. package/dist/checkbox-group/checkbox-group-root.d.mts +100 -0
  17. package/dist/checkbox-group/checkbox-group-root.d.mts.map +1 -0
  18. package/dist/checkbox-group/checkbox-group-root.mjs +291 -0
  19. package/dist/checkbox-group/checkbox-group-root.mjs.map +1 -0
  20. package/dist/checkbox-group/index.d.mts +23 -0
  21. package/dist/checkbox-group/index.d.mts.map +1 -0
  22. package/dist/checkbox-group/index.mjs +23 -0
  23. package/dist/checkbox-group/index.mjs.map +1 -0
  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 +40 -0
  35. package/dist/combobox/combobox-input.d.mts.map +1 -0
  36. package/dist/combobox/combobox-input.mjs +88 -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 +508 -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.map +1 -1
  67. package/dist/dialog/dialog-close.mjs +4 -4
  68. package/dist/dialog/dialog-close.mjs.map +1 -1
  69. package/dist/dialog/dialog-context.mjs.map +1 -1
  70. package/dist/dialog/dialog-popup.mjs +1 -0
  71. package/dist/dialog/dialog-popup.mjs.map +1 -1
  72. package/dist/dialog/dialog-root.mjs +9 -1
  73. package/dist/dialog/dialog-root.mjs.map +1 -1
  74. package/dist/index.d.mts +42 -2
  75. package/dist/index.mjs +6 -1
  76. package/dist/internal/anchor-position.d.mts +7 -0
  77. package/dist/internal/anchor-position.d.mts.map +1 -0
  78. package/dist/internal/anchor-position.mjs +16 -0
  79. package/dist/internal/anchor-position.mjs.map +1 -0
  80. package/dist/internal/platform-attributes.d.mts +18 -1
  81. package/dist/internal/platform-attributes.d.mts.map +1 -1
  82. package/dist/internal/sync-presence.mjs +19 -0
  83. package/dist/internal/sync-presence.mjs.map +1 -0
  84. package/dist/internal/types.d.mts +8 -0
  85. package/dist/internal/types.d.mts.map +1 -1
  86. package/dist/internal/use-action-trigger.mjs +58 -0
  87. package/dist/internal/use-action-trigger.mjs.map +1 -0
  88. package/dist/internal/use-controllable-action.mjs +5 -1
  89. package/dist/internal/use-controllable-action.mjs.map +1 -1
  90. package/dist/internal/use-delayed-pair.mjs +40 -0
  91. package/dist/internal/use-delayed-pair.mjs.map +1 -0
  92. package/dist/internal/use-exit-animation.mjs +55 -0
  93. package/dist/internal/use-exit-animation.mjs.map +1 -0
  94. package/dist/internal/use-form-reset.mjs +5 -1
  95. package/dist/internal/use-form-reset.mjs.map +1 -1
  96. package/dist/internal/use-hydrated-mount.mjs +21 -0
  97. package/dist/internal/use-hydrated-mount.mjs.map +1 -0
  98. package/dist/internal/use-native-group-reconciliation.mjs +2 -2
  99. package/dist/internal/use-native-group-reconciliation.mjs.map +1 -1
  100. package/dist/internal/use-native-select-value.mjs +182 -0
  101. package/dist/internal/use-native-select-value.mjs.map +1 -0
  102. package/dist/internal/use-popover-popup.mjs +274 -0
  103. package/dist/internal/use-popover-popup.mjs.map +1 -0
  104. package/dist/internal/use-popover-root.d.mts +2 -0
  105. package/dist/internal/use-popover-root.mjs +112 -0
  106. package/dist/internal/use-popover-root.mjs.map +1 -0
  107. package/dist/internal/use-popover-trigger.mjs +86 -0
  108. package/dist/internal/use-popover-trigger.mjs.map +1 -0
  109. package/dist/menu/index.d.mts +34 -0
  110. package/dist/menu/index.d.mts.map +1 -0
  111. package/dist/menu/index.mjs +34 -0
  112. package/dist/menu/index.mjs.map +1 -0
  113. package/dist/menu/menu-checkbox-item.d.mts +42 -0
  114. package/dist/menu/menu-checkbox-item.d.mts.map +1 -0
  115. package/dist/menu/menu-checkbox-item.mjs +71 -0
  116. package/dist/menu/menu-checkbox-item.mjs.map +1 -0
  117. package/dist/menu/menu-context.d.mts +1 -0
  118. package/dist/menu/menu-context.mjs +28 -0
  119. package/dist/menu/menu-context.mjs.map +1 -0
  120. package/dist/menu/menu-dom.mjs +37 -0
  121. package/dist/menu/menu-dom.mjs.map +1 -0
  122. package/dist/menu/menu-group.d.mts +48 -0
  123. package/dist/menu/menu-group.d.mts.map +1 -0
  124. package/dist/menu/menu-group.mjs +69 -0
  125. package/dist/menu/menu-group.mjs.map +1 -0
  126. package/dist/menu/menu-hover.mjs +29 -0
  127. package/dist/menu/menu-hover.mjs.map +1 -0
  128. package/dist/menu/menu-item.d.mts +45 -0
  129. package/dist/menu/menu-item.d.mts.map +1 -0
  130. package/dist/menu/menu-item.mjs +54 -0
  131. package/dist/menu/menu-item.mjs.map +1 -0
  132. package/dist/menu/menu-popup.d.mts +69 -0
  133. package/dist/menu/menu-popup.d.mts.map +1 -0
  134. package/dist/menu/menu-popup.mjs +207 -0
  135. package/dist/menu/menu-popup.mjs.map +1 -0
  136. package/dist/menu/menu-radio-group.d.mts +37 -0
  137. package/dist/menu/menu-radio-group.d.mts.map +1 -0
  138. package/dist/menu/menu-radio-group.mjs +72 -0
  139. package/dist/menu/menu-radio-group.mjs.map +1 -0
  140. package/dist/menu/menu-radio-item.d.mts +33 -0
  141. package/dist/menu/menu-radio-item.d.mts.map +1 -0
  142. package/dist/menu/menu-radio-item.mjs +62 -0
  143. package/dist/menu/menu-radio-item.mjs.map +1 -0
  144. package/dist/menu/menu-root.d.mts +54 -0
  145. package/dist/menu/menu-root.d.mts.map +1 -0
  146. package/dist/menu/menu-root.mjs +102 -0
  147. package/dist/menu/menu-root.mjs.map +1 -0
  148. package/dist/menu/menu-submenu-trigger.d.mts +31 -0
  149. package/dist/menu/menu-submenu-trigger.d.mts.map +1 -0
  150. package/dist/menu/menu-submenu-trigger.mjs +92 -0
  151. package/dist/menu/menu-submenu-trigger.mjs.map +1 -0
  152. package/dist/menu/menu-trigger.d.mts +36 -0
  153. package/dist/menu/menu-trigger.d.mts.map +1 -0
  154. package/dist/menu/menu-trigger.mjs +81 -0
  155. package/dist/menu/menu-trigger.mjs.map +1 -0
  156. package/dist/menu/use-menu-item.mjs +54 -0
  157. package/dist/menu/use-menu-item.mjs.map +1 -0
  158. package/dist/popover/index.d.mts +28 -0
  159. package/dist/popover/index.d.mts.map +1 -0
  160. package/dist/popover/index.mjs +28 -0
  161. package/dist/popover/index.mjs.map +1 -0
  162. package/dist/popover/popover-close.d.mts +41 -0
  163. package/dist/popover/popover-close.d.mts.map +1 -0
  164. package/dist/popover/popover-close.mjs +90 -0
  165. package/dist/popover/popover-close.mjs.map +1 -0
  166. package/dist/popover/popover-context.mjs +26 -0
  167. package/dist/popover/popover-context.mjs.map +1 -0
  168. package/dist/popover/popover-description.d.mts +22 -0
  169. package/dist/popover/popover-description.d.mts.map +1 -0
  170. package/dist/popover/popover-description.mjs +28 -0
  171. package/dist/popover/popover-description.mjs.map +1 -0
  172. package/dist/popover/popover-popup.d.mts +82 -0
  173. package/dist/popover/popover-popup.d.mts.map +1 -0
  174. package/dist/popover/popover-popup.mjs +93 -0
  175. package/dist/popover/popover-popup.mjs.map +1 -0
  176. package/dist/popover/popover-root.d.mts +64 -0
  177. package/dist/popover/popover-root.d.mts.map +1 -0
  178. package/dist/popover/popover-root.mjs +39 -0
  179. package/dist/popover/popover-root.mjs.map +1 -0
  180. package/dist/popover/popover-title.d.mts +23 -0
  181. package/dist/popover/popover-title.d.mts.map +1 -0
  182. package/dist/popover/popover-title.mjs +28 -0
  183. package/dist/popover/popover-title.mjs.map +1 -0
  184. package/dist/popover/popover-trigger.d.mts +37 -0
  185. package/dist/popover/popover-trigger.d.mts.map +1 -0
  186. package/dist/popover/popover-trigger.mjs +40 -0
  187. package/dist/popover/popover-trigger.mjs.map +1 -0
  188. package/dist/radio-group/radio-group-item.d.mts.map +1 -1
  189. package/dist/radio-group/radio-group-item.mjs +1 -6
  190. package/dist/radio-group/radio-group-item.mjs.map +1 -1
  191. package/dist/select/index.d.mts +30 -0
  192. package/dist/select/index.d.mts.map +1 -0
  193. package/dist/select/index.mjs +30 -0
  194. package/dist/select/index.mjs.map +1 -0
  195. package/dist/select/select-context.mjs +13 -0
  196. package/dist/select/select-context.mjs.map +1 -0
  197. package/dist/select/select-dom.mjs +44 -0
  198. package/dist/select/select-dom.mjs.map +1 -0
  199. package/dist/select/select-group-label.d.mts +22 -0
  200. package/dist/select/select-group-label.d.mts.map +1 -0
  201. package/dist/select/select-group-label.mjs +39 -0
  202. package/dist/select/select-group-label.mjs.map +1 -0
  203. package/dist/select/select-group.d.mts +28 -0
  204. package/dist/select/select-group.d.mts.map +1 -0
  205. package/dist/select/select-group.mjs +45 -0
  206. package/dist/select/select-group.mjs.map +1 -0
  207. package/dist/select/select-option.d.mts +42 -0
  208. package/dist/select/select-option.d.mts.map +1 -0
  209. package/dist/select/select-option.mjs +65 -0
  210. package/dist/select/select-option.mjs.map +1 -0
  211. package/dist/select/select-root.d.mts +66 -0
  212. package/dist/select/select-root.d.mts.map +1 -0
  213. package/dist/select/select-root.mjs +94 -0
  214. package/dist/select/select-root.mjs.map +1 -0
  215. package/dist/select/select-separator.d.mts +20 -0
  216. package/dist/select/select-separator.d.mts.map +1 -0
  217. package/dist/select/select-separator.mjs +30 -0
  218. package/dist/select/select-separator.mjs.map +1 -0
  219. package/dist/select/select-trigger.d.mts +29 -0
  220. package/dist/select/select-trigger.d.mts.map +1 -0
  221. package/dist/select/select-trigger.mjs +48 -0
  222. package/dist/select/select-trigger.mjs.map +1 -0
  223. package/dist/select/select-value.d.mts +31 -0
  224. package/dist/select/select-value.d.mts.map +1 -0
  225. package/dist/select/select-value.mjs +101 -0
  226. package/dist/select/select-value.mjs.map +1 -0
  227. package/dist/tabs/tabs-context.mjs +2 -1
  228. package/dist/tabs/tabs-context.mjs.map +1 -1
  229. package/dist/tabs/tabs-list.d.mts +1 -1
  230. package/dist/tabs/tabs-list.d.mts.map +1 -1
  231. package/dist/tabs/tabs-list.mjs +17 -9
  232. package/dist/tabs/tabs-list.mjs.map +1 -1
  233. package/dist/tabs/tabs-tab.d.mts +1 -1
  234. package/dist/tabs/tabs-tab.d.mts.map +1 -1
  235. package/dist/tabs/tabs-tab.mjs +6 -3
  236. package/dist/tabs/tabs-tab.mjs.map +1 -1
  237. package/dist/tooltip/tooltip-popup.d.mts.map +1 -1
  238. package/dist/tooltip/tooltip-popup.mjs +2 -7
  239. package/dist/tooltip/tooltip-popup.mjs.map +1 -1
  240. package/dist/tooltip/use-tooltip-interaction.mjs +5 -17
  241. package/dist/tooltip/use-tooltip-interaction.mjs.map +1 -1
  242. package/package.json +6 -1
@@ -0,0 +1,291 @@
1
+ "use client";
2
+ import { createActionDetails } from "../internal/action-details.mjs";
3
+ import { dataAttr } from "../internal/data-attr.mjs";
4
+ import { transitionTypes } from "../internal/transition-types.mjs";
5
+ import { useControllableAction } from "../internal/use-controllable-action.mjs";
6
+ import { useRender } from "../internal/use-render.mjs";
7
+ import { useNativeGroupReconciliation } from "../internal/use-native-group-reconciliation.mjs";
8
+ import { CheckboxGroupContext, enabledValues } from "./checkbox-group-context.mjs";
9
+ import { useCallback, useEffectEvent, useId, useLayoutEffect, useMemo, useRef, useState } from "react";
10
+ import { jsx } from "react/jsx-runtime";
11
+ //#region src/checkbox-group/checkbox-group-root.tsx
12
+ const isDev = process.env.NODE_ENV !== "production";
13
+ const EMPTY = /* @__PURE__ */ new Set();
14
+ function toSet(value) {
15
+ return new Set(value);
16
+ }
17
+ function setsEqual(a, b) {
18
+ if (a.size !== b.size) return false;
19
+ for (const v of a) if (!b.has(v)) return false;
20
+ return true;
21
+ }
22
+ /** The optimistic reducer (design 5.2): apply a patch, so stacked dispatches re-base. */
23
+ function reduce(set, patch) {
24
+ const next = new Set(set);
25
+ for (const v of patch.removed) next.delete(v);
26
+ for (const v of patch.added) next.add(v);
27
+ return next;
28
+ }
29
+ /**
30
+ * `values` sorted by `order` (the Items' values in document order); values not in `order` keep
31
+ * their iteration order, after the others.
32
+ */
33
+ function sortByOrder(values, order) {
34
+ const wanted = new Set(values);
35
+ const sorted = [];
36
+ for (const v of order) if (wanted.delete(v)) sorted.push(v);
37
+ for (const v of wanted) sorted.push(v);
38
+ return sorted;
39
+ }
40
+ function summarize(registry) {
41
+ const items = /* @__PURE__ */ new Map();
42
+ for (const { value, disabled } of registry.values()) items.set(value, (items.get(value) ?? false) || !disabled);
43
+ return items;
44
+ }
45
+ function disabledOf(items) {
46
+ if (items === null) return EMPTY;
47
+ const disabled = /* @__PURE__ */ new Set();
48
+ for (const [value, enabled] of items) if (!enabled) disabled.add(value);
49
+ return disabled.size === 0 ? EMPTY : disabled;
50
+ }
51
+ function mapsEqual(a, b) {
52
+ if (a.size !== b.size) return false;
53
+ for (const [k, v] of a) if (b.get(k) !== v) return false;
54
+ return true;
55
+ }
56
+ function arraysEqual(a, b) {
57
+ return a.length === b.length && a.every((v, i) => v === b[i]);
58
+ }
59
+ /** The group's Items: checkboxes of this `name` in the fieldset (Parents have none). */
60
+ function readItems(fieldset, name) {
61
+ if (fieldset === null) return [];
62
+ return [...fieldset.querySelectorAll("input[type=\"checkbox\"]")].filter((box) => box.name === name);
63
+ }
64
+ /**
65
+ * `<fieldset>` that owns the group's state (a set of values), the transition, the shared `name`,
66
+ * the form-reset listener, the mount reconciliation and the group-level `required`
67
+ * (design/components/checkbox-group.md 14.3). Carries `data-pending` while `valueChangeAction`
68
+ * is in flight. Its implicit role is `group`; no `role` is written.
69
+ *
70
+ * Owned keys (conventions 1.2): `disabled`, `form`, `data-pending`, `data-disabled`.
71
+ *
72
+ * Unlike RadioGroup, Items may hydrate after their Root (a Suspense boundary between them): a
73
+ * click before an Item hydrated is reconciled when it does.
74
+ */
75
+ function CheckboxGroupRoot({ value, defaultValue, valueChangeAction, name: nameProp, form, required = false, disabled = false, className, style, children, render, ...rest }) {
76
+ const fallbackName = useId();
77
+ const name = nameProp ?? fallbackName;
78
+ const fieldsetRef = useRef(null);
79
+ const warnedElement = useRef(false);
80
+ const warnedDuplicate = useRef(false);
81
+ /** Parent reports waiting for the next mount read (design 8.1). */
82
+ const parentReports = useRef([]);
83
+ const itemRegistry = useRef(/* @__PURE__ */ new Map());
84
+ const [items, setItems] = useState(null);
85
+ const registerItem = useCallback((value, disabled) => {
86
+ const publish = () => {
87
+ const next = summarize(itemRegistry.current);
88
+ setItems((prev) => prev !== null && mapsEqual(prev, next) ? prev : next);
89
+ };
90
+ const token = {};
91
+ itemRegistry.current.set(token, {
92
+ value,
93
+ disabled
94
+ });
95
+ publish();
96
+ return () => {
97
+ itemRegistry.current.delete(token);
98
+ publish();
99
+ };
100
+ }, []);
101
+ const disabledItems = useMemo(() => disabledOf(items), [items]);
102
+ const controlledSet = useMemo(() => value === void 0 ? void 0 : toSet(value), [value]);
103
+ const defaultSet = useMemo(() => defaultValue === void 0 ? void 0 : toSet(defaultValue), [defaultValue]);
104
+ /** Sorts by the Items' current document order. Reads the DOM: handlers and effects only. */
105
+ const sortByDocument = (values) => sortByOrder(values, readItems(fieldsetRef.current, name).map((item) => item.value));
106
+ const { optimistic, committed, pending, dispatch, latestInFlight } = useControllableAction({
107
+ controlled: controlledSet,
108
+ defaultValue: defaultSet,
109
+ initialValue: EMPTY,
110
+ action: valueChangeAction && ((next, details) => valueChangeAction(sortByDocument(next), details)),
111
+ transitionTypes: (_next, _prev, details) => [
112
+ ...transitionTypes("checkbox-group"),
113
+ ...details.added.length > 0 ? ["fsui-checkbox-group-check"] : [],
114
+ ...details.removed.length > 0 ? ["fsui-checkbox-group-uncheck"] : []
115
+ ],
116
+ reducer: (prev, _next, details) => reduce(prev, details),
117
+ warn: {
118
+ component: "CheckboxGroup.Root",
119
+ prop: "value"
120
+ }
121
+ });
122
+ /** What the user intends: the latest in-flight dispatch, else optimistic (conventions 3.8 rule 1). */
123
+ const base = () => {
124
+ const inFlight = latestInFlight();
125
+ return inFlight === null ? optimistic : inFlight.next;
126
+ };
127
+ /** Dispatches `patch` on top of `from`, with the details of design 3.1. */
128
+ const dispatchPatch = (from, patch, reason, event) => {
129
+ const details = createActionDetails(event, {
130
+ reason,
131
+ added: sortByDocument(patch.added),
132
+ removed: sortByDocument(patch.removed)
133
+ });
134
+ dispatch(reduce(from, details), details);
135
+ };
136
+ /**
137
+ * The group as the DOM shows it (`checked`) or as a form reset leaves it (`defaultChecked`),
138
+ * laid over `base()` so that values with no Item are kept (they have no DOM to read).
139
+ */
140
+ const readDom = (prop) => new Map(readItems(fieldsetRef.current, name).map((item) => [item.value, item[prop]]));
141
+ const readGroup = (prop, from, dom = readDom(prop)) => {
142
+ const next = new Set(from);
143
+ for (const [v, isChecked] of dom) if (isChecked) next.add(v);
144
+ else next.delete(v);
145
+ return next;
146
+ };
147
+ /**
148
+ * The Root's own mount reconciliation ran: later member mounts are "late" (8.3). Set after the
149
+ * adapter's mount effect; reset by the cleanup (StrictMode included).
150
+ */
151
+ const rootMounted = useRef(false);
152
+ /**
153
+ * Late mounts: the members' `checked` read in the first late member's mount layout effect. The
154
+ * adapter reads the group in a microtask, but the Items' registration re-renders the group
155
+ * right after the layout effects, and that re-render re-applies each Item's `checked`, erasing a
156
+ * click made before the Item hydrated. Every member of the commit is in the DOM by then.
157
+ */
158
+ const lateSnapshot = useRef(null);
159
+ const { onItemMounted: onMemberMounted } = useNativeGroupReconciliation({
160
+ element: fieldsetRef,
161
+ readCurrent: () => {
162
+ const next = readGroup("checked", base(), lateSnapshot.current ?? void 0);
163
+ lateSnapshot.current = null;
164
+ const disabledNow = disabledOf(summarize(itemRegistry.current));
165
+ for (const report of parentReports.current) for (const v of enabledValues(report.values, disabledNow)) if (report.checked) next.add(v);
166
+ else next.delete(v);
167
+ parentReports.current = [];
168
+ return next;
169
+ },
170
+ readDefault: () => readGroup("defaultChecked", value === void 0 ? defaultSet ?? EMPTY : base()),
171
+ base,
172
+ equals: setsEqual,
173
+ onPlatformChange: (next, event) => {
174
+ const from = base();
175
+ const added = [...next].filter((v) => !from.has(v));
176
+ const removed = [...from].filter((v) => !next.has(v));
177
+ dispatchPatch(from, {
178
+ added,
179
+ removed
180
+ }, "platform", event);
181
+ }
182
+ });
183
+ useLayoutEffect(() => {
184
+ rootMounted.current = true;
185
+ return () => {
186
+ rootMounted.current = false;
187
+ };
188
+ }, []);
189
+ const onItemMounted = (el) => {
190
+ const late = rootMounted.current;
191
+ if (late && lateSnapshot.current === null) lateSnapshot.current = readDom("checked");
192
+ onMemberMounted(el);
193
+ if (late) queueMicrotask(() => {
194
+ lateSnapshot.current = null;
195
+ });
196
+ };
197
+ const [itemOrder, setItemOrder] = useState([]);
198
+ const renderedValue = sortByOrder(optimistic, itemOrder);
199
+ const syncItemOrder = useEffectEvent(() => {
200
+ const order = readItems(fieldsetRef.current, name).map((item) => item.value);
201
+ if (!arraysEqual(sortByOrder(optimistic, order), renderedValue)) setItemOrder(order);
202
+ if (!isDev || warnedDuplicate.current) return;
203
+ const duplicate = order.find((v, i) => order.indexOf(v) !== i);
204
+ if (duplicate !== void 0) {
205
+ warnedDuplicate.current = true;
206
+ console.error(`CheckboxGroup.Item: two Items share the value "${duplicate}" in one CheckboxGroup.Root. Item values must be unique; both boxes follow the same value.`);
207
+ }
208
+ });
209
+ useLayoutEffect(() => {
210
+ syncItemOrder();
211
+ });
212
+ const libraryRef = useCallback((el) => {
213
+ fieldsetRef.current = el;
214
+ if (isDev && !warnedElement.current && el.tagName !== "FIELDSET") {
215
+ warnedElement.current = true;
216
+ console.error(`CheckboxGroup.Root: expected to render a <fieldset> element but got <${el.tagName.toLowerCase()}>. The platform semantics (legend naming, disabled inheritance, form reset) need a real <fieldset>.`);
217
+ }
218
+ return () => {
219
+ fieldsetRef.current = null;
220
+ };
221
+ }, []);
222
+ const context = {
223
+ name,
224
+ form,
225
+ required: required && (items === null ? optimistic.size === 0 : ![...optimistic].some((v) => items.get(v) === true)),
226
+ disabledItems,
227
+ registerItem,
228
+ optimistic,
229
+ committed,
230
+ pending,
231
+ disabled,
232
+ defaultValue: value === void 0 ? defaultSet ?? EMPTY : void 0,
233
+ onItemMounted,
234
+ reportParent: (values, checked) => {
235
+ parentReports.current.push({
236
+ values,
237
+ checked
238
+ });
239
+ },
240
+ value: renderedValue,
241
+ sortByDocument,
242
+ toggle: (item, checked, event) => {
243
+ if (optimistic.has(item) === checked) return;
244
+ dispatchPatch(optimistic, checked ? {
245
+ added: [item],
246
+ removed: []
247
+ } : {
248
+ added: [],
249
+ removed: [item]
250
+ }, "activate", event);
251
+ },
252
+ apply: (patch, event) => {
253
+ const added = patch.added.filter((v) => !optimistic.has(v));
254
+ const removed = patch.removed.filter((v) => optimistic.has(v));
255
+ if (added.length === 0 && removed.length === 0) return;
256
+ dispatchPatch(optimistic, {
257
+ added,
258
+ removed
259
+ }, "activate", event);
260
+ }
261
+ };
262
+ const element = useRender({
263
+ defaultElement: "fieldset",
264
+ render,
265
+ state: {
266
+ value: renderedValue,
267
+ pending,
268
+ disabled
269
+ },
270
+ className,
271
+ style,
272
+ children,
273
+ props: rest,
274
+ libraryProps: {
275
+ disabled,
276
+ form,
277
+ "data-pending": dataAttr(pending),
278
+ "data-disabled": dataAttr(disabled)
279
+ },
280
+ ref: libraryRef
281
+ });
282
+ return /* @__PURE__ */ jsx(CheckboxGroupContext, {
283
+ value: context,
284
+ children: element
285
+ });
286
+ }
287
+ CheckboxGroupRoot.displayName = "CheckboxGroupRoot";
288
+ //#endregion
289
+ export { CheckboxGroupRoot };
290
+
291
+ //# sourceMappingURL=checkbox-group-root.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"checkbox-group-root.mjs","names":[],"sources":["../../src/checkbox-group/checkbox-group-root.tsx"],"sourcesContent":["\"use client\";\nimport {\n useCallback,\n useEffectEvent,\n useId,\n useLayoutEffect,\n useMemo,\n useRef,\n useState,\n} from \"react\";\nimport type { ComponentPropsWithRef, CSSProperties, ReactNode } from \"react\";\nimport { createActionDetails } from \"../internal/action-details.js\";\nimport { dataAttr } from \"../internal/data-attr.js\";\nimport { transitionTypes } from \"../internal/transition-types.js\";\nimport type {\n ActionDetails,\n ChangeAction,\n ChangeReason,\n RenderProp,\n StateValue,\n} from \"../internal/types.js\";\nimport { useControllableAction } from \"../internal/use-controllable-action.js\";\nimport { useNativeGroupReconciliation } from \"../internal/use-native-group-reconciliation.js\";\nimport { useRender } from \"../internal/use-render.js\";\nimport { CheckboxGroupContext, enabledValues } from \"./checkbox-group-context.js\";\nimport type { CheckboxGroupContextValue, CheckboxGroupPatch } from \"./checkbox-group-context.js\";\n\nconst isDev = process.env.NODE_ENV !== \"production\";\n\n/** Details passed as the second argument of `valueChangeAction`. */\nexport interface CheckboxGroupValueChangeDetails extends ActionDetails {\n /**\n * `\"activate\"`: the user toggled an Item or a Parent (click, Space, label click; `event` is the\n * native `click`). `\"platform\"`: the browser changed the checked boxes itself: a form reset\n * (`event` is the `reset` event), or clicks before hydration / Firefox form-state restoration\n * found at mount (`event` is `undefined`).\n */\n readonly reason: ChangeReason;\n /** Values this dispatch checked, in document order of their Items (values with no Item last). */\n readonly added: readonly string[];\n /** Values this dispatch unchecked, same order. */\n readonly removed: readonly string[];\n}\n\nexport interface CheckboxGroupRootState {\n /**\n * Optimistic checked values, in document order of the Items as of the Root's last render\n * (values with no Item last). Items reordered by a component that re-renders without the Root\n * keep the previous order here until the Root renders again; `next` and `details` of\n * `valueChangeAction` always read the current DOM order. The server render and the first client\n * render use the value's own order. Content rendered from it must not suspend.\n */\n readonly value: readonly string[];\n /** An action dispatched through this group is in flight. */\n readonly pending: boolean;\n /** The `disabled` prop. */\n readonly disabled: boolean;\n}\n\nexport interface CheckboxGroupRootProps extends Omit<\n ComponentPropsWithRef<\"fieldset\">,\n | \"className\"\n | \"style\"\n | \"children\"\n | \"defaultValue\"\n | \"defaultChecked\"\n | \"name\"\n | \"disabled\"\n | \"form\"\n> {\n /**\n * Controlled checked values. `[]` checks nothing; `undefined` means uncontrolled. Order is not\n * significant and duplicates are ignored: the value is a set.\n *\n * A form reset returns each box to the `checked` it had when *that box* mounted (React sets\n * `defaultChecked` once, from the initial `checked`). The reset is offered to\n * `valueChangeAction` as a `\"platform\"` change; for a stable target use `defaultValue`.\n */\n value?: string[];\n /**\n * Initial values in uncontrolled mode. Defaults to `[]`. Also the values a form reset returns\n * to: the latest prop value is mirrored into the boxes' `defaultChecked`.\n */\n defaultValue?: string[];\n /**\n * Runs inside the Root's transition whenever the set of checked values changes. May be sync\n * or async (also a Server Function); rejections reach the nearest Error Boundary and roll the\n * change back.\n *\n * `next` is the whole set as a fresh array in document order of the Items; treat it as a set.\n * `details.added` / `details.removed` say what changed. When controlled and updating after\n * your own `await`, prefer a functional update keyed by `added` / `removed` over\n * `setValue(next)`: two overlapping actions completing out of order would otherwise land the\n * older whole set last. A `setState` placed after your own `await` is not a Transition update;\n * wrap it in `startTransition` (React limitation). `details.event.preventDefault()` is not a\n * veto; use controlled mode to veto a change.\n */\n valueChangeAction?: ChangeAction<string[], CheckboxGroupValueChangeDetails>;\n /**\n * The `name` shared by every Item (form data key: `formData.getAll(name)`). Defaults to a\n * `useId()` value; pass a real name when the group is inside a `<form>` you submit.\n */\n name?: string;\n /** The `form` attribute, applied to the fieldset and to every Item. */\n form?: string;\n /**\n * \"At least one Item must be checked.\" Rendered as native `required` on every Item while no\n * enabled Item is checked, and on none once one is: values with no Item and checked disabled\n * Items (neither is submitted) do not satisfy it. Until the Items have mounted (server HTML,\n * before hydration) any checked value satisfies it. Not exposed on the fieldset (ARIA does not\n * allow `aria-required` on role `group`): say it in the `Label` or through `aria-describedby`.\n */\n required?: boolean;\n /** Renders `<fieldset disabled>`: every Item and Parent is disabled and unfocusable. */\n disabled?: boolean;\n className?: StateValue<string | undefined, CheckboxGroupRootState>;\n style?: StateValue<CSSProperties | undefined, CheckboxGroupRootState>;\n children?: StateValue<ReactNode, CheckboxGroupRootState>;\n /** Replace the rendered element. Must keep a `<fieldset>`. */\n render?: RenderProp<ComponentPropsWithRef<\"fieldset\">, CheckboxGroupRootState>;\n}\n\ntype ValueSet = ReadonlySet<string>;\n\nconst EMPTY: ValueSet = new Set();\n\nfunction toSet(value: readonly string[]): ValueSet {\n return new Set(value);\n}\n\nfunction setsEqual(a: ValueSet, b: ValueSet): boolean {\n if (a.size !== b.size) return false;\n for (const v of a) if (!b.has(v)) return false;\n return true;\n}\n\n/** The optimistic reducer (design 5.2): apply a patch, so stacked dispatches re-base. */\nfunction reduce(set: ValueSet, patch: CheckboxGroupPatch): ValueSet {\n const next = new Set(set);\n for (const v of patch.removed) next.delete(v);\n for (const v of patch.added) next.add(v);\n return next;\n}\n\n/**\n * `values` sorted by `order` (the Items' values in document order); values not in `order` keep\n * their iteration order, after the others.\n */\nfunction sortByOrder(values: Iterable<string>, order: readonly string[]): string[] {\n const wanted = new Set(values);\n const sorted: string[] = [];\n for (const v of order) if (wanted.delete(v)) sorted.push(v);\n for (const v of wanted) sorted.push(v);\n return sorted;\n}\n\n/** Value → \"has an Item without its own `disabled`\", over the registered Items. */\ntype ItemRegistry = Map<object, { readonly value: string; readonly disabled: boolean }>;\n\nfunction summarize(registry: ItemRegistry): Map<string, boolean> {\n const items = new Map<string, boolean>();\n for (const { value, disabled } of registry.values()) {\n items.set(value, (items.get(value) ?? false) || !disabled);\n }\n return items;\n}\n\nfunction disabledOf(items: ReadonlyMap<string, boolean> | null): ValueSet {\n if (items === null) return EMPTY;\n const disabled = new Set<string>();\n for (const [value, enabled] of items) if (!enabled) disabled.add(value);\n return disabled.size === 0 ? EMPTY : disabled;\n}\n\nfunction mapsEqual(a: ReadonlyMap<string, boolean>, b: ReadonlyMap<string, boolean>): boolean {\n if (a.size !== b.size) return false;\n for (const [k, v] of a) if (b.get(k) !== v) return false;\n return true;\n}\n\nfunction arraysEqual(a: readonly string[], b: readonly string[]): boolean {\n return a.length === b.length && a.every((v, i) => v === b[i]);\n}\n\n/** The group's Items: checkboxes of this `name` in the fieldset (Parents have none). */\nfunction readItems(fieldset: HTMLFieldSetElement | null, name: string): HTMLInputElement[] {\n if (fieldset === null) return [];\n return [...fieldset.querySelectorAll<HTMLInputElement>('input[type=\"checkbox\"]')].filter(\n (box) => box.name === name,\n );\n}\n\n/**\n * `<fieldset>` that owns the group's state (a set of values), the transition, the shared `name`,\n * the form-reset listener, the mount reconciliation and the group-level `required`\n * (design/components/checkbox-group.md 14.3). Carries `data-pending` while `valueChangeAction`\n * is in flight. Its implicit role is `group`; no `role` is written.\n *\n * Owned keys (conventions 1.2): `disabled`, `form`, `data-pending`, `data-disabled`.\n *\n * Unlike RadioGroup, Items may hydrate after their Root (a Suspense boundary between them): a\n * click before an Item hydrated is reconciled when it does.\n */\nexport function CheckboxGroupRoot({\n value,\n defaultValue,\n valueChangeAction,\n name: nameProp,\n form,\n required = false,\n disabled = false,\n className,\n style,\n children,\n render,\n ...rest\n}: CheckboxGroupRootProps) {\n const fallbackName = useId();\n const name = nameProp ?? fallbackName;\n const fieldsetRef = useRef<HTMLFieldSetElement | null>(null);\n const warnedElement = useRef(false);\n const warnedDuplicate = useRef(false);\n /** Parent reports waiting for the next mount read (design 8.1). */\n const parentReports = useRef<{ values: readonly string[]; checked: boolean }[]>([]);\n\n // Item registration (design 6.2, 7): each Item reports its value and own `disabled` from a\n // layout effect. The ref is current for the residual-path reads; the state, set only when the\n // summary changes, is what render uses. `null` until an Item registered, so the server render\n // and the hydration render (which cannot know) agree.\n const itemRegistry = useRef<ItemRegistry>(new Map());\n const [items, setItems] = useState<ReadonlyMap<string, boolean> | null>(null);\n const registerItem = useCallback((value: string, disabled: boolean) => {\n const publish = () => {\n const next = summarize(itemRegistry.current);\n setItems((prev) => (prev !== null && mapsEqual(prev, next) ? prev : next));\n };\n const token = {};\n itemRegistry.current.set(token, { value, disabled });\n publish();\n return () => {\n itemRegistry.current.delete(token);\n publish();\n };\n }, []);\n const disabledItems = useMemo(() => disabledOf(items), [items]);\n\n const controlledSet = useMemo(() => (value === undefined ? undefined : toSet(value)), [value]);\n const defaultSet = useMemo(\n () => (defaultValue === undefined ? undefined : toSet(defaultValue)),\n [defaultValue],\n );\n\n /** Sorts by the Items' current document order. Reads the DOM: handlers and effects only. */\n const sortByDocument = (values: Iterable<string>): string[] =>\n sortByOrder(\n values,\n readItems(fieldsetRef.current, name).map((item) => item.value),\n );\n\n const { optimistic, committed, pending, dispatch, latestInFlight } = useControllableAction<\n ValueSet,\n CheckboxGroupValueChangeDetails\n >({\n controlled: controlledSet,\n defaultValue: defaultSet,\n initialValue: EMPTY,\n action:\n valueChangeAction && ((next, details) => valueChangeAction(sortByDocument(next), details)),\n transitionTypes: (_next, _prev, details) => [\n ...transitionTypes(\"checkbox-group\"),\n ...(details.added.length > 0 ? [\"fsui-checkbox-group-check\"] : []),\n ...(details.removed.length > 0 ? [\"fsui-checkbox-group-uncheck\"] : []),\n ],\n reducer: (prev, _next, details) => reduce(prev, details),\n warn: { component: \"CheckboxGroup.Root\", prop: \"value\" },\n });\n // Deliberately no warning when neither `value` nor `defaultValue` is given: an empty group is\n // the HTML default (design 3.1).\n\n /** What the user intends: the latest in-flight dispatch, else optimistic (conventions 3.8 rule 1). */\n const base = (): ValueSet => {\n const inFlight = latestInFlight();\n return inFlight === null ? optimistic : inFlight.next;\n };\n\n /** Dispatches `patch` on top of `from`, with the details of design 3.1. */\n const dispatchPatch = (\n from: ValueSet,\n patch: CheckboxGroupPatch,\n reason: ChangeReason,\n event: Event | undefined,\n ): void => {\n const details = createActionDetails(event, {\n reason,\n added: sortByDocument(patch.added),\n removed: sortByDocument(patch.removed),\n });\n dispatch(reduce(from, details), details);\n };\n\n /**\n * The group as the DOM shows it (`checked`) or as a form reset leaves it (`defaultChecked`),\n * laid over `base()` so that values with no Item are kept (they have no DOM to read).\n */\n const readDom = (prop: \"checked\" | \"defaultChecked\"): Map<string, boolean> =>\n new Map(readItems(fieldsetRef.current, name).map((item) => [item.value, item[prop]]));\n const readGroup = (\n prop: \"checked\" | \"defaultChecked\",\n from: ValueSet,\n dom: ReadonlyMap<string, boolean> = readDom(prop),\n ): Set<string> => {\n const next = new Set(from);\n for (const [v, isChecked] of dom) {\n if (isChecked) next.add(v);\n else next.delete(v);\n }\n return next;\n };\n\n /**\n * The Root's own mount reconciliation ran: later member mounts are \"late\" (8.3). Set after the\n * adapter's mount effect; reset by the cleanup (StrictMode included).\n */\n const rootMounted = useRef(false);\n /**\n * Late mounts: the members' `checked` read in the first late member's mount layout effect. The\n * adapter reads the group in a microtask, but the Items' registration re-renders the group\n * right after the layout effects, and that re-render re-applies each Item's `checked`, erasing a\n * click made before the Item hydrated. Every member of the commit is in the DOM by then.\n */\n const lateSnapshot = useRef<Map<string, boolean> | null>(null);\n\n // The residual browser paths (design section 8): one dispatch per group, from here. Items and\n // Parents that mount after the Root report through `onItemMounted`.\n const { onItemMounted: onMemberMounted } = useNativeGroupReconciliation<ValueSet>({\n element: fieldsetRef,\n readCurrent: () => {\n const next = readGroup(\"checked\", base(), lateSnapshot.current ?? undefined);\n lateSnapshot.current = null;\n // A Parent clicked before hydration means what its click means: check / uncheck all of its\n // values whose Items are enabled (8.1, 6.2). Read from the registry ref: the Items of this\n // commit registered before this runs, and render has not caught up yet.\n const disabledNow = disabledOf(summarize(itemRegistry.current));\n for (const report of parentReports.current) {\n for (const v of enabledValues(report.values, disabledNow)) {\n if (report.checked) next.add(v);\n else next.delete(v);\n }\n }\n parentReports.current = [];\n return next;\n },\n // Uncontrolled: start from `defaultValue`, so values with no Item return to it too (they have\n // no `defaultChecked` to read). Controlled: from `base()`, as for the mount read; the parent\n // decides.\n readDefault: () =>\n readGroup(\"defaultChecked\", value === undefined ? (defaultSet ?? EMPTY) : base()),\n base,\n equals: setsEqual,\n onPlatformChange: (next, event) => {\n const from = base();\n const added = [...next].filter((v) => !from.has(v));\n const removed = [...from].filter((v) => !next.has(v));\n dispatchPatch(from, { added, removed }, \"platform\", event);\n },\n });\n\n useLayoutEffect(() => {\n rootMounted.current = true;\n return () => {\n rootMounted.current = false;\n };\n }, []);\n const onItemMounted = (el: HTMLInputElement | null) => {\n const late = rootMounted.current;\n if (late && lateSnapshot.current === null) lateSnapshot.current = readDom(\"checked\");\n onMemberMounted(el);\n // A snapshot the adapter did not consume (it queues one read per commit, before this).\n if (late) {\n queueMicrotask(() => {\n lateSnapshot.current = null;\n });\n }\n };\n\n // The state's `value` is in document order. Render cannot read the DOM, so the Items' order is\n // kept in state, refreshed after a commit only when the rendered `value` would change (a value\n // given out of document order, Items reordered or added). The first render, and the server,\n // use the value's own order.\n const [itemOrder, setItemOrder] = useState<readonly string[]>([]);\n const renderedValue = sortByOrder(optimistic, itemOrder);\n // After every commit of the Root (a deps-less layout effect runs only when the Root itself\n // committed: Items moved by a child that re-renders alone are picked up at the Root's next\n // render). The comparison ends the chain: the re-render it causes renders exactly the value\n // just compared against.\n const syncItemOrder = useEffectEvent(() => {\n const order = readItems(fieldsetRef.current, name).map((item) => item.value);\n if (!arraysEqual(sortByOrder(optimistic, order), renderedValue)) setItemOrder(order);\n\n // Dev only: Item values must be unique within the Root (design 14.6).\n if (!isDev || warnedDuplicate.current) return;\n const duplicate = order.find((v, i) => order.indexOf(v) !== i);\n if (duplicate !== undefined) {\n warnedDuplicate.current = true;\n console.error(\n `CheckboxGroup.Item: two Items share the value \"${duplicate}\" in one ` +\n `CheckboxGroup.Root. Item values must be unique; both boxes follow the same value.`,\n );\n }\n });\n useLayoutEffect(() => {\n syncItemOrder();\n });\n\n const libraryRef = useCallback((el: HTMLFieldSetElement) => {\n fieldsetRef.current = el;\n if (isDev && !warnedElement.current && el.tagName !== \"FIELDSET\") {\n warnedElement.current = true;\n console.error(\n `CheckboxGroup.Root: expected to render a <fieldset> element but got ` +\n `<${el.tagName.toLowerCase()}>. The platform semantics (legend naming, disabled ` +\n `inheritance, form reset) need a real <fieldset>.`,\n );\n }\n return () => {\n fieldsetRef.current = null;\n };\n }, []);\n\n const context: CheckboxGroupContextValue = {\n name,\n form,\n required:\n required &&\n (items === null\n ? optimistic.size === 0\n : ![...optimistic].some((v) => items.get(v) === true)),\n disabledItems,\n registerItem,\n optimistic,\n committed,\n pending,\n disabled,\n defaultValue: value === undefined ? (defaultSet ?? EMPTY) : undefined,\n onItemMounted,\n reportParent: (values, checked) => {\n parentReports.current.push({ values, checked });\n },\n value: renderedValue,\n sortByDocument,\n toggle: (item, checked, event) => {\n // No-op rule against the optimistic set (conventions 2.3 item 8).\n if (optimistic.has(item) === checked) return;\n const patch = checked ? { added: [item], removed: [] } : { added: [], removed: [item] };\n dispatchPatch(optimistic, patch, \"activate\", event);\n },\n apply: (patch, event) => {\n const added = patch.added.filter((v) => !optimistic.has(v));\n const removed = patch.removed.filter((v) => optimistic.has(v));\n if (added.length === 0 && removed.length === 0) return;\n dispatchPatch(optimistic, { added, removed }, \"activate\", event);\n },\n };\n\n const state: CheckboxGroupRootState = {\n value: renderedValue,\n pending,\n disabled,\n };\n\n const element = useRender<ComponentPropsWithRef<\"fieldset\">, CheckboxGroupRootState>({\n defaultElement: \"fieldset\",\n render,\n state,\n className,\n style,\n children,\n props: rest,\n libraryProps: {\n disabled,\n form,\n \"data-pending\": dataAttr(pending),\n \"data-disabled\": dataAttr(disabled),\n },\n ref: libraryRef,\n });\n\n return <CheckboxGroupContext value={context}>{element}</CheckboxGroupContext>;\n}\n\nCheckboxGroupRoot.displayName = \"CheckboxGroupRoot\";\n"],"mappings":"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"}
@@ -0,0 +1,23 @@
1
+ import "../internal/platform-attributes.mjs";
2
+ import { CheckboxGroupItem, CheckboxGroupItemProps, CheckboxGroupItemState } from "./checkbox-group-item.mjs";
3
+ import { CheckboxGroupRoot, CheckboxGroupRootProps, CheckboxGroupRootState, CheckboxGroupValueChangeDetails } from "./checkbox-group-root.mjs";
4
+ import { CheckboxGroupLabel, CheckboxGroupLabelProps } from "./checkbox-group-label.mjs";
5
+ import { CheckboxGroupParent, CheckboxGroupParentProps, CheckboxGroupParentState } from "./checkbox-group-parent.mjs";
6
+ //#region src/checkbox-group/index.d.ts
7
+ /**
8
+ * CheckboxGroup (WAI-ARIA APG Checkbox pattern, native HTML variant): `Root` (`fieldset`, role
9
+ * `group`), an optional `Label` (`legend`, first child), one `Item` (`input[type=checkbox]`) per
10
+ * value, and optional `Parent` boxes (the mixed-state "select all" over a subset of values). The
11
+ * value is a set of strings; the keyboard is entirely native. Each box's DOM `checked` is
12
+ * optimistic; the Root and the boxes whose state is changing carry `data-pending` while
13
+ * `valueChangeAction` is in flight.
14
+ */
15
+ export declare const CheckboxGroup: {
16
+ readonly Root: typeof CheckboxGroupRoot;
17
+ readonly Label: typeof CheckboxGroupLabel;
18
+ readonly Item: typeof CheckboxGroupItem;
19
+ readonly Parent: typeof CheckboxGroupParent;
20
+ };
21
+ //#endregion
22
+ export type { CheckboxGroupItemProps, CheckboxGroupItemState, CheckboxGroupLabelProps, CheckboxGroupParentProps, CheckboxGroupParentState, CheckboxGroupRootProps, CheckboxGroupRootState, CheckboxGroupValueChangeDetails };
23
+ //# sourceMappingURL=index.d.mts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.mts","names":[],"sources":["../../src/checkbox-group/index.ts"],"mappings":";;;;;;;;;;;;;;qBAgBa;WACX,aAAI;WACJ,cAAK;WACL,aAAI;WACJ,eAAM"}
@@ -0,0 +1,23 @@
1
+ import { CheckboxGroupItem } from "./checkbox-group-item.mjs";
2
+ import { CheckboxGroupLabel } from "./checkbox-group-label.mjs";
3
+ import { CheckboxGroupParent } from "./checkbox-group-parent.mjs";
4
+ import { CheckboxGroupRoot } from "./checkbox-group-root.mjs";
5
+ //#region src/checkbox-group/index.ts
6
+ /**
7
+ * CheckboxGroup (WAI-ARIA APG Checkbox pattern, native HTML variant): `Root` (`fieldset`, role
8
+ * `group`), an optional `Label` (`legend`, first child), one `Item` (`input[type=checkbox]`) per
9
+ * value, and optional `Parent` boxes (the mixed-state "select all" over a subset of values). The
10
+ * value is a set of strings; the keyboard is entirely native. Each box's DOM `checked` is
11
+ * optimistic; the Root and the boxes whose state is changing carry `data-pending` while
12
+ * `valueChangeAction` is in flight.
13
+ */
14
+ const CheckboxGroup = {
15
+ Root: CheckboxGroupRoot,
16
+ Label: CheckboxGroupLabel,
17
+ Item: CheckboxGroupItem,
18
+ Parent: CheckboxGroupParent
19
+ };
20
+ //#endregion
21
+ export { CheckboxGroup };
22
+
23
+ //# sourceMappingURL=index.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.mjs","names":[],"sources":["../../src/checkbox-group/index.ts"],"sourcesContent":["// Ships the platform attribute typings with this subpath (conventions 7.4). Type-only: the\n// built JS has no import of it.\nimport \"../internal/platform-attributes.js\";\nimport { CheckboxGroupItem } from \"./checkbox-group-item.js\";\nimport { CheckboxGroupLabel } from \"./checkbox-group-label.js\";\nimport { CheckboxGroupParent } from \"./checkbox-group-parent.js\";\nimport { CheckboxGroupRoot } from \"./checkbox-group-root.js\";\n\n/**\n * CheckboxGroup (WAI-ARIA APG Checkbox pattern, native HTML variant): `Root` (`fieldset`, role\n * `group`), an optional `Label` (`legend`, first child), one `Item` (`input[type=checkbox]`) per\n * value, and optional `Parent` boxes (the mixed-state \"select all\" over a subset of values). The\n * value is a set of strings; the keyboard is entirely native. Each box's DOM `checked` is\n * optimistic; the Root and the boxes whose state is changing carry `data-pending` while\n * `valueChangeAction` is in flight.\n */\nexport const CheckboxGroup = {\n Root: CheckboxGroupRoot,\n Label: CheckboxGroupLabel,\n Item: CheckboxGroupItem,\n Parent: CheckboxGroupParent,\n} as const;\n\nexport type {\n CheckboxGroupRootProps,\n CheckboxGroupRootState,\n CheckboxGroupValueChangeDetails,\n} from \"./checkbox-group-root.js\";\nexport type { CheckboxGroupLabelProps } from \"./checkbox-group-label.js\";\nexport type { CheckboxGroupItemProps, CheckboxGroupItemState } from \"./checkbox-group-item.js\";\nexport type {\n CheckboxGroupParentProps,\n CheckboxGroupParentState,\n} from \"./checkbox-group-parent.js\";\n"],"mappings":";;;;;;;;;;;;;AAgBA,MAAa,gBAAgB;CAC3B,MAAM;CACN,OAAO;CACP,MAAM;CACN,QAAQ;AACV"}
@@ -0,0 +1,34 @@
1
+ import { RenderProp, StateValue } from "../internal/types.mjs";
2
+ import { CSSProperties, ComponentPropsWithRef, ReactNode } from "react";
3
+ //#region src/combobox/combobox-clear.d.ts
4
+ export interface ComboboxClearState {
5
+ /** Optimistic selection. */
6
+ readonly value: string | null;
7
+ readonly inputValue: string;
8
+ readonly disabled: boolean;
9
+ }
10
+ export interface ComboboxClearProps extends Omit<ComponentPropsWithRef<"button">, "onClick" | "onPointerDown" | "onMouseDown" | "tabIndex" | "disabled" | "className" | "style" | "children"> {
11
+ /** Disables this button; the Root's `disabled` does too. */
12
+ disabled?: boolean;
13
+ className?: StateValue<string | undefined, ComboboxClearState>;
14
+ style?: StateValue<CSSProperties | undefined, ComboboxClearState>;
15
+ children?: StateValue<ReactNode, ComboboxClearState>;
16
+ /** Should render a `<button>`. */
17
+ render?: RenderProp<ComponentPropsWithRef<"button">, ComboboxClearState>;
18
+ }
19
+ /**
20
+ * The optional button that clears the combobox: `<button type="button" tabindex="-1">`. A click
21
+ * empties the text (urgent) and dispatches `valueChangeAction(null)` unless nothing is selected;
22
+ * the input keeps focus. Always rendered: hide it with CSS (`:placeholder-shown`) or `state`.
23
+ * A click while the listbox is open closes it (the platform's light dismiss: the button is
24
+ * outside the popup).
25
+ *
26
+ * Owned keys: `type` (on a `<button>`), `tabIndex`, `disabled`, `onClick`, `onPointerDown`,
27
+ * `onMouseDown`, `data-disabled`.
28
+ */
29
+ export declare function ComboboxClear({ disabled: ownDisabled, className, style, children, render, ...rest }: ComboboxClearProps): ReactNode;
30
+ export declare namespace ComboboxClear {
31
+ var displayName: string;
32
+ }
33
+ //#endregion
34
+ //# sourceMappingURL=combobox-clear.d.mts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"combobox-clear.d.mts","names":[],"sources":["../../src/combobox/combobox-clear.tsx"],"mappings":";;;iBAeiB;;WAEN;WACA;WACA;;iBAGM,2BAA2B,KAC1C;;EAWA;EACA,YAAY,+BAA+B;EAC3C,QAAQ,WAAW,2BAA2B;EAC9C,WAAW,WAAW,WAAW;;EAEjC,SAAS,WAAW,iCAAiC;;;;;;;;;;;;wBAavC,gBACd,UAAU,aACV,WACA,OACA,UACA,WACG,QACF,qBAAkB;yBAPL"}
@@ -0,0 +1,58 @@
1
+ "use client";
2
+ import { dataAttr } from "../internal/data-attr.mjs";
3
+ import { rendersButton, useRender } from "../internal/use-render.mjs";
4
+ import { useComboboxContext } from "./combobox-context.mjs";
5
+ import { useNameWarning } from "./combobox-trigger.mjs";
6
+ //#region src/combobox/combobox-clear.tsx
7
+ /**
8
+ * The optional button that clears the combobox: `<button type="button" tabindex="-1">`. A click
9
+ * empties the text (urgent) and dispatches `valueChangeAction(null)` unless nothing is selected;
10
+ * the input keeps focus. Always rendered: hide it with CSS (`:placeholder-shown`) or `state`.
11
+ * A click while the listbox is open closes it (the platform's light dismiss: the button is
12
+ * outside the popup).
13
+ *
14
+ * Owned keys: `type` (on a `<button>`), `tabIndex`, `disabled`, `onClick`, `onPointerDown`,
15
+ * `onMouseDown`, `data-disabled`.
16
+ */
17
+ function ComboboxClear({ disabled: ownDisabled = false, className, style, children, render, ...rest }) {
18
+ const ctx = useComboboxContext("Combobox.Clear");
19
+ const disabled = ownDisabled || ctx.disabled;
20
+ const ref = useNameWarning("Combobox.Clear");
21
+ const state = {
22
+ value: ctx.value,
23
+ inputValue: ctx.inputValue,
24
+ disabled
25
+ };
26
+ const libraryProps = {
27
+ ...rendersButton(render) && { type: "button" },
28
+ tabIndex: -1,
29
+ disabled,
30
+ onPointerDown: (event) => {
31
+ event.preventDefault();
32
+ },
33
+ onMouseDown: (event) => {
34
+ event.preventDefault();
35
+ },
36
+ onClick: (event) => {
37
+ if (disabled) return;
38
+ ctx.clear(event.nativeEvent);
39
+ },
40
+ "data-disabled": dataAttr(disabled)
41
+ };
42
+ return useRender({
43
+ defaultElement: "button",
44
+ render,
45
+ state,
46
+ className,
47
+ style,
48
+ children,
49
+ props: rest,
50
+ libraryProps,
51
+ ref
52
+ });
53
+ }
54
+ ComboboxClear.displayName = "ComboboxClear";
55
+ //#endregion
56
+ export { ComboboxClear };
57
+
58
+ //# sourceMappingURL=combobox-clear.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"combobox-clear.mjs","names":[],"sources":["../../src/combobox/combobox-clear.tsx"],"sourcesContent":["\"use client\";\nimport type {\n ComponentPropsWithRef,\n CSSProperties,\n MouseEvent,\n PointerEvent,\n ReactNode,\n} from \"react\";\nimport { dataAttr } from \"../internal/data-attr.js\";\nimport type { RenderProp, StateValue } from \"../internal/types.js\";\nimport { rendersButton, useRender } from \"../internal/use-render.js\";\nimport type { DataAttributes } from \"../internal/use-render.js\";\nimport { useComboboxContext } from \"./combobox-context.js\";\nimport { useNameWarning } from \"./combobox-trigger.js\";\n\nexport interface ComboboxClearState {\n /** Optimistic selection. */\n readonly value: string | null;\n readonly inputValue: string;\n readonly disabled: boolean;\n}\n\nexport interface ComboboxClearProps extends Omit<\n ComponentPropsWithRef<\"button\">,\n | \"onClick\"\n | \"onPointerDown\"\n | \"onMouseDown\"\n | \"tabIndex\"\n | \"disabled\"\n | \"className\"\n | \"style\"\n | \"children\"\n> {\n /** Disables this button; the Root's `disabled` does too. */\n disabled?: boolean;\n className?: StateValue<string | undefined, ComboboxClearState>;\n style?: StateValue<CSSProperties | undefined, ComboboxClearState>;\n children?: StateValue<ReactNode, ComboboxClearState>;\n /** Should render a `<button>`. */\n render?: RenderProp<ComponentPropsWithRef<\"button\">, ComboboxClearState>;\n}\n\n/**\n * The optional button that clears the combobox: `<button type=\"button\" tabindex=\"-1\">`. A click\n * empties the text (urgent) and dispatches `valueChangeAction(null)` unless nothing is selected;\n * the input keeps focus. Always rendered: hide it with CSS (`:placeholder-shown`) or `state`.\n * A click while the listbox is open closes it (the platform's light dismiss: the button is\n * outside the popup).\n *\n * Owned keys: `type` (on a `<button>`), `tabIndex`, `disabled`, `onClick`, `onPointerDown`,\n * `onMouseDown`, `data-disabled`.\n */\nexport function ComboboxClear({\n disabled: ownDisabled = false,\n className,\n style,\n children,\n render,\n ...rest\n}: ComboboxClearProps) {\n const ctx = useComboboxContext(\"Combobox.Clear\");\n const disabled = ownDisabled || ctx.disabled;\n const ref = useNameWarning(\"Combobox.Clear\");\n const state: ComboboxClearState = { value: ctx.value, inputValue: ctx.inputValue, disabled };\n\n const libraryProps: ComponentPropsWithRef<\"button\"> & DataAttributes = {\n ...(rendersButton(render) && { type: \"button\" as const }),\n tabIndex: -1,\n disabled,\n onPointerDown: (event: PointerEvent<HTMLButtonElement>) => {\n event.preventDefault();\n },\n onMouseDown: (event: MouseEvent<HTMLButtonElement>) => {\n event.preventDefault();\n },\n onClick: (event: MouseEvent<HTMLButtonElement>) => {\n if (disabled) return;\n ctx.clear(event.nativeEvent);\n },\n \"data-disabled\": dataAttr(disabled),\n };\n\n return useRender<ComponentPropsWithRef<\"button\">, ComboboxClearState>({\n defaultElement: \"button\",\n render,\n state,\n className,\n style,\n children,\n props: rest,\n libraryProps,\n ref,\n });\n}\n\nComboboxClear.displayName = \"ComboboxClear\";\n"],"mappings":";;;;;;;;;;;;;;;;AAoDA,SAAgB,cAAc,EAC5B,UAAU,cAAc,OACxB,WACA,OACA,UACA,QACA,GAAG,QACkB;CACrB,MAAM,MAAM,mBAAmB,gBAAgB;CAC/C,MAAM,WAAW,eAAe,IAAI;CACpC,MAAM,MAAM,eAAe,gBAAgB;CAC3C,MAAM,QAA4B;EAAE,OAAO,IAAI;EAAO,YAAY,IAAI;EAAY;CAAS;CAE3F,MAAM,eAAiE;EACrE,GAAI,cAAc,MAAM,KAAK,EAAE,MAAM,SAAkB;EACvD,UAAU;EACV;EACA,gBAAgB,UAA2C;GACzD,MAAM,eAAe;EACvB;EACA,cAAc,UAAyC;GACrD,MAAM,eAAe;EACvB;EACA,UAAU,UAAyC;GACjD,IAAI,UAAU;GACd,IAAI,MAAM,MAAM,WAAW;EAC7B;EACA,iBAAiB,SAAS,QAAQ;CACpC;CAEA,OAAO,UAA+D;EACpE,gBAAgB;EAChB;EACA;EACA;EACA;EACA;EACA,OAAO;EACP;EACA;CACF,CAAC;AACH;AAEA,cAAc,cAAc"}
@@ -0,0 +1,23 @@
1
+ "use client";
2
+ import { createContext, use } from "react";
3
+ //#region src/combobox/combobox-context.ts
4
+ const ComboboxContext = createContext(null);
5
+ function useComboboxContext(part) {
6
+ const context = use(ComboboxContext);
7
+ if (context === null) throw new Error(`${part} must be rendered inside a Combobox.Root.`);
8
+ return context;
9
+ }
10
+ /** Provided by `Combobox.Popup` (a List outside a Popup warns). */
11
+ const ComboboxPopupContext = createContext(false);
12
+ /** Provided by `Combobox.List` (an Option outside a List warns). */
13
+ const ComboboxListContext = createContext(false);
14
+ /** `Combobox.Group`: the id its `Combobox.GroupLabel` renders. */
15
+ const ComboboxGroupContext = createContext(null);
16
+ /** The id fragment of an option value: stable across result sets, no whitespace (design 3.5). */
17
+ function optionId(prefix, value) {
18
+ return `${prefix}${encodeURIComponent(value)}`;
19
+ }
20
+ //#endregion
21
+ export { ComboboxContext, ComboboxGroupContext, ComboboxListContext, ComboboxPopupContext, optionId, useComboboxContext };
22
+
23
+ //# sourceMappingURL=combobox-context.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"combobox-context.mjs","names":[],"sources":["../../src/combobox/combobox-context.ts"],"sourcesContent":["\"use client\";\nimport { createContext, use } from \"react\";\nimport type { RefObject } from \"react\";\nimport type { PopoverRootValue } from \"../internal/use-popover-root.js\";\n\n/** What a mounted `Combobox.Option` tells its Root (design 2, \"mounted options, labels\"). */\nexport interface ComboboxOptionEntry {\n readonly element: HTMLElement;\n /** The `label` prop; `undefined`: the element's trimmed `textContent` at the moment of use. */\n readonly label: string | undefined;\n readonly disabled: boolean;\n}\n\n/** One Combobox: what its Root provides to every part (design 14, `combobox-context.ts`). */\nexport interface ComboboxContextValue {\n /** The shared popover machinery (`usePopoverRoot`, library mode). */\n readonly popover: PopoverRootValue;\n readonly ids: {\n readonly input: string;\n readonly label: string;\n readonly list: string;\n /** `${root}-option-`: an Option's id is this plus `encodeURIComponent(value)`. */\n readonly optionPrefix: string;\n };\n /** Optimistic selection. */\n readonly value: string | null;\n /** The `valueChangeAction` transition is in flight. */\n readonly valuePending: boolean;\n /** `valuePending` or the open transition is in flight (the Root's `pending`). */\n readonly pending: boolean;\n /** The urgent text. */\n readonly inputValue: string;\n /** `useDeferredValue(inputValue)`: what the mounted results are for. */\n readonly query: string;\n readonly stale: boolean;\n /** The option `aria-activedescendant` points at (urgent virtual focus), or `null`. */\n readonly activeValue: string | null;\n /** No option is mounted and the results are settled (`Empty`, `data-empty`). */\n readonly empty: boolean;\n /** A `Combobox.Status` is mounted inside the List (a Suspense fallback): results are loading. */\n readonly loading: boolean;\n /** A `Combobox.Label` is mounted (the List's `aria-labelledby` names it only then). */\n readonly hasLabel: boolean;\n readonly disabled: boolean;\n readonly inputRef: RefObject<HTMLInputElement | null>;\n readonly listRef: RefObject<HTMLElement | null>;\n /** Registers a mounted Option; returns the unregistration. */\n readonly registerOption: (value: string, entry: ComboboxOptionEntry) => () => void;\n /** Registers a mounted `Combobox.Status`; while one is mounted the results count as loading. */\n readonly registerStatus: () => () => void;\n /** Registers a mounted `Combobox.Label`; returns the unregistration. */\n readonly registerLabel: () => () => void;\n /** Make `value` the active option (urgent; never dispatches). */\n readonly setActive: (value: string | null) => void;\n /** Accept an option (design 5.2): urgent text write, dispatch, close when it settles. */\n readonly accept: (value: string, event: Event) => void;\n /** `Combobox.Clear` (design 3.3). */\n readonly clear: (event: Event) => void;\n /** The `change` of the Input: urgent text write and an open request (design 2.2, 5.1). */\n readonly typed: (text: string, event: Event) => void;\n /** Keyboard model of design section 8, run by the Input's `onKeyDown`. */\n readonly keyDown: (event: KeyboardEvent) => boolean;\n /** Focus left the Input (design 5.4): close at once, then revert or commit free text. */\n readonly focusOut: (event: FocusEvent) => void;\n /** A pointer click in the Input (`openOnClick`). */\n readonly inputClick: (event: MouseEvent) => void;\n}\n\nexport const ComboboxContext = createContext<ComboboxContextValue | null>(null);\n\nexport function useComboboxContext(part: string): ComboboxContextValue {\n const context = use(ComboboxContext);\n if (context === null) {\n throw new Error(`${part} must be rendered inside a Combobox.Root.`);\n }\n return context;\n}\n\n/** Provided by `Combobox.Popup` (a List outside a Popup warns). */\nexport const ComboboxPopupContext = createContext<boolean>(false);\n\n/** Provided by `Combobox.List` (an Option outside a List warns). */\nexport const ComboboxListContext = createContext<boolean>(false);\n\n/** `Combobox.Group`: the id its `Combobox.GroupLabel` renders. */\nexport const ComboboxGroupContext = createContext<string | null>(null);\n\n/** The id fragment of an option value: stable across result sets, no whitespace (design 3.5). */\nexport function optionId(prefix: string, value: string): string {\n return `${prefix}${encodeURIComponent(value)}`;\n}\n"],"mappings":";;;AAoEA,MAAa,kBAAkB,cAA2C,IAAI;AAE9E,SAAgB,mBAAmB,MAAoC;CACrE,MAAM,UAAU,IAAI,eAAe;CACnC,IAAI,YAAY,MACd,MAAM,IAAI,MAAM,GAAG,KAAK,0CAA0C;CAEpE,OAAO;AACT;;AAGA,MAAa,uBAAuB,cAAuB,KAAK;;AAGhE,MAAa,sBAAsB,cAAuB,KAAK;;AAG/D,MAAa,uBAAuB,cAA6B,IAAI;;AAGrE,SAAgB,SAAS,QAAgB,OAAuB;CAC9D,OAAO,GAAG,SAAS,mBAAmB,KAAK;AAC7C"}
@@ -0,0 +1,72 @@
1
+ import { RenderProp, StateValue } from "../internal/types.mjs";
2
+ import { ComboboxListState } from "./combobox-list.mjs";
3
+ import { CSSProperties, ComponentPropsWithRef, ReactNode } from "react";
4
+ //#region src/combobox/combobox-group.d.ts
5
+ /** `Combobox.Group`, `Combobox.GroupLabel` and `Combobox.Status` have no state of their own. */
6
+ export type ComboboxGroupState = Record<string, never>;
7
+ export type ComboboxGroupLabelState = Record<string, never>;
8
+ export type ComboboxStatusState = Record<string, never>;
9
+ /** `Combobox.Empty` renders from the List's state. */
10
+ export type ComboboxEmptyState = ComboboxListState;
11
+ export interface ComboboxGroupProps extends Omit<ComponentPropsWithRef<"div">, "role" | "className" | "style" | "children"> {
12
+ className?: StateValue<string | undefined, ComboboxGroupState>;
13
+ style?: StateValue<CSSProperties | undefined, ComboboxGroupState>;
14
+ children?: StateValue<ReactNode, ComboboxGroupState>;
15
+ render?: RenderProp<ComponentPropsWithRef<"div">, ComboboxGroupState>;
16
+ }
17
+ /**
18
+ * A labelled set of options: `<div role="group" aria-labelledby>` named by the
19
+ * `Combobox.GroupLabel` inside it. Owned keys: `role`, `aria-labelledby` (token-merged).
20
+ */
21
+ export declare function ComboboxGroup({ className, style, children, render, ...rest }: ComboboxGroupProps): import("react").JSX.Element;
22
+ export declare namespace ComboboxGroup {
23
+ var displayName: string;
24
+ }
25
+ export interface ComboboxGroupLabelProps extends Omit<ComponentPropsWithRef<"div">, "id" | "className" | "style" | "children"> {
26
+ className?: StateValue<string | undefined, ComboboxGroupLabelState>;
27
+ style?: StateValue<CSSProperties | undefined, ComboboxGroupLabelState>;
28
+ children?: StateValue<ReactNode, ComboboxGroupLabelState>;
29
+ render?: RenderProp<ComponentPropsWithRef<"div">, ComboboxGroupLabelState>;
30
+ }
31
+ /** The name of the enclosing `Combobox.Group`: a `<div>` with the referenced id. Owned key: `id`. */
32
+ export declare function ComboboxGroupLabel({ className, style, children, render, ...rest }: ComboboxGroupLabelProps): ReactNode;
33
+ export declare namespace ComboboxGroupLabel {
34
+ var displayName: string;
35
+ }
36
+ export interface ComboboxEmptyProps extends Omit<ComponentPropsWithRef<"div">, "className" | "style" | "children"> {
37
+ className?: StateValue<string | undefined, ComboboxEmptyState>;
38
+ style?: StateValue<CSSProperties | undefined, ComboboxEmptyState>;
39
+ children?: StateValue<ReactNode, ComboboxEmptyState>;
40
+ render?: RenderProp<ComponentPropsWithRef<"div">, ComboboxEmptyState>;
41
+ }
42
+ /**
43
+ * "No results": renders a `<div>` with its children only when the List has **no mounted
44
+ * options, the results are not stale and none are loading** (no `Combobox.Status` mounted inside
45
+ * the List, i.e. no Suspense fallback showing); nothing otherwise (design 6.3). Place it in the
46
+ * Popup, outside the List (a listbox holds only options). Owns no keys.
47
+ */
48
+ export declare function ComboboxEmpty({ className, style, children, render, ...rest }: ComboboxEmptyProps): ReactNode;
49
+ export declare namespace ComboboxEmpty {
50
+ var displayName: string;
51
+ }
52
+ export interface ComboboxStatusProps extends Omit<ComponentPropsWithRef<"div">, "role" | "className" | "style" | "children"> {
53
+ className?: StateValue<string | undefined, ComboboxStatusState>;
54
+ style?: StateValue<CSSProperties | undefined, ComboboxStatusState>;
55
+ children?: StateValue<ReactNode, ComboboxStatusState>;
56
+ render?: RenderProp<ComponentPropsWithRef<"div">, ComboboxStatusState>;
57
+ }
58
+ /**
59
+ * A loading row or a hint. Rendered **inside the List** (the usual Suspense fallback,
60
+ * `<Suspense fallback={<Combobox.Status>Searching…</Combobox.Status>}>`), it marks the results
61
+ * as loading: `<div role="none">` (a listbox owns only options and groups, and a live region
62
+ * there would talk over the count; the List is `aria-busy` instead), `Empty` stays hidden and
63
+ * nothing is announced while it is mounted. Rendered in the Popup outside the List it is a plain
64
+ * hint, `<div role="status">`. No live-region work of its own either way (the result count is
65
+ * announced with `ariaNotify`). Owned key: `role`.
66
+ */
67
+ export declare function ComboboxStatus({ className, style, children, render, ...rest }: ComboboxStatusProps): ReactNode;
68
+ export declare namespace ComboboxStatus {
69
+ var displayName: string;
70
+ }
71
+ //#endregion
72
+ //# sourceMappingURL=combobox-group.d.mts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"combobox-group.d.mts","names":[],"sources":["../../src/combobox/combobox-group.tsx"],"mappings":";;;;;YAcY,qBAAqB;YACrB,0BAA0B;YAC1B,sBAAsB;;YAEtB,qBAAqB;iBAIhB,2BAA2B,KAC1C;EAGA,YAAY,+BAA+B;EAC3C,QAAQ,WAAW,2BAA2B;EAC9C,WAAW,WAAW,WAAW;EACjC,SAAS,WAAW,8BAA8B;;;;;;wBAOpC,gBAAgB,WAAW,OAAO,UAAU,WAAW,QAAQ,qCAAkB,IAAA;yBAAjF;;;iBAkBC,gCAAgC,KAC/C;EAGA,YAAY,+BAA+B;EAC3C,QAAQ,WAAW,2BAA2B;EAC9C,WAAW,WAAW,WAAW;EACjC,SAAS,WAAW,8BAA8B;;;wBAIpC,qBACd,WACA,OACA,UACA,WACG,QACF,0BAAuB;yBANV;;;iBA0BC,2BAA2B,KAC1C;EAGA,YAAY,+BAA+B;EAC3C,QAAQ,WAAW,2BAA2B;EAC9C,WAAW,WAAW,WAAW;EACjC,SAAS,WAAW,8BAA8B;;;;;;;;wBASpC,gBAAgB,WAAW,OAAO,UAAU,WAAW,QAAQ,qBAAkB;yBAAjF;;;iBAiBC,4BAA4B,KAC3C;EAGA,YAAY,+BAA+B;EAC3C,QAAQ,WAAW,2BAA2B;EAC9C,WAAW,WAAW,WAAW;EACjC,SAAS,WAAW,8BAA8B;;;;;;;;;;;wBAYpC,iBACd,WACA,OACA,UACA,WACG,QACF,sBAAmB;yBANN"}