@selvajs/ui 6.5.1-beta.1 → 6.6.0-beta.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (348) hide show
  1. package/dist/components/ConfirmDialog.svelte +2 -10
  2. package/dist/components/ErrorScreen.svelte +16 -26
  3. package/dist/components/compute/AppLayout.svelte +7 -13
  4. package/dist/components/compute/ComputeApp.svelte +3 -3
  5. package/dist/components/compute/ComputeMessagesDialog.svelte +23 -49
  6. package/dist/components/compute/ParameterPresetManager.svelte +24 -68
  7. package/dist/components/compute/SolveMessages.svelte +23 -31
  8. package/dist/components/layout/Pagination.svelte +2 -4
  9. package/dist/components/layout/SideNav.svelte +1 -1
  10. package/dist/components/preview/ChartOutput.svelte +1 -5
  11. package/dist/components/preview/Group.svelte +71 -102
  12. package/dist/components/preview/Group.svelte.d.ts +1 -1
  13. package/dist/components/preview/HelpButton.svelte +34 -0
  14. package/dist/components/preview/HelpButton.svelte.d.ts +8 -0
  15. package/dist/components/preview/ImageOutput.svelte +2 -4
  16. package/dist/components/preview/InputControl.svelte +45 -45
  17. package/dist/components/preview/OutputDisplay.svelte +12 -22
  18. package/dist/components/preview/TabBar.svelte +106 -22
  19. package/dist/components/preview/TabBar.svelte.d.ts +1 -0
  20. package/dist/components/preview/TabContent.svelte +27 -56
  21. package/dist/components/preview/TabLayout.svelte +3 -3
  22. package/dist/components/preview/inputs/CheckboxInput.svelte +7 -7
  23. package/dist/components/preview/inputs/CheckboxInput.svelte.d.ts +3 -0
  24. package/dist/components/preview/inputs/ColorInput.svelte +1 -1
  25. package/dist/components/preview/inputs/FileInput.svelte +10 -13
  26. package/dist/components/preview/inputs/NumberInput.svelte +37 -57
  27. package/dist/components/preview/inputs/TextInput.svelte +0 -1
  28. package/dist/components/primitives/CalculateButton.svelte +1 -1
  29. package/dist/components/primitives/Callout.svelte +11 -18
  30. package/dist/components/primitives/DataTable.svelte +3 -4
  31. package/dist/components/primitives/FilterableDropdown.svelte +42 -129
  32. package/dist/components/primitives/FilterableDropdown.svelte.d.ts +1 -1
  33. package/dist/components/primitives/IssueList.svelte +56 -0
  34. package/dist/components/primitives/IssueList.svelte.d.ts +20 -0
  35. package/dist/components/primitives/SeverityIcon.svelte +15 -0
  36. package/dist/components/primitives/SeverityIcon.svelte.d.ts +8 -0
  37. package/dist/components/primitives/SolvingIndicator.svelte +1 -29
  38. package/dist/components/primitives/StateDisplay.svelte +19 -79
  39. package/dist/components/primitives/ViewToggle.svelte +18 -33
  40. package/dist/components/primitives/alert-dialog/alert-dialog-action.svelte +9 -3
  41. package/dist/components/primitives/alert-dialog/alert-dialog-action.svelte.d.ts +6 -1
  42. package/dist/components/primitives/alert-dialog/alert-dialog-content.svelte +1 -1
  43. package/dist/components/primitives/alert-dialog/alert-dialog-overlay.svelte +1 -1
  44. package/dist/components/primitives/badge/badge.svelte +2 -2
  45. package/dist/components/primitives/badge/badge.svelte.d.ts +4 -4
  46. package/dist/components/primitives/button/button.svelte +3 -2
  47. package/dist/components/primitives/button/button.svelte.d.ts +7 -4
  48. package/dist/components/primitives/card/card-header.svelte +1 -1
  49. package/dist/components/primitives/card/card.svelte +21 -5
  50. package/dist/components/primitives/card/card.svelte.d.ts +25 -1
  51. package/dist/components/primitives/card/index.d.ts +1 -2
  52. package/dist/components/primitives/card/index.js +2 -3
  53. package/dist/components/primitives/checkbox/checkbox.svelte +1 -1
  54. package/dist/components/primitives/command/command-dialog.svelte +42 -0
  55. package/dist/components/primitives/command/command-dialog.svelte.d.ts +14 -0
  56. package/dist/components/primitives/command/command-empty.svelte +17 -0
  57. package/dist/components/primitives/command/command-empty.svelte.d.ts +4 -0
  58. package/dist/components/primitives/command/command-group.svelte +33 -0
  59. package/dist/components/primitives/command/command-group.svelte.d.ts +7 -0
  60. package/dist/components/primitives/command/command-input.svelte +36 -0
  61. package/dist/components/primitives/command/command-input.svelte.d.ts +4 -0
  62. package/dist/components/primitives/command/command-item.svelte +28 -0
  63. package/dist/components/primitives/command/command-item.svelte.d.ts +4 -0
  64. package/dist/components/primitives/command/command-link-item.svelte +21 -0
  65. package/dist/components/primitives/command/command-link-item.svelte.d.ts +4 -0
  66. package/dist/components/primitives/command/command-list.svelte +20 -0
  67. package/dist/components/primitives/command/command-list.svelte.d.ts +4 -0
  68. package/dist/components/primitives/command/command-loading.svelte +7 -0
  69. package/dist/components/primitives/command/command-loading.svelte.d.ts +4 -0
  70. package/dist/components/primitives/command/command-separator.svelte +17 -0
  71. package/dist/components/primitives/command/command-separator.svelte.d.ts +4 -0
  72. package/dist/components/primitives/command/command-shortcut.svelte +23 -0
  73. package/dist/components/primitives/command/command-shortcut.svelte.d.ts +5 -0
  74. package/dist/components/primitives/command/command.svelte +28 -0
  75. package/dist/components/primitives/command/command.svelte.d.ts +8 -0
  76. package/dist/components/primitives/command/index.d.ts +12 -0
  77. package/dist/components/primitives/command/index.js +14 -0
  78. package/dist/components/primitives/context-menu/context-menu-content.svelte +1 -1
  79. package/dist/components/primitives/context-menu/context-menu-sub-content.svelte +1 -1
  80. package/dist/components/primitives/dialog/dialog-content.svelte +5 -1
  81. package/dist/components/primitives/dialog/dialog-content.svelte.d.ts +2 -0
  82. package/dist/components/primitives/dialog/dialog-overlay.svelte +1 -1
  83. package/dist/components/primitives/dialog/dialog-title.svelte +6 -2
  84. package/dist/components/primitives/dialog/dialog-title.svelte.d.ts +5 -1
  85. package/dist/components/primitives/drawer/Drawer.svelte +1 -1
  86. package/dist/components/primitives/dropdown-menu/dropdown-menu-checkbox-group.svelte +16 -0
  87. package/dist/components/primitives/dropdown-menu/dropdown-menu-checkbox-group.svelte.d.ts +4 -0
  88. package/dist/components/primitives/dropdown-menu/dropdown-menu-checkbox-item.svelte +46 -0
  89. package/dist/components/primitives/dropdown-menu/dropdown-menu-checkbox-item.svelte.d.ts +9 -0
  90. package/dist/components/primitives/dropdown-menu/dropdown-menu-content.svelte +31 -0
  91. package/dist/components/primitives/dropdown-menu/dropdown-menu-content.svelte.d.ts +10 -0
  92. package/dist/components/primitives/dropdown-menu/dropdown-menu-group-heading.svelte +22 -0
  93. package/dist/components/primitives/dropdown-menu/dropdown-menu-group-heading.svelte.d.ts +8 -0
  94. package/dist/components/primitives/dropdown-menu/dropdown-menu-group.svelte +7 -0
  95. package/dist/components/primitives/dropdown-menu/dropdown-menu-group.svelte.d.ts +4 -0
  96. package/dist/components/primitives/dropdown-menu/dropdown-menu-item.svelte +27 -0
  97. package/dist/components/primitives/dropdown-menu/dropdown-menu-item.svelte.d.ts +8 -0
  98. package/dist/components/primitives/dropdown-menu/dropdown-menu-label.svelte +24 -0
  99. package/dist/components/primitives/dropdown-menu/dropdown-menu-label.svelte.d.ts +8 -0
  100. package/dist/components/primitives/dropdown-menu/dropdown-menu-portal.svelte +7 -0
  101. package/dist/components/primitives/dropdown-menu/dropdown-menu-portal.svelte.d.ts +3 -0
  102. package/dist/components/primitives/dropdown-menu/dropdown-menu-radio-group.svelte +16 -0
  103. package/dist/components/primitives/dropdown-menu/dropdown-menu-radio-group.svelte.d.ts +4 -0
  104. package/dist/components/primitives/dropdown-menu/dropdown-menu-radio-item.svelte +36 -0
  105. package/dist/components/primitives/dropdown-menu/dropdown-menu-radio-item.svelte.d.ts +4 -0
  106. package/dist/components/primitives/dropdown-menu/dropdown-menu-separator.svelte +17 -0
  107. package/dist/components/primitives/dropdown-menu/dropdown-menu-separator.svelte.d.ts +4 -0
  108. package/dist/components/primitives/dropdown-menu/dropdown-menu-shortcut.svelte +23 -0
  109. package/dist/components/primitives/dropdown-menu/dropdown-menu-shortcut.svelte.d.ts +5 -0
  110. package/dist/components/primitives/dropdown-menu/dropdown-menu-sub-content.svelte +31 -0
  111. package/dist/components/primitives/dropdown-menu/dropdown-menu-sub-content.svelte.d.ts +10 -0
  112. package/dist/components/primitives/dropdown-menu/dropdown-menu-sub-trigger.svelte +29 -0
  113. package/dist/components/primitives/dropdown-menu/dropdown-menu-sub-trigger.svelte.d.ts +7 -0
  114. package/dist/components/primitives/dropdown-menu/dropdown-menu-sub.svelte +7 -0
  115. package/dist/components/primitives/dropdown-menu/dropdown-menu-sub.svelte.d.ts +3 -0
  116. package/dist/components/primitives/dropdown-menu/dropdown-menu-trigger.svelte +7 -0
  117. package/dist/components/primitives/dropdown-menu/dropdown-menu-trigger.svelte.d.ts +4 -0
  118. package/dist/components/primitives/dropdown-menu/dropdown-menu.svelte +7 -0
  119. package/dist/components/primitives/dropdown-menu/dropdown-menu.svelte.d.ts +3 -0
  120. package/dist/components/primitives/dropdown-menu/index.d.ts +18 -0
  121. package/dist/components/primitives/dropdown-menu/index.js +18 -0
  122. package/dist/components/primitives/{alert/alert-description.svelte → empty/empty-content.svelte} +3 -3
  123. package/dist/components/primitives/empty/empty-content.svelte.d.ts +5 -0
  124. package/dist/components/primitives/empty/empty-description.svelte +23 -0
  125. package/dist/components/primitives/{alert/alert-description.svelte.d.ts → empty/empty-description.svelte.d.ts} +4 -4
  126. package/dist/components/primitives/{alert/alert-title.svelte → empty/empty-header.svelte} +3 -3
  127. package/dist/components/primitives/empty/empty-header.svelte.d.ts +5 -0
  128. package/dist/components/primitives/empty/empty-media.svelte +41 -0
  129. package/dist/components/primitives/empty/empty-media.svelte.d.ts +26 -0
  130. package/dist/components/primitives/{card/card-action.svelte → empty/empty-title.svelte} +2 -2
  131. package/dist/components/primitives/{card/card-action.svelte.d.ts → empty/empty-title.svelte.d.ts} +3 -3
  132. package/dist/components/primitives/empty/empty.svelte +23 -0
  133. package/dist/components/primitives/empty/empty.svelte.d.ts +5 -0
  134. package/dist/components/primitives/empty/index.d.ts +7 -0
  135. package/dist/components/primitives/empty/index.js +9 -0
  136. package/dist/components/primitives/index.d.ts +10 -1
  137. package/dist/components/primitives/index.js +10 -1
  138. package/dist/components/primitives/input/input.svelte +4 -4
  139. package/dist/components/primitives/input-group/index.d.ts +7 -0
  140. package/dist/components/primitives/input-group/index.js +9 -0
  141. package/dist/components/primitives/input-group/input-group-addon.svelte +53 -0
  142. package/dist/components/primitives/input-group/input-group-addon.svelte.d.ts +32 -0
  143. package/dist/components/primitives/input-group/input-group-button.svelte +49 -0
  144. package/dist/components/primitives/input-group/input-group-button.svelte.d.ts +32 -0
  145. package/dist/components/primitives/input-group/input-group-input.svelte +25 -0
  146. package/dist/components/primitives/input-group/input-group-input.svelte.d.ts +11 -0
  147. package/dist/components/primitives/input-group/input-group-text.svelte +22 -0
  148. package/dist/components/primitives/input-group/input-group-text.svelte.d.ts +5 -0
  149. package/dist/components/primitives/input-group/input-group-textarea.svelte +23 -0
  150. package/dist/components/primitives/input-group/input-group-textarea.svelte.d.ts +3 -0
  151. package/dist/components/primitives/input-group/input-group.svelte +24 -0
  152. package/dist/components/primitives/{alert/alert-title.svelte.d.ts → input-group/input-group.svelte.d.ts} +4 -4
  153. package/dist/components/primitives/mode-toggle/mode-toggle.svelte +2 -6
  154. package/dist/components/primitives/popover/index.d.ts +9 -0
  155. package/dist/components/primitives/popover/index.js +11 -0
  156. package/dist/components/primitives/popover/popover-close.svelte +7 -0
  157. package/dist/components/primitives/popover/popover-close.svelte.d.ts +4 -0
  158. package/dist/components/primitives/popover/popover-content.svelte +31 -0
  159. package/dist/components/primitives/popover/popover-content.svelte.d.ts +10 -0
  160. package/dist/components/primitives/popover/popover-description.svelte +20 -0
  161. package/dist/components/primitives/popover/popover-description.svelte.d.ts +5 -0
  162. package/dist/components/primitives/popover/popover-header.svelte +20 -0
  163. package/dist/components/primitives/popover/popover-header.svelte.d.ts +5 -0
  164. package/dist/components/primitives/popover/popover-portal.svelte +7 -0
  165. package/dist/components/primitives/popover/popover-portal.svelte.d.ts +3 -0
  166. package/dist/components/primitives/popover/popover-title.svelte +15 -0
  167. package/dist/components/primitives/popover/popover-title.svelte.d.ts +5 -0
  168. package/dist/components/primitives/popover/popover-trigger.svelte +17 -0
  169. package/dist/components/primitives/popover/popover-trigger.svelte.d.ts +4 -0
  170. package/dist/components/primitives/popover/popover.svelte +7 -0
  171. package/dist/components/primitives/popover/popover.svelte.d.ts +3 -0
  172. package/dist/components/primitives/resizable/resizable-handle.svelte +6 -2
  173. package/dist/components/primitives/resizable/resizable-handle.svelte.d.ts +2 -0
  174. package/dist/components/primitives/resizable/resizable-pane-group.svelte +1 -4
  175. package/dist/components/primitives/scroll-area/scroll-area.svelte +1 -1
  176. package/dist/components/primitives/search/search.svelte +3 -3
  177. package/dist/components/primitives/select/select-content.svelte +1 -1
  178. package/dist/components/primitives/select/select-trigger.svelte +1 -1
  179. package/dist/components/primitives/severity.d.ts +7 -0
  180. package/dist/components/primitives/severity.js +19 -0
  181. package/dist/components/primitives/slider/slider.svelte +2 -2
  182. package/dist/components/primitives/sonner/sonner.svelte +1 -1
  183. package/dist/components/primitives/spinner/index.d.ts +1 -0
  184. package/dist/components/primitives/spinner/index.js +1 -0
  185. package/dist/components/primitives/spinner/spinner.svelte +29 -0
  186. package/dist/components/primitives/spinner/spinner.svelte.d.ts +4 -0
  187. package/dist/components/primitives/switch/switch.svelte +2 -2
  188. package/dist/components/primitives/tabs/tabs-list.svelte +27 -5
  189. package/dist/components/primitives/tabs/tabs-list.svelte.d.ts +22 -1
  190. package/dist/components/primitives/tabs/tabs-trigger.svelte +23 -5
  191. package/dist/components/primitives/tabs/tabs-trigger.svelte.d.ts +22 -1
  192. package/dist/components/primitives/tabs/tabs.svelte +6 -2
  193. package/dist/components/primitives/tabs/tabs.svelte.d.ts +5 -1
  194. package/dist/components/primitives/textarea/textarea.svelte +1 -1
  195. package/dist/components/primitives/theme-switcher/ThemeSwitcher.svelte +1 -1
  196. package/dist/components/primitives/toggle/index.d.ts +3 -0
  197. package/dist/components/primitives/toggle/index.js +5 -0
  198. package/dist/components/primitives/toggle/toggle.svelte +52 -0
  199. package/dist/components/primitives/toggle/toggle.svelte.d.ts +43 -0
  200. package/dist/components/primitives/toggle-group/index.d.ts +3 -0
  201. package/dist/components/primitives/toggle-group/index.js +5 -0
  202. package/dist/components/primitives/toggle-group/toggle-group-item.svelte +35 -0
  203. package/dist/components/primitives/toggle-group/toggle-group-item.svelte.d.ts +6 -0
  204. package/dist/components/primitives/toggle-group/toggle-group.svelte +75 -0
  205. package/dist/components/primitives/toggle-group/toggle-group.svelte.d.ts +17 -0
  206. package/dist/components/viewer/MeshMetadataDialog.svelte +3 -3
  207. package/dist/components/viewer/SceneManager.svelte +14 -5
  208. package/dist/components/viewer/Viewer.svelte +120 -171
  209. package/dist/i18n/messages.d.ts +2 -0
  210. package/dist/i18n/messages.js +4 -0
  211. package/dist/styles/base.css +43 -0
  212. package/package.json +9 -9
  213. package/src/lib/components/ConfirmDialog.svelte +2 -10
  214. package/src/lib/components/ErrorScreen.svelte +16 -26
  215. package/src/lib/components/compute/AppLayout.svelte +7 -13
  216. package/src/lib/components/compute/ComputeApp.svelte +3 -3
  217. package/src/lib/components/compute/ComputeMessagesDialog.svelte +23 -49
  218. package/src/lib/components/compute/ParameterPresetManager.svelte +24 -68
  219. package/src/lib/components/compute/SolveMessages.svelte +23 -31
  220. package/src/lib/components/layout/Pagination.svelte +2 -4
  221. package/src/lib/components/layout/SideNav.svelte +1 -1
  222. package/src/lib/components/preview/ChartOutput.svelte +1 -5
  223. package/src/lib/components/preview/Group.svelte +71 -102
  224. package/src/lib/components/preview/HelpButton.svelte +34 -0
  225. package/src/lib/components/preview/ImageOutput.svelte +2 -4
  226. package/src/lib/components/preview/InputControl.svelte +45 -45
  227. package/src/lib/components/preview/OutputDisplay.svelte +12 -22
  228. package/src/lib/components/preview/TabBar.svelte +106 -22
  229. package/src/lib/components/preview/TabContent.svelte +27 -56
  230. package/src/lib/components/preview/TabLayout.svelte +3 -3
  231. package/src/lib/components/preview/inputs/CheckboxInput.svelte +7 -7
  232. package/src/lib/components/preview/inputs/ColorInput.svelte +1 -1
  233. package/src/lib/components/preview/inputs/FileInput.svelte +10 -13
  234. package/src/lib/components/preview/inputs/NumberInput.svelte +37 -57
  235. package/src/lib/components/preview/inputs/TextInput.svelte +0 -1
  236. package/src/lib/components/primitives/CalculateButton.svelte +1 -1
  237. package/src/lib/components/primitives/Callout.svelte +11 -18
  238. package/src/lib/components/primitives/DataTable.svelte +3 -4
  239. package/src/lib/components/primitives/FilterableDropdown.svelte +42 -129
  240. package/src/lib/components/primitives/IssueList.svelte +56 -0
  241. package/src/lib/components/primitives/SeverityIcon.svelte +15 -0
  242. package/src/lib/components/primitives/SolvingIndicator.svelte +1 -29
  243. package/src/lib/components/primitives/StateDisplay.svelte +19 -79
  244. package/src/lib/components/primitives/ViewToggle.svelte +18 -33
  245. package/src/lib/components/primitives/alert-dialog/alert-dialog-action.svelte +9 -3
  246. package/src/lib/components/primitives/alert-dialog/alert-dialog-content.svelte +1 -1
  247. package/src/lib/components/primitives/alert-dialog/alert-dialog-overlay.svelte +1 -1
  248. package/src/lib/components/primitives/badge/badge.svelte +2 -2
  249. package/src/lib/components/primitives/button/button.svelte +3 -2
  250. package/src/lib/components/primitives/card/card-header.svelte +1 -1
  251. package/src/lib/components/primitives/card/card.svelte +21 -5
  252. package/src/lib/components/primitives/card/index.ts +1 -4
  253. package/src/lib/components/primitives/checkbox/checkbox.svelte +1 -1
  254. package/src/lib/components/primitives/command/command-dialog.svelte +42 -0
  255. package/src/lib/components/primitives/command/command-empty.svelte +17 -0
  256. package/src/lib/components/primitives/command/command-group.svelte +33 -0
  257. package/src/lib/components/primitives/command/command-input.svelte +36 -0
  258. package/src/lib/components/primitives/command/command-item.svelte +28 -0
  259. package/src/lib/components/primitives/command/command-link-item.svelte +21 -0
  260. package/src/lib/components/primitives/command/command-list.svelte +20 -0
  261. package/src/lib/components/primitives/command/command-loading.svelte +7 -0
  262. package/src/lib/components/primitives/command/command-separator.svelte +17 -0
  263. package/src/lib/components/primitives/command/command-shortcut.svelte +23 -0
  264. package/src/lib/components/primitives/command/command.svelte +28 -0
  265. package/src/lib/components/primitives/command/index.ts +37 -0
  266. package/src/lib/components/primitives/context-menu/context-menu-content.svelte +1 -1
  267. package/src/lib/components/primitives/context-menu/context-menu-sub-content.svelte +1 -1
  268. package/src/lib/components/primitives/dialog/dialog-content.svelte +5 -1
  269. package/src/lib/components/primitives/dialog/dialog-overlay.svelte +1 -1
  270. package/src/lib/components/primitives/dialog/dialog-title.svelte +6 -2
  271. package/src/lib/components/primitives/drawer/Drawer.svelte +1 -1
  272. package/src/lib/components/primitives/dropdown-menu/dropdown-menu-checkbox-group.svelte +16 -0
  273. package/src/lib/components/primitives/dropdown-menu/dropdown-menu-checkbox-item.svelte +46 -0
  274. package/src/lib/components/primitives/dropdown-menu/dropdown-menu-content.svelte +31 -0
  275. package/src/lib/components/primitives/dropdown-menu/dropdown-menu-group-heading.svelte +22 -0
  276. package/src/lib/components/primitives/dropdown-menu/dropdown-menu-group.svelte +7 -0
  277. package/src/lib/components/primitives/dropdown-menu/dropdown-menu-item.svelte +27 -0
  278. package/src/lib/components/primitives/dropdown-menu/dropdown-menu-label.svelte +24 -0
  279. package/src/lib/components/primitives/dropdown-menu/dropdown-menu-portal.svelte +7 -0
  280. package/src/lib/components/primitives/dropdown-menu/dropdown-menu-radio-group.svelte +16 -0
  281. package/src/lib/components/primitives/dropdown-menu/dropdown-menu-radio-item.svelte +36 -0
  282. package/src/lib/components/primitives/dropdown-menu/dropdown-menu-separator.svelte +17 -0
  283. package/src/lib/components/primitives/dropdown-menu/dropdown-menu-shortcut.svelte +23 -0
  284. package/src/lib/components/primitives/dropdown-menu/dropdown-menu-sub-content.svelte +31 -0
  285. package/src/lib/components/primitives/dropdown-menu/dropdown-menu-sub-trigger.svelte +29 -0
  286. package/src/lib/components/primitives/dropdown-menu/dropdown-menu-sub.svelte +7 -0
  287. package/src/lib/components/primitives/dropdown-menu/dropdown-menu-trigger.svelte +7 -0
  288. package/src/lib/components/primitives/dropdown-menu/dropdown-menu.svelte +7 -0
  289. package/src/lib/components/primitives/dropdown-menu/index.ts +54 -0
  290. package/src/lib/components/primitives/{alert/alert-description.svelte → empty/empty-content.svelte} +3 -3
  291. package/src/lib/components/primitives/empty/empty-description.svelte +23 -0
  292. package/src/lib/components/primitives/{alert/alert-title.svelte → empty/empty-header.svelte} +3 -3
  293. package/src/lib/components/primitives/empty/empty-media.svelte +41 -0
  294. package/src/lib/components/primitives/{card/card-action.svelte → empty/empty-title.svelte} +2 -2
  295. package/src/lib/components/primitives/empty/empty.svelte +23 -0
  296. package/src/lib/components/primitives/empty/index.ts +22 -0
  297. package/src/lib/components/primitives/index.ts +10 -1
  298. package/src/lib/components/primitives/input/input.svelte +4 -4
  299. package/src/lib/components/primitives/input-group/index.ts +22 -0
  300. package/src/lib/components/primitives/input-group/input-group-addon.svelte +53 -0
  301. package/src/lib/components/primitives/input-group/input-group-button.svelte +49 -0
  302. package/src/lib/components/primitives/input-group/input-group-input.svelte +25 -0
  303. package/src/lib/components/primitives/input-group/input-group-text.svelte +22 -0
  304. package/src/lib/components/primitives/input-group/input-group-textarea.svelte +23 -0
  305. package/src/lib/components/primitives/input-group/input-group.svelte +24 -0
  306. package/src/lib/components/primitives/mode-toggle/mode-toggle.svelte +2 -6
  307. package/src/lib/components/primitives/popover/index.ts +28 -0
  308. package/src/lib/components/primitives/popover/popover-close.svelte +7 -0
  309. package/src/lib/components/primitives/popover/popover-content.svelte +31 -0
  310. package/src/lib/components/primitives/popover/popover-description.svelte +20 -0
  311. package/src/lib/components/primitives/popover/popover-header.svelte +20 -0
  312. package/src/lib/components/primitives/popover/popover-portal.svelte +7 -0
  313. package/src/lib/components/primitives/popover/popover-title.svelte +15 -0
  314. package/src/lib/components/primitives/popover/popover-trigger.svelte +17 -0
  315. package/src/lib/components/primitives/popover/popover.svelte +7 -0
  316. package/src/lib/components/primitives/resizable/resizable-handle.svelte +6 -2
  317. package/src/lib/components/primitives/resizable/resizable-pane-group.svelte +1 -4
  318. package/src/lib/components/primitives/scroll-area/scroll-area.svelte +1 -1
  319. package/src/lib/components/primitives/search/search.svelte +3 -3
  320. package/src/lib/components/primitives/select/select-content.svelte +1 -1
  321. package/src/lib/components/primitives/select/select-trigger.svelte +1 -1
  322. package/src/lib/components/primitives/severity.ts +26 -0
  323. package/src/lib/components/primitives/slider/slider.svelte +2 -2
  324. package/src/lib/components/primitives/sonner/sonner.svelte +1 -1
  325. package/src/lib/components/primitives/spinner/index.ts +1 -0
  326. package/src/lib/components/primitives/spinner/spinner.svelte +29 -0
  327. package/src/lib/components/primitives/switch/switch.svelte +2 -2
  328. package/src/lib/components/primitives/tabs/tabs-list.svelte +27 -5
  329. package/src/lib/components/primitives/tabs/tabs-trigger.svelte +23 -5
  330. package/src/lib/components/primitives/tabs/tabs.svelte +6 -2
  331. package/src/lib/components/primitives/textarea/textarea.svelte +1 -1
  332. package/src/lib/components/primitives/theme-switcher/ThemeSwitcher.svelte +1 -1
  333. package/src/lib/components/primitives/toggle/index.ts +13 -0
  334. package/src/lib/components/primitives/toggle/toggle.svelte +52 -0
  335. package/src/lib/components/primitives/toggle-group/index.ts +10 -0
  336. package/src/lib/components/primitives/toggle-group/toggle-group-item.svelte +35 -0
  337. package/src/lib/components/primitives/toggle-group/toggle-group.svelte +75 -0
  338. package/src/lib/components/viewer/MeshMetadataDialog.svelte +3 -3
  339. package/src/lib/components/viewer/SceneManager.svelte +14 -5
  340. package/src/lib/components/viewer/Viewer.svelte +120 -171
  341. package/src/lib/i18n/messages.ts +6 -0
  342. package/src/lib/styles/base.css +43 -0
  343. package/dist/components/primitives/alert/alert.svelte +0 -44
  344. package/dist/components/primitives/alert/alert.svelte.d.ts +0 -26
  345. package/dist/components/primitives/alert/index.d.ts +0 -5
  346. package/dist/components/primitives/alert/index.js +0 -7
  347. package/src/lib/components/primitives/alert/alert.svelte +0 -44
  348. package/src/lib/components/primitives/alert/index.ts +0 -14
@@ -83,60 +83,31 @@
83
83
  {/if}
84
84
  {/snippet}
85
85
 
86
- <Tabs.Content value={tab.id} class="min-h-0 p-0 flex-1">
87
- <ScrollArea class="h-full" orientation="vertical">
88
- <div class="p-4 tab-content-container">
89
- {#if visibleGroups.length === 0}
90
- <StateDisplay type="empty" size="medium" message={t.tabNoGroups} />
91
- {:else}
92
- <div class="gap-8 flex flex-col">
93
- {#each visibleGroups as group (group.id)}
94
- <Group
95
- label={group.label}
96
- description={group.description}
97
- items={group.items}
98
- columns={group.columns ?? 1}
99
- collapsed={collapsedGroups[group.id] ?? false}
100
- {values}
101
- onToggle={() => onToggleGroup(group.id)}
102
- inputSnippet={renderInput}
103
- outputSnippet={renderOutput}
104
- flat={visibleGroups.length === 1}
105
- />
106
- {/each}
107
- </div>
108
- {/if}
109
- </div>
110
- </ScrollArea>
86
+ <Tabs.Content value={tab.id} class="min-h-0">
87
+ <div class="h-full bg-muted dark:bg-background">
88
+ <ScrollArea class="h-full" orientation="vertical">
89
+ <div class="px-3">
90
+ {#if visibleGroups.length === 0}
91
+ <StateDisplay type="empty" size="medium" message={t.tabNoGroups} />
92
+ {:else}
93
+ <div class="gap-3 py-3 flex flex-col">
94
+ {#each visibleGroups as group (group.id)}
95
+ <Group
96
+ label={group.label}
97
+ description={group.description}
98
+ items={group.items}
99
+ columns={group.columns ?? 1}
100
+ collapsed={collapsedGroups[group.id] ?? false}
101
+ {values}
102
+ onToggle={() => onToggleGroup(group.id)}
103
+ inputSnippet={renderInput}
104
+ outputSnippet={renderOutput}
105
+ flat={visibleGroups.length === 1}
106
+ />
107
+ {/each}
108
+ </div>
109
+ {/if}
110
+ </div>
111
+ </ScrollArea>
112
+ </div>
111
113
  </Tabs.Content>
112
-
113
- <style>
114
- .tab-content-container {
115
- container-type: inline-size;
116
- }
117
-
118
- @container (max-width: 320px) {
119
- :global(.schema-grid) {
120
- grid-template-columns: 1fr;
121
- }
122
- }
123
- @container (min-width: 321px) and (max-width: 560px) {
124
- :global(.schema-grid) {
125
- grid-template-columns: repeat(min(2, var(--schema-cols)), minmax(0, 1fr));
126
- }
127
- }
128
-
129
- /* Fallback for browsers without container query support */
130
- @supports not (container-type: inline-size) {
131
- @media (max-width: 639px) {
132
- :global(.schema-grid) {
133
- grid-template-columns: 1fr;
134
- }
135
- }
136
- @media (min-width: 640px) and (max-width: 1023px) {
137
- :global(.schema-grid) {
138
- grid-template-columns: repeat(min(2, var(--schema-cols)), minmax(0, 1fr));
139
- }
140
- }
141
- }
142
- </style>
@@ -66,10 +66,10 @@
66
66
  }
67
67
  </script>
68
68
 
69
- <Card.Root class="pt-1 min-h-0 mx-1 flex flex-1 flex-col overflow-hidden">
70
- <Tabs.Root value={activeTabId} class="gap-0 min-h-0 flex flex-1 flex-col">
69
+ <Card.Root variant="flush" class="min-h-0 mx-1 flex-1">
70
+ <Tabs.Root value={activeTabId} variant="line" class="min-h-0 flex-1">
71
71
  {#if showTabBar}
72
- <TabBar tabs={visibleTabs} onTabChange={handleTabChange} />
72
+ <TabBar tabs={visibleTabs} {activeTabId} onTabChange={handleTabChange} />
73
73
  {/if}
74
74
 
75
75
  {#each visibleTabs as tab (tab.id)}
@@ -1,19 +1,21 @@
1
1
  <script lang="ts">
2
+ import type { Snippet } from 'svelte';
2
3
  import type { SupportedTypes } from '@selvajs/schemas';
3
4
  import { Checkbox } from '$lib/components/primitives/checkbox';
4
- import * as Field from '$lib/components/primitives/field';
5
5
 
6
6
  interface Props {
7
7
  inputId: string;
8
8
  value?: boolean;
9
9
  onChange: (value: SupportedTypes) => void;
10
10
  disabled?: boolean;
11
+ /** Rendered beside the box; must contain a label pointing at `inputId`. */
12
+ children?: Snippet;
11
13
  }
12
14
 
13
- let { inputId, value = $bindable(), onChange, disabled = false }: Props = $props();
15
+ let { inputId, value = $bindable(), onChange, disabled = false, children }: Props = $props();
14
16
  </script>
15
17
 
16
- <Field.Field orientation="horizontal">
18
+ <div class="gap-2.5 min-h-6 min-w-0 flex items-center">
17
19
  <Checkbox
18
20
  id={inputId}
19
21
  checked={typeof value === 'boolean' ? value : false}
@@ -23,7 +25,5 @@
23
25
  }}
24
26
  {disabled}
25
27
  />
26
- <Field.Label for={inputId} class="font-normal cursor-pointer text-muted-foreground">
27
- Enabled
28
- </Field.Label>
29
- </Field.Field>
28
+ {@render children?.()}
29
+ </div>
@@ -11,7 +11,7 @@
11
11
  <input
12
12
  type="color"
13
13
  value={value || '#000000'}
14
- class="h-9 w-12 rounded p-1 cursor-pointer border border-input bg-background"
14
+ class="h-9 w-12 rounded p-1 cursor-pointer border border-input bg-background dark:bg-input/30"
15
15
  onchange={(e) => {
16
16
  const newValue = (e.currentTarget as HTMLInputElement).value;
17
17
  onChange(newValue);
@@ -257,7 +257,7 @@
257
257
  const acceptAttribute = $derived(acceptedFormats.length > 0 ? acceptedFormats.join(',') : '*');
258
258
  </script>
259
259
 
260
- <div class="gap-4 flex w-full flex-col">
260
+ <div class="gap-2.5 flex w-full flex-col">
261
261
  <input
262
262
  bind:this={fileInput}
263
263
  type="file"
@@ -267,15 +267,16 @@
267
267
  />
268
268
 
269
269
  {#if showToggle}
270
- <div class="rounded text-xs flex overflow-hidden border">
270
+ <div class="p-0.5 text-xs flex rounded-md bg-muted">
271
271
  {#each effectiveModes as mode (mode)}
272
272
  <button
273
273
  type="button"
274
274
  onclick={() => (activeMode = mode)}
275
- class={`px-3 py-1 flex-1 transition-colors ${
275
+ aria-pressed={activeMode === mode}
276
+ class={`px-3 py-1 font-medium flex-1 rounded-sm transition-colors ${
276
277
  activeMode === mode
277
- ? 'bg-primary text-primary-foreground'
278
- : 'text-muted-foreground hover:bg-accent'
278
+ ? 'shadow-xs bg-background text-foreground'
279
+ : 'text-muted-foreground hover:text-foreground'
279
280
  }`}
280
281
  >
281
282
  {mode === 'upload' ? 'Upload' : 'URL'}
@@ -286,7 +287,7 @@
286
287
 
287
288
  {#if activeMode === 'url'}
288
289
  <div class="gap-2 flex flex-col">
289
- <Label for="url-input" class="gap-2 flex items-center">
290
+ <Label for="url-input">
290
291
  <Link size={16} />
291
292
  File URL
292
293
  </Label>
@@ -297,7 +298,7 @@
297
298
  bind:value={urlInput}
298
299
  placeholder="https://example.com/model.3dm"
299
300
  disabled={isLoading}
300
- class={urlError ? 'border-destructive focus-visible:ring-destructive' : ''}
301
+ aria-invalid={urlError ? true : undefined}
301
302
  oninput={() => {
302
303
  urlError = null;
303
304
  urlSuccess = '';
@@ -342,14 +343,10 @@
342
343
  </div>
343
344
  {:else}
344
345
  <div class="gap-2 flex flex-col">
345
- <Label for="file-upload" class="gap-2 flex items-center">
346
- <FileUp size={16} />
347
- Upload File
348
- </Label>
349
346
  <Button
350
347
  type="button"
351
- variant="outline"
352
- class="gap-2 w-full {isDragging ? 'border-primary' : ''}"
348
+ variant={isDragging ? 'secondary' : 'outline'}
349
+ class="w-full"
353
350
  onclick={openFilePicker}
354
351
  ondragover={handleDragOver}
355
352
  ondragleave={handleDragLeave}
@@ -3,6 +3,8 @@
3
3
  import { debounce } from '$lib/utils/debounce';
4
4
  import { Input } from '$lib/components/primitives/input';
5
5
  import { Slider } from '$lib/components/primitives/slider';
6
+ import * as InputGroup from '$lib/components/primitives/input-group';
7
+ import { Separator } from '$lib/components/primitives/separator';
6
8
  import * as Field from '$lib/components/primitives/field';
7
9
 
8
10
  interface Props {
@@ -16,8 +18,6 @@
16
18
  let { inputId, value = $bindable(), config, onChange, disabled = false }: Props = $props();
17
19
 
18
20
  let validationError = $state<string | null>(null);
19
- let sliderEditing = $state(false);
20
- let sliderInputValue = $state('');
21
21
 
22
22
  const min = $derived(config?.minimum ?? 0);
23
23
  const max = $derived(config?.maximum ?? 100);
@@ -49,72 +49,53 @@
49
49
  commitSliderDebounced(newValue);
50
50
  }
51
51
 
52
- function focusSliderInput(node: HTMLInputElement) {
53
- node.focus();
54
- node.select();
55
- }
52
+ const decimals = $derived(Math.max(0, -Math.floor(Math.log10(step))));
56
53
 
57
- function decimalPlaces(stepSize: number): number {
58
- return Math.max(0, -Math.floor(Math.log10(stepSize)));
54
+ function commitTypedValue(input: HTMLInputElement) {
55
+ const parsed = parseFloat(input.value);
56
+ if (isNaN(parsed)) {
57
+ input.value = numValue.toFixed(decimals);
58
+ return;
59
+ }
60
+ const clamped = Math.min(max, Math.max(min, parsed));
61
+ input.value = clamped.toFixed(decimals);
62
+ if (clamped === value) return;
63
+ value = clamped;
64
+ onChange(clamped);
59
65
  }
60
66
  </script>
61
67
 
62
68
  {#if config?.renderAsSlider}
63
- {@const dp = decimalPlaces(step)}
64
- <div class="gap-4 flex items-center">
65
- <Slider
66
- type="single"
67
- value={numValue}
69
+ <!-- Value and track share one box, the height of a number input, so mixed rows line up and
70
+ a value can't be read as belonging to the neighbouring slider. -->
71
+ <InputGroup.Root>
72
+ <InputGroup.Input
73
+ id={inputId}
74
+ type="number"
75
+ class="w-18 flex-none"
76
+ value={numValue.toFixed(decimals)}
68
77
  {min}
69
78
  {max}
70
79
  {step}
71
- class="flex-1"
72
- onValueChange={handleSliderChange}
73
80
  {disabled}
81
+ onchange={(e) => commitTypedValue(e.currentTarget)}
82
+ onkeydown={(e) => {
83
+ if (e.key === 'Enter') e.currentTarget.blur();
84
+ }}
74
85
  />
75
- {#if sliderEditing}
76
- <input
77
- use:focusSliderInput
78
- type="number"
86
+ <Separator orientation="vertical" />
87
+ <div class="px-3.5 min-w-0 flex flex-1 items-center">
88
+ <Slider
89
+ type="single"
90
+ value={numValue}
91
+ {min}
92
+ {max}
79
93
  {step}
80
- class="min-w-12 w-16 text-sm border-b border-border bg-transparent text-right outline-none focus:border-foreground"
81
- bind:value={sliderInputValue}
82
- onblur={() => {
83
- const parsed = parseFloat(sliderInputValue);
84
- if (!isNaN(parsed)) handleSliderChange(Math.min(max, Math.max(min, parsed)));
85
- sliderEditing = false;
86
- }}
87
- onkeydown={(e) => {
88
- if (e.key === 'Enter') {
89
- const parsed = parseFloat(sliderInputValue);
90
- if (!isNaN(parsed)) handleSliderChange(Math.min(max, Math.max(min, parsed)));
91
- sliderEditing = false;
92
- } else if (e.key === 'Escape') {
93
- sliderEditing = false;
94
- }
95
- }}
94
+ onValueChange={handleSliderChange}
95
+ {disabled}
96
96
  />
97
- {:else}
98
- <span
99
- role="button"
100
- tabindex={0}
101
- class="min-w-12 text-sm cursor-text text-right text-muted-foreground select-none"
102
- title="Double-click to edit"
103
- ondblclick={() => {
104
- sliderInputValue = numValue.toFixed(dp);
105
- sliderEditing = true;
106
- }}
107
- onkeydown={(e) => {
108
- if (e.key === 'Enter' || e.key === ' ') {
109
- sliderInputValue = numValue.toFixed(dp);
110
- sliderEditing = true;
111
- }
112
- }}
113
- >
114
- {numValue.toFixed(dp)}
115
- </span>
116
- {/if}
117
- </div>
97
+ </div>
98
+ </InputGroup.Root>
118
99
  {:else}
119
100
  <Input
120
101
  id={inputId}
@@ -126,7 +107,6 @@
126
107
  placeholder={config?.placeholder}
127
108
  data-invalid={validationError ? true : undefined}
128
109
  aria-invalid={validationError ? true : undefined}
129
- class={validationError ? 'border-destructive' : ''}
130
110
  {disabled}
131
111
  oninput={() => (validationError = null)}
132
112
  onchange={(e) => {
@@ -51,7 +51,6 @@
51
51
  maxlength={config?.maxLength}
52
52
  data-invalid={validationError ? true : undefined}
53
53
  aria-invalid={validationError ? true : undefined}
54
- class={validationError ? 'border-destructive' : ''}
55
54
  {disabled}
56
55
  oninput={() => {
57
56
  validationError = null;
@@ -24,7 +24,7 @@
24
24
  disabled={!hasPendingChanges && !isSolving}
25
25
  aria-busy={isSolving}
26
26
  class="gap-2 font-medium w-full transition-all duration-200
27
- {hasPendingChanges && !isSolving ? 'selva-pending-pulse shadow-md' : ''}
27
+ {hasPendingChanges && !isSolving ? 'shadow-md' : ''}
28
28
  {!hasPendingChanges && !isSolving ? 'text-muted-foreground' : ''}"
29
29
  >
30
30
  {#if isSolving}
@@ -1,7 +1,7 @@
1
1
  <script lang="ts" module>
2
2
  import { type VariantProps, tv } from 'tailwind-variants';
3
- import { Info, TriangleAlert, CircleCheck, CircleX, Lightbulb } from '@lucide/svelte';
4
3
  import type { Component } from 'svelte';
4
+ import { SEVERITY_ICON, SEVERITY_TEXT, type Severity } from './severity';
5
5
 
6
6
  export const calloutVariants = tv({
7
7
  base: 'flex items-start gap-2.5 rounded-lg border px-3.5 py-3 text-sm',
@@ -19,20 +19,12 @@
19
19
 
20
20
  export type CalloutTone = NonNullable<VariantProps<typeof calloutVariants>['tone']>;
21
21
 
22
- const TONE_ICON: Record<CalloutTone, Component> = {
23
- info: Info,
24
- tip: Lightbulb,
25
- success: CircleCheck,
26
- warning: TriangleAlert,
27
- danger: CircleX
28
- };
29
-
30
- const TONE_ACCENT: Record<CalloutTone, string> = {
31
- info: 'text-info',
32
- tip: 'text-muted-foreground',
33
- success: 'text-success',
34
- warning: 'text-warning',
35
- danger: 'text-destructive'
22
+ const TONE_SEVERITY: Record<CalloutTone, Severity> = {
23
+ info: 'info',
24
+ tip: 'note',
25
+ success: 'success',
26
+ warning: 'warning',
27
+ danger: 'error'
36
28
  };
37
29
  </script>
38
30
 
@@ -51,7 +43,8 @@
51
43
 
52
44
  let { tone = 'info', title, icon, class: className, children, ...restProps }: Props = $props();
53
45
 
54
- const Icon = $derived(icon === undefined ? TONE_ICON[tone] : icon);
46
+ const Icon = $derived(icon === undefined ? SEVERITY_ICON[TONE_SEVERITY[tone]] : icon);
47
+ const accent = $derived(SEVERITY_TEXT[TONE_SEVERITY[tone]]);
55
48
  </script>
56
49
 
57
50
  <div
@@ -60,11 +53,11 @@
60
53
  {...restProps}
61
54
  >
62
55
  {#if Icon}
63
- <Icon class={cn('mt-0.5 h-4 w-4 shrink-0', TONE_ACCENT[tone])} />
56
+ <Icon class={cn('mt-0.5 h-4 w-4 shrink-0', accent)} />
64
57
  {/if}
65
58
  <div class="min-w-0 space-y-1">
66
59
  {#if title}
67
- <p class={cn('leading-tight font-medium', TONE_ACCENT[tone])}>{title}</p>
60
+ <p class={cn('leading-tight font-medium', accent)}>{title}</p>
68
61
  {/if}
69
62
  <div class="leading-relaxed text-muted-foreground [&_a]:underline [&_strong]:text-foreground">
70
63
  {@render children()}
@@ -22,17 +22,16 @@
22
22
  const gridTemplate = $derived(columns.map((c) => c.width ?? '1fr').join(' '));
23
23
  </script>
24
24
 
25
- <div class={`divide-y rounded-lg border ${className}`}>
25
+ <div class={`divide-y rounded-lg border ${className}`} style:--table-cols={gridTemplate}>
26
26
  <div
27
- class="gap-4 px-4 py-2 text-xs font-medium tracking-wide grid bg-muted/40 text-muted-foreground uppercase"
28
- style:grid-template-columns={gridTemplate}
27
+ class="gap-4 px-4 py-2 text-xs font-medium tracking-wide grid grid-cols-(--table-cols) bg-muted/40 text-muted-foreground uppercase"
29
28
  >
30
29
  {#each columns as col (col.label)}
31
30
  <span class={col.align === 'right' ? 'text-right' : ''}>{col.label}</span>
32
31
  {/each}
33
32
  </div>
34
33
  {#each rows as item (getKey(item))}
35
- <div class="gap-4 px-4 py-3 grid items-center" style:grid-template-columns={gridTemplate}>
34
+ <div class="gap-4 px-4 py-3 grid grid-cols-(--table-cols) items-center">
36
35
  {@render row(item)}
37
36
  </div>
38
37
  {/each}
@@ -1,14 +1,16 @@
1
1
  <script lang="ts">
2
2
  import type { Component } from 'svelte';
3
- import { Search, ChevronDown, Check } from '@lucide/svelte';
3
+ import { ChevronDown } from '@lucide/svelte';
4
4
  import { cn } from '$lib/utils.js';
5
+ import { buttonVariants } from './button';
6
+ import * as Popover from './popover';
7
+ import * as Command from './command';
5
8
 
6
9
  export interface FilterableDropdownItem {
7
10
  id: string;
8
11
  label: string;
9
12
  icon?: Component;
10
13
  iconClass?: string;
11
- iconStyle?: string;
12
14
  }
13
15
 
14
16
  interface Props {
@@ -19,6 +21,7 @@
19
21
  placeholder?: string;
20
22
  triggerIcon?: Component;
21
23
  searchPlaceholder?: string;
24
+ /** Above this many items the list gets a filter box. */
22
25
  searchThreshold?: number;
23
26
  disabled?: boolean;
24
27
  class?: string;
@@ -38,149 +41,59 @@
38
41
  }: Props = $props();
39
42
 
40
43
  let open = $state(false);
41
- let query = $state('');
42
- let highlighted = $state(0);
43
- let inputRef = $state<HTMLInputElement>();
44
- let containerRef = $state<HTMLDivElement>();
45
44
 
46
45
  const selected = $derived(items.find((item) => item.id === value) ?? null);
47
- const showSearch = $derived(items.length > searchThreshold);
48
-
49
- const matches = $derived.by(() => {
50
- const q = query.trim().toLowerCase();
51
- if (!q) return items;
52
- return items.filter((item) => item.label.toLowerCase().includes(q));
53
- });
54
-
55
- $effect(() => {
56
- void matches;
57
- highlighted = 0;
58
- });
59
-
60
- $effect(() => {
61
- if (open) {
62
- queueMicrotask(() => inputRef?.focus());
63
- } else {
64
- query = '';
65
- }
66
- });
46
+ const SelectedIcon = $derived(selected?.icon ?? TriggerIcon);
67
47
 
68
48
  function pick(item: FilterableDropdownItem) {
69
49
  onChange(item.id);
70
50
  open = false;
71
51
  }
72
-
73
- function handleKey(e: KeyboardEvent) {
74
- if (e.key === 'ArrowDown') {
75
- e.preventDefault();
76
- highlighted = Math.min(highlighted + 1, matches.length - 1);
77
- } else if (e.key === 'ArrowUp') {
78
- e.preventDefault();
79
- highlighted = Math.max(highlighted - 1, 0);
80
- } else if (e.key === 'Enter') {
81
- e.preventDefault();
82
- const m = matches[highlighted];
83
- if (m) pick(m);
84
- } else if (e.key === 'Escape') {
85
- e.preventDefault();
86
- open = false;
87
- }
88
- }
89
-
90
- function handleDocClick(e: MouseEvent) {
91
- if (!open) return;
92
- if (containerRef && !containerRef.contains(e.target as Node)) open = false;
93
- }
94
-
95
- $effect(() => {
96
- if (!open) return;
97
- document.addEventListener('mousedown', handleDocClick);
98
- return () => document.removeEventListener('mousedown', handleDocClick);
99
- });
100
-
101
- const SelectedIcon = $derived(selected?.icon);
102
52
  </script>
103
53
 
104
- <div bind:this={containerRef} class={cn('relative', className)}>
105
- <button
106
- type="button"
54
+ <Popover.Root bind:open>
55
+ <Popover.Trigger
107
56
  {id}
108
57
  {disabled}
109
- onclick={() => (open = !open)}
58
+ role="combobox"
59
+ aria-expanded={open}
110
60
  class={cn(
111
- 'h-10 gap-2 px-3 text-sm flex w-full items-center rounded-md border bg-background text-left transition-colors disabled:cursor-not-allowed disabled:opacity-50',
112
- open ? 'border-ring ring-2 ring-ring/30' : 'border-input hover:bg-muted/40'
61
+ buttonVariants({ variant: 'outline' }),
62
+ 'font-normal w-full justify-start',
63
+ className
113
64
  )}
114
65
  >
115
66
  {#if SelectedIcon}
116
- <SelectedIcon
117
- class={cn('h-3.5 w-3.5 shrink-0', selected?.iconClass ?? 'text-muted-foreground')}
118
- style={selected?.iconStyle}
119
- />
120
- {:else if TriggerIcon}
121
- <TriggerIcon class="h-3.5 w-3.5 shrink-0 text-muted-foreground" />
67
+ <SelectedIcon class={cn('size-3.5', selected?.iconClass ?? 'text-muted-foreground')} />
122
68
  {/if}
123
- <span class={cn('min-w-0 flex-1 truncate', selected ? '' : 'text-muted-foreground')}>
69
+ <span class={cn('min-w-0 flex-1 truncate text-left', !selected && 'text-muted-foreground')}>
124
70
  {selected ? selected.label : placeholder}
125
71
  </span>
126
- <ChevronDown
127
- class={cn(
128
- 'h-3.5 w-3.5 shrink-0 text-muted-foreground transition-transform',
129
- open ? 'rotate-180' : ''
130
- )}
131
- />
132
- </button>
133
-
134
- {#if open}
135
- <div
136
- class="right-0 left-0 mt-1 shadow-md absolute top-full z-50 overflow-hidden rounded-md border border-border bg-popover"
137
- >
138
- {#if showSearch}
139
- <div class="relative border-b border-border">
140
- <Search
141
- class="left-3 h-3.5 w-3.5 pointer-events-none absolute top-1/2 -translate-y-1/2 text-muted-foreground"
142
- />
143
- <input
144
- bind:this={inputRef}
145
- bind:value={query}
146
- onkeydown={handleKey}
147
- placeholder={searchPlaceholder}
148
- class="h-8 pr-3 pl-9 text-sm w-full bg-transparent outline-none placeholder:text-muted-foreground"
149
- />
150
- </div>
72
+ <ChevronDown class="size-3.5 text-muted-foreground" />
73
+ </Popover.Trigger>
74
+ <Popover.Content align="start" class="p-0 w-(--bits-popover-anchor-width)">
75
+ <Command.Root>
76
+ {#if items.length > searchThreshold}
77
+ <Command.Input placeholder={searchPlaceholder} />
151
78
  {/if}
152
-
153
- <div class="max-h-60 overflow-y-auto">
154
- {#if matches.length === 0}
155
- <p class="px-3 py-3 text-xs text-center text-muted-foreground">
156
- No matches for "{query}"
157
- </p>
158
- {:else}
159
- {#each matches as item, i (item.id)}
160
- {@const ItemIcon = item.icon}
161
- <button
162
- type="button"
163
- onclick={() => pick(item)}
164
- onmouseenter={() => (highlighted = i)}
165
- class={cn(
166
- 'gap-2 px-3 py-1.5 text-sm flex w-full items-center text-left transition-colors',
167
- highlighted === i ? 'bg-accent text-accent-foreground' : 'hover:bg-muted/60'
168
- )}
169
- >
170
- {#if ItemIcon}
171
- <ItemIcon
172
- class={cn('h-3.5 w-3.5 shrink-0', item.iconClass ?? 'text-muted-foreground')}
173
- style={item.iconStyle}
174
- />
175
- {/if}
176
- <span class="flex-1 truncate">{item.label}</span>
177
- {#if item.id === value}
178
- <Check class="h-3.5 w-3.5 shrink-0 text-primary" />
179
- {/if}
180
- </button>
181
- {/each}
182
- {/if}
183
- </div>
184
- </div>
185
- {/if}
186
- </div>
79
+ <Command.List>
80
+ <Command.Empty>No matches.</Command.Empty>
81
+ {#each items as item (item.id)}
82
+ {@const ItemIcon = item.icon}
83
+ <!-- Filter on the label; the id is only the key. -->
84
+ <Command.Item
85
+ value={item.id}
86
+ keywords={[item.label]}
87
+ data-checked={item.id === value}
88
+ onSelect={() => pick(item)}
89
+ >
90
+ {#if ItemIcon}
91
+ <ItemIcon class={cn('size-3.5', item.iconClass ?? 'text-muted-foreground')} />
92
+ {/if}
93
+ <span class="truncate">{item.label}</span>
94
+ </Command.Item>
95
+ {/each}
96
+ </Command.List>
97
+ </Command.Root>
98
+ </Popover.Content>
99
+ </Popover.Root>