@keboola/design 11.9.0 → 13.0.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 (367) hide show
  1. package/AGENTS.md +45 -14
  2. package/CHANGELOG.md +246 -0
  3. package/COMPONENT-API.md +352 -294
  4. package/README.md +11 -1
  5. package/dist/{AIKit-ELIsegJz.js → AIKit-DcOlHIBr.js} +420 -247
  6. package/dist/AIKit-DcOlHIBr.js.map +1 -0
  7. package/dist/{Alert-BsIM_oFK.js → Alert-UI-CIs-1.js} +16 -16
  8. package/dist/Alert-UI-CIs-1.js.map +1 -0
  9. package/dist/{AlertModal-BGJdaRRj.js → AlertModal-mbSnuuka.js} +8 -8
  10. package/dist/AlertModal-mbSnuuka.js.map +1 -0
  11. package/dist/{AppShell-DRgJxpXW.js → AppShell-WSQabK8r.js} +15 -52
  12. package/dist/AppShell-WSQabK8r.js.map +1 -0
  13. package/dist/{AutoSaveTextarea-BeuWuszr.js → AutoSaveTextarea-C5iIr2Ts.js} +2 -2
  14. package/dist/{AutoSaveTextarea-BeuWuszr.js.map → AutoSaveTextarea-C5iIr2Ts.js.map} +1 -1
  15. package/dist/{Badge-o5jURtpB.js → Badge-DIjhwPvh.js} +4 -9
  16. package/dist/Badge-DIjhwPvh.js.map +1 -0
  17. package/dist/{BrandEditor-BsTBM3s6.js → BrandEditor-Bkd6dhb8.js} +7 -7
  18. package/dist/{BrandEditor-BsTBM3s6.js.map → BrandEditor-Bkd6dhb8.js.map} +1 -1
  19. package/dist/{BrandLogo-CoKBk4MI.js → BrandLogo-CKpVSZb6.js} +4 -4
  20. package/dist/BrandLogo-CKpVSZb6.js.map +1 -0
  21. package/dist/{Breadcrumb-xqEUxfwn.js → Breadcrumb-BUoAZyhO.js} +8 -14
  22. package/dist/Breadcrumb-BUoAZyhO.js.map +1 -0
  23. package/dist/{Button-DsRrU5F1.js → Button-B21oVcPU.js} +31 -14
  24. package/dist/Button-B21oVcPU.js.map +1 -0
  25. package/dist/{Calendar-cG5NIJ4O.js → Calendar-DFtmZzbF.js} +99 -99
  26. package/dist/Calendar-DFtmZzbF.js.map +1 -0
  27. package/dist/{Card-Dyo7055Q.js → Card-BQmBWHXl.js} +35 -7
  28. package/dist/Card-BQmBWHXl.js.map +1 -0
  29. package/dist/{Checkbox-Dq5fPNrO.js → Checkbox-8yIIgY7g.js} +4 -4
  30. package/dist/Checkbox-8yIIgY7g.js.map +1 -0
  31. package/dist/{Clipboard-BZitKiSA.js → Clipboard-_xf6hMDD.js} +2 -2
  32. package/dist/{Clipboard-BZitKiSA.js.map → Clipboard-_xf6hMDD.js.map} +1 -1
  33. package/dist/{ClipboardButton-KHQnPEh_.js → ClipboardButton-srUgWgwH.js} +4 -4
  34. package/dist/{ClipboardButton-KHQnPEh_.js.map → ClipboardButton-srUgWgwH.js.map} +1 -1
  35. package/dist/{CodeEditor-BJKJXA0c.js → CodeEditor-jZMw1ioj.js} +55 -165
  36. package/dist/CodeEditor-jZMw1ioj.js.map +1 -0
  37. package/dist/{ColorInput-Bj3dfPFA.js → ColorInput-B3gBZ8Ya.js} +12 -27
  38. package/dist/ColorInput-B3gBZ8Ya.js.map +1 -0
  39. package/dist/{ColorModeSwitch-CWt-DZGC.js → ColorModeSwitch-Bw9wMyoL.js} +7 -17
  40. package/dist/ColorModeSwitch-Bw9wMyoL.js.map +1 -0
  41. package/dist/Combobox-C_MTpQ1l.js +1081 -0
  42. package/dist/Combobox-C_MTpQ1l.js.map +1 -0
  43. package/dist/{Command-BDU3C1gg.js → Command-BQPiZCNz.js} +4 -4
  44. package/dist/{Command-BDU3C1gg.js.map → Command-BQPiZCNz.js.map} +1 -1
  45. package/dist/{CopyField-BHRQUyuF.js → CopyField-Ba7h8MqZ.js} +19 -55
  46. package/dist/{CopyField-BHRQUyuF.js.map → CopyField-Ba7h8MqZ.js.map} +1 -1
  47. package/dist/{DateInput-B6N8g0ra.js → DateInput-D0nWUasV.js} +6 -6
  48. package/dist/{DateInput-B6N8g0ra.js.map → DateInput-D0nWUasV.js.map} +1 -1
  49. package/dist/{DatePicker-nA8YdE84.js → DatePicker-CtSa9b3V.js} +3 -3
  50. package/dist/{DatePicker-nA8YdE84.js.map → DatePicker-CtSa9b3V.js.map} +1 -1
  51. package/dist/{DateRangeInput-7M9Z4cVO.js → DateRangeInput-i6wE45-V.js} +10 -14
  52. package/dist/DateRangeInput-i6wE45-V.js.map +1 -0
  53. package/dist/{DateRangePicker-DjSUFjh4.js → DateRangePicker-BAnuz6vO.js} +9 -13
  54. package/dist/DateRangePicker-BAnuz6vO.js.map +1 -0
  55. package/dist/DiffView-ppTIzh2h.js.map +1 -1
  56. package/dist/{DropdownMenu-CgVwhDvl.js → DropdownMenu-gRM-A8qU.js} +3 -3
  57. package/dist/DropdownMenu-gRM-A8qU.js.map +1 -0
  58. package/dist/{FieldItem-DZGIdiXE.js → FieldItem-BHViBJS3.js} +12 -25
  59. package/dist/FieldItem-BHViBJS3.js.map +1 -0
  60. package/dist/{FieldItem-vhIJW4pj.d.ts → FieldItem-CPN1xjN5.d.ts} +11 -25
  61. package/dist/{FileUpload-DYQCTGjU.js → FileUpload-Ch2hrIWq.js} +11 -16
  62. package/dist/{FileUpload-DYQCTGjU.js.map → FileUpload-Ch2hrIWq.js.map} +1 -1
  63. package/dist/{Form-CcK7dRrC.js → Form-VkzfVPyT.js} +15 -23
  64. package/dist/{Form-CcK7dRrC.js.map → Form-VkzfVPyT.js.map} +1 -1
  65. package/dist/{FormGroup-DR_gZ25-.js → FormGroup-CmCppxOn.js} +11 -17
  66. package/dist/FormGroup-CmCppxOn.js.map +1 -0
  67. package/dist/{FullScreenModal-CN9nAs72.js → FullScreenModal-Cwnm8WdM.js} +15 -29
  68. package/dist/FullScreenModal-Cwnm8WdM.js.map +1 -0
  69. package/dist/{HoverCard-B9ZMJxjn.js → HoverCard-B8OKu9XZ.js} +2 -2
  70. package/dist/{HoverCard-B9ZMJxjn.js.map → HoverCard-B8OKu9XZ.js.map} +1 -1
  71. package/dist/{InputGroup-Do8K2hvD.js → InputGroup-CrlMRz4X.js} +11 -13
  72. package/dist/InputGroup-CrlMRz4X.js.map +1 -0
  73. package/dist/{Kbd-LCFMumoq.js → Kbd-J4n1J6i4.js} +23 -13
  74. package/dist/Kbd-J4n1J6i4.js.map +1 -0
  75. package/dist/{MarkedText-DNGEUVWc.js → MarkedText-DlmlJO0u.js} +2 -2
  76. package/dist/{MarkedText-DNGEUVWc.js.map → MarkedText-DlmlJO0u.js.map} +1 -1
  77. package/dist/{MermaidDiagram-ZG0iiDV_.js → MermaidDiagram-B30x2yV7.js} +16 -34
  78. package/dist/MermaidDiagram-B30x2yV7.js.map +1 -0
  79. package/dist/{Modal-I3VWk2oo.js → Modal-BkSuS9Yz.js} +17 -34
  80. package/dist/Modal-BkSuS9Yz.js.map +1 -0
  81. package/dist/{ModalTitle-CeLM_mX9.js → ModalTitle-BxopkIoQ.js} +3 -3
  82. package/dist/ModalTitle-BxopkIoQ.js.map +1 -0
  83. package/dist/{MultiActionButtonGroup-CYZ8IgSf.js → MultiActionButtonGroup-AWLj1Tg7.js} +3 -3
  84. package/dist/{MultiActionButtonGroup-CYZ8IgSf.js.map → MultiActionButtonGroup-AWLj1Tg7.js.map} +1 -1
  85. package/dist/{Navbar-Liy1TPI7.js → Navbar-B_DNpv5s.js} +9 -13
  86. package/dist/{Navbar-Liy1TPI7.js.map → Navbar-B_DNpv5s.js.map} +1 -1
  87. package/dist/{OtpInput-CbbymqYs.js → OtpInput-I8B8Vjdl.js} +2 -2
  88. package/dist/{OtpInput-CbbymqYs.js.map → OtpInput-I8B8Vjdl.js.map} +1 -1
  89. package/dist/{PageHeader-Q3IpCnQd.js → PageHeader-CgfyXpf1.js} +13 -26
  90. package/dist/PageHeader-CgfyXpf1.js.map +1 -0
  91. package/dist/{PageLayout-DSVM-ASz.js → PageLayout-DiAAILuS.js} +12 -20
  92. package/dist/PageLayout-DiAAILuS.js.map +1 -0
  93. package/dist/{PageToolbar-BKVJiDAL.js → PageToolbar-CIW0VmKe.js} +12 -42
  94. package/dist/PageToolbar-CIW0VmKe.js.map +1 -0
  95. package/dist/{Pagination-C_b7Xhey.js → Pagination-DH3f6LN0.js} +3 -3
  96. package/dist/{Pagination-C_b7Xhey.js.map → Pagination-DH3f6LN0.js.map} +1 -1
  97. package/dist/{Popconfirm-5pBzT_HQ.js → Popconfirm-Dhzn7sy6.js} +3 -3
  98. package/dist/{Popconfirm-5pBzT_HQ.js.map → Popconfirm-Dhzn7sy6.js.map} +1 -1
  99. package/dist/{Popover-BEbQrC_P.js → Popover-CEk915I1.js} +4 -3
  100. package/dist/Popover-CEk915I1.js.map +1 -0
  101. package/dist/{RadioGroup-Byc2mym4.js → RadioGroup-CBPRLRN0.js} +4 -4
  102. package/dist/{RadioGroup-Byc2mym4.js.map → RadioGroup-CBPRLRN0.js.map} +1 -1
  103. package/dist/{Search-Dhig_hPD.js → Search-DmkQo716.js} +3 -3
  104. package/dist/{Search-Dhig_hPD.js.map → Search-DmkQo716.js.map} +1 -1
  105. package/dist/{SearchInput-92r2bkA7.js → SearchInput-D3qMkHaW.js} +8 -13
  106. package/dist/SearchInput-D3qMkHaW.js.map +1 -0
  107. package/dist/{SectionNav-CNLJsiVG.js → SectionNav-TU5i3JeM.js} +10 -42
  108. package/dist/SectionNav-TU5i3JeM.js.map +1 -0
  109. package/dist/{Select-DV3Hak_l.js → Select-B4uK0QVm.js} +10 -12
  110. package/dist/Select-B4uK0QVm.js.map +1 -0
  111. package/dist/{Sheet-OparjIi-.js → Sheet-D0fAE14T.js} +5 -9
  112. package/dist/Sheet-D0fAE14T.js.map +1 -0
  113. package/dist/{Sidebar-CLEbBtNY.js → Sidebar-ZLMNIbg5.js} +16 -33
  114. package/dist/Sidebar-ZLMNIbg5.js.map +1 -0
  115. package/dist/{Slider-DLclvRws.js → Slider-B0ou9r0B.js} +3 -3
  116. package/dist/{Slider-DLclvRws.js.map → Slider-B0ou9r0B.js.map} +1 -1
  117. package/dist/{Stepper-C8sm9ZFM.js → Stepper-BLkLhfnd.js} +6 -9
  118. package/dist/{Stepper-C8sm9ZFM.js.map → Stepper-BLkLhfnd.js.map} +1 -1
  119. package/dist/{Switch-B0WdGSjz.js → Switch-DZvNePOm.js} +2 -2
  120. package/dist/{Switch-B0WdGSjz.js.map → Switch-DZvNePOm.js.map} +1 -1
  121. package/dist/{Table-CvYuTuNU.js → Table-BaVT5Tkn.js} +6 -11
  122. package/dist/{Table-CvYuTuNU.js.map → Table-BaVT5Tkn.js.map} +1 -1
  123. package/dist/{Tabs-eB974xJf.js → Tabs-CTDmye1N.js} +3 -3
  124. package/dist/{Tabs-eB974xJf.js.map → Tabs-CTDmye1N.js.map} +1 -1
  125. package/dist/{TextInput-CFTf4Cz1.js → TextInput-BWYXbfs4.js} +10 -14
  126. package/dist/TextInput-BWYXbfs4.js.map +1 -0
  127. package/dist/{ThemeProvider-DAmNn9Sd.js → ThemeProvider-VF38VfUa.js} +13 -31
  128. package/dist/ThemeProvider-VF38VfUa.js.map +1 -0
  129. package/dist/{TimePicker-BGeqc-Ed.js → TimePicker-CDGYrRxi.js} +2 -2
  130. package/dist/{TimePicker-BGeqc-Ed.js.map → TimePicker-CDGYrRxi.js.map} +1 -1
  131. package/dist/{Timeline-C8-wnRRr.js → Timeline-B-I6sb0M.js} +2 -2
  132. package/dist/{Timeline-C8-wnRRr.js.map → Timeline-B-I6sb0M.js.map} +1 -1
  133. package/dist/{Toast-Dr6Uw7ao.js → Toast-PfQf3vj-.js} +2 -2
  134. package/dist/{Toast-Dr6Uw7ao.js.map → Toast-PfQf3vj-.js.map} +1 -1
  135. package/dist/{Workbench-UR2FBC7I.js → Workbench-DmZzpkj7.js} +2 -2
  136. package/dist/{Workbench-UR2FBC7I.js.map → Workbench-DmZzpkj7.js.map} +1 -1
  137. package/dist/{ZoomableSvg-BZlk3PFJ.js → ZoomableSvg-C65nta_r.js} +5 -19
  138. package/dist/ZoomableSvg-C65nta_r.js.map +1 -0
  139. package/dist/charts.d.ts +21 -52
  140. package/dist/charts.js +13 -39
  141. package/dist/charts.js.map +1 -1
  142. package/dist/{context-D7X470dy.js → context-U293r3v-.js} +5 -9
  143. package/dist/context-U293r3v-.js.map +1 -0
  144. package/dist/editors.d.ts +27 -124
  145. package/dist/editors.js +29 -71
  146. package/dist/editors.js.map +1 -1
  147. package/dist/{findLiteralMatches-BGBw8Vbl.js → findLiteralMatches-DgGLzyG0.js} +8 -20
  148. package/dist/findLiteralMatches-DgGLzyG0.js.map +1 -0
  149. package/dist/flint.d.ts +3 -6
  150. package/dist/flint.js +31 -80
  151. package/dist/flint.js.map +1 -1
  152. package/dist/{icons-Cek151jC.js → icons-wcA70iQx.js} +105 -2
  153. package/dist/icons-wcA70iQx.js.map +1 -0
  154. package/dist/icons.d.ts +2 -2
  155. package/dist/icons.js +2 -2
  156. package/dist/{index-m-kQEWiY.d.ts → index-B7k-09i1.d.ts} +14 -2
  157. package/dist/index.d.ts +1180 -1867
  158. package/dist/index.js +69 -68
  159. package/dist/internal/blocks/AIKit.js +2 -2
  160. package/dist/internal/blocks/AppShell.js +1 -1
  161. package/dist/internal/blocks/AuthLayout.js +7 -27
  162. package/dist/internal/blocks/AuthLayout.js.map +1 -1
  163. package/dist/internal/blocks/AutoSaveTextarea.js +1 -1
  164. package/dist/internal/blocks/BrandEditor.js +1 -1
  165. package/dist/internal/blocks/ChartPanel.js +4 -8
  166. package/dist/internal/blocks/ChartPanel.js.map +1 -1
  167. package/dist/internal/blocks/ChipEditor.js +13 -21
  168. package/dist/internal/blocks/ChipEditor.js.map +1 -1
  169. package/dist/internal/blocks/ClipboardButton.js +1 -1
  170. package/dist/internal/blocks/ColorInput.js +1 -1
  171. package/dist/internal/blocks/ColorModeSwitch.js +1 -1
  172. package/dist/internal/blocks/Command.js +1 -1
  173. package/dist/internal/blocks/CopyField.js +1 -1
  174. package/dist/internal/blocks/DashboardHero.js +4 -7
  175. package/dist/internal/blocks/DashboardHero.js.map +1 -1
  176. package/dist/internal/blocks/DateInput.js +1 -1
  177. package/dist/internal/blocks/DateRangeInput.js +1 -1
  178. package/dist/internal/blocks/EmptyConfigurationActionCard.js +1 -1
  179. package/dist/internal/blocks/EmptyState.js +5 -9
  180. package/dist/internal/blocks/EmptyState.js.map +1 -1
  181. package/dist/internal/blocks/ErrorState.js +5 -9
  182. package/dist/internal/blocks/ErrorState.js.map +1 -1
  183. package/dist/internal/blocks/ExportMenu.js +6 -10
  184. package/dist/internal/blocks/ExportMenu.js.map +1 -1
  185. package/dist/internal/blocks/Form/connectors/ComboboxField.js +40 -0
  186. package/dist/internal/blocks/Form/connectors/ComboboxField.js.map +1 -0
  187. package/dist/internal/blocks/Form/connectors/MultiComboboxField.js +5 -22
  188. package/dist/internal/blocks/Form/connectors/MultiComboboxField.js.map +1 -1
  189. package/dist/internal/blocks/Form.js +1 -1
  190. package/dist/internal/blocks/FormGroup.js +1 -1
  191. package/dist/internal/blocks/GoogleButton.js +1 -1
  192. package/dist/internal/blocks/MicrosoftButton.js +1 -1
  193. package/dist/internal/blocks/MultiActionButtonGroup.js +1 -1
  194. package/dist/internal/blocks/MultiCombobox.js +160 -154
  195. package/dist/internal/blocks/MultiCombobox.js.map +1 -1
  196. package/dist/internal/blocks/NumberInput.js +1 -1
  197. package/dist/internal/blocks/OtpInput.js +1 -1
  198. package/dist/internal/blocks/PageHeader.js +1 -1
  199. package/dist/internal/blocks/PageLayout.js +1 -1
  200. package/dist/internal/blocks/PageToolbar.js +1 -1
  201. package/dist/internal/blocks/PasswordInput.js +4 -4
  202. package/dist/internal/blocks/PasswordInput.js.map +1 -1
  203. package/dist/internal/blocks/SearchInput.js +1 -1
  204. package/dist/internal/blocks/SearchInputWithNavigation.js +4 -4
  205. package/dist/internal/blocks/SectionNav.js +1 -1
  206. package/dist/internal/blocks/SplitButton.js +3 -3
  207. package/dist/internal/blocks/StatCard.js +4 -8
  208. package/dist/internal/blocks/StatCard.js.map +1 -1
  209. package/dist/internal/blocks/TextInput.js +1 -1
  210. package/dist/internal/blocks/Textarea.js +2 -2
  211. package/dist/internal/blocks/TimeInput.js +5 -5
  212. package/dist/internal/blocks/TimeInput.js.map +1 -1
  213. package/dist/internal/components/Alert.js +1 -1
  214. package/dist/internal/components/AlertModal.js +1 -1
  215. package/dist/internal/components/Avatar.js +1 -1
  216. package/dist/internal/components/Badge.js +1 -1
  217. package/dist/internal/components/BrandLogo.js +1 -1
  218. package/dist/internal/components/Breadcrumb.js +1 -1
  219. package/dist/internal/components/Button.js +1 -1
  220. package/dist/internal/components/ButtonGroup.js.map +1 -1
  221. package/dist/internal/components/Calendar.js +1 -1
  222. package/dist/internal/components/Card.js +1 -1
  223. package/dist/internal/components/Checkbox.js +1 -1
  224. package/dist/internal/components/Clipboard.js +1 -1
  225. package/dist/internal/components/CollapsiblePanel.js +3 -3
  226. package/dist/internal/components/CollapsiblePanel.js.map +1 -1
  227. package/dist/internal/components/Combobox.js +2 -2
  228. package/dist/internal/components/DatePicker.js +1 -1
  229. package/dist/internal/components/DateRangePicker.js +1 -1
  230. package/dist/internal/components/DropdownMenu.js +1 -1
  231. package/dist/internal/components/FileUpload.js +1 -1
  232. package/dist/internal/components/FullScreenModal.js +1 -1
  233. package/dist/internal/components/HoverCard.js +1 -1
  234. package/dist/internal/components/InputGroup.js +1 -1
  235. package/dist/internal/components/Kbd.js +1 -1
  236. package/dist/internal/components/MarkedText.js +1 -1
  237. package/dist/internal/components/MergeEditor/ConflictStatus/ConflictStatusIndicator.js +1 -1
  238. package/dist/internal/components/Modal.js +1 -1
  239. package/dist/internal/components/Navbar.js +1 -1
  240. package/dist/internal/components/Pagination.js +1 -1
  241. package/dist/internal/components/PanelWithDetails.js +1 -1
  242. package/dist/internal/components/Popconfirm.js +1 -1
  243. package/dist/internal/components/Popover.js +1 -1
  244. package/dist/internal/components/RadioGroup.js +1 -1
  245. package/dist/internal/components/Search.js +1 -1
  246. package/dist/internal/components/Select.js +1 -1
  247. package/dist/internal/components/Separator.js.map +1 -1
  248. package/dist/internal/components/Sheet.js +1 -1
  249. package/dist/internal/components/Sidebar.js +1 -1
  250. package/dist/internal/components/Skeleton/Skeleton.js +5 -9
  251. package/dist/internal/components/Skeleton/Skeleton.js.map +1 -1
  252. package/dist/internal/components/Slider.js +1 -1
  253. package/dist/internal/components/SpotlightBackdrop.js +3 -8
  254. package/dist/internal/components/SpotlightBackdrop.js.map +1 -1
  255. package/dist/internal/components/Stepper.js +1 -1
  256. package/dist/internal/components/Switch.js +1 -1
  257. package/dist/internal/components/Table.js +1 -1
  258. package/dist/internal/components/Tabs.js +1 -1
  259. package/dist/internal/components/ThemeProvider.js +3 -3
  260. package/dist/internal/components/TimePicker.js +1 -1
  261. package/dist/internal/components/Timeline.js +1 -1
  262. package/dist/internal/components/Toast.js +2 -2
  263. package/dist/internal/components/Tooltip.js +1 -1
  264. package/dist/internal/components/Truncated.js +4 -8
  265. package/dist/internal/components/Truncated.js.map +1 -1
  266. package/dist/internal/components/Workbench.js +1 -1
  267. package/dist/internal/components/ZoomableSvg.js +1 -1
  268. package/dist/internal/hooks/useScrollbarPosition.js +1 -1
  269. package/dist/internal/hooks/useStableCallback.js +4 -13
  270. package/dist/internal/hooks/useStableCallback.js.map +1 -1
  271. package/dist/internal/utils/accessibleName.js +11 -0
  272. package/dist/internal/utils/accessibleName.js.map +1 -0
  273. package/dist/internal/utils.js +2 -2
  274. package/dist/markdown-editor.d.ts +15 -37
  275. package/dist/markdown-editor.js +17 -23
  276. package/dist/markdown-editor.js.map +1 -1
  277. package/dist/mermaid.d.ts +4 -6
  278. package/dist/mermaid.js +1 -1
  279. package/dist/rjsf.d.ts +9 -19
  280. package/dist/rjsf.js +44 -84
  281. package/dist/rjsf.js.map +1 -1
  282. package/dist/{styles-BAFscjSV.js → styles-gzcjOyKv.js} +6 -6
  283. package/dist/styles-gzcjOyKv.js.map +1 -0
  284. package/dist/styles.css +1 -1
  285. package/dist/svgTransforms-Dw2hRA-i.js +51 -0
  286. package/dist/svgTransforms-Dw2hRA-i.js.map +1 -0
  287. package/dist/testing.d.ts +3 -10
  288. package/dist/testing.js +7 -15
  289. package/dist/testing.js.map +1 -1
  290. package/dist/themeTokens-7DEab0kp.js +31 -0
  291. package/dist/themeTokens-7DEab0kp.js.map +1 -0
  292. package/dist/{toast-DIC0iNRf.js → toast-CyXEdaCZ.js} +3 -3
  293. package/dist/toast-CyXEdaCZ.js.map +1 -0
  294. package/dist/{themeScopeContext-D7fSyb06.js → useColorSchemeState-Cw4MCXUs.js} +14 -14
  295. package/dist/useColorSchemeState-Cw4MCXUs.js.map +1 -0
  296. package/dist/{useControllableState-M6494mCT.js → useControllableState-CUYXMsfz.js} +3 -7
  297. package/dist/useControllableState-CUYXMsfz.js.map +1 -0
  298. package/dist/useElement-D-RjrGa4.js +21 -0
  299. package/dist/useElement-D-RjrGa4.js.map +1 -0
  300. package/dist/useThemeAttributes-B199Regy.js +67 -0
  301. package/dist/useThemeAttributes-B199Regy.js.map +1 -0
  302. package/dist/{utils-BW4AE8VP.js → utils-Bd28bWgm.js} +31 -26
  303. package/dist/utils-Bd28bWgm.js.map +1 -0
  304. package/dist/vega.d.ts +8 -30
  305. package/dist/vega.js +5 -13
  306. package/dist/vega.js.map +1 -1
  307. package/dist/{vegaTheme-CSvFEMBl.js → vegaTheme-VQMWTlBw.js} +7 -21
  308. package/dist/vegaTheme-VQMWTlBw.js.map +1 -0
  309. package/package.json +28 -27
  310. package/src/styles.css +120 -14
  311. package/src/theme.css +130 -5
  312. package/dist/AIKit-ELIsegJz.js.map +0 -1
  313. package/dist/Alert-BsIM_oFK.js.map +0 -1
  314. package/dist/AlertModal-BGJdaRRj.js.map +0 -1
  315. package/dist/AppShell-DRgJxpXW.js.map +0 -1
  316. package/dist/Badge-o5jURtpB.js.map +0 -1
  317. package/dist/BrandLogo-CoKBk4MI.js.map +0 -1
  318. package/dist/Breadcrumb-xqEUxfwn.js.map +0 -1
  319. package/dist/Button-DsRrU5F1.js.map +0 -1
  320. package/dist/Calendar-cG5NIJ4O.js.map +0 -1
  321. package/dist/Card-Dyo7055Q.js.map +0 -1
  322. package/dist/Checkbox-Dq5fPNrO.js.map +0 -1
  323. package/dist/CodeEditor-BJKJXA0c.js.map +0 -1
  324. package/dist/ColorInput-Bj3dfPFA.js.map +0 -1
  325. package/dist/ColorModeSwitch-CWt-DZGC.js.map +0 -1
  326. package/dist/Combobox-D8PrEqTG.js +0 -619
  327. package/dist/Combobox-D8PrEqTG.js.map +0 -1
  328. package/dist/DateRangeInput-7M9Z4cVO.js.map +0 -1
  329. package/dist/DateRangePicker-DjSUFjh4.js.map +0 -1
  330. package/dist/DropdownMenu-CgVwhDvl.js.map +0 -1
  331. package/dist/FieldItem-DZGIdiXE.js.map +0 -1
  332. package/dist/FormGroup-DR_gZ25-.js.map +0 -1
  333. package/dist/FullScreenModal-CN9nAs72.js.map +0 -1
  334. package/dist/InputGroup-Do8K2hvD.js.map +0 -1
  335. package/dist/Kbd-LCFMumoq.js.map +0 -1
  336. package/dist/MermaidDiagram-ZG0iiDV_.js.map +0 -1
  337. package/dist/Modal-I3VWk2oo.js.map +0 -1
  338. package/dist/ModalTitle-CeLM_mX9.js.map +0 -1
  339. package/dist/PageHeader-Q3IpCnQd.js.map +0 -1
  340. package/dist/PageLayout-DSVM-ASz.js.map +0 -1
  341. package/dist/PageToolbar-BKVJiDAL.js.map +0 -1
  342. package/dist/Popover-BEbQrC_P.js.map +0 -1
  343. package/dist/SearchInput-92r2bkA7.js.map +0 -1
  344. package/dist/SectionNav-CNLJsiVG.js.map +0 -1
  345. package/dist/Select-DV3Hak_l.js.map +0 -1
  346. package/dist/Sheet-OparjIi-.js.map +0 -1
  347. package/dist/Sidebar-CLEbBtNY.js.map +0 -1
  348. package/dist/TextInput-CFTf4Cz1.js.map +0 -1
  349. package/dist/ThemeProvider-DAmNn9Sd.js.map +0 -1
  350. package/dist/ZoomableSvg-BZlk3PFJ.js.map +0 -1
  351. package/dist/context-D7X470dy.js.map +0 -1
  352. package/dist/findLiteralMatches-BGBw8Vbl.js.map +0 -1
  353. package/dist/icons-Cek151jC.js.map +0 -1
  354. package/dist/internal/components/StatusDot.js +0 -67
  355. package/dist/internal/components/StatusDot.js.map +0 -1
  356. package/dist/styles-BAFscjSV.js.map +0 -1
  357. package/dist/svgTransforms-CJAYPk7o.js +0 -81
  358. package/dist/svgTransforms-CJAYPk7o.js.map +0 -1
  359. package/dist/themeScopeContext-D7fSyb06.js.map +0 -1
  360. package/dist/themeTokens-qP1-55O7.js +0 -119
  361. package/dist/themeTokens-qP1-55O7.js.map +0 -1
  362. package/dist/toast-DIC0iNRf.js.map +0 -1
  363. package/dist/useControllableState-M6494mCT.js.map +0 -1
  364. package/dist/useElement-BK3AAiIf.js +0 -24
  365. package/dist/useElement-BK3AAiIf.js.map +0 -1
  366. package/dist/utils-BW4AE8VP.js.map +0 -1
  367. package/dist/vegaTheme-CSvFEMBl.js.map +0 -1
@@ -0,0 +1,1081 @@
1
+ import { cn } from "./classNames-BzrkDnOD.js";
2
+ import { Tooltip, TooltipProvider, noop } from "./utils-Bd28bWgm.js";
3
+ import { useArgoDesign } from "./context-U293r3v-.js";
4
+ import { Check, ChevronDown, ChevronRight, Loader2, Minus, Plus, X } from "./icons-wcA70iQx.js";
5
+ import { hasTruncationOverflow } from "./internal/components/Truncated.js";
6
+ import { useStableCallback } from "./internal/hooks/useStableCallback.js";
7
+ import { InputGroup } from "./InputGroup-CrlMRz4X.js";
8
+ import { forwardAccessibleName } from "./internal/utils/accessibleName.js";
9
+ import { Badge } from "./Badge-DIjhwPvh.js";
10
+ import { useElement } from "./useElement-D-RjrGa4.js";
11
+ import { Fragment, createContext, isValidElement, use, useCallback, useEffect, useImperativeHandle, useMemo, useRef, useState } from "react";
12
+ import { Fragment as Fragment$1, jsx, jsxs } from "react/jsx-runtime";
13
+ import { Combobox } from "@base-ui/react/combobox";
14
+ import { DismissableLayer } from "@radix-ui/react-dismissable-layer";
15
+ import { measureElement, observeElementRect, useVirtualizer } from "@tanstack/react-virtual";
16
+ //#region src/components/Combobox/blankValues.ts
17
+ /**
18
+ * The two blank strings a Storage filter predicates on, in list order.
19
+ *
20
+ * - Hand it to `items` where the blanks are the whole list — a value field whose other entries the
21
+ * user types. {@link withBlankValues} is for offering them ahead of a list that already exists.
22
+ */
23
+ const BLANK_VALUES = ["", " "];
24
+ const BLANK_LABELS = {
25
+ "": "[empty string]",
26
+ " ": "[space character]"
27
+ };
28
+ /**
29
+ * A string as a row or a chip shows it — named when its text is blank, itself otherwise.
30
+ *
31
+ * - Exported so an `itemToLabel` / `itemToChipLabel` / `itemToSelectedLabel` / `renderChips`
32
+ * override can keep the default naming instead of retyping the labels.
33
+ * - The two blank values get their own labels; any other whitespace-only string (a tab, a run of
34
+ * spaces) falls back to a generic one, so two different ones read alike.
35
+ * - Identity is unaffected — `toLabel` keys items off the raw string.
36
+ */
37
+ const namedLabel = (text) => text.trim() === "" ? BLANK_LABELS[text] ?? "[whitespace]" : text;
38
+ /**
39
+ * `values` with both blank values offered ahead of them, and duplicates collapsed.
40
+ *
41
+ * - The dedupe matters where the list is the distinct values of a Storage column: it can already
42
+ * hold a blank, and two `Object.is`-equal rows both report `aria-selected` and toggle each other.
43
+ * - First occurrence wins, so the order of the values is otherwise preserved.
44
+ * - String items only — the seven Storage filters this serves are all string-valued.
45
+ */
46
+ const withBlankValues = (values) => [.../* @__PURE__ */ new Set([...BLANK_VALUES, ...values])];
47
+ //#endregion
48
+ //#region src/components/Combobox/ComboboxChip.tsx
49
+ /**
50
+ * Keeps a chip's keyboard handling off its content's.
51
+ *
52
+ * - Base UI's chip `preventDefault`s Enter/Space, cancelling a nested link or button.
53
+ * - Capture phase, so the chip's own bubble handler never runs.
54
+ * - Target-guarded, so the chip's own keys (Backspace, Delete, arrows) still reach it.
55
+ */
56
+ const stopNestedKeyDown = (event) => {
57
+ if (event.target !== event.currentTarget) event.stopPropagation();
58
+ };
59
+ /**
60
+ * One removable chip in a `MultiCombobox`'s `renderChips` slot or `Combobox.Chips`.
61
+ *
62
+ * - Position is identity: Base UI removes by index, so render one per `value` entry, in order.
63
+ * - A non-removable chip is a plain `Badge`: a chip would shift the removal index.
64
+ * - A label cut off by the chip's width shows in full in a tooltip on hover.
65
+ */
66
+ const ComboboxChip = ({ children, color, variant }) => {
67
+ const renderArgo = useArgoDesign();
68
+ const [isTruncated, setIsTruncated] = useState(false);
69
+ return /* @__PURE__ */ jsxs(Combobox.Chip, {
70
+ render: /* @__PURE__ */ jsx(Badge.Root, {
71
+ color,
72
+ variant: variant ?? (renderArgo ? "pill" : "default"),
73
+ className: "tw:max-w-40"
74
+ }),
75
+ onKeyDownCapture: stopNestedKeyDown,
76
+ children: [/* @__PURE__ */ jsx(TooltipProvider, { children: /* @__PURE__ */ jsx(Tooltip, {
77
+ tooltip: children,
78
+ forceHide: !isTruncated,
79
+ placement: "top",
80
+ triggerAsChild: true,
81
+ children: /* @__PURE__ */ jsx(Badge.Label, {
82
+ className: "tw:leading-[inherit]",
83
+ onMouseOver: (event) => setIsTruncated(hasTruncationOverflow(event.currentTarget)),
84
+ children
85
+ })
86
+ }) }), /* @__PURE__ */ jsx(Badge.Addon, {
87
+ variant: "inline",
88
+ align: "inline-end",
89
+ children: /* @__PURE__ */ jsx(Combobox.ChipRemove, { render: /* @__PURE__ */ jsx(Badge.Button, {
90
+ "aria-label": "Remove value",
91
+ children: /* @__PURE__ */ jsx(X, { className: "tw:size-3" })
92
+ }) })
93
+ })]
94
+ });
95
+ };
96
+ //#endregion
97
+ //#region src/components/Combobox/utils.ts
98
+ const isPlainObject = (item) => item !== null && typeof item === "object";
99
+ const isNumber = (item) => typeof item === "number";
100
+ const isString = (item) => typeof item === "string";
101
+ const isLabeledItem = (item) => isPlainObject(item) && Reflect.has(item, "label") && Reflect.has(item, "value");
102
+ const isInternalItem = (item) => isLabeledItem(item) && "__marker" in item;
103
+ const isSelectAllItem = (item) => isInternalItem(item) && item.__marker === "selectAll";
104
+ const isCreatableItem = (item) => {
105
+ return isInternalItem(item) && item.__marker === "creatable";
106
+ };
107
+ const isGroupHeaderItem = (item) => isInternalItem(item) && item.__marker === "groupHeader";
108
+ const isGroupItem = (itemOrGroup) => itemOrGroup !== null && typeof itemOrGroup === "object" && Reflect.has(itemOrGroup, "items");
109
+ const isGroupItems = (items) => {
110
+ if (!Array.isArray(items)) return false;
111
+ if (items.length === 0) return false;
112
+ return isGroupItem(items[0]);
113
+ };
114
+ /** The options of a list, its groups flattened. */
115
+ const flatItems = (list) => {
116
+ const items = [];
117
+ for (const entry of list) if (!isGroupItem(entry)) items.push(entry);
118
+ else for (const item of entry.items) items.push(item);
119
+ return items;
120
+ };
121
+ /** The create row and Select All: each is drawn outside any group, even in a grouped list. */
122
+ const isLooseItem = (item) => isCreatableItem(item) || isSelectAllItem(item);
123
+ /**
124
+ * `items` as entries of `list`: one group without a label in a grouped list, else themselves.
125
+ *
126
+ * - Base UI reads the first entry to tell a grouped list, so every entry has to match it.
127
+ * - The group never reaches `groupToLabel`: {@link isEntriesGroup} tells it apart.
128
+ */
129
+ const toEntries = (list, items) => isGroupItems(list) ? [{
130
+ __marker: "entries",
131
+ items
132
+ }] : items;
133
+ /** A group {@link toEntries} made; Base UI's filter keeps the marker, as it spreads each group. */
134
+ const isEntriesGroup = (entry) => isGroupItem(entry) && Reflect.get(entry, "__marker") === "entries";
135
+ /**
136
+ * Node a list row and a chip show for an item.
137
+ *
138
+ * - Names a blank string, so a row or chip never renders as nothing; {@link toLabel} does not.
139
+ * - A non-string `label` (a JSX node) passes through.
140
+ */
141
+ const defaultItemToLabel = (item) => {
142
+ if (isNumber(item)) return item;
143
+ if (isString(item)) return namedLabel(item);
144
+ if (!isLabeledItem(item)) throw new Error("Combobox: Item does not have a `label` field. Pass `itemToLabel` for custom item shapes.");
145
+ return isString(item.label) ? namedLabel(item.label) : item.label;
146
+ };
147
+ /**
148
+ * The node a row or a chip shows for an item when the caller passes no `itemToLabel`.
149
+ *
150
+ * - Names a blank `itemToStringLabel` result, which would otherwise render as nothing.
151
+ * - Falls back to the item's own string: Base UI reports the outgoing selection for one render
152
+ * after `items` and `itemToStringLabel` change together.
153
+ */
154
+ const defaultRowLabel = (itemToStringLabel) => itemToStringLabel ? (item) => namedLabel(itemToStringLabel(item) ?? toLabel(item)) : defaultItemToLabel;
155
+ /**
156
+ * Display string for an item — mirrors Base UI's default.
157
+ *
158
+ * - Also the default search text and the closed `Combobox`'s input value.
159
+ * - Does not name blank strings: `''` may mean "nothing selected", and must keep the placeholder.
160
+ * - A labelled item with neither a label nor a value reads as `''`, like the string item `''`.
161
+ * - Naming lives in {@link defaultItemToLabel}, read only by rows and chips.
162
+ */
163
+ const toLabel = (item) => {
164
+ if (isString(item)) return item;
165
+ if (isLabeledItem(item)) return item.label || item.value || "";
166
+ return String(item);
167
+ };
168
+ /** Equality key for an item — prefers `.value`. */
169
+ const toValueKey = (item) => {
170
+ if (isString(item)) return item;
171
+ if (isLabeledItem(item)) return item.value || item.label || "";
172
+ return String(item);
173
+ };
174
+ const defaultItemToIcon = (item) => isLabeledItem(item) ? item.icon : void 0;
175
+ const defaultItemToDisabled = (item) => isLabeledItem(item) ? item.disabled ?? false : false;
176
+ /** A group's `label`, as `Select` renders it; one that is not a node reads as none. */
177
+ const defaultGroupToLabel = (group) => {
178
+ const label = Reflect.get(group, "label");
179
+ return isString(label) || isNumber(label) || isValidElement(label) ? label : void 0;
180
+ };
181
+ const selectAllItem = {
182
+ __marker: "selectAll",
183
+ value: "__select_all__",
184
+ label: "Select All"
185
+ };
186
+ /** Row for the group at `index` — keyed by position, like its collapse state. */
187
+ const createGroupHeaderItem = (index) => ({
188
+ __marker: "groupHeader",
189
+ value: `__group_${index}__`,
190
+ label: ""
191
+ });
192
+ const createCreatableItem = (query) => ({
193
+ __marker: "creatable",
194
+ value: query,
195
+ label: query
196
+ });
197
+ /**
198
+ * Item-equality predicate for a Base UI combobox root.
199
+ *
200
+ * - A sentinel equals a sentinel by marker and value key (the create row is rebuilt per keystroke).
201
+ * - A sentinel never equals a real item.
202
+ * - Real items use the caller's predicate, else `Object.is`.
203
+ */
204
+ const toItemEquality = (isItemEqualToValue) => (a, b) => {
205
+ if (isInternalItem(a) || isInternalItem(b)) return isInternalItem(a) && isInternalItem(b) && a.__marker === b.__marker && toValueKey(a) === toValueKey(b);
206
+ return isItemEqualToValue?.(a, b) ?? Object.is(a, b);
207
+ };
208
+ /**
209
+ * {@link toItemEquality} with a stable identity.
210
+ *
211
+ * - Base UI lists the comparer in an effect's deps; a new one per render re-renders every row.
212
+ */
213
+ const useItemEquality = (isItemEqualToValue) => useMemo(() => toItemEquality(isItemEqualToValue), [isItemEqualToValue]);
214
+ /** `itemToStringValue` that also reads the create row, whose value is its query. */
215
+ const withCreatableStringValue = (itemToStringValue, allowCreate) => !allowCreate || !itemToStringValue ? itemToStringValue : (item) => isCreatableItem(item) ? item.value : itemToStringValue(item);
216
+ /** {@link withCreatableStringValue} with a stable identity. */
217
+ const useCreatableStringValue = (itemToStringValue, allowCreate) => useMemo(() => withCreatableStringValue(itemToStringValue, allowCreate), [itemToStringValue, allowCreate]);
218
+ const defaultToCreateValue = (query, value, queryToCreateValue) => {
219
+ if (queryToCreateValue) return queryToCreateValue(query);
220
+ if (value === void 0) return query;
221
+ if (isString(value)) return query;
222
+ if (isLabeledItem(value)) return {
223
+ value: query,
224
+ label: query
225
+ };
226
+ throw new Error("Combobox: Cannot infer item shape for `allowCreate`. Pass `queryToCreateValue` for custom item shapes.");
227
+ };
228
+ const defaultQueryToCreateLabel = (query) => `Create "${query}"`;
229
+ //#endregion
230
+ //#region src/components/Combobox/ComboboxChips.tsx
231
+ const ComboboxChips = ({ maxVisible = 3, itemToChipLabel = defaultItemToLabel, itemToChipColor, pinnedChipLabels, variant = "pill", children }) => /* @__PURE__ */ jsx(Combobox.Value, { children: (rawValues) => {
232
+ const values = rawValues.filter((value) => !isInternalItem(value));
233
+ if (children) return children(values);
234
+ const visible = values.slice(0, maxVisible);
235
+ const overflow = values.slice(maxVisible);
236
+ return /* @__PURE__ */ jsxs(Fragment$1, { children: [
237
+ pinnedChipLabels?.map((label, index) => /* @__PURE__ */ jsx(Badge, {
238
+ className: "tw:max-w-40",
239
+ variant,
240
+ children: label
241
+ }, index)),
242
+ visible.map((value, index) => /* @__PURE__ */ jsx(ComboboxChip, {
243
+ color: itemToChipColor?.(value),
244
+ variant,
245
+ children: itemToChipLabel(value)
246
+ }, index)),
247
+ overflow.length > 0 && /* @__PURE__ */ jsx(Badge.Root, {
248
+ variant: "pill",
249
+ children: /* @__PURE__ */ jsxs(Badge.Label, { children: ["+", overflow.length] })
250
+ })
251
+ ] });
252
+ } });
253
+ //#endregion
254
+ //#region src/components/Combobox/ComboboxClearButton.tsx
255
+ /** The button that clears the selection. */
256
+ const ComboboxClearButton = ({ disabled }) => /* @__PURE__ */ jsx(InputGroup.Button, {
257
+ asChild: true,
258
+ "aria-label": "Clear",
259
+ disabled,
260
+ children: /* @__PURE__ */ jsx(Combobox.Clear, { children: /* @__PURE__ */ jsx(X, { className: "tw:size-4" }) })
261
+ });
262
+ //#endregion
263
+ //#region src/components/Combobox/ComboboxContent.tsx
264
+ const ComboboxContent = ({ ref, align = "start", side = "bottom", sideOffset = 4, alignOffset, anchor, container, className, children, ...popupProps }) => {
265
+ const effectiveVariant = useArgoDesign() ? "argo" : "default";
266
+ return /* @__PURE__ */ jsx(Combobox.Portal, {
267
+ container,
268
+ children: /* @__PURE__ */ jsx(DismissableLayer, {
269
+ asChild: true,
270
+ children: /* @__PURE__ */ jsx(Combobox.Positioner, {
271
+ align,
272
+ side,
273
+ sideOffset,
274
+ alignOffset,
275
+ anchor,
276
+ className: "tw:isolate tw:z-[1055] tailwindcss-preflight",
277
+ children: /* @__PURE__ */ jsx(Combobox.Popup, {
278
+ ref,
279
+ "data-chips": !!anchor,
280
+ "data-variant": effectiveVariant,
281
+ className: cn("tw:!pointer-events-auto", "tw:group/combobox-content", "tw:relative", "tw:flex tw:flex-col tw:overflow-hidden", "tw:max-h-[var(--available-height)]", "tw:w-[var(--anchor-width)]", "tw:max-w-[var(--available-width)]", "tw:origin-[var(--transform-origin)]", "tw:data-[chips=true]:min-w-[var(--anchor-width)]", "tw:rounded-lg", "tw:data-[variant=default]:rounded", "tw:bg-popover", "tw:text-popover-foreground", "tw:shadow-md", "tw:ring-1", "tw:ring-foreground/10", "tw:duration-100", "data-open:animate-in", "data-open:fade-in-0", "data-open:zoom-in-95", "data-closed:animate-out", "data-closed:fade-out-0", "data-closed:zoom-out-95", "tw:data-[side=bottom]:slide-in-from-top-2", "tw:data-[side=top]:slide-in-from-bottom-2", "tw:data-[side=left]:slide-in-from-right-2", "tw:data-[side=right]:slide-in-from-left-2", "tw:data-[side=inline-start]:slide-in-from-right-2", "tw:data-[side=inline-end]:slide-in-from-left-2", className),
282
+ ...popupProps,
283
+ children
284
+ })
285
+ })
286
+ })
287
+ });
288
+ };
289
+ //#endregion
290
+ //#region src/components/Combobox/ComboboxEmpty.tsx
291
+ const ComboboxEmpty = ({ ref, className, ...props }) => /* @__PURE__ */ jsx(Combobox.Empty, {
292
+ ref,
293
+ className: cn("tw:hidden", "tw:group-data-[empty]/combobox-content:flex", "tw:justify-center", "tw:items-center", "tw:text-center", "tw:px-2", "tw:text-muted-foreground", "tw:py-6", className),
294
+ ...props
295
+ });
296
+ //#endregion
297
+ //#region src/components/Combobox/ComboboxInput.tsx
298
+ const ComboboxInput = ({ ref, ...props }) => /* @__PURE__ */ jsx(Combobox.Input, {
299
+ ...props,
300
+ ref,
301
+ render: /* @__PURE__ */ jsx(InputGroup.Input, {})
302
+ });
303
+ //#endregion
304
+ //#region src/components/Combobox/ComboboxInputGroup.tsx
305
+ const ComboboxInputGroup = ({ ref, state, variant, sizing, focusVariant, className, ...props }) => /* @__PURE__ */ jsx(Combobox.InputGroup, {
306
+ ...props,
307
+ ref,
308
+ render: /* @__PURE__ */ jsx(InputGroup, {
309
+ state,
310
+ variant,
311
+ focusVariant,
312
+ sizing,
313
+ className
314
+ })
315
+ });
316
+ //#endregion
317
+ //#region src/components/Combobox/ComboboxItemIndicator.tsx
318
+ const ComboboxItemIndicator = ({ ref, className, variant = "check", ...props }) => {
319
+ if (variant === "checkbox") return /* @__PURE__ */ jsx(Combobox.ItemIndicator, {
320
+ ref,
321
+ keepMounted: true,
322
+ className: cn("tw:group/combobox-item-indicator", "tw:inline-flex", "tw:items-center", "tw:justify-center", "tw:size-4", "tw:shrink-0", "tw:rounded", "tw:border", "tw:border-input", "tw:bg-background", "tw:data-[selected]:bg-primary", "tw:data-[selected]:border-primary", className),
323
+ ...props,
324
+ children: /* @__PURE__ */ jsx(Check, { className: cn("tw:hidden", "tw:size-3", "tw:stroke-[4px]", "tw:!text-primary-foreground", "tw:group-data-[selected]/combobox-item-indicator:block") })
325
+ });
326
+ return /* @__PURE__ */ jsx(Combobox.ItemIndicator, {
327
+ ...props,
328
+ ref,
329
+ className: cn("tw:pointer-events-none", "tw:absolute", "tw:right-2", "tw:group-data-[variant=default]/combobox-content:right-4", "tw:size-4", "tw:group-data-[highlighted]/combobox-item:text-accent-foreground", "tw:group-data-[variant=default]/combobox-content:group-data-[highlighted]/combobox-item:text-secondary-foreground", className),
330
+ render: /* @__PURE__ */ jsx(Check, {})
331
+ });
332
+ };
333
+ //#endregion
334
+ //#region src/components/Combobox/virtualList.ts
335
+ /** Above this many options, `virtualized="auto"` windows the list. */
336
+ const VIRTUALIZE_THRESHOLD = 30;
337
+ /**
338
+ * The position a windowed list gives each option it renders.
339
+ *
340
+ * - `index` is the option's place in Base UI's flat filtered list, which keyboard navigation counts.
341
+ * - `aria-setsize` / `aria-posinset` keep the set's length known while most options are out of the DOM.
342
+ * - `undefined` outside a windowed list, so a fully rendered list keeps its markup.
343
+ */
344
+ const VirtualRowContext = createContext(void 0);
345
+ /**
346
+ * Whether a combobox windows its list, and what ties Base UI's highlight to the window.
347
+ *
348
+ * - `'auto'` counts options, not groups, so a list of a few large groups windows too.
349
+ * - `onItemHighlighted` scrolls a row the keyboard reached into view; a pointer is over its row.
350
+ * - `ref` goes to `ComboboxVirtualList`.
351
+ */
352
+ const useVirtualList = (items, virtualized) => {
353
+ const ref = useRef(null);
354
+ const count = isGroupItems(items) ? items.reduce((sum, group) => sum + group.items.length, 0) : items.length;
355
+ return {
356
+ enabled: virtualized === "auto" ? count > VIRTUALIZE_THRESHOLD : virtualized,
357
+ ref,
358
+ onItemHighlighted: ({ reason, index }) => {
359
+ if (reason !== "pointer") ref.current?.scrollToItem(index);
360
+ }
361
+ };
362
+ };
363
+ //#endregion
364
+ //#region src/components/Combobox/ComboboxItem.tsx
365
+ const ComboboxItem = ({ ref, className, children, icon, indicator: variant = "check", ...props }) => /* @__PURE__ */ jsxs(Combobox.Item, {
366
+ ...props,
367
+ ...use(VirtualRowContext),
368
+ ref,
369
+ "data-slot": "combobox-item",
370
+ className: cn("tw:group/combobox-item", "tw:relative", "tw:flex", "tw:flex-1", "tw:items-center", "tw:gap-2", "tw:py-2", "tw:px-2", "tw:group-data-[variant=default]/combobox-content:px-4", "tw:rounded-lg", "tw:group-data-[variant=default]/combobox-content:rounded-none", "tw:text-sm", "tw:select-none", "tw:cursor-default", "tw:outline-hidden", "tw:data-[highlighted]:bg-accent", "tw:group-data-[variant=default]/combobox-content:data-[highlighted]:bg-secondary", "tw:data-[highlighted]:text-accent-foreground", "tw:group-data-[variant=default]/combobox-content:data-[highlighted]:text-secondary-foreground", "tw:data-disabled:pointer-events-none", "tw:data-disabled:opacity-50", "tw:[&_svg]:pointer-events-none", "tw:[&_svg]:shrink-0", "tw:[&_svg]:text-muted-foreground", "tw:[&_svg:not([class*='size-'])]:size-4", className),
371
+ children: [
372
+ variant !== "none" && /* @__PURE__ */ jsx(ComboboxItemIndicator, { variant }),
373
+ icon,
374
+ /* @__PURE__ */ jsx("span", {
375
+ className: "tw:min-w-0 tw:wrap-anywhere",
376
+ children
377
+ })
378
+ ]
379
+ });
380
+ //#endregion
381
+ //#region src/components/Combobox/ComboboxList.tsx
382
+ const ComboboxList = ({ ref, className, ...props }) => /* @__PURE__ */ jsx(Combobox.List, {
383
+ ref,
384
+ className: cn("tw:no-scrollbar", "tw:scroll-py-1", "tw:overflow-y-auto", "tw:overscroll-contain", "tw:min-h-0", "tw:flex-1", "tw:p-2", "tw:data-[empty]:p-0", "tw:group-data-[variant=default]/combobox-content:p-0", className),
385
+ ...props
386
+ });
387
+ //#endregion
388
+ //#region src/components/Combobox/ComboboxOpenButton.tsx
389
+ /**
390
+ * The button that opens the popup: a chevron, or a spinner while the options are loading.
391
+ *
392
+ * - The spinner swaps in for the icon inside the one button, which costs the control no width, so
393
+ * the input keeps its room whether or not a request is in flight.
394
+ * - The button keeps opening the popup while loading, so a list that already has items stays
395
+ * reachable.
396
+ */
397
+ const ComboboxOpenButton = ({ disabled, isLoading }) => /* @__PURE__ */ jsx(InputGroup.Button, {
398
+ disabled,
399
+ asChild: true,
400
+ "aria-label": "Open combobox",
401
+ children: /* @__PURE__ */ jsx(Combobox.Trigger, { children: isLoading ? /* @__PURE__ */ jsx(Loader2, { className: "tw:size-4 tw:animate-spin" }) : /* @__PURE__ */ jsx(ChevronDown, { className: "tw:size-4" }) })
402
+ });
403
+ //#endregion
404
+ //#region src/components/Combobox/Group/ComboboxGroupHeader.tsx
405
+ /** Indent of a header's children (chevron + gap); the legacy variant pads its rows wider. */
406
+ const GROUP_CHILD_INDENT = "tw:pl-8 tw:group-data-[variant=default]/combobox-content:pl-10";
407
+ /**
408
+ * A group's own row, standing for the whole group.
409
+ *
410
+ * - Keyboard-navigable like any other row.
411
+ * - The chevron toggles by pointer; `ArrowRight` / `ArrowLeft` are handled by the owning control.
412
+ */
413
+ const ComboboxGroupHeader = ({ value, children, expanded, onToggle, checked, indicator = "check", disabled }) => {
414
+ const toggleFromChevron = (event) => {
415
+ event.stopPropagation();
416
+ onToggle();
417
+ };
418
+ return /* @__PURE__ */ jsx(ComboboxItem, {
419
+ value,
420
+ disabled,
421
+ "aria-description": expanded === void 0 ? void 0 : expanded ? "Expanded" : "Collapsed",
422
+ ...checked !== void 0 && { "aria-selected": checked === true },
423
+ indicator: "none",
424
+ className: "tw:font-medium",
425
+ onClick: checked !== void 0 ? void 0 : (event) => {
426
+ event.preventBaseUIHandler();
427
+ onToggle();
428
+ },
429
+ icon: /* @__PURE__ */ jsxs(Fragment$1, { children: [expanded !== void 0 && /* @__PURE__ */ jsx("span", {
430
+ "aria-hidden": true,
431
+ "data-slot": "combobox-group-toggle",
432
+ className: "tw:inline-flex tw:shrink-0",
433
+ onClick: toggleFromChevron,
434
+ children: /* @__PURE__ */ jsx(ChevronRight, { className: cn("tw:transition-transform", expanded && "tw:rotate-90") })
435
+ }), checked !== void 0 && /* @__PURE__ */ jsx(GroupIndicator, {
436
+ checked,
437
+ variant: indicator
438
+ })] }),
439
+ children
440
+ });
441
+ };
442
+ /**
443
+ * A group row's selection mark: all, some, or none.
444
+ *
445
+ * - Base UI's indicator is binary, so the row draws its own.
446
+ * - `checkbox` matches the children's leading box; `check` their trailing mark.
447
+ */
448
+ const GroupIndicator = ({ checked, variant }) => {
449
+ const Mark = checked === "mixed" ? Minus : Check;
450
+ if (variant === "checkbox") return /* @__PURE__ */ jsx("span", {
451
+ "aria-hidden": true,
452
+ "data-checked": checked,
453
+ className: cn("tw:inline-flex tw:size-4 tw:shrink-0 tw:items-center tw:justify-center", "tw:rounded tw:border tw:border-input tw:bg-background", checked !== false && "tw:border-primary tw:bg-primary"),
454
+ children: checked !== false && /* @__PURE__ */ jsx(Mark, { className: "tw:size-3 tw:stroke-[4px] tw:!text-primary-foreground" })
455
+ });
456
+ if (checked === false) return null;
457
+ return /* @__PURE__ */ jsx(Mark, {
458
+ "aria-hidden": true,
459
+ className: cn("tw:pointer-events-none tw:absolute tw:right-2 tw:size-4", "tw:group-data-[variant=default]/combobox-content:right-4", "tw:group-data-[highlighted]/combobox-item:text-accent-foreground", "tw:group-data-[variant=default]/combobox-content:group-data-[highlighted]/combobox-item:text-secondary-foreground")
460
+ });
461
+ };
462
+ //#endregion
463
+ //#region src/components/Combobox/Group/ComboboxGroup.tsx
464
+ const ComboboxGroup = ({ ref, className, children, ...props }) => /* @__PURE__ */ jsx(Combobox.Group, {
465
+ ref,
466
+ className: cn("tw:flex tw:flex-col tw:gap-0.5", className),
467
+ ...props,
468
+ children
469
+ });
470
+ //#endregion
471
+ //#region src/components/Combobox/Group/ComboboxGroupLabel.tsx
472
+ const ComboboxGroupLabel = ({ ref, className, ...props }) => /* @__PURE__ */ jsx(Combobox.GroupLabel, {
473
+ ...props,
474
+ ref,
475
+ className: cn("tw:px-2", "tw:py-1.5", "tw:text-xs", "tw:font-medium", "tw:leading-none", "tw:text-muted-foreground", "tw:group-data-[variant=default]/combobox-content:uppercase", "tw:group-data-[variant=default]/combobox-content:font-semibold", "tw:group-data-[variant=default]/combobox-content:tracking-wider", "tw:group-data-[variant=default]/combobox-content:text-[11px]", "tw:group-data-[variant=default]/combobox-content:text-black", "tw:group-data-[variant=default]/combobox-content:pt-2", "tw:group-data-[variant=default]/combobox-content:px-4", className)
476
+ });
477
+ //#endregion
478
+ //#region src/components/Combobox/Group/ComboboxCollection.tsx
479
+ const ComboboxCollection = ({ ...props }) => /* @__PURE__ */ jsx(Combobox.Collection, {
480
+ "data-slot": "combobox-collection",
481
+ ...props
482
+ });
483
+ //#endregion
484
+ //#region src/components/Combobox/ComboboxFullList.tsx
485
+ /** A Base UI collection child whose rows are keyed by position, as React matches unkeyed ones. */
486
+ const keyed = (render) => (entry, index) => /* @__PURE__ */ jsx(Fragment, { children: render(entry) }, index);
487
+ /** A combobox list that renders every row; `ComboboxVirtualList` renders only those in view. */
488
+ const ComboboxFullList = ({ renderItem, renderGroupItem, groupToLabel }) => {
489
+ const renderEntry = (entry) => {
490
+ if (!isGroupItem(entry)) return renderItem(entry);
491
+ const [lead] = entry.items;
492
+ if (isLooseItem(lead)) return renderItem(lead);
493
+ const label = groupToLabel?.(entry);
494
+ return /* @__PURE__ */ jsxs(ComboboxGroup, {
495
+ items: entry.items,
496
+ children: [label && /* @__PURE__ */ jsx(ComboboxGroupLabel, { children: label }), /* @__PURE__ */ jsx(ComboboxCollection, { children: keyed(renderGroupItem) })]
497
+ });
498
+ };
499
+ return /* @__PURE__ */ jsx(ComboboxList, { children: keyed(renderEntry) });
500
+ };
501
+ //#endregion
502
+ //#region src/components/Combobox/ComboboxLoadingEmpty.tsx
503
+ /**
504
+ * What the empty slot says while the options are loading, in place of the `empty` node.
505
+ *
506
+ * - `Combobox.Empty` is a polite live region, so the switch to this text and back to `empty` once
507
+ * the request settles is announced.
508
+ * - Base UI renders the slot only while the list has nothing to show, so a list that already has
509
+ * items keeps showing them.
510
+ */
511
+ const ComboboxLoadingEmpty = () => /* @__PURE__ */ jsxs("span", {
512
+ className: "tw:inline-flex tw:items-center tw:gap-2",
513
+ children: [/* @__PURE__ */ jsx(Loader2, { className: "tw:size-4 tw:animate-spin" }), "Loading…"]
514
+ });
515
+ //#endregion
516
+ //#region src/components/Combobox/ComboboxVirtualList.tsx
517
+ /** Rows rendered past each edge of the viewport, so a fast scroll does not paint blank. */
518
+ const OVERSCAN = 12;
519
+ const kindOf = (row) => row.type === "label" ? "label" : row.padded ? "padded" : "plain";
520
+ /**
521
+ * Base UI's filtered entries as one row per option or group label, in list order.
522
+ *
523
+ * - A group whose lone item is the create row or Select All stays outside any group, as the fully
524
+ * rendered list draws it.
525
+ * - `aria-setsize` counts the options sharing a parent, as a browser does for a rendered list.
526
+ */
527
+ const toRows = (entries, groupToLabel) => {
528
+ const rows = [];
529
+ const rowOfItem = [];
530
+ const labels = /* @__PURE__ */ new Map();
531
+ const push = (item, row) => {
532
+ rows.push({
533
+ type: "item",
534
+ item,
535
+ index: rowOfItem.length,
536
+ ...row
537
+ });
538
+ rowOfItem.push(rows.length - 1);
539
+ };
540
+ if (!isGroupItems(entries)) {
541
+ const items = entries.filter((entry) => !isGroupItem(entry));
542
+ items.forEach((item, i) => push(item, {
543
+ padded: false,
544
+ setSize: items.length,
545
+ posInSet: i + 1
546
+ }));
547
+ return {
548
+ rows,
549
+ rowOfItem,
550
+ labels
551
+ };
552
+ }
553
+ const isLoose = (group) => isLooseItem(group.items[0]);
554
+ const looseCount = entries.filter(isLoose).length;
555
+ let loosePosition = 0;
556
+ entries.forEach((entry, groupIndex) => {
557
+ if (isLoose(entry)) {
558
+ for (const item of entry.items) push(item, {
559
+ padded: false,
560
+ setSize: looseCount,
561
+ posInSet: ++loosePosition
562
+ });
563
+ return;
564
+ }
565
+ const group = `group-${groupIndex}`;
566
+ const label = groupToLabel?.(entry);
567
+ if (label) {
568
+ labels.set(group, label);
569
+ rows.push({
570
+ type: "label",
571
+ group,
572
+ padded: false
573
+ });
574
+ }
575
+ entry.items.forEach((item, i) => push(item, {
576
+ group,
577
+ padded: Boolean(label) || i > 0,
578
+ setSize: entry.items.length,
579
+ posInSet: i + 1
580
+ }));
581
+ });
582
+ return {
583
+ rows,
584
+ rowOfItem,
585
+ labels
586
+ };
587
+ };
588
+ /**
589
+ * A combobox list that puts only the rows in view into the DOM.
590
+ *
591
+ * - Lists what Base UI filtered, so search and keyboard navigation cover every option.
592
+ * - A group keeps its `role="group"` wrapper, and its name while its label row is out of view.
593
+ * - Rows keep their own height; a row not yet rendered is estimated from those that were.
594
+ */
595
+ const ComboboxVirtualList = ({ renderItem, renderGroupItem, groupToLabel, ref }) => {
596
+ const [listElement, attachList] = useElement();
597
+ const scrollMargin = useMemo(() => listElement ? Number.parseFloat(getComputedStyle(listElement).paddingTop) || 0 : 0, [listElement]);
598
+ const filteredItems = Combobox.useFilteredItems();
599
+ const { rows, rowOfItem, labels } = useMemo(() => toRows(filteredItems, groupToLabel), [filteredItems, groupToLabel]);
600
+ const sizesRef = useRef({});
601
+ const [rem] = useState(() => Number.parseFloat(getComputedStyle(document.documentElement).fontSize) || 0);
602
+ const virtualizer = useVirtualizer({
603
+ count: rows.length,
604
+ getScrollElement: () => listElement,
605
+ estimateSize: (index) => {
606
+ const row = rows[index];
607
+ const measured = sizesRef.current;
608
+ return (row && measured[kindOf(row)]) ?? measured.plain ?? measured.padded ?? rem;
609
+ },
610
+ measureElement: (element, entry, instance) => {
611
+ const size = measureElement(element, entry, instance);
612
+ const row = rows[instance.indexFromElement(element)];
613
+ if (row) {
614
+ const kind = kindOf(row);
615
+ sizesRef.current[kind] = Math.min(sizesRef.current[kind] ?? size, size);
616
+ }
617
+ return size;
618
+ },
619
+ observeElementRect: (instance, callback) => observeElementRect(instance, (rect) => callback({
620
+ ...rect,
621
+ height: Math.min(rect.height, instance.targetWindow?.innerHeight ?? rect.height)
622
+ })),
623
+ overscan: OVERSCAN,
624
+ scrollMargin
625
+ });
626
+ useImperativeHandle(ref, () => ({ scrollToItem: (index) => {
627
+ const row = rowOfItem[index];
628
+ if (row !== void 0) virtualizer.scrollToIndex(row, { align: "auto" });
629
+ } }));
630
+ const segments = [];
631
+ for (const virtualItem of virtualizer.getVirtualItems()) {
632
+ const group = rows[virtualItem.index]?.group;
633
+ const last = segments.at(-1);
634
+ if (group !== void 0 && last?.group === group) last.items.push(virtualItem);
635
+ else segments.push({
636
+ group,
637
+ start: virtualItem.start,
638
+ items: [virtualItem]
639
+ });
640
+ }
641
+ const placeAt = (start) => ({
642
+ position: "absolute",
643
+ top: 0,
644
+ left: 0,
645
+ width: "100%",
646
+ transform: `translateY(${start - scrollMargin}px)`
647
+ });
648
+ const renderRow = ({ key, index, start }) => {
649
+ const row = rows[index];
650
+ if (!row) return null;
651
+ return /* @__PURE__ */ jsx("div", {
652
+ "data-index": index,
653
+ ref: virtualizer.measureElement,
654
+ className: row.padded ? "tw:pt-0.5" : void 0,
655
+ style: row.group === void 0 ? placeAt(start) : void 0,
656
+ children: row.type === "label" ? /* @__PURE__ */ jsx(ComboboxGroupLabel, { children: labels.get(row.group ?? "") }) : /* @__PURE__ */ jsx(VirtualRowContext, {
657
+ value: {
658
+ index: row.index,
659
+ "aria-setsize": row.setSize,
660
+ "aria-posinset": row.posInSet
661
+ },
662
+ children: row.group === void 0 ? renderItem(row.item) : renderGroupItem(row.item)
663
+ })
664
+ }, key);
665
+ };
666
+ return /* @__PURE__ */ jsx(ComboboxList, {
667
+ ref: attachList,
668
+ children: /* @__PURE__ */ jsx("div", {
669
+ style: {
670
+ position: "relative",
671
+ width: "100%",
672
+ height: `${virtualizer.getTotalSize()}px`
673
+ },
674
+ children: segments.flatMap(({ group, start, items }) => {
675
+ if (group === void 0) return items.map(renderRow);
676
+ const label = labels.get(group);
677
+ const labelInView = items.some((item) => rows[item.index]?.type === "label");
678
+ return /* @__PURE__ */ jsxs(ComboboxGroup, {
679
+ className: "tw:gap-0",
680
+ style: placeAt(start),
681
+ children: [label && !labelInView && /* @__PURE__ */ jsx(ComboboxGroupLabel, {
682
+ className: "tw:sr-only",
683
+ children: label
684
+ }), items.map(renderRow)]
685
+ }, group);
686
+ })
687
+ })
688
+ });
689
+ };
690
+ //#endregion
691
+ //#region src/components/Combobox/ComboboxOptions.tsx
692
+ /**
693
+ * What a combobox popup lists: every row, the empty slot, and the loading text in it.
694
+ *
695
+ * - The list is windowed while `virtualized`, fully rendered otherwise.
696
+ */
697
+ const ComboboxOptions = ({ isLoading, empty, virtualized, listRef, groupHeaders, groupToLabel, indicator, itemToLabel, itemToIcon, itemToDisabled, createLabel, groupRowState }) => {
698
+ const renderItem = (item, className) => {
699
+ if (isSelectAllItem(item)) return /* @__PURE__ */ jsx(ComboboxItem, {
700
+ value: item,
701
+ indicator: "checkbox",
702
+ children: item.label
703
+ });
704
+ if (isCreatableItem(item)) return /* @__PURE__ */ jsx(ComboboxItem, {
705
+ value: item,
706
+ icon: /* @__PURE__ */ jsx(Plus, {}),
707
+ children: createLabel(item.value)
708
+ });
709
+ return /* @__PURE__ */ jsx(ComboboxItem, {
710
+ value: item,
711
+ indicator,
712
+ icon: itemToIcon(item),
713
+ disabled: itemToDisabled(item),
714
+ className,
715
+ children: itemToLabel(item)
716
+ });
717
+ };
718
+ const renderGroupItem = (item) => {
719
+ const header = groupHeaders.headerOf(item);
720
+ if (!header) return renderItem(item, groupHeaders.collapsible ? GROUP_CHILD_INDENT : void 0);
721
+ const state = groupRowState?.(item);
722
+ return /* @__PURE__ */ jsx(ComboboxGroupHeader, {
723
+ value: item,
724
+ expanded: groupHeaders.collapsible ? !header.collapsed : void 0,
725
+ onToggle: () => groupHeaders.toggle(header.index),
726
+ checked: state?.checked,
727
+ indicator,
728
+ disabled: state?.disabled ?? false,
729
+ children: groupToLabel(header.group)
730
+ });
731
+ };
732
+ const labelOfGroup = useCallback((group) => isEntriesGroup(group) ? void 0 : groupToLabel(group), [groupToLabel]);
733
+ const listProps = {
734
+ renderItem,
735
+ renderGroupItem,
736
+ groupToLabel: groupHeaders.enabled ? void 0 : labelOfGroup
737
+ };
738
+ return /* @__PURE__ */ jsxs(Fragment$1, { children: [/* @__PURE__ */ jsx(ComboboxEmpty, { children: isLoading ? /* @__PURE__ */ jsx(ComboboxLoadingEmpty, {}) : empty }), virtualized ? /* @__PURE__ */ jsx(ComboboxVirtualList, {
739
+ ref: listRef,
740
+ ...listProps
741
+ }) : /* @__PURE__ */ jsx(ComboboxFullList, { ...listProps })] });
742
+ };
743
+ //#endregion
744
+ //#region src/components/Combobox/ComboboxRoot.tsx
745
+ const ComboboxRoot = (props) => /* @__PURE__ */ jsx(Combobox.Root, { ...props });
746
+ //#endregion
747
+ //#region src/components/Combobox/ComboboxSeparator.tsx
748
+ const ComboboxSeparator = ({ ref, className, ...props }) => /* @__PURE__ */ jsx(Combobox.Separator, {
749
+ ref,
750
+ className: cn("tw:mx-2 tw:my-1 tw:h-px tw:bg-border", className),
751
+ ...props
752
+ });
753
+ //#endregion
754
+ //#region src/components/Combobox/createdItems.ts
755
+ /**
756
+ * `list` followed by the values `allowCreate` minted, as entries of it; stable while both are.
757
+ *
758
+ * - Listed so a created value stays pickable: Base UI drops a value missing from `items`.
759
+ */
760
+ const useWithCreated = (list, created) => useMemo(() => created.length === 0 ? list : [...list, ...toEntries(list, created)], [list, created]);
761
+ //#endregion
762
+ //#region src/components/Combobox/groupHeaders.ts
763
+ /**
764
+ * Gives each group a row (rendered with `ComboboxGroupHeader`) and collapses groups on request.
765
+ *
766
+ * - The row is the group's first item, so Base UI navigates, filters and presses it.
767
+ * - A collapsed group hands Base UI only its row: Base UI never filters an empty query.
768
+ * - A query shows every match regardless of collapse; a row shows only while its group matches.
769
+ * - Collapse state is keyed by a group's position in `items`.
770
+ */
771
+ const useGroupHeaders = ({ items, collapsible, selectable, query, matches, groupToValue, isItemEqualToValue }) => {
772
+ const [collapsedIndexes, setCollapsedIndexes] = useState(() => /* @__PURE__ */ new Set());
773
+ const highlightedRef = useRef(void 0);
774
+ const enabled = (collapsible || selectable) && isGroupItems(items);
775
+ const headers = /* @__PURE__ */ new Map();
776
+ const listItems = !enabled ? items : items.map((group, index) => {
777
+ const collapsed = collapsible && query === "" && collapsedIndexes.has(index);
778
+ const matching = query === "" ? group.items : group.items.filter(matches);
779
+ const row = groupToValue ? groupToValue(group) : createGroupHeaderItem(index);
780
+ headers.set(row, {
781
+ group,
782
+ index,
783
+ collapsed,
784
+ matching
785
+ });
786
+ return {
787
+ ...group,
788
+ items: collapsed ? [row] : [row, ...group.items]
789
+ };
790
+ });
791
+ const toggle = (index) => setCollapsedIndexes((prev) => {
792
+ const next = new Set(prev);
793
+ if (!next.delete(index)) next.add(index);
794
+ return next;
795
+ });
796
+ const headerOf = (item) => headers.get(item);
797
+ const highlightedHeader = () => {
798
+ const item = highlightedRef.current;
799
+ if (item === void 0) return void 0;
800
+ const header = headers.get(item);
801
+ if (header) return header;
802
+ for (const [row, entry] of headers) if (isItemEqualToValue(row, item)) return entry;
803
+ };
804
+ return {
805
+ enabled,
806
+ /** `items` with group rows added and collapsed groups' items removed. */
807
+ items: listItems,
808
+ collapsible,
809
+ headerOf,
810
+ toggle,
811
+ /** `filter` verdict for a group row (matches while its group does); `undefined` otherwise. */
812
+ filterHeader: (item) => {
813
+ const header = headerOf(item);
814
+ return header ? header.matching.length > 0 : void 0;
815
+ },
816
+ /** For the root's `onItemHighlighted`: which row the keyboard would toggle. */
817
+ onItemHighlighted: (item) => {
818
+ highlightedRef.current = item;
819
+ },
820
+ /** For the root's `onOpenChange`: a closed popup has no highlighted row to toggle. */
821
+ onOpenChange: (open) => {
822
+ if (!open) highlightedRef.current = void 0;
823
+ },
824
+ /**
825
+ * For the input's `onKeyDown`: `ArrowRight` / `ArrowLeft` expand / collapse the highlighted row.
826
+ * Only without a query; also stops Base UI moving to the chips.
827
+ */
828
+ onInputKeyDown: (event) => {
829
+ if (!collapsible || query !== "") return;
830
+ if (event.key !== "ArrowRight" && event.key !== "ArrowLeft") return;
831
+ const header = highlightedHeader();
832
+ if (!header) return;
833
+ event.preventDefault();
834
+ event.preventBaseUIHandler();
835
+ if (header.collapsed === (event.key === "ArrowRight")) toggle(header.index);
836
+ }
837
+ };
838
+ };
839
+ //#endregion
840
+ //#region src/components/Combobox/query.ts
841
+ /**
842
+ * The text a combobox's input takes, with `inputValueSanitizer` applied to what the user typed.
843
+ *
844
+ * - `input-change` is the only reason carrying typed text. Every other one carries text the
845
+ * control itself put there — an item's label, or the empty string a clear leaves — which a
846
+ * sanitizer aimed at typed characters would mangle.
847
+ * - A sanitizer sees the whole text on every keystroke, so it has to rewrite rather than drop:
848
+ * one calling `trim()` swallows the space before the next word.
849
+ */
850
+ const sanitizeTypedInput = (next, reason, sanitizer) => reason === "input-change" ? sanitizer?.(next) ?? next : next;
851
+ /**
852
+ * Reports the query the list filters by to the control's owner.
853
+ *
854
+ * - Fires from an effect rather than the input handler because `query` is derived: in `Combobox`
855
+ * a selection showing its own label empties it without a keystroke, so the handler never sees
856
+ * every value it takes.
857
+ * - The mount render reports too, so an owner fetching on the query needs no first fetch of its
858
+ * own.
859
+ */
860
+ const useQueryReporter = (query, onQueryChange) => {
861
+ const report = useStableCallback(onQueryChange ?? noop);
862
+ useEffect(() => {
863
+ report(query);
864
+ }, [query, report]);
865
+ };
866
+ //#endregion
867
+ //#region src/components/Combobox/Combobox.tsx
868
+ const acceptAll = () => true;
869
+ const NO_ITEMS = [];
870
+ const Combobox$1 = ({ items: _itemsProp, value, defaultValue, onValueChange, itemToStringLabel: itemToStringLabelProp, itemToStringValue, disabled, filter, inputRef, onOpenChangeComplete, isItemEqualToValue: isItemEqualToValueProp, variant, sizing, focusVariant, state, placeholder, allowClear = true, name, onBlur, "aria-describedby": ariaDescribedBy, "aria-invalid": ariaInvalid, "aria-required": ariaRequired, className, empty = "No items", isLoading = false, indicator, prefix, virtualized = false, inputValueSanitizer, onQueryChange, allowCreate, queryToCreateValue, queryToCreateLabel = defaultQueryToCreateLabel, canCreate, itemToDisabled = defaultItemToDisabled, itemToLabel: itemToLabelProp, itemToIcon = defaultItemToIcon, itemToSelectedLabel, groupToLabel = defaultGroupToLabel, collapsibleGroups = false, groupToValue, ...accessibleName }) => {
871
+ /** An item's string identity: input text, default search subject, accessible value. */
872
+ const labelOf = useCallback((item) => itemToStringLabelProp?.(item) ?? toLabel(item), [itemToStringLabelProp]);
873
+ const itemToLabel = itemToLabelProp ?? defaultRowLabel(itemToStringLabelProp);
874
+ const [typedText, setTypedText] = useState(null);
875
+ const [uncontrolledValue, setUncontrolledValue] = useState(defaultValue ?? null);
876
+ const selectedItem = value !== void 0 ? value : uncontrolledValue;
877
+ const selectedLabel = selectedItem == null ? null : labelOf(selectedItem);
878
+ const isShowingSelection = selectedLabel !== null && (typedText === null || typedText === selectedLabel);
879
+ const inputValue = typedText ?? selectedLabel ?? "";
880
+ const selectedNode = itemToSelectedLabel && selectedItem != null && isShowingSelection ? itemToSelectedLabel(selectedItem) : null;
881
+ const query = (isShowingSelection ? "" : typedText ?? "").trim();
882
+ const queryLower = query.toLowerCase();
883
+ useQueryReporter(query, onQueryChange);
884
+ const busy = isLoading || void 0;
885
+ const itemsProp = _itemsProp ?? NO_ITEMS;
886
+ const sampleItem = flatItems(itemsProp)[0];
887
+ const isItemEqualToValue = useItemEquality(isItemEqualToValueProp);
888
+ const [createdItems, setCreatedItems] = useState([]);
889
+ const [createdItem] = createdItems;
890
+ const itemsHoldCreated = useMemo(() => createdItem !== void 0 && flatItems(itemsProp).some((listed) => isItemEqualToValue(listed, createdItem)), [
891
+ createdItem,
892
+ itemsProp,
893
+ isItemEqualToValue
894
+ ]);
895
+ const created = createdItem !== void 0 && !itemsHoldCreated && selectedItem != null && isItemEqualToValue(createdItem, selectedItem) ? createdItems : NO_ITEMS;
896
+ const itemToStringLabel = useCallback((value) => {
897
+ if (isCreatableItem(value)) return value.value;
898
+ if (isGroupHeaderItem(value)) return value.label;
899
+ return labelOf(value);
900
+ }, [labelOf]);
901
+ const { contains } = Combobox.useFilter();
902
+ const itemFilter = filter ?? contains;
903
+ const groupHeaders = useGroupHeaders({
904
+ items: itemsProp,
905
+ collapsible: collapsibleGroups,
906
+ selectable: groupToValue !== void 0,
907
+ query,
908
+ matches: (item) => itemFilter(item, query, itemToStringLabel),
909
+ groupToValue,
910
+ isItemEqualToValue
911
+ });
912
+ const allItems = useWithCreated(itemsProp, created);
913
+ const listItems = useWithCreated(groupHeaders.items, created);
914
+ const showCreateRow = (() => {
915
+ if (!allowCreate || disabled || !query) return false;
916
+ return !flatItems(allItems).some((item) => labelOf(item).toLowerCase() === queryLower) && (!canCreate || canCreate(query));
917
+ })();
918
+ const items = useMemo(() => showCreateRow ? [...listItems, ...toEntries(listItems, [createCreatableItem(query)])] : listItems, [
919
+ showCreateRow,
920
+ listItems,
921
+ query
922
+ ]);
923
+ const filterHeader = groupHeaders.enabled ? groupHeaders.filterHeader : void 0;
924
+ const queryFilter = useMemo(() => {
925
+ if (filterHeader) return (item, query, itemToString) => filterHeader(item) ?? (isCreatableItem(item) || itemFilter(item, query, itemToString));
926
+ if (!allowCreate || !filter) return filter;
927
+ return (item, query, itemToString) => isCreatableItem(item) || filter(item, query, itemToString);
928
+ }, [
929
+ filterHeader,
930
+ allowCreate,
931
+ filter,
932
+ itemFilter
933
+ ]);
934
+ const wrappedFilter = isShowingSelection ? acceptAll : queryFilter;
935
+ const wrappedItemToStringValue = useCreatableStringValue(itemToStringValue, allowCreate);
936
+ const handleInputValueChange = (next, { reason }) => {
937
+ if (reason !== "input-change") {
938
+ setTypedText(null);
939
+ return;
940
+ }
941
+ const typed = inputValueSanitizer?.(next) ?? next;
942
+ setTypedText(typed === "" || typed.trim() !== "" ? typed : null);
943
+ };
944
+ const handleOpenChange = (open) => {
945
+ if (!open) setTypedText(null);
946
+ groupHeaders.onOpenChange(open);
947
+ };
948
+ const handleOnValueChange = (next, event) => {
949
+ if (isCreatableItem(next)) {
950
+ const newValue = defaultToCreateValue(next.value, sampleItem, queryToCreateValue);
951
+ setCreatedItems([newValue]);
952
+ setUncontrolledValue(newValue);
953
+ onValueChange?.(newValue, event);
954
+ return;
955
+ }
956
+ setUncontrolledValue(next);
957
+ onValueChange?.(next, event);
958
+ };
959
+ const virtualList = useVirtualList(allItems, virtualized);
960
+ const comboboxInput = /* @__PURE__ */ jsx(ComboboxInput, {
961
+ ...forwardAccessibleName(accessibleName),
962
+ name,
963
+ onBlur,
964
+ "aria-describedby": ariaDescribedBy,
965
+ "aria-invalid": ariaInvalid,
966
+ "aria-required": ariaRequired,
967
+ "aria-busy": busy,
968
+ placeholder,
969
+ disabled,
970
+ onKeyDown: groupHeaders.onInputKeyDown,
971
+ className: cn(selectedNode != null && "tw:text-transparent tw:caret-foreground tw:selection:bg-transparent")
972
+ });
973
+ return /* @__PURE__ */ jsxs(Combobox.Root, {
974
+ items,
975
+ autoHighlight: true,
976
+ value,
977
+ defaultValue,
978
+ filter: wrappedFilter,
979
+ onValueChange: handleOnValueChange,
980
+ itemToStringLabel,
981
+ isItemEqualToValue,
982
+ itemToStringValue: wrappedItemToStringValue,
983
+ onOpenChange: handleOpenChange,
984
+ onOpenChangeComplete,
985
+ disabled,
986
+ inputRef,
987
+ inputValue,
988
+ onInputValueChange: handleInputValueChange,
989
+ virtualized: virtualList.enabled,
990
+ onItemHighlighted: (item, details) => {
991
+ groupHeaders.onItemHighlighted(item);
992
+ virtualList.onItemHighlighted(details);
993
+ },
994
+ children: [/* @__PURE__ */ jsxs(ComboboxInputGroup, {
995
+ variant,
996
+ sizing,
997
+ state,
998
+ focusVariant,
999
+ className,
1000
+ children: [itemToSelectedLabel != null ? /* @__PURE__ */ jsxs("div", {
1001
+ className: "tw:relative tw:flex tw:min-w-0 tw:flex-1",
1002
+ children: [(prefix != null || selectedNode != null) && /* @__PURE__ */ jsxs(InputGroup.Addon, {
1003
+ align: "inline-start",
1004
+ className: cn(selectedNode != null && "tw:pointer-events-none tw:absolute tw:inset-y-0 tw:left-0 tw:max-w-full tw:overflow-hidden"),
1005
+ children: [prefix, selectedNode]
1006
+ }), comboboxInput]
1007
+ }) : /* @__PURE__ */ jsxs(Fragment$1, { children: [prefix != null && /* @__PURE__ */ jsx(InputGroup.Addon, {
1008
+ align: "inline-start",
1009
+ children: prefix
1010
+ }), comboboxInput] }), /* @__PURE__ */ jsxs(InputGroup.Addon, {
1011
+ align: "inline-end",
1012
+ children: [allowClear && /* @__PURE__ */ jsx(ComboboxClearButton, { disabled }), /* @__PURE__ */ jsx(ComboboxOpenButton, {
1013
+ disabled,
1014
+ isLoading
1015
+ })]
1016
+ })]
1017
+ }), /* @__PURE__ */ jsx(ComboboxContent, { children: /* @__PURE__ */ jsx(ComboboxOptions, {
1018
+ isLoading,
1019
+ empty,
1020
+ virtualized: virtualList.enabled,
1021
+ listRef: virtualList.ref,
1022
+ groupHeaders,
1023
+ groupToLabel,
1024
+ indicator,
1025
+ itemToLabel: (item) => itemToLabel(item, query),
1026
+ itemToIcon,
1027
+ itemToDisabled,
1028
+ createLabel: queryToCreateLabel,
1029
+ groupRowState: groupToValue ? (row) => ({
1030
+ checked: selectedItem != null && isItemEqualToValue(row, selectedItem),
1031
+ disabled: itemToDisabled(row)
1032
+ }) : void 0
1033
+ }) })]
1034
+ });
1035
+ };
1036
+ Combobox$1.Root = ComboboxRoot;
1037
+ Combobox$1.InputGroup = ComboboxInputGroup;
1038
+ Combobox$1.Input = ComboboxInput;
1039
+ Combobox$1.Content = ComboboxContent;
1040
+ Combobox$1.List = ComboboxList;
1041
+ Combobox$1.Item = ComboboxItem;
1042
+ Combobox$1.Empty = ComboboxEmpty;
1043
+ Combobox$1.Value = Combobox.Value;
1044
+ Combobox$1.Separator = ComboboxSeparator;
1045
+ Combobox$1.Group = ComboboxGroup;
1046
+ Combobox$1.GroupLabel = ComboboxGroupLabel;
1047
+ Combobox$1.GroupList = ComboboxCollection;
1048
+ Combobox$1.Chips = ComboboxChips;
1049
+ Combobox$1.Chip = ComboboxChip;
1050
+ //#endregion
1051
+ //#region src/components/Combobox/matchByWords.ts
1052
+ const toWords = (query) => query.toLowerCase().split(/\s+/).filter(Boolean);
1053
+ const isFilled = (text) => text !== null && text !== void 0 && text !== "";
1054
+ const isTextList = (texts) => Array.isArray(texts);
1055
+ /**
1056
+ * Whether every word of `query` occurs in some of `texts`, ignoring case.
1057
+ *
1058
+ * - Words match by containment, across texts, in any order; an empty query matches everything.
1059
+ * - Plain lowercased `includes`, not `Intl.Collator`: `Muller` does not find `Müller`.
1060
+ * - Not interchangeable with kbc-ui's own `matchByWords` (single-space split, keeps empty texts).
1061
+ * - A `filter` for when the searchable text is not the label; else use {@link filterByWords}.
1062
+ */
1063
+ const matchByWords = (texts, query) => {
1064
+ const words = toWords(query);
1065
+ if (words.length === 0) return true;
1066
+ const haystacks = (isTextList(texts) ? texts : [texts]).filter(isFilled).map((text) => String(text).toLowerCase());
1067
+ return words.every((word) => haystacks.some((haystack) => haystack.includes(word)));
1068
+ };
1069
+ /**
1070
+ * `filter` matching the item's displayed label word by word: `filter={filterByWords}`.
1071
+ *
1072
+ * - Generic, so it keeps the combobox's item type inference.
1073
+ * - Label: `itemToStringLabel` if passed, else the item's `label`, `value`, or string form.
1074
+ * - Only the label is searched, by design: never match what the user cannot see.
1075
+ * - Close over {@link matchByWords} to search other fields.
1076
+ */
1077
+ const filterByWords = (item, query, itemToString) => matchByWords(itemToString?.(item) ?? toLabel(item), query);
1078
+ //#endregion
1079
+ export { BLANK_VALUES, Combobox$1 as Combobox, ComboboxClearButton, ComboboxOpenButton, ComboboxOptions, createCreatableItem, defaultGroupToLabel, defaultItemToDisabled, defaultItemToIcon, defaultQueryToCreateLabel, defaultRowLabel, defaultToCreateValue, filterByWords, flatItems, isCreatableItem, isGroupHeaderItem, isSelectAllItem, matchByWords, namedLabel, sanitizeTypedInput, selectAllItem, toEntries, toLabel, useCreatableStringValue, useGroupHeaders, useItemEquality, useQueryReporter, useVirtualList, useWithCreated, withBlankValues };
1080
+
1081
+ //# sourceMappingURL=Combobox-C_MTpQ1l.js.map