@svgrid/grid 1.2.34 → 2.0.1

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 (444) hide show
  1. package/README.md +6 -5
  2. package/dist/SvAccordion.svelte +109 -0
  3. package/dist/SvAccordion.svelte.d.ts +28 -0
  4. package/dist/SvAlert.svelte +92 -0
  5. package/dist/SvAlert.svelte.d.ts +27 -0
  6. package/dist/SvAutoComplete.svelte +47 -18
  7. package/dist/SvAutoComplete.svelte.d.ts +2 -5
  8. package/dist/SvAvatar.svelte +75 -0
  9. package/dist/SvAvatar.svelte.d.ts +14 -0
  10. package/dist/SvAvatarGroup.svelte +48 -0
  11. package/dist/SvAvatarGroup.svelte.d.ts +16 -0
  12. package/dist/SvBadge.svelte +54 -0
  13. package/dist/SvBadge.svelte.d.ts +23 -0
  14. package/dist/SvBreadcrumb.svelte +88 -0
  15. package/dist/SvBreadcrumb.svelte.d.ts +23 -0
  16. package/dist/SvButtonGroup.svelte +121 -0
  17. package/dist/SvButtonGroup.svelte.d.ts +16 -0
  18. package/dist/SvCalendar.svelte +72 -9
  19. package/dist/SvCalendar.svelte.d.ts +34 -1
  20. package/dist/SvCard.svelte +71 -0
  21. package/dist/SvCard.svelte.d.ts +28 -0
  22. package/dist/SvCarousel.svelte +134 -0
  23. package/dist/SvCarousel.svelte.d.ts +28 -0
  24. package/dist/SvCheckBox.svelte +36 -21
  25. package/dist/SvCheckBox.svelte.d.ts +2 -5
  26. package/dist/SvChip.svelte +88 -0
  27. package/dist/SvChip.svelte.d.ts +31 -0
  28. package/dist/SvCircularProgress.svelte +109 -0
  29. package/dist/SvCircularProgress.svelte.d.ts +31 -0
  30. package/dist/SvColorInput.svelte +54 -27
  31. package/dist/SvColorInput.svelte.d.ts +10 -6
  32. package/dist/SvComboBox.svelte +119 -33
  33. package/dist/SvComboBox.svelte.d.ts +18 -6
  34. package/dist/SvCommand.svelte +197 -0
  35. package/dist/SvCommand.svelte.d.ts +26 -0
  36. package/dist/SvContextMenu.svelte +96 -0
  37. package/dist/SvContextMenu.svelte.d.ts +25 -0
  38. package/dist/SvCountryInput.svelte +56 -21
  39. package/dist/SvCountryInput.svelte.d.ts +9 -5
  40. package/dist/SvDateRangeInput.svelte +256 -0
  41. package/dist/SvDateRangeInput.svelte.d.ts +37 -0
  42. package/dist/SvDateTimePicker.svelte +57 -20
  43. package/dist/SvDateTimePicker.svelte.d.ts +12 -4
  44. package/dist/SvDivider.svelte +61 -0
  45. package/dist/SvDivider.svelte.d.ts +22 -0
  46. package/dist/SvDrawer.svelte +226 -0
  47. package/dist/SvDrawer.svelte.d.ts +43 -0
  48. package/dist/SvDropDownList.svelte +114 -35
  49. package/dist/SvDropDownList.svelte.d.ts +7 -6
  50. package/dist/SvDurationInput.svelte +108 -0
  51. package/dist/SvDurationInput.svelte.d.ts +15 -0
  52. package/dist/SvEmptyState.svelte +59 -0
  53. package/dist/SvEmptyState.svelte.d.ts +25 -0
  54. package/dist/SvField.svelte +85 -0
  55. package/dist/SvField.svelte.d.ts +30 -0
  56. package/dist/SvFileUpload.svelte +169 -0
  57. package/dist/SvFileUpload.svelte.d.ts +24 -0
  58. package/dist/SvForm.svelte +7 -1
  59. package/dist/SvForm.svelte.d.ts +3 -0
  60. package/dist/SvGauge.svelte +7 -1
  61. package/dist/SvGauge.svelte.d.ts +11 -0
  62. package/dist/SvGrid.controller.svelte.d.ts +6 -0
  63. package/dist/SvGrid.controller.svelte.js +64 -9
  64. package/dist/SvGrid.css +33 -0
  65. package/dist/SvGrid.svelte +116 -3
  66. package/dist/SvGrid.types.d.ts +366 -0
  67. package/dist/SvGridBoard.svelte +2281 -0
  68. package/dist/SvGridBoard.svelte.d.ts +31 -0
  69. package/dist/SvGridDropdown.svelte +2 -1
  70. package/dist/SvGridSelect.svelte +255 -0
  71. package/dist/SvGridSelect.svelte.d.ts +32 -0
  72. package/dist/SvGroupCell.svelte +109 -0
  73. package/dist/SvGroupCell.svelte.d.ts +41 -0
  74. package/dist/SvListBox.svelte +123 -36
  75. package/dist/SvListBox.svelte.d.ts +12 -8
  76. package/dist/SvMaskedInput.svelte +53 -13
  77. package/dist/SvMaskedInput.svelte.d.ts +16 -6
  78. package/dist/SvMenu.svelte +101 -0
  79. package/dist/SvMenu.svelte.d.ts +26 -0
  80. package/dist/SvMenuList.svelte +143 -0
  81. package/dist/SvMenuList.svelte.d.ts +24 -0
  82. package/dist/SvModal.svelte +220 -0
  83. package/dist/SvModal.svelte.d.ts +31 -0
  84. package/dist/SvMultiSelect.svelte +277 -0
  85. package/dist/SvMultiSelect.svelte.d.ts +31 -0
  86. package/dist/SvNavPane.svelte +314 -0
  87. package/dist/SvNavPane.svelte.d.ts +59 -0
  88. package/dist/SvNumberInput.svelte +23 -9
  89. package/dist/SvNumberInput.svelte.d.ts +2 -0
  90. package/dist/SvOtpInput.svelte +157 -0
  91. package/dist/SvOtpInput.svelte.d.ts +16 -0
  92. package/dist/SvPagination.svelte +100 -0
  93. package/dist/SvPagination.svelte.d.ts +16 -0
  94. package/dist/SvPasswordInput.svelte +42 -12
  95. package/dist/SvPasswordInput.svelte.d.ts +13 -6
  96. package/dist/SvPhoneInput.svelte +43 -11
  97. package/dist/SvPhoneInput.svelte.d.ts +10 -11
  98. package/dist/SvPopover.svelte +127 -0
  99. package/dist/SvPopover.svelte.d.ts +35 -0
  100. package/dist/SvProgress.svelte +116 -0
  101. package/dist/SvProgress.svelte.d.ts +32 -0
  102. package/dist/SvRadioGroup.svelte +40 -22
  103. package/dist/SvRadioGroup.svelte.d.ts +2 -10
  104. package/dist/SvRating.svelte +51 -30
  105. package/dist/SvRating.svelte.d.ts +10 -7
  106. package/dist/SvRichText.svelte +192 -0
  107. package/dist/SvRichText.svelte.d.ts +16 -0
  108. package/dist/SvRowGroupPanel.svelte +147 -0
  109. package/dist/SvRowGroupPanel.svelte.d.ts +17 -0
  110. package/dist/SvScrollArea.svelte +50 -0
  111. package/dist/SvScrollArea.svelte.d.ts +22 -0
  112. package/dist/SvSkeleton.svelte +76 -0
  113. package/dist/SvSkeleton.svelte.d.ts +25 -0
  114. package/dist/SvSlider.svelte +41 -9
  115. package/dist/SvSlider.svelte.d.ts +4 -4
  116. package/dist/SvSparkline.svelte +77 -0
  117. package/dist/SvSparkline.svelte.d.ts +28 -0
  118. package/dist/SvSplitter.svelte +138 -0
  119. package/dist/SvSplitter.svelte.d.ts +32 -0
  120. package/dist/SvStat.svelte +86 -0
  121. package/dist/SvStat.svelte.d.ts +30 -0
  122. package/dist/SvStepper.svelte +91 -0
  123. package/dist/SvStepper.svelte.d.ts +24 -0
  124. package/dist/SvSwitchButton.svelte +39 -24
  125. package/dist/SvSwitchButton.svelte.d.ts +2 -5
  126. package/dist/SvTabs.svelte +147 -28
  127. package/dist/SvTabs.svelte.d.ts +16 -4
  128. package/dist/SvTagsInput.svelte +59 -22
  129. package/dist/SvTagsInput.svelte.d.ts +9 -4
  130. package/dist/SvTextArea.svelte +113 -0
  131. package/dist/SvTextArea.svelte.d.ts +18 -0
  132. package/dist/SvTextInput.svelte +115 -0
  133. package/dist/SvTextInput.svelte.d.ts +20 -0
  134. package/dist/SvTimePicker.svelte +19 -5
  135. package/dist/SvTimePicker.svelte.d.ts +12 -0
  136. package/dist/SvTimeline.svelte +75 -0
  137. package/dist/SvTimeline.svelte.d.ts +24 -0
  138. package/dist/SvToaster.svelte +124 -0
  139. package/dist/SvToaster.svelte.d.ts +10 -0
  140. package/dist/SvToggleButton.svelte +37 -12
  141. package/dist/SvToggleButton.svelte.d.ts +2 -4
  142. package/dist/SvTooltip.svelte +113 -0
  143. package/dist/SvTooltip.svelte.d.ts +22 -0
  144. package/dist/SvTour.svelte +151 -0
  145. package/dist/SvTour.svelte.d.ts +25 -0
  146. package/dist/SvTree.svelte +286 -44
  147. package/dist/SvTree.svelte.d.ts +26 -9
  148. package/dist/SvTreeSelect.svelte +222 -0
  149. package/dist/SvTreeSelect.svelte.d.ts +24 -0
  150. package/dist/a11y/dismissable.d.ts +32 -0
  151. package/dist/a11y/dismissable.js +54 -0
  152. package/dist/a11y/focus-trap.d.ts +39 -0
  153. package/dist/a11y/focus-trap.js +142 -0
  154. package/dist/a11y/live-region.d.ts +8 -0
  155. package/dist/a11y/live-region.js +60 -0
  156. package/dist/a11y/scroll-lock.d.ts +4 -0
  157. package/dist/a11y/scroll-lock.js +41 -0
  158. package/dist/a11y.d.ts +3 -1
  159. package/dist/a11y.js +1 -1
  160. package/dist/avatar.d.ts +10 -0
  161. package/dist/avatar.js +22 -0
  162. package/dist/builtin-editors.d.ts +5 -0
  163. package/dist/builtin-editors.js +55 -0
  164. package/dist/cdn/svgrid.js +16021 -8234
  165. package/dist/cdn/svgrid.svelte-external.js +14542 -6764
  166. package/dist/columns.js +3 -2
  167. package/dist/core.d.ts +7 -1
  168. package/dist/countries.d.ts +12 -0
  169. package/dist/countries.js +24 -0
  170. package/dist/createAccordion.svelte.d.ts +71 -0
  171. package/dist/createAccordion.svelte.js +99 -0
  172. package/dist/createAutocomplete.svelte.d.ts +13 -4
  173. package/dist/createAutocomplete.svelte.js +11 -1
  174. package/dist/createButtonGroup.svelte.d.ts +62 -0
  175. package/dist/createButtonGroup.svelte.js +154 -0
  176. package/dist/createCalendar.svelte.d.ts +5 -0
  177. package/dist/createCalendar.svelte.js +2 -0
  178. package/dist/createCheckbox.svelte.d.ts +9 -17
  179. package/dist/createCheckbox.svelte.js +28 -1
  180. package/dist/createColorInput.svelte.d.ts +12 -22
  181. package/dist/createColorInput.svelte.js +13 -0
  182. package/dist/createCombobox.svelte.d.ts +16 -5
  183. package/dist/createCombobox.svelte.js +13 -2
  184. package/dist/createCountryInput.svelte.d.ts +14 -3
  185. package/dist/createCountryInput.svelte.js +12 -2
  186. package/dist/createDateTimePicker.svelte.d.ts +17 -2
  187. package/dist/createDateTimePicker.svelte.js +12 -2
  188. package/dist/createDropdownList.svelte.d.ts +14 -5
  189. package/dist/createDropdownList.svelte.js +50 -1
  190. package/dist/createFileUpload.svelte.d.ts +68 -0
  191. package/dist/createFileUpload.svelte.js +122 -0
  192. package/dist/createListbox.svelte.d.ts +10 -1
  193. package/dist/createListbox.svelte.js +48 -1
  194. package/dist/createMaskedInput.svelte.d.ts +11 -2
  195. package/dist/createMaskedInput.svelte.js +11 -1
  196. package/dist/createNumberInput.svelte.d.ts +1 -0
  197. package/dist/createNumberInput.svelte.js +1 -0
  198. package/dist/createPasswordInput.svelte.d.ts +19 -21
  199. package/dist/createPasswordInput.svelte.js +16 -4
  200. package/dist/createPhoneInput.svelte.d.ts +26 -7
  201. package/dist/createPhoneInput.svelte.js +24 -3
  202. package/dist/createPopoverSelect.svelte.d.ts +86 -0
  203. package/dist/createPopoverSelect.svelte.js +196 -0
  204. package/dist/createRadioGroup.svelte.d.ts +9 -0
  205. package/dist/createRadioGroup.svelte.js +11 -1
  206. package/dist/createRating.svelte.d.ts +11 -0
  207. package/dist/createRating.svelte.js +12 -1
  208. package/dist/createSlider.svelte.d.ts +2 -0
  209. package/dist/createSlider.svelte.js +16 -5
  210. package/dist/createSplitter.svelte.d.ts +67 -0
  211. package/dist/createSplitter.svelte.js +99 -0
  212. package/dist/createSwitch.svelte.d.ts +9 -16
  213. package/dist/createSwitch.svelte.js +28 -1
  214. package/dist/createTabs.svelte.d.ts +7 -0
  215. package/dist/createTabs.svelte.js +17 -2
  216. package/dist/createTagsInput.svelte.d.ts +13 -1
  217. package/dist/createTagsInput.svelte.js +12 -3
  218. package/dist/createToggle.svelte.d.ts +9 -0
  219. package/dist/createToggle.svelte.js +12 -1
  220. package/dist/createTree.svelte.d.ts +19 -0
  221. package/dist/createTree.svelte.js +108 -15
  222. package/dist/debounce.d.ts +11 -0
  223. package/dist/debounce.js +30 -0
  224. package/dist/duration.d.ts +15 -0
  225. package/dist/duration.js +51 -0
  226. package/dist/editor-contract.d.ts +62 -7
  227. package/dist/editor-contract.js +45 -5
  228. package/dist/editor-registry.d.ts +67 -0
  229. package/dist/editor-registry.js +38 -0
  230. package/dist/editors/cell-editors.d.ts +1 -1
  231. package/dist/editors/cell-editors.js +5 -0
  232. package/dist/fuzzy.d.ts +3 -0
  233. package/dist/fuzzy.js +41 -0
  234. package/dist/grid-select.d.ts +9 -0
  235. package/dist/grid-select.js +8 -0
  236. package/dist/index.d.ts +79 -6
  237. package/dist/index.js +78 -4
  238. package/dist/keyboard-handlers.js +19 -0
  239. package/dist/list-option.d.ts +36 -0
  240. package/dist/list-option.js +68 -0
  241. package/dist/otp.d.ts +16 -0
  242. package/dist/otp.js +26 -0
  243. package/dist/paginate.d.ts +20 -0
  244. package/dist/paginate.js +40 -0
  245. package/dist/popover.d.ts +15 -1
  246. package/dist/popover.js +29 -3
  247. package/dist/recurrence.d.ts +36 -0
  248. package/dist/recurrence.js +90 -0
  249. package/dist/server-data-source.d.ts +89 -0
  250. package/dist/server-data-source.js +4 -0
  251. package/dist/server-group-model.d.ts +98 -0
  252. package/dist/server-group-model.js +263 -0
  253. package/dist/toast-store.svelte.d.ts +41 -0
  254. package/dist/toast-store.svelte.js +113 -0
  255. package/dist/tree-select.d.ts +19 -0
  256. package/dist/tree-select.js +40 -0
  257. package/dist/ui-app.types.d.ts +80 -0
  258. package/dist/ui-app.types.js +7 -0
  259. package/dist/validators.d.ts +43 -0
  260. package/dist/validators.js +53 -0
  261. package/dist/virtual.d.ts +35 -0
  262. package/dist/virtual.js +34 -0
  263. package/package.json +1 -1
  264. package/src/SvAccordion.svelte +109 -0
  265. package/src/SvAccordion.test.ts +66 -0
  266. package/src/SvAlert.svelte +92 -0
  267. package/src/SvAutoComplete.svelte +47 -18
  268. package/src/SvAvatar.svelte +75 -0
  269. package/src/SvAvatarGroup.svelte +48 -0
  270. package/src/SvBadge.svelte +54 -0
  271. package/src/SvBreadcrumb.svelte +88 -0
  272. package/src/SvButtonGroup.svelte +121 -0
  273. package/src/SvButtonGroup.test.ts +72 -0
  274. package/src/SvCalendar.svelte +72 -9
  275. package/src/SvCard.svelte +71 -0
  276. package/src/SvCarousel.svelte +134 -0
  277. package/src/SvCheckBox.svelte +36 -21
  278. package/src/SvChip.svelte +88 -0
  279. package/src/SvCircularProgress.svelte +109 -0
  280. package/src/SvColorInput.svelte +54 -27
  281. package/src/SvComboBox.svelte +119 -33
  282. package/src/SvCommand.svelte +197 -0
  283. package/src/SvContextMenu.svelte +96 -0
  284. package/src/SvCountryInput.svelte +56 -21
  285. package/src/SvDateRangeInput.svelte +256 -0
  286. package/src/SvDateRangeInput.test.ts +80 -0
  287. package/src/SvDateTimePicker.svelte +57 -20
  288. package/src/SvDivider.svelte +61 -0
  289. package/src/SvDrawer.svelte +226 -0
  290. package/src/SvDrawer.test.ts +125 -0
  291. package/src/SvDropDownList.svelte +114 -35
  292. package/src/SvDurationInput.svelte +108 -0
  293. package/src/SvEmptyState.svelte +59 -0
  294. package/src/SvField.svelte +85 -0
  295. package/src/SvFileUpload.svelte +169 -0
  296. package/src/SvFileUpload.test.ts +59 -0
  297. package/src/SvForm.svelte +7 -1
  298. package/src/SvGauge.svelte +7 -1
  299. package/src/SvGrid.controller.svelte.ts +68 -9
  300. package/src/SvGrid.css +33 -0
  301. package/src/SvGrid.svelte +116 -3
  302. package/src/SvGrid.types.ts +374 -0
  303. package/src/SvGridBoard.svelte +2281 -0
  304. package/src/SvGridDropdown.svelte +2 -1
  305. package/src/SvGridSelect.svelte +255 -0
  306. package/src/SvGridSelect.test.ts +100 -0
  307. package/src/SvGroupCell.svelte +109 -0
  308. package/src/SvListBox.svelte +123 -36
  309. package/src/SvMaskedInput.svelte +53 -13
  310. package/src/SvMenu.svelte +101 -0
  311. package/src/SvMenu.test.ts +73 -0
  312. package/src/SvMenuList.svelte +143 -0
  313. package/src/SvModal.svelte +220 -0
  314. package/src/SvMultiSelect.svelte +277 -0
  315. package/src/SvMultiSelect.test.ts +167 -0
  316. package/src/SvNavPane.svelte +314 -0
  317. package/src/SvNavPane.test.ts +70 -0
  318. package/src/SvNumberInput.svelte +23 -9
  319. package/src/SvOtpInput.svelte +157 -0
  320. package/src/SvPagination.svelte +100 -0
  321. package/src/SvPasswordInput.svelte +42 -12
  322. package/src/SvPhoneInput.svelte +43 -11
  323. package/src/SvPopover.svelte +127 -0
  324. package/src/SvProgress.svelte +116 -0
  325. package/src/SvRadioGroup.svelte +40 -22
  326. package/src/SvRating.svelte +51 -30
  327. package/src/SvRichText.svelte +192 -0
  328. package/src/SvRowGroupPanel.svelte +147 -0
  329. package/src/SvScrollArea.svelte +50 -0
  330. package/src/SvSkeleton.svelte +76 -0
  331. package/src/SvSlider.svelte +41 -9
  332. package/src/SvSparkline.svelte +77 -0
  333. package/src/SvSplitter.svelte +138 -0
  334. package/src/SvSplitter.test.ts +55 -0
  335. package/src/SvStat.svelte +86 -0
  336. package/src/SvStepper.svelte +91 -0
  337. package/src/SvSwitchButton.svelte +39 -24
  338. package/src/SvTabs.svelte +147 -28
  339. package/src/SvTagsInput.svelte +59 -22
  340. package/src/SvTextArea.svelte +113 -0
  341. package/src/SvTextInput.svelte +115 -0
  342. package/src/SvTimePicker.svelte +19 -5
  343. package/src/SvTimeline.svelte +75 -0
  344. package/src/SvToaster.svelte +124 -0
  345. package/src/SvToaster.test.ts +63 -0
  346. package/src/SvToggleButton.svelte +37 -12
  347. package/src/SvTooltip.svelte +113 -0
  348. package/src/SvTour.svelte +151 -0
  349. package/src/SvTree.svelte +286 -44
  350. package/src/SvTreeSelect.svelte +222 -0
  351. package/src/SvTreeSelect.test.ts +87 -0
  352. package/src/a11y/dismissable.test.ts +95 -0
  353. package/src/a11y/dismissable.ts +94 -0
  354. package/src/a11y/focus-trap.test.ts +110 -0
  355. package/src/a11y/focus-trap.ts +134 -0
  356. package/src/a11y/live-region.test.ts +40 -0
  357. package/src/a11y/live-region.ts +65 -0
  358. package/src/a11y/scroll-lock.test.ts +38 -0
  359. package/src/a11y/scroll-lock.ts +41 -0
  360. package/src/a11y.ts +3 -1
  361. package/src/avatar.test.ts +32 -0
  362. package/src/avatar.ts +21 -0
  363. package/src/board.test.ts +927 -0
  364. package/src/builtin-editors.grid.test.ts +85 -0
  365. package/src/builtin-editors.test.ts +58 -0
  366. package/src/builtin-editors.ts +56 -0
  367. package/src/columns.ts +3 -2
  368. package/src/core.ts +10 -0
  369. package/src/countries.ts +25 -0
  370. package/src/createAccordion.svelte.ts +126 -0
  371. package/src/createAutocomplete.svelte.ts +18 -1
  372. package/src/createButtonGroup.svelte.ts +166 -0
  373. package/src/createCalendar.svelte.ts +6 -0
  374. package/src/createCheckbox.svelte.ts +23 -1
  375. package/src/createColorInput.svelte.ts +20 -0
  376. package/src/createCombobox.svelte.ts +22 -2
  377. package/src/createCountryInput.svelte.ts +21 -2
  378. package/src/createDateTimePicker.svelte.ts +23 -2
  379. package/src/createDropdownList.svelte.ts +31 -2
  380. package/src/createFileUpload.svelte.ts +144 -0
  381. package/src/createListbox.svelte.ts +35 -3
  382. package/src/createMaskedInput.svelte.ts +18 -1
  383. package/src/createNumberInput.svelte.ts +2 -0
  384. package/src/createPasswordInput.svelte.ts +31 -5
  385. package/src/createPhoneInput.svelte.ts +38 -4
  386. package/src/createPopoverSelect.svelte.ts +208 -0
  387. package/src/createRadioGroup.svelte.ts +23 -1
  388. package/src/createRating.svelte.ts +26 -1
  389. package/src/createSlider.svelte.ts +14 -3
  390. package/src/createSplitter.svelte.ts +119 -0
  391. package/src/createSwitch.svelte.ts +26 -3
  392. package/src/createTabs.svelte.ts +20 -3
  393. package/src/createTagsInput.svelte.ts +24 -3
  394. package/src/createToggle.svelte.ts +25 -2
  395. package/src/createTree.svelte.ts +104 -14
  396. package/src/debounce.test.ts +50 -0
  397. package/src/debounce.ts +41 -0
  398. package/src/duration.test.ts +41 -0
  399. package/src/duration.ts +51 -0
  400. package/src/editor-contract.ts +87 -8
  401. package/src/editor-registry.grid.test.ts +98 -0
  402. package/src/editor-registry.test.ts +86 -0
  403. package/src/editor-registry.ts +106 -0
  404. package/src/editors/cell-editors.ts +10 -1
  405. package/src/fuzzy.test.ts +40 -0
  406. package/src/fuzzy.ts +39 -0
  407. package/src/grid-select.test.ts +28 -0
  408. package/src/grid-select.ts +20 -0
  409. package/src/index.ts +150 -5
  410. package/src/keyboard-handlers.ts +20 -0
  411. package/src/list-option.ts +74 -0
  412. package/src/list-power.test.ts +71 -0
  413. package/src/otp.test.ts +28 -0
  414. package/src/otp.ts +28 -0
  415. package/src/overlays.test.ts +85 -0
  416. package/src/paginate.test.ts +41 -0
  417. package/src/paginate.ts +61 -0
  418. package/src/phone-valid.test.ts +26 -0
  419. package/src/popover.ts +31 -3
  420. package/src/progress.test.ts +55 -0
  421. package/src/recurrence.test.ts +91 -0
  422. package/src/recurrence.ts +111 -0
  423. package/src/server-data-source.ts +102 -0
  424. package/src/server-group-model.test.ts +294 -0
  425. package/src/server-group-model.ts +370 -0
  426. package/src/svgrid.responsive.test.ts +72 -0
  427. package/src/toast-store.svelte.ts +148 -0
  428. package/src/toast-store.test.ts +86 -0
  429. package/src/tree-edit.test.ts +49 -0
  430. package/src/tree-filter.test.ts +56 -0
  431. package/src/tree-select.test.ts +48 -0
  432. package/src/tree-select.ts +62 -0
  433. package/src/ui-app.test.ts +130 -0
  434. package/src/ui-app.types.ts +88 -0
  435. package/src/ui-display.test.ts +79 -0
  436. package/src/ui-inputs.test.ts +1 -1
  437. package/src/ui-status.test.ts +141 -0
  438. package/src/ui-tier1.test.ts +142 -0
  439. package/src/ui-tier2.test.ts +124 -0
  440. package/src/ui-tier3.test.ts +139 -0
  441. package/src/validators.test.ts +58 -0
  442. package/src/validators.ts +105 -0
  443. package/src/virtual.test.ts +72 -0
  444. package/src/virtual.ts +67 -0
@@ -19,13 +19,22 @@ export function createTabs(config) {
19
19
  return;
20
20
  config.onChange?.(tid);
21
21
  }
22
+ function close(tid) {
23
+ const t = tabs().find((x) => x.id === tid);
24
+ if (t?.closable)
25
+ config.onClose?.(tid);
26
+ }
22
27
  function onKeydown(e) {
23
28
  const list = enabled;
24
29
  if (!list.length)
25
30
  return;
26
31
  const idx = list.findIndex((t) => t.id === active);
27
- const fwd = orientation() === 'horizontal' ? 'ArrowRight' : 'ArrowDown';
28
- const back = orientation() === 'horizontal' ? 'ArrowLeft' : 'ArrowUp';
32
+ const horiz = orientation() === 'horizontal';
33
+ // Under horizontal RTL the Left/Right arrows swap so focus follows the
34
+ // visual order (Right moves to the previous tab).
35
+ const rtlH = horiz && config.dir?.() === 'rtl';
36
+ const fwd = horiz ? (rtlH ? 'ArrowLeft' : 'ArrowRight') : 'ArrowDown';
37
+ const back = horiz ? (rtlH ? 'ArrowRight' : 'ArrowLeft') : 'ArrowUp';
29
38
  let next = null;
30
39
  if (e.key === fwd)
31
40
  next = list[(idx + 1) % list.length]?.id ?? null;
@@ -40,6 +49,11 @@ export function createTabs(config) {
40
49
  select(active);
41
50
  return;
42
51
  }
52
+ else if (e.key === 'Delete') {
53
+ e.preventDefault();
54
+ close(active);
55
+ return;
56
+ }
43
57
  else
44
58
  return;
45
59
  if (next) {
@@ -58,6 +72,7 @@ export function createTabs(config) {
58
72
  get focusTick() { return focusTick; },
59
73
  isActive: (tid) => tid === active,
60
74
  select,
75
+ close,
61
76
  onKeydown,
62
77
  /** Spread onto the tablist container element. */
63
78
  tablistProps: () => ({
@@ -27,6 +27,15 @@ export type TagsInputConfig = {
27
27
  unique?: () => boolean;
28
28
  max?: () => number;
29
29
  ariaLabel?: () => string | undefined;
30
+ /** Accessible label for the draft input (localizable). */
31
+ addLabel?: () => string | undefined;
32
+ /** Accessible label prefix for a chip's remove button (localizable). */
33
+ removeLabel?: () => string | undefined;
34
+ id?: () => string | undefined;
35
+ invalid?: () => boolean;
36
+ required?: () => boolean;
37
+ error?: () => string | undefined;
38
+ hint?: () => string | undefined;
30
39
  };
31
40
  export declare function createTagsInput(config: TagsInputConfig): {
32
41
  /** Current tags (controlled value). */
@@ -38,8 +47,11 @@ export declare function createTagsInput(config: TagsInputConfig): {
38
47
  setDraft: (v: string) => void;
39
48
  /** Spread onto the container element. */
40
49
  rootProps: () => {
41
- role: "group";
50
+ 'aria-invalid': "true" | undefined;
51
+ 'aria-required': "true" | undefined;
52
+ 'aria-describedby': string | undefined;
42
53
  'aria-label': string | undefined;
54
+ role: "group";
43
55
  };
44
56
  /** Spread onto the text <input>. */
45
57
  inputProps: () => {
@@ -18,11 +18,20 @@
18
18
  * </div>
19
19
  * ```
20
20
  */
21
+ import { editorAria } from './editor-contract';
21
22
  export function createTagsInput(config) {
22
23
  const disabled = () => config.disabled?.() ?? false;
23
24
  const unique = () => config.unique?.() ?? true;
24
25
  const max = () => config.max?.() ?? Infinity;
25
26
  const tags = () => config.value();
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 draft = $state('');
27
36
  function add(raw) {
28
37
  const tag = raw.trim();
@@ -67,14 +76,14 @@ export function createTagsInput(config) {
67
76
  /** Spread onto the container element. */
68
77
  rootProps: () => ({
69
78
  role: 'group',
70
- 'aria-label': config.ariaLabel?.(),
79
+ ...editorAria(ariaState()),
71
80
  }),
72
81
  /** Spread onto the text <input>. */
73
82
  inputProps: () => ({
74
83
  type: 'text',
75
84
  value: draft,
76
85
  disabled: disabled(),
77
- 'aria-label': config.ariaLabel?.() ?? 'Add tag',
86
+ 'aria-label': config.addLabel?.() ?? config.ariaLabel?.() ?? 'Add tag',
78
87
  oninput: onInput,
79
88
  onkeydown: onKeydown,
80
89
  onblur: onBlur,
@@ -86,7 +95,7 @@ export function createTagsInput(config) {
86
95
  /** Spread onto a chip's remove <button> at `index`. */
87
96
  removeProps: (index) => ({
88
97
  type: 'button',
89
- 'aria-label': `Remove ${tags()[index] ?? ''}`,
98
+ 'aria-label': `${config.removeLabel?.() ?? 'Remove'} ${tags()[index] ?? ''}`.trim(),
90
99
  onclick: () => removeAt(index),
91
100
  }),
92
101
  };
@@ -21,11 +21,20 @@ export type ToggleConfig = {
21
21
  pressed: () => boolean;
22
22
  onChange?: (pressed: boolean) => void;
23
23
  disabled?: () => boolean;
24
+ id?: () => string | undefined;
25
+ invalid?: () => boolean;
26
+ required?: () => boolean;
27
+ error?: () => string | undefined;
28
+ hint?: () => string | undefined;
24
29
  ariaLabel?: () => string | undefined;
25
30
  };
26
31
  export type ToggleButtonProps = {
27
32
  type: 'button';
33
+ id: string | undefined;
28
34
  'aria-pressed': boolean;
35
+ 'aria-invalid': 'true' | undefined;
36
+ 'aria-required': 'true' | undefined;
37
+ 'aria-describedby': string | undefined;
29
38
  'aria-label': string | undefined;
30
39
  disabled: boolean;
31
40
  'data-pressed': '' | undefined;
@@ -15,6 +15,7 @@
15
15
  *
16
16
  * The styled <SvToggleButton> is just one renderer over this core.
17
17
  */
18
+ import { editorAria } from './editor-contract';
18
19
  export function createToggle(config) {
19
20
  const pressed = () => config.pressed();
20
21
  const disabled = () => config.disabled?.() ?? false;
@@ -23,16 +24,26 @@ export function createToggle(config) {
23
24
  return;
24
25
  config.onChange?.(!pressed());
25
26
  }
27
+ // The validation/label ARIA is derived from the editor contract state.
28
+ const ariaState = () => ({
29
+ id: config.id?.(),
30
+ invalid: config.invalid?.(),
31
+ required: config.required?.(),
32
+ error: config.error?.(),
33
+ hint: config.hint?.(),
34
+ ariaLabel: config.ariaLabel?.(),
35
+ });
26
36
  return {
27
37
  get pressed() { return pressed(); },
28
38
  get disabled() { return disabled(); },
29
39
  toggle,
30
40
  buttonProps: () => ({
31
41
  type: 'button',
42
+ id: config.id?.(),
32
43
  'aria-pressed': pressed(),
33
- 'aria-label': config.ariaLabel?.(),
34
44
  disabled: disabled(),
35
45
  'data-pressed': pressed() ? '' : undefined,
46
+ ...editorAria(ariaState()),
36
47
  onclick: toggle,
37
48
  }),
38
49
  };
@@ -26,6 +26,9 @@ export type TreeNode = {
26
26
  label: string;
27
27
  children?: TreeNode[];
28
28
  disabled?: boolean;
29
+ /** Marks a node whose children are loaded on demand (shows an expand arrow
30
+ * even with no children yet; the renderer loads them on first expand). */
31
+ lazy?: boolean;
29
32
  };
30
33
  export type CheckState = 'checked' | 'indeterminate' | 'unchecked';
31
34
  /** A single visible (flattened) tree row. */
@@ -41,6 +44,18 @@ export type TreeRow = {
41
44
  export declare function treeDescendantIds(node: TreeNode): string[];
42
45
  /** Tri-state check status of `node` given the set of checked ids (pure). */
43
46
  export declare function treeCheckState(node: TreeNode, checked: ReadonlySet<string>): CheckState;
47
+ /** Where a dragged node lands relative to the drop target. */
48
+ export type TreeDropPosition = 'before' | 'after' | 'inside';
49
+ /** True if `id` is inside `node`'s subtree (used to block invalid drops). */
50
+ export declare function treeContains(node: TreeNode, id: string): boolean;
51
+ /**
52
+ * Return a NEW node tree with `dragId` moved before/after/inside `targetId`
53
+ * (pure - the caller sets it back as the controlled `nodes`). No-ops on invalid
54
+ * moves (self, or into own descendant).
55
+ */
56
+ export declare function moveTreeNode(nodes: ReadonlyArray<TreeNode>, dragId: string, targetId: string, position: TreeDropPosition): TreeNode[];
57
+ /** Sort siblings (recursively) by a comparator (pure). */
58
+ export declare function sortTreeNodes(nodes: ReadonlyArray<TreeNode>, compare: (a: TreeNode, b: TreeNode) => number): TreeNode[];
44
59
  /** Reactive inputs are passed as getters so the core tracks live prop changes. */
45
60
  export type TreeConfig = {
46
61
  nodes: () => ReadonlyArray<TreeNode>;
@@ -53,6 +68,10 @@ export type TreeConfig = {
53
68
  checked?: () => string[];
54
69
  onCheck?: (ids: string[]) => void;
55
70
  ariaLabel?: () => string | undefined;
71
+ /** Text direction; under `'rtl'` the Left/Right expand/collapse keys swap. */
72
+ dir?: () => import('./editor-contract').EditorDir | undefined;
73
+ /** Filter query: show only matching nodes + their ancestors, auto-expanded. */
74
+ filter?: () => string | undefined;
56
75
  };
57
76
  export declare function createTree(config: TreeConfig): {
58
77
  /** Internally-managed set of expanded node ids. */
@@ -32,6 +32,61 @@ function findNode(list, id) {
32
32
  }
33
33
  return null;
34
34
  }
35
+ /** True if `id` is inside `node`'s subtree (used to block invalid drops). */
36
+ export function treeContains(node, id) {
37
+ return node.children?.some((c) => c.id === id || treeContains(c, id)) ?? false;
38
+ }
39
+ /**
40
+ * Return a NEW node tree with `dragId` moved before/after/inside `targetId`
41
+ * (pure - the caller sets it back as the controlled `nodes`). No-ops on invalid
42
+ * moves (self, or into own descendant).
43
+ */
44
+ export function moveTreeNode(nodes, dragId, targetId, position) {
45
+ if (dragId === targetId)
46
+ return [...nodes];
47
+ const dragged = findNode(nodes, dragId);
48
+ if (!dragged)
49
+ return [...nodes];
50
+ if (dragId === targetId || treeContains(dragged, targetId))
51
+ return [...nodes];
52
+ const without = (list) => {
53
+ const out = [];
54
+ for (const n of list) {
55
+ if (n.id === dragId)
56
+ continue;
57
+ out.push(n.children ? { ...n, children: without(n.children) } : n);
58
+ }
59
+ return out;
60
+ };
61
+ const insert = (list) => {
62
+ const out = [];
63
+ for (const n of list) {
64
+ if (n.id === targetId) {
65
+ if (position === 'before') {
66
+ out.push(dragged, n);
67
+ }
68
+ else if (position === 'after') {
69
+ out.push(n, dragged);
70
+ }
71
+ else
72
+ out.push({ ...n, children: [...(n.children ?? []), dragged] });
73
+ }
74
+ else if (n.children) {
75
+ out.push({ ...n, children: insert(n.children) });
76
+ }
77
+ else
78
+ out.push(n);
79
+ }
80
+ return out;
81
+ };
82
+ return insert(without(nodes));
83
+ }
84
+ /** Sort siblings (recursively) by a comparator (pure). */
85
+ export function sortTreeNodes(nodes, compare) {
86
+ return [...nodes]
87
+ .sort(compare)
88
+ .map((n) => (n.children ? { ...n, children: sortTreeNodes(n.children, compare) } : n));
89
+ }
35
90
  export function createTree(config) {
36
91
  const nodes = () => config.nodes();
37
92
  const checkable = () => config.checkable?.() ?? false;
@@ -47,14 +102,41 @@ export function createTree(config) {
47
102
  expanded = new Set(ids);
48
103
  }
49
104
  });
105
+ // When filtering, precompute which nodes to show (a match, or an ancestor of a
106
+ // match) and which to force-open so matches are revealed.
107
+ const filterInfo = $derived.by(() => {
108
+ const q = config.filter?.()?.trim().toLowerCase();
109
+ if (!q)
110
+ return null;
111
+ const visible = new Set();
112
+ const forceOpen = new Set();
113
+ const walk = (node) => {
114
+ let childMatch = false;
115
+ for (const c of node.children ?? [])
116
+ childMatch = walk(c) || childMatch;
117
+ const selfMatch = node.label.toLowerCase().includes(q);
118
+ if (selfMatch || childMatch) {
119
+ visible.add(node.id);
120
+ if (childMatch)
121
+ forceOpen.add(node.id);
122
+ }
123
+ return selfMatch || childMatch;
124
+ };
125
+ for (const n of nodes())
126
+ walk(n);
127
+ return { visible, forceOpen };
128
+ });
50
129
  const rows = $derived.by(() => {
51
130
  const out = [];
131
+ const fi = filterInfo;
52
132
  const walk = (list, depth, parentId) => {
53
133
  for (const node of list) {
54
- const hasChildren = !!node.children?.length;
55
- const open = expanded.has(node.id);
134
+ if (fi && !fi.visible.has(node.id))
135
+ continue;
136
+ const hasChildren = !!node.children?.length || !!node.lazy;
137
+ const open = fi ? fi.forceOpen.has(node.id) || expanded.has(node.id) : expanded.has(node.id);
56
138
  out.push({ node, depth, hasChildren, open, parentId, index: out.length });
57
- if (hasChildren && open)
139
+ if (open && node.children?.length)
58
140
  walk(node.children, depth + 1, node.id);
59
141
  }
60
142
  };
@@ -73,7 +155,9 @@ export function createTree(config) {
73
155
  const isExpanded = (id) => expanded.has(id);
74
156
  const isSelected = (id) => id === selected();
75
157
  function toggleExpand(node) {
76
- if (!node.children?.length)
158
+ // Lazy nodes have no children *yet* - they must still be expandable so the
159
+ // toggle fires onToggle and the host can fetch children on demand.
160
+ if (!node.children?.length && !node.lazy)
77
161
  return;
78
162
  const next = new Set(expanded);
79
163
  const willOpen = !next.has(node.id);
@@ -104,6 +188,24 @@ export function createTree(config) {
104
188
  const item = rows[active];
105
189
  if (!item)
106
190
  return;
191
+ // "expand" opens the node or steps into the first child; "collapse" closes it
192
+ // or jumps to the parent. Under RTL the physical Left/Right keys are swapped.
193
+ const expand = () => {
194
+ if (item.hasChildren && !item.open)
195
+ toggleExpand(item.node);
196
+ else if (item.hasChildren && item.open)
197
+ setActive(Math.min(active + 1, rows.length - 1), true);
198
+ };
199
+ const collapse = () => {
200
+ if (item.hasChildren && item.open)
201
+ toggleExpand(item.node);
202
+ else if (item.parentId) {
203
+ const pi = rows.findIndex((r) => r.node.id === item.parentId);
204
+ if (pi >= 0)
205
+ setActive(pi, true);
206
+ }
207
+ };
208
+ const rtl = config.dir?.() === 'rtl';
107
209
  switch (e.key) {
108
210
  case 'ArrowDown':
109
211
  e.preventDefault();
@@ -115,20 +217,11 @@ export function createTree(config) {
115
217
  break;
116
218
  case 'ArrowRight':
117
219
  e.preventDefault();
118
- if (item.hasChildren && !item.open)
119
- toggleExpand(item.node);
120
- else if (item.hasChildren && item.open)
121
- setActive(Math.min(active + 1, rows.length - 1), true);
220
+ (rtl ? collapse : expand)();
122
221
  break;
123
222
  case 'ArrowLeft':
124
223
  e.preventDefault();
125
- if (item.hasChildren && item.open)
126
- toggleExpand(item.node);
127
- else if (item.parentId) {
128
- const pi = rows.findIndex((r) => r.node.id === item.parentId);
129
- if (pi >= 0)
130
- setActive(pi, true);
131
- }
224
+ (rtl ? expand : collapse)();
132
225
  break;
133
226
  case 'Enter':
134
227
  e.preventDefault();
@@ -0,0 +1,11 @@
1
+ /**
2
+ * debounce - coalesce rapid calls into one trailing call after `ms` of quiet.
3
+ * Used for remote search in the async dropdowns (SvMultiSelect/GridSelect/...),
4
+ * but general-purpose. Returns the debounced function with `cancel()` (drop the
5
+ * pending call) and `flush()` (run it now). Framework-free + pure.
6
+ */
7
+ export type Debounced<A extends unknown[]> = ((...args: A) => void) & {
8
+ cancel: () => void;
9
+ flush: () => void;
10
+ };
11
+ export declare function debounce<A extends unknown[]>(fn: (...args: A) => void, ms: number): Debounced<A>;
@@ -0,0 +1,30 @@
1
+ export function debounce(fn, ms) {
2
+ let timer = null;
3
+ let pending = null;
4
+ const run = () => {
5
+ timer = null;
6
+ const args = pending;
7
+ pending = null;
8
+ if (args)
9
+ fn(...args);
10
+ };
11
+ const debounced = ((...args) => {
12
+ pending = args;
13
+ if (timer)
14
+ clearTimeout(timer);
15
+ timer = setTimeout(run, ms);
16
+ });
17
+ debounced.cancel = () => {
18
+ if (timer)
19
+ clearTimeout(timer);
20
+ timer = null;
21
+ pending = null;
22
+ };
23
+ debounced.flush = () => {
24
+ if (timer) {
25
+ clearTimeout(timer);
26
+ run();
27
+ }
28
+ };
29
+ return debounced;
30
+ }
@@ -0,0 +1,15 @@
1
+ /**
2
+ * duration - pure parse/format helpers behind <SvDurationInput>. The canonical
3
+ * value is a number of MINUTES; these convert to/from the human forms people
4
+ * type ("1h 30m", "1:30", "90"). Framework-free + pure so they are unit-tested
5
+ * directly.
6
+ */
7
+ /**
8
+ * Parse a human duration to minutes, or `null` if unparseable. Accepts:
9
+ * - `h:mm` e.g. "1:30" -> 90
10
+ * - unit form e.g. "1h 30m", "1.5h", "45m" -> 90 / 90 / 45
11
+ * - a bare number e.g. "90" -> 90 (treated as minutes)
12
+ */
13
+ export declare function parseDuration(input: string): number | null;
14
+ /** Format minutes as `h:mm` (colon) or `Nh Nm` (units). Empty for invalid input. */
15
+ export declare function formatDuration(minutes: number, style?: 'colon' | 'units'): string;
@@ -0,0 +1,51 @@
1
+ /**
2
+ * duration - pure parse/format helpers behind <SvDurationInput>. The canonical
3
+ * value is a number of MINUTES; these convert to/from the human forms people
4
+ * type ("1h 30m", "1:30", "90"). Framework-free + pure so they are unit-tested
5
+ * directly.
6
+ */
7
+ /**
8
+ * Parse a human duration to minutes, or `null` if unparseable. Accepts:
9
+ * - `h:mm` e.g. "1:30" -> 90
10
+ * - unit form e.g. "1h 30m", "1.5h", "45m" -> 90 / 90 / 45
11
+ * - a bare number e.g. "90" -> 90 (treated as minutes)
12
+ */
13
+ export function parseDuration(input) {
14
+ const s = input.trim().toLowerCase();
15
+ if (!s)
16
+ return null;
17
+ const colon = s.match(/^(\d+):(\d{1,2})$/);
18
+ if (colon) {
19
+ const mm = parseInt(colon[2], 10);
20
+ if (mm > 59)
21
+ return null;
22
+ return parseInt(colon[1], 10) * 60 + mm;
23
+ }
24
+ const units = s.match(/\d+(?:\.\d+)?\s*[hm]/g);
25
+ if (units && units.join('').replace(/\s/g, '').length === s.replace(/\s/g, '').length) {
26
+ let total = 0;
27
+ for (const part of units) {
28
+ const m = part.match(/(\d+(?:\.\d+)?)\s*([hm])/);
29
+ const n = parseFloat(m[1]);
30
+ total += m[2] === 'h' ? n * 60 : n;
31
+ }
32
+ return Math.round(total);
33
+ }
34
+ const num = Number(s);
35
+ return Number.isFinite(num) ? Math.round(num) : null;
36
+ }
37
+ /** Format minutes as `h:mm` (colon) or `Nh Nm` (units). Empty for invalid input. */
38
+ export function formatDuration(minutes, style = 'colon') {
39
+ if (!Number.isFinite(minutes) || minutes < 0)
40
+ return '';
41
+ const h = Math.floor(minutes / 60);
42
+ const m = Math.round(minutes % 60);
43
+ if (style === 'units') {
44
+ if (h && m)
45
+ return `${h}h ${m}m`;
46
+ if (h)
47
+ return `${h}h`;
48
+ return `${m}m`;
49
+ }
50
+ return `${h}:${String(m).padStart(2, '0')}`;
51
+ }
@@ -2,13 +2,16 @@
2
2
  * The shared "editor contract" for the SvGrid UI kit.
3
3
  *
4
4
  * Every value-bearing editor (inputs, selects, pickers) accepts this common set
5
- * of props so forms, validation and accessibility behave IDENTICALLY across the
6
- * kit - the same spirit as the grid's headless core, applied to the editors.
7
- * Components spread their own `value` / options / behavior on top of this base.
5
+ * of props so forms, validation, accessibility, direction (RTL) and localization
6
+ * behave IDENTICALLY across the kit - the same spirit as the grid's headless
7
+ * core, applied to the editors. Components spread their own `value` / options /
8
+ * behavior on top of this base.
8
9
  *
9
10
  * Framework-free (no Svelte, no DOM) so it can be unit-tested and reused.
10
11
  */
11
12
  export type EditorSize = 'sm' | 'md' | 'lg';
13
+ /** Text direction. `'auto'` defers to the surrounding document/CSS. */
14
+ export type EditorDir = 'ltr' | 'rtl' | 'auto';
12
15
  /** Props shared by every value-bearing editor in the kit. */
13
16
  export type SvEditorProps = {
14
17
  /** Disable interaction + form submission. */
@@ -21,23 +24,62 @@ export type SvEditorProps = {
21
24
  invalid?: boolean;
22
25
  /** Error message; when set it is announced via `aria-describedby` and shown. */
23
26
  error?: string;
27
+ /** Visible field label, rendered above the control and wired via `for`/`id`. */
28
+ label?: string;
29
+ /** Helper text shown under the control and announced via `aria-describedby`. */
30
+ hint?: string;
24
31
  /** Control size / density. */
25
32
  size?: EditorSize;
33
+ /**
34
+ * Text direction. `'rtl'` mirrors layout for Arabic/Hebrew/etc.; `'auto'` (the
35
+ * default) inherits from the surrounding document so a page-level `dir` wins.
36
+ */
37
+ dir?: EditorDir;
26
38
  /** Form field name; the editor emits a hidden input carrying its value. */
27
39
  name?: string;
28
- /** Root/control element id (the error-text id derives from it). */
40
+ /** Root/control element id (the label/hint/error ids derive from it). */
29
41
  id?: string;
30
- /** Accessible name when there is no visible `<label>`. */
42
+ /** Accessible name when there is no visible `label`. */
31
43
  ariaLabel?: string;
32
44
  };
45
+ /**
46
+ * The interaction surface an editor exposes when embedded as a grid cell editor.
47
+ * Standalone usage ignores these; the grid supplies them so every editor shares
48
+ * one commit / cancel / move contract (Enter commits, Escape cancels, Tab moves)
49
+ * instead of each cell-editor re-implementing key handling. Consumed in Phase 2
50
+ * (the editor registry).
51
+ */
52
+ export type EditorInteraction<V = unknown> = {
53
+ /** Commit the current value and stop editing (Enter, option pick, blur-commit). */
54
+ onCommit?: (value: V) => void;
55
+ /** Abandon the edit and restore the previous value (Escape). */
56
+ onCancel?: () => void;
57
+ /** Commit, then move to the adjacent cell (Tab = 1, Shift+Tab = -1). */
58
+ onCommitAndMove?: (value: V, direction: 1 | -1) => void;
59
+ /** Ask the surrounding popover/panel to close without committing. */
60
+ onRequestClose?: () => void;
61
+ /** True while mounted inside a grid cell (lets an editor tune autofocus etc.). */
62
+ inCell?: boolean;
63
+ };
33
64
  /** Stable DOM id for an editor's error text, for `aria-describedby` wiring. */
34
65
  export declare const editorErrorId: (id?: string) => string | undefined;
66
+ /** Stable DOM id for an editor's hint text, for `aria-describedby` wiring. */
67
+ export declare const editorHintId: (id?: string) => string | undefined;
68
+ /**
69
+ * Generate a stable-per-instance element id. Call ONCE per component instance
70
+ * (e.g. `const uid = provided ?? nextEditorId()`) so label/hint/error wiring
71
+ * always has an anchor even when the consumer passes no `id`.
72
+ */
73
+ export declare const nextEditorId: (prefix?: string) => string;
35
74
  /** A subset of {@link SvEditorProps} that drives ARIA on the focusable control. */
36
- export type EditorAriaState = Pick<SvEditorProps, 'id' | 'invalid' | 'required' | 'error' | 'ariaLabel'>;
75
+ export type EditorAriaState = Pick<SvEditorProps, 'id' | 'invalid' | 'required' | 'error' | 'hint' | 'ariaLabel'>;
37
76
  /**
38
77
  * ARIA attributes for the focusable control of an editor, derived from its state.
39
78
  * Spread onto the input/button/combobox:
40
- * `<input {...editorAria({ id, invalid, required, error, ariaLabel })} />`
79
+ * `<input {...editorAria({ id, invalid, required, error, hint, ariaLabel })} />`
80
+ *
81
+ * `aria-describedby` points at the error text (when invalid) and/or the hint
82
+ * text - both ids derive from `id`, so pass a stable `id` (see {@link nextEditorId}).
41
83
  * Undefined values are omitted by Svelte, so unset props add no attributes.
42
84
  */
43
85
  export declare function editorAria(state: EditorAriaState): {
@@ -46,3 +88,16 @@ export declare function editorAria(state: EditorAriaState): {
46
88
  'aria-describedby': string | undefined;
47
89
  'aria-label': string | undefined;
48
90
  };
91
+ /**
92
+ * Merge a component's default message strings with a consumer's `messages`
93
+ * override, so every editor can be fully localized from the outside.
94
+ *
95
+ * ```ts
96
+ * const M = { clear: 'Clear', noResults: 'No results' }
97
+ * const messages = resolveMessages(M, props.messages) // Partial override
98
+ * ```
99
+ *
100
+ * Undefined/empty overrides fall back to the default, so a partial `messages`
101
+ * object only replaces the keys it sets.
102
+ */
103
+ export declare function resolveMessages<T extends Record<string, string>>(defaults: T, overrides?: Partial<T> | null): T;
@@ -2,25 +2,65 @@
2
2
  * The shared "editor contract" for the SvGrid UI kit.
3
3
  *
4
4
  * Every value-bearing editor (inputs, selects, pickers) accepts this common set
5
- * of props so forms, validation and accessibility behave IDENTICALLY across the
6
- * kit - the same spirit as the grid's headless core, applied to the editors.
7
- * Components spread their own `value` / options / behavior on top of this base.
5
+ * of props so forms, validation, accessibility, direction (RTL) and localization
6
+ * behave IDENTICALLY across the kit - the same spirit as the grid's headless
7
+ * core, applied to the editors. Components spread their own `value` / options /
8
+ * behavior on top of this base.
8
9
  *
9
10
  * Framework-free (no Svelte, no DOM) so it can be unit-tested and reused.
10
11
  */
11
12
  /** Stable DOM id for an editor's error text, for `aria-describedby` wiring. */
12
13
  export const editorErrorId = (id) => (id ? `${id}__error` : undefined);
14
+ /** Stable DOM id for an editor's hint text, for `aria-describedby` wiring. */
15
+ export const editorHintId = (id) => (id ? `${id}__hint` : undefined);
16
+ let _autoId = 0;
17
+ /**
18
+ * Generate a stable-per-instance element id. Call ONCE per component instance
19
+ * (e.g. `const uid = provided ?? nextEditorId()`) so label/hint/error wiring
20
+ * always has an anchor even when the consumer passes no `id`.
21
+ */
22
+ export const nextEditorId = (prefix = 'sv-ed') => `${prefix}-${_autoId++}`;
13
23
  /**
14
24
  * ARIA attributes for the focusable control of an editor, derived from its state.
15
25
  * Spread onto the input/button/combobox:
16
- * `<input {...editorAria({ id, invalid, required, error, ariaLabel })} />`
26
+ * `<input {...editorAria({ id, invalid, required, error, hint, ariaLabel })} />`
27
+ *
28
+ * `aria-describedby` points at the error text (when invalid) and/or the hint
29
+ * text - both ids derive from `id`, so pass a stable `id` (see {@link nextEditorId}).
17
30
  * Undefined values are omitted by Svelte, so unset props add no attributes.
18
31
  */
19
32
  export function editorAria(state) {
33
+ const described = [
34
+ state.error ? editorErrorId(state.id) : undefined,
35
+ state.hint ? editorHintId(state.id) : undefined,
36
+ ].filter(Boolean);
20
37
  return {
21
38
  'aria-invalid': state.invalid ? 'true' : undefined,
22
39
  'aria-required': state.required ? 'true' : undefined,
23
- 'aria-describedby': state.error ? editorErrorId(state.id) : undefined,
40
+ 'aria-describedby': described.length ? described.join(' ') : undefined,
24
41
  'aria-label': state.ariaLabel,
25
42
  };
26
43
  }
44
+ /**
45
+ * Merge a component's default message strings with a consumer's `messages`
46
+ * override, so every editor can be fully localized from the outside.
47
+ *
48
+ * ```ts
49
+ * const M = { clear: 'Clear', noResults: 'No results' }
50
+ * const messages = resolveMessages(M, props.messages) // Partial override
51
+ * ```
52
+ *
53
+ * Undefined/empty overrides fall back to the default, so a partial `messages`
54
+ * object only replaces the keys it sets.
55
+ */
56
+ export function resolveMessages(defaults, overrides) {
57
+ if (!overrides)
58
+ return defaults;
59
+ const out = { ...defaults };
60
+ for (const k in overrides) {
61
+ const v = overrides[k];
62
+ if (v != null && v !== '')
63
+ out[k] = v;
64
+ }
65
+ return out;
66
+ }