@mortenbrudvik/waveui 0.9.0 → 0.11.0

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 (264) hide show
  1. package/README.md +758 -152
  2. package/dist/components/button/Toolbar.cjs +1 -1
  3. package/dist/components/button/Toolbar.cjs.map +1 -1
  4. package/dist/components/button/Toolbar.mjs +25 -30
  5. package/dist/components/button/Toolbar.mjs.map +1 -1
  6. package/dist/components/button/Toolbar.navigation.cjs +2 -0
  7. package/dist/components/button/Toolbar.navigation.cjs.map +1 -0
  8. package/dist/components/button/Toolbar.navigation.mjs +17 -0
  9. package/dist/components/button/Toolbar.navigation.mjs.map +1 -0
  10. package/dist/components/data-display/Avatar.cjs +1 -1
  11. package/dist/components/data-display/Avatar.cjs.map +1 -1
  12. package/dist/components/data-display/Avatar.context.cjs +2 -0
  13. package/dist/components/data-display/Avatar.context.cjs.map +1 -0
  14. package/dist/components/data-display/Avatar.context.mjs +9 -0
  15. package/dist/components/data-display/Avatar.context.mjs.map +1 -0
  16. package/dist/components/data-display/Avatar.mjs +66 -61
  17. package/dist/components/data-display/Avatar.mjs.map +1 -1
  18. package/dist/components/data-display/InteractionTag.cjs +2 -0
  19. package/dist/components/data-display/InteractionTag.cjs.map +1 -0
  20. package/dist/components/data-display/InteractionTag.mjs +234 -0
  21. package/dist/components/data-display/InteractionTag.mjs.map +1 -0
  22. package/dist/components/data-display/Tag.cjs +1 -1
  23. package/dist/components/data-display/Tag.cjs.map +1 -1
  24. package/dist/components/data-display/Tag.mjs +222 -90
  25. package/dist/components/data-display/Tag.mjs.map +1 -1
  26. package/dist/components/data-display/TagGroup.cjs +2 -0
  27. package/dist/components/data-display/TagGroup.cjs.map +1 -0
  28. package/dist/components/data-display/TagGroup.context.cjs +2 -0
  29. package/dist/components/data-display/TagGroup.context.cjs.map +1 -0
  30. package/dist/components/data-display/TagGroup.context.mjs +113 -0
  31. package/dist/components/data-display/TagGroup.context.mjs.map +1 -0
  32. package/dist/components/data-display/TagGroup.mjs +149 -0
  33. package/dist/components/data-display/TagGroup.mjs.map +1 -0
  34. package/dist/components/data-display/tagStyles.cjs +2 -0
  35. package/dist/components/data-display/tagStyles.cjs.map +1 -0
  36. package/dist/components/data-display/tagStyles.mjs +108 -0
  37. package/dist/components/data-display/tagStyles.mjs.map +1 -0
  38. package/dist/components/feedback/Toaster.cjs +1 -1
  39. package/dist/components/feedback/Toaster.cjs.map +1 -1
  40. package/dist/components/feedback/Toaster.mjs +213 -181
  41. package/dist/components/feedback/Toaster.mjs.map +1 -1
  42. package/dist/components/input/Calendar.cjs +2 -0
  43. package/dist/components/input/Calendar.cjs.map +1 -0
  44. package/dist/components/input/Calendar.mjs +352 -0
  45. package/dist/components/input/Calendar.mjs.map +1 -0
  46. package/dist/components/input/Calendar.views.cjs +2 -0
  47. package/dist/components/input/Calendar.views.cjs.map +1 -0
  48. package/dist/components/input/Calendar.views.mjs +194 -0
  49. package/dist/components/input/Calendar.views.mjs.map +1 -0
  50. package/dist/components/input/ColorArea.cjs +1 -1
  51. package/dist/components/input/ColorArea.cjs.map +1 -1
  52. package/dist/components/input/ColorArea.mjs +18 -18
  53. package/dist/components/input/ColorArea.mjs.map +1 -1
  54. package/dist/components/input/Combobox.cjs +1 -1
  55. package/dist/components/input/Combobox.cjs.map +1 -1
  56. package/dist/components/input/Combobox.expand.cjs +1 -1
  57. package/dist/components/input/Combobox.expand.cjs.map +1 -1
  58. package/dist/components/input/Combobox.expand.mjs +7 -7
  59. package/dist/components/input/Combobox.expand.mjs.map +1 -1
  60. package/dist/components/input/Combobox.mjs +232 -156
  61. package/dist/components/input/Combobox.mjs.map +1 -1
  62. package/dist/components/input/Combobox.text.cjs +2 -0
  63. package/dist/components/input/Combobox.text.cjs.map +1 -0
  64. package/dist/components/input/Combobox.text.mjs +24 -0
  65. package/dist/components/input/Combobox.text.mjs.map +1 -0
  66. package/dist/components/input/DatePicker.cjs +1 -1
  67. package/dist/components/input/DatePicker.cjs.map +1 -1
  68. package/dist/components/input/DatePicker.mjs +203 -288
  69. package/dist/components/input/DatePicker.mjs.map +1 -1
  70. package/dist/components/input/Dropdown.cjs +1 -1
  71. package/dist/components/input/Dropdown.cjs.map +1 -1
  72. package/dist/components/input/Dropdown.mjs +135 -109
  73. package/dist/components/input/Dropdown.mjs.map +1 -1
  74. package/dist/components/input/Listbox.cjs +2 -0
  75. package/dist/components/input/Listbox.cjs.map +1 -0
  76. package/dist/components/input/Listbox.mjs +134 -0
  77. package/dist/components/input/Listbox.mjs.map +1 -0
  78. package/dist/components/input/Option.cjs +1 -1
  79. package/dist/components/input/Option.cjs.map +1 -1
  80. package/dist/components/input/Option.mjs +163 -97
  81. package/dist/components/input/Option.mjs.map +1 -1
  82. package/dist/components/input/SwatchPicker.cjs +1 -1
  83. package/dist/components/input/SwatchPicker.cjs.map +1 -1
  84. package/dist/components/input/SwatchPicker.context.cjs +2 -0
  85. package/dist/components/input/SwatchPicker.context.cjs.map +1 -0
  86. package/dist/components/input/SwatchPicker.context.mjs +35 -0
  87. package/dist/components/input/SwatchPicker.context.mjs.map +1 -0
  88. package/dist/components/input/SwatchPicker.mjs +131 -103
  89. package/dist/components/input/SwatchPicker.mjs.map +1 -1
  90. package/dist/components/input/SwatchPicker.swatches.cjs +2 -0
  91. package/dist/components/input/SwatchPicker.swatches.cjs.map +1 -0
  92. package/dist/components/input/SwatchPicker.swatches.mjs +207 -0
  93. package/dist/components/input/SwatchPicker.swatches.mjs.map +1 -0
  94. package/dist/components/input/TagPicker.cjs +1 -1
  95. package/dist/components/input/TagPicker.cjs.map +1 -1
  96. package/dist/components/input/TagPicker.context.cjs +2 -0
  97. package/dist/components/input/TagPicker.context.cjs.map +1 -0
  98. package/dist/components/input/TagPicker.context.mjs +20 -0
  99. package/dist/components/input/TagPicker.context.mjs.map +1 -0
  100. package/dist/components/input/TagPicker.create.cjs +2 -0
  101. package/dist/components/input/TagPicker.create.cjs.map +1 -0
  102. package/dist/components/input/TagPicker.create.mjs +24 -0
  103. package/dist/components/input/TagPicker.create.mjs.map +1 -0
  104. package/dist/components/input/TagPicker.mjs +13 -274
  105. package/dist/components/input/TagPicker.mjs.map +1 -1
  106. package/dist/components/input/TagPicker.option.cjs +2 -0
  107. package/dist/components/input/TagPicker.option.cjs.map +1 -0
  108. package/dist/components/input/TagPicker.option.mjs +45 -0
  109. package/dist/components/input/TagPicker.option.mjs.map +1 -0
  110. package/dist/components/input/TagPicker.overflow.cjs +2 -0
  111. package/dist/components/input/TagPicker.overflow.cjs.map +1 -0
  112. package/dist/components/input/TagPicker.overflow.mjs +153 -0
  113. package/dist/components/input/TagPicker.overflow.mjs.map +1 -0
  114. package/dist/components/input/TagPicker.parts.cjs +2 -0
  115. package/dist/components/input/TagPicker.parts.cjs.map +1 -0
  116. package/dist/components/input/TagPicker.parts.mjs +425 -0
  117. package/dist/components/input/TagPicker.parts.mjs.map +1 -0
  118. package/dist/components/input/TagPicker.root.cjs +2 -0
  119. package/dist/components/input/TagPicker.root.cjs.map +1 -0
  120. package/dist/components/input/TagPicker.root.mjs +416 -0
  121. package/dist/components/input/TagPicker.root.mjs.map +1 -0
  122. package/dist/components/input/TimePicker.cjs +1 -1
  123. package/dist/components/input/TimePicker.cjs.map +1 -1
  124. package/dist/components/input/TimePicker.mjs +77 -77
  125. package/dist/components/input/TimePicker.mjs.map +1 -1
  126. package/dist/components/input/dateUtils.cjs +1 -1
  127. package/dist/components/input/dateUtils.cjs.map +1 -1
  128. package/dist/components/input/dateUtils.mjs +114 -96
  129. package/dist/components/input/dateUtils.mjs.map +1 -1
  130. package/dist/components/input/pickerLabels.cjs +2 -0
  131. package/dist/components/input/pickerLabels.cjs.map +1 -0
  132. package/dist/components/input/pickerLabels.mjs +16 -0
  133. package/dist/components/input/pickerLabels.mjs.map +1 -0
  134. package/dist/components/input/pickerValues.cjs +2 -0
  135. package/dist/components/input/pickerValues.cjs.map +1 -0
  136. package/dist/components/input/pickerValues.mjs +24 -0
  137. package/dist/components/input/pickerValues.mjs.map +1 -0
  138. package/dist/components/layout/Accordion.cjs.map +1 -1
  139. package/dist/components/layout/Accordion.mjs.map +1 -1
  140. package/dist/components/navigation/Hamburger.cjs +2 -0
  141. package/dist/components/navigation/Hamburger.cjs.map +1 -0
  142. package/dist/components/navigation/Hamburger.mjs +54 -0
  143. package/dist/components/navigation/Hamburger.mjs.map +1 -0
  144. package/dist/components/navigation/Nav.cjs +1 -1
  145. package/dist/components/navigation/Nav.cjs.map +1 -1
  146. package/dist/components/navigation/Nav.context.cjs +2 -0
  147. package/dist/components/navigation/Nav.context.cjs.map +1 -0
  148. package/dist/components/navigation/Nav.context.mjs +137 -0
  149. package/dist/components/navigation/Nav.context.mjs.map +1 -0
  150. package/dist/components/navigation/Nav.mjs +151 -258
  151. package/dist/components/navigation/Nav.mjs.map +1 -1
  152. package/dist/components/navigation/Nav.parts.cjs +2 -0
  153. package/dist/components/navigation/Nav.parts.cjs.map +1 -0
  154. package/dist/components/navigation/Nav.parts.mjs +77 -0
  155. package/dist/components/navigation/Nav.parts.mjs.map +1 -0
  156. package/dist/components/navigation/Nav.root.cjs +2 -0
  157. package/dist/components/navigation/Nav.root.cjs.map +1 -0
  158. package/dist/components/navigation/Nav.root.mjs +78 -0
  159. package/dist/components/navigation/Nav.root.mjs.map +1 -0
  160. package/dist/components/navigation/NavDrawer.cjs +2 -0
  161. package/dist/components/navigation/NavDrawer.cjs.map +1 -0
  162. package/dist/components/navigation/NavDrawer.context.cjs +2 -0
  163. package/dist/components/navigation/NavDrawer.context.cjs.map +1 -0
  164. package/dist/components/navigation/NavDrawer.context.mjs +11 -0
  165. package/dist/components/navigation/NavDrawer.context.mjs.map +1 -0
  166. package/dist/components/navigation/NavDrawer.mjs +179 -0
  167. package/dist/components/navigation/NavDrawer.mjs.map +1 -0
  168. package/dist/components/overlays/Dialog.cjs +1 -1
  169. package/dist/components/overlays/Dialog.cjs.map +1 -1
  170. package/dist/components/overlays/Dialog.mjs +190 -125
  171. package/dist/components/overlays/Dialog.mjs.map +1 -1
  172. package/dist/components/overlays/Dialog.shared.cjs +1 -1
  173. package/dist/components/overlays/Dialog.shared.cjs.map +1 -1
  174. package/dist/components/overlays/Dialog.shared.mjs +157 -81
  175. package/dist/components/overlays/Dialog.shared.mjs.map +1 -1
  176. package/dist/components/overlays/Dialog.slots.cjs +2 -0
  177. package/dist/components/overlays/Dialog.slots.cjs.map +1 -0
  178. package/dist/components/overlays/Dialog.slots.mjs +129 -0
  179. package/dist/components/overlays/Dialog.slots.mjs.map +1 -0
  180. package/dist/components/overlays/Drawer.cjs +1 -1
  181. package/dist/components/overlays/Drawer.cjs.map +1 -1
  182. package/dist/components/overlays/Drawer.context.cjs +2 -0
  183. package/dist/components/overlays/Drawer.context.cjs.map +1 -0
  184. package/dist/components/overlays/Drawer.context.mjs +29 -0
  185. package/dist/components/overlays/Drawer.context.mjs.map +1 -0
  186. package/dist/components/overlays/Drawer.mjs +12 -187
  187. package/dist/components/overlays/Drawer.mjs.map +1 -1
  188. package/dist/components/overlays/Drawer.panel.cjs +2 -0
  189. package/dist/components/overlays/Drawer.panel.cjs.map +1 -0
  190. package/dist/components/overlays/Drawer.panel.mjs +159 -0
  191. package/dist/components/overlays/Drawer.panel.mjs.map +1 -0
  192. package/dist/components/overlays/Drawer.parts.cjs +2 -0
  193. package/dist/components/overlays/Drawer.parts.cjs.map +1 -0
  194. package/dist/components/overlays/Drawer.parts.mjs +152 -0
  195. package/dist/components/overlays/Drawer.parts.mjs.map +1 -0
  196. package/dist/components/overlays/Drawer.root.cjs +2 -0
  197. package/dist/components/overlays/Drawer.root.cjs.map +1 -0
  198. package/dist/components/overlays/Drawer.root.mjs +207 -0
  199. package/dist/components/overlays/Drawer.root.mjs.map +1 -0
  200. package/dist/components/overlays/Drawer.scroll.cjs +2 -0
  201. package/dist/components/overlays/Drawer.scroll.cjs.map +1 -0
  202. package/dist/components/overlays/Drawer.scroll.mjs +42 -0
  203. package/dist/components/overlays/Drawer.scroll.mjs.map +1 -0
  204. package/dist/components/table/useGridNavigation.cjs +1 -1
  205. package/dist/components/table/useGridNavigation.cjs.map +1 -1
  206. package/dist/components/table/useGridNavigation.mjs +67 -70
  207. package/dist/components/table/useGridNavigation.mjs.map +1 -1
  208. package/dist/hooks/useActiveDescendant.cjs +2 -0
  209. package/dist/hooks/useActiveDescendant.cjs.map +1 -0
  210. package/dist/hooks/useActiveDescendant.mjs +56 -0
  211. package/dist/hooks/useActiveDescendant.mjs.map +1 -0
  212. package/dist/hooks/useDismiss.cjs.map +1 -1
  213. package/dist/hooks/useDismiss.mjs.map +1 -1
  214. package/dist/hooks/useFocusTrap.cjs +1 -1
  215. package/dist/hooks/useFocusTrap.cjs.map +1 -1
  216. package/dist/hooks/useFocusTrap.mjs +115 -98
  217. package/dist/hooks/useFocusTrap.mjs.map +1 -1
  218. package/dist/hooks/useListbox.cjs +1 -1
  219. package/dist/hooks/useListbox.cjs.map +1 -1
  220. package/dist/hooks/useListbox.mjs +250 -210
  221. package/dist/hooks/useListbox.mjs.map +1 -1
  222. package/dist/hooks/useModalLayer.cjs +1 -1
  223. package/dist/hooks/useModalLayer.cjs.map +1 -1
  224. package/dist/hooks/useModalLayer.mjs +35 -22
  225. package/dist/hooks/useModalLayer.mjs.map +1 -1
  226. package/dist/hooks/useRestoreFocus.cjs +1 -1
  227. package/dist/hooks/useRestoreFocus.cjs.map +1 -1
  228. package/dist/hooks/useRestoreFocus.mjs +91 -78
  229. package/dist/hooks/useRestoreFocus.mjs.map +1 -1
  230. package/dist/hooks/useRovingGrid.cjs +2 -0
  231. package/dist/hooks/useRovingGrid.cjs.map +1 -0
  232. package/dist/hooks/useRovingGrid.mjs +69 -0
  233. package/dist/hooks/useRovingGrid.mjs.map +1 -0
  234. package/dist/hooks/useRovingTabIndex.cjs +1 -1
  235. package/dist/hooks/useRovingTabIndex.cjs.map +1 -1
  236. package/dist/hooks/useRovingTabIndex.mjs +102 -78
  237. package/dist/hooks/useRovingTabIndex.mjs.map +1 -1
  238. package/dist/index.cjs +1 -1
  239. package/dist/index.d.cts +4391 -394
  240. package/dist/index.d.ts +4391 -394
  241. package/dist/index.mjs +115 -101
  242. package/dist/lib/classNames.cjs +1 -1
  243. package/dist/lib/classNames.cjs.map +1 -1
  244. package/dist/lib/classNames.mjs +170 -74
  245. package/dist/lib/classNames.mjs.map +1 -1
  246. package/dist/lib/date.cjs +2 -0
  247. package/dist/lib/date.cjs.map +1 -0
  248. package/dist/lib/date.mjs +44 -0
  249. package/dist/lib/date.mjs.map +1 -0
  250. package/dist/lib/focus.cjs +1 -1
  251. package/dist/lib/focus.cjs.map +1 -1
  252. package/dist/lib/focus.mjs +5 -2
  253. package/dist/lib/focus.mjs.map +1 -1
  254. package/dist/lib/icons.cjs +1 -1
  255. package/dist/lib/icons.cjs.map +1 -1
  256. package/dist/lib/icons.mjs +13 -6
  257. package/dist/lib/icons.mjs.map +1 -1
  258. package/dist/lib/layers.cjs +1 -1
  259. package/dist/lib/layers.cjs.map +1 -1
  260. package/dist/lib/layers.mjs +53 -47
  261. package/dist/lib/layers.mjs.map +1 -1
  262. package/dist/styles.css +1 -1
  263. package/package.json +1 -1
  264. package/src/styles/tokens.css +25 -0
@@ -0,0 +1,24 @@
1
+ "use client";
2
+ //#region src/components/input/TagPicker.create.ts
3
+ function e(e, t) {
4
+ for (let n of e) if (n.toLowerCase() === t) return !0;
5
+ return !1;
6
+ }
7
+ function t(t, n) {
8
+ let r = t.trim();
9
+ if (r === "") return null;
10
+ let i = r.toLowerCase();
11
+ if (e(n.labels, i) || e(n.selected, i)) return null;
12
+ for (let e of n.values) if (e === r) return null;
13
+ return r;
14
+ }
15
+ function n(e, t, n) {
16
+ let r = e.trim().toLowerCase();
17
+ if (r === "") return null;
18
+ for (let { value: e, label: i } of t) if (!n.has(e) && i.toLowerCase() === r) return e;
19
+ return null;
20
+ }
21
+ //#endregion
22
+ export { t as createOptionText, n as findExactMatch };
23
+
24
+ //# sourceMappingURL=TagPicker.create.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"TagPicker.create.mjs","names":["foldedIncludes","text","texts","folded","createOptionText","query","known","value","findExactMatch","candidates","selected","label"],"sources":["../../../src/components/input/TagPicker.create.ts"],"sourcesContent":["/**\n * The pure rules of TagPicker's free tagging (`allowCreate`) and of its exact-match ranking:\n * which typed text offers a create option, and which option a typed text makes active.\n * Module-only (never exported from a barrel); it imports no component.\n */\n\n/** What the create option is checked against (case folding as the default filter's). */\nexport interface CreateOptionKnown {\n /** The labels of every known option: listed, filtered out or selected alike. */\n labels: Iterable<string>;\n /** The values of every known option, compared exactly. */\n values: Iterable<string>;\n /** The selected values (a created tag has no option), compared ignoring case. */\n selected: Iterable<string>;\n}\n\nfunction foldedIncludes(texts: Iterable<string>, folded: string): boolean {\n for (const text of texts) if (text.toLowerCase() === folded) return true;\n return false;\n}\n\n/**\n * The text a create option would add for the typed `query`, or `null` when none is offered: the\n * trimmed text, unless it is empty, equals a known option label or a selected value ignoring case\n * (`toLowerCase()`, the default filter's folding), or equals a known option value exactly\n * (creating must not select another option by its value).\n *\n * @param query The typed text.\n * @param known The labels, values and selected values to check against.\n */\nexport function createOptionText(query: string, known: CreateOptionKnown): string | null {\n const text = query.trim();\n if (text === '') return null;\n const folded = text.toLowerCase();\n if (foldedIncludes(known.labels, folded)) return null;\n if (foldedIncludes(known.selected, folded)) return null;\n for (const value of known.values) if (value === text) return null;\n return text;\n}\n\n/** An option the exact match may pick: its value and its label. */\nexport interface ExactMatchCandidate {\n value: string;\n label: string;\n}\n\n/**\n * The value of the first option in list order whose label equals the trimmed `text` ignoring case\n * and that is not selected, or `null` (no text, or no such option).\n *\n * @param text The typed text.\n * @param candidates The known options, in list order.\n * @param selected The selected values (skipped).\n */\nexport function findExactMatch(\n text: string,\n candidates: Iterable<ExactMatchCandidate>,\n selected: ReadonlySet<string>,\n): string | null {\n const folded = text.trim().toLowerCase();\n if (folded === '') return null;\n for (const { value, label } of candidates) {\n if (!selected.has(value) && label.toLowerCase() === folded) return value;\n }\n return null;\n}\n"],"mappings":";;AAgBA,SAASA,EAAe,GAAyB,GAAyB;CACxE,KAAK,IAAMC,KAAQC,GAAO,IAAID,EAAK,YAAY,MAAME,GAAQ,OAAO;CACpE,OAAO;AACT;AAWA,SAAgBC,EAAiB,GAAe,GAAyC;CACvF,IAAMH,IAAOI,EAAM,KAAK;CACxB,IAAIJ,MAAS,IAAI,OAAO;CACxB,IAAME,IAASF,EAAK,YAAY;CAEhC,IADID,EAAeM,EAAM,QAAQH,CAAM,KACnCH,EAAeM,EAAM,UAAUH,CAAM,GAAG,OAAO;CACnD,KAAK,IAAMI,KAASD,EAAM,QAAQ,IAAIC,MAAUN,GAAM,OAAO;CAC7D,OAAOA;AACT;AAgBA,SAAgBO,EACd,GACA,GACA,GACe;CACf,IAAML,IAASF,EAAK,KAAK,CAAC,CAAC,YAAY;CACvC,IAAIE,MAAW,IAAI,OAAO;CAC1B,KAAK,IAAM,EAAE,UAAO,cAAWM,GAC7B,IAAI,CAACC,EAAS,IAAIH,CAAK,KAAKI,EAAM,YAAY,MAAMR,GAAQ,OAAOI;CAErE,OAAO;AACT"}
@@ -1,279 +1,18 @@
1
1
  "use client";
2
- import { cn as e } from "../../lib/cn.mjs";
3
- import { warnDeprecated as t, warnOnce as n } from "../../lib/dev.mjs";
4
- import { composeEventHandlers as r } from "../../lib/composeEventHandlers.mjs";
5
- import { tagPickerClassNames as i } from "../../lib/classNames.mjs";
6
- import { useControllable as a } from "../../hooks/useControllable.mjs";
7
- import { useId as o } from "../../hooks/useId.mjs";
8
- import { getArrowIntent as s, getDirection as ee } from "../../lib/direction.mjs";
9
- import { useMergedRefs as c } from "../../hooks/useMergedRefs.mjs";
10
- import { useIsClient as te } from "../../hooks/useIsClient.mjs";
11
- import { joinIds as ne } from "../../lib/aria.mjs";
12
- import { useFieldContext as re, useFieldControl as ie } from "../../hooks/useFieldControl.mjs";
13
- import { useAnnounce as ae } from "../../hooks/useAnnounce.mjs";
14
- import { focusRing as l, inputAppearanceClasses as oe, inputFocusWithin as se } from "../../lib/styles.mjs";
15
- import { DismissIcon as u } from "../../lib/icons.mjs";
16
- import { useInputLook as ce } from "./inputLook.mjs";
17
- import { isInvalidLook as le } from "./Input.mjs";
18
- import { useFormReset as ue } from "../../hooks/useFormReset.mjs";
19
- import { HiddenInput as de } from "../internal/HiddenInput.mjs";
20
- import { useListbox as fe } from "../../hooks/useListbox.mjs";
21
- import { ListboxSurface as pe, Option as me, useListboxPopup as he } from "./Option.mjs";
22
- import { usePreserveFocus as d } from "../../hooks/usePreserveFocus.mjs";
23
- import * as f from "react";
24
- import { jsx as p, jsxs as m } from "react/jsx-runtime";
2
+ import { TagPickerOption as e } from "./TagPicker.option.mjs";
3
+ import { TagPickerButton as t, TagPickerControl as n, TagPickerGroup as r, TagPickerInput as i, TagPickerList as a, TagPickerOptionGroup as o } from "./TagPicker.parts.mjs";
4
+ import { TagPickerRoot as s } from "./TagPicker.root.mjs";
25
5
  //#region src/components/input/TagPicker.tsx
26
- var ge = (e) => `Remove ${e}`, _e = (e) => `Selected: ${e.join(", ")}`, ve = (e, t) => `${e} added, ${t} selected`, ye = (e, t) => `${e} removed, ${t} selected`, be = [
27
- "medium",
28
- "large",
29
- "extra-large"
30
- ], xe = {
31
- medium: {
32
- control: "gap-1 px-2 py-1.5",
33
- tag: "px-2 py-0.5 text-body-1",
34
- input: "py-0.5 text-body-1"
35
- },
36
- large: {
37
- control: "gap-1.5 px-2.5 py-2",
38
- tag: "px-2 py-1 text-body-1",
39
- input: "py-1 text-body-1"
40
- },
41
- "extra-large": {
42
- control: "gap-1.5 px-3 py-2.5",
43
- tag: "px-2.5 py-1 text-body-2",
44
- input: "py-1 text-body-2"
45
- }
46
- }, h = [];
47
- function Se(e, t) {
48
- return e.length === t.length && e.every((e, n) => e === t[n]);
49
- }
50
- function Ce({ label: t, disabled: n, onClick: r, onKeyDown: a, getFallback: o }) {
51
- let s = f.useRef(null);
52
- return d(s, o), /* @__PURE__ */ p("button", {
53
- ref: s,
54
- type: "button",
55
- "data-wave-tagpicker-remove": "",
56
- disabled: n,
57
- "aria-label": t,
58
- className: e(i.removeButton, "ms-0.5 inline-flex rounded-sm bg-transparent p-0 text-muted-foreground not-disabled:not-aria-disabled:hover:text-foreground disabled:cursor-not-allowed", l),
59
- onClick: r,
60
- onKeyDown: a,
61
- children: /* @__PURE__ */ p(u, { size: 12 })
62
- });
63
- }
64
- var g = (l) => {
65
- let { options: u, value: d, defaultValue: g, onValueChange: we, onChange: _, open: Te, defaultOpen: Ee, onOpenChange: De, placeholder: Oe = "Select...", disabled: v = !1, size: ke, appearance: Ae, name: je, form: y, required: b, autoComplete: x = "off", autoCapitalize: S, autoCorrect: Me, maxLength: Ne, readOnly: C, labels: w, inputMode: Pe, spellCheck: Fe, enterKeyHint: Ie, autoFocus: Le, tabIndex: Re, id: ze, "aria-label": Be, "aria-labelledby": Ve, "aria-describedby": He, "aria-invalid": Ue, "aria-required": We, "aria-errormessage": Ge, "aria-details": Ke, onFocus: qe, onBlur: T, onKeyDown: Je, onKeyUp: Ye, controlRef: Xe, className: Ze, ref: Qe, ...$e } = l;
66
- _ !== void 0 && t("TagPicker", "onChange", "onValueChange");
67
- let E = re(), et = (b ?? E?.required ?? !1) && !C, D = ie({
68
- id: ze,
69
- "aria-label": Be,
70
- "aria-labelledby": Ve,
71
- "aria-describedby": He,
72
- "aria-invalid": Ue,
73
- "aria-required": We ?? b
74
- }), tt = le(!1, D["aria-invalid"]), { size: O, appearance: k } = ce(ke, Ae, {
75
- sizes: be,
76
- defaultSize: "medium"
77
- }), A = xe[O], [j, M] = a(d, g ?? h, (e) => {
78
- let t = [...e];
79
- we?.(t), _?.(t);
80
- }), N = !v && !C, [P, F] = a(Te, (Ee ?? !1) && N, De), nt = te(), I = P && N && nt, [L, R] = f.useState(""), [rt, it] = f.useState(N);
81
- rt !== N && (it(N), N || R(""));
82
- let z = !N && P;
83
- f.useEffect(() => {
84
- z && F(!1);
85
- }, [z, F]);
86
- let B = ae(), V = f.useRef(null), H = f.useRef(null), U = o("tagpicker-summary"), W = f.useMemo(() => {
87
- let e = /* @__PURE__ */ new Map();
88
- for (let t of u) e.has(t.value) || e.set(t.value, t.label);
89
- return e;
90
- }, [u]), G = (e) => W.get(e) ?? e, at = f.useMemo(() => {
91
- let e = new Set(j);
92
- return u.filter((t) => !e.has(t.value)).map((e) => ({
93
- value: e.value,
94
- label: e.label
95
- }));
96
- }, [u, j]), ot = f.useMemo(() => L ? (e) => e.label.toLowerCase().includes(L.toLowerCase()) : void 0, [L]), K = j.filter((e) => !W.has(e)).join("\0");
97
- f.useEffect(() => {
98
- K && n("TagPicker:unknown-value", `TagPicker: the selected value(s) ${K.split("\0").map((e) => `"${e}"`).join(", ")} match no option; the raw value is shown as the tag label.`);
99
- }, [K]);
100
- let st = () => H.current, q = () => H.current?.focus(), ct = (e) => {
101
- if (!N || j.includes(e)) return;
102
- let t = [...j, e];
103
- M(t), R(""), B((w?.added ?? ve)(G(e), t.length));
104
- }, J = (e) => {
105
- if (!N) return;
106
- let t = j.filter((t) => t !== e);
107
- M(t), B((w?.removed ?? ye)(G(e), t.length)), q();
108
- }, Y = fe({
109
- open: I,
110
- onOpenChange: (e) => F(e),
111
- mode: "editable",
112
- multiple: !0,
113
- selectedValues: j,
114
- onSelect: (e) => ct(e),
115
- items: at,
116
- filter: ot,
117
- autoHighlight: L ? "first" : !1,
118
- idPrefix: "tagpicker-listbox",
119
- onClearDraft: L ? () => R("") : void 0
120
- }), X = I && Y.items.length > 0, Z = w?.noMatches ?? "No matches", Q = I && (X || L !== ""), lt = Q && !X, { layerId: ut, setReference: dt, surfaceRef: ft, floatingProps: pt } = he({
121
- open: I,
122
- surfaceOpen: Q,
123
- onDismiss: () => F(!1),
124
- rootRef: V,
125
- anchorRef: H
126
- }), mt = c(V, Qe), ht = c(H, Xe);
127
- ue(H, () => {
128
- let e = g ?? h;
129
- M((t) => Se(t, e) ? t : [...e]), R("");
130
- }, y);
131
- let $ = (e) => Array.from(e.closest("[data-wave-tagpicker-control]")?.querySelectorAll("[data-wave-tagpicker-remove]") ?? []), gt = (e) => {
132
- if (N) {
133
- if (e.key === "Escape" && I && !Q && !e.nativeEvent.isComposing) {
134
- F(!1);
135
- return;
136
- }
137
- if (e.key === "Backspace" && L === "" && j.length > 0) {
138
- e.preventDefault(), $(e.currentTarget).at(-1)?.focus();
139
- return;
140
- }
141
- Y.onKeyDown(e);
142
- }
143
- }, _t = (e, t) => {
144
- if (!N) return;
145
- let n = $(e.currentTarget), r = n.indexOf(e.currentTarget);
146
- if (e.key === "Backspace" || e.key === "Delete") {
147
- e.preventDefault(), J(t);
148
- return;
149
- }
150
- if (e.key === "Escape") {
151
- e.preventDefault(), q();
152
- return;
153
- }
154
- let i = s(e.key, {
155
- orientation: "horizontal",
156
- dir: ee(e.currentTarget)
157
- });
158
- i === "prev" && r > 0 ? (e.preventDefault(), n[r - 1].focus()) : i === "next" && (e.preventDefault(), r < n.length - 1 ? n[r + 1].focus() : q());
159
- }, vt = D["aria-label"] === void 0 ? D["aria-labelledby"] ?? E?.labelId : D["aria-labelledby"];
160
- return /* @__PURE__ */ m("div", {
161
- "data-size": O,
162
- "data-appearance": k,
163
- ...$e,
164
- ref: mt,
165
- className: e(i.root, "relative", Ze),
166
- children: [
167
- /* @__PURE__ */ m("div", {
168
- ref: dt,
169
- "data-wave-tagpicker-control": "",
170
- role: "group",
171
- "aria-disabled": v || void 0,
172
- className: e(i.control, "flex flex-wrap items-center", A.control, oe[k], se, tt && "border-destructive", v && "cursor-not-allowed opacity-50"),
173
- onClick: (e) => {
174
- v || e.target.closest("button") || (q(), !I && N && F(!0));
175
- },
176
- children: [
177
- j.length > 0 && /* @__PURE__ */ p("div", {
178
- role: "list",
179
- "aria-label": w?.selected ?? "Selected",
180
- className: e(i.group, "flex flex-wrap items-center gap-1"),
181
- children: j.map((t) => {
182
- let n = G(t);
183
- return /* @__PURE__ */ m("div", {
184
- role: "listitem",
185
- className: e(i.tag, "inline-flex items-center gap-1 rounded bg-muted text-foreground", A.tag),
186
- children: [n, !C && /* @__PURE__ */ p(Ce, {
187
- label: (w?.remove ?? ge)(n),
188
- disabled: v,
189
- onClick: () => J(t),
190
- onKeyDown: (e) => _t(e, t),
191
- getFallback: st
192
- })]
193
- }, t);
194
- })
195
- }),
196
- j.length > 0 && /* @__PURE__ */ p("span", {
197
- id: U,
198
- hidden: !0,
199
- children: (w?.summary ?? _e)(j.map(G))
200
- }),
201
- /* @__PURE__ */ p("input", {
202
- type: "text",
203
- autoComplete: x,
204
- ...D,
205
- ...Y.getComboboxProps(),
206
- "aria-expanded": X,
207
- "aria-describedby": ne(D["aria-describedby"], j.length > 0 ? U : void 0),
208
- "aria-errormessage": Ge,
209
- "aria-details": Ke,
210
- ref: ht,
211
- disabled: v,
212
- readOnly: C,
213
- placeholder: j.length === 0 ? Oe : "",
214
- autoCapitalize: S,
215
- autoCorrect: Me,
216
- maxLength: Ne,
217
- inputMode: Pe,
218
- spellCheck: Fe,
219
- enterKeyHint: Ie,
220
- autoFocus: Le,
221
- tabIndex: Re,
222
- value: L,
223
- onChange: (e) => {
224
- N && (R(e.target.value), I || F(!0));
225
- },
226
- onKeyDown: r(Je, gt),
227
- onKeyUp: r(Ye, Y.onKeyUp),
228
- onFocus: qe,
229
- onBlur: T,
230
- className: e(i.input, "min-w-15 flex-1 bg-transparent text-foreground placeholder:text-muted-foreground focus:outline-hidden", A.input)
231
- })
232
- ]
233
- }),
234
- /* @__PURE__ */ p("span", {
235
- role: "status",
236
- className: "sr-only",
237
- children: lt && Z
238
- }),
239
- /* @__PURE__ */ p(pe, {
240
- listbox: Y,
241
- layerId: ut,
242
- surfaceRef: ft,
243
- floatingProps: pt,
244
- open: I,
245
- expanded: X,
246
- showCheck: !1,
247
- listboxClassName: i.listbox,
248
- surfaceClassName: i.surface,
249
- emptyContent: L === "" ? void 0 : /* @__PURE__ */ p("div", {
250
- "aria-hidden": "true",
251
- className: e(i.emptyMessage, "px-3 py-1.5 text-body-1 text-muted-foreground"),
252
- children: Z
253
- }),
254
- "aria-label": D["aria-label"],
255
- "aria-labelledby": vt,
256
- children: X && Y.items.map((e) => /* @__PURE__ */ p(me, {
257
- value: e.value,
258
- label: e.label,
259
- className: i.option,
260
- children: e.label
261
- }, e.value))
262
- }),
263
- /* @__PURE__ */ p(de, {
264
- name: je,
265
- form: y,
266
- disabled: v,
267
- value: j,
268
- type: "text",
269
- required: et,
270
- onInvalid: q
271
- })
272
- ]
273
- });
274
- };
275
- g.displayName = "TagPicker";
6
+ var c = /* @__PURE__ */ Object.assign(s, {
7
+ Control: n,
8
+ Group: r,
9
+ Input: i,
10
+ Button: t,
11
+ List: a,
12
+ Option: e,
13
+ OptionGroup: o
14
+ });
276
15
  //#endregion
277
- export { g as TagPicker };
16
+ export { c as TagPicker, t as TagPickerButton, n as TagPickerControl, r as TagPickerGroup, i as TagPickerInput, a as TagPickerList, e as TagPickerOption, o as TagPickerOptionGroup };
278
17
 
279
18
  //# sourceMappingURL=TagPicker.mjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"TagPicker.mjs","names":["useId","useId$1","React","defaultRemoveLabel","label","defaultSummaryLabel","labels","defaultAddedLabel","count","defaultRemovedLabel","TAG_PICKER_SIZES","TAG_PICKER_SIZE","EMPTY","sameTags","a","b","value","index","TagRemoveButton","ref","usePreserveFocus","getFallback","cn","tagPickerClassNames","focusRing","DismissIcon","TagPicker","valueProp","openProp","sizeProp","appearanceProp","ariaLabel","ariaLabelledBy","ariaDescribedBy","ariaInvalid","ariaRequired","ariaErrorMessage","ariaDetails","rest","props","onChange","warnDeprecated","field","useFieldContext","validates","required","readOnly","fieldProps","useFieldControl","invalidLook","isInvalidLook","useInputLook","sizeClasses","size","selected","setSelected","useControllable","defaultValue","list","next","onValueChange","interactive","disabled","openState","setOpen","defaultOpen","onOpenChange","isClient","useIsClient","open","query","setQuery","wasInteractive","setWasInteractive","lockedOpen","announce","useAnnounce","rootRef","inputRef","summaryId","labelByValue","map","option","options","labelOf","available","chosen","filter","item","unknownValues","warnOnce","v","getInput","focusInput","addTag","removeTag","listbox","useListbox","expanded","noMatchesText","surfaceOpen","noMatches","useListboxPopup","rootMergedRef","useMergedRefs","inputMergedRef","controlRef","initial","current","form","removeButtons","from","handleInputKeyDown","event","handleTagKeyDown","buttons","intent","getArrowIntent","getDirection","listLabelledBy","appearance","className","setReference","inputAppearanceClasses","inputFocusWithin","joinIds","placeholder","composeEventHandlers","onKeyDown","onKeyUp","ListboxSurface","Option","HiddenInput"],"sources":["../../../src/components/input/TagPicker.tsx"],"sourcesContent":["import * as React from 'react';\nimport { cn } from '../../lib/cn';\nimport { joinIds } from '../../lib/aria';\nimport { tagPickerClassNames } from '../../lib/classNames';\nimport { composeEventHandlers } from '../../lib/composeEventHandlers';\nimport { warnDeprecated, warnOnce } from '../../lib/dev';\nimport { getArrowIntent, getDirection } from '../../lib/direction';\nimport { DismissIcon } from '../../lib/icons';\nimport {\n focusRing,\n inputAppearanceClasses,\n inputFocusWithin,\n inputInvalidWithin,\n} from '../../lib/styles';\nimport type { InputAppearance, Size } from '../../lib/types';\nimport { useAnnounce } from '../../hooks/useAnnounce';\nimport { useControllable } from '../../hooks/useControllable';\nimport { useFieldContext, useFieldControl } from '../../hooks/useFieldControl';\nimport { useFormReset } from '../../hooks/useFormReset';\nimport { useId } from '../../hooks/useId';\nimport { useIsClient } from '../../hooks/useIsClient';\nimport { useListbox, type ListboxItem } from '../../hooks/useListbox';\nimport { useMergedRefs } from '../../hooks/useMergedRefs';\nimport { usePreserveFocus } from '../../hooks/usePreserveFocus';\nimport { HiddenInput } from '../internal/HiddenInput';\nimport { useInputLook } from './inputLook';\nimport { isInvalidLook } from './Input';\nimport { ListboxSurface, Option, useListboxPopup } from './Option';\nimport type { RoutedHandlers } from './routedHandlers';\n\n/** Represents a single selectable tag option. */\nexport interface TagPickerOption {\n /** Unique identifier for the option. */\n value: string;\n /** Display label for the option. */\n label: string;\n}\n\n/**\n * The TagPicker's built-in names, descriptions and announcements, for localization. Each member is\n * optional and falls back to its English default.\n */\nexport interface TagPickerLabels {\n /** Name of a tag's remove button.\n * @default (label) => `Remove ${label}`\n */\n remove?: (label: string) => string;\n /** Name of the list of selected tags.\n * @default 'Selected'\n */\n selected?: string;\n /** Description of the input that sums up the selected tags (their labels, in order).\n * @default (labels) => `Selected: ${labels.join(', ')}`\n */\n summary?: (labels: string[]) => string;\n /** Announcement when a tag is added; `count` is the number of tags after the change.\n * @default (label, count) => `${label} added, ${count} selected`\n */\n added?: (label: string, count: number) => string;\n /** Announcement when a tag is removed; `count` is the number of tags after the change.\n * @default (label, count) => `${label} removed, ${count} selected`\n */\n removed?: (label: string, count: number) => string;\n /** Status text (announced, and shown in the popup) when the typed text matches no option.\n * @default 'No matches'\n */\n noMatches?: string;\n}\n\nconst defaultRemoveLabel = (label: string) => `Remove ${label}`;\nconst defaultSummaryLabel = (labels: string[]) => `Selected: ${labels.join(', ')}`;\nconst defaultAddedLabel = (label: string, count: number) => `${label} added, ${count} selected`;\nconst defaultRemovedLabel = (label: string, count: number) => `${label} removed, ${count} selected`;\n\n/** Properties for the TagPicker component. */\nexport interface TagPickerProps extends Omit<\n React.HTMLAttributes<HTMLDivElement>,\n 'onChange' | 'defaultValue' | RoutedHandlers\n> {\n /** Available options to choose from. */\n options: readonly TagPickerOption[];\n /** Controlled array of selected option values (`[]` for none). */\n value?: readonly string[];\n /**\n * Initial selected values for uncontrolled usage; a form reset restores them.\n * @default []\n */\n defaultValue?: readonly string[];\n /**\n * Called with the new selection when it changes: a tag is added or removed, or a form reset\n * restores different tags.\n */\n onValueChange?: (value: string[]) => void;\n /**\n * Called with the new selection when it changes: a tag is added or removed, or a form reset\n * restores different tags.\n * @deprecated Use `onValueChange`.\n */\n onChange?: (value: string[]) => void;\n /**\n * Controlled open state of the option list. The list renders only in the browser: an open\n * picker is closed in the server HTML and opens once it has hydrated.\n */\n open?: boolean;\n /**\n * Initial open state for uncontrolled usage. A picker that starts disabled or read-only starts\n * closed. The list renders only in the browser: it is closed in the server HTML and opens once\n * the picker has hydrated.\n * @default false\n */\n defaultOpen?: boolean;\n /** Called when the option list opens or closes. */\n onOpenChange?: (open: boolean) => void;\n /**\n * Placeholder text shown when no tags are selected.\n * @default 'Select...'\n */\n placeholder?: string;\n /**\n * Whether the tag picker is disabled: nothing can be added or removed, and the remove buttons\n * leave the tab order.\n * @default false\n */\n disabled?: boolean;\n /**\n * Size of the tags and the field: `medium` (the 0.7 look), `large` or `extra-large` (Fluent's\n * union; TagPicker has no `small`). Default: the surrounding Field's `size`, else `WaveProvider\n * inputDefaults.size`, else `'medium'`; a Field or provider `small` gives `'medium'` too. The\n * option list keeps its size.\n */\n size?: TagPickerSize;\n /**\n * Look of the field: `outline` (a full border), `underline` (a bottom stroke only),\n * `filled-darker` or `filled-lighter` (a fill without a visible stroke: give the field a visible\n * label). Default: `WaveProvider inputDefaults.appearance`, else `'outline'`.\n */\n appearance?: InputAppearance;\n /** Name of the values in form submissions (one hidden input per value). */\n name?: string;\n /** Id of the form the values belong to, when the TagPicker is outside it. */\n form?: string;\n /**\n * At least one tag is required to submit the form (native constraint validation). Like a native\n * readonly input, a `readOnly` TagPicker does not block submission.\n */\n required?: boolean;\n /**\n * The `autocomplete` attribute of the `<input role=\"combobox\">`. Browser autofill is off unless\n * you set it (for example `'on'`).\n * @default 'off'\n */\n autoComplete?: string;\n /** The maximum length of the typed text (`maxlength` of the `<input role=\"combobox\">`). */\n maxLength?: number;\n /**\n * The selection cannot be changed: the input is read-only, the tags have no remove buttons, and\n * the option list neither opens (click, keyboard, a controlled `open`) nor adds tags; Escape is\n * left to the page. Turning it (or `disabled`) on while the list is open closes it\n * (`onOpenChange(false)`); a focused remove button hands focus to the input.\n */\n readOnly?: boolean;\n /**\n * Names of the remove buttons and the tag list, the summary that describes the input, the\n * announcements and the \"No matches\" text, for localization. Unset members keep their English\n * defaults.\n */\n labels?: TagPickerLabels;\n /** Called when the `<input role=\"combobox\">` receives focus (the root keeps other handlers). */\n onFocus?: React.FocusEventHandler<HTMLInputElement>;\n /** Called when the `<input role=\"combobox\">` loses focus. */\n onBlur?: React.FocusEventHandler<HTMLInputElement>;\n /**\n * Called on a key press in the `<input role=\"combobox\">`, before the built-in listbox and tag\n * keys; `event.preventDefault()` skips them.\n */\n onKeyDown?: React.KeyboardEventHandler<HTMLInputElement>;\n /** Called when a key is released in the `<input role=\"combobox\">`. */\n onKeyUp?: React.KeyboardEventHandler<HTMLInputElement>;\n /** Ref to the `<input role=\"combobox\">` (the focusable element). */\n controlRef?: React.Ref<HTMLInputElement>;\n /** Ref to the root `<div>`. */\n ref?: React.Ref<HTMLDivElement>;\n}\n\n/** Sizes of TagPicker (Fluent's): medium (the 0.7 look), large and extra-large. */\nexport type TagPickerSize = Extract<Size, 'medium' | 'large' | 'extra-large'>;\n\nconst TAG_PICKER_SIZES: readonly TagPickerSize[] = ['medium', 'large', 'extra-large'];\n\n/** Per-size classes of the control, the tags and the input (medium is the 0.7 look). */\nconst TAG_PICKER_SIZE: Readonly<\n Record<TagPickerSize, { control: string; tag: string; input: string }>\n> = {\n medium: {\n control: 'gap-1 px-2 py-1.5',\n tag: 'px-2 py-0.5 text-body-1',\n input: 'py-0.5 text-body-1',\n },\n large: {\n control: 'gap-1.5 px-2.5 py-2',\n tag: 'px-2 py-1 text-body-1',\n input: 'py-1 text-body-1',\n },\n 'extra-large': {\n control: 'gap-1.5 px-3 py-2.5',\n tag: 'px-2.5 py-1 text-body-2',\n input: 'py-1 text-body-2',\n },\n};\n\nconst EMPTY: readonly string[] = [];\n\n/** Whether two selections hold the same values in the same order. */\nfunction sameTags(a: readonly string[], b: readonly string[]): boolean {\n return a.length === b.length && a.every((value, index) => value === b[index]);\n}\n\ninterface TagRemoveButtonProps {\n /** The accessible name of the button. */\n label: string;\n disabled: boolean;\n onClick: () => void;\n onKeyDown: React.KeyboardEventHandler<HTMLButtonElement>;\n /** The element that receives focus when this button unmounts while focused (the input). */\n getFallback: () => HTMLElement | null;\n}\n\n/**\n * A tag's remove button. When it unmounts while focused (`readOnly` turned on, or a controlled\n * value dropped its tag), focus moves to the input instead of falling to `<body>` (C-DISABLED).\n */\nfunction TagRemoveButton({\n label,\n disabled,\n onClick,\n onKeyDown,\n getFallback,\n}: TagRemoveButtonProps) {\n const ref = React.useRef<HTMLButtonElement | null>(null);\n usePreserveFocus(ref, getFallback);\n return (\n <button\n ref={ref}\n type=\"button\"\n data-wave-tagpicker-remove=\"\"\n disabled={disabled}\n aria-label={label}\n className={cn(\n tagPickerClassNames.removeButton,\n // Padding and background set here (C-NATIVE), not left to an app-wide rule.\n 'ms-0.5 inline-flex rounded-sm bg-transparent p-0 text-muted-foreground not-disabled:not-aria-disabled:hover:text-foreground disabled:cursor-not-allowed',\n focusRing,\n )}\n onClick={onClick}\n onKeyDown={onKeyDown}\n >\n <DismissIcon size={12} />\n </button>\n );\n}\n\n/**\n * A multi-select combobox that shows the selected values as removable tags. Typing filters the\n * options and makes the first match active; ArrowDown/ArrowUp move the highlight\n * (`aria-activedescendant`), Enter adds the highlighted option and keeps the list open (with\n * nothing highlighted — no typed text, or text that matches no option — Enter is left to the\n * surrounding form), Escape closes the list (and then clears the typed text). Backspace in the\n * empty input moves focus to the last tag; Backspace or Delete there removes it. Additions and\n * removals are announced (\"Cherry removed, 2 selected\"), and so is \"No matches\" for text that\n * matches no option.\n *\n * The tags form a list named \"Selected\"; the input is described by a summary of the selected\n * labels (\"Selected: Apple, Banana\"). These built-in texts are English; `labels` localizes them.\n * The `<input>` receives `id`, `aria-label`, `aria-labelledby`, `aria-describedby`,\n * `aria-invalid`, `aria-required`, `aria-errormessage`, `aria-details`, `tabIndex`, `autoFocus`,\n * `onFocus`/`onBlur`/`onKeyDown`/`onKeyUp` and the text input attributes `autoComplete`,\n * `autoCapitalize`, `autoCorrect`, `maxLength`, `inputMode`, `spellCheck` and `enterKeyHint`.\n * `ref`, `className`, `style`, other `aria-*` attributes and the remaining props stay on the root\n * `<div>`. Inside a `Field` the input is labelled and described by it. The tag area shows the\n * error look whenever the input ends up `aria-invalid` (its own `aria-invalid` or a `Field`\n * error). With `name`/`required` the values take part in form submission, validation and reset.\n * Selected values without a matching option are shown with their raw value. The open list renders\n * only in the browser: an open list (`defaultOpen`, `open`) is closed in the server HTML and opens\n * once the picker has hydrated.\n *\n * **Size and appearance**: `size` resolves from its own prop, then the surrounding `Field`'s\n * `size`, then `WaveProvider inputDefaults.size`, else `'medium'` (a Field or provider `small`\n * gives `'medium'` too, since TagPicker has no `small`); `appearance` from its own prop, then\n * `WaveProvider inputDefaults.appearance`, else `'outline'`; both render as `data-size` and\n * `data-appearance` on the root `<div>`. The option list keeps its size.\n */\nexport const TagPicker = (props: TagPickerProps) => {\n const {\n options,\n value: valueProp,\n defaultValue,\n onValueChange,\n onChange,\n open: openProp,\n defaultOpen,\n onOpenChange,\n placeholder = 'Select...',\n disabled = false,\n size: sizeProp,\n appearance: appearanceProp,\n name,\n form,\n required,\n autoComplete = 'off',\n autoCapitalize,\n autoCorrect,\n maxLength,\n readOnly,\n labels,\n inputMode,\n spellCheck,\n enterKeyHint,\n autoFocus,\n tabIndex,\n id,\n 'aria-label': ariaLabel,\n 'aria-labelledby': ariaLabelledBy,\n 'aria-describedby': ariaDescribedBy,\n 'aria-invalid': ariaInvalid,\n 'aria-required': ariaRequired,\n 'aria-errormessage': ariaErrorMessage,\n 'aria-details': ariaDetails,\n onFocus,\n onBlur,\n onKeyDown,\n onKeyUp,\n controlRef,\n className,\n ref,\n ...rest\n } = props;\n\n if (onChange !== undefined) warnDeprecated('TagPicker', 'onChange', 'onValueChange');\n\n const field = useFieldContext();\n const isRequired = required ?? field?.required ?? false;\n // Like a native readonly input, a read-only TagPicker is barred from constraint validation (the\n // user could not fix it); its values are still submitted.\n const validates = isRequired && !readOnly;\n const fieldProps = useFieldControl({\n id,\n 'aria-label': ariaLabel,\n 'aria-labelledby': ariaLabelledBy,\n 'aria-describedby': ariaDescribedBy,\n 'aria-invalid': ariaInvalid,\n // An explicit `required={false}` wins over a required Field, so aria-required matches\n // `isRequired`.\n 'aria-required': ariaRequired ?? required,\n });\n // The error look follows the resolved state: the consumer's `aria-invalid` or the Field's.\n const invalidLook = isInvalidLook(false, fieldProps['aria-invalid']);\n const { size, appearance } = useInputLook(sizeProp, appearanceProp, {\n sizes: TAG_PICKER_SIZES,\n defaultSize: 'medium',\n });\n // A size outside TAG_PICKER_SIZES (from untyped code) is skipped by useInputLook, so `size` is\n // always one of them and the data attribute carries the resolved value.\n const sizeClasses = TAG_PICKER_SIZE[size];\n\n const [selected, setSelected] = useControllable<readonly string[]>(\n valueProp,\n defaultValue ?? EMPTY,\n (next) => {\n // The props are read only; the callbacks receive an array of their own.\n const list = [...next];\n onValueChange?.(list);\n onChange?.(list);\n },\n );\n const interactive = !disabled && !readOnly;\n // A picker that starts disabled or read-only never shows its list, so it starts closed (no close\n // to report later).\n const [openState, setOpen] = useControllable(\n openProp,\n (defaultOpen ?? false) && interactive,\n onOpenChange,\n );\n // The open list lives in a portal, which renders only in the browser: until then (the server\n // HTML, hydration) the picker shows the closed inline list and reports it closed, so\n // aria-controls and aria-activedescendant never name a missing element.\n const isClient = useIsClient();\n const open = openState && interactive && isClient;\n const [query, setQuery] = React.useState('');\n // Locking the control (readOnly/disabled) while typing drops the typed text (adjust-during-render\n // pattern, C-HOOKS).\n const [wasInteractive, setWasInteractive] = React.useState(interactive);\n if (wasInteractive !== interactive) {\n setWasInteractive(interactive);\n if (!interactive) setQuery('');\n }\n // Locking also closes the list itself, not only the derived `open`, so unlocking does not reopen\n // it without a user action. The close is reported through onOpenChange (a consumer callback from\n // an effect, C-HOOKS); a controlled `open` that stays true is still not shown while locked.\n const lockedOpen = !interactive && openState;\n React.useEffect(() => {\n if (lockedOpen) setOpen(false);\n }, [lockedOpen, setOpen]);\n const announce = useAnnounce();\n\n const rootRef = React.useRef<HTMLDivElement | null>(null);\n const inputRef = React.useRef<HTMLInputElement | null>(null);\n const summaryId = useId('tagpicker-summary');\n\n const labelByValue = React.useMemo(() => {\n const map = new Map<string, string>();\n for (const option of options) if (!map.has(option.value)) map.set(option.value, option.label);\n return map;\n }, [options]);\n const labelOf = (value: string) => labelByValue.get(value) ?? value;\n\n const available = React.useMemo<ListboxItem[]>(() => {\n const chosen = new Set(selected);\n return options\n .filter((option) => !chosen.has(option.value))\n .map((option) => ({ value: option.value, label: option.label }));\n }, [options, selected]);\n const filter = React.useMemo(\n () =>\n query\n ? (item: ListboxItem) => item.label.toLowerCase().includes(query.toLowerCase())\n : undefined,\n [query],\n );\n\n // Unknown values render as tags labelled by the raw value (input-pickers#22).\n const unknownValues = selected.filter((value) => !labelByValue.has(value)).join('\\u0000');\n React.useEffect(() => {\n if (!unknownValues) return;\n warnOnce(\n 'TagPicker:unknown-value',\n `TagPicker: the selected value(s) ${unknownValues\n .split('\\u0000')\n .map((v) => `\"${v}\"`)\n .join(', ')} match no option; the raw value is shown as the tag label.`,\n );\n }, [unknownValues]);\n\n const getInput = () => inputRef.current;\n const focusInput = () => inputRef.current?.focus();\n\n const addTag = (value: string) => {\n if (!interactive || selected.includes(value)) return;\n const next = [...selected, value];\n setSelected(next);\n setQuery('');\n announce((labels?.added ?? defaultAddedLabel)(labelOf(value), next.length));\n };\n\n const removeTag = (value: string) => {\n if (!interactive) return;\n const next = selected.filter((v) => v !== value);\n setSelected(next);\n announce((labels?.removed ?? defaultRemovedLabel)(labelOf(value), next.length));\n focusInput();\n };\n\n const listbox = useListbox({\n open,\n onOpenChange: (next) => setOpen(next),\n mode: 'editable',\n multiple: true,\n selectedValues: selected,\n onSelect: (value) => addTag(value),\n items: available,\n filter,\n // Typing a filter makes its first match active (as in Combobox), so Enter adds what the list\n // shows instead of submitting the form. Nothing is active on open or with the text cleared.\n autoHighlight: query ? 'first' : false,\n idPrefix: 'tagpicker-listbox',\n onClearDraft: query ? () => setQuery('') : undefined,\n });\n\n // aria-expanded only while a listbox with options is shown (input-pickers#21).\n const expanded = open && listbox.items.length > 0;\n const noMatchesText = labels?.noMatches ?? 'No matches';\n // The popup shows the options, or \"No matches\" for a query.\n const surfaceOpen = open && (expanded || query !== '');\n const noMatches = surfaceOpen && !expanded;\n const { layerId, setReference, surfaceRef, floatingProps } = useListboxPopup({\n open,\n surfaceOpen,\n onDismiss: () => setOpen(false),\n rootRef,\n anchorRef: inputRef,\n });\n\n const rootMergedRef = useMergedRefs<HTMLDivElement>(rootRef, ref);\n const inputMergedRef = useMergedRefs<HTMLInputElement>(inputRef, controlRef);\n\n useFormReset(\n inputRef,\n () => {\n // Compared by content (C-FORMS): an inline default is a new array on every render, and a\n // reset that keeps the same tags reports nothing.\n const initial = defaultValue ?? EMPTY;\n setSelected((current) => (sameTags(current, initial) ? current : [...initial]));\n setQuery('');\n },\n form,\n );\n\n /** The tags' remove buttons, found from an element inside the component (no ref read). */\n const removeButtons = (from: Element) =>\n Array.from(\n from\n .closest('[data-wave-tagpicker-control]')\n ?.querySelectorAll<HTMLButtonElement>('[data-wave-tagpicker-remove]') ?? [],\n );\n\n const handleInputKeyDown = (event: React.KeyboardEvent<HTMLInputElement>) => {\n // Read-only: no listbox keys at all (they would open the list, add or remove tags).\n if (!interactive) return;\n if (event.key === 'Escape' && open && !surfaceOpen && !event.nativeEvent.isComposing) {\n // Open with nothing shown (every option selected, no query): close, but leave Escape to an\n // enclosing layer (overlays#1) instead of consuming it for an invisible list.\n setOpen(false);\n return;\n }\n if (event.key === 'Backspace' && query === '' && selected.length > 0) {\n // First Backspace moves to the last tag; Backspace/Delete there removes it (input-pickers#14).\n event.preventDefault();\n removeButtons(event.currentTarget).at(-1)?.focus();\n return;\n }\n listbox.onKeyDown(event);\n };\n\n const handleTagKeyDown = (event: React.KeyboardEvent<HTMLButtonElement>, value: string) => {\n if (!interactive) return;\n const buttons = removeButtons(event.currentTarget);\n const index = buttons.indexOf(event.currentTarget);\n if (event.key === 'Backspace' || event.key === 'Delete') {\n event.preventDefault();\n removeTag(value);\n return;\n }\n if (event.key === 'Escape') {\n // Consumed here, so an enclosing layer ignores it (spec §5.4).\n event.preventDefault();\n focusInput();\n return;\n }\n const intent = getArrowIntent(event.key, {\n orientation: 'horizontal',\n dir: getDirection(event.currentTarget),\n });\n if (intent === 'prev' && index > 0) {\n event.preventDefault();\n buttons[index - 1].focus();\n } else if (intent === 'next') {\n event.preventDefault();\n if (index < buttons.length - 1) buttons[index + 1].focus();\n else focusInput();\n }\n };\n\n const listLabelledBy =\n fieldProps['aria-label'] === undefined\n ? (fieldProps['aria-labelledby'] ?? field?.labelId)\n : fieldProps['aria-labelledby'];\n\n return (\n <div\n data-size={size}\n data-appearance={appearance}\n {...rest}\n ref={rootMergedRef}\n className={cn(tagPickerClassNames.root, 'relative', className)}\n >\n <div\n ref={setReference}\n data-wave-tagpicker-control=\"\"\n role=\"group\"\n aria-disabled={disabled || undefined}\n className={cn(\n tagPickerClassNames.control,\n 'flex flex-wrap items-center',\n sizeClasses.control,\n inputAppearanceClasses[appearance],\n inputFocusWithin,\n invalidLook && inputInvalidWithin,\n disabled && 'cursor-not-allowed opacity-50',\n )}\n onClick={(event) => {\n if (disabled || (event.target as Element).closest('button')) return;\n focusInput();\n if (!open && interactive) setOpen(true);\n }}\n >\n {selected.length > 0 && (\n <div\n role=\"list\"\n aria-label={labels?.selected ?? 'Selected'}\n className={cn(tagPickerClassNames.group, 'flex flex-wrap items-center gap-1')}\n >\n {selected.map((value) => {\n const label = labelOf(value);\n return (\n <div\n key={value}\n role=\"listitem\"\n className={cn(\n tagPickerClassNames.tag,\n 'inline-flex items-center gap-1 rounded bg-muted text-foreground',\n sizeClasses.tag,\n )}\n >\n {label}\n {!readOnly && (\n <TagRemoveButton\n label={(labels?.remove ?? defaultRemoveLabel)(label)}\n disabled={disabled}\n onClick={() => removeTag(value)}\n onKeyDown={(event) => handleTagKeyDown(event, value)}\n getFallback={getInput}\n />\n )}\n </div>\n );\n })}\n </div>\n )}\n {selected.length > 0 && (\n // Describes the input. The list itself would read every \"Remove …\" button name too.\n <span id={summaryId} hidden>\n {(labels?.summary ?? defaultSummaryLabel)(selected.map(labelOf))}\n </span>\n )}\n <input\n type=\"text\"\n autoComplete={autoComplete}\n {...fieldProps}\n {...listbox.getComboboxProps()}\n aria-expanded={expanded}\n aria-describedby={joinIds(\n fieldProps['aria-describedby'],\n selected.length > 0 ? summaryId : undefined,\n )}\n aria-errormessage={ariaErrorMessage}\n aria-details={ariaDetails}\n ref={inputMergedRef}\n disabled={disabled}\n readOnly={readOnly}\n placeholder={selected.length === 0 ? placeholder : ''}\n autoCapitalize={autoCapitalize}\n autoCorrect={autoCorrect}\n maxLength={maxLength}\n inputMode={inputMode}\n spellCheck={spellCheck}\n enterKeyHint={enterKeyHint}\n autoFocus={autoFocus}\n tabIndex={tabIndex}\n value={query}\n onChange={(event) => {\n if (!interactive) return;\n setQuery(event.target.value);\n if (!open) setOpen(true);\n }}\n onKeyDown={composeEventHandlers(onKeyDown, handleInputKeyDown)}\n onKeyUp={composeEventHandlers(onKeyUp, listbox.onKeyUp)}\n onFocus={onFocus}\n onBlur={onBlur}\n className={cn(\n tagPickerClassNames.input,\n 'min-w-15 flex-1 bg-transparent text-foreground placeholder:text-muted-foreground focus:outline-hidden',\n sizeClasses.input,\n )}\n />\n </div>\n {/* Mounted before its text: a live region added together with its text is not announced by\n every screen reader. The row in the popup is the visible copy. */}\n <span role=\"status\" className=\"sr-only\">\n {noMatches && noMatchesText}\n </span>\n <ListboxSurface\n listbox={listbox}\n layerId={layerId}\n surfaceRef={surfaceRef}\n floatingProps={floatingProps}\n open={open}\n expanded={expanded}\n showCheck={false}\n listboxClassName={tagPickerClassNames.listbox}\n surfaceClassName={tagPickerClassNames.surface}\n emptyContent={\n query !== '' ? (\n <div\n aria-hidden=\"true\"\n className={cn(\n tagPickerClassNames.emptyMessage,\n 'px-3 py-1.5 text-body-1 text-muted-foreground',\n )}\n >\n {noMatchesText}\n </div>\n ) : undefined\n }\n aria-label={fieldProps['aria-label']}\n aria-labelledby={listLabelledBy}\n >\n {expanded &&\n listbox.items.map((item) => (\n <Option\n key={item.value}\n value={item.value}\n label={item.label}\n className={tagPickerClassNames.option}\n >\n {item.label}\n </Option>\n ))}\n </ListboxSurface>\n <HiddenInput\n name={name}\n form={form}\n disabled={disabled}\n value={selected}\n type=\"text\"\n required={validates}\n onInvalid={focusInput}\n />\n </div>\n );\n};\nTagPicker.displayName = 'TagPicker';\n"],"mappings":"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"}
1
+ {"version":3,"file":"TagPicker.mjs","names":["TagPicker","TagPickerRoot","TagPickerControl","TagPickerGroup","TagPickerInput","TagPickerButton","TagPickerList","TagPickerOption","TagPickerOptionGroup"],"sources":["../../../src/components/input/TagPicker.tsx"],"sourcesContent":["import { TagPickerRoot } from './TagPicker.root';\nimport {\n TagPickerButton,\n TagPickerControl,\n TagPickerGroup,\n TagPickerInput,\n TagPickerList,\n TagPickerOptionGroup,\n} from './TagPicker.parts';\nimport { TagPickerOption } from './TagPicker.option';\n\nexport type { TagPickerLabels, TagPickerProps, TagPickerSize } from './TagPicker.root';\nexport type {\n TagPickerButtonProps,\n TagPickerControlProps,\n TagPickerGroupProps,\n TagPickerInputProps,\n TagPickerListProps,\n} from './TagPicker.parts';\nexport type { TagPickerOptionProps } from './TagPicker.option';\n\n/**\n * A multi-select combobox that shows the selected values as removable tags.\n *\n * - **Two forms.** The array form, `<TagPicker options={…} />`, is the parts with their defaults:\n * `TagPicker.Control` (holding `TagPicker.Group` and `TagPicker.Input`) and `TagPicker.List`.\n * The parts form replaces only the part you need: your own Tags in `TagPicker.Group` (an\n * Avatar in each), rich `TagPicker.Option`s in `TagPicker.List`, or `TagPicker.Button`, a\n * select-only trigger, in place of the Input. Render `TagPicker.Control` and `TagPicker.List`\n * directly inside TagPicker (a Fragment is fine); without a List the default list is added.\n * `options` names the values in both forms (`media` and `secondaryContent` show in the default\n * options, `media` in the default tags too); with a List that has children, its options are\n * the options, and the options of selected values stay rendered (TagPicker hides them).\n * - **Keyboard.** Typing filters the options (`filter`, by default a case-insensitive match on\n * `textValue ?? label`); the option whose label equals the typed text becomes active, else the\n * first match. ArrowDown/ArrowUp move the highlight (`aria-activedescendant`), Enter adds the\n * active option and keeps the list open (with nothing active, Enter is left to the surrounding\n * form), Escape closes the list (and then clears the typed text). Backspace in the empty input,\n * and Left (Right in RTL) with the caret at its start, focus the last tag; Delete, Backspace,\n * Enter or Space on a remove button remove its tag and focus the next tag, else the input; a\n * click on one focuses the input. Additions and removals are announced (\"Cherry removed, 2\n * selected\"), and so is \"No matches\" for text that matches no option.\n * - **Free tagging.** With `allowCreate`, text that matches no option label, no selected value\n * (ignoring case) and no option value ends the list with a create option (`Create \"infra\"`);\n * Enter on it or a click adds the text as a tag after `onOptionCreate(text)` and clears the\n * text.\n * - **End buttons.** A chevron (`expandIcon`, `false` hides it) opens and closes the list without\n * taking focus; `clearable` adds a clear button, a Tab stop after the input, which removes every\n * tag. A press on the field's empty area toggles the list.\n * - **One line.** `overflow=\"count\"` keeps the tags on one line while focus is outside the picker\n * and its list is closed; the tags that do not fit give way to \"+N\".\n * - **Names.** The tags form a list named \"Selected\"; the trigger is described by a summary of the\n * selected labels (\"Selected: Apple, Banana\"). These built-in texts are English; `labels`\n * localizes them.\n * - **Routing.** The trigger (`TagPicker.Input` or `TagPicker.Button`) receives `id`,\n * `aria-label`, `aria-labelledby`, `aria-describedby`, `aria-invalid`, `aria-required`,\n * `aria-errormessage`, `aria-details`, `tabIndex` and `autoFocus`; the Input also\n * `onFocus`/`onBlur`/`onKeyDown`/`onKeyUp`, `controlRef` and the text input attributes\n * `autoComplete`, `autoCapitalize`, `autoCorrect`, `maxLength`, `inputMode`, `spellCheck` and\n * `enterKeyHint` (with the Button, pass handlers and `ref` on it). `ref`, `className`, `style`,\n * other `aria-*` attributes and the remaining props stay on the root `<div>`. Inside a `Field`\n * the trigger is labelled and described by it; the field shows the error look whenever the\n * trigger ends up `aria-invalid`. With `name`/`required` the values take part in form\n * submission, validation and reset. Selected values without a matching option show their raw\n * value.\n * - **Size and appearance**: `size` resolves from its own prop, then the surrounding `Field`'s\n * `size`, then `WaveProvider inputDefaults.size`, else `'medium'` (a Field or provider `small`\n * gives `'medium'` too, since TagPicker has no `small`); `appearance` from its own prop, then\n * `WaveProvider inputDefaults.appearance`, else `'outline'`; both render as `data-size` and\n * `data-appearance` on the root `<div>`, and the parts take neither. The tags are one Tag size\n * smaller. The option list keeps its size.\n * - **Server rendering.** The open list renders only in the browser: an open list (`defaultOpen`,\n * `open`) is closed in the server HTML and opens once the picker has hydrated; the tags of\n * registered options show their labels in the server HTML.\n *\n * Fluent's `selectedOptions` and `onOptionSelect` are `value` and `onValueChange` (plus\n * `onOptionCreate`). `TagPickerOption` is both the type of an `options` entry and the flat name of\n * `TagPicker.Option`.\n *\n * @example\n * <TagPicker aria-label=\"Fruits\" options={[{ value: 'apple', label: 'Apple' }]} clearable />\n *\n * @example\n * <TagPicker aria-label=\"People\" value={people} onValueChange={setPeople}>\n * <TagPicker.Control>\n * <TagPicker.Group>\n * {people.map((id) => (\n * <Tag key={id} value={id} media={<Avatar name={nameOf(id)} />} dismissLabel=\"Remove\">\n * {nameOf(id)}\n * </Tag>\n * ))}\n * </TagPicker.Group>\n * <TagPicker.Input />\n * </TagPicker.Control>\n * <TagPicker.List>\n * <TagPicker.Option value=\"katri\" label=\"Katri Athokas\" secondaryContent=\"Designer\">\n * Katri Athokas\n * </TagPicker.Option>\n * </TagPicker.List>\n * </TagPicker>\n */\nexport const TagPicker = /* @__PURE__ */ Object.assign(TagPickerRoot, {\n Control: TagPickerControl,\n Group: TagPickerGroup,\n Input: TagPickerInput,\n Button: TagPickerButton,\n List: TagPickerList,\n Option: TagPickerOption,\n OptionGroup: TagPickerOptionGroup,\n});\n\nexport {\n TagPickerControl,\n TagPickerGroup,\n TagPickerInput,\n TagPickerButton,\n TagPickerList,\n // The component (`TagPicker.Option`) and the interface of an `options` entry, one name (D64).\n TagPickerOption,\n TagPickerOptionGroup,\n};\n"],"mappings":";;;;;AAqGA,IAAaA,IAA4B,uBAAO,OAAOC,GAAe;CACpE,SAASC;CACT,OAAOC;CACP,OAAOC;CACP,QAAQC;CACR,MAAMC;CACN,QAAQC;CACR,aAAaC;AACf,CAAC"}
@@ -0,0 +1,2 @@
1
+ "use client";const e=require("../../_virtual/_rolldown/runtime.cjs"),t=require("../../lib/cn.cjs"),n=require("../../lib/dev.cjs"),r=require("../../lib/slot.cjs"),i=require("../../lib/classNames.cjs"),a=require("../../hooks/useListbox.cjs"),o=require("./Option.cjs");let s=require("react");s=e.__toESM(s,1);let c=require("react/jsx-runtime");function l(e,t,n,r){return t===void 0?n===void 0?r==null?e:a.collectOptionLabels((0,c.jsx)(o.Option,{value:e,children:r})).get(e):n:t}function u({value:e,label:a,textValue:u,media:d,secondaryContent:f,className:p,children:m,...h}){let g=l(e,a,u,m),_=g===void 0;s.useEffect(()=>{_&&n.warnOnce(`TagPicker.Option:label`,`TagPicker.Option: the option "${e}" has no text label, so its value is used. Pass \`label\` when its children are not plain text: its tag, the filter and the announcements read it.`)},[_,e]);let v=r.slotRendersContent(d),y=r.slotRendersContent(f),b=v||y?(0,c.jsxs)(`span`,{className:`flex items-center gap-2`,children:[v&&r.renderSlot(d,`span`,t.cn(i.tagPickerOptionClassNames.media,`inline-flex shrink-0`),{"aria-hidden":!0}),(0,c.jsxs)(`span`,{className:`flex min-w-0 flex-col`,children:[(0,c.jsx)(`span`,{children:m??e}),y&&r.renderSlot(f,`span`,t.cn(i.tagPickerOptionClassNames.secondaryContent,`text-caption-1 text-muted-foreground`))]})]}):m;return(0,c.jsx)(o.Option,{...h,value:e,label:g??e,textValue:u,className:t.cn(i.tagPickerOptionClassNames.root,p),children:b})}u.displayName=`TagPickerOption`;var d=s.memo(u);d.displayName=`TagPickerOption`;var f=a.markListboxElement(d,`option`);exports.TagPickerOption=f;
2
+ //# sourceMappingURL=TagPicker.option.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"TagPicker.option.cjs","names":["collectOptionLabels","Option","slotRendersContent","renderSlot","cn","tagPickerOptionClassNames","require_runtime","require_cn","require_dev","require_slot","require_classNames","require_useListbox","require_Option","react","react_jsx_runtime","optionLabel","label","textValue","children","value","TagPickerOptionImpl","rest","resolved","missingLabel","hasMedia","media","hasSecondary","secondaryContent","content","className","MemoTagPickerOption","TagPickerOption"],"sources":["../../../src/components/input/TagPicker.option.tsx"],"sourcesContent":["import * as React from 'react';\nimport { cn } from '../../lib/cn';\nimport { tagPickerOptionClassNames } from '../../lib/classNames';\nimport { warnOnce } from '../../lib/dev';\nimport { renderSlot, slotRendersContent } from '../../lib/slot';\nimport type { Slot } from '../../lib/slot';\nimport { collectOptionLabels, markListboxElement } from '../../hooks/useListbox';\nimport { Option } from './Option';\nimport type { OptionProps } from './Option';\n\n/**\n * One entry of `options`, the array form of TagPicker's options. The component of the same name,\n * `TagPickerOption`, is `TagPicker.Option`, the parts form.\n */\nexport interface TagPickerOption {\n /** Unique identifier for the option. */\n value: string;\n /** Display label: the option's text, its tag's text, and the text the filter and the announcements use. */\n label: string;\n /**\n * A visual before the label, in the option and in its tag (an Avatar, an icon). Decorative in\n * both: the label names them. An Avatar takes the tag's size (24, 20 or 16px).\n */\n media?: React.ReactNode;\n /** A second line under the label in the option (a role, an e-mail address); tags do not show it. */\n secondaryContent?: React.ReactNode;\n}\n\n/** Props of TagPicker.Option: an Option with a visual and a second line. */\nexport interface TagPickerOptionProps extends OptionProps {\n /** A visual before the text (an Avatar, an icon). Decorative (`aria-hidden`): the text names the option. */\n media?: Slot<'span'>;\n /** A second, smaller line under the text. Part of the option's name, not of its label. */\n secondaryContent?: Slot<'span'>;\n /**\n * The text of the option's tag, the filter and the announcements. Pass it when the children\n * are not plain text (a development warning otherwise).\n * @default `textValue`, else the text of the children, else `value`\n */\n label?: string;\n}\n\n/**\n * The label of an option: its own `label`, else `textValue`, else the text of its string, number\n * and host-element children (read as the root reads it on the server, so both agree), else\n * `undefined` (children without text).\n */\nfunction optionLabel(\n value: string,\n label: string | undefined,\n textValue: string | undefined,\n children: React.ReactNode,\n): string | undefined {\n if (label !== undefined) return label;\n if (textValue !== undefined) return textValue;\n if (children === null || children === undefined) return value;\n return collectOptionLabels(<Option value={value}>{children}</Option>).get(value);\n}\n\nfunction TagPickerOptionImpl({\n value,\n label,\n textValue,\n media,\n secondaryContent,\n className,\n children,\n ...rest\n}: TagPickerOptionProps) {\n const resolved = optionLabel(value, label, textValue, children);\n const missingLabel = resolved === undefined;\n React.useEffect(() => {\n if (missingLabel) {\n warnOnce(\n 'TagPicker.Option:label',\n `TagPicker.Option: the option \"${value}\" has no text label, so its value is used. Pass \\`label\\` when its children are not plain text: its tag, the filter and the announcements read it.`,\n );\n }\n }, [missingLabel, value]);\n\n const hasMedia = slotRendersContent(media);\n const hasSecondary = slotRendersContent(secondaryContent);\n const content =\n hasMedia || hasSecondary ? (\n // A layout wrapper (no class): the visual, then the text over its second line.\n <span className=\"flex items-center gap-2\">\n {hasMedia &&\n renderSlot(media, 'span', cn(tagPickerOptionClassNames.media, 'inline-flex shrink-0'), {\n 'aria-hidden': true,\n })}\n <span className=\"flex min-w-0 flex-col\">\n <span>{children ?? value}</span>\n {hasSecondary &&\n renderSlot(\n secondaryContent,\n 'span',\n cn(\n tagPickerOptionClassNames.secondaryContent,\n 'text-caption-1 text-muted-foreground',\n ),\n )}\n </span>\n </span>\n ) : (\n children\n );\n\n return (\n <Option\n {...rest}\n value={value}\n label={resolved ?? value}\n textValue={textValue}\n className={cn(tagPickerOptionClassNames.root, className)}\n >\n {content}\n </Option>\n );\n}\nTagPickerOptionImpl.displayName = 'TagPickerOption';\n\nconst MemoTagPickerOption = /* @__PURE__ */ React.memo(TagPickerOptionImpl);\nMemoTagPickerOption.displayName = 'TagPickerOption';\n\n/**\n * An option of TagPicker's list (`TagPicker.Option`): an `Option` with a decorative visual before\n * its text (`media`, an Avatar or an icon, `aria-hidden`) and a second, smaller line under it\n * (`secondaryContent`, part of the option's accessible name). Its label, the text of its tag, the\n * filter and the announcements, is `label`, else `textValue`, else the text of its children, so\n * the visual and the second line never enter it; pass `label` when the children are not plain\n * text (a development warning otherwise, and the value is used). It works in any listbox:\n * TagPicker's list, a Combobox, a Listbox or a custom picker. Without `media` and\n * `secondaryContent` it renders `Option`'s DOM. The interface of the same name, `TagPickerOption`,\n * is an entry of TagPicker's `options` array.\n *\n * @example\n * <TagPicker.Option\n * value=\"katri\"\n * label=\"Katri Athokas\"\n * media={<Avatar name=\"Katri Athokas\" />}\n * secondaryContent=\"Designer\"\n * >\n * Katri Athokas\n * </TagPicker.Option>\n */\nexport const TagPickerOption: React.NamedExoticComponent<TagPickerOptionProps> =\n /* @__PURE__ */ markListboxElement(MemoTagPickerOption, 'option');\n"],"mappings":"qVA+CA,SAASe,EACP,EACA,EACA,EACA,EACoB,CAIpB,OAHIC,IAAU,IAAA,GACVC,IAAc,IAAA,GACdC,GAAa,KAAuCC,EACjDnB,EAAAA,qBAAoB,EAAAc,EAAA,IAAA,CAACb,EAAAA,OAAD,CAAe,QAAQ,UAAiB,CAAA,CAAC,CAAC,CAAC,IAAIkB,CAAK,EAF3CF,EADJD,CAIlC,CAEA,SAASI,EAAoB,CAC3B,QACA,QACA,YACA,QACA,mBACA,YACA,WACA,GAAGC,GACoB,CACvB,IAAMC,EAAWP,EAAYI,EAAOH,EAAOC,EAAWC,CAAQ,EACxDK,EAAeD,IAAa,IAAA,GAClCT,EAAM,cAAgB,CAChBU,GACFf,EAAA,SACE,yBACA,iCAAiCW,EAAM,mJACzC,CAEJ,EAAG,CAACI,EAAcJ,CAAK,CAAC,EAExB,IAAMK,EAAWtB,EAAAA,mBAAmBuB,CAAK,EACnCC,EAAexB,EAAAA,mBAAmByB,CAAgB,EAClDC,EACJJ,GAAYE,GAEV,EAAAZ,EAAA,KAAA,CAAC,OAAD,CAAM,UAAU,0BAAhB,SAAA,CACGU,GACCrB,EAAAA,WAAWsB,EAAO,OAAQrB,EAAAA,GAAGC,EAAAA,0BAA0B,MAAO,sBAAsB,EAAG,CACrF,cAAe,EACjB,CAAC,GACH,EAAAS,EAAA,KAAA,CAAC,OAAD,CAAM,UAAU,wBAAhB,SAAA,EACE,EAAAA,EAAA,IAAA,CAAC,OAAD,CAAA,SAAOI,GAAYC,CAAY,CAAA,EAC9BO,GACCvB,EAAAA,WACEwB,EACA,OACAvB,EAAAA,GACEC,EAAAA,0BAA0B,iBAC1B,sCACF,CACF,CACE,CACF,CAAA,CAAA,CAEN,CAAA,EAAAa,EAGJ,OACE,EAAAJ,EAAA,IAAA,CAACb,EAAAA,OAAD,CACE,GAAIoB,EACG,QACP,MAAOC,GAAYH,EACR,YACX,UAAWf,EAAAA,GAAGC,EAAAA,0BAA0B,KAAMwB,CAAS,EAEtD,SAAAD,CACK,CAAA,CAEZ,CACA,EAAoB,YAAc,kBAElC,IAAME,EAAsCjB,EAAM,KAAKO,CAAmB,EAC1E,EAAoB,YAAc,kBAuBlC,IAAaW,EACKpB,EAAA,mBAAmBmB,EAAqB,QAAQ"}
@@ -0,0 +1,45 @@
1
+ "use client";
2
+ import { cn as e } from "../../lib/cn.mjs";
3
+ import { warnOnce as t } from "../../lib/dev.mjs";
4
+ import { renderSlot as n, slotRendersContent as r } from "../../lib/slot.mjs";
5
+ import { tagPickerOptionClassNames as i } from "../../lib/classNames.mjs";
6
+ import { collectOptionLabels as a, markListboxElement as o } from "../../hooks/useListbox.mjs";
7
+ import { Option as s } from "./Option.mjs";
8
+ import * as c from "react";
9
+ import { jsx as l, jsxs as u } from "react/jsx-runtime";
10
+ //#region src/components/input/TagPicker.option.tsx
11
+ function d(e, t, n, r) {
12
+ return t === void 0 ? n === void 0 ? r == null ? e : a(/* @__PURE__ */ l(s, {
13
+ value: e,
14
+ children: r
15
+ })).get(e) : n : t;
16
+ }
17
+ function f({ value: a, label: o, textValue: f, media: p, secondaryContent: m, className: h, children: g, ..._ }) {
18
+ let v = d(a, o, f, g), y = v === void 0;
19
+ c.useEffect(() => {
20
+ y && t("TagPicker.Option:label", `TagPicker.Option: the option "${a}" has no text label, so its value is used. Pass \`label\` when its children are not plain text: its tag, the filter and the announcements read it.`);
21
+ }, [y, a]);
22
+ let b = r(p), x = r(m), S = b || x ? /* @__PURE__ */ u("span", {
23
+ className: "flex items-center gap-2",
24
+ children: [b && n(p, "span", e(i.media, "inline-flex shrink-0"), { "aria-hidden": !0 }), /* @__PURE__ */ u("span", {
25
+ className: "flex min-w-0 flex-col",
26
+ children: [/* @__PURE__ */ l("span", { children: g ?? a }), x && n(m, "span", e(i.secondaryContent, "text-caption-1 text-muted-foreground"))]
27
+ })]
28
+ }) : g;
29
+ return /* @__PURE__ */ l(s, {
30
+ ..._,
31
+ value: a,
32
+ label: v ?? a,
33
+ textValue: f,
34
+ className: e(i.root, h),
35
+ children: S
36
+ });
37
+ }
38
+ f.displayName = "TagPickerOption";
39
+ var p = /* @__PURE__ */ c.memo(f);
40
+ p.displayName = "TagPickerOption";
41
+ var m = /* @__PURE__ */ o(p, "option");
42
+ //#endregion
43
+ export { m as TagPickerOption };
44
+
45
+ //# sourceMappingURL=TagPicker.option.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"TagPicker.option.mjs","names":["React","optionLabel","label","textValue","children","value","collectOptionLabels","Option","TagPickerOptionImpl","rest","resolved","missingLabel","warnOnce","hasMedia","slotRendersContent","media","hasSecondary","secondaryContent","content","renderSlot","cn","tagPickerOptionClassNames","className","MemoTagPickerOption","TagPickerOption"],"sources":["../../../src/components/input/TagPicker.option.tsx"],"sourcesContent":["import * as React from 'react';\nimport { cn } from '../../lib/cn';\nimport { tagPickerOptionClassNames } from '../../lib/classNames';\nimport { warnOnce } from '../../lib/dev';\nimport { renderSlot, slotRendersContent } from '../../lib/slot';\nimport type { Slot } from '../../lib/slot';\nimport { collectOptionLabels, markListboxElement } from '../../hooks/useListbox';\nimport { Option } from './Option';\nimport type { OptionProps } from './Option';\n\n/**\n * One entry of `options`, the array form of TagPicker's options. The component of the same name,\n * `TagPickerOption`, is `TagPicker.Option`, the parts form.\n */\nexport interface TagPickerOption {\n /** Unique identifier for the option. */\n value: string;\n /** Display label: the option's text, its tag's text, and the text the filter and the announcements use. */\n label: string;\n /**\n * A visual before the label, in the option and in its tag (an Avatar, an icon). Decorative in\n * both: the label names them. An Avatar takes the tag's size (24, 20 or 16px).\n */\n media?: React.ReactNode;\n /** A second line under the label in the option (a role, an e-mail address); tags do not show it. */\n secondaryContent?: React.ReactNode;\n}\n\n/** Props of TagPicker.Option: an Option with a visual and a second line. */\nexport interface TagPickerOptionProps extends OptionProps {\n /** A visual before the text (an Avatar, an icon). Decorative (`aria-hidden`): the text names the option. */\n media?: Slot<'span'>;\n /** A second, smaller line under the text. Part of the option's name, not of its label. */\n secondaryContent?: Slot<'span'>;\n /**\n * The text of the option's tag, the filter and the announcements. Pass it when the children\n * are not plain text (a development warning otherwise).\n * @default `textValue`, else the text of the children, else `value`\n */\n label?: string;\n}\n\n/**\n * The label of an option: its own `label`, else `textValue`, else the text of its string, number\n * and host-element children (read as the root reads it on the server, so both agree), else\n * `undefined` (children without text).\n */\nfunction optionLabel(\n value: string,\n label: string | undefined,\n textValue: string | undefined,\n children: React.ReactNode,\n): string | undefined {\n if (label !== undefined) return label;\n if (textValue !== undefined) return textValue;\n if (children === null || children === undefined) return value;\n return collectOptionLabels(<Option value={value}>{children}</Option>).get(value);\n}\n\nfunction TagPickerOptionImpl({\n value,\n label,\n textValue,\n media,\n secondaryContent,\n className,\n children,\n ...rest\n}: TagPickerOptionProps) {\n const resolved = optionLabel(value, label, textValue, children);\n const missingLabel = resolved === undefined;\n React.useEffect(() => {\n if (missingLabel) {\n warnOnce(\n 'TagPicker.Option:label',\n `TagPicker.Option: the option \"${value}\" has no text label, so its value is used. Pass \\`label\\` when its children are not plain text: its tag, the filter and the announcements read it.`,\n );\n }\n }, [missingLabel, value]);\n\n const hasMedia = slotRendersContent(media);\n const hasSecondary = slotRendersContent(secondaryContent);\n const content =\n hasMedia || hasSecondary ? (\n // A layout wrapper (no class): the visual, then the text over its second line.\n <span className=\"flex items-center gap-2\">\n {hasMedia &&\n renderSlot(media, 'span', cn(tagPickerOptionClassNames.media, 'inline-flex shrink-0'), {\n 'aria-hidden': true,\n })}\n <span className=\"flex min-w-0 flex-col\">\n <span>{children ?? value}</span>\n {hasSecondary &&\n renderSlot(\n secondaryContent,\n 'span',\n cn(\n tagPickerOptionClassNames.secondaryContent,\n 'text-caption-1 text-muted-foreground',\n ),\n )}\n </span>\n </span>\n ) : (\n children\n );\n\n return (\n <Option\n {...rest}\n value={value}\n label={resolved ?? value}\n textValue={textValue}\n className={cn(tagPickerOptionClassNames.root, className)}\n >\n {content}\n </Option>\n );\n}\nTagPickerOptionImpl.displayName = 'TagPickerOption';\n\nconst MemoTagPickerOption = /* @__PURE__ */ React.memo(TagPickerOptionImpl);\nMemoTagPickerOption.displayName = 'TagPickerOption';\n\n/**\n * An option of TagPicker's list (`TagPicker.Option`): an `Option` with a decorative visual before\n * its text (`media`, an Avatar or an icon, `aria-hidden`) and a second, smaller line under it\n * (`secondaryContent`, part of the option's accessible name). Its label, the text of its tag, the\n * filter and the announcements, is `label`, else `textValue`, else the text of its children, so\n * the visual and the second line never enter it; pass `label` when the children are not plain\n * text (a development warning otherwise, and the value is used). It works in any listbox:\n * TagPicker's list, a Combobox, a Listbox or a custom picker. Without `media` and\n * `secondaryContent` it renders `Option`'s DOM. The interface of the same name, `TagPickerOption`,\n * is an entry of TagPicker's `options` array.\n *\n * @example\n * <TagPicker.Option\n * value=\"katri\"\n * label=\"Katri Athokas\"\n * media={<Avatar name=\"Katri Athokas\" />}\n * secondaryContent=\"Designer\"\n * >\n * Katri Athokas\n * </TagPicker.Option>\n */\nexport const TagPickerOption: React.NamedExoticComponent<TagPickerOptionProps> =\n /* @__PURE__ */ markListboxElement(MemoTagPickerOption, 'option');\n"],"mappings":";;;;;;;;;;AA+CA,SAASC,EACP,GACA,GACA,GACA,GACoB;CAIpB,OAHIC,MAAU,KAAA,IACVC,MAAc,KAAA,IACdC,KAAa,OAAuCC,IACjDC,EAAoB,kBAACC,GAAD;EAAe;EAAQ;CAAiB,CAAA,CAAC,CAAC,CAAC,IAAIF,CAAK,IAF3CF,IADJD;AAIlC;AAEA,SAASM,EAAoB,EAC3B,UACA,UACA,cACA,UACA,qBACA,cACA,aACA,GAAGC,KACoB;CACvB,IAAMC,IAAWT,EAAYI,GAAOH,GAAOC,GAAWC,CAAQ,GACxDO,IAAeD,MAAa,KAAA;CAClC,EAAM,gBAAgB;EACpB,AAAIC,KACFC,EACE,0BACA,iCAAiCP,EAAM,mJACzC;CAEJ,GAAG,CAACM,GAAcN,CAAK,CAAC;CAExB,IAAMQ,IAAWC,EAAmBC,CAAK,GACnCC,IAAeF,EAAmBG,CAAgB,GAClDC,IACJL,KAAYG,IAEV,kBAAC,QAAD;EAAM,WAAU;EAAhB,UAAA,CACGH,KACCM,EAAWJ,GAAO,QAAQK,EAAGC,EAA0B,OAAO,sBAAsB,GAAG,EACrF,eAAe,GACjB,CAAC,GACH,kBAAC,QAAD;GAAM,WAAU;GAAhB,UAAA,CACE,kBAAC,QAAD,EAAA,UAAOjB,KAAYC,EAAY,CAAA,GAC9BW,KACCG,EACEF,GACA,QACAG,EACEC,EAA0B,kBAC1B,sCACF,CACF,CACE;EACF,CAAA,CAAA;CAEN,CAAA,IAAAjB;CAGJ,OACE,kBAACG,GAAD;EACE,GAAIE;EACG;EACP,OAAOC,KAAYL;EACR;EACX,WAAWe,EAAGC,EAA0B,MAAMC,CAAS;EAEtD,UAAAJ;CACK,CAAA;AAEZ;AACA,EAAoB,cAAc;AAElC,IAAMK,IAAsC,kBAAM,KAAKf,CAAmB;AAC1E,EAAoB,cAAc;AAuBlC,IAAagB,IACK,kBAAmBD,GAAqB,QAAQ"}
@@ -0,0 +1,2 @@
1
+ "use client";const e=require("../../_virtual/_rolldown/runtime.cjs"),t=require("../data-display/TagGroup.context.cjs");let n=require("react");n=e.__toESM(n,1);var r=new Set;function i(){return r}function a(e,t){if(e===t)return!0;if(e.size!==t.size)return!1;for(let n of e)if(!t.has(n))return!1;return!0}function o(e){let t=parseFloat(e);return Number.isFinite(t)?t:0}var s=class{snapshot=r;listeners=new Set;widths=new Map;revealPending=!1;chipWidth=0;chipPassAllowed=!1;subscribe=e=>(this.listeners.add(e),()=>{this.listeners.delete(e)});getSnapshot=()=>this.snapshot;reset(){this.chipWidth=0,this.revealPending=!1,this.chipPassAllowed=!1,this.publish(r)}measureFromTrigger(e,t,n){this.chipPassAllowed=!0,this.measure(e,t,n)}measureAfterChip(e,t,n){if(this.revealPending){this.revealPending=!1,this.measureFromTrigger(e,t,n);return}this.chipPassAllowed&&(n.chip?.offsetWidth??0)!==this.chipWidth&&(this.chipPassAllowed=!1,this.measure(e,t,n))}measure(e,n,i){let{control:a,group:s,chip:c,trigger:l}=i,u=c?.offsetWidth??0;if(this.chipWidth=u,!a||!s||a.clientWidth===0){this.publish(r);return}let d=a.ownerDocument.defaultView,f=d?.getComputedStyle(a),p=d?.getComputedStyle(s),m=o(f?.columnGap??``),h=o(p?.columnGap??``),g=l?o(d?.getComputedStyle(l).minWidth??``):0,_=c?2:1,v=a.clientWidth-o(f?.paddingLeft??``)-o(f?.paddingRight??``)-g-u-m*_,y=Array.from(s.querySelectorAll(t.TAG_ITEM_SELECTOR)).filter(e=>e.parentElement?.closest(t.TAG_GROUP_SELECTOR)===s),b=!1;if(e.forEach((e,t)=>{let r=y[t],i=n[t]??e;r&&!r.hidden?this.widths.set(e,{width:r.offsetWidth,label:i}):this.widths.get(e)?.label!==i&&(b=!0)}),b){this.revealPending=!0,this.publish(r);return}let x=0,S=0;for(let t of e){let e=this.widths.get(t)?.width??0,n=x+(S>0?h:0)+e;if(S>0&&n>v)break;x=n,S++}this.publish(S>=e.length?r:new Set(e.slice(S)))}publish(e){if(!a(e,this.snapshot)){this.snapshot=e;for(let e of Array.from(this.listeners))e()}}};function c(e){let{active:t,values:r,labels:a,control:o,group:c,chip:l,trigger:u}=e,[d]=n.useState(()=>new s),f=n.useSyncExternalStore(d.subscribe,d.getSnapshot,i),p=`${r.join(`\0`)}\u0001${a.join(`\0`)}`,m=n.useRef(r),h=n.useRef(a);n.useLayoutEffect(()=>{m.current=r,h.current=a}),n.useLayoutEffect(()=>{if(!t){d.reset();return}let e=()=>({control:o.current,group:c.current,chip:l.current,trigger:u.current}),n=()=>d.measureFromTrigger(m.current,h.current,e());n();let r=o.current;if(!r||typeof ResizeObserver>`u`)return;let i=new ResizeObserver(()=>{n()});return i.observe(r),()=>i.disconnect()},[t,p,d,o,c,l,u]);let g=f.size;return n.useLayoutEffect(()=>{t&&d.measureAfterChip(m.current,h.current,{control:o.current,group:c.current,chip:l.current,trigger:u.current})},[t,g,d,o,c,l,u]),t?f:void 0}var l=class{focused=!1;listeners=new Set;subscribe=e=>(this.listeners.add(e),()=>{this.listeners.delete(e)});set(e){if(e!==this.focused){this.focused=e;for(let e of Array.from(this.listeners))e()}}};function u(e){let[t]=n.useState(()=>new l);return{focusWithin:n.useSyncExternalStore(t.subscribe,()=>e&&t.focused,()=>!1),onFocus:e=>{e.currentTarget.contains(e.target)&&t.set(!0)},onBlur:e=>{let n=e.relatedTarget;(!(n instanceof Node)||!e.currentTarget.contains(n))&&t.set(!1)}}}exports.useFocusWithin=u,exports.useTagOverflow=c;
2
+ //# sourceMappingURL=TagPicker.overflow.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"TagPicker.overflow.cjs","names":["TAG_ITEM_SELECTOR","TAG_GROUP_SELECTOR","React","require_runtime","require_TagGroup_context","react","EMPTY","getEmpty","sameSet","a","b","value","px","number","TagOverflowStore","listener","values","labels","elements","chipWidth","chip","control","group","view","controlStyle","groupStyle","controlGap","tagGap","triggerMin","trigger","outerGaps","available","tags","element","stale","tag","index","label","used","visible","width","next","useTagOverflow","options","store","hidden","valuesKey","valuesRef","labelsRef","active","measure","observed","observer","hiddenCount","FocusWithinStore","focused","useFocusWithin","event"],"sources":["../../../src/components/input/TagPicker.overflow.ts"],"sourcesContent":["import * as React from 'react';\nimport { TAG_GROUP_SELECTOR, TAG_ITEM_SELECTOR } from '../data-display/TagGroup.context';\n\n/*\n * The measuring of TagPicker's single-line layout (`overflow=\"count\"`, D62): which tags give way\n * to the \"+N\" count. A store read through `useSyncExternalStore` (Overflow's pattern), connected\n * in a layout effect, so the first result renders before paint, with one ResizeObserver on the\n * control. Module-only.\n */\n\nconst EMPTY: ReadonlySet<string> = new Set<string>();\n\nfunction getEmpty(): ReadonlySet<string> {\n return EMPTY;\n}\n\nfunction sameSet(a: ReadonlySet<string>, b: ReadonlySet<string>): boolean {\n if (a === b) return true;\n if (a.size !== b.size) return false;\n for (const value of a) if (!b.has(value)) return false;\n return true;\n}\n\n/** A number of pixels from a computed length (`''` and `normal` are 0). */\nfunction px(value: string): number {\n const number = parseFloat(value);\n return Number.isFinite(number) ? number : 0;\n}\n\n/** The elements the measuring reads. */\nexport interface TagOverflowElements {\n control: HTMLElement | null;\n group: HTMLElement | null;\n chip: HTMLElement | null;\n trigger: HTMLElement | null;\n}\n\n/** The hidden tag values of the single-line layout, and the tag widths seen while visible. */\nclass TagOverflowStore {\n private snapshot: ReadonlySet<string> = EMPTY;\n private readonly listeners = new Set<() => void>();\n /** Widths of the tags (by value), read while each was visible, with the label they had then. */\n private readonly widths = new Map<string, { width: number; label: string }>();\n /** A hidden tag changed its label: every tag shows, and the next pass measures again. */\n private revealPending = false;\n /** The chip's width at the last measure (0 while it does not render). */\n private chipWidth = 0;\n /** Whether the chip's next width change may measure once more (reset by every trigger). */\n private chipPassAllowed = false;\n\n subscribe = (listener: () => void): (() => void) => {\n this.listeners.add(listener);\n return () => {\n this.listeners.delete(listener);\n };\n };\n\n getSnapshot = (): ReadonlySet<string> => this.snapshot;\n\n /** Shows every tag again (the layout wraps, or nothing is measured). */\n reset(): void {\n this.chipWidth = 0;\n this.revealPending = false;\n this.chipPassAllowed = false;\n this.publish(EMPTY);\n }\n\n /** A measure from a trigger (connect, resize, new values): allows one chip pass after it. */\n measureFromTrigger(\n values: readonly string[],\n labels: readonly string[],\n elements: TagOverflowElements,\n ): void {\n this.chipPassAllowed = true;\n this.measure(values, labels, elements);\n }\n\n /**\n * After the chip rendered, unmounted or changed: measures once more when its width changed,\n * at most once per trigger, so a count that changes the chip's width cannot loop.\n */\n measureAfterChip(\n values: readonly string[],\n labels: readonly string[],\n elements: TagOverflowElements,\n ): void {\n if (this.revealPending) {\n // The tags a label change revealed have rendered: measure them (a trigger of its own).\n this.revealPending = false;\n this.measureFromTrigger(values, labels, elements);\n return;\n }\n if (!this.chipPassAllowed) return;\n if ((elements.chip?.offsetWidth ?? 0) === this.chipWidth) return;\n this.chipPassAllowed = false;\n this.measure(values, labels, elements);\n }\n\n /**\n * Measures and publishes the hidden values: the largest prefix of `values` whose tags (and the\n * gaps between them) fit the control's content width less the trigger's minimum width, the\n * chip's width and the gaps around them; at least one tag stays.\n */\n private measure(\n values: readonly string[],\n labels: readonly string[],\n elements: TagOverflowElements,\n ): void {\n const { control, group, chip, trigger } = elements;\n const chipWidth = chip?.offsetWidth ?? 0;\n this.chipWidth = chipWidth;\n if (!control || !group || control.clientWidth === 0) {\n // No layout (the server, a hidden picker): every tag shows.\n this.publish(EMPTY);\n return;\n }\n const view = control.ownerDocument.defaultView;\n const controlStyle = view?.getComputedStyle(control);\n const groupStyle = view?.getComputedStyle(group);\n const controlGap = px(controlStyle?.columnGap ?? '');\n const tagGap = px(groupStyle?.columnGap ?? '');\n const triggerMin = trigger ? px(view?.getComputedStyle(trigger).minWidth ?? '') : 0;\n // The group and the trigger, plus the chip while it renders: one gap between each.\n const outerGaps = chip ? 2 : 1;\n const available =\n control.clientWidth -\n px(controlStyle?.paddingLeft ?? '') -\n px(controlStyle?.paddingRight ?? '') -\n triggerMin -\n chipWidth -\n controlGap * outerGaps;\n\n // The tags of this group in DOM order, which is the order of the selected values.\n const tags = Array.from(group.querySelectorAll<HTMLElement>(TAG_ITEM_SELECTOR)).filter(\n (element) => element.parentElement?.closest(TAG_GROUP_SELECTOR) === group,\n );\n let stale = false;\n values.forEach((value, index) => {\n const tag = tags[index];\n const label = labels[index] ?? value;\n if (tag && !tag.hidden) this.widths.set(value, { width: tag.offsetWidth, label });\n else if (this.widths.get(value)?.label !== label) stale = true;\n });\n if (stale) {\n // A hidden tag's width is unknown for its current label: show every tag, and measure once\n // they have rendered.\n this.revealPending = true;\n this.publish(EMPTY);\n return;\n }\n\n let used = 0;\n let visible = 0;\n for (const value of values) {\n const width = this.widths.get(value)?.width ?? 0;\n const next = used + (visible > 0 ? tagGap : 0) + width;\n if (visible > 0 && next > available) break;\n used = next;\n visible++;\n }\n this.publish(visible >= values.length ? EMPTY : new Set(values.slice(visible)));\n }\n\n private publish(next: ReadonlySet<string>): void {\n if (sameSet(next, this.snapshot)) return;\n this.snapshot = next;\n for (const listener of Array.from(this.listeners)) listener();\n }\n}\n\n/** Options of {@link useTagOverflow}. */\nexport interface UseTagOverflowOptions {\n /** Whether the tags stay on one line (count mode while collapsed); otherwise nothing is measured. */\n active: boolean;\n /** The selected values, in tag order. */\n values: readonly string[];\n /** Their labels, in the same order: a changed label re-measures its tag. */\n labels: readonly string[];\n control: React.RefObject<HTMLElement | null>;\n group: React.RefObject<HTMLElement | null>;\n chip: React.RefObject<HTMLElement | null>;\n trigger: React.RefObject<HTMLElement | null>;\n}\n\n/**\n * The values of the tags that give way to the \"+N\" count while TagPicker's single-line layout is\n * active (`undefined` otherwise). It measures when it becomes active, on a resize of the control\n * (one ResizeObserver, created only while active), when the selected values or their labels\n * change (a hidden tag whose label changed is shown first, then measured), and once more when\n * the chip's width changes; it publishes only when the hidden set changes. The server render\n * and the first client render hide nothing.\n */\nexport function useTagOverflow(options: UseTagOverflowOptions): ReadonlySet<string> | undefined {\n const { active, values, labels, control, group, chip, trigger } = options;\n const [store] = React.useState(() => new TagOverflowStore());\n const hidden = React.useSyncExternalStore(store.subscribe, store.getSnapshot, getEmpty);\n // The values and their labels, compared by content: a new tag or a new label measures again.\n const valuesKey = `${values.join('\\u0000')}\\u0001${labels.join('\\u0000')}`;\n const valuesRef = React.useRef(values);\n const labelsRef = React.useRef(labels);\n\n React.useLayoutEffect(() => {\n valuesRef.current = values;\n labelsRef.current = labels;\n });\n\n React.useLayoutEffect(() => {\n if (!active) {\n store.reset();\n return undefined;\n }\n const elements = (): TagOverflowElements => ({\n control: control.current,\n group: group.current,\n chip: chip.current,\n trigger: trigger.current,\n });\n const measure = () =>\n store.measureFromTrigger(valuesRef.current, labelsRef.current, elements());\n measure();\n const observed = control.current;\n if (!observed || typeof ResizeObserver === 'undefined') return undefined;\n const observer = new ResizeObserver(() => {\n measure();\n });\n observer.observe(observed);\n return () => observer.disconnect();\n // `valuesKey` re-measures when the selected values change (compared by content).\n }, [active, valuesKey, store, control, group, chip, trigger]);\n\n // The chip mounts, unmounts or changes its text after a measure: when its width changed, measure\n // once more (a second change in that pass is left for the next trigger, so nothing loops).\n const hiddenCount = hidden.size;\n React.useLayoutEffect(() => {\n if (!active) return;\n store.measureAfterChip(valuesRef.current, labelsRef.current, {\n control: control.current,\n group: group.current,\n chip: chip.current,\n trigger: trigger.current,\n });\n }, [active, hiddenCount, store, control, group, chip, trigger]);\n\n return active ? hidden : undefined;\n}\n\n/** Whether focus is inside the picker's root, kept outside React state (see {@link useFocusWithin}). */\nclass FocusWithinStore {\n focused = false;\n private readonly listeners = new Set<() => void>();\n\n subscribe = (listener: () => void): (() => void) => {\n this.listeners.add(listener);\n return () => {\n this.listeners.delete(listener);\n };\n };\n\n set(focused: boolean): void {\n if (focused === this.focused) return;\n this.focused = focused;\n for (const listener of Array.from(this.listeners)) listener();\n }\n}\n\n/** What {@link useFocusWithin} returns. */\nexport interface FocusWithin {\n /** Whether focus is inside the root; always `false` while `active` is. */\n focusWithin: boolean;\n /** For the root's `onFocus`: focus inside it in the DOM. */\n onFocus: (event: React.FocusEvent<HTMLElement>) => void;\n /** For the root's `onBlur`: focus left it (or the window). */\n onBlur: (event: React.FocusEvent<HTMLElement>) => void;\n}\n\n/**\n * Tracks focus inside the root in every mode, but renders only for it while `active` (count\n * mode): a wrapping picker does not re-render on focus moves, and a switch to count mode reads\n * where focus is at that moment, so the state never goes stale. The server render and hydration\n * read `false`.\n */\nexport function useFocusWithin(active: boolean): FocusWithin {\n const [store] = React.useState(() => new FocusWithinStore());\n const focusWithin = React.useSyncExternalStore(\n store.subscribe,\n () => active && store.focused,\n () => false,\n );\n const onFocus = (event: React.FocusEvent<HTMLElement>) => {\n if (event.currentTarget.contains(event.target)) store.set(true);\n };\n const onBlur = (event: React.FocusEvent<HTMLElement>) => {\n const next = event.relatedTarget;\n if (!(next instanceof Node) || !event.currentTarget.contains(next)) store.set(false);\n };\n return { focusWithin, onFocus, onBlur };\n}\n"],"mappings":"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"}