@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,104 @@
1
+ "use client";
2
+ import { useRender } from "../internal/use-render.mjs";
3
+ import { useDevWarning } from "../internal/use-dev-warning.mjs";
4
+ import { ComboboxGroupContext, ComboboxListContext, useComboboxContext } from "./combobox-context.mjs";
5
+ import { use, useId, useLayoutEffect } from "react";
6
+ import { jsx } from "react/jsx-runtime";
7
+ //#region src/combobox/combobox-group.tsx
8
+ const EMPTY = {};
9
+ /**
10
+ * A labelled set of options: `<div role="group" aria-labelledby>` named by the
11
+ * `Combobox.GroupLabel` inside it. Owned keys: `role`, `aria-labelledby` (token-merged).
12
+ */
13
+ function ComboboxGroup({ className, style, children, render, ...rest }) {
14
+ useComboboxContext("Combobox.Group");
15
+ const labelId = `${useId()}-label`;
16
+ const element = useRender({
17
+ defaultElement: "div",
18
+ render,
19
+ state: EMPTY,
20
+ className,
21
+ style,
22
+ children,
23
+ props: rest,
24
+ libraryProps: {
25
+ role: "group",
26
+ "aria-labelledby": labelId
27
+ }
28
+ });
29
+ return /* @__PURE__ */ jsx(ComboboxGroupContext, {
30
+ value: labelId,
31
+ children: element
32
+ });
33
+ }
34
+ ComboboxGroup.displayName = "ComboboxGroup";
35
+ /** The name of the enclosing `Combobox.Group`: a `<div>` with the referenced id. Owned key: `id`. */
36
+ function ComboboxGroupLabel({ className, style, children, render, ...rest }) {
37
+ const labelId = use(ComboboxGroupContext);
38
+ useDevWarning(labelId === null, "Combobox.GroupLabel: rendered outside a Combobox.Group; it names nothing.");
39
+ return useRender({
40
+ defaultElement: "div",
41
+ render,
42
+ state: EMPTY,
43
+ className,
44
+ style,
45
+ children,
46
+ props: rest,
47
+ libraryProps: labelId === null ? {} : { id: labelId }
48
+ });
49
+ }
50
+ ComboboxGroupLabel.displayName = "ComboboxGroupLabel";
51
+ /**
52
+ * "No results": renders a `<div>` with its children only when the List has **no mounted
53
+ * options, the results are not stale and none are loading** (no `Combobox.Status` mounted inside
54
+ * the List, i.e. no Suspense fallback showing); nothing otherwise (design 6.3). Place it in the
55
+ * Popup, outside the List (a listbox holds only options). Owns no keys.
56
+ */
57
+ function ComboboxEmpty({ className, style, children, render, ...rest }) {
58
+ const ctx = useComboboxContext("Combobox.Empty");
59
+ const state = {
60
+ query: ctx.query,
61
+ stale: ctx.stale,
62
+ empty: ctx.empty
63
+ };
64
+ const element = useRender({
65
+ defaultElement: "div",
66
+ render,
67
+ state,
68
+ className,
69
+ style,
70
+ children,
71
+ props: rest
72
+ });
73
+ return ctx.empty ? element : null;
74
+ }
75
+ ComboboxEmpty.displayName = "ComboboxEmpty";
76
+ /**
77
+ * A loading row or a hint. Rendered **inside the List** (the usual Suspense fallback,
78
+ * `<Suspense fallback={<Combobox.Status>Searching…</Combobox.Status>}>`), it marks the results
79
+ * as loading: `<div role="none">` (a listbox owns only options and groups, and a live region
80
+ * there would talk over the count; the List is `aria-busy` instead), `Empty` stays hidden and
81
+ * nothing is announced while it is mounted. Rendered in the Popup outside the List it is a plain
82
+ * hint, `<div role="status">`. No live-region work of its own either way (the result count is
83
+ * announced with `ariaNotify`). Owned key: `role`.
84
+ */
85
+ function ComboboxStatus({ className, style, children, render, ...rest }) {
86
+ const { registerStatus } = useComboboxContext("Combobox.Status");
87
+ const inList = use(ComboboxListContext);
88
+ useLayoutEffect(() => inList ? registerStatus() : void 0, [inList, registerStatus]);
89
+ return useRender({
90
+ defaultElement: "div",
91
+ render,
92
+ state: EMPTY,
93
+ className,
94
+ style,
95
+ children,
96
+ props: rest,
97
+ libraryProps: { role: inList ? "none" : "status" }
98
+ });
99
+ }
100
+ ComboboxStatus.displayName = "ComboboxStatus";
101
+ //#endregion
102
+ export { ComboboxEmpty, ComboboxGroup, ComboboxGroupLabel, ComboboxStatus };
103
+
104
+ //# sourceMappingURL=combobox-group.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"combobox-group.mjs","names":[],"sources":["../../src/combobox/combobox-group.tsx"],"sourcesContent":["\"use client\";\nimport { use, useId, useLayoutEffect } from \"react\";\nimport type { ComponentPropsWithRef, CSSProperties, ReactNode } from \"react\";\nimport type { RenderProp, StateValue } from \"../internal/types.js\";\nimport { useDevWarning } from \"../internal/use-dev-warning.js\";\nimport { useRender } from \"../internal/use-render.js\";\nimport {\n ComboboxGroupContext,\n ComboboxListContext,\n useComboboxContext,\n} from \"./combobox-context.js\";\nimport type { ComboboxListState } from \"./combobox-list.js\";\n\n/** `Combobox.Group`, `Combobox.GroupLabel` and `Combobox.Status` have no state of their own. */\nexport type ComboboxGroupState = Record<string, never>;\nexport type ComboboxGroupLabelState = Record<string, never>;\nexport type ComboboxStatusState = Record<string, never>;\n/** `Combobox.Empty` renders from the List's state. */\nexport type ComboboxEmptyState = ComboboxListState;\n\nconst EMPTY: Record<string, never> = {};\n\nexport interface ComboboxGroupProps extends Omit<\n ComponentPropsWithRef<\"div\">,\n \"role\" | \"className\" | \"style\" | \"children\"\n> {\n className?: StateValue<string | undefined, ComboboxGroupState>;\n style?: StateValue<CSSProperties | undefined, ComboboxGroupState>;\n children?: StateValue<ReactNode, ComboboxGroupState>;\n render?: RenderProp<ComponentPropsWithRef<\"div\">, ComboboxGroupState>;\n}\n\n/**\n * A labelled set of options: `<div role=\"group\" aria-labelledby>` named by the\n * `Combobox.GroupLabel` inside it. Owned keys: `role`, `aria-labelledby` (token-merged).\n */\nexport function ComboboxGroup({ className, style, children, render, ...rest }: ComboboxGroupProps) {\n useComboboxContext(\"Combobox.Group\");\n const labelId = `${useId()}-label`;\n const element = useRender<ComponentPropsWithRef<\"div\">, ComboboxGroupState>({\n defaultElement: \"div\",\n render,\n state: EMPTY,\n className,\n style,\n children,\n props: rest,\n libraryProps: { role: \"group\", \"aria-labelledby\": labelId },\n });\n return <ComboboxGroupContext value={labelId}>{element}</ComboboxGroupContext>;\n}\n\nComboboxGroup.displayName = \"ComboboxGroup\";\n\nexport interface ComboboxGroupLabelProps extends Omit<\n ComponentPropsWithRef<\"div\">,\n \"id\" | \"className\" | \"style\" | \"children\"\n> {\n className?: StateValue<string | undefined, ComboboxGroupLabelState>;\n style?: StateValue<CSSProperties | undefined, ComboboxGroupLabelState>;\n children?: StateValue<ReactNode, ComboboxGroupLabelState>;\n render?: RenderProp<ComponentPropsWithRef<\"div\">, ComboboxGroupLabelState>;\n}\n\n/** The name of the enclosing `Combobox.Group`: a `<div>` with the referenced id. Owned key: `id`. */\nexport function ComboboxGroupLabel({\n className,\n style,\n children,\n render,\n ...rest\n}: ComboboxGroupLabelProps) {\n const labelId = use(ComboboxGroupContext);\n useDevWarning(\n labelId === null,\n \"Combobox.GroupLabel: rendered outside a Combobox.Group; it names nothing.\",\n );\n return useRender<ComponentPropsWithRef<\"div\">, ComboboxGroupLabelState>({\n defaultElement: \"div\",\n render,\n state: EMPTY,\n className,\n style,\n children,\n props: rest,\n libraryProps: labelId === null ? {} : { id: labelId },\n });\n}\n\nComboboxGroupLabel.displayName = \"ComboboxGroupLabel\";\n\nexport interface ComboboxEmptyProps extends Omit<\n ComponentPropsWithRef<\"div\">,\n \"className\" | \"style\" | \"children\"\n> {\n className?: StateValue<string | undefined, ComboboxEmptyState>;\n style?: StateValue<CSSProperties | undefined, ComboboxEmptyState>;\n children?: StateValue<ReactNode, ComboboxEmptyState>;\n render?: RenderProp<ComponentPropsWithRef<\"div\">, ComboboxEmptyState>;\n}\n\n/**\n * \"No results\": renders a `<div>` with its children only when the List has **no mounted\n * options, the results are not stale and none are loading** (no `Combobox.Status` mounted inside\n * the List, i.e. no Suspense fallback showing); nothing otherwise (design 6.3). Place it in the\n * Popup, outside the List (a listbox holds only options). Owns no keys.\n */\nexport function ComboboxEmpty({ className, style, children, render, ...rest }: ComboboxEmptyProps) {\n const ctx = useComboboxContext(\"Combobox.Empty\");\n const state: ComboboxEmptyState = { query: ctx.query, stale: ctx.stale, empty: ctx.empty };\n const element = useRender<ComponentPropsWithRef<\"div\">, ComboboxEmptyState>({\n defaultElement: \"div\",\n render,\n state,\n className,\n style,\n children,\n props: rest,\n });\n return ctx.empty ? element : null;\n}\n\nComboboxEmpty.displayName = \"ComboboxEmpty\";\n\nexport interface ComboboxStatusProps extends Omit<\n ComponentPropsWithRef<\"div\">,\n \"role\" | \"className\" | \"style\" | \"children\"\n> {\n className?: StateValue<string | undefined, ComboboxStatusState>;\n style?: StateValue<CSSProperties | undefined, ComboboxStatusState>;\n children?: StateValue<ReactNode, ComboboxStatusState>;\n render?: RenderProp<ComponentPropsWithRef<\"div\">, ComboboxStatusState>;\n}\n\n/**\n * A loading row or a hint. Rendered **inside the List** (the usual Suspense fallback,\n * `<Suspense fallback={<Combobox.Status>Searching…</Combobox.Status>}>`), it marks the results\n * as loading: `<div role=\"none\">` (a listbox owns only options and groups, and a live region\n * there would talk over the count; the List is `aria-busy` instead), `Empty` stays hidden and\n * nothing is announced while it is mounted. Rendered in the Popup outside the List it is a plain\n * hint, `<div role=\"status\">`. No live-region work of its own either way (the result count is\n * announced with `ariaNotify`). Owned key: `role`.\n */\nexport function ComboboxStatus({\n className,\n style,\n children,\n render,\n ...rest\n}: ComboboxStatusProps) {\n const { registerStatus } = useComboboxContext(\"Combobox.Status\");\n const inList = use(ComboboxListContext);\n useLayoutEffect(() => (inList ? registerStatus() : undefined), [inList, registerStatus]);\n return useRender<ComponentPropsWithRef<\"div\">, ComboboxStatusState>({\n defaultElement: \"div\",\n render,\n state: EMPTY,\n className,\n style,\n children,\n props: rest,\n libraryProps: { role: inList ? \"none\" : \"status\" },\n });\n}\n\nComboboxStatus.displayName = \"ComboboxStatus\";\n"],"mappings":";;;;;;;AAoBA,MAAM,QAA+B,CAAC;;;;;AAgBtC,SAAgB,cAAc,EAAE,WAAW,OAAO,UAAU,QAAQ,GAAG,QAA4B;CACjG,mBAAmB,gBAAgB;CACnC,MAAM,UAAU,GAAG,MAAM,EAAE;CAC3B,MAAM,UAAU,UAA4D;EAC1E,gBAAgB;EAChB;EACA,OAAO;EACP;EACA;EACA;EACA,OAAO;EACP,cAAc;GAAE,MAAM;GAAS,mBAAmB;EAAQ;CAC5D,CAAC;CACD,OAAO,oBAAC,sBAAD;EAAsB,OAAO;EAAU,UAAA;CAA8B,CAAA;AAC9E;AAEA,cAAc,cAAc;;AAa5B,SAAgB,mBAAmB,EACjC,WACA,OACA,UACA,QACA,GAAG,QACuB;CAC1B,MAAM,UAAU,IAAI,oBAAoB;CACxC,cACE,YAAY,MACZ,2EACF;CACA,OAAO,UAAiE;EACtE,gBAAgB;EAChB;EACA,OAAO;EACP;EACA;EACA;EACA,OAAO;EACP,cAAc,YAAY,OAAO,CAAC,IAAI,EAAE,IAAI,QAAQ;CACtD,CAAC;AACH;AAEA,mBAAmB,cAAc;;;;;;;AAkBjC,SAAgB,cAAc,EAAE,WAAW,OAAO,UAAU,QAAQ,GAAG,QAA4B;CACjG,MAAM,MAAM,mBAAmB,gBAAgB;CAC/C,MAAM,QAA4B;EAAE,OAAO,IAAI;EAAO,OAAO,IAAI;EAAO,OAAO,IAAI;CAAM;CACzF,MAAM,UAAU,UAA4D;EAC1E,gBAAgB;EAChB;EACA;EACA;EACA;EACA;EACA,OAAO;CACT,CAAC;CACD,OAAO,IAAI,QAAQ,UAAU;AAC/B;AAEA,cAAc,cAAc;;;;;;;;;;AAqB5B,SAAgB,eAAe,EAC7B,WACA,OACA,UACA,QACA,GAAG,QACmB;CACtB,MAAM,EAAE,mBAAmB,mBAAmB,iBAAiB;CAC/D,MAAM,SAAS,IAAI,mBAAmB;CACtC,sBAAuB,SAAS,eAAe,IAAI,KAAA,GAAY,CAAC,QAAQ,cAAc,CAAC;CACvF,OAAO,UAA6D;EAClE,gBAAgB;EAChB;EACA,OAAO;EACP;EACA;EACA;EACA,OAAO;EACP,cAAc,EAAE,MAAM,SAAS,SAAS,SAAS;CACnD,CAAC;AACH;AAEA,eAAe,cAAc"}
@@ -0,0 +1,42 @@
1
+ import { RenderProp, StateValue } from "../internal/types.mjs";
2
+ import { CSSProperties, ComponentPropsWithRef } from "react";
3
+ //#region src/combobox/combobox-input.d.ts
4
+ export interface ComboboxInputState {
5
+ /** Optimistic selection. */
6
+ readonly value: string | null;
7
+ /** The urgent text. */
8
+ readonly inputValue: string;
9
+ /** Optimistic open state. */
10
+ readonly open: boolean;
11
+ /** The Root is pending (a selection or an open in flight). */
12
+ readonly pending: boolean;
13
+ /** The results shown are for an older text. */
14
+ readonly stale: boolean;
15
+ readonly disabled: boolean;
16
+ }
17
+ export interface ComboboxInputProps extends Omit<ComponentPropsWithRef<"input">, "value" | "defaultValue" | "onChange" | "onInput" | "onKeyDown" | "onBlur" | "onClick" | "role" | "aria-autocomplete" | "aria-expanded" | "aria-controls" | "aria-activedescendant" | "autoComplete" | "disabled" | "className" | "style" | "children"> {
18
+ /** Native `required`: empty text is `valueMissing`. */
19
+ required?: boolean;
20
+ className?: StateValue<string | undefined, ComboboxInputState>;
21
+ style?: StateValue<CSSProperties | undefined, ComboboxInputState>;
22
+ /** Must render an `<input>` of type `text` or `search`. */
23
+ render?: RenderProp<ComponentPropsWithRef<"input">, ComboboxInputState>;
24
+ }
25
+ /**
26
+ * The text field: `<input role="combobox" aria-autocomplete="list" aria-expanded aria-controls
27
+ * aria-activedescendant autocomplete="off">`. DOM focus stays here while the active option moves
28
+ * virtually (design 7.1). Its text is the Root's urgent `inputValue`; typing opens the listbox.
29
+ *
30
+ * Owned keys (conventions 1.2): `id`, `type`, `role`, `value`, `autoComplete`,
31
+ * `aria-autocomplete`, `aria-expanded`, `aria-controls` (token-merged), `aria-activedescendant`,
32
+ * `disabled`, `onChange`, `onKeyDown`, `onBlur`, `onClick`, `data-open`, `data-pending`,
33
+ * `data-selected`, `data-stale`, `data-disabled`; inside a `Field.Root` (whose control id it
34
+ * takes) also `required` (own or the Field's), `aria-describedby` (token-merged), `aria-invalid`
35
+ * and `data-invalid`.
36
+ */
37
+ export declare function ComboboxInput({ className, style, render, ...rest }: ComboboxInputProps): import("react").ReactNode;
38
+ export declare namespace ComboboxInput {
39
+ var displayName: string;
40
+ }
41
+ //#endregion
42
+ //# sourceMappingURL=combobox-input.d.mts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"combobox-input.d.mts","names":[],"sources":["../../src/combobox/combobox-input.tsx"],"mappings":";;;iBAiBiB;;WAEN;;WAEA;;WAEA;;WAEA;;WAEA;WACA;;iBAGM,2BAA2B,KAC1C;;EAoBA;EACA,YAAY,+BAA+B;EAC3C,QAAQ,WAAW,2BAA2B;;EAE9C,SAAS,WAAW,gCAAgC;;;;;;;;;;;;;;wBAiBtC,gBAAgB,WAAW,OAAO,WAAW,QAAQ,qCAAkB;yBAAvE"}
@@ -0,0 +1,102 @@
1
+ "use client";
2
+ import { dataAttr } from "../internal/data-attr.mjs";
3
+ import { useRender } from "../internal/use-render.mjs";
4
+ import { useFieldControl, useMergedRef } from "../internal/use-field-control.mjs";
5
+ import { optionId, useComboboxContext } from "./combobox-context.mjs";
6
+ import { isValidElement, useCallback } from "react";
7
+ //#region src/combobox/combobox-input.tsx
8
+ const isDev = process.env.NODE_ENV !== "production";
9
+ /**
10
+ * The text field: `<input role="combobox" aria-autocomplete="list" aria-expanded aria-controls
11
+ * aria-activedescendant autocomplete="off">`. DOM focus stays here while the active option moves
12
+ * virtually (design 7.1). Its text is the Root's urgent `inputValue`; typing opens the listbox.
13
+ *
14
+ * Owned keys (conventions 1.2): `id`, `type`, `role`, `value`, `autoComplete`,
15
+ * `aria-autocomplete`, `aria-expanded`, `aria-controls` (token-merged), `aria-activedescendant`,
16
+ * `disabled`, `onChange`, `onKeyDown`, `onBlur`, `onClick`, `data-open`, `data-pending`,
17
+ * `data-selected`, `data-stale`, `data-disabled`; inside a `Field.Root` (whose control id it
18
+ * takes) also `required` (own or the Field's), `aria-describedby` (token-merged), `aria-invalid`
19
+ * and `data-invalid`.
20
+ */
21
+ function ComboboxInput({ className, style, render, ...rest }) {
22
+ const ctx = useComboboxContext("Combobox.Input");
23
+ const { popover, ids, inputRef } = ctx;
24
+ const { triggerRef } = popover;
25
+ const state = {
26
+ value: ctx.value,
27
+ inputValue: ctx.inputValue,
28
+ open: popover.optimistic,
29
+ pending: ctx.pending,
30
+ stale: ctx.stale,
31
+ disabled: ctx.disabled
32
+ };
33
+ const ref = useCallback((element) => {
34
+ if (isDev) {
35
+ const type = element.tagName === "INPUT" ? element.type : "";
36
+ if (type !== "text" && type !== "search") console.error(`Combobox.Input: rendered <${element.tagName.toLowerCase()}${type === "" ? "" : ` type="${type}"`}>. Render an <input> of type "text" or "search": \`role="combobox"\`, \`aria-activedescendant\` and the caret keys depend on it.`);
37
+ }
38
+ inputRef.current = element;
39
+ triggerRef.current = element;
40
+ return () => {
41
+ if (inputRef.current === element) inputRef.current = null;
42
+ if (triggerRef.current === element) triggerRef.current = null;
43
+ };
44
+ }, [inputRef, triggerRef]);
45
+ const field = useFieldControl({
46
+ labelable: true,
47
+ required: rest.required,
48
+ disabled: ctx.disabled,
49
+ id: rest.id,
50
+ component: "Combobox.Input"
51
+ });
52
+ const fieldRef = field.ref;
53
+ const mergedRef = useMergedRef(ref, fieldRef);
54
+ const searchType = isValidElement(render) && render.props.type === "search";
55
+ const libraryProps = {
56
+ ...field.attributes,
57
+ ...field.inField && { required: field.required },
58
+ id: ids.input,
59
+ type: searchType ? "search" : "text",
60
+ role: "combobox",
61
+ value: ctx.inputValue,
62
+ autoComplete: "off",
63
+ "aria-autocomplete": "list",
64
+ "aria-expanded": popover.optimistic,
65
+ "aria-controls": ids.list,
66
+ "aria-activedescendant": ctx.activeValue === null ? void 0 : optionId(ids.optionPrefix, ctx.activeValue),
67
+ disabled: ctx.disabled,
68
+ onChange: (event) => {
69
+ ctx.typed(event.currentTarget.value, event.nativeEvent);
70
+ },
71
+ onKeyDown: (event) => {
72
+ ctx.keyDown(event.nativeEvent);
73
+ },
74
+ onBlur: (event) => {
75
+ ctx.focusOut(event.nativeEvent);
76
+ },
77
+ onClick: (event) => {
78
+ ctx.inputClick(event.nativeEvent);
79
+ },
80
+ "data-open": dataAttr(popover.optimistic),
81
+ "data-pending": dataAttr(state.pending),
82
+ "data-selected": dataAttr(ctx.value !== null),
83
+ "data-stale": dataAttr(ctx.stale),
84
+ "data-disabled": dataAttr(ctx.disabled)
85
+ };
86
+ return useRender({
87
+ defaultElement: "input",
88
+ render,
89
+ state,
90
+ className,
91
+ style,
92
+ children: void 0,
93
+ props: rest,
94
+ libraryProps,
95
+ ref: mergedRef
96
+ });
97
+ }
98
+ ComboboxInput.displayName = "ComboboxInput";
99
+ //#endregion
100
+ export { ComboboxInput };
101
+
102
+ //# sourceMappingURL=combobox-input.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"combobox-input.mjs","names":[],"sources":["../../src/combobox/combobox-input.tsx"],"sourcesContent":["\"use client\";\nimport { isValidElement, useCallback } from \"react\";\nimport type {\n ChangeEvent,\n ComponentPropsWithRef,\n CSSProperties,\n FocusEvent,\n KeyboardEvent,\n MouseEvent,\n} from \"react\";\nimport { dataAttr } from \"../internal/data-attr.js\";\nimport type { RenderProp, StateValue } from \"../internal/types.js\";\nimport { useFieldControl, useMergedRef } from \"../internal/use-field-control.js\";\nimport { useRender } from \"../internal/use-render.js\";\nimport type { DataAttributes } from \"../internal/use-render.js\";\nimport { optionId, useComboboxContext } from \"./combobox-context.js\";\n\nexport interface ComboboxInputState {\n /** Optimistic selection. */\n readonly value: string | null;\n /** The urgent text. */\n readonly inputValue: string;\n /** Optimistic open state. */\n readonly open: boolean;\n /** The Root is pending (a selection or an open in flight). */\n readonly pending: boolean;\n /** The results shown are for an older text. */\n readonly stale: boolean;\n readonly disabled: boolean;\n}\n\nexport interface ComboboxInputProps extends Omit<\n ComponentPropsWithRef<\"input\">,\n | \"value\"\n | \"defaultValue\"\n | \"onChange\"\n | \"onInput\"\n | \"onKeyDown\"\n | \"onBlur\"\n | \"onClick\"\n | \"role\"\n | \"aria-autocomplete\"\n | \"aria-expanded\"\n | \"aria-controls\"\n | \"aria-activedescendant\"\n | \"autoComplete\"\n | \"disabled\"\n | \"className\"\n | \"style\"\n | \"children\"\n> {\n /** Native `required`: empty text is `valueMissing`. */\n required?: boolean;\n className?: StateValue<string | undefined, ComboboxInputState>;\n style?: StateValue<CSSProperties | undefined, ComboboxInputState>;\n /** Must render an `<input>` of type `text` or `search`. */\n render?: RenderProp<ComponentPropsWithRef<\"input\">, ComboboxInputState>;\n}\n\nconst isDev = process.env.NODE_ENV !== \"production\";\n\n/**\n * The text field: `<input role=\"combobox\" aria-autocomplete=\"list\" aria-expanded aria-controls\n * aria-activedescendant autocomplete=\"off\">`. DOM focus stays here while the active option moves\n * virtually (design 7.1). Its text is the Root's urgent `inputValue`; typing opens the listbox.\n *\n * Owned keys (conventions 1.2): `id`, `type`, `role`, `value`, `autoComplete`,\n * `aria-autocomplete`, `aria-expanded`, `aria-controls` (token-merged), `aria-activedescendant`,\n * `disabled`, `onChange`, `onKeyDown`, `onBlur`, `onClick`, `data-open`, `data-pending`,\n * `data-selected`, `data-stale`, `data-disabled`; inside a `Field.Root` (whose control id it\n * takes) also `required` (own or the Field's), `aria-describedby` (token-merged), `aria-invalid`\n * and `data-invalid`.\n */\nexport function ComboboxInput({ className, style, render, ...rest }: ComboboxInputProps) {\n const ctx = useComboboxContext(\"Combobox.Input\");\n const { popover, ids, inputRef } = ctx;\n const { triggerRef } = popover;\n\n const state: ComboboxInputState = {\n value: ctx.value,\n inputValue: ctx.inputValue,\n open: popover.optimistic,\n pending: ctx.pending,\n stale: ctx.stale,\n disabled: ctx.disabled,\n };\n\n const ref = useCallback(\n (element: HTMLElement) => {\n if (isDev) {\n const type = element.tagName === \"INPUT\" ? (element as HTMLInputElement).type : \"\";\n if (type !== \"text\" && type !== \"search\") {\n console.error(\n `Combobox.Input: rendered <${element.tagName.toLowerCase()}` +\n `${type === \"\" ? \"\" : ` type=\"${type}\"`}>. Render an <input> of type \"text\" or ` +\n '\"search\": `role=\"combobox\"`, `aria-activedescendant` and the caret keys depend on it.',\n );\n }\n }\n inputRef.current = element as HTMLInputElement;\n // The popover's \"trigger\" is the input: `showPopover({ source: input })` anchors the\n // listbox to it and makes clicks on it not light-dismiss (design 5.1).\n triggerRef.current = element;\n return () => {\n if (inputRef.current === element) inputRef.current = null;\n if (triggerRef.current === element) triggerRef.current = null;\n };\n },\n [inputRef, triggerRef],\n );\n\n // Inside a Field.Root (design/components/field-form.md 7.2) the Input is the Field's control:\n // the Root already took the Field's id for it; `required` and the ARIA wiring land here.\n const field = useFieldControl({\n labelable: true,\n required: rest.required,\n disabled: ctx.disabled,\n id: rest.id,\n component: \"Combobox.Input\",\n });\n const fieldRef = field.ref;\n const mergedRef = useMergedRef<HTMLElement>(ref, fieldRef);\n\n const searchType = isValidElement<{ type?: unknown }>(render) && render.props.type === \"search\";\n\n const libraryProps: ComponentPropsWithRef<\"input\"> & DataAttributes = {\n ...field.attributes,\n ...(field.inField && { required: field.required }),\n id: ids.input,\n type: searchType ? \"search\" : \"text\",\n role: \"combobox\",\n value: ctx.inputValue,\n autoComplete: \"off\",\n \"aria-autocomplete\": \"list\",\n \"aria-expanded\": popover.optimistic,\n \"aria-controls\": ids.list,\n \"aria-activedescendant\":\n ctx.activeValue === null ? undefined : optionId(ids.optionPrefix, ctx.activeValue),\n disabled: ctx.disabled,\n onChange: (event: ChangeEvent<HTMLInputElement>) => {\n ctx.typed(event.currentTarget.value, event.nativeEvent);\n },\n onKeyDown: (event: KeyboardEvent<HTMLInputElement>) => {\n ctx.keyDown(event.nativeEvent);\n },\n onBlur: (event: FocusEvent<HTMLInputElement>) => {\n ctx.focusOut(event.nativeEvent);\n },\n onClick: (event: MouseEvent<HTMLInputElement>) => {\n ctx.inputClick(event.nativeEvent);\n },\n \"data-open\": dataAttr(popover.optimistic),\n \"data-pending\": dataAttr(state.pending),\n \"data-selected\": dataAttr(ctx.value !== null),\n \"data-stale\": dataAttr(ctx.stale),\n \"data-disabled\": dataAttr(ctx.disabled),\n };\n\n return useRender<ComponentPropsWithRef<\"input\">, ComboboxInputState>({\n defaultElement: \"input\",\n render,\n state,\n className,\n style,\n children: undefined,\n props: rest,\n libraryProps,\n ref: mergedRef,\n });\n}\n\nComboboxInput.displayName = \"ComboboxInput\";\n"],"mappings":";;;;;;;AA2DA,MAAM,QAAQ,QAAQ,IAAI,aAAa;;;;;;;;;;;;;AAcvC,SAAgB,cAAc,EAAE,WAAW,OAAO,QAAQ,GAAG,QAA4B;CACvF,MAAM,MAAM,mBAAmB,gBAAgB;CAC/C,MAAM,EAAE,SAAS,KAAK,aAAa;CACnC,MAAM,EAAE,eAAe;CAEvB,MAAM,QAA4B;EAChC,OAAO,IAAI;EACX,YAAY,IAAI;EAChB,MAAM,QAAQ;EACd,SAAS,IAAI;EACb,OAAO,IAAI;EACX,UAAU,IAAI;CAChB;CAEA,MAAM,MAAM,aACT,YAAyB;EACxB,IAAI,OAAO;GACT,MAAM,OAAO,QAAQ,YAAY,UAAW,QAA6B,OAAO;GAChF,IAAI,SAAS,UAAU,SAAS,UAC9B,QAAQ,MACN,6BAA6B,QAAQ,QAAQ,YAAY,IACpD,SAAS,KAAK,KAAK,UAAU,KAAK,GAAG,iIAE5C;EAEJ;EACA,SAAS,UAAU;EAGnB,WAAW,UAAU;EACrB,aAAa;GACX,IAAI,SAAS,YAAY,SAAS,SAAS,UAAU;GACrD,IAAI,WAAW,YAAY,SAAS,WAAW,UAAU;EAC3D;CACF,GACA,CAAC,UAAU,UAAU,CACvB;CAIA,MAAM,QAAQ,gBAAgB;EAC5B,WAAW;EACX,UAAU,KAAK;EACf,UAAU,IAAI;EACd,IAAI,KAAK;EACT,WAAW;CACb,CAAC;CACD,MAAM,WAAW,MAAM;CACvB,MAAM,YAAY,aAA0B,KAAK,QAAQ;CAEzD,MAAM,aAAa,eAAmC,MAAM,KAAK,OAAO,MAAM,SAAS;CAEvF,MAAM,eAAgE;EACpE,GAAG,MAAM;EACT,GAAI,MAAM,WAAW,EAAE,UAAU,MAAM,SAAS;EAChD,IAAI,IAAI;EACR,MAAM,aAAa,WAAW;EAC9B,MAAM;EACN,OAAO,IAAI;EACX,cAAc;EACd,qBAAqB;EACrB,iBAAiB,QAAQ;EACzB,iBAAiB,IAAI;EACrB,yBACE,IAAI,gBAAgB,OAAO,KAAA,IAAY,SAAS,IAAI,cAAc,IAAI,WAAW;EACnF,UAAU,IAAI;EACd,WAAW,UAAyC;GAClD,IAAI,MAAM,MAAM,cAAc,OAAO,MAAM,WAAW;EACxD;EACA,YAAY,UAA2C;GACrD,IAAI,QAAQ,MAAM,WAAW;EAC/B;EACA,SAAS,UAAwC;GAC/C,IAAI,SAAS,MAAM,WAAW;EAChC;EACA,UAAU,UAAwC;GAChD,IAAI,WAAW,MAAM,WAAW;EAClC;EACA,aAAa,SAAS,QAAQ,UAAU;EACxC,gBAAgB,SAAS,MAAM,OAAO;EACtC,iBAAiB,SAAS,IAAI,UAAU,IAAI;EAC5C,cAAc,SAAS,IAAI,KAAK;EAChC,iBAAiB,SAAS,IAAI,QAAQ;CACxC;CAEA,OAAO,UAA8D;EACnE,gBAAgB;EAChB;EACA;EACA;EACA;EACA,UAAU,KAAA;EACV,OAAO;EACP;EACA,KAAK;CACP,CAAC;AACH;AAEA,cAAc,cAAc"}
@@ -0,0 +1,22 @@
1
+ import { RenderProp, StateValue } from "../internal/types.mjs";
2
+ import { CSSProperties, ComponentPropsWithRef, ReactNode } from "react";
3
+ //#region src/combobox/combobox-label.d.ts
4
+ export interface ComboboxLabelState {
5
+ readonly disabled: boolean;
6
+ }
7
+ export interface ComboboxLabelProps extends Omit<ComponentPropsWithRef<"label">, "id" | "htmlFor" | "className" | "style" | "children"> {
8
+ className?: StateValue<string | undefined, ComboboxLabelState>;
9
+ style?: StateValue<CSSProperties | undefined, ComboboxLabelState>;
10
+ children?: StateValue<ReactNode, ComboboxLabelState>;
11
+ render?: RenderProp<ComponentPropsWithRef<"label">, ComboboxLabelState>;
12
+ }
13
+ /**
14
+ * The visible name of the combobox: `<label for={input}>`, whose id also names the listbox
15
+ * (`aria-labelledby` on `Combobox.List`, emitted only while a Label is mounted). Owned keys: `id`, `htmlFor`.
16
+ */
17
+ export declare function ComboboxLabel({ className, style, children, render, ...rest }: ComboboxLabelProps): ReactNode;
18
+ export declare namespace ComboboxLabel {
19
+ var displayName: string;
20
+ }
21
+ //#endregion
22
+ //# sourceMappingURL=combobox-label.d.mts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"combobox-label.d.mts","names":[],"sources":["../../src/combobox/combobox-label.tsx"],"mappings":";;;iBAOiB;WACN;;iBAGM,2BAA2B,KAC1C;EAGA,YAAY,+BAA+B;EAC3C,QAAQ,WAAW,2BAA2B;EAC9C,WAAW,WAAW,WAAW;EACjC,SAAS,WAAW,gCAAgC;;;;;;wBAOtC,gBAAgB,WAAW,OAAO,UAAU,WAAW,QAAQ,qBAAkB;yBAAjF"}
@@ -0,0 +1,32 @@
1
+ "use client";
2
+ import { useRender } from "../internal/use-render.mjs";
3
+ import { useComboboxContext } from "./combobox-context.mjs";
4
+ import { useLayoutEffect } from "react";
5
+ //#region src/combobox/combobox-label.tsx
6
+ /**
7
+ * The visible name of the combobox: `<label for={input}>`, whose id also names the listbox
8
+ * (`aria-labelledby` on `Combobox.List`, emitted only while a Label is mounted). Owned keys: `id`, `htmlFor`.
9
+ */
10
+ function ComboboxLabel({ className, style, children, render, ...rest }) {
11
+ const ctx = useComboboxContext("Combobox.Label");
12
+ const { registerLabel } = ctx;
13
+ useLayoutEffect(registerLabel, [registerLabel]);
14
+ return useRender({
15
+ defaultElement: "label",
16
+ render,
17
+ state: { disabled: ctx.disabled },
18
+ className,
19
+ style,
20
+ children,
21
+ props: rest,
22
+ libraryProps: {
23
+ id: ctx.ids.label,
24
+ htmlFor: ctx.ids.input
25
+ }
26
+ });
27
+ }
28
+ ComboboxLabel.displayName = "ComboboxLabel";
29
+ //#endregion
30
+ export { ComboboxLabel };
31
+
32
+ //# sourceMappingURL=combobox-label.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"combobox-label.mjs","names":[],"sources":["../../src/combobox/combobox-label.tsx"],"sourcesContent":["\"use client\";\nimport { useLayoutEffect } from \"react\";\nimport type { ComponentPropsWithRef, CSSProperties, ReactNode } from \"react\";\nimport type { RenderProp, StateValue } from \"../internal/types.js\";\nimport { useRender } from \"../internal/use-render.js\";\nimport { useComboboxContext } from \"./combobox-context.js\";\n\nexport interface ComboboxLabelState {\n readonly disabled: boolean;\n}\n\nexport interface ComboboxLabelProps extends Omit<\n ComponentPropsWithRef<\"label\">,\n \"id\" | \"htmlFor\" | \"className\" | \"style\" | \"children\"\n> {\n className?: StateValue<string | undefined, ComboboxLabelState>;\n style?: StateValue<CSSProperties | undefined, ComboboxLabelState>;\n children?: StateValue<ReactNode, ComboboxLabelState>;\n render?: RenderProp<ComponentPropsWithRef<\"label\">, ComboboxLabelState>;\n}\n\n/**\n * The visible name of the combobox: `<label for={input}>`, whose id also names the listbox\n * (`aria-labelledby` on `Combobox.List`, emitted only while a Label is mounted). Owned keys: `id`, `htmlFor`.\n */\nexport function ComboboxLabel({ className, style, children, render, ...rest }: ComboboxLabelProps) {\n const ctx = useComboboxContext(\"Combobox.Label\");\n const { registerLabel } = ctx;\n useLayoutEffect(registerLabel, [registerLabel]);\n return useRender<ComponentPropsWithRef<\"label\">, ComboboxLabelState>({\n defaultElement: \"label\",\n render,\n state: { disabled: ctx.disabled },\n className,\n style,\n children,\n props: rest,\n libraryProps: { id: ctx.ids.label, htmlFor: ctx.ids.input },\n });\n}\n\nComboboxLabel.displayName = \"ComboboxLabel\";\n"],"mappings":";;;;;;;;;AAyBA,SAAgB,cAAc,EAAE,WAAW,OAAO,UAAU,QAAQ,GAAG,QAA4B;CACjG,MAAM,MAAM,mBAAmB,gBAAgB;CAC/C,MAAM,EAAE,kBAAkB;CAC1B,gBAAgB,eAAe,CAAC,aAAa,CAAC;CAC9C,OAAO,UAA8D;EACnE,gBAAgB;EAChB;EACA,OAAO,EAAE,UAAU,IAAI,SAAS;EAChC;EACA;EACA;EACA,OAAO;EACP,cAAc;GAAE,IAAI,IAAI,IAAI;GAAO,SAAS,IAAI,IAAI;EAAM;CAC5D,CAAC;AACH;AAEA,cAAc,cAAc"}
@@ -0,0 +1,37 @@
1
+ import { RenderProp, StateValue } from "../internal/types.mjs";
2
+ import { CSSProperties, ComponentPropsWithRef, ReactNode } from "react";
3
+ //#region src/combobox/combobox-list.d.ts
4
+ export interface ComboboxListState {
5
+ /** The deferred text the results are for: render the Options for it. */
6
+ readonly query: string;
7
+ /** The results shown are for an older text (`aria-busy`). */
8
+ readonly stale: boolean;
9
+ /** No option is mounted and the results are settled. */
10
+ readonly empty: boolean;
11
+ }
12
+ export interface ComboboxListProps extends Omit<ComponentPropsWithRef<"div">, "id" | "role" | "aria-busy" | "className" | "style" | "children"> {
13
+ className?: StateValue<string | undefined, ComboboxListState>;
14
+ style?: StateValue<CSSProperties | undefined, ComboboxListState>;
15
+ /**
16
+ * Usually a function of `{ query }` rendering the Options for it, under your own `<Suspense>`
17
+ * for an async search. The previous results stay visible while the next ones load.
18
+ */
19
+ children?: StateValue<ReactNode, ComboboxListState>;
20
+ render?: RenderProp<ComponentPropsWithRef<"div">, ComboboxListState>;
21
+ }
22
+ /**
23
+ * The listbox: `<div role="listbox" aria-labelledby={label}>` (only while a `Combobox.Label` is
24
+ * mounted), the element the input's `aria-controls` names. Its children are the results for the
25
+ * deferred `query` (the library filters nothing, design 6.1); while they are for an older text it
26
+ * carries `aria-busy="true"` and `data-stale`. Contains only Options and Groups; a `Status`
27
+ * rendered as a Suspense fallback inside it marks the results as loading (`aria-busy="true"`).
28
+ *
29
+ * Owned keys (conventions 1.2): `id`, `role`, `aria-labelledby` (token-merged), `aria-busy`,
30
+ * `data-stale`, `data-empty`.
31
+ */
32
+ export declare function ComboboxList({ className, style, children, render, ...rest }: ComboboxListProps): ReactNode;
33
+ export declare namespace ComboboxList {
34
+ var displayName: string;
35
+ }
36
+ //#endregion
37
+ //# sourceMappingURL=combobox-list.d.mts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"combobox-list.d.mts","names":[],"sources":["../../src/combobox/combobox-list.tsx"],"mappings":";;;iBAgBiB;;WAEN;;WAEA;;WAEA;;iBAGM,0BAA0B,KACzC;EAGA,YAAY,+BAA+B;EAC3C,QAAQ,WAAW,2BAA2B;;;;;EAK9C,WAAW,WAAW,WAAW;EACjC,SAAS,WAAW,8BAA8B;;;;;;;;;;;;wBAapC,eAAe,WAAW,OAAO,UAAU,WAAW,QAAQ,oBAAiB;yBAA/E"}
@@ -0,0 +1,70 @@
1
+ "use client";
2
+ import { dataAttr } from "../internal/data-attr.mjs";
3
+ import { resolveStateValue, useRender } from "../internal/use-render.mjs";
4
+ import { useDevWarning } from "../internal/use-dev-warning.mjs";
5
+ import { ComboboxListContext, ComboboxPopupContext, useComboboxContext } from "./combobox-context.mjs";
6
+ import { use, useCallback, useRef } from "react";
7
+ import { jsx } from "react/jsx-runtime";
8
+ //#region src/combobox/combobox-list.tsx
9
+ const isDev = process.env.NODE_ENV !== "production";
10
+ /**
11
+ * The listbox: `<div role="listbox" aria-labelledby={label}>` (only while a `Combobox.Label` is
12
+ * mounted), the element the input's `aria-controls` names. Its children are the results for the
13
+ * deferred `query` (the library filters nothing, design 6.1); while they are for an older text it
14
+ * carries `aria-busy="true"` and `data-stale`. Contains only Options and Groups; a `Status`
15
+ * rendered as a Suspense fallback inside it marks the results as loading (`aria-busy="true"`).
16
+ *
17
+ * Owned keys (conventions 1.2): `id`, `role`, `aria-labelledby` (token-merged), `aria-busy`,
18
+ * `data-stale`, `data-empty`.
19
+ */
20
+ function ComboboxList({ className, style, children, render, ...rest }) {
21
+ const ctx = useComboboxContext("Combobox.List");
22
+ const inPopup = use(ComboboxPopupContext);
23
+ useDevWarning(!inPopup, "Combobox.List: rendered outside a Combobox.Popup. The listbox must be inside the Popup, which is the element the library shows and hides.");
24
+ const { listRef } = ctx;
25
+ const warnedName = useRef(false);
26
+ const ref = useCallback((element) => {
27
+ listRef.current = element;
28
+ if (isDev && !warnedName.current) queueMicrotask(() => {
29
+ if (warnedName.current || !element.isConnected) return;
30
+ if (element.hasAttribute("aria-labelledby") || element.hasAttribute("aria-label")) return;
31
+ warnedName.current = true;
32
+ console.error("Combobox.List: the listbox has no accessible name. Render a Combobox.Label, or give the List an `aria-label` / `aria-labelledby`.");
33
+ });
34
+ return () => {
35
+ if (listRef.current === element) listRef.current = null;
36
+ };
37
+ }, [listRef]);
38
+ const state = {
39
+ query: ctx.query,
40
+ stale: ctx.stale,
41
+ empty: ctx.empty
42
+ };
43
+ const libraryProps = {
44
+ id: ctx.ids.list,
45
+ role: "listbox",
46
+ ...ctx.hasLabel ? { "aria-labelledby": ctx.ids.label } : ctx.fieldLabelId !== void 0 && { "aria-labelledby": ctx.fieldLabelId },
47
+ "aria-busy": ctx.stale || ctx.loading ? true : void 0,
48
+ "data-stale": dataAttr(ctx.stale),
49
+ "data-empty": dataAttr(ctx.empty)
50
+ };
51
+ return useRender({
52
+ defaultElement: "div",
53
+ render,
54
+ state,
55
+ className,
56
+ style,
57
+ children: (s) => /* @__PURE__ */ jsx(ComboboxListContext, {
58
+ value: true,
59
+ children: resolveStateValue(children, s)
60
+ }),
61
+ props: rest,
62
+ libraryProps,
63
+ ref
64
+ });
65
+ }
66
+ ComboboxList.displayName = "ComboboxList";
67
+ //#endregion
68
+ export { ComboboxList };
69
+
70
+ //# sourceMappingURL=combobox-list.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"combobox-list.mjs","names":[],"sources":["../../src/combobox/combobox-list.tsx"],"sourcesContent":["\"use client\";\nimport { use, useCallback, useRef } from \"react\";\nimport type { ComponentPropsWithRef, CSSProperties, ReactNode } from \"react\";\nimport { dataAttr } from \"../internal/data-attr.js\";\nimport type { RenderProp, StateValue } from \"../internal/types.js\";\nimport { useDevWarning } from \"../internal/use-dev-warning.js\";\nimport { resolveStateValue, useRender } from \"../internal/use-render.js\";\nimport type { DataAttributes } from \"../internal/use-render.js\";\nimport {\n ComboboxListContext,\n ComboboxPopupContext,\n useComboboxContext,\n} from \"./combobox-context.js\";\n\nconst isDev = process.env.NODE_ENV !== \"production\";\n\nexport interface ComboboxListState {\n /** The deferred text the results are for: render the Options for it. */\n readonly query: string;\n /** The results shown are for an older text (`aria-busy`). */\n readonly stale: boolean;\n /** No option is mounted and the results are settled. */\n readonly empty: boolean;\n}\n\nexport interface ComboboxListProps extends Omit<\n ComponentPropsWithRef<\"div\">,\n \"id\" | \"role\" | \"aria-busy\" | \"className\" | \"style\" | \"children\"\n> {\n className?: StateValue<string | undefined, ComboboxListState>;\n style?: StateValue<CSSProperties | undefined, ComboboxListState>;\n /**\n * Usually a function of `{ query }` rendering the Options for it, under your own `<Suspense>`\n * for an async search. The previous results stay visible while the next ones load.\n */\n children?: StateValue<ReactNode, ComboboxListState>;\n render?: RenderProp<ComponentPropsWithRef<\"div\">, ComboboxListState>;\n}\n\n/**\n * The listbox: `<div role=\"listbox\" aria-labelledby={label}>` (only while a `Combobox.Label` is\n * mounted), the element the input's `aria-controls` names. Its children are the results for the\n * deferred `query` (the library filters nothing, design 6.1); while they are for an older text it\n * carries `aria-busy=\"true\"` and `data-stale`. Contains only Options and Groups; a `Status`\n * rendered as a Suspense fallback inside it marks the results as loading (`aria-busy=\"true\"`).\n *\n * Owned keys (conventions 1.2): `id`, `role`, `aria-labelledby` (token-merged), `aria-busy`,\n * `data-stale`, `data-empty`.\n */\nexport function ComboboxList({ className, style, children, render, ...rest }: ComboboxListProps) {\n const ctx = useComboboxContext(\"Combobox.List\");\n const inPopup = use(ComboboxPopupContext);\n useDevWarning(\n !inPopup,\n \"Combobox.List: rendered outside a Combobox.Popup. The listbox must be inside the Popup, \" +\n \"which is the element the library shows and hides.\",\n );\n const { listRef } = ctx;\n const warnedName = useRef(false);\n\n const ref = useCallback(\n (element: HTMLElement) => {\n listRef.current = element;\n if (isDev && !warnedName.current) {\n // A mounted Label registers in a layout effect whose re-render is flushed before this\n // microtask, so `aria-labelledby` is in the DOM by then if it ever will be.\n queueMicrotask(() => {\n if (warnedName.current || !element.isConnected) return;\n if (element.hasAttribute(\"aria-labelledby\") || element.hasAttribute(\"aria-label\")) return;\n warnedName.current = true;\n console.error(\n \"Combobox.List: the listbox has no accessible name. Render a Combobox.Label, or give \" +\n \"the List an `aria-label` / `aria-labelledby`.\",\n );\n });\n }\n return () => {\n if (listRef.current === element) listRef.current = null;\n };\n },\n [listRef],\n );\n\n const state: ComboboxListState = { query: ctx.query, stale: ctx.stale, empty: ctx.empty };\n\n const libraryProps: ComponentPropsWithRef<\"div\"> & DataAttributes = {\n id: ctx.ids.list,\n role: \"listbox\",\n ...(ctx.hasLabel\n ? { \"aria-labelledby\": ctx.ids.label }\n : ctx.fieldLabelId !== undefined && { \"aria-labelledby\": ctx.fieldLabelId }),\n \"aria-busy\": ctx.stale || ctx.loading ? true : undefined,\n \"data-stale\": dataAttr(ctx.stale),\n \"data-empty\": dataAttr(ctx.empty),\n };\n\n return useRender<ComponentPropsWithRef<\"div\">, ComboboxListState>({\n defaultElement: \"div\",\n render,\n state,\n className,\n style,\n children: (s) => (\n <ComboboxListContext value={true}>{resolveStateValue(children, s)}</ComboboxListContext>\n ),\n props: rest,\n libraryProps,\n ref,\n });\n}\n\nComboboxList.displayName = \"ComboboxList\";\n"],"mappings":";;;;;;;;AAcA,MAAM,QAAQ,QAAQ,IAAI,aAAa;;;;;;;;;;;AAmCvC,SAAgB,aAAa,EAAE,WAAW,OAAO,UAAU,QAAQ,GAAG,QAA2B;CAC/F,MAAM,MAAM,mBAAmB,eAAe;CAC9C,MAAM,UAAU,IAAI,oBAAoB;CACxC,cACE,CAAC,SACD,2IAEF;CACA,MAAM,EAAE,YAAY;CACpB,MAAM,aAAa,OAAO,KAAK;CAE/B,MAAM,MAAM,aACT,YAAyB;EACxB,QAAQ,UAAU;EAClB,IAAI,SAAS,CAAC,WAAW,SAGvB,qBAAqB;GACnB,IAAI,WAAW,WAAW,CAAC,QAAQ,aAAa;GAChD,IAAI,QAAQ,aAAa,iBAAiB,KAAK,QAAQ,aAAa,YAAY,GAAG;GACnF,WAAW,UAAU;GACrB,QAAQ,MACN,mIAEF;EACF,CAAC;EAEH,aAAa;GACX,IAAI,QAAQ,YAAY,SAAS,QAAQ,UAAU;EACrD;CACF,GACA,CAAC,OAAO,CACV;CAEA,MAAM,QAA2B;EAAE,OAAO,IAAI;EAAO,OAAO,IAAI;EAAO,OAAO,IAAI;CAAM;CAExF,MAAM,eAA8D;EAClE,IAAI,IAAI,IAAI;EACZ,MAAM;EACN,GAAI,IAAI,WACJ,EAAE,mBAAmB,IAAI,IAAI,MAAM,IACnC,IAAI,iBAAiB,KAAA,KAAa,EAAE,mBAAmB,IAAI,aAAa;EAC5E,aAAa,IAAI,SAAS,IAAI,UAAU,OAAO,KAAA;EAC/C,cAAc,SAAS,IAAI,KAAK;EAChC,cAAc,SAAS,IAAI,KAAK;CAClC;CAEA,OAAO,UAA2D;EAChE,gBAAgB;EAChB;EACA;EACA;EACA;EACA,WAAW,MACT,oBAAC,qBAAD;GAAqB,OAAO;GAAO,UAAA,kBAAkB,UAAU,CAAC;EAAuB,CAAA;EAEzF,OAAO;EACP;EACA;CACF,CAAC;AACH;AAEA,aAAa,cAAc"}
@@ -0,0 +1,45 @@
1
+ import { RenderProp, StateValue } from "../internal/types.mjs";
2
+ import { CSSProperties, ComponentPropsWithRef, ReactNode } from "react";
3
+ //#region src/combobox/combobox-option.d.ts
4
+ export interface ComboboxOptionState {
5
+ readonly value: string;
6
+ /** Optimistic: this option's value is the (optimistic) selection. */
7
+ readonly selected: boolean;
8
+ /** `aria-activedescendant` points here (urgent). */
9
+ readonly active: boolean;
10
+ /** The selection is pending and this option is its optimistic target. */
11
+ readonly pending: boolean;
12
+ readonly disabled: boolean;
13
+ }
14
+ export interface ComboboxOptionProps extends Omit<ComponentPropsWithRef<"div">, "id" | "role" | "aria-selected" | "aria-disabled" | "onClick" | "onPointerDown" | "onMouseDown" | "onPointerMove" | "className" | "style" | "children"> {
15
+ /** The selection this option stands for. Unique within the Combobox; also its id fragment. */
16
+ value: string;
17
+ /**
18
+ * The text written into the input when this option is accepted. Default: the element's
19
+ * trimmed `textContent` at that moment. Not used for matching: the library filters nothing.
20
+ */
21
+ label?: string;
22
+ /** `aria-disabled`: shown, skipped by the arrow keys, never accepted. */
23
+ disabled?: boolean;
24
+ className?: StateValue<string | undefined, ComboboxOptionState>;
25
+ style?: StateValue<CSSProperties | undefined, ComboboxOptionState>;
26
+ /** Rendered from optimistic state; must not suspend. */
27
+ children?: StateValue<ReactNode, ComboboxOptionState>;
28
+ render?: RenderProp<ComponentPropsWithRef<"div">, ComboboxOptionState>;
29
+ }
30
+ /**
31
+ * One result: `<div role="option" id aria-selected>`, never focused (virtual focus through the
32
+ * input's `aria-activedescendant`). A click (or Enter while it is active) accepts it: the input
33
+ * text becomes its label at once, it shows `data-selected` and `data-pending`, and the listbox
34
+ * closes when `valueChangeAction` settles. Hovering makes it active.
35
+ *
36
+ * Owned keys (conventions 1.2): `id` (derived from `value`), `role`, `aria-selected`,
37
+ * `aria-disabled`, `onClick`, `onPointerDown`, `onMouseDown`, `onPointerMove`, `data-selected`,
38
+ * `data-active`, `data-pending`, `data-disabled`.
39
+ */
40
+ export declare function ComboboxOption({ value, label, disabled, className, style, children, render, ...rest }: ComboboxOptionProps): ReactNode;
41
+ export declare namespace ComboboxOption {
42
+ var displayName: string;
43
+ }
44
+ //#endregion
45
+ //# sourceMappingURL=combobox-option.d.mts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"combobox-option.d.mts","names":[],"sources":["../../src/combobox/combobox-option.tsx"],"mappings":";;;iBAgBiB;WACN;;WAEA;;WAEA;;WAEA;WACA;;iBAGM,4BAA4B,KAC3C;;EAcA;;;;;EAKA;;EAEA;EACA,YAAY,+BAA+B;EAC3C,QAAQ,WAAW,2BAA2B;;EAE9C,WAAW,WAAW,WAAW;EACjC,SAAS,WAAW,8BAA8B;;;;;;;;;;;;wBAepC,iBACd,OACA,OACA,UACA,WACA,OACA,UACA,WACG,QACF,sBAAmB;yBATN"}
@@ -0,0 +1,88 @@
1
+ "use client";
2
+ import { dataAttr } from "../internal/data-attr.mjs";
3
+ import { useRender } from "../internal/use-render.mjs";
4
+ import { useDevWarning } from "../internal/use-dev-warning.mjs";
5
+ import { ComboboxListContext, optionId, useComboboxContext } from "./combobox-context.mjs";
6
+ import { use, useCallback, useLayoutEffect, useRef } from "react";
7
+ //#region src/combobox/combobox-option.tsx
8
+ const preventDefault = (event) => event.preventDefault();
9
+ /**
10
+ * One result: `<div role="option" id aria-selected>`, never focused (virtual focus through the
11
+ * input's `aria-activedescendant`). A click (or Enter while it is active) accepts it: the input
12
+ * text becomes its label at once, it shows `data-selected` and `data-pending`, and the listbox
13
+ * closes when `valueChangeAction` settles. Hovering makes it active.
14
+ *
15
+ * Owned keys (conventions 1.2): `id` (derived from `value`), `role`, `aria-selected`,
16
+ * `aria-disabled`, `onClick`, `onPointerDown`, `onMouseDown`, `onPointerMove`, `data-selected`,
17
+ * `data-active`, `data-pending`, `data-disabled`.
18
+ */
19
+ function ComboboxOption({ value, label, disabled = false, className, style, children, render, ...rest }) {
20
+ const ctx = useComboboxContext("Combobox.Option");
21
+ const inList = use(ComboboxListContext);
22
+ useDevWarning(!inList, "Combobox.Option: rendered outside a Combobox.List. Options belong in the listbox.");
23
+ const { registerOption } = ctx;
24
+ const elementRef = useRef(null);
25
+ const ref = useCallback((element) => {
26
+ elementRef.current = element;
27
+ return () => {
28
+ if (elementRef.current === element) elementRef.current = null;
29
+ };
30
+ }, []);
31
+ useLayoutEffect(() => {
32
+ const element = elementRef.current;
33
+ if (element === null) return;
34
+ return registerOption(value, {
35
+ element,
36
+ label,
37
+ disabled
38
+ });
39
+ }, [
40
+ registerOption,
41
+ value,
42
+ label,
43
+ disabled
44
+ ]);
45
+ const selected = ctx.value === value;
46
+ const state = {
47
+ value,
48
+ selected,
49
+ active: ctx.activeValue === value,
50
+ pending: ctx.valuePending && selected,
51
+ disabled
52
+ };
53
+ const libraryProps = {
54
+ id: optionId(ctx.ids.optionPrefix, value),
55
+ role: "option",
56
+ "aria-selected": selected,
57
+ "aria-disabled": disabled ? true : void 0,
58
+ onClick: (event) => {
59
+ if (disabled) return;
60
+ ctx.accept(value, event.nativeEvent);
61
+ },
62
+ onPointerDown: (event) => preventDefault(event),
63
+ onMouseDown: (event) => preventDefault(event),
64
+ onPointerMove: () => {
65
+ if (!disabled && ctx.activeValue !== value) ctx.setActive(value);
66
+ },
67
+ "data-selected": dataAttr(selected),
68
+ "data-active": dataAttr(state.active),
69
+ "data-pending": dataAttr(state.pending),
70
+ "data-disabled": dataAttr(disabled)
71
+ };
72
+ return useRender({
73
+ defaultElement: "div",
74
+ render,
75
+ state,
76
+ className,
77
+ style,
78
+ children,
79
+ props: rest,
80
+ libraryProps,
81
+ ref
82
+ });
83
+ }
84
+ ComboboxOption.displayName = "ComboboxOption";
85
+ //#endregion
86
+ export { ComboboxOption };
87
+
88
+ //# sourceMappingURL=combobox-option.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"combobox-option.mjs","names":[],"sources":["../../src/combobox/combobox-option.tsx"],"sourcesContent":["\"use client\";\nimport { use, useCallback, useLayoutEffect, useRef } from \"react\";\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 { useDevWarning } from \"../internal/use-dev-warning.js\";\nimport { useRender } from \"../internal/use-render.js\";\nimport type { DataAttributes } from \"../internal/use-render.js\";\nimport { ComboboxListContext, optionId, useComboboxContext } from \"./combobox-context.js\";\n\nexport interface ComboboxOptionState {\n readonly value: string;\n /** Optimistic: this option's value is the (optimistic) selection. */\n readonly selected: boolean;\n /** `aria-activedescendant` points here (urgent). */\n readonly active: boolean;\n /** The selection is pending and this option is its optimistic target. */\n readonly pending: boolean;\n readonly disabled: boolean;\n}\n\nexport interface ComboboxOptionProps extends Omit<\n ComponentPropsWithRef<\"div\">,\n | \"id\"\n | \"role\"\n | \"aria-selected\"\n | \"aria-disabled\"\n | \"onClick\"\n | \"onPointerDown\"\n | \"onMouseDown\"\n | \"onPointerMove\"\n | \"className\"\n | \"style\"\n | \"children\"\n> {\n /** The selection this option stands for. Unique within the Combobox; also its id fragment. */\n value: string;\n /**\n * The text written into the input when this option is accepted. Default: the element's\n * trimmed `textContent` at that moment. Not used for matching: the library filters nothing.\n */\n label?: string;\n /** `aria-disabled`: shown, skipped by the arrow keys, never accepted. */\n disabled?: boolean;\n className?: StateValue<string | undefined, ComboboxOptionState>;\n style?: StateValue<CSSProperties | undefined, ComboboxOptionState>;\n /** Rendered from optimistic state; must not suspend. */\n children?: StateValue<ReactNode, ComboboxOptionState>;\n render?: RenderProp<ComponentPropsWithRef<\"div\">, ComboboxOptionState>;\n}\n\nconst preventDefault = (event: { preventDefault(): void }) => event.preventDefault();\n\n/**\n * One result: `<div role=\"option\" id aria-selected>`, never focused (virtual focus through the\n * input's `aria-activedescendant`). A click (or Enter while it is active) accepts it: the input\n * text becomes its label at once, it shows `data-selected` and `data-pending`, and the listbox\n * closes when `valueChangeAction` settles. Hovering makes it active.\n *\n * Owned keys (conventions 1.2): `id` (derived from `value`), `role`, `aria-selected`,\n * `aria-disabled`, `onClick`, `onPointerDown`, `onMouseDown`, `onPointerMove`, `data-selected`,\n * `data-active`, `data-pending`, `data-disabled`.\n */\nexport function ComboboxOption({\n value,\n label,\n disabled = false,\n className,\n style,\n children,\n render,\n ...rest\n}: ComboboxOptionProps) {\n const ctx = useComboboxContext(\"Combobox.Option\");\n const inList = use(ComboboxListContext);\n useDevWarning(\n !inList,\n \"Combobox.Option: rendered outside a Combobox.List. Options belong in the listbox.\",\n );\n const { registerOption } = ctx;\n\n const elementRef = useRef<HTMLElement | null>(null);\n const ref = useCallback((element: HTMLElement) => {\n elementRef.current = element;\n return () => {\n if (elementRef.current === element) elementRef.current = null;\n };\n }, []);\n\n // Registration (design 14): the Root's registry drives the arrow keys, `Empty`, the active\n // option's revalidation, the announcements and the label cache.\n useLayoutEffect(() => {\n const element = elementRef.current;\n if (element === null) return;\n return registerOption(value, { element, label, disabled });\n }, [registerOption, value, label, disabled]);\n\n const selected = ctx.value === value;\n const state: ComboboxOptionState = {\n value,\n selected,\n active: ctx.activeValue === value,\n pending: ctx.valuePending && selected,\n disabled,\n };\n\n const libraryProps: ComponentPropsWithRef<\"div\"> & DataAttributes = {\n id: optionId(ctx.ids.optionPrefix, value),\n role: \"option\",\n \"aria-selected\": selected,\n \"aria-disabled\": disabled ? true : undefined,\n onClick: (event: MouseEvent<HTMLDivElement>) => {\n if (disabled) return;\n ctx.accept(value, event.nativeEvent);\n },\n // The input keeps focus (design 7.1), also for an Option rendered outside a Popup.\n onPointerDown: (event: PointerEvent<HTMLDivElement>) => preventDefault(event),\n onMouseDown: (event: MouseEvent<HTMLDivElement>) => preventDefault(event),\n onPointerMove: () => {\n if (!disabled && ctx.activeValue !== value) ctx.setActive(value);\n },\n \"data-selected\": dataAttr(selected),\n \"data-active\": dataAttr(state.active),\n \"data-pending\": dataAttr(state.pending),\n \"data-disabled\": dataAttr(disabled),\n };\n\n return useRender<ComponentPropsWithRef<\"div\">, ComboboxOptionState>({\n defaultElement: \"div\",\n render,\n state,\n className,\n style,\n children,\n props: rest,\n libraryProps,\n ref,\n });\n}\n\nComboboxOption.displayName = \"ComboboxOption\";\n"],"mappings":";;;;;;;AAyDA,MAAM,kBAAkB,UAAsC,MAAM,eAAe;;;;;;;;;;;AAYnF,SAAgB,eAAe,EAC7B,OACA,OACA,WAAW,OACX,WACA,OACA,UACA,QACA,GAAG,QACmB;CACtB,MAAM,MAAM,mBAAmB,iBAAiB;CAChD,MAAM,SAAS,IAAI,mBAAmB;CACtC,cACE,CAAC,QACD,mFACF;CACA,MAAM,EAAE,mBAAmB;CAE3B,MAAM,aAAa,OAA2B,IAAI;CAClD,MAAM,MAAM,aAAa,YAAyB;EAChD,WAAW,UAAU;EACrB,aAAa;GACX,IAAI,WAAW,YAAY,SAAS,WAAW,UAAU;EAC3D;CACF,GAAG,CAAC,CAAC;CAIL,sBAAsB;EACpB,MAAM,UAAU,WAAW;EAC3B,IAAI,YAAY,MAAM;EACtB,OAAO,eAAe,OAAO;GAAE;GAAS;GAAO;EAAS,CAAC;CAC3D,GAAG;EAAC;EAAgB;EAAO;EAAO;CAAQ,CAAC;CAE3C,MAAM,WAAW,IAAI,UAAU;CAC/B,MAAM,QAA6B;EACjC;EACA;EACA,QAAQ,IAAI,gBAAgB;EAC5B,SAAS,IAAI,gBAAgB;EAC7B;CACF;CAEA,MAAM,eAA8D;EAClE,IAAI,SAAS,IAAI,IAAI,cAAc,KAAK;EACxC,MAAM;EACN,iBAAiB;EACjB,iBAAiB,WAAW,OAAO,KAAA;EACnC,UAAU,UAAsC;GAC9C,IAAI,UAAU;GACd,IAAI,OAAO,OAAO,MAAM,WAAW;EACrC;EAEA,gBAAgB,UAAwC,eAAe,KAAK;EAC5E,cAAc,UAAsC,eAAe,KAAK;EACxE,qBAAqB;GACnB,IAAI,CAAC,YAAY,IAAI,gBAAgB,OAAO,IAAI,UAAU,KAAK;EACjE;EACA,iBAAiB,SAAS,QAAQ;EAClC,eAAe,SAAS,MAAM,MAAM;EACpC,gBAAgB,SAAS,MAAM,OAAO;EACtC,iBAAiB,SAAS,QAAQ;CACpC;CAEA,OAAO,UAA6D;EAClE,gBAAgB;EAChB;EACA;EACA;EACA;EACA;EACA,OAAO;EACP;EACA;CACF,CAAC;AACH;AAEA,eAAe,cAAc"}