@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
@@ -0,0 +1,220 @@
1
+ <script lang="ts">
2
+ /**
3
+ * SvModal - an accessible modal dialog: backdrop, focus trap, Escape / backdrop
4
+ * close, and a scale-in animation. Optionally draggable (by its header) and
5
+ * resizable (bottom-right handle). Parity: Smart `smart-window` (modal mode).
6
+ * Portalled to <body> so it overlays everything. Controlled via `open`.
7
+ *
8
+ * ```svelte
9
+ * <SvModal bind:open title="Edit row" draggable resizable>
10
+ * <p>Body</p>
11
+ * {#snippet footer()}<button onclick={() => open = false}>Close</button>{/snippet}
12
+ * </SvModal>
13
+ * ```
14
+ */
15
+ import type { Snippet } from 'svelte'
16
+ import { portalToBody } from './popover'
17
+ import { nextEditorId } from './editor-contract'
18
+ import { createFocusTrap } from './a11y/focus-trap'
19
+ import { lockScroll } from './a11y/scroll-lock'
20
+ import { createDismissableLayer } from './a11y/dismissable'
21
+
22
+ type Props = {
23
+ open?: boolean
24
+ onClose?: () => void
25
+ title?: string
26
+ size?: 'sm' | 'md' | 'lg'
27
+ draggable?: boolean
28
+ resizable?: boolean
29
+ closeOnBackdrop?: boolean
30
+ closeOnEsc?: boolean
31
+ /** Hide the header close (x) button. */
32
+ hideClose?: boolean
33
+ children?: Snippet
34
+ footer?: Snippet
35
+ }
36
+
37
+ let {
38
+ open = $bindable(false),
39
+ onClose,
40
+ title,
41
+ size = 'md',
42
+ draggable = false,
43
+ resizable = false,
44
+ closeOnBackdrop = true,
45
+ closeOnEsc = true,
46
+ hideClose = false,
47
+ children,
48
+ footer,
49
+ }: Props = $props()
50
+
51
+ const titleId = nextEditorId('sv-modal-title')
52
+ let dialogEl = $state<HTMLDivElement | null>(null)
53
+ let drag = $state({ x: 0, y: 0 })
54
+ let box = $state<{ w: number; h: number } | null>(null)
55
+
56
+ function close() { open = false; onClose?.() }
57
+
58
+ // Reset transient drag/resize each time it opens, then hand focus, scroll-lock
59
+ // and Escape/backdrop dismissal to the shared a11y primitives (one tested
60
+ // implementation, and nested overlays close top-first via the layer stack).
61
+ $effect(() => {
62
+ if (!open || !dialogEl) return
63
+ drag = { x: 0, y: 0 }
64
+ box = null
65
+ const trap = createFocusTrap(dialogEl)
66
+ trap.activate()
67
+ const unlockScroll = lockScroll()
68
+ const layer = createDismissableLayer({
69
+ element: () => dialogEl,
70
+ onDismiss: (reason) => {
71
+ if (reason === 'escape' ? closeOnEsc : closeOnBackdrop) close()
72
+ },
73
+ })
74
+ layer.activate()
75
+ return () => {
76
+ layer.release()
77
+ unlockScroll()
78
+ trap.release()
79
+ }
80
+ })
81
+
82
+ // --- Drag (header) ---------------------------------------------------------
83
+ let dragging = false
84
+ let dragStart = { x: 0, y: 0, ox: 0, oy: 0 }
85
+ function onHeaderDown(e: PointerEvent) {
86
+ if (!draggable || (e.target as HTMLElement).closest('.sv-modal__x')) return
87
+ dragging = true
88
+ dragStart = { x: e.clientX, y: e.clientY, ox: drag.x, oy: drag.y }
89
+ ;(e.currentTarget as HTMLElement).setPointerCapture(e.pointerId)
90
+ }
91
+ function onHeaderMove(e: PointerEvent) {
92
+ if (!dragging) return
93
+ drag = { x: dragStart.ox + (e.clientX - dragStart.x), y: dragStart.oy + (e.clientY - dragStart.y) }
94
+ }
95
+ function onHeaderUp(e: PointerEvent) {
96
+ if (!dragging) return
97
+ dragging = false
98
+ ;(e.currentTarget as HTMLElement).releasePointerCapture(e.pointerId)
99
+ }
100
+
101
+ // --- Resize (bottom-right handle) ------------------------------------------
102
+ let resizing = false
103
+ let resizeStart = { x: 0, y: 0, w: 0, h: 0 }
104
+ function onResizeDown(e: PointerEvent) {
105
+ if (!resizable || !dialogEl) return
106
+ e.stopPropagation()
107
+ resizing = true
108
+ const r = dialogEl.getBoundingClientRect()
109
+ resizeStart = { x: e.clientX, y: e.clientY, w: r.width, h: r.height }
110
+ box = { w: r.width, h: r.height }
111
+ ;(e.currentTarget as HTMLElement).setPointerCapture(e.pointerId)
112
+ }
113
+ function onResizeMove(e: PointerEvent) {
114
+ if (!resizing) return
115
+ box = {
116
+ w: Math.max(240, resizeStart.w + (e.clientX - resizeStart.x)),
117
+ h: Math.max(160, resizeStart.h + (e.clientY - resizeStart.y)),
118
+ }
119
+ }
120
+ function onResizeUp(e: PointerEvent) {
121
+ if (!resizing) return
122
+ resizing = false
123
+ ;(e.currentTarget as HTMLElement).releasePointerCapture(e.pointerId)
124
+ }
125
+ </script>
126
+
127
+ {#if open}
128
+ <div class="sv-modal__backdrop" use:portalToBody>
129
+ <div
130
+ bind:this={dialogEl}
131
+ class="sv-modal sv-modal--{size}"
132
+ class:is-resized={!!box}
133
+ role="dialog"
134
+ aria-modal="true"
135
+ aria-labelledby={title ? titleId : undefined}
136
+ tabindex="-1"
137
+ style:transform={`translate(calc(-50% + ${drag.x}px), calc(-50% + ${drag.y}px))`}
138
+ style:width={box ? `${box.w}px` : undefined}
139
+ style:height={box ? `${box.h}px` : undefined}
140
+ >
141
+ {#if title || !hideClose}
142
+ <!-- svelte-ignore a11y_no_static_element_interactions -->
143
+ <div
144
+ class="sv-modal__header"
145
+ class:is-draggable={draggable}
146
+ onpointerdown={onHeaderDown}
147
+ onpointermove={onHeaderMove}
148
+ onpointerup={onHeaderUp}
149
+ onpointercancel={onHeaderUp}
150
+ >
151
+ <h2 class="sv-modal__title" id={titleId}>{title ?? ''}</h2>
152
+ {#if !hideClose}
153
+ <button type="button" class="sv-modal__x" aria-label="Close" onclick={close}>&times;</button>
154
+ {/if}
155
+ </div>
156
+ {/if}
157
+ <div class="sv-modal__body">{@render children?.()}</div>
158
+ {#if footer}<div class="sv-modal__footer">{@render footer()}</div>{/if}
159
+ {#if resizable}
160
+ <!-- svelte-ignore a11y_no_static_element_interactions -->
161
+ <div
162
+ class="sv-modal__resize"
163
+ aria-hidden="true"
164
+ onpointerdown={onResizeDown}
165
+ onpointermove={onResizeMove}
166
+ onpointerup={onResizeUp}
167
+ onpointercancel={onResizeUp}
168
+ ></div>
169
+ {/if}
170
+ </div>
171
+ </div>
172
+ {/if}
173
+
174
+ <style>
175
+ :global(.sv-modal__backdrop) {
176
+ position: fixed; inset: 0; z-index: 2147483000;
177
+ background: rgba(15, 23, 42, 0.45); backdrop-filter: blur(1.5px);
178
+ animation: sv-modal-fade 0.16s ease both;
179
+ }
180
+ @keyframes sv-modal-fade { from { opacity: 0 } to { opacity: 1 } }
181
+ @media (prefers-reduced-motion: reduce) { :global(.sv-modal__backdrop) { animation: none } }
182
+
183
+ :global(.sv-modal) {
184
+ position: fixed; top: 50%; left: 50%;
185
+ display: flex; flex-direction: column; max-width: 94vw; max-height: 88vh; min-width: 240px;
186
+ background: var(--sg-bg, #fff); color: var(--sg-fg, #0f172a);
187
+ border: 1px solid var(--sg-border, #e2e8f0); border-radius: 12px; overflow: hidden;
188
+ box-shadow: 0 24px 64px -16px rgba(15, 23, 42, 0.5);
189
+ animation: sv-modal-in 0.18s cubic-bezier(0.16, 1, 0.3, 1) both;
190
+ }
191
+ @keyframes sv-modal-in { from { opacity: 0; transform: translate(-50%, -50%) scale(0.94) } to { opacity: 1 } }
192
+ @media (prefers-reduced-motion: reduce) { :global(.sv-modal) { animation: none } }
193
+ :global(.sv-modal--sm) { width: 360px; }
194
+ :global(.sv-modal--md) { width: 520px; }
195
+ :global(.sv-modal--lg) { width: 720px; }
196
+ :global(.sv-modal.is-resized) { max-width: none; max-height: none; }
197
+
198
+ :global(.sv-modal__header) {
199
+ display: flex; align-items: center; gap: 12px; padding: 13px 16px;
200
+ border-bottom: 1px solid var(--sg-border, #e2e8f0);
201
+ }
202
+ :global(.sv-modal__header.is-draggable) { cursor: move; touch-action: none; user-select: none; }
203
+ :global(.sv-modal__title) { flex: 1; margin: 0; font-size: 15px; font-weight: 650; }
204
+ :global(.sv-modal__x) {
205
+ flex: none; width: 28px; height: 28px; display: grid; place-items: center;
206
+ background: none; border: 0; color: var(--sg-muted, #64748b); cursor: pointer; border-radius: 6px; font-size: 20px; line-height: 1;
207
+ }
208
+ :global(.sv-modal__x:hover) { background: var(--sg-row-hover-bg, #f1f5f9); color: var(--sg-fg, #0f172a); }
209
+ :global(.sv-modal__body) { padding: 16px; overflow: auto; flex: 1; font-size: 13.5px; line-height: 1.6; }
210
+ :global(.sv-modal__footer) {
211
+ display: flex; justify-content: flex-end; gap: 8px; padding: 12px 16px;
212
+ border-top: 1px solid var(--sg-border, #e2e8f0);
213
+ }
214
+ :global(.sv-modal__resize) {
215
+ position: absolute; inset-inline-end: 0; bottom: 0; width: 16px; height: 16px;
216
+ cursor: nwse-resize; touch-action: none;
217
+ background: linear-gradient(135deg, transparent 50%, var(--sg-muted, #94a3b8) 50%, var(--sg-muted, #94a3b8) 60%, transparent 60%, transparent 72%, var(--sg-muted, #94a3b8) 72%, var(--sg-muted, #94a3b8) 82%, transparent 82%);
218
+ opacity: 0.6;
219
+ }
220
+ </style>
@@ -0,0 +1,31 @@
1
+ /**
2
+ * SvModal - an accessible modal dialog: backdrop, focus trap, Escape / backdrop
3
+ * close, and a scale-in animation. Optionally draggable (by its header) and
4
+ * resizable (bottom-right handle). Parity: Smart `smart-window` (modal mode).
5
+ * Portalled to <body> so it overlays everything. Controlled via `open`.
6
+ *
7
+ * ```svelte
8
+ * <SvModal bind:open title="Edit row" draggable resizable>
9
+ * <p>Body</p>
10
+ * {#snippet footer()}<button onclick={() => open = false}>Close</button>{/snippet}
11
+ * </SvModal>
12
+ * ```
13
+ */
14
+ import type { Snippet } from 'svelte';
15
+ type Props = {
16
+ open?: boolean;
17
+ onClose?: () => void;
18
+ title?: string;
19
+ size?: 'sm' | 'md' | 'lg';
20
+ draggable?: boolean;
21
+ resizable?: boolean;
22
+ closeOnBackdrop?: boolean;
23
+ closeOnEsc?: boolean;
24
+ /** Hide the header close (x) button. */
25
+ hideClose?: boolean;
26
+ children?: Snippet;
27
+ footer?: Snippet;
28
+ };
29
+ declare const SvModal: import("svelte").Component<Props, {}, "open">;
30
+ type SvModal = ReturnType<typeof SvModal>;
31
+ export default SvModal;
@@ -0,0 +1,277 @@
1
+ <script lang="ts" module>
2
+ export type { MultiSelectOption } from './ui-app.types'
3
+ </script>
4
+
5
+ <script lang="ts">
6
+ /**
7
+ * SvMultiSelect - a multi-select dropdown: a trigger showing the picked items
8
+ * as chips (collapsing to "+N"), and a portalled panel with an optional search
9
+ * box and a checkbox list. The open/position/dismiss/roving/keyboard/ARIA
10
+ * mechanics come from the shared `createPopoverSelect` core; this component adds
11
+ * the multi-select toggle, chips and search UI. Distinct from SvTagsInput
12
+ * (free-form tags) - this picks from a fixed set.
13
+ */
14
+ import type { MultiSelectOption } from './ui-app.types'
15
+ import SvField from './SvField.svelte'
16
+ import { editorAria, nextEditorId, type SvEditorProps } from './editor-contract'
17
+ import { portalToBody, popIn } from './popover'
18
+ import { createPopoverSelect } from './createPopoverSelect.svelte'
19
+ import { debounce } from './debounce'
20
+
21
+ type Props = Pick<
22
+ SvEditorProps,
23
+ 'disabled' | 'label' | 'hint' | 'error' | 'required' | 'invalid' | 'size' | 'id' | 'name' | 'dir' | 'ariaLabel'
24
+ > & {
25
+ options: ReadonlyArray<MultiSelectOption>
26
+ value?: ReadonlyArray<string | number>
27
+ onChange?: (value: Array<string | number>) => void
28
+ onCommit?: (value: Array<string | number>) => void
29
+ onCancel?: () => void
30
+ placeholder?: string
31
+ searchable?: boolean
32
+ maxTagCount?: number
33
+ clearable?: boolean
34
+ /** Async/remote options: called (debounced) with the search query; its
35
+ * result replaces the list. When set, `options` is only the initial list. */
36
+ loadOptions?: (query: string) => Promise<MultiSelectOption[]>
37
+ /** Debounce for remote search. Default 250ms. */
38
+ debounceMs?: number
39
+ loadingText?: string
40
+ }
41
+
42
+ let {
43
+ options,
44
+ value = $bindable<ReadonlyArray<string | number>>([]),
45
+ onChange,
46
+ onCommit,
47
+ onCancel,
48
+ placeholder = 'Select…',
49
+ searchable = true,
50
+ maxTagCount = 3,
51
+ clearable = true,
52
+ loadOptions,
53
+ debounceMs = 250,
54
+ loadingText = 'Loading…',
55
+ disabled = false,
56
+ label,
57
+ hint,
58
+ error,
59
+ required = false,
60
+ invalid = false,
61
+ size = 'md',
62
+ id,
63
+ name,
64
+ dir,
65
+ ariaLabel,
66
+ }: Props = $props()
67
+
68
+ const autoId = nextEditorId('sv-ms')
69
+ const uid = $derived(id ?? autoId)
70
+
71
+ let search = $state('')
72
+ let triggerEl = $state<HTMLButtonElement | null>(null)
73
+ let panelEl = $state<HTMLDivElement | null>(null)
74
+ let searchEl = $state<HTMLInputElement | null>(null)
75
+ let listEl = $state<HTMLUListElement | null>(null)
76
+
77
+ // Remote/async options: `asyncOptions` holds the last server result; a stale
78
+ // response is dropped by comparing the request sequence.
79
+ let asyncOptions = $state<MultiSelectOption[]>([])
80
+ let loading = $state(false)
81
+ let reqSeq = 0
82
+ function doLoad(q: string) {
83
+ if (!loadOptions) return
84
+ const seq = ++reqSeq
85
+ loading = true
86
+ loadOptions(q)
87
+ .then((opts) => { if (seq === reqSeq) { asyncOptions = opts; loading = false } })
88
+ .catch(() => { if (seq === reqSeq) loading = false })
89
+ }
90
+ const runLoad = debounce((q: string) => doLoad(q), debounceMs)
91
+
92
+ const selected = $derived(new Set(value))
93
+ const filtered = $derived(
94
+ loadOptions
95
+ ? asyncOptions
96
+ : search.trim()
97
+ ? options.filter((o) => o.label.toLowerCase().includes(search.trim().toLowerCase()))
98
+ : options,
99
+ )
100
+ // Pool for resolving selected-chip labels (a remote-selected value may not be
101
+ // in the current result page, so merge the seed options with the last result).
102
+ const pool = $derived(
103
+ loadOptions
104
+ ? [...new Map([...options, ...asyncOptions].map((o) => [o.value, o])).values()]
105
+ : options,
106
+ )
107
+ const selectedOptions = $derived(pool.filter((o) => selected.has(o.value)))
108
+
109
+ function emit(next: Array<string | number>) {
110
+ value = next // enables bind:value
111
+ onChange?.(next)
112
+ }
113
+ function toggleOption(opt: MultiSelectOption) {
114
+ if (opt.disabled) return
115
+ const next = new Set(selected)
116
+ next.has(opt.value) ? next.delete(opt.value) : next.add(opt.value)
117
+ emit([...next])
118
+ }
119
+
120
+ const ms = createPopoverSelect({
121
+ itemCount: () => filtered.length,
122
+ disabled: (i) => !!filtered[i]?.disabled,
123
+ onSelect: (i) => { const o = filtered[i]; if (o) toggleOption(o) },
124
+ onOpenChange: (o) => { if (o) { search = ''; if (loadOptions) doLoad('') } },
125
+ getTrigger: () => triggerEl,
126
+ getPanel: () => panelEl,
127
+ getInitialFocus: () => (searchable ? searchEl : listEl),
128
+ closeOnSelect: () => false,
129
+ minWidth: () => 200,
130
+ idPrefix: 'sv-ms',
131
+ })
132
+ </script>
133
+
134
+ <SvField id={uid} {label} {hint} {error} {required} {dir}>
135
+ <div class="sv-ms sv-ms--{size}" class:is-disabled={disabled}>
136
+ <button
137
+ bind:this={triggerEl}
138
+ type="button"
139
+ class="sv-ms__trigger"
140
+ class:is-invalid={invalid}
141
+ {disabled}
142
+ onclick={() => ms.toggle()}
143
+ {...ms.triggerProps()}
144
+ {...editorAria({ id: uid, invalid, required, error, hint, ariaLabel })}
145
+ >
146
+ {#if selectedOptions.length === 0}
147
+ <span class="sv-ms__placeholder">{placeholder}</span>
148
+ {:else}
149
+ <span class="sv-ms__chips">
150
+ {#each selectedOptions.slice(0, maxTagCount) as opt (opt.value)}
151
+ <span class="sv-ms__chip">{opt.label}</span>
152
+ {/each}
153
+ {#if selectedOptions.length > maxTagCount}
154
+ <span class="sv-ms__chip sv-ms__chip--more">+{selectedOptions.length - maxTagCount}</span>
155
+ {/if}
156
+ </span>
157
+ {/if}
158
+ <span class="sv-ms__arrow" aria-hidden="true">▾</span>
159
+ </button>
160
+ {#if clearable && selectedOptions.length && !disabled}
161
+ <button type="button" class="sv-ms__clear" tabindex="-1" aria-label="Clear all" onclick={() => emit([])}>&times;</button>
162
+ {/if}
163
+ {#if name}
164
+ {#each value as v (v)}<input type="hidden" {name} value={v} />{/each}
165
+ {/if}
166
+ </div>
167
+ </SvField>
168
+
169
+ {#if ms.open}
170
+ <!-- svelte-ignore a11y_no_static_element_interactions -->
171
+ <div
172
+ bind:this={panelEl}
173
+ class="sv-ms__panel"
174
+ dir={dir}
175
+ use:portalToBody
176
+ use:popIn={{ up: ms.rect.openUpward }}
177
+ style:position="fixed"
178
+ style:top={`${ms.rect.top}px`}
179
+ style:left={`${ms.rect.left}px`}
180
+ style:min-width={`${ms.rect.width}px`}
181
+ onkeydown={ms.onPanelKeydown}
182
+ >
183
+ {#if searchable}
184
+ <!-- svelte-ignore a11y_autofocus -->
185
+ <input
186
+ bind:this={searchEl}
187
+ class="sv-ms__search"
188
+ role="combobox"
189
+ aria-expanded="true"
190
+ aria-controls={ms.panelId}
191
+ aria-activedescendant={ms.activeDescendant()}
192
+ aria-label="Search options"
193
+ placeholder="Search…"
194
+ value={search}
195
+ oninput={(e) => { search = e.currentTarget.value; ms.active = 0; if (loadOptions) runLoad(search) }}
196
+ />
197
+ {/if}
198
+ <ul
199
+ bind:this={listEl}
200
+ id={ms.panelId}
201
+ class="sv-ms__list"
202
+ role="listbox"
203
+ aria-multiselectable="true"
204
+ aria-label={label ?? ariaLabel ?? 'Options'}
205
+ aria-activedescendant={searchable ? undefined : ms.activeDescendant()}
206
+ tabindex={searchable ? -1 : 0}
207
+ >
208
+ {#each filtered as opt, i (opt.value)}
209
+ <li
210
+ class="sv-ms__opt"
211
+ class:is-active={ms.isActive(i)}
212
+ class:is-disabled={opt.disabled}
213
+ role="option"
214
+ aria-selected={selected.has(opt.value)}
215
+ aria-disabled={opt.disabled}
216
+ onclick={() => toggleOption(opt)}
217
+ {...ms.itemProps(i)}
218
+ >
219
+ <span class="sv-ms__check" aria-hidden="true">{selected.has(opt.value) ? '✓' : ''}</span>
220
+ <span class="sv-ms__label">{opt.label}</span>
221
+ </li>
222
+ {:else}
223
+ <li class="sv-ms__empty" aria-disabled="true">{loading ? loadingText : 'No matches'}</li>
224
+ {/each}
225
+ {#if loading && filtered.length}
226
+ <li class="sv-ms__loading" aria-live="polite">{loadingText}</li>
227
+ {/if}
228
+ </ul>
229
+ <div class="sv-ms__footer">
230
+ <button type="button" class="sv-ms__done" onclick={() => { onCommit?.([...value]); ms.closePanel() }}>Done</button>
231
+ </div>
232
+ </div>
233
+ {/if}
234
+
235
+ <style>
236
+ .sv-ms { --_accent: var(--sg-accent, #2563eb); display: inline-flex; align-items: stretch; position: relative; width: 240px; }
237
+ .sv-ms.is-disabled { opacity: 0.6; }
238
+ .sv-ms__trigger {
239
+ flex: 1; min-width: 0; display: flex; align-items: center; gap: 6px; text-align: start;
240
+ background: var(--sg-input-bg, #fff); color: var(--sg-fg, #0f172a);
241
+ border: 1px solid var(--sg-input-border, var(--sg-border, #cbd5e1)); border-radius: var(--sg-radius, 8px);
242
+ font: inherit; cursor: pointer; padding: 0 8px;
243
+ }
244
+ .sv-ms--sm .sv-ms__trigger { min-height: 28px; font-size: 12px; }
245
+ .sv-ms--md .sv-ms__trigger { min-height: 34px; font-size: 13px; }
246
+ .sv-ms--lg .sv-ms__trigger { min-height: 40px; font-size: 15px; }
247
+ .sv-ms__trigger:focus-visible { outline: none; border-color: var(--_accent); box-shadow: 0 0 0 2px color-mix(in srgb, var(--_accent) 22%, transparent); }
248
+ .sv-ms__trigger.is-invalid { border-color: var(--sg-danger, #dc2626); }
249
+ .sv-ms__placeholder { flex: 1; color: var(--sg-muted, #94a3b8); }
250
+ .sv-ms__chips { flex: 1; display: flex; flex-wrap: wrap; gap: 4px; padding: 4px 0; }
251
+ .sv-ms__chip { display: inline-flex; align-items: center; padding: 1px 7px; border-radius: 999px; font-size: 11.5px; background: color-mix(in srgb, var(--_accent) 14%, transparent); color: var(--_accent); }
252
+ .sv-ms__chip--more { background: var(--sg-row-hover-bg, #f1f5f9); color: var(--sg-muted, #64748b); }
253
+ .sv-ms__arrow { margin-inline-start: auto; color: var(--sg-muted, #64748b); font-size: 10px; }
254
+ .sv-ms__clear { position: absolute; inset-inline-end: 26px; align-self: center; background: none; border: 0; color: var(--sg-muted, #64748b); cursor: pointer; font-size: 15px; }
255
+ .sv-ms__clear:hover { color: var(--sg-fg, #0f172a); }
256
+
257
+ :global(.sv-ms__panel) {
258
+ z-index: 2147483646; display: flex; flex-direction: column; max-height: 300px; max-width: 92vw;
259
+ background: var(--sg-bg, #fff); color: var(--sg-fg, #0f172a);
260
+ border: 1px solid var(--sg-border, #e2e8f0); border-radius: 10px; overflow: hidden;
261
+ box-shadow: 0 16px 48px -12px rgba(15, 23, 42, 0.35); font-size: 13px;
262
+ }
263
+ :global(.sv-ms__search) {
264
+ margin: 8px; padding: 6px 9px; font: inherit; font-size: 13px;
265
+ border: 1px solid var(--sg-input-border, #cbd5e1); border-radius: 7px;
266
+ background: var(--sg-input-bg, #fff); color: var(--sg-fg, #0f172a); outline: none;
267
+ }
268
+ :global(.sv-ms__list) { list-style: none; margin: 0; padding: 4px; overflow: auto; flex: 1; outline: none; }
269
+ :global(.sv-ms__opt) { display: flex; align-items: center; gap: 8px; padding: 6px 8px; border-radius: 6px; cursor: pointer; }
270
+ :global(.sv-ms__opt.is-active) { background: var(--sg-row-hover-bg, #f1f5f9); }
271
+ :global(.sv-ms__opt.is-disabled) { opacity: 0.5; cursor: not-allowed; }
272
+ :global(.sv-ms__check) { width: 14px; color: var(--sg-accent, #2563eb); font-weight: 700; }
273
+ :global(.sv-ms__empty) { padding: 10px; color: var(--sg-muted, #94a3b8); text-align: center; }
274
+ :global(.sv-ms__loading) { padding: 8px 10px; color: var(--sg-muted, #94a3b8); font-size: 12px; text-align: center; }
275
+ :global(.sv-ms__footer) { display: flex; justify-content: flex-end; padding: 6px 8px; border-top: 1px solid var(--sg-border, #e2e8f0); }
276
+ :global(.sv-ms__done) { font: inherit; font-size: 12.5px; padding: 4px 12px; border: 0; border-radius: 6px; background: var(--sg-accent, #2563eb); color: var(--sg-on-accent, #fff); cursor: pointer; }
277
+ </style>
@@ -0,0 +1,31 @@
1
+ export type { MultiSelectOption } from './ui-app.types';
2
+ /**
3
+ * SvMultiSelect - a multi-select dropdown: a trigger showing the picked items
4
+ * as chips (collapsing to "+N"), and a portalled panel with an optional search
5
+ * box and a checkbox list. The open/position/dismiss/roving/keyboard/ARIA
6
+ * mechanics come from the shared `createPopoverSelect` core; this component adds
7
+ * the multi-select toggle, chips and search UI. Distinct from SvTagsInput
8
+ * (free-form tags) - this picks from a fixed set.
9
+ */
10
+ import type { MultiSelectOption } from './ui-app.types';
11
+ import { type SvEditorProps } from './editor-contract';
12
+ type Props = Pick<SvEditorProps, 'disabled' | 'label' | 'hint' | 'error' | 'required' | 'invalid' | 'size' | 'id' | 'name' | 'dir' | 'ariaLabel'> & {
13
+ options: ReadonlyArray<MultiSelectOption>;
14
+ value?: ReadonlyArray<string | number>;
15
+ onChange?: (value: Array<string | number>) => void;
16
+ onCommit?: (value: Array<string | number>) => void;
17
+ onCancel?: () => void;
18
+ placeholder?: string;
19
+ searchable?: boolean;
20
+ maxTagCount?: number;
21
+ clearable?: boolean;
22
+ /** Async/remote options: called (debounced) with the search query; its
23
+ * result replaces the list. When set, `options` is only the initial list. */
24
+ loadOptions?: (query: string) => Promise<MultiSelectOption[]>;
25
+ /** Debounce for remote search. Default 250ms. */
26
+ debounceMs?: number;
27
+ loadingText?: string;
28
+ };
29
+ declare const SvMultiSelect: import("svelte").Component<Props, {}, "value">;
30
+ type SvMultiSelect = ReturnType<typeof SvMultiSelect>;
31
+ export default SvMultiSelect;