@svgrid/grid 1.2.34 → 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
@@ -1,22 +1,3 @@
1
- /**
2
- * createColorInput - the HEADLESS core behind <SvColorInput>: a runes-based
3
- * state machine that owns hex normalization, the palette, the draft hex field
4
- * and the open/close popover state, exposed as **prop-getters** you spread onto
5
- * YOUR OWN markup. DOM measurement / portalling stays in the renderer, NOT here.
6
- *
7
- * ```svelte
8
- * <script lang="ts">
9
- * import { createColorInput } from '@svgrid/grid'
10
- * let value = $state('#3b82f6')
11
- * const col = createColorInput({ value: () => value, onChange: (v) => (value = v) })
12
- * </script>
13
- * <button {...col.swatchProps()}>{col.normalized}</button>
14
- * {#if col.popover.open}<div>...palette...</div>{/if}
15
- * ```
16
- *
17
- * The styled <SvColorInput> is just one renderer over this core (it adds the
18
- * portal + anchored positioning).
19
- */
20
1
  export declare const DEFAULT_PALETTE: string[];
21
2
  /** Normalize a hex string (#abc / #aabbcc / abc) to lower-case #aabbcc (pure). */
22
3
  export declare function normalizeHex(h: string): string | null;
@@ -27,6 +8,11 @@ export type ColorInputConfig = {
27
8
  disabled?: () => boolean;
28
9
  readonly?: () => boolean;
29
10
  palette?: () => string[];
11
+ id?: () => string | undefined;
12
+ invalid?: () => boolean;
13
+ required?: () => boolean;
14
+ error?: () => string | undefined;
15
+ hint?: () => string | undefined;
30
16
  ariaLabel?: () => string | undefined;
31
17
  };
32
18
  export declare function createColorInput(config: ColorInputConfig): {
@@ -50,12 +36,16 @@ export declare function createColorInput(config: ColorInputConfig): {
50
36
  isActive: (c: string) => boolean;
51
37
  /** Spread onto the swatch trigger button. */
52
38
  swatchProps: () => {
53
- type: "button";
54
- 'aria-haspopup': "dialog";
55
- 'aria-expanded': boolean;
56
39
  'aria-label': string;
57
40
  disabled: boolean;
58
41
  onclick: () => void;
42
+ 'aria-invalid': "true" | undefined;
43
+ 'aria-required': "true" | undefined;
44
+ 'aria-describedby': string | undefined;
45
+ id: string | undefined;
46
+ type: "button";
47
+ 'aria-haspopup': "dialog";
48
+ 'aria-expanded': boolean;
59
49
  };
60
50
  /** Spread onto a palette chip button for color `c`. */
61
51
  chipProps: (c: string) => {
@@ -17,6 +17,7 @@
17
17
  * The styled <SvColorInput> is just one renderer over this core (it adds the
18
18
  * portal + anchored positioning).
19
19
  */
20
+ import { editorAria } from './editor-contract';
20
21
  export const DEFAULT_PALETTE = [
21
22
  '#ef4444', '#f97316', '#f59e0b', '#eab308', '#84cc16', '#22c55e', '#10b981', '#14b8a6',
22
23
  '#06b6d4', '#0ea5e9', '#3b82f6', '#6366f1', '#8b5cf6', '#a855f7', '#d946ef', '#ec4899',
@@ -59,6 +60,14 @@ export function createColorInput(config) {
59
60
  hexDraft = normalized;
60
61
  }
61
62
  const isActive = (c) => normalizeHex(c) === normalized;
63
+ const ariaState = () => ({
64
+ id: config.id?.(),
65
+ invalid: config.invalid?.(),
66
+ required: config.required?.(),
67
+ error: config.error?.(),
68
+ hint: config.hint?.(),
69
+ ariaLabel: config.ariaLabel?.(),
70
+ });
62
71
  // Popover control surface (open state + show/close/toggle), no DOM measurement.
63
72
  const popover = {
64
73
  get open() { return open; },
@@ -83,9 +92,13 @@ export function createColorInput(config) {
83
92
  isActive,
84
93
  /** Spread onto the swatch trigger button. */
85
94
  swatchProps: () => ({
95
+ id: config.id?.(),
86
96
  type: 'button',
87
97
  'aria-haspopup': 'dialog',
88
98
  'aria-expanded': open,
99
+ // aria-invalid/required/describedby come from the editor contract; the
100
+ // aria-label default names the swatch by its current color when unset.
101
+ ...editorAria(ariaState()),
89
102
  'aria-label': config.ariaLabel?.() ?? `Color ${normalized}`,
90
103
  disabled: !isInteractive,
91
104
  onclick: toggle,
@@ -28,9 +28,16 @@ export type ComboboxConfig = {
28
28
  onChange?: (value: ComboboxValue) => void;
29
29
  disabled?: () => boolean;
30
30
  ariaLabel?: () => string | undefined;
31
+ /** Filter the options locally by the query. Set false for server-side search. */
32
+ localFilter?: () => boolean;
31
33
  /** DOM focus hooks provided by the renderer; the core itself never touches the DOM. */
32
34
  focusInput?: () => void;
33
35
  blurInput?: () => void;
36
+ id?: () => string | undefined;
37
+ invalid?: () => boolean;
38
+ required?: () => boolean;
39
+ error?: () => string | undefined;
40
+ hint?: () => string | undefined;
34
41
  };
35
42
  export declare function createCombobox(config: ComboboxConfig): {
36
43
  /** Panel open state. */
@@ -63,16 +70,20 @@ export declare function createCombobox(config: ComboboxConfig): {
63
70
  onKeydown: (e: KeyboardEvent) => void;
64
71
  /** Spread onto the editable <input>. */
65
72
  inputProps: () => {
66
- role: "combobox";
67
- 'aria-expanded': boolean;
68
- 'aria-controls': string;
69
- 'aria-autocomplete': "list";
70
- 'aria-label': string | undefined;
71
73
  value: string;
72
74
  disabled: boolean;
73
75
  oninput: (e: Event) => void;
74
76
  onfocus: () => void;
75
77
  onkeydown: (e: KeyboardEvent) => void;
78
+ 'aria-invalid': "true" | undefined;
79
+ 'aria-required': "true" | undefined;
80
+ 'aria-describedby': string | undefined;
81
+ 'aria-label': string | undefined;
82
+ id: string | undefined;
83
+ role: "combobox";
84
+ 'aria-expanded': boolean;
85
+ 'aria-controls': string;
86
+ 'aria-autocomplete': "list";
76
87
  };
77
88
  /** Spread onto the chevron/toggle <button>. */
78
89
  triggerProps: () => {
@@ -20,6 +20,7 @@
20
20
  * ```
21
21
  */
22
22
  import { filterOptions } from './list-option';
23
+ import { editorAria } from './editor-contract';
23
24
  let uid = 0;
24
25
  export function createCombobox(config) {
25
26
  const id = `sv-combo-${uid++}`;
@@ -30,13 +31,22 @@ export function createCombobox(config) {
30
31
  let query = $state('');
31
32
  let active = $state(0);
32
33
  let editing = $state(false);
34
+ const localFilter = () => config.localFilter?.() ?? true;
33
35
  const selected = $derived(opts().find((o) => o.value === config.value()) ?? null);
34
- const filtered = $derived(editing ? filterOptions(opts(), query) : [...opts()]);
36
+ const filtered = $derived(editing && localFilter() ? filterOptions(opts(), query) : [...opts()]);
35
37
  const shownText = $derived(editing ? query : selected?.label ?? '');
36
38
  // While not actively editing, keep the shown text in sync with the selection.
37
39
  $effect(() => { if (!editing)
38
40
  query = selected?.label ?? ''; });
39
41
  const optionId = (i) => `${listId}-opt-${i}`;
42
+ const ariaState = () => ({
43
+ id: config.id?.(),
44
+ invalid: config.invalid?.(),
45
+ required: config.required?.(),
46
+ error: config.error?.(),
47
+ hint: config.hint?.(),
48
+ ariaLabel: config.ariaLabel?.(),
49
+ });
40
50
  function openPanel() { if (disabled() || open)
41
51
  return; open = true; active = 0; }
42
52
  function close(revert = true) {
@@ -128,11 +138,12 @@ export function createCombobox(config) {
128
138
  onKeydown,
129
139
  /** Spread onto the editable <input>. */
130
140
  inputProps: () => ({
141
+ id: config.id?.(),
131
142
  role: 'combobox',
132
143
  'aria-expanded': open,
133
144
  'aria-controls': listId,
134
145
  'aria-autocomplete': 'list',
135
- 'aria-label': config.ariaLabel?.(),
146
+ ...editorAria(ariaState()),
136
147
  value: shownText,
137
148
  disabled: disabled(),
138
149
  oninput: onInput,
@@ -27,8 +27,15 @@ export type CountryInputConfig = {
27
27
  onChange?: (code: string) => void;
28
28
  disabled?: () => boolean;
29
29
  ariaLabel?: () => string | undefined;
30
+ /** Accessible label for the in-panel search field (localizable). */
31
+ searchLabel?: () => string | undefined;
30
32
  /** DOM focus hook provided by the renderer; the core never touches the DOM. */
31
33
  focusTrigger?: () => void;
34
+ id?: () => string | undefined;
35
+ invalid?: () => boolean;
36
+ required?: () => boolean;
37
+ error?: () => string | undefined;
38
+ hint?: () => string | undefined;
32
39
  };
33
40
  export declare function createCountryInput(config: CountryInputConfig): {
34
41
  /** Panel open state. */
@@ -54,12 +61,16 @@ export declare function createCountryInput(config: CountryInputConfig): {
54
61
  onKeydown: (e: KeyboardEvent) => void;
55
62
  /** Spread onto the trigger <button>. */
56
63
  triggerProps: () => {
64
+ disabled: boolean;
65
+ onclick: () => void;
66
+ 'aria-invalid': "true" | undefined;
67
+ 'aria-required': "true" | undefined;
68
+ 'aria-describedby': string | undefined;
69
+ 'aria-label': string | undefined;
70
+ id: string | undefined;
57
71
  type: "button";
58
72
  'aria-haspopup': "listbox";
59
73
  'aria-expanded': boolean;
60
- 'aria-label': string | undefined;
61
- disabled: boolean;
62
- onclick: () => void;
63
74
  };
64
75
  /** Spread onto the search <input> inside the panel. */
65
76
  searchProps: () => {
@@ -21,8 +21,17 @@
21
21
  * ```
22
22
  */
23
23
  import { COUNTRIES, COUNTRY_BY_CODE } from './countries';
24
+ import { editorAria } from './editor-contract';
24
25
  export function createCountryInput(config) {
25
26
  const disabled = () => config.disabled?.() ?? false;
27
+ const ariaState = () => ({
28
+ id: config.id?.(),
29
+ invalid: config.invalid?.(),
30
+ required: config.required?.(),
31
+ error: config.error?.(),
32
+ hint: config.hint?.(),
33
+ ariaLabel: config.ariaLabel?.(),
34
+ });
26
35
  let open = $state(false);
27
36
  let query = $state('');
28
37
  let active = $state(0);
@@ -90,10 +99,11 @@ export function createCountryInput(config) {
90
99
  onKeydown,
91
100
  /** Spread onto the trigger <button>. */
92
101
  triggerProps: () => ({
102
+ id: config.id?.(),
93
103
  type: 'button',
94
104
  'aria-haspopup': 'listbox',
95
105
  'aria-expanded': open,
96
- 'aria-label': config.ariaLabel?.(),
106
+ ...editorAria(ariaState()),
97
107
  disabled: disabled(),
98
108
  onclick: toggle,
99
109
  }),
@@ -101,7 +111,7 @@ export function createCountryInput(config) {
101
111
  searchProps: () => ({
102
112
  type: 'text',
103
113
  value: query,
104
- 'aria-label': 'Search countries',
114
+ 'aria-label': config.searchLabel?.() ?? 'Search countries',
105
115
  oninput: onSearchInput,
106
116
  onkeydown: onKeydown,
107
117
  }),
@@ -20,6 +20,16 @@ export type DateTimePickerConfig = {
20
20
  stepMinutes?: () => number;
21
21
  disabled?: () => boolean;
22
22
  readonly?: () => boolean;
23
+ /** Accessible label for the open-picker toggle (localizable). */
24
+ toggleLabel?: () => string | undefined;
25
+ /** Accessible label for the clear button (localizable). */
26
+ clearLabel?: () => string | undefined;
27
+ id?: () => string | undefined;
28
+ invalid?: () => boolean;
29
+ required?: () => boolean;
30
+ error?: () => string | undefined;
31
+ hint?: () => string | undefined;
32
+ ariaLabel?: () => string | undefined;
23
33
  };
24
34
  export declare function createDateTimePicker(config: DateTimePickerConfig): {
25
35
  readonly current: Date | null;
@@ -42,11 +52,16 @@ export declare function createDateTimePicker(config: DateTimePickerConfig): {
42
52
  /** The text field: masked value round-trips through the token engine. Spread
43
53
  * alongside `bind:value={dtp.text}`. */
44
54
  inputProps(): {
55
+ onblur: () => void;
56
+ onkeydown: (e: KeyboardEvent) => void;
57
+ 'aria-invalid': "true" | undefined;
58
+ 'aria-required': "true" | undefined;
59
+ 'aria-describedby': string | undefined;
60
+ 'aria-label': string | undefined;
61
+ id: string | undefined;
45
62
  type: "text";
46
63
  disabled: boolean;
47
64
  readonly: boolean;
48
- onblur: () => void;
49
- onkeydown: (e: KeyboardEvent) => void;
50
65
  };
51
66
  /** The calendar-toggle button. */
52
67
  toggleProps(): {
@@ -17,6 +17,7 @@
17
17
  */
18
18
  import { formatDate, parseDate } from './datetime/date-format';
19
19
  import { toDate, clampDate, withTime, startOfDay } from './datetime/date-core';
20
+ import { editorAria } from './editor-contract';
20
21
  const defaultLocale = () => (typeof navigator !== 'undefined' ? navigator.language : 'en-US');
21
22
  export function createDateTimePicker(config) {
22
23
  const formatString = () => config.formatString?.() ?? 'yyyy-MM-dd HH:mm';
@@ -152,9 +153,18 @@ export function createDateTimePicker(config) {
152
153
  * alongside `bind:value={dtp.text}`. */
153
154
  inputProps() {
154
155
  return {
156
+ id: config.id?.(),
155
157
  type: 'text',
156
158
  disabled: isDisabled(),
157
159
  readonly: isReadonly(),
160
+ ...editorAria({
161
+ id: config.id?.(),
162
+ invalid: config.invalid?.(),
163
+ required: config.required?.(),
164
+ error: config.error?.(),
165
+ hint: config.hint?.(),
166
+ ariaLabel: config.ariaLabel?.(),
167
+ }),
158
168
  onblur: commitFromText,
159
169
  onkeydown: onInputKeydown,
160
170
  };
@@ -163,7 +173,7 @@ export function createDateTimePicker(config) {
163
173
  toggleProps() {
164
174
  return {
165
175
  type: 'button',
166
- 'aria-label': 'Open picker',
176
+ 'aria-label': config.toggleLabel?.() ?? 'Open picker',
167
177
  'aria-haspopup': 'dialog',
168
178
  'aria-expanded': open,
169
179
  disabled: !isInteractive,
@@ -175,7 +185,7 @@ export function createDateTimePicker(config) {
175
185
  clearProps() {
176
186
  return {
177
187
  type: 'button',
178
- 'aria-label': 'Clear',
188
+ 'aria-label': config.clearLabel?.() ?? 'Clear',
179
189
  onclick: () => commit(null),
180
190
  };
181
191
  },
@@ -19,7 +19,7 @@
19
19
  * {/if}
20
20
  * ```
21
21
  */
22
- import type { ListOption } from './list-option';
22
+ import { type ListOption } from './list-option';
23
23
  export type DropdownValue = string | number | null;
24
24
  /** Reactive inputs are passed as getters so the core tracks live prop changes. */
25
25
  export type DropdownListConfig = {
@@ -30,6 +30,11 @@ export type DropdownListConfig = {
30
30
  ariaLabel?: () => string | undefined;
31
31
  /** DOM focus hook provided by the renderer; the core never touches the DOM. */
32
32
  focusTrigger?: () => void;
33
+ id?: () => string | undefined;
34
+ invalid?: () => boolean;
35
+ required?: () => boolean;
36
+ error?: () => string | undefined;
37
+ hint?: () => string | undefined;
33
38
  };
34
39
  export declare function createDropdownList(config: DropdownListConfig): {
35
40
  /** Panel open state. */
@@ -51,13 +56,17 @@ export declare function createDropdownList(config: DropdownListConfig): {
51
56
  onKeydown: (e: KeyboardEvent) => void;
52
57
  /** Spread onto the trigger <button>. */
53
58
  triggerProps: () => {
54
- type: "button";
55
- 'aria-haspopup': "listbox";
56
- 'aria-expanded': boolean;
57
- 'aria-label': string | undefined;
58
59
  disabled: boolean;
59
60
  onclick: () => void;
60
61
  onkeydown: (e: KeyboardEvent) => void;
62
+ 'aria-invalid': "true" | undefined;
63
+ 'aria-required': "true" | undefined;
64
+ 'aria-describedby': string | undefined;
65
+ 'aria-label': string | undefined;
66
+ id: string | undefined;
67
+ type: "button";
68
+ 'aria-haspopup': "listbox";
69
+ 'aria-expanded': boolean;
61
70
  };
62
71
  /** Spread onto the listbox container. */
63
72
  listboxProps: () => {
@@ -1,6 +1,37 @@
1
+ /**
2
+ * createDropdownList - the HEADLESS core behind <SvDropDownList>: a single-select
3
+ * dropdown state machine (trigger open/close, roving active index over enabled
4
+ * options, full keyboard) exposed as **prop-getters** you spread onto YOUR OWN
5
+ * markup. No styles, no DOM, no portal/measurement - those stay in the styled
6
+ * component.
7
+ *
8
+ * ```svelte
9
+ * <script lang="ts">
10
+ * import { createDropdownList } from '@svgrid/grid'
11
+ * let value = $state<string | null>(null)
12
+ * const dd = createDropdownList({ options: () => options, value: () => value, onChange: (v) => (value = v) })
13
+ * </script>
14
+ * <button {...dd.triggerProps()}>{dd.selected?.label ?? 'Select'}</button>
15
+ * {#if dd.open}
16
+ * <ul {...dd.listboxProps()}>
17
+ * {#each options as opt, i (opt.value)}<li {...dd.optionProps(i)}>{opt.label}</li>{/each}
18
+ * </ul>
19
+ * {/if}
20
+ * ```
21
+ */
22
+ import { createTypeaheadBuffer, isTypeaheadKey, nextTypeaheadIndex } from './list-option';
23
+ import { editorAria } from './editor-contract';
1
24
  export function createDropdownList(config) {
2
25
  const opts = () => config.options();
3
26
  const disabled = () => config.disabled?.() ?? false;
27
+ const ariaState = () => ({
28
+ id: config.id?.(),
29
+ invalid: config.invalid?.(),
30
+ required: config.required?.(),
31
+ error: config.error?.(),
32
+ hint: config.hint?.(),
33
+ ariaLabel: config.ariaLabel?.(),
34
+ });
4
35
  let open = $state(false);
5
36
  let active = $state(-1);
6
37
  const selected = $derived(opts().find((o) => o.value === config.value()) ?? null);
@@ -32,9 +63,26 @@ export function createDropdownList(config) {
32
63
  const pos = enabledIdx.indexOf(active);
33
64
  active = enabledIdx[(pos + d + enabledIdx.length) % enabledIdx.length] ?? active;
34
65
  }
66
+ // Type-ahead: typing a label prefix jumps the active option (and selects it
67
+ // when the list is closed, matching a native <select>).
68
+ const typeahead = createTypeaheadBuffer();
69
+ function onTypeahead(char) {
70
+ const buffer = typeahead.push(char.toLowerCase());
71
+ const from = active >= 0 ? active : opts().findIndex((o) => o.value === config.value());
72
+ const next = nextTypeaheadIndex(opts(), buffer, from);
73
+ if (next < 0)
74
+ return;
75
+ active = next;
76
+ if (!open)
77
+ config.onChange?.(opts()[next].value);
78
+ }
35
79
  function onKeydown(e) {
36
80
  if (disabled())
37
81
  return;
82
+ if (isTypeaheadKey(e)) {
83
+ onTypeahead(e.key);
84
+ return;
85
+ }
38
86
  if (!open && (e.key === 'ArrowDown' || e.key === 'Enter' || e.key === ' ')) {
39
87
  e.preventDefault();
40
88
  openPanel();
@@ -86,10 +134,11 @@ export function createDropdownList(config) {
86
134
  onKeydown,
87
135
  /** Spread onto the trigger <button>. */
88
136
  triggerProps: () => ({
137
+ id: config.id?.(),
89
138
  type: 'button',
90
139
  'aria-haspopup': 'listbox',
91
140
  'aria-expanded': open,
92
- 'aria-label': config.ariaLabel?.(),
141
+ ...editorAria(ariaState()),
93
142
  disabled: disabled(),
94
143
  onclick: toggle,
95
144
  onkeydown: onKeydown,
@@ -0,0 +1,68 @@
1
+ export type FileRejectReason = 'type' | 'size' | 'count';
2
+ export type FileRejection = {
3
+ file: File;
4
+ reason: FileRejectReason;
5
+ };
6
+ /** Reactive inputs are passed as getters so the core tracks live prop changes. */
7
+ export type FileUploadConfig = {
8
+ files: () => File[];
9
+ onChange?: (files: File[]) => void;
10
+ /** Rejected files (wrong type / too big / over the count cap). */
11
+ onReject?: (rejections: FileRejection[]) => void;
12
+ /** Comma list like "image/*,.pdf" (matches MIME or extension). */
13
+ accept?: () => string | undefined;
14
+ multiple?: () => boolean;
15
+ /** Max size per file in bytes. */
16
+ maxSize?: () => number | undefined;
17
+ /** Max number of files (multiple mode). */
18
+ maxFiles?: () => number | undefined;
19
+ disabled?: () => boolean;
20
+ readonly?: () => boolean;
21
+ id?: () => string | undefined;
22
+ invalid?: () => boolean;
23
+ required?: () => boolean;
24
+ error?: () => string | undefined;
25
+ hint?: () => string | undefined;
26
+ ariaLabel?: () => string | undefined;
27
+ };
28
+ /** Whether `file` satisfies an `accept` list (MIME globs or extensions). Pure. */
29
+ export declare function fileMatchesAccept(file: File, accept?: string): boolean;
30
+ export declare function createFileUpload(config: FileUploadConfig): {
31
+ /** Whether files are being dragged over the dropzone. */
32
+ readonly dragging: boolean;
33
+ /** Whether interaction is allowed. */
34
+ readonly isInteractive: boolean;
35
+ /** Selected files (controlled value). */
36
+ readonly files: File[];
37
+ addFiles: (incoming: File[]) => void;
38
+ removeAt: (index: number) => void;
39
+ clear: () => void;
40
+ setDragging: (v: boolean) => void;
41
+ /** Spread onto the dropzone element (click opens the picker; Enter/Space too).
42
+ * Wire drag events yourself (they carry a DataTransfer): call
43
+ * `setDragging(true/false)` and `addFiles([...e.dataTransfer.files])`. */
44
+ dropzoneProps: () => {
45
+ 'aria-invalid': "true" | undefined;
46
+ 'aria-required': "true" | undefined;
47
+ 'aria-describedby': string | undefined;
48
+ 'aria-label': string | undefined;
49
+ role: "button";
50
+ tabindex: number;
51
+ 'data-dragging': string | undefined;
52
+ 'aria-disabled': true | undefined;
53
+ };
54
+ /** Spread onto the visually-hidden <input type="file">. */
55
+ inputProps: () => {
56
+ type: "file";
57
+ accept: string | undefined;
58
+ multiple: boolean;
59
+ disabled: boolean;
60
+ tabindex: number;
61
+ };
62
+ /** Spread onto a selected file's remove <button> at `index`. */
63
+ removeProps: (index: number) => {
64
+ type: "button";
65
+ onclick: () => void;
66
+ };
67
+ };
68
+ export type FileUpload = ReturnType<typeof createFileUpload>;
@@ -0,0 +1,122 @@
1
+ /**
2
+ * createFileUpload - the HEADLESS core behind <SvFileUpload>: a drag-and-drop
3
+ * file field with accept / size / count validation, exposed as **prop-getters**
4
+ * you spread onto YOUR OWN markup. The DataTransfer / File reading is a DOM
5
+ * concern that stays in the renderer; the core owns the list, validation, drag
6
+ * state and ARIA. Parity: Smart `smart-file-upload`.
7
+ *
8
+ * ```svelte
9
+ * <script lang="ts">
10
+ * import { createFileUpload } from '@svgrid/grid'
11
+ * let files = $state<File[]>([])
12
+ * const fu = createFileUpload({ files: () => files, onChange: (f) => (files = f) })
13
+ * </script>
14
+ * <div {...fu.dropzoneProps()} ondrop={(e) => fu.addFiles([...e.dataTransfer.files])}>...</div>
15
+ * <input {...fu.inputProps()} />
16
+ * ```
17
+ */
18
+ import { editorAria } from './editor-contract';
19
+ /** Whether `file` satisfies an `accept` list (MIME globs or extensions). Pure. */
20
+ export function fileMatchesAccept(file, accept) {
21
+ if (!accept || !accept.trim())
22
+ return true;
23
+ const name = file.name.toLowerCase();
24
+ const type = file.type.toLowerCase();
25
+ return accept.split(',').map((s) => s.trim().toLowerCase()).filter(Boolean).some((tok) => {
26
+ if (tok.startsWith('.'))
27
+ return name.endsWith(tok);
28
+ if (tok.endsWith('/*'))
29
+ return type.startsWith(tok.slice(0, -1));
30
+ return type === tok;
31
+ });
32
+ }
33
+ export function createFileUpload(config) {
34
+ const accept = () => config.accept?.();
35
+ const multiple = () => config.multiple?.() ?? false;
36
+ const maxSize = () => config.maxSize?.();
37
+ const maxFiles = () => config.maxFiles?.();
38
+ const disabled = () => config.disabled?.() ?? false;
39
+ const readonly = () => config.readonly?.() ?? false;
40
+ const isInteractive = $derived(!disabled() && !readonly());
41
+ let dragging = $state(false);
42
+ function addFiles(incoming) {
43
+ if (!isInteractive || !incoming.length)
44
+ return;
45
+ const accepted = [];
46
+ const rejected = [];
47
+ for (const f of incoming) {
48
+ if (!fileMatchesAccept(f, accept())) {
49
+ rejected.push({ file: f, reason: 'type' });
50
+ continue;
51
+ }
52
+ const cap = maxSize();
53
+ if (cap != null && f.size > cap) {
54
+ rejected.push({ file: f, reason: 'size' });
55
+ continue;
56
+ }
57
+ accepted.push(f);
58
+ }
59
+ let next = multiple() ? [...config.files(), ...accepted] : accepted.slice(-1);
60
+ const limit = maxFiles();
61
+ if (multiple() && limit != null && next.length > limit) {
62
+ for (const f of next.slice(limit))
63
+ rejected.push({ file: f, reason: 'count' });
64
+ next = next.slice(0, limit);
65
+ }
66
+ if (rejected.length)
67
+ config.onReject?.(rejected);
68
+ config.onChange?.(next);
69
+ }
70
+ function removeAt(index) {
71
+ if (!isInteractive)
72
+ return;
73
+ config.onChange?.(config.files().filter((_, i) => i !== index));
74
+ }
75
+ function clear() { if (isInteractive)
76
+ config.onChange?.([]); }
77
+ const setDragging = (v) => { if (isInteractive)
78
+ dragging = v; };
79
+ const ariaState = () => ({
80
+ id: config.id?.(),
81
+ invalid: config.invalid?.(),
82
+ required: config.required?.(),
83
+ error: config.error?.(),
84
+ hint: config.hint?.(),
85
+ ariaLabel: config.ariaLabel?.(),
86
+ });
87
+ return {
88
+ /** Whether files are being dragged over the dropzone. */
89
+ get dragging() { return dragging; },
90
+ /** Whether interaction is allowed. */
91
+ get isInteractive() { return isInteractive; },
92
+ /** Selected files (controlled value). */
93
+ get files() { return config.files(); },
94
+ addFiles,
95
+ removeAt,
96
+ clear,
97
+ setDragging,
98
+ /** Spread onto the dropzone element (click opens the picker; Enter/Space too).
99
+ * Wire drag events yourself (they carry a DataTransfer): call
100
+ * `setDragging(true/false)` and `addFiles([...e.dataTransfer.files])`. */
101
+ dropzoneProps: () => ({
102
+ role: 'button',
103
+ tabindex: isInteractive ? 0 : -1,
104
+ 'data-dragging': dragging ? '' : undefined,
105
+ 'aria-disabled': !isInteractive || undefined,
106
+ ...editorAria(ariaState()),
107
+ }),
108
+ /** Spread onto the visually-hidden <input type="file">. */
109
+ inputProps: () => ({
110
+ type: 'file',
111
+ accept: accept(),
112
+ multiple: multiple(),
113
+ disabled: !isInteractive,
114
+ tabindex: -1,
115
+ }),
116
+ /** Spread onto a selected file's remove <button> at `index`. */
117
+ removeProps: (index) => ({
118
+ type: 'button',
119
+ onclick: () => removeAt(index),
120
+ }),
121
+ };
122
+ }