sheer-ui 0.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 (635) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +42 -0
  3. package/package.json +369 -0
  4. package/src/lib/VENDORED.md +109 -0
  5. package/src/lib/assets/ui.css +733 -0
  6. package/src/lib/blocks/Appearance.svelte +104 -0
  7. package/src/lib/blocks/BoundaryFailed.svelte +33 -0
  8. package/src/lib/blocks/ErrorPage.svelte +65 -0
  9. package/src/lib/blocks/Footer.svelte +71 -0
  10. package/src/lib/blocks/Header.svelte +67 -0
  11. package/src/lib/blocks/Heading.svelte +86 -0
  12. package/src/lib/blocks/MobileNav.svelte +169 -0
  13. package/src/lib/blocks/PhoneInput.svelte +68 -0
  14. package/src/lib/blocks/Section.svelte +34 -0
  15. package/src/lib/blocks/alert-modal/AlertModal.svelte +83 -0
  16. package/src/lib/blocks/alert-modal/alert-modal.svelte.ts +81 -0
  17. package/src/lib/blocks/alert-modal/index.ts +2 -0
  18. package/src/lib/blocks/calendar.svelte +155 -0
  19. package/src/lib/blocks/copy-button/copy-button.svelte +43 -0
  20. package/src/lib/blocks/copy-button/copy.svelte.ts +52 -0
  21. package/src/lib/blocks/copy-button/index.ts +2 -0
  22. package/src/lib/blocks/date-picker/date-picker-with-presets.svelte +109 -0
  23. package/src/lib/blocks/date-picker/date-picker.svelte +91 -0
  24. package/src/lib/blocks/date-picker/index.ts +13 -0
  25. package/src/lib/blocks/date-picker/range-date-picker.svelte +128 -0
  26. package/src/lib/blocks/index.ts +12 -0
  27. package/src/lib/blocks/mockups/Browser.svelte +22 -0
  28. package/src/lib/blocks/mockups/Phone.svelte +44 -0
  29. package/src/lib/blocks/mockups/index.ts +2 -0
  30. package/src/lib/blocks/tab-nav/index.ts +1 -0
  31. package/src/lib/blocks/tab-nav/tab-nav.svelte +46 -0
  32. package/src/lib/blocks/time-picker/index.ts +16 -0
  33. package/src/lib/blocks/time-picker/time-input.svelte +118 -0
  34. package/src/lib/blocks/time-picker/time-picker.svelte +93 -0
  35. package/src/lib/blocks/time-picker/time-range-input.svelte +34 -0
  36. package/src/lib/blocks/time-picker/time-range-picker.svelte +180 -0
  37. package/src/lib/blocks/time-picker/time-slots.ts +61 -0
  38. package/src/lib/blocks/user-avatar/UserAvatar.svelte +36 -0
  39. package/src/lib/blocks/user-avatar/index.ts +1 -0
  40. package/src/lib/blocks/verification-code-field/VerificationCodeField.svelte +57 -0
  41. package/src/lib/blocks/verification-code-field/index.ts +1 -0
  42. package/src/lib/components/accordion/accordion.svelte.ts +43 -0
  43. package/src/lib/components/accordion/components/accordion-content.svelte +17 -0
  44. package/src/lib/components/accordion/components/accordion-item.svelte +58 -0
  45. package/src/lib/components/accordion/components/accordion-trigger.svelte +38 -0
  46. package/src/lib/components/accordion/components/accordion.svelte +24 -0
  47. package/src/lib/components/accordion/index.ts +11 -0
  48. package/src/lib/components/accordion/types.ts +19 -0
  49. package/src/lib/components/alert/alert-action.svelte +11 -0
  50. package/src/lib/components/alert/alert-description.svelte +18 -0
  51. package/src/lib/components/alert/alert-title.svelte +18 -0
  52. package/src/lib/components/alert/alert.svelte +37 -0
  53. package/src/lib/components/alert/index.ts +17 -0
  54. package/src/lib/components/avatar/avatar.svelte.ts +154 -0
  55. package/src/lib/components/avatar/components/avatar-fallback.svelte +38 -0
  56. package/src/lib/components/avatar/components/avatar-image.svelte +47 -0
  57. package/src/lib/components/avatar/components/avatar.svelte +57 -0
  58. package/src/lib/components/avatar/index.ts +9 -0
  59. package/src/lib/components/avatar/types.ts +46 -0
  60. package/src/lib/components/badge/badge.svelte +49 -0
  61. package/src/lib/components/badge/index.ts +1 -0
  62. package/src/lib/components/breadcrumb/breadcrumb-ellipsis.svelte +23 -0
  63. package/src/lib/components/breadcrumb/breadcrumb-item.svelte +11 -0
  64. package/src/lib/components/breadcrumb/breadcrumb-link.svelte +32 -0
  65. package/src/lib/components/breadcrumb/breadcrumb-list.svelte +15 -0
  66. package/src/lib/components/breadcrumb/breadcrumb-page.svelte +18 -0
  67. package/src/lib/components/breadcrumb/breadcrumb-separator.svelte +22 -0
  68. package/src/lib/components/breadcrumb/breadcrumb.svelte +10 -0
  69. package/src/lib/components/breadcrumb/index.ts +25 -0
  70. package/src/lib/components/button/button.svelte +55 -0
  71. package/src/lib/components/button/index.ts +12 -0
  72. package/src/lib/components/button/variants.ts +51 -0
  73. package/src/lib/components/calendar/calendar.svelte.ts +1081 -0
  74. package/src/lib/components/calendar/components/calendar-cell.svelte +34 -0
  75. package/src/lib/components/calendar/components/calendar-day.svelte +36 -0
  76. package/src/lib/components/calendar/components/calendar-grid-body.svelte +29 -0
  77. package/src/lib/components/calendar/components/calendar-grid-head.svelte +29 -0
  78. package/src/lib/components/calendar/components/calendar-grid-row.svelte +29 -0
  79. package/src/lib/components/calendar/components/calendar-grid.svelte +29 -0
  80. package/src/lib/components/calendar/components/calendar-head-cell.svelte +29 -0
  81. package/src/lib/components/calendar/components/calendar-header.svelte +29 -0
  82. package/src/lib/components/calendar/components/calendar-heading.svelte +33 -0
  83. package/src/lib/components/calendar/components/calendar-month-select.svelte +50 -0
  84. package/src/lib/components/calendar/components/calendar-next-button.svelte +37 -0
  85. package/src/lib/components/calendar/components/calendar-prev-button.svelte +37 -0
  86. package/src/lib/components/calendar/components/calendar-year-select.svelte +50 -0
  87. package/src/lib/components/calendar/components/calendar.svelte +123 -0
  88. package/src/lib/components/calendar/exports.ts +31 -0
  89. package/src/lib/components/calendar/index.ts +2 -0
  90. package/src/lib/components/calendar/types.ts +417 -0
  91. package/src/lib/components/card/card-action.svelte +15 -0
  92. package/src/lib/components/card/card-content.svelte +11 -0
  93. package/src/lib/components/card/card-description.svelte +11 -0
  94. package/src/lib/components/card/card-footer.svelte +11 -0
  95. package/src/lib/components/card/card-header.svelte +18 -0
  96. package/src/lib/components/card/card-title.svelte +11 -0
  97. package/src/lib/components/card/card.svelte +15 -0
  98. package/src/lib/components/card/index.ts +25 -0
  99. package/src/lib/components/carousel/carousel-arrow.svelte +42 -0
  100. package/src/lib/components/carousel/carousel-content.svelte +25 -0
  101. package/src/lib/components/carousel/carousel-dots.svelte +21 -0
  102. package/src/lib/components/carousel/carousel-item.svelte +21 -0
  103. package/src/lib/components/carousel/carousel-next.svelte +9 -0
  104. package/src/lib/components/carousel/carousel-previous.svelte +9 -0
  105. package/src/lib/components/carousel/carousel.svelte +245 -0
  106. package/src/lib/components/carousel/carouselState.svelte.ts +30 -0
  107. package/src/lib/components/carousel/index.ts +24 -0
  108. package/src/lib/components/chart/chart-container.svelte +52 -0
  109. package/src/lib/components/chart/chart-style.svelte +29 -0
  110. package/src/lib/components/chart/chart-tooltip.svelte +163 -0
  111. package/src/lib/components/chart/chart-utils.ts +50 -0
  112. package/src/lib/components/chart/index.ts +6 -0
  113. package/src/lib/components/checkbox/checkbox.svelte.ts +199 -0
  114. package/src/lib/components/checkbox/components/checkbox-group-label.svelte +29 -0
  115. package/src/lib/components/checkbox/components/checkbox-group.svelte +48 -0
  116. package/src/lib/components/checkbox/components/checkbox.svelte +85 -0
  117. package/src/lib/components/checkbox/index.ts +9 -0
  118. package/src/lib/components/checkbox/types.ts +131 -0
  119. package/src/lib/components/checkbox-native/checkbox.svelte +96 -0
  120. package/src/lib/components/checkbox-native/index.ts +8 -0
  121. package/src/lib/components/collapsible/components/collapsible-content.svelte +16 -0
  122. package/src/lib/components/collapsible/components/collapsible-trigger.svelte +14 -0
  123. package/src/lib/components/collapsible/components/collapsible.svelte +68 -0
  124. package/src/lib/components/collapsible/index.ts +9 -0
  125. package/src/lib/components/collapsible/types.ts +25 -0
  126. package/src/lib/components/combobox/components/combobox-arrow.svelte +8 -0
  127. package/src/lib/components/combobox/components/combobox-input.svelte +46 -0
  128. package/src/lib/components/combobox/components/combobox-trigger.svelte +44 -0
  129. package/src/lib/components/combobox/components/combobox.svelte +83 -0
  130. package/src/lib/components/combobox/index.ts +30 -0
  131. package/src/lib/components/combobox/select/components/select-content-static.svelte +56 -0
  132. package/src/lib/components/combobox/select/components/select-content.svelte +95 -0
  133. package/src/lib/components/combobox/select/components/select-group-heading.svelte +51 -0
  134. package/src/lib/components/combobox/select/components/select-group.svelte +51 -0
  135. package/src/lib/components/combobox/select/components/select-hidden-input.svelte +16 -0
  136. package/src/lib/components/combobox/select/components/select-item.svelte +86 -0
  137. package/src/lib/components/combobox/select/components/select-scroll-down-button.svelte +41 -0
  138. package/src/lib/components/combobox/select/components/select-scroll-up-button.svelte +34 -0
  139. package/src/lib/components/combobox/select/components/select-trigger.svelte +32 -0
  140. package/src/lib/components/combobox/select/components/select-value.svelte +40 -0
  141. package/src/lib/components/combobox/select/components/select-viewport.svelte +29 -0
  142. package/src/lib/components/combobox/select/components/select.svelte +92 -0
  143. package/src/lib/components/combobox/select/select.svelte.ts +1596 -0
  144. package/src/lib/components/combobox/select/types.ts +291 -0
  145. package/src/lib/components/combobox/types.ts +77 -0
  146. package/src/lib/components/command/command-dialog.svelte +42 -0
  147. package/src/lib/components/command/command.svelte.ts +1506 -0
  148. package/src/lib/components/command/components/_command-label.svelte +31 -0
  149. package/src/lib/components/command/components/command-empty.svelte +34 -0
  150. package/src/lib/components/command/components/command-group-heading.svelte +29 -0
  151. package/src/lib/components/command/components/command-group-items.svelte +31 -0
  152. package/src/lib/components/command/components/command-group.svelte +76 -0
  153. package/src/lib/components/command/components/command-input.svelte +55 -0
  154. package/src/lib/components/command/components/command-item.svelte +61 -0
  155. package/src/lib/components/command/components/command-link-item.svelte +61 -0
  156. package/src/lib/components/command/components/command-list.svelte +41 -0
  157. package/src/lib/components/command/components/command-loading.svelte +30 -0
  158. package/src/lib/components/command/components/command-separator.svelte +34 -0
  159. package/src/lib/components/command/components/command-viewport.svelte +29 -0
  160. package/src/lib/components/command/components/command.svelte +143 -0
  161. package/src/lib/components/command/compute-command-score.ts +207 -0
  162. package/src/lib/components/command/index.ts +29 -0
  163. package/src/lib/components/command/types.ts +222 -0
  164. package/src/lib/components/command/utils.ts +30 -0
  165. package/src/lib/components/context-menu/components/context-menu-content-static.svelte +22 -0
  166. package/src/lib/components/context-menu/components/context-menu-content.svelte +22 -0
  167. package/src/lib/components/context-menu/components/context-menu-trigger.svelte +46 -0
  168. package/src/lib/components/context-menu/components/context-menu.svelte +8 -0
  169. package/src/lib/components/context-menu/index.ts +39 -0
  170. package/src/lib/components/context-menu/types.ts +56 -0
  171. package/src/lib/components/data-table/column-helpers.ts +97 -0
  172. package/src/lib/components/data-table/data-table-checkbox.svelte +25 -0
  173. package/src/lib/components/data-table/data-table-sort-button.svelte +16 -0
  174. package/src/lib/components/data-table/flex-render.svelte +42 -0
  175. package/src/lib/components/data-table/index.ts +29 -0
  176. package/src/lib/components/data-table/render-helpers.ts +105 -0
  177. package/src/lib/components/data-table/table-cell.svelte +9 -0
  178. package/src/lib/components/data-table/table-column-header.svelte +51 -0
  179. package/src/lib/components/data-table/table-faceted-filter.svelte +109 -0
  180. package/src/lib/components/data-table/table-pagination.svelte +54 -0
  181. package/src/lib/components/data-table/table-priority-cell.svelte +22 -0
  182. package/src/lib/components/data-table/table-row-actions.svelte +29 -0
  183. package/src/lib/components/data-table/table-status-cell.svelte +34 -0
  184. package/src/lib/components/data-table/table-title-cell.svelte +24 -0
  185. package/src/lib/components/data-table/table-toolbar.svelte +55 -0
  186. package/src/lib/components/data-table/table-view-options.svelte +25 -0
  187. package/src/lib/components/date-field/components/date-field-hidden-input.svelte +9 -0
  188. package/src/lib/components/date-field/components/date-field-input.svelte +33 -0
  189. package/src/lib/components/date-field/components/date-field-label.svelte +29 -0
  190. package/src/lib/components/date-field/components/date-field-segment.svelte +29 -0
  191. package/src/lib/components/date-field/components/date-field.svelte +106 -0
  192. package/src/lib/components/date-field/date-field.svelte.ts +1474 -0
  193. package/src/lib/components/date-field/index.ts +11 -0
  194. package/src/lib/components/date-field/types.ts +165 -0
  195. package/src/lib/components/date-range-field/components/date-range-field-input.svelte +36 -0
  196. package/src/lib/components/date-range-field/components/date-range-field-label.svelte +29 -0
  197. package/src/lib/components/date-range-field/components/date-range-field.svelte +106 -0
  198. package/src/lib/components/date-range-field/date-range-field.svelte.ts +212 -0
  199. package/src/lib/components/date-range-field/index.ts +11 -0
  200. package/src/lib/components/date-range-field/types.ts +171 -0
  201. package/src/lib/components/dialog/components/dialog-close.svelte +31 -0
  202. package/src/lib/components/dialog/components/dialog-content-headless.svelte +104 -0
  203. package/src/lib/components/dialog/components/dialog-content.svelte +69 -0
  204. package/src/lib/components/dialog/components/dialog-description.svelte +29 -0
  205. package/src/lib/components/dialog/components/dialog-overlay.svelte +31 -0
  206. package/src/lib/components/dialog/components/dialog-title.svelte +30 -0
  207. package/src/lib/components/dialog/components/dialog-trigger.svelte +30 -0
  208. package/src/lib/components/dialog/components/dialog.svelte +21 -0
  209. package/src/lib/components/dialog/components/modal-surface.svelte +131 -0
  210. package/src/lib/components/dialog/dialog.svelte.ts +371 -0
  211. package/src/lib/components/dialog/index.ts +19 -0
  212. package/src/lib/components/dialog/types.ts +105 -0
  213. package/src/lib/components/drawer/drawer-close.svelte +7 -0
  214. package/src/lib/components/drawer/drawer-content.svelte +91 -0
  215. package/src/lib/components/drawer/drawer-description.svelte +8 -0
  216. package/src/lib/components/drawer/drawer-footer.svelte +11 -0
  217. package/src/lib/components/drawer/drawer-header.svelte +11 -0
  218. package/src/lib/components/drawer/drawer-nested.svelte +44 -0
  219. package/src/lib/components/drawer/drawer-overlay.svelte +60 -0
  220. package/src/lib/components/drawer/drawer-portal.svelte +10 -0
  221. package/src/lib/components/drawer/drawer-title.svelte +8 -0
  222. package/src/lib/components/drawer/drawer-trigger.svelte +7 -0
  223. package/src/lib/components/drawer/drawer.svelte +290 -0
  224. package/src/lib/components/drawer/index.ts +37 -0
  225. package/src/lib/components/dropdown-menu/components/dropdown-menu-content-static.svelte +22 -0
  226. package/src/lib/components/dropdown-menu/components/dropdown-menu-content.svelte +22 -0
  227. package/src/lib/components/dropdown-menu/index.ts +39 -0
  228. package/src/lib/components/dropdown-menu/types.ts +41 -0
  229. package/src/lib/components/empty/empty-content.svelte +15 -0
  230. package/src/lib/components/empty/empty-description.svelte +15 -0
  231. package/src/lib/components/empty/empty-header.svelte +15 -0
  232. package/src/lib/components/empty/empty-media.svelte +21 -0
  233. package/src/lib/components/empty/empty-title.svelte +11 -0
  234. package/src/lib/components/empty/empty.svelte +18 -0
  235. package/src/lib/components/empty/index.ts +22 -0
  236. package/src/lib/components/empty/variants.ts +16 -0
  237. package/src/lib/components/field/field-content.svelte +14 -0
  238. package/src/lib/components/field/field-description.svelte +19 -0
  239. package/src/lib/components/field/field-error.svelte +47 -0
  240. package/src/lib/components/field/field-group.svelte +17 -0
  241. package/src/lib/components/field/field-label.svelte +18 -0
  242. package/src/lib/components/field/field-legend.svelte +23 -0
  243. package/src/lib/components/field/field-separator.svelte +32 -0
  244. package/src/lib/components/field/field-set.svelte +14 -0
  245. package/src/lib/components/field/field-title.svelte +14 -0
  246. package/src/lib/components/field/field.svelte +36 -0
  247. package/src/lib/components/field/index.ts +32 -0
  248. package/src/lib/components/field/variants.ts +19 -0
  249. package/src/lib/components/input/file-input.svelte +26 -0
  250. package/src/lib/components/input/index.ts +12 -0
  251. package/src/lib/components/input/input.svelte +24 -0
  252. package/src/lib/components/input/variants.ts +28 -0
  253. package/src/lib/components/item/index.ts +34 -0
  254. package/src/lib/components/item/item-actions.svelte +11 -0
  255. package/src/lib/components/item/item-content.svelte +15 -0
  256. package/src/lib/components/item/item-description.svelte +18 -0
  257. package/src/lib/components/item/item-footer.svelte +11 -0
  258. package/src/lib/components/item/item-group.svelte +16 -0
  259. package/src/lib/components/item/item-header.svelte +11 -0
  260. package/src/lib/components/item/item-media.svelte +37 -0
  261. package/src/lib/components/item/item-separator.svelte +9 -0
  262. package/src/lib/components/item/item-title.svelte +15 -0
  263. package/src/lib/components/item/item.svelte +61 -0
  264. package/src/lib/components/kbd/index.ts +10 -0
  265. package/src/lib/components/kbd/kbd-group.svelte +11 -0
  266. package/src/lib/components/kbd/kbd.svelte +18 -0
  267. package/src/lib/components/label/exports.ts +2 -0
  268. package/src/lib/components/label/index.ts +2 -0
  269. package/src/lib/components/label/label.svelte +39 -0
  270. package/src/lib/components/label/label.svelte.ts +39 -0
  271. package/src/lib/components/label/types.ts +7 -0
  272. package/src/lib/components/link-preview/components/link-preview-content.svelte +60 -0
  273. package/src/lib/components/link-preview/components/link-preview-trigger.svelte +29 -0
  274. package/src/lib/components/link-preview/components/link-preview.svelte +34 -0
  275. package/src/lib/components/link-preview/exports.ts +11 -0
  276. package/src/lib/components/link-preview/index.ts +2 -0
  277. package/src/lib/components/link-preview/link-preview.svelte.ts +270 -0
  278. package/src/lib/components/link-preview/types.ts +102 -0
  279. package/src/lib/components/menu/components/menu-arrow.svelte +14 -0
  280. package/src/lib/components/menu/components/menu-checkbox-group.svelte +42 -0
  281. package/src/lib/components/menu/components/menu-checkbox-item.svelte +85 -0
  282. package/src/lib/components/menu/components/menu-content.svelte +98 -0
  283. package/src/lib/components/menu/components/menu-group-heading.svelte +47 -0
  284. package/src/lib/components/menu/components/menu-group.svelte +36 -0
  285. package/src/lib/components/menu/components/menu-item.svelte +58 -0
  286. package/src/lib/components/menu/components/menu-radio-group.svelte +44 -0
  287. package/src/lib/components/menu/components/menu-radio-item.svelte +65 -0
  288. package/src/lib/components/menu/components/menu-separator.svelte +38 -0
  289. package/src/lib/components/menu/components/menu-sub-content-static.svelte +8 -0
  290. package/src/lib/components/menu/components/menu-sub-content.svelte +133 -0
  291. package/src/lib/components/menu/components/menu-sub-trigger.svelte +60 -0
  292. package/src/lib/components/menu/components/menu-sub.svelte +23 -0
  293. package/src/lib/components/menu/components/menu-trigger.svelte +50 -0
  294. package/src/lib/components/menu/components/menu.svelte +58 -0
  295. package/src/lib/components/menu/exports.ts +38 -0
  296. package/src/lib/components/menu/menu.svelte.ts +1599 -0
  297. package/src/lib/components/menu/types.ts +285 -0
  298. package/src/lib/components/menu/utils.ts +68 -0
  299. package/src/lib/components/menubar/components/menubar-content-static.svelte +50 -0
  300. package/src/lib/components/menubar/components/menubar-content.svelte +54 -0
  301. package/src/lib/components/menubar/components/menubar-menu.svelte +34 -0
  302. package/src/lib/components/menubar/components/menubar-trigger.svelte +50 -0
  303. package/src/lib/components/menubar/components/menubar.svelte +57 -0
  304. package/src/lib/components/menubar/exports.ts +44 -0
  305. package/src/lib/components/menubar/index.ts +2 -0
  306. package/src/lib/components/menubar/menubar.svelte.ts +394 -0
  307. package/src/lib/components/menubar/types.ts +97 -0
  308. package/src/lib/components/meter/components/meter.svelte +41 -0
  309. package/src/lib/components/meter/exports.ts +2 -0
  310. package/src/lib/components/meter/index.ts +2 -0
  311. package/src/lib/components/meter/meter.svelte.ts +47 -0
  312. package/src/lib/components/meter/types.ts +28 -0
  313. package/src/lib/components/navigation-menu/components/navigation-menu-content-impl.svelte +85 -0
  314. package/src/lib/components/navigation-menu/components/navigation-menu-content.svelte +58 -0
  315. package/src/lib/components/navigation-menu/components/navigation-menu-indicator-impl.svelte +29 -0
  316. package/src/lib/components/navigation-menu/components/navigation-menu-indicator.svelte +56 -0
  317. package/src/lib/components/navigation-menu/components/navigation-menu-item.svelte +56 -0
  318. package/src/lib/components/navigation-menu/components/navigation-menu-link.svelte +64 -0
  319. package/src/lib/components/navigation-menu/components/navigation-menu-list.svelte +59 -0
  320. package/src/lib/components/navigation-menu/components/navigation-menu-sub.svelte +46 -0
  321. package/src/lib/components/navigation-menu/components/navigation-menu-trigger.svelte +80 -0
  322. package/src/lib/components/navigation-menu/components/navigation-menu-viewport.svelte +63 -0
  323. package/src/lib/components/navigation-menu/components/navigation-menu.svelte +80 -0
  324. package/src/lib/components/navigation-menu/index.ts +28 -0
  325. package/src/lib/components/navigation-menu/navigation-menu.svelte.ts +1092 -0
  326. package/src/lib/components/navigation-menu/types.ts +189 -0
  327. package/src/lib/components/pagination/components/pagination-next-button.svelte +60 -0
  328. package/src/lib/components/pagination/components/pagination-page.svelte +65 -0
  329. package/src/lib/components/pagination/components/pagination-prev-button.svelte +60 -0
  330. package/src/lib/components/pagination/components/pagination.svelte +65 -0
  331. package/src/lib/components/pagination/index.ts +11 -0
  332. package/src/lib/components/pagination/pagination.svelte.ts +275 -0
  333. package/src/lib/components/pagination/types.ts +100 -0
  334. package/src/lib/components/pin-input/components/pin-input-cell.svelte +49 -0
  335. package/src/lib/components/pin-input/components/pin-input.svelte +76 -0
  336. package/src/lib/components/pin-input/exports.ts +4 -0
  337. package/src/lib/components/pin-input/index.ts +2 -0
  338. package/src/lib/components/pin-input/pin-input.svelte.ts +541 -0
  339. package/src/lib/components/pin-input/types.ts +118 -0
  340. package/src/lib/components/pin-input/usePasswordManager.svelte.ts +122 -0
  341. package/src/lib/components/popover/components/popover-close.svelte +38 -0
  342. package/src/lib/components/popover/components/popover-content.svelte +70 -0
  343. package/src/lib/components/popover/components/popover-header.svelte +11 -0
  344. package/src/lib/components/popover/components/popover-title.svelte +11 -0
  345. package/src/lib/components/popover/components/popover-trigger.svelte +44 -0
  346. package/src/lib/components/popover/components/popover.svelte +26 -0
  347. package/src/lib/components/popover/exports.ts +13 -0
  348. package/src/lib/components/popover/index.ts +2 -0
  349. package/src/lib/components/popover/popover.svelte.ts +392 -0
  350. package/src/lib/components/popover/types.ts +92 -0
  351. package/src/lib/components/progress/components/progress.svelte +56 -0
  352. package/src/lib/components/progress/exports.ts +3 -0
  353. package/src/lib/components/progress/index.ts +2 -0
  354. package/src/lib/components/progress/progress.svelte.ts +58 -0
  355. package/src/lib/components/progress/types.ts +28 -0
  356. package/src/lib/components/radio-group/context.ts +13 -0
  357. package/src/lib/components/radio-group/index.ts +10 -0
  358. package/src/lib/components/radio-group/radio-group-item.svelte +67 -0
  359. package/src/lib/components/radio-group/radio-group.svelte +87 -0
  360. package/src/lib/components/range-calendar/components/range-calendar-cell.svelte +31 -0
  361. package/src/lib/components/range-calendar/components/range-calendar-day.svelte +33 -0
  362. package/src/lib/components/range-calendar/components/range-calendar.svelte +120 -0
  363. package/src/lib/components/range-calendar/exports.ts +31 -0
  364. package/src/lib/components/range-calendar/index.ts +2 -0
  365. package/src/lib/components/range-calendar/range-calendar.svelte.ts +739 -0
  366. package/src/lib/components/range-calendar/types.ts +258 -0
  367. package/src/lib/components/resizable/index.ts +15 -0
  368. package/src/lib/components/resizable/resizable-handle.svelte +30 -0
  369. package/src/lib/components/resizable/resizable-pane-group.svelte +19 -0
  370. package/src/lib/components/scroll-area/components/scroll-area-corner-impl.svelte +34 -0
  371. package/src/lib/components/scroll-area/components/scroll-area-corner.svelte +18 -0
  372. package/src/lib/components/scroll-area/components/scroll-area-scrollbar-auto.svelte +15 -0
  373. package/src/lib/components/scroll-area/components/scroll-area-scrollbar-hover.svelte +22 -0
  374. package/src/lib/components/scroll-area/components/scroll-area-scrollbar-scroll.svelte +16 -0
  375. package/src/lib/components/scroll-area/components/scroll-area-scrollbar-shared.svelte +19 -0
  376. package/src/lib/components/scroll-area/components/scroll-area-scrollbar-visible.svelte +16 -0
  377. package/src/lib/components/scroll-area/components/scroll-area-scrollbar-x.svelte +23 -0
  378. package/src/lib/components/scroll-area/components/scroll-area-scrollbar-y.svelte +24 -0
  379. package/src/lib/components/scroll-area/components/scroll-area-scrollbar.svelte +56 -0
  380. package/src/lib/components/scroll-area/components/scroll-area-thumb-impl.svelte +49 -0
  381. package/src/lib/components/scroll-area/components/scroll-area-thumb.svelte +21 -0
  382. package/src/lib/components/scroll-area/components/scroll-area-viewport.svelte +39 -0
  383. package/src/lib/components/scroll-area/components/scroll-area.svelte +82 -0
  384. package/src/lib/components/scroll-area/exports.ts +13 -0
  385. package/src/lib/components/scroll-area/index.ts +2 -0
  386. package/src/lib/components/scroll-area/scroll-area.svelte.ts +946 -0
  387. package/src/lib/components/scroll-area/types.ts +67 -0
  388. package/src/lib/components/select/index.ts +5 -0
  389. package/src/lib/components/select/select-opt-group.svelte +10 -0
  390. package/src/lib/components/select/select-option.svelte +15 -0
  391. package/src/lib/components/select/select.svelte +281 -0
  392. package/src/lib/components/separator/components/separator.svelte +49 -0
  393. package/src/lib/components/separator/index.ts +3 -0
  394. package/src/lib/components/separator/separator.svelte.ts +43 -0
  395. package/src/lib/components/separator/types.ts +22 -0
  396. package/src/lib/components/sheet/components/sheet-close.svelte +8 -0
  397. package/src/lib/components/sheet/components/sheet-content.svelte +108 -0
  398. package/src/lib/components/sheet/components/sheet-description.svelte +9 -0
  399. package/src/lib/components/sheet/components/sheet-footer.svelte +11 -0
  400. package/src/lib/components/sheet/components/sheet-header.svelte +11 -0
  401. package/src/lib/components/sheet/components/sheet-overlay.svelte +13 -0
  402. package/src/lib/components/sheet/components/sheet-title.svelte +9 -0
  403. package/src/lib/components/sheet/components/sheet-trigger.svelte +8 -0
  404. package/src/lib/components/sheet/index.ts +47 -0
  405. package/src/lib/components/sheet/variants.ts +20 -0
  406. package/src/lib/components/sidebar/constants.ts +6 -0
  407. package/src/lib/components/sidebar/context.svelte.ts +54 -0
  408. package/src/lib/components/sidebar/index.ts +77 -0
  409. package/src/lib/components/sidebar/sidebar-content.svelte +16 -0
  410. package/src/lib/components/sidebar/sidebar-desktop-surface.svelte +60 -0
  411. package/src/lib/components/sidebar/sidebar-footer.svelte +11 -0
  412. package/src/lib/components/sidebar/sidebar-group-action.svelte +34 -0
  413. package/src/lib/components/sidebar/sidebar-group-content.svelte +16 -0
  414. package/src/lib/components/sidebar/sidebar-group-label.svelte +34 -0
  415. package/src/lib/components/sidebar/sidebar-group.svelte +16 -0
  416. package/src/lib/components/sidebar/sidebar-header.svelte +11 -0
  417. package/src/lib/components/sidebar/sidebar-input.svelte +14 -0
  418. package/src/lib/components/sidebar/sidebar-inset.svelte +18 -0
  419. package/src/lib/components/sidebar/sidebar-menu-action.svelte +38 -0
  420. package/src/lib/components/sidebar/sidebar-menu-badge.svelte +19 -0
  421. package/src/lib/components/sidebar/sidebar-menu-button.svelte +99 -0
  422. package/src/lib/components/sidebar/sidebar-menu-item.svelte +21 -0
  423. package/src/lib/components/sidebar/sidebar-menu-skeleton.svelte +34 -0
  424. package/src/lib/components/sidebar/sidebar-menu-sub-button.svelte +40 -0
  425. package/src/lib/components/sidebar/sidebar-menu-sub-item.svelte +16 -0
  426. package/src/lib/components/sidebar/sidebar-menu-sub.svelte +19 -0
  427. package/src/lib/components/sidebar/sidebar-menu.svelte +21 -0
  428. package/src/lib/components/sidebar/sidebar-mobile-surface.svelte +48 -0
  429. package/src/lib/components/sidebar/sidebar-provider.svelte +60 -0
  430. package/src/lib/components/sidebar/sidebar-rail.svelte +36 -0
  431. package/src/lib/components/sidebar/sidebar-separator.svelte +9 -0
  432. package/src/lib/components/sidebar/sidebar-trigger.svelte +35 -0
  433. package/src/lib/components/sidebar/sidebar.svelte +30 -0
  434. package/src/lib/components/sidebar/types.ts +14 -0
  435. package/src/lib/components/skeleton/index.ts +7 -0
  436. package/src/lib/components/skeleton/skeleton.svelte +9 -0
  437. package/src/lib/components/slider/components/slider-range.svelte +37 -0
  438. package/src/lib/components/slider/components/slider-thumb-label.svelte +42 -0
  439. package/src/lib/components/slider/components/slider-thumb.svelte +46 -0
  440. package/src/lib/components/slider/components/slider-tick-label.svelte +42 -0
  441. package/src/lib/components/slider/components/slider-tick.svelte +28 -0
  442. package/src/lib/components/slider/components/slider.svelte +136 -0
  443. package/src/lib/components/slider/helpers.ts +249 -0
  444. package/src/lib/components/slider/index.ts +15 -0
  445. package/src/lib/components/slider/slider.svelte.ts +979 -0
  446. package/src/lib/components/slider/types.ts +294 -0
  447. package/src/lib/components/sonner/Toast.svelte +558 -0
  448. package/src/lib/components/sonner/Toaster.svelte +603 -0
  449. package/src/lib/components/sonner/index.ts +15 -0
  450. package/src/lib/components/sonner/toast-state.svelte.ts +217 -0
  451. package/src/lib/components/sonner/types.ts +384 -0
  452. package/src/lib/components/switch/index.ts +8 -0
  453. package/src/lib/components/switch/switch.svelte +78 -0
  454. package/src/lib/components/table/index.ts +28 -0
  455. package/src/lib/components/table/table-body.svelte +15 -0
  456. package/src/lib/components/table/table-caption.svelte +10 -0
  457. package/src/lib/components/table/table-cell.svelte +14 -0
  458. package/src/lib/components/table/table-footer.svelte +19 -0
  459. package/src/lib/components/table/table-head.svelte +17 -0
  460. package/src/lib/components/table/table-header.svelte +15 -0
  461. package/src/lib/components/table/table-row.svelte +14 -0
  462. package/src/lib/components/table/table.svelte +12 -0
  463. package/src/lib/components/tabs/components/tabs-content.svelte +30 -0
  464. package/src/lib/components/tabs/components/tabs-list.svelte +38 -0
  465. package/src/lib/components/tabs/components/tabs-trigger.svelte +51 -0
  466. package/src/lib/components/tabs/components/tabs.svelte +52 -0
  467. package/src/lib/components/tabs/index.ts +11 -0
  468. package/src/lib/components/tabs/tabs.svelte.ts +253 -0
  469. package/src/lib/components/tabs/types.ts +86 -0
  470. package/src/lib/components/textarea/index.ts +7 -0
  471. package/src/lib/components/textarea/textarea.svelte +23 -0
  472. package/src/lib/components/theme-toggle/index.ts +5 -0
  473. package/src/lib/components/theme-toggle/theme-toggle.svelte +49 -0
  474. package/src/lib/components/theme-toggle/theme.svelte.ts +81 -0
  475. package/src/lib/components/time-field/components/time-field-hidden-input.svelte +9 -0
  476. package/src/lib/components/time-field/components/time-field-input.svelte +33 -0
  477. package/src/lib/components/time-field/components/time-field-label.svelte +29 -0
  478. package/src/lib/components/time-field/components/time-field-segment.svelte +29 -0
  479. package/src/lib/components/time-field/components/time-field.svelte +99 -0
  480. package/src/lib/components/time-field/index.ts +11 -0
  481. package/src/lib/components/time-field/time-field.svelte.ts +1198 -0
  482. package/src/lib/components/time-field/types.ts +174 -0
  483. package/src/lib/components/time-range-field/components/time-range-field-input.svelte +36 -0
  484. package/src/lib/components/time-range-field/components/time-range-field-label.svelte +29 -0
  485. package/src/lib/components/time-range-field/components/time-range-field.svelte +104 -0
  486. package/src/lib/components/time-range-field/index.ts +11 -0
  487. package/src/lib/components/time-range-field/time-range-field.svelte.ts +235 -0
  488. package/src/lib/components/time-range-field/types.ts +181 -0
  489. package/src/lib/components/toggle/components/toggle.svelte +55 -0
  490. package/src/lib/components/toggle/index.ts +3 -0
  491. package/src/lib/components/toggle/toggle.svelte.ts +57 -0
  492. package/src/lib/components/toggle/types.ts +33 -0
  493. package/src/lib/components/toggle/variants.ts +25 -0
  494. package/src/lib/components/toggle-group/components/toggle-group-item.svelte +67 -0
  495. package/src/lib/components/toggle-group/components/toggle-group.svelte +109 -0
  496. package/src/lib/components/toggle-group/index.ts +7 -0
  497. package/src/lib/components/toggle-group/toggle-group.svelte.ts +235 -0
  498. package/src/lib/components/toggle-group/types.ts +89 -0
  499. package/src/lib/components/toolbar/components/toolbar-button.svelte +46 -0
  500. package/src/lib/components/toolbar/components/toolbar-group-item.svelte +56 -0
  501. package/src/lib/components/toolbar/components/toolbar-group.svelte +69 -0
  502. package/src/lib/components/toolbar/components/toolbar-link.svelte +39 -0
  503. package/src/lib/components/toolbar/components/toolbar.svelte +39 -0
  504. package/src/lib/components/toolbar/index.ts +13 -0
  505. package/src/lib/components/toolbar/toolbar.svelte.ts +373 -0
  506. package/src/lib/components/toolbar/types.ts +60 -0
  507. package/src/lib/components/tooltip/components/tooltip-content.svelte +81 -0
  508. package/src/lib/components/tooltip/components/tooltip-provider.svelte +26 -0
  509. package/src/lib/components/tooltip/components/tooltip-trigger.svelte +48 -0
  510. package/src/lib/components/tooltip/components/tooltip.svelte +55 -0
  511. package/src/lib/components/tooltip/index.ts +17 -0
  512. package/src/lib/components/tooltip/tooltip.svelte.ts +796 -0
  513. package/src/lib/components/tooltip/types.ts +175 -0
  514. package/src/lib/components/tooltip/utils.ts +1 -0
  515. package/src/lib/icons/google-icon.svelte +10 -0
  516. package/src/lib/icons/google-workspace.svelte +18 -0
  517. package/src/lib/icons/icon.d.ts +19 -0
  518. package/src/lib/icons/instagram.svelte +9 -0
  519. package/src/lib/icons/instantly.svelte +10 -0
  520. package/src/lib/icons/linkedin.svelte +9 -0
  521. package/src/lib/icons/mailshake.svelte +10 -0
  522. package/src/lib/icons/microsoft-365.svelte +7 -0
  523. package/src/lib/icons/passcode.svelte +12 -0
  524. package/src/lib/icons/replyio.svelte +10 -0
  525. package/src/lib/icons/smartlead.svelte +19 -0
  526. package/src/lib/icons/smtp.svelte +10 -0
  527. package/src/lib/icons/x-logo.svelte +9 -0
  528. package/src/lib/internal/animations-settled.svelte.ts +119 -0
  529. package/src/lib/internal/arrays.ts +253 -0
  530. package/src/lib/internal/attribute-types.ts +53 -0
  531. package/src/lib/internal/attrs.ts +64 -0
  532. package/src/lib/internal/body-scroll-lock.svelte.ts +330 -0
  533. package/src/lib/internal/create-id.ts +12 -0
  534. package/src/lib/internal/date-time/announcer.ts +74 -0
  535. package/src/lib/internal/date-time/calendar-helpers.svelte.ts +399 -0
  536. package/src/lib/internal/date-time/field/helpers.ts +431 -0
  537. package/src/lib/internal/date-time/field/parts.ts +15 -0
  538. package/src/lib/internal/date-time/field/range-field.svelte.ts +45 -0
  539. package/src/lib/internal/date-time/field/segments.ts +89 -0
  540. package/src/lib/internal/date-time/field/time-helpers.ts +311 -0
  541. package/src/lib/internal/date-time/formatter.ts +86 -0
  542. package/src/lib/internal/date-time/placeholders.ts +131 -0
  543. package/src/lib/internal/date-time/types.ts +121 -0
  544. package/src/lib/internal/date-time/utils.ts +243 -0
  545. package/src/lib/internal/dismissible-layer/index.ts +3 -0
  546. package/src/lib/internal/dismissible-layer/scheduler.ts +48 -0
  547. package/src/lib/internal/dismissible-layer/types.ts +57 -0
  548. package/src/lib/internal/dismissible-layer/use-dismissable-layer.svelte.ts +291 -0
  549. package/src/lib/internal/escape-layer/index.ts +3 -0
  550. package/src/lib/internal/escape-layer/types.ts +34 -0
  551. package/src/lib/internal/escape-layer/use-escape-layer.svelte.ts +91 -0
  552. package/src/lib/internal/floating-layer/components/floating-layer-arrow.svelte +35 -0
  553. package/src/lib/internal/floating-layer/components/floating-layer-content-static.svelte +19 -0
  554. package/src/lib/internal/floating-layer/components/floating-layer-content.svelte +52 -0
  555. package/src/lib/internal/floating-layer/components/floating-layer.svelte +12 -0
  556. package/src/lib/internal/floating-layer/components/index.ts +6 -0
  557. package/src/lib/internal/floating-layer/index.ts +2 -0
  558. package/src/lib/internal/floating-layer/types.ts +126 -0
  559. package/src/lib/internal/floating-layer/use-floating-layer.svelte.ts +471 -0
  560. package/src/lib/internal/focus-scope/focus-scope.svelte.ts +310 -0
  561. package/src/lib/internal/focus-scope/index.ts +4 -0
  562. package/src/lib/internal/focus-scope/types.ts +45 -0
  563. package/src/lib/internal/focus.ts +14 -0
  564. package/src/lib/internal/global-singleton.ts +10 -0
  565. package/src/lib/internal/group-value.svelte.ts +36 -0
  566. package/src/lib/internal/hover-intent-geometry.ts +96 -0
  567. package/src/lib/internal/index.ts +44 -0
  568. package/src/lib/internal/kbd.ts +60 -0
  569. package/src/lib/internal/layer-stack.ts +37 -0
  570. package/src/lib/internal/math.ts +11 -0
  571. package/src/lib/internal/merge-props.ts +7 -0
  572. package/src/lib/internal/native-dialog-controller.svelte.ts +102 -0
  573. package/src/lib/internal/native-popover.svelte.ts +173 -0
  574. package/src/lib/internal/open-cell.svelte.ts +41 -0
  575. package/src/lib/internal/paneforge/index.ts +5 -0
  576. package/src/lib/internal/paneforge/internal/constants.ts +2 -0
  577. package/src/lib/internal/paneforge/internal/helpers.ts +354 -0
  578. package/src/lib/internal/paneforge/internal/types.ts +33 -0
  579. package/src/lib/internal/paneforge/internal/utils/adjust-layout.ts +122 -0
  580. package/src/lib/internal/paneforge/internal/utils/aria.ts +36 -0
  581. package/src/lib/internal/paneforge/internal/utils/compare.ts +15 -0
  582. package/src/lib/internal/paneforge/internal/utils/is.ts +13 -0
  583. package/src/lib/internal/paneforge/internal/utils/resize.ts +31 -0
  584. package/src/lib/internal/paneforge/internal/utils/storage.ts +116 -0
  585. package/src/lib/internal/paneforge/internal/utils/style.ts +104 -0
  586. package/src/lib/internal/paneforge/pane-group.svelte +48 -0
  587. package/src/lib/internal/paneforge/pane-resizer.svelte +40 -0
  588. package/src/lib/internal/paneforge/pane.svelte +60 -0
  589. package/src/lib/internal/paneforge/paneforge.svelte.ts +821 -0
  590. package/src/lib/internal/paneforge/types.ts +179 -0
  591. package/src/lib/internal/popper-layer/index.ts +2 -0
  592. package/src/lib/internal/popper-layer/popper-content.svelte +19 -0
  593. package/src/lib/internal/popper-layer/popper-layer-inner.svelte +122 -0
  594. package/src/lib/internal/popper-layer/popper-layer.svelte +71 -0
  595. package/src/lib/internal/popper-layer/types.ts +65 -0
  596. package/src/lib/internal/portal/index.ts +2 -0
  597. package/src/lib/internal/portal/portal.svelte +61 -0
  598. package/src/lib/internal/prop-resolvers.ts +14 -0
  599. package/src/lib/internal/roving-focus-group.ts +147 -0
  600. package/src/lib/internal/roving-focus-item.svelte.ts +51 -0
  601. package/src/lib/internal/safe-polygon.svelte.ts +239 -0
  602. package/src/lib/internal/shared-state.svelte.ts +39 -0
  603. package/src/lib/internal/state-machine.ts +32 -0
  604. package/src/lib/internal/svelte-resize-observer.svelte.ts +37 -0
  605. package/src/lib/internal/tabbable.ts +398 -0
  606. package/src/lib/internal/table/core.ts +53 -0
  607. package/src/lib/internal/table/filter-fns.ts +10 -0
  608. package/src/lib/internal/table/index.ts +2 -0
  609. package/src/lib/internal/table/sorting-fns.ts +84 -0
  610. package/src/lib/internal/table/table.svelte.ts +330 -0
  611. package/src/lib/internal/table/types.ts +33 -0
  612. package/src/lib/internal/timeout-fn.svelte.ts +36 -0
  613. package/src/lib/internal/tools/box.svelte.ts +153 -0
  614. package/src/lib/internal/tools/index.ts +22 -0
  615. package/src/lib/internal/tools/types.ts +44 -0
  616. package/src/lib/internal/tools/utils/attach-ref.ts +74 -0
  617. package/src/lib/internal/tools/utils/compose-handlers.ts +36 -0
  618. package/src/lib/internal/tools/utils/dom-context.svelte.ts +63 -0
  619. package/src/lib/internal/tools/utils/dom.ts +62 -0
  620. package/src/lib/internal/tools/utils/events.ts +1 -0
  621. package/src/lib/internal/tools/utils/index.ts +5 -0
  622. package/src/lib/internal/tools/utils/strings.ts +28 -0
  623. package/src/lib/internal/typeahead.svelte.ts +48 -0
  624. package/src/lib/internal/types.ts +119 -0
  625. package/src/lib/internal/utils.ts +10 -0
  626. package/src/lib/internal/vaul/browser.ts +8 -0
  627. package/src/lib/internal/vaul/constants.ts +19 -0
  628. package/src/lib/internal/vaul/helpers.ts +39 -0
  629. package/src/lib/internal/vaul/types.ts +142 -0
  630. package/src/lib/internal/vaul/use-drawer-content.svelte.ts +219 -0
  631. package/src/lib/internal/vaul/use-drawer-root.svelte.ts +682 -0
  632. package/src/lib/internal/vaul/use-position-fixed.svelte.ts +142 -0
  633. package/src/lib/internal/vaul/use-prevent-scroll.svelte.ts +267 -0
  634. package/src/lib/internal/vaul/use-snap-points.svelte.ts +246 -0
  635. package/src/lib/time.ts +73 -0
@@ -0,0 +1,1506 @@
1
+ import { srOnlyStyles, attachRef, type WritableBoxedValues, type ReadableBoxedValues } from '../../internal/tools/index.js';
2
+ import { createContext, tick, untrack } from 'svelte';
3
+ import { findNextSibling, findPreviousSibling } from './utils.js';
4
+ import type { CommandState } from './types.js';
5
+ import type { BitsKeyboardEvent, BitsMouseEvent, BitsPointerEvent, RefAttachment, WithRefOpts } from '../../internal/types.js';
6
+ import { kbd } from '../../internal/kbd.js';
7
+ import { createBitsAttrs, boolToStr, boolToEmptyStrOrUndef } from '../../internal/attrs.js';
8
+ import { computeCommandScore } from './index.js';
9
+
10
+ const COMMAND_VALUE_ATTR = 'data-value';
11
+
12
+ const commandAttrs = createBitsAttrs({
13
+ component: 'command',
14
+ parts: [
15
+ 'root',
16
+ 'list',
17
+ 'input',
18
+ 'separator',
19
+ 'loading',
20
+ 'empty',
21
+ 'group',
22
+ 'group-items',
23
+ 'group-heading',
24
+ 'item',
25
+ 'viewport',
26
+ 'input-label',
27
+ ],
28
+ });
29
+
30
+ // selectors
31
+ const COMMAND_GROUP_SELECTOR = commandAttrs.selector('group');
32
+ const COMMAND_GROUP_ITEMS_SELECTOR = commandAttrs.selector('group-items');
33
+ const COMMAND_GROUP_HEADING_SELECTOR = commandAttrs.selector('group-heading');
34
+ const COMMAND_ITEM_SELECTOR = commandAttrs.selector('item');
35
+ const COMMAND_VALID_ITEM_SELECTOR = `${commandAttrs.selector('item')}:not([aria-disabled="true"])`;
36
+
37
+ const [getCommandRoot, setCommandRoot] = createContext<CommandRootState>();
38
+ const [getCommandList, setCommandList] = createContext<CommandListState>();
39
+ const [getCommandGroupContainer, setCommandGroupContainer, hasCommandGroupContainer] =
40
+ createContext<CommandGroupContainerState>();
41
+
42
+ interface GridItem {
43
+ index: number;
44
+ firstRowOfGroup: boolean;
45
+ ref: HTMLElement;
46
+ }
47
+
48
+ type ItemsGrid = GridItem[][];
49
+
50
+ interface CommandRootStateOpts
51
+ extends
52
+ WithRefOpts,
53
+ ReadableBoxedValues<{
54
+ filter: (value: string, search: string, keywords?: string[]) => number;
55
+ shouldFilter: boolean;
56
+ loop: boolean;
57
+ vimBindings: boolean;
58
+ columns: number | null;
59
+ disablePointerSelection: boolean;
60
+ disableInitialScroll: boolean;
61
+ onStateChange?: (state: Readonly<CommandState>) => void;
62
+ }>,
63
+ WritableBoxedValues<{
64
+ value: string;
65
+ }> {}
66
+
67
+ const defaultState = {
68
+ /** Value of the search query */
69
+ search: '',
70
+ /** Currently selected item value */
71
+ value: '',
72
+ filtered: {
73
+ /** The count of all visible items. */
74
+ count: 0,
75
+ /** Map from visible item id to its search store. */
76
+ items: new Map<string, number>(),
77
+ /** Set of groups with at least one visible item. */
78
+ groups: new Set<string>(),
79
+ },
80
+ };
81
+
82
+ export class CommandRootState {
83
+ static create(opts: CommandRootStateOpts) {
84
+ return setCommandRoot(new CommandRootState(opts));
85
+ }
86
+ readonly opts: CommandRootStateOpts;
87
+ readonly attachment: RefAttachment;
88
+ #updateScheduled = false;
89
+ #isInitialMount = true;
90
+ sortAfterTick = false;
91
+ sortAndFilterAfterTick = false;
92
+ allItems = new Set<string>();
93
+ allGroups = new Map<string, Set<string>>();
94
+ allIds = new Map<string, { value: string; keywords?: string[] }>();
95
+ // attempt to prevent the harsh delay when user is typing fast
96
+ key = $state(0);
97
+ viewportNode = $state<HTMLElement | null>(null);
98
+ inputNode = $state<HTMLElement | null>(null);
99
+ labelNode = $state<HTMLElement | null>(null);
100
+ // published state that the components and other things can react to
101
+ commandState = $state.raw<CommandState>(defaultState);
102
+ // internal state that we mutate in batches and publish to the `state` at once
103
+ _commandState = $state<CommandState>(defaultState);
104
+
105
+ #scheduleUpdate() {
106
+ if (this.#updateScheduled) return;
107
+ this.#updateScheduled = true;
108
+
109
+ tick().then(() => {
110
+ this.#updateScheduled = false;
111
+
112
+ const currentState = $state.snapshot(this._commandState);
113
+ const hasStateChanged = !Object.is(this.commandState, currentState);
114
+
115
+ if (hasStateChanged) {
116
+ this.commandState = currentState;
117
+ this.opts.onStateChange?.current?.(currentState);
118
+ }
119
+ });
120
+ }
121
+
122
+ setState<K extends keyof CommandState>(key: K, value: CommandState[K], preventScroll?: boolean) {
123
+ if (Object.is(this._commandState[key], value)) return;
124
+
125
+ this._commandState[key] = value;
126
+
127
+ if (key === 'search') {
128
+ // Filter synchronously before emitting back to children
129
+ this.#filterItems();
130
+ this.#sort();
131
+ } else if (key === 'value') {
132
+ if (!preventScroll) this.#scrollSelectedIntoView();
133
+ }
134
+
135
+ this.#scheduleUpdate();
136
+ }
137
+
138
+ constructor(opts: CommandRootStateOpts) {
139
+ this.opts = opts;
140
+ this.attachment = attachRef(this.opts.ref);
141
+
142
+ const defaults = { ...this._commandState, value: this.opts.value.current ?? '' };
143
+
144
+ this._commandState = defaults;
145
+ this.commandState = defaults;
146
+
147
+ this.onkeydown = this.onkeydown.bind(this);
148
+ }
149
+
150
+ /**
151
+ * Calculates score for an item based on search text and keywords.
152
+ * Higher score = better match.
153
+ *
154
+ * @param value - Item's display text
155
+ * @param keywords - Optional keywords to boost scoring
156
+ * @returns Score from 0-1, where 0 = no match
157
+ */
158
+ #score(value: string, keywords?: string[]) {
159
+ const filter = this.opts.filter.current ?? computeCommandScore;
160
+ return value ? filter(value, this._commandState.search, keywords) : 0;
161
+ }
162
+
163
+ /**
164
+ * Sorts items and groups based on search scores.
165
+ * Groups are sorted by their highest scoring item.
166
+ * When no search active, selects first item.
167
+ */
168
+ #sort(): void {
169
+ if (!this._commandState.search || this.opts.shouldFilter.current === false) {
170
+ // if no search and no initial value set or when clearing search,
171
+ // we select the first item.
172
+ if (!this._commandState.value || !this.#isInitialMount) {
173
+ this.#selectFirstItem();
174
+ } else if (this.#isInitialMount && this._commandState.value) {
175
+ // scroll the initial value into view if it exists
176
+ this.#scrollInitialValue();
177
+ }
178
+ return;
179
+ }
180
+
181
+ const scores = this._commandState.filtered.items;
182
+
183
+ // sort the groups
184
+ const groups: [string, number][] = [];
185
+ for (const value of this._commandState.filtered.groups) {
186
+ const items = this.allGroups.get(value);
187
+ let max = 0;
188
+ if (!items) {
189
+ groups.push([value, max]);
190
+ continue;
191
+ }
192
+
193
+ // get the max score of the group's items
194
+ for (const item of items!) {
195
+ const score = scores.get(item);
196
+ max = Math.max(score ?? 0, max);
197
+ }
198
+ groups.push([value, max]);
199
+ }
200
+
201
+ // Sort items within groups to bottom
202
+ // Sort items outside of groups
203
+ // Sort groups to bottom (pushes all non-grouped items to the top)
204
+ const listInsertionElement = this.viewportNode;
205
+
206
+ const sorted = this.getValidItems().sort((a, b) => {
207
+ const valueA = a.getAttribute('data-value');
208
+ const valueB = b.getAttribute('data-value');
209
+ const scoresA = scores.get(valueA!) ?? 0;
210
+ const scoresB = scores.get(valueB!) ?? 0;
211
+ return scoresB - scoresA;
212
+ });
213
+
214
+ for (const item of sorted) {
215
+ const group = item.closest(COMMAND_GROUP_ITEMS_SELECTOR);
216
+
217
+ if (group) {
218
+ const itemToAppend = item.parentElement === group ? item : item.closest(`${COMMAND_GROUP_ITEMS_SELECTOR} > *`);
219
+
220
+ if (itemToAppend) {
221
+ group.appendChild(itemToAppend);
222
+ }
223
+ } else {
224
+ const itemToAppend = item.parentElement === listInsertionElement ? item : item.closest(`${COMMAND_GROUP_ITEMS_SELECTOR} > *`);
225
+
226
+ if (itemToAppend) {
227
+ listInsertionElement?.appendChild(itemToAppend);
228
+ }
229
+ }
230
+ }
231
+
232
+ const sortedGroups = groups.sort((a, b) => b[1] - a[1]);
233
+
234
+ for (const group of sortedGroups) {
235
+ const element = listInsertionElement?.querySelector(`${COMMAND_GROUP_SELECTOR}[${COMMAND_VALUE_ATTR}="${CSS.escape(group[0])}"]`);
236
+ element?.parentElement?.appendChild(element);
237
+ }
238
+
239
+ this.#selectFirstItem();
240
+ }
241
+
242
+ /**
243
+ * Sets current value and triggers re-render if cleared.
244
+ *
245
+ * @param value - New value to set
246
+ */
247
+ setValue(value: string, opts?: boolean) {
248
+ if (value !== this.opts.value.current && value === '') {
249
+ tick().then(() => {
250
+ this.key++;
251
+ });
252
+ }
253
+ this.setState('value', value, opts);
254
+ this.opts.value.current = value;
255
+ }
256
+
257
+ /**
258
+ * Selects first non-disabled item on next tick.
259
+ */
260
+ #selectFirstItem(): void {
261
+ tick().then(() => {
262
+ const item = this.getValidItems().find((item) => item.getAttribute('aria-disabled') !== 'true');
263
+ const value = item?.getAttribute(COMMAND_VALUE_ATTR);
264
+ const shouldPreventScroll = this.#isInitialMount && this.opts.disableInitialScroll.current;
265
+ this.setValue(value ?? '', shouldPreventScroll);
266
+ this.#isInitialMount = false;
267
+ });
268
+ }
269
+
270
+ /**
271
+ * Scrolls the initial value into view if it exists and is not the first item.
272
+ * Called during initial mount when a value is provided.
273
+ */
274
+ #scrollInitialValue(): void {
275
+ tick().then(() => {
276
+ const shouldPreventScroll = this.opts.disableInitialScroll.current;
277
+ if (!shouldPreventScroll) {
278
+ this.#scrollSelectedIntoView();
279
+ }
280
+ this.#isInitialMount = false;
281
+ });
282
+ }
283
+
284
+ /**
285
+ * Updates filtered items/groups based on search.
286
+ * Recalculates scores and filtered count.
287
+ */
288
+ #filterItems(): void {
289
+ if (!this._commandState.search || this.opts.shouldFilter.current === false) {
290
+ this._commandState.filtered.count = this.allItems.size;
291
+ return;
292
+ }
293
+
294
+ // reset the groups
295
+ this._commandState.filtered.groups = new Set();
296
+ let itemCount = 0;
297
+
298
+ // Check which items should be included
299
+ for (const id of this.allItems) {
300
+ const value = this.allIds.get(id)?.value ?? '';
301
+ const keywords = this.allIds.get(id)?.keywords ?? [];
302
+ const rank = this.#score(value, keywords);
303
+ this._commandState.filtered.items.set(id, rank);
304
+ if (rank > 0) itemCount++;
305
+ }
306
+
307
+ // Check which groups have at least 1 item shown
308
+ for (const [groupId, group] of this.allGroups) {
309
+ const hasVisibleItem = group.values().some((itemId) => (this._commandState.filtered.items.get(itemId) ?? 0) > 0);
310
+ if (hasVisibleItem) this._commandState.filtered.groups.add(groupId);
311
+ }
312
+
313
+ this._commandState.filtered.count = itemCount;
314
+ }
315
+
316
+ /**
317
+ * Gets all non-disabled, visible command items.
318
+ *
319
+ * @returns Array of valid item elements
320
+ * @remarks Exposed for direct item access and bound checking
321
+ */
322
+ getValidItems(): HTMLElement[] {
323
+ const node = this.opts.ref.current;
324
+ if (!node) return [];
325
+ return [...node.querySelectorAll<HTMLElement>(COMMAND_VALID_ITEM_SELECTOR)];
326
+ }
327
+
328
+ /**
329
+ * Gets all visible command items.
330
+ *
331
+ * @returns Array of valid item elements
332
+ * @remarks Exposed for direct item access and bound checking
333
+ */
334
+ getVisibleItems(): HTMLElement[] {
335
+ const node = this.opts.ref.current;
336
+ if (!node) return [];
337
+ return [...node.querySelectorAll<HTMLElement>(COMMAND_ITEM_SELECTOR)];
338
+ }
339
+ /** Returns all visible items in a matrix structure
340
+ *
341
+ * @remarks Returns empty if the command isn't configured as a grid
342
+ *
343
+ * @returns
344
+ */
345
+ get itemsGrid(): ItemsGrid {
346
+ if (!this.isGrid) return [];
347
+
348
+ const columns = this.opts.columns.current ?? 1;
349
+
350
+ const items = this.getVisibleItems();
351
+
352
+ const grid: ItemsGrid = [[]];
353
+
354
+ let currentGroup = items[0]?.getAttribute('data-group');
355
+ let column = 0;
356
+ let row = 0;
357
+
358
+ for (let i = 0; i < items.length; i++) {
359
+ const item = items[i];
360
+ const itemGroup = item?.getAttribute('data-group');
361
+
362
+ if (currentGroup !== itemGroup) {
363
+ currentGroup = itemGroup;
364
+ column = 1;
365
+ row++;
366
+ grid.push([{ index: i, firstRowOfGroup: true, ref: item! }]);
367
+ } else {
368
+ column++;
369
+
370
+ if (column > columns) {
371
+ row++;
372
+ column = 1;
373
+ grid.push([]);
374
+ }
375
+
376
+ grid[row]?.push({
377
+ index: i,
378
+ firstRowOfGroup: grid[row]?.[0]?.firstRowOfGroup ?? i === 0,
379
+ ref: item!,
380
+ });
381
+ }
382
+ }
383
+
384
+ return grid;
385
+ }
386
+
387
+ /**
388
+ * @returns Selected element or null
389
+ */
390
+ #getSelectedItem(): HTMLElement | null {
391
+ return this.opts.ref.current?.querySelector(`${COMMAND_VALID_ITEM_SELECTOR}[data-selected]`) ?? null;
392
+ }
393
+
394
+ /**
395
+ * Scrolls selected item into view.
396
+ * Special handling for first items in groups.
397
+ */
398
+ #scrollSelectedIntoView(): void {
399
+ tick().then(() => {
400
+ const item = this.#getSelectedItem();
401
+ if (!item) return;
402
+ const grandparent = item.parentElement?.parentElement;
403
+ if (!grandparent) return;
404
+
405
+ if (this.isGrid) item.scrollIntoView({ block: 'nearest' });
406
+
407
+ const scrollHeader = this.isGrid
408
+ ? !!this.itemsGrid.flat().find((cell) => cell?.ref === item)?.firstRowOfGroup
409
+ : (grandparent.firstElementChild?.getAttribute('data-value') ?? undefined) === item.dataset?.value;
410
+
411
+ if (scrollHeader) {
412
+ item.closest(COMMAND_GROUP_SELECTOR)?.querySelector(COMMAND_GROUP_HEADING_SELECTOR)?.scrollIntoView({ block: 'nearest' });
413
+ return;
414
+ }
415
+
416
+ item.scrollIntoView({ block: 'nearest' });
417
+ });
418
+ }
419
+
420
+ /**
421
+ * Sets selection to item at specified index in valid items array.
422
+ * If index is out of bounds, does nothing.
423
+ *
424
+ * @param index - Zero-based index of item to select
425
+ * @remarks
426
+ * Uses `getValidItems()` to get selectable items, filtering out disabled/hidden ones.
427
+ * Access valid items directly via `getValidItems()` to check bounds before calling.
428
+ *
429
+ * @example
430
+ * // get valid items length for bounds check
431
+ * const items = getValidItems()
432
+ * if (index < items.length) {
433
+ * updateSelectedToIndex(index)
434
+ * }
435
+ */
436
+ updateSelectedToIndex(index: number) {
437
+ const item = this.getValidItems()[index];
438
+ if (!item) return;
439
+ this.setValue(item.getAttribute(COMMAND_VALUE_ATTR) ?? '');
440
+ }
441
+
442
+ /**
443
+ * Updates selected item by moving up/down relative to current selection.
444
+ * Handles wrapping when loop option is enabled.
445
+ *
446
+ * @param change - Direction to move: 1 for next item, -1 for previous item
447
+ * @remarks
448
+ * The loop behavior wraps:
449
+ * - From last item to first when moving next
450
+ * - From first item to last when moving previous
451
+ *
452
+ * Uses `getValidItems()` to get all selectable items, which filters out disabled/hidden items.
453
+ * You can call `getValidItems()` directly to get the current valid items array.
454
+ *
455
+ * @example
456
+ * // select next item
457
+ * updateSelectedByItem(1)
458
+ *
459
+ * // get all valid items
460
+ * const items = getValidItems()
461
+ */
462
+ updateSelectedByItem(change: number): void {
463
+ const selected = this.#getSelectedItem();
464
+ const items = this.getValidItems();
465
+ const index = items.findIndex((item) => item === selected);
466
+
467
+ // Get item at this index
468
+ let newSelected = items[index + change];
469
+
470
+ if (this.opts.loop.current) {
471
+ newSelected = index + change < 0 ? items[items.length - 1] : index + change === items.length ? items[0] : items[index + change];
472
+ }
473
+
474
+ if (newSelected) {
475
+ this.setValue(newSelected.getAttribute(COMMAND_VALUE_ATTR) ?? '');
476
+ }
477
+ }
478
+
479
+ /**
480
+ * Moves selection to the first valid item in the next/previous group.
481
+ * If no group is found, falls back to selecting the next/previous item globally.
482
+ *
483
+ * @param change - Direction to move: 1 for next group, -1 for previous group
484
+ * @example
485
+ * // move to first item in next group
486
+ * updateSelectedByGroup(1)
487
+ *
488
+ * // move to first item in previous group
489
+ * updateSelectedByGroup(-1)
490
+ */
491
+ updateSelectedByGroup(change: 1 | -1): void {
492
+ const selected = this.#getSelectedItem();
493
+ let group = selected?.closest(COMMAND_GROUP_SELECTOR);
494
+ let item: HTMLElement | null | undefined;
495
+
496
+ while (group && !item) {
497
+ group = change > 0 ? findNextSibling(group, COMMAND_GROUP_SELECTOR) : findPreviousSibling(group, COMMAND_GROUP_SELECTOR);
498
+ item = group?.querySelector(COMMAND_VALID_ITEM_SELECTOR);
499
+ }
500
+
501
+ if (item) {
502
+ this.setValue(item.getAttribute(COMMAND_VALUE_ATTR) ?? '');
503
+ } else {
504
+ this.updateSelectedByItem(change);
505
+ }
506
+ }
507
+
508
+ /**
509
+ * Maps item id to display value and search keywords.
510
+ * Returns cleanup function to remove mapping.
511
+ *
512
+ * @param id - Unique item identifier
513
+ * @param value - Display text
514
+ * @param keywords - Optional search boost terms
515
+ * @returns Cleanup function
516
+ */
517
+ registerValue(value: string, keywords?: string[]): () => void {
518
+ if (!(value && value === this.allIds.get(value)?.value)) {
519
+ this.allIds.set(value, { value, keywords });
520
+ }
521
+ this._commandState.filtered.items.set(value, this.#score(value, keywords));
522
+
523
+ // Schedule sorting to run after this tick when all items are added not each time an item is added
524
+ if (!this.sortAfterTick) {
525
+ this.sortAfterTick = true;
526
+ tick().then(() => {
527
+ this.#sort();
528
+ this.sortAfterTick = false;
529
+ });
530
+ }
531
+
532
+ return () => {
533
+ this.allIds.delete(value);
534
+ };
535
+ }
536
+
537
+ /**
538
+ * Registers item in command list and its group.
539
+ * Handles filtering, sorting and selection updates.
540
+ *
541
+ * @param id - Item identifier
542
+ * @param groupId - Optional group to add item to
543
+ * @returns Cleanup function that handles selection
544
+ */
545
+ registerItem(id: string, groupId: string | undefined): () => void {
546
+ this.allItems.add(id);
547
+
548
+ // Track this item within the group
549
+ if (groupId) {
550
+ if (!this.allGroups.has(groupId)) {
551
+ this.allGroups.set(groupId, new Set([id]));
552
+ } else {
553
+ this.allGroups.get(groupId)!.add(id);
554
+ }
555
+ }
556
+
557
+ // Schedule sorting and filtering to run after this tick when all items are added not each time an item is added
558
+ if (!this.sortAndFilterAfterTick) {
559
+ this.sortAndFilterAfterTick = true;
560
+ tick().then(() => {
561
+ this.#filterItems();
562
+ this.#sort();
563
+ this.sortAndFilterAfterTick = false;
564
+ });
565
+ }
566
+
567
+ this.#scheduleUpdate();
568
+ return () => {
569
+ const selectedItem = this.#getSelectedItem();
570
+ this.allItems.delete(id);
571
+ this.commandState.filtered.items.delete(id);
572
+
573
+ this.#filterItems();
574
+
575
+ // The item removed have been the selected one,
576
+ // so selection should be moved to the first
577
+ if (selectedItem?.getAttribute('id') === id) {
578
+ this.#selectFirstItem();
579
+ }
580
+
581
+ this.#scheduleUpdate();
582
+ };
583
+ }
584
+
585
+ /**
586
+ * Creates empty group if not exists.
587
+ *
588
+ * @param id - Group identifier
589
+ * @returns Cleanup function
590
+ */
591
+ registerGroup(id: string): () => void {
592
+ if (!this.allGroups.has(id)) {
593
+ this.allGroups.set(id, new Set());
594
+ }
595
+
596
+ return () => {
597
+ this.allIds.delete(id);
598
+ this.allGroups.delete(id);
599
+ };
600
+ }
601
+
602
+ get isGrid() {
603
+ return this.opts.columns.current !== null;
604
+ }
605
+
606
+ /**
607
+ * Selects last valid item.
608
+ */
609
+ #last() {
610
+ return this.updateSelectedToIndex(this.getValidItems().length - 1);
611
+ }
612
+
613
+ /**
614
+ * Handles next item selection:
615
+ * - Meta: Jump to last
616
+ * - Alt: Next group
617
+ * - Default: Next item
618
+ *
619
+ * @param e - Keyboard event
620
+ */
621
+ #next(e: BitsKeyboardEvent) {
622
+ e.preventDefault();
623
+
624
+ if (e.metaKey) {
625
+ this.#last();
626
+ } else if (e.altKey) {
627
+ this.updateSelectedByGroup(1);
628
+ } else {
629
+ this.updateSelectedByItem(1);
630
+ }
631
+ }
632
+
633
+ #down(e: BitsKeyboardEvent) {
634
+ if (this.opts.columns.current === null) return;
635
+
636
+ e.preventDefault();
637
+
638
+ if (e.metaKey) {
639
+ this.updateSelectedByGroup(1);
640
+ } else {
641
+ this.updateSelectedByItem(this.#nextRowColumnOffset(e));
642
+ }
643
+ }
644
+
645
+ #getColumn(item: HTMLElement, grid: ItemsGrid): { columnIndex: number; rowIndex: number } | null {
646
+ if (grid.length === 0) return null;
647
+
648
+ for (let r = 0; r < grid.length; r++) {
649
+ const row = grid[r];
650
+ if (row === undefined) continue;
651
+
652
+ for (let c = 0; c < row.length; c++) {
653
+ const column = row[c];
654
+ if (column === undefined || column.ref !== item) continue;
655
+
656
+ return { columnIndex: c, rowIndex: r };
657
+ }
658
+ }
659
+
660
+ return null;
661
+ }
662
+
663
+ #nextRowColumnOffset(e: BitsKeyboardEvent): number {
664
+ const grid = this.itemsGrid;
665
+ const selected = this.#getSelectedItem();
666
+ if (!selected) return 0;
667
+ const column = this.#getColumn(selected, grid);
668
+ if (!column) return 0;
669
+
670
+ let newItem: HTMLElement | null = null;
671
+
672
+ const skipRows = e.altKey ? 1 : 0;
673
+
674
+ // if this is the second to last row then we need to go to the last row when skipping and not in loop mode
675
+ if (e.altKey && column.rowIndex === grid.length - 2 && !this.opts.loop.current) {
676
+ newItem = this.#findNextNonDisabledItem({
677
+ start: grid.length - 1,
678
+ end: grid.length,
679
+ expectedColumnIndex: column.columnIndex,
680
+ grid,
681
+ });
682
+ } else if (column.rowIndex === grid.length - 1) {
683
+ // if this is the last row we apply the loop logic
684
+ if (!this.opts.loop.current) return 0;
685
+
686
+ newItem = this.#findNextNonDisabledItem({
687
+ start: 0 + skipRows,
688
+ end: column.rowIndex,
689
+ expectedColumnIndex: column.columnIndex,
690
+ grid,
691
+ });
692
+ } else {
693
+ newItem = this.#findNextNonDisabledItem({
694
+ start: column.rowIndex + 1 + skipRows,
695
+ end: grid.length,
696
+ expectedColumnIndex: column.columnIndex,
697
+ grid,
698
+ });
699
+
700
+ // this happens if there were no non-disabled columns below the current column
701
+ // we can now try starting from the beginning to find the right column
702
+ if (newItem === null && this.opts.loop.current) {
703
+ newItem = this.#findNextNonDisabledItem({
704
+ start: 0,
705
+ end: column.rowIndex,
706
+ expectedColumnIndex: column.columnIndex,
707
+ grid,
708
+ });
709
+ }
710
+ }
711
+
712
+ return this.#calculateOffset(selected, newItem);
713
+ }
714
+
715
+ /** Attempts to find the next non-disabled column that matches the expected column.
716
+ *
717
+ * @remarks
718
+ * - Skips over disabled columns
719
+ * - When a row is shorter than the expected column it defaults to the last item in the row
720
+ *
721
+ * @param param0
722
+ * @returns
723
+ */
724
+ #findNextNonDisabledItem({
725
+ start,
726
+ end,
727
+ grid,
728
+ expectedColumnIndex,
729
+ }: {
730
+ start: number;
731
+ end: number;
732
+ grid: ItemsGrid;
733
+ expectedColumnIndex: number;
734
+ }): HTMLElement | null {
735
+ for (let r = start; r < end; r++) {
736
+ const row = grid[r];
737
+ if (!row) continue;
738
+
739
+ const atColumn = row[expectedColumnIndex]?.ref ?? null;
740
+
741
+ // preferred: the cell at the expected column, if it exists and isn't disabled
742
+ if (atColumn) {
743
+ if (!itemIsDisabled(atColumn)) return atColumn;
744
+ continue; // expected column is disabled — try the next row
745
+ }
746
+
747
+ // the expected column doesn't exist (short row) — fall back to the
748
+ // first non-disabled cell in the row, else keep scanning
749
+ const fallback = row.find((cell) => cell && !itemIsDisabled(cell.ref))?.ref ?? null;
750
+ if (fallback) return fallback;
751
+ }
752
+
753
+ return null;
754
+ }
755
+
756
+ #calculateOffset(selected: HTMLElement, newSelected: HTMLElement | null): number {
757
+ if (newSelected === null) return 0;
758
+
759
+ const items = this.getValidItems();
760
+
761
+ const ogIndex = items.findIndex((item) => item === selected);
762
+ const newIndex = items.findIndex((item) => item === newSelected);
763
+
764
+ return newIndex - ogIndex;
765
+ }
766
+
767
+ #up(e: BitsKeyboardEvent) {
768
+ if (this.opts.columns.current === null) return;
769
+
770
+ e.preventDefault();
771
+
772
+ if (e.metaKey) {
773
+ this.updateSelectedByGroup(-1);
774
+ } else {
775
+ this.updateSelectedByItem(this.#previousRowColumnOffset(e));
776
+ }
777
+ }
778
+
779
+ #previousRowColumnOffset(e: BitsKeyboardEvent) {
780
+ const grid = this.itemsGrid;
781
+ const selected = this.#getSelectedItem();
782
+ if (!selected) return 0;
783
+ const column = this.#getColumn(selected, grid);
784
+ if (column === null) return 0;
785
+
786
+ let newItem: HTMLElement | null = null;
787
+
788
+ const skipRows = e.altKey ? 1 : 0;
789
+
790
+ // if this is the second row then we need to go to the top when skipping and not in loop mode
791
+ if (e.altKey && column.rowIndex === 1 && this.opts.loop.current === false) {
792
+ newItem = this.#findNextNonDisabledItemDesc({
793
+ start: 0,
794
+ end: 0,
795
+ expectedColumnIndex: column.columnIndex,
796
+ grid,
797
+ });
798
+ } else if (column.rowIndex === 0) {
799
+ // if this is the last row we apply the loop logic
800
+ if (this.opts.loop.current === false) return 0;
801
+
802
+ newItem = this.#findNextNonDisabledItemDesc({
803
+ start: grid.length - 1 - skipRows,
804
+ end: column.rowIndex + 1,
805
+ expectedColumnIndex: column.columnIndex,
806
+ grid,
807
+ });
808
+ } else {
809
+ newItem = this.#findNextNonDisabledItemDesc({
810
+ start: column.rowIndex - 1 - skipRows,
811
+ end: 0,
812
+ expectedColumnIndex: column.columnIndex,
813
+ grid,
814
+ });
815
+
816
+ // this happens if there were no non-disabled columns below the current column
817
+ // we can now try starting from the beginning to find the right column
818
+ if (newItem === null && this.opts.loop.current) {
819
+ newItem = this.#findNextNonDisabledItemDesc({
820
+ start: grid.length - 1,
821
+ end: column.rowIndex + 1,
822
+ expectedColumnIndex: column.columnIndex,
823
+ grid,
824
+ });
825
+ }
826
+ }
827
+
828
+ return this.#calculateOffset(selected, newItem);
829
+ }
830
+
831
+ /**
832
+ * Attempts to find the next non-disabled column that matches the expected column.
833
+ *
834
+ * @remarks
835
+ * - Skips over disabled columns
836
+ * - When a row is shorter than the expected column it defaults to the last item in the row
837
+ */
838
+ #findNextNonDisabledItemDesc({
839
+ start,
840
+ end,
841
+ grid,
842
+ expectedColumnIndex,
843
+ }: {
844
+ start: number;
845
+ end: number;
846
+ grid: ItemsGrid;
847
+ expectedColumnIndex: number;
848
+ }): HTMLElement | null {
849
+ for (let r = start; r >= end; r--) {
850
+ const row = grid[r];
851
+ if (!row) continue;
852
+
853
+ const atColumn = row[expectedColumnIndex]?.ref ?? null;
854
+
855
+ // preferred: the cell at the expected column, if it exists and isn't disabled
856
+ if (atColumn && !itemIsDisabled(atColumn)) return atColumn;
857
+
858
+ // the expected column was disabled — this row yields nothing
859
+ if (atColumn) return null;
860
+
861
+ // the expected column doesn't exist (short row) — fall back to the
862
+ // last non-disabled cell in the row
863
+ return row.findLast((cell) => cell && !itemIsDisabled(cell.ref))?.ref ?? null;
864
+ }
865
+
866
+ return null;
867
+ }
868
+
869
+ /**
870
+ * Handles previous item selection:
871
+ * - Meta: Jump to first
872
+ * - Alt: Previous group
873
+ * - Default: Previous item
874
+ *
875
+ * @param e - Keyboard event
876
+ */
877
+ #prev(e: BitsKeyboardEvent) {
878
+ e.preventDefault();
879
+ if (e.metaKey) this.updateSelectedToIndex(0);
880
+ else if (e.altKey) this.updateSelectedByGroup(-1);
881
+ else this.updateSelectedByItem(-1);
882
+ }
883
+
884
+ onkeydown(e: BitsKeyboardEvent) {
885
+ const isVim = this.opts.vimBindings.current && e.ctrlKey;
886
+ switch (e.key) {
887
+ case kbd.n:
888
+ case kbd.j: {
889
+ // vim down
890
+ if (isVim) {
891
+ if (this.isGrid) {
892
+ this.#down(e);
893
+ } else {
894
+ this.#next(e);
895
+ }
896
+ }
897
+ break;
898
+ }
899
+ case kbd.l: {
900
+ // vim right
901
+ if (isVim) {
902
+ if (this.isGrid) {
903
+ this.#next(e);
904
+ }
905
+ }
906
+ break;
907
+ }
908
+ case kbd.ARROW_DOWN:
909
+ if (this.isGrid) {
910
+ this.#down(e);
911
+ } else {
912
+ this.#next(e);
913
+ }
914
+ break;
915
+ case kbd.ARROW_RIGHT:
916
+ if (!this.isGrid) break;
917
+
918
+ this.#next(e);
919
+
920
+ break;
921
+ case kbd.p:
922
+ case kbd.k: {
923
+ // vim up
924
+ if (isVim) {
925
+ if (this.isGrid) {
926
+ this.#up(e);
927
+ } else {
928
+ this.#prev(e);
929
+ }
930
+ }
931
+ break;
932
+ }
933
+ case kbd.h: {
934
+ // vim left
935
+ if (isVim && this.isGrid) {
936
+ this.#prev(e);
937
+ }
938
+ break;
939
+ }
940
+ case kbd.ARROW_UP:
941
+ if (this.isGrid) {
942
+ this.#up(e);
943
+ } else {
944
+ this.#prev(e);
945
+ }
946
+ break;
947
+ case kbd.ARROW_LEFT:
948
+ if (!this.isGrid) break;
949
+ this.#prev(e);
950
+ break;
951
+ case kbd.HOME:
952
+ // first item
953
+ e.preventDefault();
954
+ this.updateSelectedToIndex(0);
955
+ break;
956
+ case kbd.END:
957
+ // last item
958
+ e.preventDefault();
959
+ this.#last();
960
+ break;
961
+ case kbd.ENTER: {
962
+ /**
963
+ * Check if IME composition is finished before triggering the select event.
964
+ * This prevents unwanted triggering while user is still inputting text with IME.
965
+ * e.keyCode === 229 is for the Japanese IME && Safari as `isComposing` does not
966
+ * work with Japanese IME and Safari in combination.
967
+ */
968
+ if (!e.isComposing && e.keyCode !== 229) {
969
+ e.preventDefault();
970
+ const item = this.#getSelectedItem();
971
+ if (item) {
972
+ item?.click();
973
+ }
974
+ }
975
+ }
976
+ }
977
+ }
978
+
979
+ props = $derived.by(
980
+ () =>
981
+ ({
982
+ id: this.opts.id.current,
983
+ role: 'application',
984
+ [commandAttrs.root]: '',
985
+ tabindex: -1,
986
+ onkeydown: this.onkeydown,
987
+ ...this.attachment,
988
+ }) as const,
989
+ );
990
+ }
991
+
992
+ function itemIsDisabled(item: HTMLElement) {
993
+ return item.getAttribute('aria-disabled') === 'true';
994
+ }
995
+
996
+ interface CommandEmptyStateOpts
997
+ extends
998
+ WithRefOpts,
999
+ ReadableBoxedValues<{
1000
+ forceMount: boolean;
1001
+ }> {}
1002
+
1003
+ export class CommandEmptyState {
1004
+ static create(opts: CommandEmptyStateOpts) {
1005
+ return new CommandEmptyState(opts, getCommandRoot());
1006
+ }
1007
+
1008
+ readonly opts: CommandEmptyStateOpts;
1009
+ readonly root: CommandRootState;
1010
+ readonly attachment: RefAttachment;
1011
+ readonly shouldRender = $derived.by(() => {
1012
+ return (this.root._commandState.filtered.count === 0 && this.#isInitialRender === false) || this.opts.forceMount.current;
1013
+ });
1014
+ #isInitialRender = true;
1015
+
1016
+ constructor(opts: CommandEmptyStateOpts, root: CommandRootState) {
1017
+ this.opts = opts;
1018
+ this.root = root;
1019
+ this.attachment = attachRef(this.opts.ref);
1020
+
1021
+ $effect.pre(() => {
1022
+ this.#isInitialRender = false;
1023
+ });
1024
+ }
1025
+
1026
+ readonly props = $derived.by(
1027
+ () =>
1028
+ ({
1029
+ id: this.opts.id.current,
1030
+ role: 'presentation',
1031
+ [commandAttrs.empty]: '',
1032
+ ...this.attachment,
1033
+ }) as const,
1034
+ );
1035
+ }
1036
+
1037
+ interface CommandGroupContainerStateOpts
1038
+ extends
1039
+ WithRefOpts,
1040
+ ReadableBoxedValues<{
1041
+ value: string;
1042
+ forceMount: boolean;
1043
+ }> {}
1044
+
1045
+ export class CommandGroupContainerState {
1046
+ static create(opts: CommandGroupContainerStateOpts) {
1047
+ return setCommandGroupContainer(new CommandGroupContainerState(opts, getCommandRoot()));
1048
+ }
1049
+
1050
+ readonly opts: CommandGroupContainerStateOpts;
1051
+ readonly root: CommandRootState;
1052
+ readonly attachment: RefAttachment;
1053
+ readonly shouldRender = $derived.by(() => {
1054
+ if (this.opts.forceMount.current) return true;
1055
+ if (this.root.opts.shouldFilter.current === false) return true;
1056
+ if (!this.root.commandState.search) return true;
1057
+ return this.root._commandState.filtered.groups.has(this.trueValue);
1058
+ });
1059
+
1060
+ headingNode = $state<HTMLElement | null>(null);
1061
+ trueValue = $state('');
1062
+
1063
+ constructor(opts: CommandGroupContainerStateOpts, root: CommandRootState) {
1064
+ this.opts = opts;
1065
+ this.root = root;
1066
+ this.attachment = attachRef(this.opts.ref);
1067
+ this.trueValue = opts.value.current ?? opts.id.current;
1068
+
1069
+ $effect(() => {
1070
+ this.trueValue;
1071
+ return untrack(() => {
1072
+ return this.root.registerGroup(this.trueValue);
1073
+ });
1074
+ });
1075
+
1076
+ $effect(() => {
1077
+ if (this.opts.value.current) {
1078
+ this.trueValue = this.opts.value.current;
1079
+ return this.root.registerValue(this.opts.value.current);
1080
+ } else if (this.headingNode && this.headingNode.textContent) {
1081
+ this.trueValue = this.headingNode.textContent.trim().toLowerCase();
1082
+ return this.root.registerValue(this.trueValue);
1083
+ } else {
1084
+ this.trueValue = `-----${this.opts.id.current}`;
1085
+ return this.root.registerValue(this.trueValue);
1086
+ }
1087
+ });
1088
+ }
1089
+
1090
+ readonly props = $derived.by(
1091
+ () =>
1092
+ ({
1093
+ id: this.opts.id.current,
1094
+ role: 'presentation',
1095
+ hidden: this.shouldRender ? undefined : true,
1096
+ 'data-value': this.trueValue,
1097
+ [commandAttrs.group]: '',
1098
+ ...this.attachment,
1099
+ }) as const,
1100
+ );
1101
+ }
1102
+
1103
+ interface CommandGroupHeadingStateOpts extends WithRefOpts {}
1104
+
1105
+ export class CommandGroupHeadingState {
1106
+ static create(opts: CommandGroupHeadingStateOpts) {
1107
+ return new CommandGroupHeadingState(opts, getCommandGroupContainer());
1108
+ }
1109
+
1110
+ readonly opts: CommandGroupHeadingStateOpts;
1111
+ readonly group: CommandGroupContainerState;
1112
+ readonly attachment: RefAttachment;
1113
+
1114
+ constructor(opts: CommandGroupHeadingStateOpts, group: CommandGroupContainerState) {
1115
+ this.opts = opts;
1116
+ this.group = group;
1117
+ this.attachment = attachRef(this.opts.ref, (v) => (this.group.headingNode = v));
1118
+ }
1119
+
1120
+ readonly props = $derived.by(
1121
+ () =>
1122
+ ({
1123
+ id: this.opts.id.current,
1124
+ [commandAttrs['group-heading']]: '',
1125
+ ...this.attachment,
1126
+ }) as const,
1127
+ );
1128
+ }
1129
+
1130
+ interface CommandGroupItemsStateOpts extends WithRefOpts {}
1131
+
1132
+ export class CommandGroupItemsState {
1133
+ static create(opts: CommandGroupItemsStateOpts) {
1134
+ return new CommandGroupItemsState(opts, getCommandGroupContainer());
1135
+ }
1136
+
1137
+ readonly opts: CommandGroupItemsStateOpts;
1138
+ readonly group: CommandGroupContainerState;
1139
+ readonly attachment: RefAttachment;
1140
+
1141
+ constructor(opts: CommandGroupItemsStateOpts, group: CommandGroupContainerState) {
1142
+ this.opts = opts;
1143
+ this.group = group;
1144
+ this.attachment = attachRef(this.opts.ref);
1145
+ }
1146
+
1147
+ readonly props = $derived.by(
1148
+ () =>
1149
+ ({
1150
+ id: this.opts.id.current,
1151
+ role: 'group',
1152
+ [commandAttrs['group-items']]: '',
1153
+ 'aria-labelledby': this.group.headingNode?.id ?? undefined,
1154
+ ...this.attachment,
1155
+ }) as const,
1156
+ );
1157
+ }
1158
+
1159
+ interface CommandInputStateOpts
1160
+ extends
1161
+ WithRefOpts,
1162
+ WritableBoxedValues<{
1163
+ value: string;
1164
+ }>,
1165
+ ReadableBoxedValues<{
1166
+ autofocus: boolean;
1167
+ }> {}
1168
+
1169
+ export class CommandInputState {
1170
+ static create(opts: CommandInputStateOpts) {
1171
+ return new CommandInputState(opts, getCommandRoot());
1172
+ }
1173
+
1174
+ readonly opts: CommandInputStateOpts;
1175
+ readonly root: CommandRootState;
1176
+ readonly attachment: RefAttachment;
1177
+ readonly #selectedItemId = $derived.by(() => {
1178
+ const item = this.root.viewportNode?.querySelector<HTMLElement>(
1179
+ `${COMMAND_ITEM_SELECTOR}[${COMMAND_VALUE_ATTR}="${CSS.escape(this.root.opts.value.current)}"]`,
1180
+ );
1181
+ if (item === undefined || item === null) return;
1182
+ return item.getAttribute('id') ?? undefined;
1183
+ });
1184
+
1185
+ constructor(opts: CommandInputStateOpts, root: CommandRootState) {
1186
+ this.opts = opts;
1187
+ this.root = root;
1188
+ this.attachment = attachRef(this.opts.ref, (v) => (this.root.inputNode = v));
1189
+ $effect(() => {
1190
+ this.opts.ref.current;
1191
+ untrack(() => {
1192
+ const node = this.opts.ref.current;
1193
+ if (node && this.opts.autofocus.current) {
1194
+ setTimeout(() => node.focus(), 10);
1195
+ }
1196
+ });
1197
+ });
1198
+
1199
+ $effect(() => {
1200
+ this.opts.value.current;
1201
+ untrack(() => {
1202
+ if (this.root.commandState.search !== this.opts.value.current) {
1203
+ this.root.setState('search', this.opts.value.current);
1204
+ }
1205
+ });
1206
+ });
1207
+ }
1208
+
1209
+ readonly props = $derived.by(
1210
+ () =>
1211
+ ({
1212
+ id: this.opts.id.current,
1213
+ type: 'text',
1214
+ [commandAttrs.input]: '',
1215
+ autocomplete: 'off',
1216
+ autocorrect: 'off',
1217
+ spellcheck: false,
1218
+ 'aria-autocomplete': 'list',
1219
+ role: 'combobox',
1220
+ 'aria-expanded': boolToStr(true),
1221
+ 'aria-controls': this.root.viewportNode?.id ?? undefined,
1222
+ 'aria-labelledby': this.root.labelNode?.id ?? undefined,
1223
+ 'aria-activedescendant': this.#selectedItemId,
1224
+ ...this.attachment,
1225
+ }) as const,
1226
+ );
1227
+ }
1228
+
1229
+ interface CommandItemStateOpts
1230
+ extends
1231
+ WithRefOpts,
1232
+ ReadableBoxedValues<{
1233
+ value: string;
1234
+ disabled: boolean;
1235
+ onSelect: () => void;
1236
+ forceMount: boolean;
1237
+ keywords: string[];
1238
+ }> {
1239
+ group: CommandGroupContainerState | null;
1240
+ }
1241
+
1242
+ export class CommandItemState {
1243
+ static create(opts: Omit<CommandItemStateOpts, 'group'>) {
1244
+ const group = hasCommandGroupContainer() ? getCommandGroupContainer() : null;
1245
+ return new CommandItemState({ ...opts, group }, getCommandRoot());
1246
+ }
1247
+ readonly opts: CommandItemStateOpts;
1248
+ readonly root: CommandRootState;
1249
+ readonly attachment: RefAttachment;
1250
+ readonly #trueForceMount = $derived.by(() => {
1251
+ return this.opts.forceMount.current || this.opts.group?.opts.forceMount.current === true;
1252
+ });
1253
+ readonly shouldRender = $derived.by(() => {
1254
+ this.opts.ref.current;
1255
+ if (this.#trueForceMount || this.root.opts.shouldFilter.current === false || !this.root.commandState.search) {
1256
+ return true;
1257
+ }
1258
+ const currentScore = this.root.commandState.filtered.items.get(this.trueValue);
1259
+ if (currentScore === undefined) return false;
1260
+ return currentScore > 0;
1261
+ });
1262
+
1263
+ readonly isSelected = $derived.by(() => this.root.opts.value.current === this.trueValue && this.trueValue !== '');
1264
+ trueValue = $state('');
1265
+
1266
+ constructor(opts: CommandItemStateOpts, root: CommandRootState) {
1267
+ this.opts = opts;
1268
+ this.root = root;
1269
+ this.trueValue = opts.value.current;
1270
+ this.attachment = attachRef(this.opts.ref);
1271
+
1272
+ $effect(() => {
1273
+ this.trueValue;
1274
+ this.opts.group?.trueValue;
1275
+ this.opts.forceMount.current;
1276
+ return untrack(() => {
1277
+ if (this.opts.forceMount.current || !this.trueValue) return;
1278
+ return this.root.registerItem(this.trueValue, this.opts.group?.trueValue);
1279
+ });
1280
+ });
1281
+
1282
+ $effect(() => {
1283
+ this.opts.value.current;
1284
+ this.opts.ref.current;
1285
+ untrack(() => {
1286
+ if (this.opts.value.current) {
1287
+ this.trueValue = this.opts.value.current;
1288
+ } else if (this.opts.ref.current?.textContent) {
1289
+ this.trueValue = this.opts.ref.current.textContent.trim();
1290
+ }
1291
+
1292
+ if (this.trueValue) {
1293
+ this.root.registerValue(
1294
+ this.trueValue,
1295
+ opts.keywords.current.map((kw) => kw.trim()),
1296
+ );
1297
+ this.opts.ref.current?.setAttribute(COMMAND_VALUE_ATTR, this.trueValue);
1298
+ }
1299
+ });
1300
+ });
1301
+
1302
+ // bindings
1303
+ this.onclick = this.onclick.bind(this);
1304
+ this.onpointermove = this.onpointermove.bind(this);
1305
+ }
1306
+
1307
+ #onSelect() {
1308
+ if (this.opts.disabled.current) return;
1309
+ this.#select();
1310
+ this.opts.onSelect?.current();
1311
+ }
1312
+
1313
+ #select() {
1314
+ if (this.opts.disabled.current) return;
1315
+ this.root.setValue(this.trueValue, true);
1316
+ }
1317
+
1318
+ onpointermove(_: BitsPointerEvent) {
1319
+ if (this.opts.disabled.current || this.root.opts.disablePointerSelection.current) return;
1320
+ this.#select();
1321
+ }
1322
+
1323
+ onclick(_: BitsMouseEvent) {
1324
+ if (this.opts.disabled.current) return;
1325
+ this.#onSelect();
1326
+ }
1327
+
1328
+ readonly props = $derived.by(
1329
+ () =>
1330
+ ({
1331
+ id: this.opts.id.current,
1332
+ 'aria-disabled': boolToStr(this.opts.disabled.current),
1333
+ 'aria-selected': boolToStr(this.isSelected),
1334
+ 'data-disabled': boolToEmptyStrOrUndef(this.opts.disabled.current),
1335
+ 'data-selected': boolToEmptyStrOrUndef(this.isSelected),
1336
+ 'data-value': this.trueValue,
1337
+ 'data-group': this.opts.group?.trueValue,
1338
+ [commandAttrs.item]: '',
1339
+ role: 'option',
1340
+ onpointermove: this.onpointermove,
1341
+ onclick: this.onclick,
1342
+ ...this.attachment,
1343
+ }) as const,
1344
+ );
1345
+ }
1346
+
1347
+ interface CommandLoadingStateOpts
1348
+ extends
1349
+ WithRefOpts,
1350
+ ReadableBoxedValues<{
1351
+ progress: number;
1352
+ }> {}
1353
+
1354
+ export class CommandLoadingState {
1355
+ static create(opts: CommandLoadingStateOpts) {
1356
+ return new CommandLoadingState(opts);
1357
+ }
1358
+
1359
+ readonly opts: CommandLoadingStateOpts;
1360
+ readonly attachment: RefAttachment;
1361
+ constructor(opts: CommandLoadingStateOpts) {
1362
+ this.opts = opts;
1363
+ this.attachment = attachRef(this.opts.ref);
1364
+ }
1365
+
1366
+ readonly props = $derived.by(
1367
+ () =>
1368
+ ({
1369
+ id: this.opts.id.current,
1370
+ role: 'progressbar',
1371
+ 'aria-valuenow': this.opts.progress.current,
1372
+ 'aria-valuemin': 0,
1373
+ 'aria-valuemax': 100,
1374
+ 'aria-label': 'Loading...',
1375
+ [commandAttrs.loading]: '',
1376
+ ...this.attachment,
1377
+ }) as const,
1378
+ );
1379
+ }
1380
+
1381
+ interface CommandSeparatorStateOpts
1382
+ extends
1383
+ WithRefOpts,
1384
+ ReadableBoxedValues<{
1385
+ forceMount: boolean;
1386
+ }> {}
1387
+
1388
+ export class CommandSeparatorState {
1389
+ static create(opts: CommandSeparatorStateOpts) {
1390
+ return new CommandSeparatorState(opts, getCommandRoot());
1391
+ }
1392
+
1393
+ readonly opts: CommandSeparatorStateOpts;
1394
+ readonly root: CommandRootState;
1395
+ readonly attachment: RefAttachment;
1396
+ readonly shouldRender = $derived.by(() => !this.root._commandState.search || this.opts.forceMount.current);
1397
+
1398
+ constructor(opts: CommandSeparatorStateOpts, root: CommandRootState) {
1399
+ this.opts = opts;
1400
+ this.root = root;
1401
+ this.attachment = attachRef(this.opts.ref);
1402
+ }
1403
+
1404
+ readonly props = $derived.by(
1405
+ () =>
1406
+ ({
1407
+ id: this.opts.id.current,
1408
+ // role="separator" cannot belong to a role="listbox"
1409
+ 'aria-hidden': 'true',
1410
+ [commandAttrs.separator]: '',
1411
+ ...this.attachment,
1412
+ }) as const,
1413
+ );
1414
+ }
1415
+
1416
+ interface CommandListStateOpts
1417
+ extends
1418
+ WithRefOpts,
1419
+ ReadableBoxedValues<{
1420
+ ariaLabel: string;
1421
+ }> {}
1422
+
1423
+ export class CommandListState {
1424
+ static create(opts: CommandListStateOpts) {
1425
+ return setCommandList(new CommandListState(opts, getCommandRoot()));
1426
+ }
1427
+
1428
+ readonly opts: CommandListStateOpts;
1429
+ readonly root: CommandRootState;
1430
+ readonly attachment: RefAttachment;
1431
+
1432
+ constructor(opts: CommandListStateOpts, root: CommandRootState) {
1433
+ this.opts = opts;
1434
+ this.root = root;
1435
+ this.attachment = attachRef(this.opts.ref);
1436
+ }
1437
+
1438
+ readonly props = $derived.by(
1439
+ () =>
1440
+ ({
1441
+ id: this.opts.id.current,
1442
+ role: 'listbox',
1443
+ 'aria-label': this.opts.ariaLabel.current,
1444
+ [commandAttrs.list]: '',
1445
+ ...this.attachment,
1446
+ }) as const,
1447
+ );
1448
+ }
1449
+
1450
+ interface CommandLabelStateOpts
1451
+ extends
1452
+ WithRefOpts,
1453
+ ReadableBoxedValues<{
1454
+ for?: string;
1455
+ }> {}
1456
+
1457
+ export class CommandLabelState {
1458
+ static create(opts: CommandLabelStateOpts) {
1459
+ return new CommandLabelState(opts, getCommandRoot());
1460
+ }
1461
+ readonly opts: CommandLabelStateOpts;
1462
+ readonly root: CommandRootState;
1463
+ readonly attachment: RefAttachment;
1464
+ constructor(opts: CommandLabelStateOpts, root: CommandRootState) {
1465
+ this.opts = opts;
1466
+ this.root = root;
1467
+ this.attachment = attachRef(this.opts.ref, (v) => (this.root.labelNode = v));
1468
+ }
1469
+
1470
+ readonly props = $derived.by(
1471
+ () =>
1472
+ ({
1473
+ id: this.opts.id.current,
1474
+ [commandAttrs['input-label']]: '',
1475
+ for: this.opts.for?.current,
1476
+ style: srOnlyStyles,
1477
+ ...this.attachment,
1478
+ }) as const,
1479
+ );
1480
+ }
1481
+
1482
+ interface CommandViewportStateOpts extends WithRefOpts {}
1483
+
1484
+ export class CommandViewportState {
1485
+ static create(opts: CommandViewportStateOpts) {
1486
+ return new CommandViewportState(opts, getCommandList());
1487
+ }
1488
+
1489
+ readonly opts: CommandViewportStateOpts;
1490
+ readonly list: CommandListState;
1491
+ readonly attachment: RefAttachment;
1492
+ constructor(opts: CommandViewportStateOpts, list: CommandListState) {
1493
+ this.opts = opts;
1494
+ this.list = list;
1495
+ this.attachment = attachRef(this.opts.ref, (v) => (this.list.root.viewportNode = v));
1496
+ }
1497
+
1498
+ readonly props = $derived.by(
1499
+ () =>
1500
+ ({
1501
+ id: this.opts.id.current,
1502
+ [commandAttrs.viewport]: '',
1503
+ ...this.attachment,
1504
+ }) as const,
1505
+ );
1506
+ }