@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
@@ -0,0 +1,108 @@
1
+ <script lang="ts">
2
+ /**
3
+ * SvDurationInput - a duration editor whose value is a number of MINUTES but
4
+ * which accepts the human forms people type ("1h 30m", "1:30", "90"). It shows
5
+ * a formatted value when unfocused and re-parses on blur / Enter. As a grid
6
+ * cell editor: Enter commits, Escape cancels.
7
+ */
8
+ import SvField from './SvField.svelte'
9
+ import { editorAria, nextEditorId, type SvEditorProps } from './editor-contract'
10
+ import { parseDuration, formatDuration } from './duration'
11
+
12
+ type Props = SvEditorProps & {
13
+ /** Value in minutes (or null when empty). */
14
+ value?: number | null
15
+ onChange?: (minutes: number | null) => void
16
+ onCommit?: (minutes: number | null) => void
17
+ onCancel?: () => void
18
+ /** Display style when unfocused: `1:30` (colon) or `1h 30m` (units). */
19
+ style?: 'colon' | 'units'
20
+ placeholder?: string
21
+ autofocus?: boolean
22
+ }
23
+
24
+ let {
25
+ value = $bindable<number | null>(null),
26
+ onChange,
27
+ onCommit,
28
+ onCancel,
29
+ style = 'colon',
30
+ placeholder = 'e.g. 1h 30m',
31
+ disabled = false,
32
+ readonly = false,
33
+ name,
34
+ size = 'md',
35
+ ariaLabel,
36
+ invalid = false,
37
+ required = false,
38
+ error,
39
+ label,
40
+ hint,
41
+ dir,
42
+ id,
43
+ autofocus = false,
44
+ }: Props = $props()
45
+
46
+ const autoId = nextEditorId('sv-dur')
47
+ const uid = $derived(id ?? autoId)
48
+
49
+ let text = $state('')
50
+ let focused = $state(false)
51
+ // Reflect the external value into the field whenever the user isn't typing.
52
+ $effect(() => {
53
+ if (!focused) text = value == null ? '' : formatDuration(value, style)
54
+ })
55
+
56
+ function commit(): number | null {
57
+ const mins = parseDuration(text)
58
+ value = mins // enables bind:value
59
+ onChange?.(mins)
60
+ if (mins != null) text = formatDuration(mins, style)
61
+ return mins
62
+ }
63
+ function mountFocus(node: HTMLInputElement) {
64
+ if (autofocus) { node.focus(); node.select() }
65
+ }
66
+ </script>
67
+
68
+ <SvField id={uid} {label} {hint} {error} {required} {dir}>
69
+ <div class="sv-dur sv-dur--{size}" class:is-disabled={disabled}>
70
+ <div class="sv-dur__field" class:is-invalid={invalid}>
71
+ <input
72
+ use:mountFocus
73
+ class="sv-dur__input"
74
+ type="text"
75
+ inputmode="numeric"
76
+ value={text}
77
+ {placeholder}
78
+ {disabled}
79
+ {readonly}
80
+ oninput={(e) => (text = e.currentTarget.value)}
81
+ onfocus={() => (focused = true)}
82
+ onblur={() => { focused = false; commit() }}
83
+ onkeydown={(e) => {
84
+ if (e.key === 'Enter') { onCommit?.(commit()) }
85
+ else if (e.key === 'Escape') onCancel?.()
86
+ }}
87
+ {...editorAria({ id: uid, invalid, required, error, hint, ariaLabel })}
88
+ />
89
+ </div>
90
+ {#if name}<input type="hidden" {name} value={value ?? ''} />{/if}
91
+ </div>
92
+ </SvField>
93
+
94
+ <style>
95
+ .sv-dur { --_accent: var(--sg-accent, #2563eb); display: inline-flex; flex-direction: column; width: 160px; }
96
+ .sv-dur.is-disabled { opacity: 0.6; }
97
+ .sv-dur__field {
98
+ display: flex; align-items: center;
99
+ background: var(--sg-input-bg, #fff); color: var(--sg-fg, #0f172a);
100
+ border: 1px solid var(--sg-input-border, var(--sg-border, #cbd5e1)); border-radius: var(--sg-radius, 8px);
101
+ }
102
+ .sv-dur__field:focus-within { border-color: var(--_accent); box-shadow: 0 0 0 2px color-mix(in srgb, var(--_accent) 22%, transparent); }
103
+ .sv-dur__field.is-invalid { border-color: var(--sg-danger, #dc2626); }
104
+ .sv-dur__input { flex: 1; min-width: 0; border: 0; background: none; outline: none; color: inherit; font: inherit; padding: 0 10px; }
105
+ .sv-dur--sm .sv-dur__field { height: 28px; font-size: 12px; }
106
+ .sv-dur--md .sv-dur__field { height: 34px; font-size: 13px; }
107
+ .sv-dur--lg .sv-dur__field { height: 40px; font-size: 15px; }
108
+ </style>
@@ -0,0 +1,59 @@
1
+ <script lang="ts">
2
+ /**
3
+ * SvEmptyState - a centered placeholder for empty lists, no-results, and blank
4
+ * screens: an icon/illustration, a title, a description, and an action slot.
5
+ * Theme-driven. Parity: Smart empty / no-data states.
6
+ *
7
+ * ```svelte
8
+ * <SvEmptyState title="No orders yet" description="Create your first order to get going.">
9
+ * <SvButton>New order</SvButton>
10
+ * </SvEmptyState>
11
+ * ```
12
+ */
13
+ import type { Snippet } from 'svelte'
14
+
15
+ type Props = {
16
+ title?: string
17
+ description?: string
18
+ /** Tighter padding (for empty dropdowns / small panels). */
19
+ compact?: boolean
20
+ /** Custom icon / illustration (overrides the default). */
21
+ icon?: Snippet
22
+ /** Action buttons / links. */
23
+ children?: Snippet
24
+ }
25
+
26
+ let { title, description, compact = false, icon, children }: Props = $props()
27
+ </script>
28
+
29
+ <div class="sv-empty" class:is-compact={compact}>
30
+ <div class="sv-empty__icon" aria-hidden="true">
31
+ {#if icon}
32
+ {@render icon()}
33
+ {:else}
34
+ <svg viewBox="0 0 48 48" width="44" height="44" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
35
+ <rect x="8" y="12" width="32" height="26" rx="3" /><path d="M8 20h32" /><path d="M16 28h10" />
36
+ </svg>
37
+ {/if}
38
+ </div>
39
+ {#if title}<div class="sv-empty__title">{title}</div>{/if}
40
+ {#if description}<p class="sv-empty__desc">{description}</p>{/if}
41
+ {#if children}<div class="sv-empty__actions">{@render children()}</div>{/if}
42
+ </div>
43
+
44
+ <style>
45
+ .sv-empty {
46
+ display: flex; flex-direction: column; align-items: center; text-align: center;
47
+ gap: 6px; padding: 44px 24px; color: var(--sg-fg, #0f172a);
48
+ }
49
+ .sv-empty.is-compact { padding: 22px 16px; }
50
+ .sv-empty__icon {
51
+ color: var(--sg-muted, #94a3b8); margin-bottom: 4px;
52
+ display: grid; place-items: center; width: 66px; height: 66px; border-radius: 16px;
53
+ background: var(--sg-row-hover-bg, #f1f5f9);
54
+ }
55
+ .sv-empty.is-compact .sv-empty__icon { width: 48px; height: 48px; }
56
+ .sv-empty__title { font-size: 15px; font-weight: 650; }
57
+ .sv-empty__desc { margin: 0; max-width: 42ch; color: var(--sg-muted, #64748b); font-size: 13px; line-height: 1.55; }
58
+ .sv-empty__actions { display: flex; gap: 8px; margin-top: 8px; }
59
+ </style>
@@ -0,0 +1,85 @@
1
+ <script lang="ts">
2
+ /**
3
+ * SvField - the shared field chrome for every value-bearing editor in the kit.
4
+ * It renders an optional `label` (wired to the control via `for`/`id`), the
5
+ * control itself (passed as children), and an optional `hint` / `error` line
6
+ * whose ids match `editorHintId` / `editorErrorId` for `aria-describedby`.
7
+ *
8
+ * Editors wrap their control box in it so label/hint/error/RTL behave
9
+ * IDENTICALLY across the kit:
10
+ *
11
+ * ```svelte
12
+ * <SvField {id} {label} {hint} {error} {required} {dir}>
13
+ * <div class="sv-num">...control...</div>
14
+ * </SvField>
15
+ * ```
16
+ */
17
+ import type { Snippet } from 'svelte'
18
+ import { editorErrorId, editorHintId, type EditorDir } from './editor-contract'
19
+
20
+ let {
21
+ id,
22
+ label,
23
+ hint,
24
+ error,
25
+ required = false,
26
+ dir,
27
+ /** Stretch the field (and its control) to the container width. */
28
+ block = false,
29
+ children,
30
+ }: {
31
+ id?: string
32
+ label?: string
33
+ hint?: string
34
+ error?: string
35
+ required?: boolean
36
+ dir?: EditorDir
37
+ block?: boolean
38
+ children: Snippet
39
+ } = $props()
40
+
41
+ // 'auto'/undefined -> inherit from the document (no dir attribute).
42
+ const resolvedDir = $derived(dir === 'ltr' || dir === 'rtl' ? dir : undefined)
43
+ </script>
44
+
45
+ <div class="sv-field" class:sv-field--block={block} dir={resolvedDir}>
46
+ {#if label}
47
+ <label class="sv-field__label" for={id}>
48
+ {label}{#if required}<span class="sv-field__req" aria-hidden="true">*</span>{/if}
49
+ </label>
50
+ {/if}
51
+ {@render children()}
52
+ {#if error}
53
+ <span class="sv-field__error" id={editorErrorId(id)} role="alert">{error}</span>
54
+ {:else if hint}
55
+ <span class="sv-field__hint" id={editorHintId(id)}>{hint}</span>
56
+ {/if}
57
+ </div>
58
+
59
+ <style>
60
+ .sv-field {
61
+ display: inline-flex;
62
+ flex-direction: column;
63
+ gap: 3px;
64
+ text-align: start;
65
+ }
66
+ .sv-field--block { display: flex; width: 100%; }
67
+ .sv-field__label {
68
+ font-size: 12.5px;
69
+ font-weight: 550;
70
+ color: var(--sg-fg, #0f172a);
71
+ line-height: 1.3;
72
+ }
73
+ .sv-field__req { color: var(--sg-danger, #dc2626); margin-inline-start: 2px; }
74
+ .sv-field__hint {
75
+ font-size: 11.5px;
76
+ color: var(--sg-muted, #64748b);
77
+ line-height: 1.35;
78
+ }
79
+ .sv-field__error {
80
+ font-size: 11.5px;
81
+ font-weight: 500;
82
+ color: var(--sg-danger, #dc2626);
83
+ line-height: 1.35;
84
+ }
85
+ </style>
@@ -0,0 +1,169 @@
1
+ <script lang="ts" module>
2
+ export type { FileRejection, FileRejectReason } from './createFileUpload.svelte'
3
+ </script>
4
+
5
+ <script lang="ts">
6
+ /**
7
+ * SvFileUpload - a drag-and-drop file field with a click-to-browse fallback,
8
+ * accept / size / count validation and a selected-files list. Parity: Smart
9
+ * `smart-file-upload`. Controlled via `files` + `onChange`.
10
+ *
11
+ * Part of the editor kit: carries the shared contract (label, hint, error
12
+ * validation, `dir`/RTL and localizable `messages`) via `<SvField>`. The
13
+ * drag/validation/state live in the headless `createFileUpload` core.
14
+ */
15
+ import SvField from './SvField.svelte'
16
+ import { nextEditorId, resolveMessages, type SvEditorProps } from './editor-contract'
17
+ import { createFileUpload, type FileRejection } from './createFileUpload.svelte'
18
+
19
+ /** User-facing strings (localizable via `messages`). */
20
+ type FileMessages = { prompt: string; browse: string; remove: string; hintTypes: string }
21
+ const DEFAULT_MESSAGES: FileMessages = {
22
+ prompt: 'Drag files here or', browse: 'browse', remove: 'Remove', hintTypes: 'Accepted:',
23
+ }
24
+
25
+ type Props = SvEditorProps & {
26
+ files?: File[]
27
+ onChange?: (files: File[]) => void
28
+ onReject?: (rejections: FileRejection[]) => void
29
+ accept?: string
30
+ multiple?: boolean
31
+ /** Max size per file in bytes. */
32
+ maxSize?: number
33
+ maxFiles?: number
34
+ messages?: Partial<FileMessages>
35
+ }
36
+
37
+ let {
38
+ files = $bindable([]),
39
+ onChange,
40
+ onReject,
41
+ accept,
42
+ multiple = false,
43
+ maxSize,
44
+ maxFiles,
45
+ disabled = false,
46
+ readonly = false,
47
+ ariaLabel,
48
+ invalid = false,
49
+ required = false,
50
+ error,
51
+ label,
52
+ hint,
53
+ dir,
54
+ id,
55
+ messages,
56
+ }: Props = $props()
57
+
58
+ const autoId = nextEditorId('sv-file')
59
+ const uid = $derived(id ?? autoId)
60
+ const M = $derived(resolveMessages(DEFAULT_MESSAGES, messages))
61
+
62
+ const fu = createFileUpload({
63
+ files: () => files,
64
+ onChange: (f) => { files = f; onChange?.(f) },
65
+ onReject: (r) => onReject?.(r),
66
+ accept: () => accept,
67
+ multiple: () => multiple,
68
+ maxSize: () => maxSize,
69
+ maxFiles: () => maxFiles,
70
+ disabled: () => disabled,
71
+ readonly: () => readonly,
72
+ id: () => uid,
73
+ invalid: () => invalid,
74
+ required: () => required,
75
+ error: () => error,
76
+ hint: () => hint,
77
+ ariaLabel: () => ariaLabel,
78
+ })
79
+
80
+ let inputEl = $state<HTMLInputElement | null>(null)
81
+
82
+ function openPicker() { if (fu.isInteractive) inputEl?.click() }
83
+ function onDropzoneKey(e: KeyboardEvent) {
84
+ if (e.key === 'Enter' || e.key === ' ') { e.preventDefault(); openPicker() }
85
+ }
86
+ function onInputChange(e: Event) {
87
+ const list = (e.currentTarget as HTMLInputElement).files
88
+ if (list) fu.addFiles([...list])
89
+ ;(e.currentTarget as HTMLInputElement).value = '' // allow re-selecting the same file
90
+ }
91
+ function onDrop(e: DragEvent) {
92
+ e.preventDefault()
93
+ fu.setDragging(false)
94
+ if (e.dataTransfer?.files) fu.addFiles([...e.dataTransfer.files])
95
+ }
96
+
97
+ const fmtSize = (n: number) => (n < 1024 ? `${n} B` : n < 1048576 ? `${(n / 1024).toFixed(0)} KB` : `${(n / 1048576).toFixed(1)} MB`)
98
+ </script>
99
+
100
+ <SvField id={uid} {label} {hint} {error} {required} {dir}>
101
+ <div class="sv-file" class:is-disabled={disabled}>
102
+ <!-- svelte-ignore a11y_no_static_element_interactions -->
103
+ <div
104
+ class="sv-file__zone"
105
+ class:is-dragging={fu.dragging}
106
+ class:is-invalid={invalid}
107
+ {...fu.dropzoneProps()}
108
+ onclick={openPicker}
109
+ onkeydown={onDropzoneKey}
110
+ ondragenter={(e) => { e.preventDefault(); fu.setDragging(true) }}
111
+ ondragover={(e) => { e.preventDefault(); fu.setDragging(true) }}
112
+ ondragleave={() => fu.setDragging(false)}
113
+ ondrop={onDrop}
114
+ >
115
+ <svg class="sv-file__icon" viewBox="0 0 24 24" width="26" height="26" fill="none" stroke="currentColor" stroke-width="1.7" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4M17 8l-5-5-5 5M12 3v12" /></svg>
116
+ <p class="sv-file__prompt">{M.prompt} <span class="sv-file__browse">{M.browse}</span></p>
117
+ {#if accept}<p class="sv-file__types">{M.hintTypes} {accept}</p>{/if}
118
+ <input bind:this={inputEl} class="sv-file__input" {...fu.inputProps()} onchange={onInputChange} />
119
+ </div>
120
+
121
+ {#if files.length}
122
+ <ul class="sv-file__list">
123
+ {#each files as file, i (file.name + file.size + i)}
124
+ <li class="sv-file__item">
125
+ <span class="sv-file__name" title={file.name}>{file.name}</span>
126
+ <span class="sv-file__size">{fmtSize(file.size)}</span>
127
+ {#if fu.isInteractive}
128
+ <button class="sv-file__remove" aria-label={`${M.remove} ${file.name}`} {...fu.removeProps(i)}>&times;</button>
129
+ {/if}
130
+ </li>
131
+ {/each}
132
+ </ul>
133
+ {/if}
134
+ </div>
135
+ </SvField>
136
+
137
+ <style>
138
+ .sv-file {
139
+ --_accent: var(--sg-accent, #2563eb);
140
+ --_border: var(--sg-input-border, var(--sg-border, #cbd5e1));
141
+ --_radius: var(--sg-radius, 8px);
142
+ display: flex; flex-direction: column; gap: 8px; width: 320px; color: var(--sg-fg, #0f172a);
143
+ }
144
+ .sv-file.is-disabled { opacity: 0.6; }
145
+ .sv-file__zone {
146
+ display: flex; flex-direction: column; align-items: center; gap: 4px;
147
+ padding: 22px 16px; text-align: center; cursor: pointer;
148
+ background: var(--sg-input-bg, #fff); color: var(--sg-muted, #64748b);
149
+ border: 1.5px dashed var(--_border); border-radius: var(--_radius); transition: border-color 0.15s, background 0.15s;
150
+ }
151
+ .sv-file__zone:hover, .sv-file__zone:focus-visible { border-color: var(--_accent); outline: none; }
152
+ .sv-file__zone.is-dragging { border-color: var(--_accent); background: color-mix(in srgb, var(--_accent) 8%, transparent); color: var(--_accent); }
153
+ .sv-file__zone.is-invalid { border-color: var(--sg-danger, #dc2626); }
154
+ .sv-file__icon { color: var(--_accent); }
155
+ .sv-file__prompt { margin: 0; font-size: 13px; }
156
+ .sv-file__browse { color: var(--_accent); font-weight: 650; text-decoration: underline; }
157
+ .sv-file__types { margin: 0; font-size: 11.5px; opacity: 0.8; }
158
+ .sv-file__input { position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px; overflow: hidden; clip: rect(0 0 0 0); border: 0; }
159
+
160
+ .sv-file__list { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: 4px; }
161
+ .sv-file__item {
162
+ display: flex; align-items: center; gap: 8px; padding: 6px 8px; font-size: 12.5px;
163
+ background: var(--sg-header-bg, #f8fafc); border: 1px solid var(--sg-border, #e2e8f0); border-radius: 6px;
164
+ }
165
+ .sv-file__name { flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; font-weight: 550; }
166
+ .sv-file__size { color: var(--sg-muted, #64748b); flex: none; font-variant-numeric: tabular-nums; }
167
+ .sv-file__remove { flex: none; background: none; border: 0; color: var(--sg-muted, #64748b); cursor: pointer; font-size: 16px; line-height: 1; padding: 0 2px; }
168
+ .sv-file__remove:hover { color: var(--sg-danger, #dc2626); }
169
+ </style>
@@ -0,0 +1,59 @@
1
+ /**
2
+ * Tests for the file-upload core's pure accept matching, plus SvFileUpload
3
+ * rendering: the dropzone carries the editor contract, the selected-files list
4
+ * renders, and remove emits the trimmed list.
5
+ */
6
+ import { describe, expect, it } from 'vitest'
7
+ import { mount, unmount, flushSync } from 'svelte'
8
+ import SvFileUpload from './SvFileUpload.svelte'
9
+ import { fileMatchesAccept } from './createFileUpload.svelte'
10
+
11
+ const mk = (name: string, type = '') => new File(['x'], name, { type })
12
+
13
+ describe('fileMatchesAccept', () => {
14
+ it('matches extensions, MIME globs and exact MIME (and passes when empty)', () => {
15
+ expect(fileMatchesAccept(mk('a.pdf'), '.pdf')).toBe(true)
16
+ expect(fileMatchesAccept(mk('a.png', 'image/png'), 'image/*')).toBe(true)
17
+ expect(fileMatchesAccept(mk('a.png', 'image/png'), 'image/png')).toBe(true)
18
+ expect(fileMatchesAccept(mk('a.txt', 'text/plain'), 'image/*,.pdf')).toBe(false)
19
+ expect(fileMatchesAccept(mk('anything'), undefined)).toBe(true)
20
+ })
21
+ })
22
+
23
+ function mountFu(props: Record<string, unknown>) {
24
+ const target = document.createElement('div')
25
+ document.body.appendChild(target)
26
+ const app = mount(SvFileUpload, { target, props: props as any })
27
+ flushSync()
28
+ return { target, destroy: () => { unmount(app); target.remove() } }
29
+ }
30
+
31
+ describe('SvFileUpload', () => {
32
+ it('renders a dropzone with the editor contract wired', () => {
33
+ const { target, destroy } = mountFu({ files: [], accept: 'image/*', label: 'Avatar', required: true, invalid: true, error: 'Required' })
34
+ try {
35
+ const zone = target.querySelector<HTMLElement>('.sv-file__zone')!
36
+ expect(zone.getAttribute('role')).toBe('button')
37
+ expect(zone.getAttribute('aria-invalid')).toBe('true')
38
+ expect(zone.getAttribute('aria-required')).toBe('true')
39
+ expect(target.querySelector('.sv-field__label')!.textContent).toContain('Avatar')
40
+ expect(target.querySelector('.sv-field__error')!.textContent).toBe('Required')
41
+ const input = target.querySelector<HTMLInputElement>('.sv-file__input')!
42
+ expect(input.accept).toBe('image/*')
43
+ } finally { destroy() }
44
+ })
45
+
46
+ it('lists selected files and removes one on click', () => {
47
+ let got: File[] | undefined
48
+ const files = [mk('a.png', 'image/png'), mk('b.png', 'image/png')]
49
+ const { target, destroy } = mountFu({ files, multiple: true, onChange: (f: File[]) => (got = f) })
50
+ try {
51
+ const items = target.querySelectorAll('.sv-file__item')
52
+ expect(items).toHaveLength(2)
53
+ target.querySelector<HTMLButtonElement>('.sv-file__remove')!.click()
54
+ flushSync()
55
+ expect(got).toHaveLength(1)
56
+ expect(got![0].name).toBe('b.png')
57
+ } finally { destroy() }
58
+ })
59
+ })
package/src/SvForm.svelte CHANGED
@@ -1,4 +1,6 @@
1
1
  <script lang="ts" module>
2
+ import type { Validator } from './validators'
3
+
2
4
  export type FormFieldType =
3
5
  | 'text' | 'email' | 'tel' | 'textarea' | 'number' | 'password'
4
6
  | 'select' | 'checkbox' | 'switch' | 'date' | 'color' | 'rating'
@@ -10,6 +12,8 @@
10
12
  required?: boolean
11
13
  placeholder?: string
12
14
  options?: Array<{ value: string | number; label: string }>
15
+ /** Declarative validation rules (see `rules` - email/pattern/min/compare...). */
16
+ rules?: ReadonlyArray<Validator>
13
17
  /** Return an error message, or null/undefined when valid. */
14
18
  validate?: (value: any, values: Record<string, any>) => string | null | undefined
15
19
  /** Span full width in the grid. */
@@ -32,6 +36,7 @@
32
36
  import SvColorInput from './SvColorInput.svelte'
33
37
  import SvRating from './SvRating.svelte'
34
38
  import SvButton from './SvButton.svelte'
39
+ import { runRules } from './validators'
35
40
 
36
41
  type Props = {
37
42
  fields: ReadonlyArray<FormField>
@@ -64,7 +69,8 @@
64
69
  const v = values[name]
65
70
  let err: string | null | undefined = null
66
71
  if (field.required && (v == null || v === '' || (Array.isArray(v) && !v.length))) err = `${field.label} is required`
67
- else if (field.validate) err = field.validate(v, values)
72
+ if (!err && field.rules) err = runRules(v, field.rules, values)
73
+ if (!err && field.validate) err = field.validate(v, values)
68
74
  const next = { ...errors }
69
75
  if (err) next[name] = err
70
76
  else delete next[name]
@@ -7,6 +7,7 @@
7
7
  * The arc/needle geometry + `role="meter"` ARIA live in the headless
8
8
  * `createGauge` core; this component is just one styled renderer over it.
9
9
  */
10
+ import { type EditorDir } from './editor-contract'
10
11
  import { createGauge } from './createGauge.svelte'
11
12
 
12
13
  type Band = { from: number; to: number; color: string }
@@ -28,6 +29,8 @@
28
29
  thickness?: number
29
30
  formatValue?: (v: number) => string
30
31
  ariaLabel?: string
32
+ /** Text direction for the center label / unit. */
33
+ dir?: EditorDir
31
34
  }
32
35
 
33
36
  let {
@@ -43,8 +46,11 @@
43
46
  thickness = 14,
44
47
  formatValue,
45
48
  ariaLabel,
49
+ dir,
46
50
  }: Props = $props()
47
51
 
52
+ const resolvedDir = $derived(dir === 'ltr' || dir === 'rtl' ? dir : undefined)
53
+
48
54
  const gauge = createGauge({
49
55
  value: () => value,
50
56
  min: () => min,
@@ -63,7 +69,7 @@
63
69
  const display = $derived(label ?? `${formatValue ? formatValue(clamped) : Math.round(clamped)}${unit}`)
64
70
  </script>
65
71
 
66
- <div class="sv-gauge" {...gauge.rootProps()}>
72
+ <div class="sv-gauge" dir={resolvedDir} {...gauge.rootProps()}>
67
73
  <svg viewBox={`0 0 ${size} ${size}`} width={size} height={size} class="sv-gauge__svg">
68
74
  <!-- track -->
69
75
  <path d={arcPath(min, max, r)} class="sv-gauge__track" fill="none" stroke-width={thickness} stroke-linecap="round" />