@svgrid/grid 1.2.33 → 2.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 (490) hide show
  1. package/README.md +6 -5
  2. package/dist/FlexRender.svelte +96 -96
  3. package/dist/GridFooter.svelte +178 -178
  4. package/dist/SvAccordion.svelte +109 -0
  5. package/dist/SvAccordion.svelte.d.ts +28 -0
  6. package/dist/SvAlert.svelte +92 -0
  7. package/dist/SvAlert.svelte.d.ts +27 -0
  8. package/dist/SvAutoComplete.svelte +47 -18
  9. package/dist/SvAutoComplete.svelte.d.ts +2 -5
  10. package/dist/SvAvatar.svelte +75 -0
  11. package/dist/SvAvatar.svelte.d.ts +14 -0
  12. package/dist/SvAvatarGroup.svelte +48 -0
  13. package/dist/SvAvatarGroup.svelte.d.ts +16 -0
  14. package/dist/SvBadge.svelte +54 -0
  15. package/dist/SvBadge.svelte.d.ts +23 -0
  16. package/dist/SvBreadcrumb.svelte +88 -0
  17. package/dist/SvBreadcrumb.svelte.d.ts +23 -0
  18. package/dist/SvButtonGroup.svelte +121 -0
  19. package/dist/SvButtonGroup.svelte.d.ts +16 -0
  20. package/dist/SvCalendar.svelte +72 -9
  21. package/dist/SvCalendar.svelte.d.ts +34 -1
  22. package/dist/SvCard.svelte +71 -0
  23. package/dist/SvCard.svelte.d.ts +28 -0
  24. package/dist/SvCarousel.svelte +134 -0
  25. package/dist/SvCarousel.svelte.d.ts +28 -0
  26. package/dist/SvCheckBox.svelte +36 -21
  27. package/dist/SvCheckBox.svelte.d.ts +2 -5
  28. package/dist/SvChip.svelte +88 -0
  29. package/dist/SvChip.svelte.d.ts +31 -0
  30. package/dist/SvCircularProgress.svelte +109 -0
  31. package/dist/SvCircularProgress.svelte.d.ts +31 -0
  32. package/dist/SvColorInput.svelte +54 -27
  33. package/dist/SvColorInput.svelte.d.ts +10 -6
  34. package/dist/SvComboBox.svelte +119 -33
  35. package/dist/SvComboBox.svelte.d.ts +18 -6
  36. package/dist/SvCommand.svelte +197 -0
  37. package/dist/SvCommand.svelte.d.ts +26 -0
  38. package/dist/SvContextMenu.svelte +96 -0
  39. package/dist/SvContextMenu.svelte.d.ts +25 -0
  40. package/dist/SvCountryInput.svelte +56 -21
  41. package/dist/SvCountryInput.svelte.d.ts +9 -5
  42. package/dist/SvDateRangeInput.svelte +256 -0
  43. package/dist/SvDateRangeInput.svelte.d.ts +37 -0
  44. package/dist/SvDateTimePicker.svelte +57 -20
  45. package/dist/SvDateTimePicker.svelte.d.ts +12 -4
  46. package/dist/SvDivider.svelte +61 -0
  47. package/dist/SvDivider.svelte.d.ts +22 -0
  48. package/dist/SvDrawer.svelte +226 -0
  49. package/dist/SvDrawer.svelte.d.ts +43 -0
  50. package/dist/SvDropDownList.svelte +114 -35
  51. package/dist/SvDropDownList.svelte.d.ts +7 -6
  52. package/dist/SvDurationInput.svelte +108 -0
  53. package/dist/SvDurationInput.svelte.d.ts +15 -0
  54. package/dist/SvEmptyState.svelte +59 -0
  55. package/dist/SvEmptyState.svelte.d.ts +25 -0
  56. package/dist/SvField.svelte +85 -0
  57. package/dist/SvField.svelte.d.ts +30 -0
  58. package/dist/SvFileUpload.svelte +169 -0
  59. package/dist/SvFileUpload.svelte.d.ts +24 -0
  60. package/dist/SvForm.svelte +7 -1
  61. package/dist/SvForm.svelte.d.ts +3 -0
  62. package/dist/SvGauge.svelte +7 -1
  63. package/dist/SvGauge.svelte.d.ts +11 -0
  64. package/dist/SvGrid.controller.svelte.d.ts +6 -0
  65. package/dist/SvGrid.controller.svelte.js +64 -9
  66. package/dist/SvGrid.css +2376 -2343
  67. package/dist/SvGrid.svelte +2764 -2651
  68. package/dist/SvGrid.types.d.ts +366 -0
  69. package/dist/SvGridBoard.svelte +2281 -0
  70. package/dist/SvGridBoard.svelte.d.ts +31 -0
  71. package/dist/SvGridDropdown.svelte +666 -665
  72. package/dist/SvGridSelect.svelte +255 -0
  73. package/dist/SvGridSelect.svelte.d.ts +32 -0
  74. package/dist/SvGroupCell.svelte +109 -0
  75. package/dist/SvGroupCell.svelte.d.ts +41 -0
  76. package/dist/SvListBox.svelte +123 -36
  77. package/dist/SvListBox.svelte.d.ts +12 -8
  78. package/dist/SvMaskedInput.svelte +53 -13
  79. package/dist/SvMaskedInput.svelte.d.ts +16 -6
  80. package/dist/SvMenu.svelte +101 -0
  81. package/dist/SvMenu.svelte.d.ts +26 -0
  82. package/dist/SvMenuList.svelte +143 -0
  83. package/dist/SvMenuList.svelte.d.ts +24 -0
  84. package/dist/SvModal.svelte +220 -0
  85. package/dist/SvModal.svelte.d.ts +31 -0
  86. package/dist/SvMultiSelect.svelte +277 -0
  87. package/dist/SvMultiSelect.svelte.d.ts +31 -0
  88. package/dist/SvNavPane.svelte +314 -0
  89. package/dist/SvNavPane.svelte.d.ts +59 -0
  90. package/dist/SvNumberInput.svelte +23 -9
  91. package/dist/SvNumberInput.svelte.d.ts +2 -0
  92. package/dist/SvOtpInput.svelte +157 -0
  93. package/dist/SvOtpInput.svelte.d.ts +16 -0
  94. package/dist/SvPagination.svelte +100 -0
  95. package/dist/SvPagination.svelte.d.ts +16 -0
  96. package/dist/SvPasswordInput.svelte +42 -12
  97. package/dist/SvPasswordInput.svelte.d.ts +13 -6
  98. package/dist/SvPhoneInput.svelte +43 -11
  99. package/dist/SvPhoneInput.svelte.d.ts +10 -11
  100. package/dist/SvPopover.svelte +127 -0
  101. package/dist/SvPopover.svelte.d.ts +35 -0
  102. package/dist/SvProgress.svelte +116 -0
  103. package/dist/SvProgress.svelte.d.ts +32 -0
  104. package/dist/SvRadioGroup.svelte +40 -22
  105. package/dist/SvRadioGroup.svelte.d.ts +2 -10
  106. package/dist/SvRating.svelte +51 -30
  107. package/dist/SvRating.svelte.d.ts +10 -7
  108. package/dist/SvRichText.svelte +192 -0
  109. package/dist/SvRichText.svelte.d.ts +16 -0
  110. package/dist/SvRowGroupPanel.svelte +147 -0
  111. package/dist/SvRowGroupPanel.svelte.d.ts +17 -0
  112. package/dist/SvScrollArea.svelte +50 -0
  113. package/dist/SvScrollArea.svelte.d.ts +22 -0
  114. package/dist/SvSkeleton.svelte +76 -0
  115. package/dist/SvSkeleton.svelte.d.ts +25 -0
  116. package/dist/SvSlider.svelte +41 -9
  117. package/dist/SvSlider.svelte.d.ts +4 -4
  118. package/dist/SvSparkline.svelte +77 -0
  119. package/dist/SvSparkline.svelte.d.ts +28 -0
  120. package/dist/SvSplitter.svelte +138 -0
  121. package/dist/SvSplitter.svelte.d.ts +32 -0
  122. package/dist/SvStat.svelte +86 -0
  123. package/dist/SvStat.svelte.d.ts +30 -0
  124. package/dist/SvStepper.svelte +91 -0
  125. package/dist/SvStepper.svelte.d.ts +24 -0
  126. package/dist/SvSwitchButton.svelte +39 -24
  127. package/dist/SvSwitchButton.svelte.d.ts +2 -5
  128. package/dist/SvTabs.svelte +147 -28
  129. package/dist/SvTabs.svelte.d.ts +16 -4
  130. package/dist/SvTagsInput.svelte +59 -22
  131. package/dist/SvTagsInput.svelte.d.ts +9 -4
  132. package/dist/SvTextArea.svelte +113 -0
  133. package/dist/SvTextArea.svelte.d.ts +18 -0
  134. package/dist/SvTextInput.svelte +115 -0
  135. package/dist/SvTextInput.svelte.d.ts +20 -0
  136. package/dist/SvTimePicker.svelte +19 -5
  137. package/dist/SvTimePicker.svelte.d.ts +12 -0
  138. package/dist/SvTimeline.svelte +75 -0
  139. package/dist/SvTimeline.svelte.d.ts +24 -0
  140. package/dist/SvToaster.svelte +124 -0
  141. package/dist/SvToaster.svelte.d.ts +10 -0
  142. package/dist/SvToggleButton.svelte +37 -12
  143. package/dist/SvToggleButton.svelte.d.ts +2 -4
  144. package/dist/SvTooltip.svelte +113 -0
  145. package/dist/SvTooltip.svelte.d.ts +22 -0
  146. package/dist/SvTour.svelte +151 -0
  147. package/dist/SvTour.svelte.d.ts +25 -0
  148. package/dist/SvTree.svelte +286 -44
  149. package/dist/SvTree.svelte.d.ts +26 -9
  150. package/dist/SvTreeSelect.svelte +222 -0
  151. package/dist/SvTreeSelect.svelte.d.ts +24 -0
  152. package/dist/a11y/dismissable.d.ts +32 -0
  153. package/dist/a11y/dismissable.js +54 -0
  154. package/dist/a11y/focus-trap.d.ts +39 -0
  155. package/dist/a11y/focus-trap.js +142 -0
  156. package/dist/a11y/live-region.d.ts +8 -0
  157. package/dist/a11y/live-region.js +60 -0
  158. package/dist/a11y/scroll-lock.d.ts +4 -0
  159. package/dist/a11y/scroll-lock.js +41 -0
  160. package/dist/a11y.d.ts +3 -1
  161. package/dist/a11y.js +1 -1
  162. package/dist/avatar.d.ts +10 -0
  163. package/dist/avatar.js +22 -0
  164. package/dist/builtin-editors.d.ts +5 -0
  165. package/dist/builtin-editors.js +55 -0
  166. package/dist/cdn/svgrid.js +16022 -8235
  167. package/dist/cdn/svgrid.svelte-external.js +14543 -6765
  168. package/dist/columns.js +3 -2
  169. package/dist/core.d.ts +7 -1
  170. package/dist/countries.d.ts +12 -0
  171. package/dist/countries.js +24 -0
  172. package/dist/createAccordion.svelte.d.ts +71 -0
  173. package/dist/createAccordion.svelte.js +99 -0
  174. package/dist/createAutocomplete.svelte.d.ts +13 -4
  175. package/dist/createAutocomplete.svelte.js +11 -1
  176. package/dist/createButtonGroup.svelte.d.ts +62 -0
  177. package/dist/createButtonGroup.svelte.js +154 -0
  178. package/dist/createCalendar.svelte.d.ts +5 -0
  179. package/dist/createCalendar.svelte.js +2 -0
  180. package/dist/createCheckbox.svelte.d.ts +9 -17
  181. package/dist/createCheckbox.svelte.js +28 -1
  182. package/dist/createColorInput.svelte.d.ts +12 -22
  183. package/dist/createColorInput.svelte.js +13 -0
  184. package/dist/createCombobox.svelte.d.ts +16 -5
  185. package/dist/createCombobox.svelte.js +13 -2
  186. package/dist/createCountryInput.svelte.d.ts +14 -3
  187. package/dist/createCountryInput.svelte.js +12 -2
  188. package/dist/createDateTimePicker.svelte.d.ts +17 -2
  189. package/dist/createDateTimePicker.svelte.js +12 -2
  190. package/dist/createDropdownList.svelte.d.ts +14 -5
  191. package/dist/createDropdownList.svelte.js +50 -1
  192. package/dist/createFileUpload.svelte.d.ts +68 -0
  193. package/dist/createFileUpload.svelte.js +122 -0
  194. package/dist/createListbox.svelte.d.ts +10 -1
  195. package/dist/createListbox.svelte.js +48 -1
  196. package/dist/createMaskedInput.svelte.d.ts +11 -2
  197. package/dist/createMaskedInput.svelte.js +11 -1
  198. package/dist/createNumberInput.svelte.d.ts +1 -0
  199. package/dist/createNumberInput.svelte.js +1 -0
  200. package/dist/createPasswordInput.svelte.d.ts +19 -21
  201. package/dist/createPasswordInput.svelte.js +16 -4
  202. package/dist/createPhoneInput.svelte.d.ts +26 -7
  203. package/dist/createPhoneInput.svelte.js +24 -3
  204. package/dist/createPopoverSelect.svelte.d.ts +86 -0
  205. package/dist/createPopoverSelect.svelte.js +196 -0
  206. package/dist/createRadioGroup.svelte.d.ts +9 -0
  207. package/dist/createRadioGroup.svelte.js +11 -1
  208. package/dist/createRating.svelte.d.ts +11 -0
  209. package/dist/createRating.svelte.js +12 -1
  210. package/dist/createSlider.svelte.d.ts +2 -0
  211. package/dist/createSlider.svelte.js +16 -5
  212. package/dist/createSplitter.svelte.d.ts +67 -0
  213. package/dist/createSplitter.svelte.js +99 -0
  214. package/dist/createSwitch.svelte.d.ts +9 -16
  215. package/dist/createSwitch.svelte.js +28 -1
  216. package/dist/createTabs.svelte.d.ts +7 -0
  217. package/dist/createTabs.svelte.js +17 -2
  218. package/dist/createTagsInput.svelte.d.ts +13 -1
  219. package/dist/createTagsInput.svelte.js +12 -3
  220. package/dist/createToggle.svelte.d.ts +9 -0
  221. package/dist/createToggle.svelte.js +12 -1
  222. package/dist/createTree.svelte.d.ts +19 -0
  223. package/dist/createTree.svelte.js +108 -15
  224. package/dist/debounce.d.ts +11 -0
  225. package/dist/debounce.js +30 -0
  226. package/dist/duration.d.ts +15 -0
  227. package/dist/duration.js +51 -0
  228. package/dist/editor-contract.d.ts +62 -7
  229. package/dist/editor-contract.js +45 -5
  230. package/dist/editor-registry.d.ts +67 -0
  231. package/dist/editor-registry.js +38 -0
  232. package/dist/editors/cell-editors.d.ts +1 -1
  233. package/dist/editors/cell-editors.js +5 -0
  234. package/dist/fuzzy.d.ts +3 -0
  235. package/dist/fuzzy.js +41 -0
  236. package/dist/grid-select.d.ts +9 -0
  237. package/dist/grid-select.js +8 -0
  238. package/dist/index.d.ts +79 -6
  239. package/dist/index.js +78 -4
  240. package/dist/keyboard-handlers.js +19 -0
  241. package/dist/list-option.d.ts +36 -0
  242. package/dist/list-option.js +68 -0
  243. package/dist/otp.d.ts +16 -0
  244. package/dist/otp.js +26 -0
  245. package/dist/paginate.d.ts +20 -0
  246. package/dist/paginate.js +40 -0
  247. package/dist/popover.d.ts +15 -1
  248. package/dist/popover.js +29 -3
  249. package/dist/recurrence.d.ts +36 -0
  250. package/dist/recurrence.js +90 -0
  251. package/dist/server-data-source.d.ts +89 -0
  252. package/dist/server-data-source.js +4 -0
  253. package/dist/server-group-model.d.ts +98 -0
  254. package/dist/server-group-model.js +263 -0
  255. package/dist/toast-store.svelte.d.ts +41 -0
  256. package/dist/toast-store.svelte.js +113 -0
  257. package/dist/tree-select.d.ts +19 -0
  258. package/dist/tree-select.js +40 -0
  259. package/dist/ui-app.types.d.ts +80 -0
  260. package/dist/ui-app.types.js +7 -0
  261. package/dist/validators.d.ts +43 -0
  262. package/dist/validators.js +53 -0
  263. package/dist/virtual.d.ts +35 -0
  264. package/dist/virtual.js +34 -0
  265. package/package.json +85 -85
  266. package/src/FlexRender.svelte +96 -96
  267. package/src/GridFooter.svelte +178 -178
  268. package/src/SvAccordion.svelte +109 -0
  269. package/src/SvAccordion.test.ts +66 -0
  270. package/src/SvAlert.svelte +92 -0
  271. package/src/SvAutoComplete.svelte +47 -18
  272. package/src/SvAvatar.svelte +75 -0
  273. package/src/SvAvatarGroup.svelte +48 -0
  274. package/src/SvBadge.svelte +54 -0
  275. package/src/SvBreadcrumb.svelte +88 -0
  276. package/src/SvButtonGroup.svelte +121 -0
  277. package/src/SvButtonGroup.test.ts +72 -0
  278. package/src/SvCalendar.svelte +72 -9
  279. package/src/SvCard.svelte +71 -0
  280. package/src/SvCarousel.svelte +134 -0
  281. package/src/SvCheckBox.svelte +36 -21
  282. package/src/SvChip.svelte +88 -0
  283. package/src/SvCircularProgress.svelte +109 -0
  284. package/src/SvColorInput.svelte +54 -27
  285. package/src/SvComboBox.svelte +119 -33
  286. package/src/SvCommand.svelte +197 -0
  287. package/src/SvContextMenu.svelte +96 -0
  288. package/src/SvCountryInput.svelte +56 -21
  289. package/src/SvDateRangeInput.svelte +256 -0
  290. package/src/SvDateRangeInput.test.ts +80 -0
  291. package/src/SvDateTimePicker.svelte +57 -20
  292. package/src/SvDivider.svelte +61 -0
  293. package/src/SvDrawer.svelte +226 -0
  294. package/src/SvDrawer.test.ts +125 -0
  295. package/src/SvDropDownList.svelte +114 -35
  296. package/src/SvDurationInput.svelte +108 -0
  297. package/src/SvEmptyState.svelte +59 -0
  298. package/src/SvField.svelte +85 -0
  299. package/src/SvFileUpload.svelte +169 -0
  300. package/src/SvFileUpload.test.ts +59 -0
  301. package/src/SvForm.svelte +7 -1
  302. package/src/SvGauge.svelte +7 -1
  303. package/src/SvGrid.controller.svelte.ts +2553 -2494
  304. package/src/SvGrid.css +2376 -2343
  305. package/src/SvGrid.svelte +2764 -2651
  306. package/src/SvGrid.types.ts +944 -570
  307. package/src/SvGridBoard.svelte +2281 -0
  308. package/src/SvGridDropdown.svelte +666 -665
  309. package/src/SvGridSelect.svelte +255 -0
  310. package/src/SvGridSelect.test.ts +100 -0
  311. package/src/SvGroupCell.svelte +109 -0
  312. package/src/SvListBox.svelte +123 -36
  313. package/src/SvMaskedInput.svelte +53 -13
  314. package/src/SvMenu.svelte +101 -0
  315. package/src/SvMenu.test.ts +73 -0
  316. package/src/SvMenuList.svelte +143 -0
  317. package/src/SvModal.svelte +220 -0
  318. package/src/SvMultiSelect.svelte +277 -0
  319. package/src/SvMultiSelect.test.ts +167 -0
  320. package/src/SvNavPane.svelte +314 -0
  321. package/src/SvNavPane.test.ts +70 -0
  322. package/src/SvNumberInput.svelte +23 -9
  323. package/src/SvOtpInput.svelte +157 -0
  324. package/src/SvPagination.svelte +100 -0
  325. package/src/SvPasswordInput.svelte +42 -12
  326. package/src/SvPhoneInput.svelte +43 -11
  327. package/src/SvPopover.svelte +127 -0
  328. package/src/SvProgress.svelte +116 -0
  329. package/src/SvRadioGroup.svelte +40 -22
  330. package/src/SvRating.svelte +51 -30
  331. package/src/SvRichText.svelte +192 -0
  332. package/src/SvRowGroupPanel.svelte +147 -0
  333. package/src/SvScrollArea.svelte +50 -0
  334. package/src/SvSkeleton.svelte +76 -0
  335. package/src/SvSlider.svelte +41 -9
  336. package/src/SvSparkline.svelte +77 -0
  337. package/src/SvSplitter.svelte +138 -0
  338. package/src/SvSplitter.test.ts +55 -0
  339. package/src/SvStat.svelte +86 -0
  340. package/src/SvStepper.svelte +91 -0
  341. package/src/SvSwitchButton.svelte +39 -24
  342. package/src/SvTabs.svelte +147 -28
  343. package/src/SvTagsInput.svelte +59 -22
  344. package/src/SvTextArea.svelte +113 -0
  345. package/src/SvTextInput.svelte +115 -0
  346. package/src/SvTimePicker.svelte +19 -5
  347. package/src/SvTimeline.svelte +75 -0
  348. package/src/SvToaster.svelte +124 -0
  349. package/src/SvToaster.test.ts +63 -0
  350. package/src/SvToggleButton.svelte +37 -12
  351. package/src/SvTooltip.svelte +113 -0
  352. package/src/SvTour.svelte +151 -0
  353. package/src/SvTree.svelte +286 -44
  354. package/src/SvTreeSelect.svelte +222 -0
  355. package/src/SvTreeSelect.test.ts +87 -0
  356. package/src/a11y/dismissable.test.ts +95 -0
  357. package/src/a11y/dismissable.ts +94 -0
  358. package/src/a11y/focus-trap.test.ts +110 -0
  359. package/src/a11y/focus-trap.ts +134 -0
  360. package/src/a11y/live-region.test.ts +40 -0
  361. package/src/a11y/live-region.ts +65 -0
  362. package/src/a11y/scroll-lock.test.ts +38 -0
  363. package/src/a11y/scroll-lock.ts +41 -0
  364. package/src/a11y.contract.test.ts +49 -49
  365. package/src/a11y.test.ts +59 -59
  366. package/src/a11y.ts +61 -59
  367. package/src/avatar.test.ts +32 -0
  368. package/src/avatar.ts +21 -0
  369. package/src/board.test.ts +927 -0
  370. package/src/build-api.ts +798 -798
  371. package/src/builtin-editors.grid.test.ts +85 -0
  372. package/src/builtin-editors.test.ts +58 -0
  373. package/src/builtin-editors.ts +56 -0
  374. package/src/cell-formatting.ts +169 -169
  375. package/src/cell-render.ts +469 -469
  376. package/src/collaboration.test.ts +104 -104
  377. package/src/collaboration.ts +167 -167
  378. package/src/columns.ts +3 -2
  379. package/src/core.performance.test.ts +30 -30
  380. package/src/core.ts +1111 -1101
  381. package/src/countries.ts +25 -0
  382. package/src/createAccordion.svelte.ts +126 -0
  383. package/src/createAutocomplete.svelte.ts +18 -1
  384. package/src/createButtonGroup.svelte.ts +166 -0
  385. package/src/createCalendar.svelte.ts +6 -0
  386. package/src/createCheckbox.svelte.ts +23 -1
  387. package/src/createColorInput.svelte.ts +20 -0
  388. package/src/createCombobox.svelte.ts +22 -2
  389. package/src/createCountryInput.svelte.ts +21 -2
  390. package/src/createDateTimePicker.svelte.ts +23 -2
  391. package/src/createDropdownList.svelte.ts +31 -2
  392. package/src/createFileUpload.svelte.ts +144 -0
  393. package/src/createGrid.svelte.ts +42 -42
  394. package/src/createGrid.test.ts +10 -10
  395. package/src/createGridState.svelte.ts +17 -17
  396. package/src/createListbox.svelte.ts +35 -3
  397. package/src/createMaskedInput.svelte.ts +18 -1
  398. package/src/createNumberInput.svelte.ts +2 -0
  399. package/src/createPasswordInput.svelte.ts +31 -5
  400. package/src/createPhoneInput.svelte.ts +38 -4
  401. package/src/createPopoverSelect.svelte.ts +208 -0
  402. package/src/createRadioGroup.svelte.ts +23 -1
  403. package/src/createRating.svelte.ts +26 -1
  404. package/src/createSlider.svelte.ts +14 -3
  405. package/src/createSplitter.svelte.ts +119 -0
  406. package/src/createSwitch.svelte.ts +26 -3
  407. package/src/createTabs.svelte.ts +20 -3
  408. package/src/createTagsInput.svelte.ts +24 -3
  409. package/src/createToggle.svelte.ts +25 -2
  410. package/src/createTree.svelte.ts +104 -14
  411. package/src/debounce.test.ts +50 -0
  412. package/src/debounce.ts +41 -0
  413. package/src/duration.test.ts +41 -0
  414. package/src/duration.ts +51 -0
  415. package/src/editing.test.ts +859 -859
  416. package/src/editing.ts +675 -675
  417. package/src/editor-contract.ts +87 -8
  418. package/src/editor-registry.grid.test.ts +98 -0
  419. package/src/editor-registry.test.ts +86 -0
  420. package/src/editor-registry.ts +106 -0
  421. package/src/editors/cell-editors.ts +10 -1
  422. package/src/export-data-api.test.ts +126 -126
  423. package/src/export-format.test.ts +107 -107
  424. package/src/export-format.ts +598 -598
  425. package/src/flex-render.ts +3 -3
  426. package/src/fuzzy.test.ts +40 -0
  427. package/src/fuzzy.ts +39 -0
  428. package/src/grid-select.test.ts +28 -0
  429. package/src/grid-select.ts +20 -0
  430. package/src/index.ts +463 -318
  431. package/src/keyboard-handlers.ts +20 -0
  432. package/src/keyboard.test.ts +59 -59
  433. package/src/keyboard.ts +97 -97
  434. package/src/list-option.ts +74 -0
  435. package/src/list-power.test.ts +71 -0
  436. package/src/menus.ts +582 -582
  437. package/src/merge-objects.ts +48 -48
  438. package/src/otp.test.ts +28 -0
  439. package/src/otp.ts +28 -0
  440. package/src/overlays.test.ts +85 -0
  441. package/src/paginate.test.ts +41 -0
  442. package/src/paginate.ts +61 -0
  443. package/src/phone-valid.test.ts +26 -0
  444. package/src/popover.ts +31 -3
  445. package/src/progress.test.ts +55 -0
  446. package/src/recurrence.test.ts +91 -0
  447. package/src/recurrence.ts +111 -0
  448. package/src/render-component.ts +28 -28
  449. package/src/selection.test.ts +754 -754
  450. package/src/selection.ts +600 -600
  451. package/src/server-data-source.test.ts +289 -289
  452. package/src/server-data-source.ts +413 -311
  453. package/src/server-group-model.test.ts +294 -0
  454. package/src/server-group-model.ts +370 -0
  455. package/src/sparkline.test.ts +68 -68
  456. package/src/sparkline.ts +169 -169
  457. package/src/spreadsheet.test.ts +489 -489
  458. package/src/spreadsheet.ts +304 -304
  459. package/src/static-functions.ts +11 -11
  460. package/src/subscribe.ts +38 -38
  461. package/src/svgrid-wrapper.types.ts +439 -439
  462. package/src/svgrid.behavior.test.ts +706 -706
  463. package/src/svgrid.features.test.ts +157 -157
  464. package/src/svgrid.new-features.wrapper.test.ts +251 -251
  465. package/src/svgrid.responsive.test.ts +72 -0
  466. package/src/svgrid.wrapper.test.ts +40 -40
  467. package/src/toast-store.svelte.ts +148 -0
  468. package/src/toast-store.test.ts +86 -0
  469. package/src/tree-edit.test.ts +49 -0
  470. package/src/tree-filter.test.ts +56 -0
  471. package/src/tree-select.test.ts +48 -0
  472. package/src/tree-select.ts +62 -0
  473. package/src/ui-app.test.ts +130 -0
  474. package/src/ui-app.types.ts +88 -0
  475. package/src/ui-display.test.ts +79 -0
  476. package/src/ui-inputs.test.ts +1 -1
  477. package/src/ui-status.test.ts +141 -0
  478. package/src/ui-tier1.test.ts +142 -0
  479. package/src/ui-tier2.test.ts +124 -0
  480. package/src/ui-tier3.test.ts +139 -0
  481. package/src/validators.test.ts +58 -0
  482. package/src/validators.ts +105 -0
  483. package/src/virtual.test.ts +72 -0
  484. package/src/virtual.ts +67 -0
  485. package/src/virtualization/column-virtualizer.test.ts +27 -27
  486. package/src/virtualization/column-virtualizer.ts +30 -30
  487. package/src/virtualization/svelte-virtualizer.svelte.ts +26 -26
  488. package/src/virtualization/types.ts +30 -30
  489. package/src/virtualization/virtualizer.test.ts +47 -47
  490. package/src/virtualization/virtualizer.ts +296 -296
@@ -8,20 +8,24 @@
8
8
  * normalization, palette, draft field and open/close state come from the core;
9
9
  * the portal + anchored measurement stay here (render concerns).
10
10
  */
11
- import { anchoredRect, portalToBody, type AnchoredRect } from './popover'
11
+ import { anchoredRect, portalToBody, popIn, type AnchoredRect } from './popover'
12
+ import { createDismissableLayer } from './a11y/dismissable'
13
+ import SvField from './SvField.svelte'
14
+ import { nextEditorId, resolveMessages, type SvEditorProps } from './editor-contract'
12
15
  import { createColorInput } from './createColorInput.svelte'
13
16
 
14
- type Props = {
17
+ /** User-facing strings for the color popover (localizable via `messages`). */
18
+ type ColorMessages = { dialog: string; picker: string; hex: string }
19
+ const DEFAULT_MESSAGES: ColorMessages = { dialog: 'Choose color', picker: 'Color picker', hex: 'Hex value' }
20
+
21
+ type Props = SvEditorProps & {
15
22
  value?: string
16
23
  onChange?: (hex: string) => void
17
- disabled?: boolean
18
- readonly?: boolean
19
24
  /** Preset swatches. */
20
25
  palette?: string[]
21
- name?: string
22
- size?: 'sm' | 'md' | 'lg'
23
- ariaLabel?: string
24
26
  autoOpen?: boolean
27
+ /** Override the built-in popover strings. */
28
+ messages?: Partial<ColorMessages>
25
29
  }
26
30
 
27
31
  let {
@@ -33,15 +37,32 @@
33
37
  name,
34
38
  size = 'md',
35
39
  ariaLabel,
40
+ invalid = false,
41
+ required = false,
42
+ error,
43
+ label,
44
+ hint,
45
+ dir,
46
+ id,
36
47
  autoOpen = false,
48
+ messages,
37
49
  }: Props = $props()
38
50
 
51
+ const autoId = nextEditorId('sv-color')
52
+ const uid = $derived(id ?? autoId)
53
+ const M = $derived(resolveMessages(DEFAULT_MESSAGES, messages))
54
+
39
55
  const col = createColorInput({
40
56
  value: () => value,
41
57
  onChange: (hex) => onChange?.(hex),
42
58
  disabled: () => disabled,
43
59
  readonly: () => readonly,
44
60
  palette: () => palette as string[],
61
+ id: () => uid,
62
+ invalid: () => invalid,
63
+ required: () => required,
64
+ error: () => error,
65
+ hint: () => hint,
45
66
  ariaLabel: () => ariaLabel,
46
67
  })
47
68
 
@@ -60,16 +81,16 @@
60
81
  const reposition = () => updatePos()
61
82
  window.addEventListener('scroll', reposition, true)
62
83
  window.addEventListener('resize', reposition)
63
- const onDown = (e: PointerEvent) => {
64
- const t = e.target as Node | null
65
- if (t && (triggerEl?.contains(t) || panelEl?.contains(t))) return
66
- col.popover.close()
67
- }
68
- document.addEventListener('pointerdown', onDown, true)
84
+ const layer = createDismissableLayer({
85
+ element: () => [triggerEl, panelEl],
86
+ onDismiss: () => col.popover.close(),
87
+ closeOnEscape: false,
88
+ })
89
+ layer.activate()
69
90
  return () => {
70
91
  window.removeEventListener('scroll', reposition, true)
71
92
  window.removeEventListener('resize', reposition)
72
- document.removeEventListener('pointerdown', onDown, true)
93
+ layer.release()
73
94
  }
74
95
  })
75
96
 
@@ -79,36 +100,40 @@
79
100
  }
80
101
  </script>
81
102
 
82
- <button
83
- bind:this={triggerEl}
84
- class="sv-color sv-color--{size}"
85
- class:is-disabled={disabled}
86
- {...col.swatchProps()}
87
- use:focusOpen
88
- >
89
- <span class="sv-color__swatch" style:background={col.normalized}></span>
90
- <span class="sv-color__hex">{col.normalized}</span>
91
- </button>
103
+ <SvField id={uid} {label} {hint} {error} {required} {dir}>
104
+ <button
105
+ bind:this={triggerEl}
106
+ class="sv-color sv-color--{size}"
107
+ class:is-disabled={disabled}
108
+ class:is-invalid={invalid}
109
+ {...col.swatchProps()}
110
+ use:focusOpen
111
+ >
112
+ <span class="sv-color__swatch" style:background={col.normalized}></span>
113
+ <span class="sv-color__hex">{col.normalized}</span>
114
+ </button>
115
+ </SvField>
92
116
 
93
117
  {#if col.popover.open}
94
118
  <div
95
119
  class="sv-color__panel"
96
120
  bind:this={panelEl}
97
121
  use:portalToBody
122
+ use:popIn={{ up: panelRect.openUpward }}
98
123
  style:position="fixed"
99
124
  style:top={`${panelRect.top}px`}
100
125
  style:left={`${panelRect.left}px`}
101
126
  role="dialog"
102
- aria-label="Choose color"
127
+ aria-label={M.dialog}
103
128
  >
104
129
  <div class="sv-color__top">
105
- <input class="sv-color__native" type="color" value={col.normalized} oninput={(e) => col.pick((e.currentTarget as HTMLInputElement).value)} aria-label="Color picker" />
130
+ <input class="sv-color__native" type="color" value={col.normalized} oninput={(e) => col.pick((e.currentTarget as HTMLInputElement).value)} aria-label={M.picker} />
106
131
  <input
107
132
  class="sv-color__field"
108
133
  type="text"
109
134
  bind:value={col.hexDraft}
110
135
  spellcheck="false"
111
- aria-label="Hex value"
136
+ aria-label={M.hex}
112
137
  onblur={col.commitHex}
113
138
  onkeydown={(e) => { if (e.key === 'Enter') col.commitHex() }}
114
139
  />
@@ -139,6 +164,8 @@
139
164
  .sv-color--md { height: 34px; font-size: 13px; }
140
165
  .sv-color--lg { height: 40px; font-size: 15px; }
141
166
  .sv-color.is-disabled { opacity: 0.6; cursor: not-allowed; }
167
+ .sv-color.is-invalid { border-color: var(--sg-danger, #dc2626); }
168
+ .sv-color.is-invalid:focus-within { box-shadow: 0 0 0 2px color-mix(in srgb, var(--sg-danger, #dc2626) 22%, transparent); }
142
169
  .sv-color:focus-visible { outline: 2px solid var(--sg-focus-ring, var(--_accent)); outline-offset: 2px; }
143
170
  .sv-color__swatch { width: 18px; height: 18px; border-radius: 4px; border: 1px solid rgba(0,0,0,0.15); flex: none; }
144
171
  .sv-color__hex { font-variant-numeric: tabular-nums; text-transform: lowercase; }
@@ -1,14 +1,18 @@
1
- type Props = {
1
+ import { type SvEditorProps } from './editor-contract';
2
+ /** User-facing strings for the color popover (localizable via `messages`). */
3
+ type ColorMessages = {
4
+ dialog: string;
5
+ picker: string;
6
+ hex: string;
7
+ };
8
+ type Props = SvEditorProps & {
2
9
  value?: string;
3
10
  onChange?: (hex: string) => void;
4
- disabled?: boolean;
5
- readonly?: boolean;
6
11
  /** Preset swatches. */
7
12
  palette?: string[];
8
- name?: string;
9
- size?: 'sm' | 'md' | 'lg';
10
- ariaLabel?: string;
11
13
  autoOpen?: boolean;
14
+ /** Override the built-in popover strings. */
15
+ messages?: Partial<ColorMessages>;
12
16
  };
13
17
  declare const SvColorInput: import("svelte").Component<Props, {}, "">;
14
18
  type SvColorInput = ReturnType<typeof SvColorInput>;
@@ -7,37 +7,106 @@
7
7
  * This is one styled renderer over the headless `createCombobox` core (state +
8
8
  * keyboard + ARIA); only portal/measure/scroll render concerns live here.
9
9
  */
10
- import { anchoredRect, portalToBody, type AnchoredRect } from './popover'
11
- import { type ListOption } from './list-option'
10
+ import { anchoredRect, portalToBody, popIn, type AnchoredRect } from './popover'
11
+ import { createDismissableLayer } from './a11y/dismissable'
12
+ import { groupOptions, type ListOption } from './list-option'
13
+ import SvField from './SvField.svelte'
14
+ import { nextEditorId, resolveMessages, type SvEditorProps } from './editor-contract'
12
15
  import { createCombobox } from './createCombobox.svelte'
13
16
 
14
- type Props = {
15
- options: ReadonlyArray<ListOption>
17
+ /** User-facing strings (localizable via `messages`). */
18
+ type ComboMessages = { noResults: string; loading: string; typeMore: string }
19
+ const DEFAULT_MESSAGES: ComboMessages = { noResults: 'No matches', loading: 'Loading...', typeMore: 'Type to search' }
20
+
21
+ type Props = SvEditorProps & {
22
+ options?: ReadonlyArray<ListOption>
16
23
  value?: string | number | null
17
24
  onChange?: (value: string | number | null) => void
18
25
  placeholder?: string
19
- disabled?: boolean
20
- name?: string
21
- size?: 'sm' | 'md' | 'lg'
22
- ariaLabel?: string
23
26
  autoOpen?: boolean
27
+ /** Load options from a server as the user types (debounced). Disables local
28
+ * filtering; the returned list is shown as-is. */
29
+ loadOptions?: (query: string) => Promise<ListOption[]>
30
+ /** Min chars before a remote search fires. Default 1. */
31
+ minLength?: number
32
+ /** Debounce (ms) before a remote search fires. Default 250. */
33
+ debounce?: number
34
+ /** Override the built-in strings (empty-state / loading text). */
35
+ messages?: Partial<ComboMessages>
24
36
  }
25
37
 
26
- let { options, value = null, onChange, placeholder = 'Select…', disabled = false, name, size = 'md', ariaLabel, autoOpen = false }: Props = $props()
38
+ let {
39
+ options = [],
40
+ value = null,
41
+ onChange,
42
+ placeholder = 'Select…',
43
+ disabled = false,
44
+ name,
45
+ size = 'md',
46
+ ariaLabel,
47
+ invalid = false,
48
+ required = false,
49
+ error,
50
+ label,
51
+ hint,
52
+ dir,
53
+ id,
54
+ autoOpen = false,
55
+ loadOptions,
56
+ minLength = 1,
57
+ debounce = 250,
58
+ messages,
59
+ }: Props = $props()
60
+
61
+ const autoId = nextEditorId('sv-combo')
62
+ const uid = $derived(id ?? autoId)
63
+ const M = $derived(resolveMessages(DEFAULT_MESSAGES, messages))
27
64
 
28
65
  let inputEl = $state<HTMLInputElement | null>(null)
29
66
  let fieldEl = $state<HTMLDivElement | null>(null)
30
67
  let panelEl = $state<HTMLDivElement | null>(null)
31
68
  let rect = $state<AnchoredRect>({ top: 0, left: 0, width: 0, openUpward: false })
32
69
 
70
+ // Remote data source: fetch options as the query changes (debounced).
71
+ let remoteOptions = $state<ListOption[]>([])
72
+ let loading = $state(false)
73
+ let searched = $state(false)
74
+ let debTimer: ReturnType<typeof setTimeout> | undefined
75
+ let reqSeq = 0
76
+ const effectiveOptions = $derived(loadOptions ? remoteOptions : options)
77
+
33
78
  const combo = createCombobox({
34
- options: () => options,
79
+ options: () => effectiveOptions,
35
80
  value: () => value,
36
81
  onChange: (v) => onChange?.(v),
37
82
  disabled: () => disabled,
38
83
  ariaLabel: () => ariaLabel,
84
+ localFilter: () => !loadOptions,
39
85
  focusInput: () => inputEl?.focus(),
40
86
  blurInput: () => inputEl?.blur(),
87
+ id: () => uid,
88
+ invalid: () => invalid,
89
+ required: () => required,
90
+ error: () => error,
91
+ hint: () => hint,
92
+ })
93
+
94
+ // Debounced remote search driven by the combobox query while editing.
95
+ $effect(() => {
96
+ if (!loadOptions) return
97
+ const q = combo.editing ? combo.query : ''
98
+ if (debTimer) clearTimeout(debTimer)
99
+ if (q.trim().length < minLength) { remoteOptions = []; loading = false; searched = false; return }
100
+ loading = true
101
+ const seq = ++reqSeq
102
+ debTimer = setTimeout(async () => {
103
+ try {
104
+ const res = await loadOptions!(q.trim())
105
+ if (seq === reqSeq) { remoteOptions = res; searched = true }
106
+ } finally {
107
+ if (seq === reqSeq) loading = false
108
+ }
109
+ }, debounce)
41
110
  })
42
111
 
43
112
  function updatePos() {
@@ -51,9 +120,9 @@
51
120
  updatePos()
52
121
  const rp = () => updatePos()
53
122
  window.addEventListener('scroll', rp, true); window.addEventListener('resize', rp)
54
- const od = (e: PointerEvent) => { const t = e.target as Node | null; if (t && (fieldEl?.contains(t) || panelEl?.contains(t))) return; combo.close() }
55
- document.addEventListener('pointerdown', od, true)
56
- return () => { window.removeEventListener('scroll', rp, true); window.removeEventListener('resize', rp); document.removeEventListener('pointerdown', od, true) }
123
+ const layer = createDismissableLayer({ element: () => [fieldEl, panelEl], onDismiss: () => combo.close(), closeOnEscape: false })
124
+ layer.activate()
125
+ return () => { window.removeEventListener('scroll', rp, true); window.removeEventListener('resize', rp); layer.release() }
57
126
  })
58
127
  // Keep the active option scrolled into view (render concern).
59
128
  $effect(() => {
@@ -64,28 +133,39 @@
64
133
  function focusOpen(node: HTMLInputElement) { if (autoOpen) { node.focus() } }
65
134
  </script>
66
135
 
67
- <div bind:this={fieldEl} class="sv-combo sv-combo--{size}" class:is-open={combo.open} class:is-disabled={disabled}>
68
- <input
69
- bind:this={inputEl}
70
- class="sv-combo__input"
71
- type="text"
72
- {placeholder}
73
- {...combo.inputProps()}
74
- use:focusOpen
75
- />
76
- <button class="sv-combo__chev" {...combo.triggerProps()}>
77
- <svg viewBox="0 0 24 24" width="15" height="15" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round"><path d="m6 9 6 6 6-6" /></svg>
78
- </button>
79
- </div>
136
+ <SvField id={uid} {label} {hint} {error} {required} {dir}>
137
+ <div bind:this={fieldEl} class="sv-combo sv-combo--{size}" class:is-open={combo.open} class:is-disabled={disabled} class:is-invalid={invalid}>
138
+ <input
139
+ bind:this={inputEl}
140
+ class="sv-combo__input"
141
+ type="text"
142
+ {placeholder}
143
+ {...combo.inputProps()}
144
+ use:focusOpen
145
+ />
146
+ <button class="sv-combo__chev" {...combo.triggerProps()}>
147
+ <svg viewBox="0 0 24 24" width="15" height="15" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round"><path d="m6 9 6 6 6-6" /></svg>
148
+ </button>
149
+ </div>
150
+ </SvField>
80
151
 
81
152
  {#if combo.open}
82
- <div bind:this={panelEl} class="sv-ddl__panel" use:portalToBody style:position="fixed" style:top={`${rect.top}px`} style:left={`${rect.left}px`} style:min-width={`${rect.width}px`} {...combo.listboxProps()}>
83
- {#each combo.filtered as opt, i (opt.value)}
84
- <!-- svelte-ignore a11y_click_events_have_key_events a11y_interactive_supports_focus -->
85
- <div class="sv-ddl__opt" class:is-active={combo.isActive(i)} class:is-selected={combo.isSelected(opt)} class:is-disabled={opt.disabled} {...combo.optionProps(i)}>{opt.label}</div>
153
+ <div bind:this={panelEl} class="sv-ddl__panel" use:portalToBody use:popIn={{ up: rect.openUpward }} style:position="fixed" style:top={`${rect.top}px`} style:left={`${rect.left}px`} style:min-width={`${rect.width}px`} {...combo.listboxProps()}>
154
+ {#if loading}
155
+ <div class="sv-ddl__empty sv-ddl__loading"><svg class="sv-ddl__spin" viewBox="0 0 24 24" width="14" height="14" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round"><path d="M21 12a9 9 0 1 1-6.2-8.6" /></svg>{M.loading}</div>
156
+ {:else if loadOptions && !searched && combo.query.trim().length < minLength}
157
+ <div class="sv-ddl__empty">{M.typeMore}</div>
158
+ {:else if combo.filtered.length}
159
+ {#each groupOptions(combo.filtered) as g (g.group ?? ' ')}
160
+ {#if g.group != null}<div class="sv-ddl__group-label" aria-hidden="true">{g.group}</div>{/if}
161
+ {#each g.options as opt (opt.value)}
162
+ <!-- svelte-ignore a11y_click_events_have_key_events a11y_interactive_supports_focus -->
163
+ <div class="sv-ddl__opt" class:is-active={combo.isActive(opt.index)} class:is-selected={combo.isSelected(opt)} class:is-disabled={opt.disabled} {...combo.optionProps(opt.index)}>{opt.label}</div>
164
+ {/each}
165
+ {/each}
86
166
  {:else}
87
- <div class="sv-ddl__empty">No matches</div>
88
- {/each}
167
+ <div class="sv-ddl__empty">{M.noResults}</div>
168
+ {/if}
89
169
  </div>
90
170
  {/if}
91
171
  {#if name}<input type="hidden" {name} value={value ?? ''} />{/if}
@@ -101,8 +181,14 @@
101
181
  .sv-combo--md { height: 34px; font-size: 13px; }
102
182
  .sv-combo--lg { height: 40px; font-size: 15px; }
103
183
  .sv-combo.is-open, .sv-combo:focus-within { border-color: var(--_accent); box-shadow: 0 0 0 2px color-mix(in srgb, var(--_accent) 22%, transparent); }
184
+ .sv-combo.is-invalid { border-color: var(--sg-danger, #dc2626); }
185
+ .sv-combo.is-invalid.is-open, .sv-combo.is-invalid:focus-within { box-shadow: 0 0 0 2px color-mix(in srgb, var(--sg-danger, #dc2626) 22%, transparent); }
104
186
  .sv-combo.is-disabled { opacity: 0.6; }
105
- .sv-combo__input { flex: 1; min-width: 0; border: 0; background: none; outline: none; color: inherit; font: inherit; padding: 0 4px 0 10px; }
187
+ .sv-combo__input { flex: 1; min-width: 0; border: 0; background: none; outline: none; color: inherit; font: inherit; padding-block: 0; padding-inline: 10px 4px; }
106
188
  .sv-combo__chev { display: grid; place-items: center; width: 28px; align-self: stretch; background: none; border: 0; color: var(--sg-muted, #64748b); cursor: pointer; }
107
189
  :global(.sv-ddl__empty) { padding: 8px 10px; color: var(--sg-muted, #94a3b8); font-size: 13px; }
190
+ :global(.sv-ddl__loading) { display: flex; align-items: center; gap: 7px; }
191
+ :global(.sv-ddl__spin) { animation: sv-combo-spin 0.7s linear infinite; }
192
+ @keyframes sv-combo-spin { to { transform: rotate(360deg); } }
193
+ @media (prefers-reduced-motion: reduce) { :global(.sv-ddl__spin) { animation: none; } }
108
194
  </style>
@@ -1,14 +1,26 @@
1
1
  import { type ListOption } from './list-option';
2
- type Props = {
3
- options: ReadonlyArray<ListOption>;
2
+ import { type SvEditorProps } from './editor-contract';
3
+ /** User-facing strings (localizable via `messages`). */
4
+ type ComboMessages = {
5
+ noResults: string;
6
+ loading: string;
7
+ typeMore: string;
8
+ };
9
+ type Props = SvEditorProps & {
10
+ options?: ReadonlyArray<ListOption>;
4
11
  value?: string | number | null;
5
12
  onChange?: (value: string | number | null) => void;
6
13
  placeholder?: string;
7
- disabled?: boolean;
8
- name?: string;
9
- size?: 'sm' | 'md' | 'lg';
10
- ariaLabel?: string;
11
14
  autoOpen?: boolean;
15
+ /** Load options from a server as the user types (debounced). Disables local
16
+ * filtering; the returned list is shown as-is. */
17
+ loadOptions?: (query: string) => Promise<ListOption[]>;
18
+ /** Min chars before a remote search fires. Default 1. */
19
+ minLength?: number;
20
+ /** Debounce (ms) before a remote search fires. Default 250. */
21
+ debounce?: number;
22
+ /** Override the built-in strings (empty-state / loading text). */
23
+ messages?: Partial<ComboMessages>;
12
24
  };
13
25
  declare const SvComboBox: import("svelte").Component<Props, {}, "">;
14
26
  type SvComboBox = ReturnType<typeof SvComboBox>;
@@ -0,0 +1,197 @@
1
+ <script lang="ts" module>
2
+ export type { CommandItem } from './ui-app.types'
3
+ </script>
4
+
5
+ <script lang="ts">
6
+ /**
7
+ * SvCommand - a ⌘K command palette: a fuzzy-searchable list of actions in a
8
+ * centered, focus-trapped dialog. Built on the shared primitives (portal +
9
+ * focus trap + scroll lock + dismissable layer) and the pure `fuzzyScore`
10
+ * matcher. Optionally binds a global hotkey to toggle itself. Emits WAI-ARIA
11
+ * combobox markup (activedescendant).
12
+ *
13
+ * ```svelte
14
+ * <SvCommand commands={cmds} onRun={(c) => run(c)} /> // Cmd/Ctrl+K opens it
15
+ * ```
16
+ */
17
+ import type { CommandItem } from './ui-app.types'
18
+ import { portalToBody, popIn } from './popover'
19
+ import { createFocusTrap } from './a11y/focus-trap'
20
+ import { lockScroll } from './a11y/scroll-lock'
21
+ import { createDismissableLayer } from './a11y/dismissable'
22
+ import { fuzzyScore } from './fuzzy'
23
+
24
+ type Props = {
25
+ open?: boolean
26
+ onClose?: () => void
27
+ commands: ReadonlyArray<CommandItem>
28
+ onRun?: (cmd: CommandItem) => void
29
+ placeholder?: string
30
+ emptyText?: string
31
+ /** Global toggle hotkey (Cmd/Ctrl + key). `false` disables it. Default 'mod+k'. */
32
+ hotkey?: 'mod+k' | 'mod+p' | false
33
+ }
34
+
35
+ let {
36
+ open = $bindable(false),
37
+ onClose,
38
+ commands,
39
+ onRun,
40
+ placeholder = 'Type a command or search…',
41
+ emptyText = 'No results',
42
+ hotkey = 'mod+k',
43
+ }: Props = $props()
44
+
45
+ let query = $state('')
46
+ let active = $state(0)
47
+ let panelEl = $state<HTMLDivElement | null>(null)
48
+ let inputEl = $state<HTMLInputElement | null>(null)
49
+
50
+ const results = $derived.by(() => {
51
+ const q = query.trim()
52
+ const list = commands.filter((c) => !c.disabled)
53
+ if (!q) return list
54
+ return list
55
+ .map((c) => ({ c, s: fuzzyScore(`${c.label} ${c.keywords ?? ''} ${c.group ?? ''}`, q) }))
56
+ .filter((x) => x.s !== null)
57
+ .sort((a, b) => (b.s as number) - (a.s as number))
58
+ .map((x) => x.c)
59
+ })
60
+
61
+ function close() { open = false; onClose?.() }
62
+ function run(cmd: CommandItem | undefined) {
63
+ if (!cmd || cmd.disabled) return
64
+ close()
65
+ onRun?.(cmd)
66
+ cmd.onRun?.()
67
+ }
68
+ function move(delta: number) {
69
+ const n = results.length
70
+ if (!n) return
71
+ active = (active + delta + n) % n
72
+ queueMicrotask(() =>
73
+ panelEl?.querySelector(`[data-idx="${active}"]`)?.scrollIntoView({ block: 'nearest' }),
74
+ )
75
+ }
76
+ function onKeydown(e: KeyboardEvent) {
77
+ if (e.key === 'ArrowDown') { e.preventDefault(); move(1) }
78
+ else if (e.key === 'ArrowUp') { e.preventDefault(); move(-1) }
79
+ else if (e.key === 'Enter') { e.preventDefault(); run(results[active]) }
80
+ // Escape + outside-click are owned by the dismissable layer.
81
+ }
82
+
83
+ // Reset active whenever the query changes.
84
+ $effect(() => { void query; active = 0 })
85
+
86
+ // Focus + scroll lock + Escape/outside dismissal while open.
87
+ $effect(() => {
88
+ if (!open || !panelEl) return
89
+ query = ''
90
+ active = 0
91
+ const trap = createFocusTrap(panelEl, { initialFocus: () => inputEl })
92
+ trap.activate()
93
+ const unlock = lockScroll()
94
+ const layer = createDismissableLayer({ element: () => panelEl, onDismiss: () => close() })
95
+ layer.activate()
96
+ return () => { layer.release(); unlock(); trap.release() }
97
+ })
98
+
99
+ // Global toggle hotkey.
100
+ $effect(() => {
101
+ if (!hotkey) return
102
+ const key = hotkey === 'mod+p' ? 'p' : 'k'
103
+ const onKey = (e: KeyboardEvent) => {
104
+ if ((e.metaKey || e.ctrlKey) && e.key.toLowerCase() === key) {
105
+ e.preventDefault()
106
+ open = !open
107
+ }
108
+ }
109
+ window.addEventListener('keydown', onKey)
110
+ return () => window.removeEventListener('keydown', onKey)
111
+ })
112
+ </script>
113
+
114
+ {#if open}
115
+ <div class="sv-cmd__backdrop" use:portalToBody>
116
+ <div
117
+ bind:this={panelEl}
118
+ class="sv-cmd"
119
+ use:popIn={{}}
120
+ role="dialog"
121
+ aria-modal="true"
122
+ aria-label="Command palette"
123
+ tabindex="-1"
124
+ onkeydown={onKeydown}
125
+ >
126
+ <div class="sv-cmd__search">
127
+ <span class="sv-cmd__searchicon" aria-hidden="true">⌕</span>
128
+ <input
129
+ bind:this={inputEl}
130
+ class="sv-cmd__input"
131
+ type="text"
132
+ role="combobox"
133
+ aria-expanded="true"
134
+ aria-controls="sv-cmd-list"
135
+ aria-activedescendant={results[active] ? `sv-cmd-opt-${active}` : undefined}
136
+ {placeholder}
137
+ value={query}
138
+ oninput={(e) => (query = e.currentTarget.value)}
139
+ />
140
+ </div>
141
+ <ul class="sv-cmd__list" id="sv-cmd-list" role="listbox" aria-label="Commands">
142
+ {#each results as c, i (c.id)}
143
+ {#if !query.trim() && c.group && c.group !== results[i - 1]?.group}
144
+ <li class="sv-cmd__group" role="presentation">{c.group}</li>
145
+ {/if}
146
+ <li
147
+ id={`sv-cmd-opt-${i}`}
148
+ data-idx={i}
149
+ class="sv-cmd__opt"
150
+ class:is-active={i === active}
151
+ role="option"
152
+ aria-selected={i === active}
153
+ onpointermove={() => (active = i)}
154
+ onclick={() => run(c)}
155
+ >
156
+ {#if c.icon}<span class="sv-cmd__icon" aria-hidden="true">{c.icon}</span>{/if}
157
+ <span class="sv-cmd__label">{c.label}</span>
158
+ {#if c.hint}<span class="sv-cmd__hint">{c.hint}</span>{/if}
159
+ {#if c.shortcut}<span class="sv-cmd__kbd">{c.shortcut}</span>{/if}
160
+ </li>
161
+ {:else}
162
+ <li class="sv-cmd__empty" aria-disabled="true">{emptyText}</li>
163
+ {/each}
164
+ </ul>
165
+ </div>
166
+ </div>
167
+ {/if}
168
+
169
+ <style>
170
+ :global(.sv-cmd__backdrop) {
171
+ position: fixed; inset: 0; z-index: 2147483000; display: flex; justify-content: center;
172
+ align-items: flex-start; padding: 12vh 16px 16px;
173
+ background: rgba(15, 23, 42, 0.42); backdrop-filter: blur(2px);
174
+ animation: sv-cmd-fade 0.14s ease both;
175
+ }
176
+ @keyframes sv-cmd-fade { from { opacity: 0 } to { opacity: 1 } }
177
+ :global(.sv-cmd) {
178
+ width: min(560px, 100%); max-height: 62vh; display: flex; flex-direction: column; overflow: hidden;
179
+ background: var(--sg-bg, #fff); color: var(--sg-fg, #0f172a);
180
+ border: 1px solid var(--sg-border, #e2e8f0); border-radius: 14px;
181
+ box-shadow: 0 24px 64px -16px rgba(15, 23, 42, 0.5);
182
+ }
183
+ :global(.sv-cmd__search) { display: flex; align-items: center; gap: 8px; padding: 12px 14px; border-bottom: 1px solid var(--sg-border, #e2e8f0); }
184
+ :global(.sv-cmd__searchicon) { color: var(--sg-muted, #64748b); font-size: 18px; }
185
+ :global(.sv-cmd__input) { flex: 1; min-width: 0; border: 0; background: none; outline: none; font: inherit; font-size: 15px; color: var(--sg-fg, #0f172a); }
186
+ :global(.sv-cmd__list) { list-style: none; margin: 0; padding: 6px; overflow: auto; }
187
+ :global(.sv-cmd__group) { padding: 8px 10px 4px; font-size: 11px; font-weight: 650; text-transform: uppercase; letter-spacing: 0.03em; color: var(--sg-muted, #94a3b8); }
188
+ :global(.sv-cmd__opt) { display: flex; align-items: center; gap: 10px; padding: 9px 10px; border-radius: 8px; cursor: pointer; font-size: 13.5px; }
189
+ :global(.sv-cmd__opt.is-active) { background: var(--sg-accent, #2563eb); color: var(--sg-on-accent, #fff); }
190
+ :global(.sv-cmd__icon) { flex: none; width: 20px; text-align: center; }
191
+ :global(.sv-cmd__label) { flex: none; }
192
+ :global(.sv-cmd__hint) { flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; color: var(--sg-muted, #94a3b8); font-size: 12.5px; }
193
+ :global(.sv-cmd__opt.is-active .sv-cmd__hint) { color: color-mix(in srgb, var(--sg-on-accent, #fff) 80%, transparent); }
194
+ :global(.sv-cmd__kbd) { flex: none; margin-inline-start: auto; font-size: 11px; padding: 1px 6px; border-radius: 5px; background: color-mix(in srgb, var(--sg-fg, #0f172a) 8%, transparent); color: var(--sg-muted, #64748b); }
195
+ :global(.sv-cmd__opt.is-active .sv-cmd__kbd) { background: color-mix(in srgb, var(--sg-on-accent, #fff) 22%, transparent); color: var(--sg-on-accent, #fff); }
196
+ :global(.sv-cmd__empty) { padding: 22px; text-align: center; color: var(--sg-muted, #94a3b8); font-size: 13px; }
197
+ </style>
@@ -0,0 +1,26 @@
1
+ export type { CommandItem } from './ui-app.types';
2
+ /**
3
+ * SvCommand - a ⌘K command palette: a fuzzy-searchable list of actions in a
4
+ * centered, focus-trapped dialog. Built on the shared primitives (portal +
5
+ * focus trap + scroll lock + dismissable layer) and the pure `fuzzyScore`
6
+ * matcher. Optionally binds a global hotkey to toggle itself. Emits WAI-ARIA
7
+ * combobox markup (activedescendant).
8
+ *
9
+ * ```svelte
10
+ * <SvCommand commands={cmds} onRun={(c) => run(c)} /> // Cmd/Ctrl+K opens it
11
+ * ```
12
+ */
13
+ import type { CommandItem } from './ui-app.types';
14
+ type Props = {
15
+ open?: boolean;
16
+ onClose?: () => void;
17
+ commands: ReadonlyArray<CommandItem>;
18
+ onRun?: (cmd: CommandItem) => void;
19
+ placeholder?: string;
20
+ emptyText?: string;
21
+ /** Global toggle hotkey (Cmd/Ctrl + key). `false` disables it. Default 'mod+k'. */
22
+ hotkey?: 'mod+k' | 'mod+p' | false;
23
+ };
24
+ declare const SvCommand: import("svelte").Component<Props, {}, "open">;
25
+ type SvCommand = ReturnType<typeof SvCommand>;
26
+ export default SvCommand;