@svgrid/grid 1.2.33 → 2.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (490) hide show
  1. package/README.md +6 -5
  2. package/dist/FlexRender.svelte +96 -96
  3. package/dist/GridFooter.svelte +178 -178
  4. package/dist/SvAccordion.svelte +109 -0
  5. package/dist/SvAccordion.svelte.d.ts +28 -0
  6. package/dist/SvAlert.svelte +92 -0
  7. package/dist/SvAlert.svelte.d.ts +27 -0
  8. package/dist/SvAutoComplete.svelte +47 -18
  9. package/dist/SvAutoComplete.svelte.d.ts +2 -5
  10. package/dist/SvAvatar.svelte +75 -0
  11. package/dist/SvAvatar.svelte.d.ts +14 -0
  12. package/dist/SvAvatarGroup.svelte +48 -0
  13. package/dist/SvAvatarGroup.svelte.d.ts +16 -0
  14. package/dist/SvBadge.svelte +54 -0
  15. package/dist/SvBadge.svelte.d.ts +23 -0
  16. package/dist/SvBreadcrumb.svelte +88 -0
  17. package/dist/SvBreadcrumb.svelte.d.ts +23 -0
  18. package/dist/SvButtonGroup.svelte +121 -0
  19. package/dist/SvButtonGroup.svelte.d.ts +16 -0
  20. package/dist/SvCalendar.svelte +72 -9
  21. package/dist/SvCalendar.svelte.d.ts +34 -1
  22. package/dist/SvCard.svelte +71 -0
  23. package/dist/SvCard.svelte.d.ts +28 -0
  24. package/dist/SvCarousel.svelte +134 -0
  25. package/dist/SvCarousel.svelte.d.ts +28 -0
  26. package/dist/SvCheckBox.svelte +36 -21
  27. package/dist/SvCheckBox.svelte.d.ts +2 -5
  28. package/dist/SvChip.svelte +88 -0
  29. package/dist/SvChip.svelte.d.ts +31 -0
  30. package/dist/SvCircularProgress.svelte +109 -0
  31. package/dist/SvCircularProgress.svelte.d.ts +31 -0
  32. package/dist/SvColorInput.svelte +54 -27
  33. package/dist/SvColorInput.svelte.d.ts +10 -6
  34. package/dist/SvComboBox.svelte +119 -33
  35. package/dist/SvComboBox.svelte.d.ts +18 -6
  36. package/dist/SvCommand.svelte +197 -0
  37. package/dist/SvCommand.svelte.d.ts +26 -0
  38. package/dist/SvContextMenu.svelte +96 -0
  39. package/dist/SvContextMenu.svelte.d.ts +25 -0
  40. package/dist/SvCountryInput.svelte +56 -21
  41. package/dist/SvCountryInput.svelte.d.ts +9 -5
  42. package/dist/SvDateRangeInput.svelte +256 -0
  43. package/dist/SvDateRangeInput.svelte.d.ts +37 -0
  44. package/dist/SvDateTimePicker.svelte +57 -20
  45. package/dist/SvDateTimePicker.svelte.d.ts +12 -4
  46. package/dist/SvDivider.svelte +61 -0
  47. package/dist/SvDivider.svelte.d.ts +22 -0
  48. package/dist/SvDrawer.svelte +226 -0
  49. package/dist/SvDrawer.svelte.d.ts +43 -0
  50. package/dist/SvDropDownList.svelte +114 -35
  51. package/dist/SvDropDownList.svelte.d.ts +7 -6
  52. package/dist/SvDurationInput.svelte +108 -0
  53. package/dist/SvDurationInput.svelte.d.ts +15 -0
  54. package/dist/SvEmptyState.svelte +59 -0
  55. package/dist/SvEmptyState.svelte.d.ts +25 -0
  56. package/dist/SvField.svelte +85 -0
  57. package/dist/SvField.svelte.d.ts +30 -0
  58. package/dist/SvFileUpload.svelte +169 -0
  59. package/dist/SvFileUpload.svelte.d.ts +24 -0
  60. package/dist/SvForm.svelte +7 -1
  61. package/dist/SvForm.svelte.d.ts +3 -0
  62. package/dist/SvGauge.svelte +7 -1
  63. package/dist/SvGauge.svelte.d.ts +11 -0
  64. package/dist/SvGrid.controller.svelte.d.ts +6 -0
  65. package/dist/SvGrid.controller.svelte.js +64 -9
  66. package/dist/SvGrid.css +2376 -2343
  67. package/dist/SvGrid.svelte +2764 -2651
  68. package/dist/SvGrid.types.d.ts +366 -0
  69. package/dist/SvGridBoard.svelte +2281 -0
  70. package/dist/SvGridBoard.svelte.d.ts +31 -0
  71. package/dist/SvGridDropdown.svelte +666 -665
  72. package/dist/SvGridSelect.svelte +255 -0
  73. package/dist/SvGridSelect.svelte.d.ts +32 -0
  74. package/dist/SvGroupCell.svelte +109 -0
  75. package/dist/SvGroupCell.svelte.d.ts +41 -0
  76. package/dist/SvListBox.svelte +123 -36
  77. package/dist/SvListBox.svelte.d.ts +12 -8
  78. package/dist/SvMaskedInput.svelte +53 -13
  79. package/dist/SvMaskedInput.svelte.d.ts +16 -6
  80. package/dist/SvMenu.svelte +101 -0
  81. package/dist/SvMenu.svelte.d.ts +26 -0
  82. package/dist/SvMenuList.svelte +143 -0
  83. package/dist/SvMenuList.svelte.d.ts +24 -0
  84. package/dist/SvModal.svelte +220 -0
  85. package/dist/SvModal.svelte.d.ts +31 -0
  86. package/dist/SvMultiSelect.svelte +277 -0
  87. package/dist/SvMultiSelect.svelte.d.ts +31 -0
  88. package/dist/SvNavPane.svelte +314 -0
  89. package/dist/SvNavPane.svelte.d.ts +59 -0
  90. package/dist/SvNumberInput.svelte +23 -9
  91. package/dist/SvNumberInput.svelte.d.ts +2 -0
  92. package/dist/SvOtpInput.svelte +157 -0
  93. package/dist/SvOtpInput.svelte.d.ts +16 -0
  94. package/dist/SvPagination.svelte +100 -0
  95. package/dist/SvPagination.svelte.d.ts +16 -0
  96. package/dist/SvPasswordInput.svelte +42 -12
  97. package/dist/SvPasswordInput.svelte.d.ts +13 -6
  98. package/dist/SvPhoneInput.svelte +43 -11
  99. package/dist/SvPhoneInput.svelte.d.ts +10 -11
  100. package/dist/SvPopover.svelte +127 -0
  101. package/dist/SvPopover.svelte.d.ts +35 -0
  102. package/dist/SvProgress.svelte +116 -0
  103. package/dist/SvProgress.svelte.d.ts +32 -0
  104. package/dist/SvRadioGroup.svelte +40 -22
  105. package/dist/SvRadioGroup.svelte.d.ts +2 -10
  106. package/dist/SvRating.svelte +51 -30
  107. package/dist/SvRating.svelte.d.ts +10 -7
  108. package/dist/SvRichText.svelte +192 -0
  109. package/dist/SvRichText.svelte.d.ts +16 -0
  110. package/dist/SvRowGroupPanel.svelte +147 -0
  111. package/dist/SvRowGroupPanel.svelte.d.ts +17 -0
  112. package/dist/SvScrollArea.svelte +50 -0
  113. package/dist/SvScrollArea.svelte.d.ts +22 -0
  114. package/dist/SvSkeleton.svelte +76 -0
  115. package/dist/SvSkeleton.svelte.d.ts +25 -0
  116. package/dist/SvSlider.svelte +41 -9
  117. package/dist/SvSlider.svelte.d.ts +4 -4
  118. package/dist/SvSparkline.svelte +77 -0
  119. package/dist/SvSparkline.svelte.d.ts +28 -0
  120. package/dist/SvSplitter.svelte +138 -0
  121. package/dist/SvSplitter.svelte.d.ts +32 -0
  122. package/dist/SvStat.svelte +86 -0
  123. package/dist/SvStat.svelte.d.ts +30 -0
  124. package/dist/SvStepper.svelte +91 -0
  125. package/dist/SvStepper.svelte.d.ts +24 -0
  126. package/dist/SvSwitchButton.svelte +39 -24
  127. package/dist/SvSwitchButton.svelte.d.ts +2 -5
  128. package/dist/SvTabs.svelte +147 -28
  129. package/dist/SvTabs.svelte.d.ts +16 -4
  130. package/dist/SvTagsInput.svelte +59 -22
  131. package/dist/SvTagsInput.svelte.d.ts +9 -4
  132. package/dist/SvTextArea.svelte +113 -0
  133. package/dist/SvTextArea.svelte.d.ts +18 -0
  134. package/dist/SvTextInput.svelte +115 -0
  135. package/dist/SvTextInput.svelte.d.ts +20 -0
  136. package/dist/SvTimePicker.svelte +19 -5
  137. package/dist/SvTimePicker.svelte.d.ts +12 -0
  138. package/dist/SvTimeline.svelte +75 -0
  139. package/dist/SvTimeline.svelte.d.ts +24 -0
  140. package/dist/SvToaster.svelte +124 -0
  141. package/dist/SvToaster.svelte.d.ts +10 -0
  142. package/dist/SvToggleButton.svelte +37 -12
  143. package/dist/SvToggleButton.svelte.d.ts +2 -4
  144. package/dist/SvTooltip.svelte +113 -0
  145. package/dist/SvTooltip.svelte.d.ts +22 -0
  146. package/dist/SvTour.svelte +151 -0
  147. package/dist/SvTour.svelte.d.ts +25 -0
  148. package/dist/SvTree.svelte +286 -44
  149. package/dist/SvTree.svelte.d.ts +26 -9
  150. package/dist/SvTreeSelect.svelte +222 -0
  151. package/dist/SvTreeSelect.svelte.d.ts +24 -0
  152. package/dist/a11y/dismissable.d.ts +32 -0
  153. package/dist/a11y/dismissable.js +54 -0
  154. package/dist/a11y/focus-trap.d.ts +39 -0
  155. package/dist/a11y/focus-trap.js +142 -0
  156. package/dist/a11y/live-region.d.ts +8 -0
  157. package/dist/a11y/live-region.js +60 -0
  158. package/dist/a11y/scroll-lock.d.ts +4 -0
  159. package/dist/a11y/scroll-lock.js +41 -0
  160. package/dist/a11y.d.ts +3 -1
  161. package/dist/a11y.js +1 -1
  162. package/dist/avatar.d.ts +10 -0
  163. package/dist/avatar.js +22 -0
  164. package/dist/builtin-editors.d.ts +5 -0
  165. package/dist/builtin-editors.js +55 -0
  166. package/dist/cdn/svgrid.js +16022 -8235
  167. package/dist/cdn/svgrid.svelte-external.js +14543 -6765
  168. package/dist/columns.js +3 -2
  169. package/dist/core.d.ts +7 -1
  170. package/dist/countries.d.ts +12 -0
  171. package/dist/countries.js +24 -0
  172. package/dist/createAccordion.svelte.d.ts +71 -0
  173. package/dist/createAccordion.svelte.js +99 -0
  174. package/dist/createAutocomplete.svelte.d.ts +13 -4
  175. package/dist/createAutocomplete.svelte.js +11 -1
  176. package/dist/createButtonGroup.svelte.d.ts +62 -0
  177. package/dist/createButtonGroup.svelte.js +154 -0
  178. package/dist/createCalendar.svelte.d.ts +5 -0
  179. package/dist/createCalendar.svelte.js +2 -0
  180. package/dist/createCheckbox.svelte.d.ts +9 -17
  181. package/dist/createCheckbox.svelte.js +28 -1
  182. package/dist/createColorInput.svelte.d.ts +12 -22
  183. package/dist/createColorInput.svelte.js +13 -0
  184. package/dist/createCombobox.svelte.d.ts +16 -5
  185. package/dist/createCombobox.svelte.js +13 -2
  186. package/dist/createCountryInput.svelte.d.ts +14 -3
  187. package/dist/createCountryInput.svelte.js +12 -2
  188. package/dist/createDateTimePicker.svelte.d.ts +17 -2
  189. package/dist/createDateTimePicker.svelte.js +12 -2
  190. package/dist/createDropdownList.svelte.d.ts +14 -5
  191. package/dist/createDropdownList.svelte.js +50 -1
  192. package/dist/createFileUpload.svelte.d.ts +68 -0
  193. package/dist/createFileUpload.svelte.js +122 -0
  194. package/dist/createListbox.svelte.d.ts +10 -1
  195. package/dist/createListbox.svelte.js +48 -1
  196. package/dist/createMaskedInput.svelte.d.ts +11 -2
  197. package/dist/createMaskedInput.svelte.js +11 -1
  198. package/dist/createNumberInput.svelte.d.ts +1 -0
  199. package/dist/createNumberInput.svelte.js +1 -0
  200. package/dist/createPasswordInput.svelte.d.ts +19 -21
  201. package/dist/createPasswordInput.svelte.js +16 -4
  202. package/dist/createPhoneInput.svelte.d.ts +26 -7
  203. package/dist/createPhoneInput.svelte.js +24 -3
  204. package/dist/createPopoverSelect.svelte.d.ts +86 -0
  205. package/dist/createPopoverSelect.svelte.js +196 -0
  206. package/dist/createRadioGroup.svelte.d.ts +9 -0
  207. package/dist/createRadioGroup.svelte.js +11 -1
  208. package/dist/createRating.svelte.d.ts +11 -0
  209. package/dist/createRating.svelte.js +12 -1
  210. package/dist/createSlider.svelte.d.ts +2 -0
  211. package/dist/createSlider.svelte.js +16 -5
  212. package/dist/createSplitter.svelte.d.ts +67 -0
  213. package/dist/createSplitter.svelte.js +99 -0
  214. package/dist/createSwitch.svelte.d.ts +9 -16
  215. package/dist/createSwitch.svelte.js +28 -1
  216. package/dist/createTabs.svelte.d.ts +7 -0
  217. package/dist/createTabs.svelte.js +17 -2
  218. package/dist/createTagsInput.svelte.d.ts +13 -1
  219. package/dist/createTagsInput.svelte.js +12 -3
  220. package/dist/createToggle.svelte.d.ts +9 -0
  221. package/dist/createToggle.svelte.js +12 -1
  222. package/dist/createTree.svelte.d.ts +19 -0
  223. package/dist/createTree.svelte.js +108 -15
  224. package/dist/debounce.d.ts +11 -0
  225. package/dist/debounce.js +30 -0
  226. package/dist/duration.d.ts +15 -0
  227. package/dist/duration.js +51 -0
  228. package/dist/editor-contract.d.ts +62 -7
  229. package/dist/editor-contract.js +45 -5
  230. package/dist/editor-registry.d.ts +67 -0
  231. package/dist/editor-registry.js +38 -0
  232. package/dist/editors/cell-editors.d.ts +1 -1
  233. package/dist/editors/cell-editors.js +5 -0
  234. package/dist/fuzzy.d.ts +3 -0
  235. package/dist/fuzzy.js +41 -0
  236. package/dist/grid-select.d.ts +9 -0
  237. package/dist/grid-select.js +8 -0
  238. package/dist/index.d.ts +79 -6
  239. package/dist/index.js +78 -4
  240. package/dist/keyboard-handlers.js +19 -0
  241. package/dist/list-option.d.ts +36 -0
  242. package/dist/list-option.js +68 -0
  243. package/dist/otp.d.ts +16 -0
  244. package/dist/otp.js +26 -0
  245. package/dist/paginate.d.ts +20 -0
  246. package/dist/paginate.js +40 -0
  247. package/dist/popover.d.ts +15 -1
  248. package/dist/popover.js +29 -3
  249. package/dist/recurrence.d.ts +36 -0
  250. package/dist/recurrence.js +90 -0
  251. package/dist/server-data-source.d.ts +89 -0
  252. package/dist/server-data-source.js +4 -0
  253. package/dist/server-group-model.d.ts +98 -0
  254. package/dist/server-group-model.js +263 -0
  255. package/dist/toast-store.svelte.d.ts +41 -0
  256. package/dist/toast-store.svelte.js +113 -0
  257. package/dist/tree-select.d.ts +19 -0
  258. package/dist/tree-select.js +40 -0
  259. package/dist/ui-app.types.d.ts +80 -0
  260. package/dist/ui-app.types.js +7 -0
  261. package/dist/validators.d.ts +43 -0
  262. package/dist/validators.js +53 -0
  263. package/dist/virtual.d.ts +35 -0
  264. package/dist/virtual.js +34 -0
  265. package/package.json +85 -85
  266. package/src/FlexRender.svelte +96 -96
  267. package/src/GridFooter.svelte +178 -178
  268. package/src/SvAccordion.svelte +109 -0
  269. package/src/SvAccordion.test.ts +66 -0
  270. package/src/SvAlert.svelte +92 -0
  271. package/src/SvAutoComplete.svelte +47 -18
  272. package/src/SvAvatar.svelte +75 -0
  273. package/src/SvAvatarGroup.svelte +48 -0
  274. package/src/SvBadge.svelte +54 -0
  275. package/src/SvBreadcrumb.svelte +88 -0
  276. package/src/SvButtonGroup.svelte +121 -0
  277. package/src/SvButtonGroup.test.ts +72 -0
  278. package/src/SvCalendar.svelte +72 -9
  279. package/src/SvCard.svelte +71 -0
  280. package/src/SvCarousel.svelte +134 -0
  281. package/src/SvCheckBox.svelte +36 -21
  282. package/src/SvChip.svelte +88 -0
  283. package/src/SvCircularProgress.svelte +109 -0
  284. package/src/SvColorInput.svelte +54 -27
  285. package/src/SvComboBox.svelte +119 -33
  286. package/src/SvCommand.svelte +197 -0
  287. package/src/SvContextMenu.svelte +96 -0
  288. package/src/SvCountryInput.svelte +56 -21
  289. package/src/SvDateRangeInput.svelte +256 -0
  290. package/src/SvDateRangeInput.test.ts +80 -0
  291. package/src/SvDateTimePicker.svelte +57 -20
  292. package/src/SvDivider.svelte +61 -0
  293. package/src/SvDrawer.svelte +226 -0
  294. package/src/SvDrawer.test.ts +125 -0
  295. package/src/SvDropDownList.svelte +114 -35
  296. package/src/SvDurationInput.svelte +108 -0
  297. package/src/SvEmptyState.svelte +59 -0
  298. package/src/SvField.svelte +85 -0
  299. package/src/SvFileUpload.svelte +169 -0
  300. package/src/SvFileUpload.test.ts +59 -0
  301. package/src/SvForm.svelte +7 -1
  302. package/src/SvGauge.svelte +7 -1
  303. package/src/SvGrid.controller.svelte.ts +2553 -2494
  304. package/src/SvGrid.css +2376 -2343
  305. package/src/SvGrid.svelte +2764 -2651
  306. package/src/SvGrid.types.ts +944 -570
  307. package/src/SvGridBoard.svelte +2281 -0
  308. package/src/SvGridDropdown.svelte +666 -665
  309. package/src/SvGridSelect.svelte +255 -0
  310. package/src/SvGridSelect.test.ts +100 -0
  311. package/src/SvGroupCell.svelte +109 -0
  312. package/src/SvListBox.svelte +123 -36
  313. package/src/SvMaskedInput.svelte +53 -13
  314. package/src/SvMenu.svelte +101 -0
  315. package/src/SvMenu.test.ts +73 -0
  316. package/src/SvMenuList.svelte +143 -0
  317. package/src/SvModal.svelte +220 -0
  318. package/src/SvMultiSelect.svelte +277 -0
  319. package/src/SvMultiSelect.test.ts +167 -0
  320. package/src/SvNavPane.svelte +314 -0
  321. package/src/SvNavPane.test.ts +70 -0
  322. package/src/SvNumberInput.svelte +23 -9
  323. package/src/SvOtpInput.svelte +157 -0
  324. package/src/SvPagination.svelte +100 -0
  325. package/src/SvPasswordInput.svelte +42 -12
  326. package/src/SvPhoneInput.svelte +43 -11
  327. package/src/SvPopover.svelte +127 -0
  328. package/src/SvProgress.svelte +116 -0
  329. package/src/SvRadioGroup.svelte +40 -22
  330. package/src/SvRating.svelte +51 -30
  331. package/src/SvRichText.svelte +192 -0
  332. package/src/SvRowGroupPanel.svelte +147 -0
  333. package/src/SvScrollArea.svelte +50 -0
  334. package/src/SvSkeleton.svelte +76 -0
  335. package/src/SvSlider.svelte +41 -9
  336. package/src/SvSparkline.svelte +77 -0
  337. package/src/SvSplitter.svelte +138 -0
  338. package/src/SvSplitter.test.ts +55 -0
  339. package/src/SvStat.svelte +86 -0
  340. package/src/SvStepper.svelte +91 -0
  341. package/src/SvSwitchButton.svelte +39 -24
  342. package/src/SvTabs.svelte +147 -28
  343. package/src/SvTagsInput.svelte +59 -22
  344. package/src/SvTextArea.svelte +113 -0
  345. package/src/SvTextInput.svelte +115 -0
  346. package/src/SvTimePicker.svelte +19 -5
  347. package/src/SvTimeline.svelte +75 -0
  348. package/src/SvToaster.svelte +124 -0
  349. package/src/SvToaster.test.ts +63 -0
  350. package/src/SvToggleButton.svelte +37 -12
  351. package/src/SvTooltip.svelte +113 -0
  352. package/src/SvTour.svelte +151 -0
  353. package/src/SvTree.svelte +286 -44
  354. package/src/SvTreeSelect.svelte +222 -0
  355. package/src/SvTreeSelect.test.ts +87 -0
  356. package/src/a11y/dismissable.test.ts +95 -0
  357. package/src/a11y/dismissable.ts +94 -0
  358. package/src/a11y/focus-trap.test.ts +110 -0
  359. package/src/a11y/focus-trap.ts +134 -0
  360. package/src/a11y/live-region.test.ts +40 -0
  361. package/src/a11y/live-region.ts +65 -0
  362. package/src/a11y/scroll-lock.test.ts +38 -0
  363. package/src/a11y/scroll-lock.ts +41 -0
  364. package/src/a11y.contract.test.ts +49 -49
  365. package/src/a11y.test.ts +59 -59
  366. package/src/a11y.ts +61 -59
  367. package/src/avatar.test.ts +32 -0
  368. package/src/avatar.ts +21 -0
  369. package/src/board.test.ts +927 -0
  370. package/src/build-api.ts +798 -798
  371. package/src/builtin-editors.grid.test.ts +85 -0
  372. package/src/builtin-editors.test.ts +58 -0
  373. package/src/builtin-editors.ts +56 -0
  374. package/src/cell-formatting.ts +169 -169
  375. package/src/cell-render.ts +469 -469
  376. package/src/collaboration.test.ts +104 -104
  377. package/src/collaboration.ts +167 -167
  378. package/src/columns.ts +3 -2
  379. package/src/core.performance.test.ts +30 -30
  380. package/src/core.ts +1111 -1101
  381. package/src/countries.ts +25 -0
  382. package/src/createAccordion.svelte.ts +126 -0
  383. package/src/createAutocomplete.svelte.ts +18 -1
  384. package/src/createButtonGroup.svelte.ts +166 -0
  385. package/src/createCalendar.svelte.ts +6 -0
  386. package/src/createCheckbox.svelte.ts +23 -1
  387. package/src/createColorInput.svelte.ts +20 -0
  388. package/src/createCombobox.svelte.ts +22 -2
  389. package/src/createCountryInput.svelte.ts +21 -2
  390. package/src/createDateTimePicker.svelte.ts +23 -2
  391. package/src/createDropdownList.svelte.ts +31 -2
  392. package/src/createFileUpload.svelte.ts +144 -0
  393. package/src/createGrid.svelte.ts +42 -42
  394. package/src/createGrid.test.ts +10 -10
  395. package/src/createGridState.svelte.ts +17 -17
  396. package/src/createListbox.svelte.ts +35 -3
  397. package/src/createMaskedInput.svelte.ts +18 -1
  398. package/src/createNumberInput.svelte.ts +2 -0
  399. package/src/createPasswordInput.svelte.ts +31 -5
  400. package/src/createPhoneInput.svelte.ts +38 -4
  401. package/src/createPopoverSelect.svelte.ts +208 -0
  402. package/src/createRadioGroup.svelte.ts +23 -1
  403. package/src/createRating.svelte.ts +26 -1
  404. package/src/createSlider.svelte.ts +14 -3
  405. package/src/createSplitter.svelte.ts +119 -0
  406. package/src/createSwitch.svelte.ts +26 -3
  407. package/src/createTabs.svelte.ts +20 -3
  408. package/src/createTagsInput.svelte.ts +24 -3
  409. package/src/createToggle.svelte.ts +25 -2
  410. package/src/createTree.svelte.ts +104 -14
  411. package/src/debounce.test.ts +50 -0
  412. package/src/debounce.ts +41 -0
  413. package/src/duration.test.ts +41 -0
  414. package/src/duration.ts +51 -0
  415. package/src/editing.test.ts +859 -859
  416. package/src/editing.ts +675 -675
  417. package/src/editor-contract.ts +87 -8
  418. package/src/editor-registry.grid.test.ts +98 -0
  419. package/src/editor-registry.test.ts +86 -0
  420. package/src/editor-registry.ts +106 -0
  421. package/src/editors/cell-editors.ts +10 -1
  422. package/src/export-data-api.test.ts +126 -126
  423. package/src/export-format.test.ts +107 -107
  424. package/src/export-format.ts +598 -598
  425. package/src/flex-render.ts +3 -3
  426. package/src/fuzzy.test.ts +40 -0
  427. package/src/fuzzy.ts +39 -0
  428. package/src/grid-select.test.ts +28 -0
  429. package/src/grid-select.ts +20 -0
  430. package/src/index.ts +463 -318
  431. package/src/keyboard-handlers.ts +20 -0
  432. package/src/keyboard.test.ts +59 -59
  433. package/src/keyboard.ts +97 -97
  434. package/src/list-option.ts +74 -0
  435. package/src/list-power.test.ts +71 -0
  436. package/src/menus.ts +582 -582
  437. package/src/merge-objects.ts +48 -48
  438. package/src/otp.test.ts +28 -0
  439. package/src/otp.ts +28 -0
  440. package/src/overlays.test.ts +85 -0
  441. package/src/paginate.test.ts +41 -0
  442. package/src/paginate.ts +61 -0
  443. package/src/phone-valid.test.ts +26 -0
  444. package/src/popover.ts +31 -3
  445. package/src/progress.test.ts +55 -0
  446. package/src/recurrence.test.ts +91 -0
  447. package/src/recurrence.ts +111 -0
  448. package/src/render-component.ts +28 -28
  449. package/src/selection.test.ts +754 -754
  450. package/src/selection.ts +600 -600
  451. package/src/server-data-source.test.ts +289 -289
  452. package/src/server-data-source.ts +413 -311
  453. package/src/server-group-model.test.ts +294 -0
  454. package/src/server-group-model.ts +370 -0
  455. package/src/sparkline.test.ts +68 -68
  456. package/src/sparkline.ts +169 -169
  457. package/src/spreadsheet.test.ts +489 -489
  458. package/src/spreadsheet.ts +304 -304
  459. package/src/static-functions.ts +11 -11
  460. package/src/subscribe.ts +38 -38
  461. package/src/svgrid-wrapper.types.ts +439 -439
  462. package/src/svgrid.behavior.test.ts +706 -706
  463. package/src/svgrid.features.test.ts +157 -157
  464. package/src/svgrid.new-features.wrapper.test.ts +251 -251
  465. package/src/svgrid.responsive.test.ts +72 -0
  466. package/src/svgrid.wrapper.test.ts +40 -40
  467. package/src/toast-store.svelte.ts +148 -0
  468. package/src/toast-store.test.ts +86 -0
  469. package/src/tree-edit.test.ts +49 -0
  470. package/src/tree-filter.test.ts +56 -0
  471. package/src/tree-select.test.ts +48 -0
  472. package/src/tree-select.ts +62 -0
  473. package/src/ui-app.test.ts +130 -0
  474. package/src/ui-app.types.ts +88 -0
  475. package/src/ui-display.test.ts +79 -0
  476. package/src/ui-inputs.test.ts +1 -1
  477. package/src/ui-status.test.ts +141 -0
  478. package/src/ui-tier1.test.ts +142 -0
  479. package/src/ui-tier2.test.ts +124 -0
  480. package/src/ui-tier3.test.ts +139 -0
  481. package/src/validators.test.ts +58 -0
  482. package/src/validators.ts +105 -0
  483. package/src/virtual.test.ts +72 -0
  484. package/src/virtual.ts +67 -0
  485. package/src/virtualization/column-virtualizer.test.ts +27 -27
  486. package/src/virtualization/column-virtualizer.ts +30 -30
  487. package/src/virtualization/svelte-virtualizer.svelte.ts +26 -26
  488. package/src/virtualization/types.ts +30 -30
  489. package/src/virtualization/virtualizer.test.ts +47 -47
  490. package/src/virtualization/virtualizer.ts +296 -296
package/src/countries.ts CHANGED
@@ -68,4 +68,29 @@ export const COUNTRIES: Country[] = [
68
68
  { code: 'MX', name: 'Mexico', dial: '+52' },
69
69
  ]
70
70
 
71
+ /**
72
+ * Expected national (post-dial-code) significant-digit count per country, as an
73
+ * exact length or an inclusive [min, max] range. A pragmatic length-based check
74
+ * (not full libphonenumber). Countries without an entry accept any non-empty
75
+ * number.
76
+ */
77
+ export const PHONE_NATIONAL_LENGTHS: Record<string, number | [number, number]> = {
78
+ US: 10, CA: 10, GB: 10, IE: 9, FR: 9, DE: [10, 11], ES: 9, IT: [9, 10], NL: 9, BE: [8, 9],
79
+ CH: 9, AT: [10, 12], SE: [7, 9], NO: 8, DK: 8, FI: [9, 10], PL: 9, PT: 9, GR: 10, CZ: 9,
80
+ RU: 10, TR: 10, IL: 9, AE: 9, SA: 9, IN: 10, PK: 10, CN: 11, HK: 8, JP: 10, KR: [9, 10],
81
+ SG: 8, MY: [9, 10], TH: 9, ID: [9, 11], PH: 10, VN: 9, AU: 9, NZ: [8, 10], ZA: 9, NG: 10,
82
+ EG: 10, KE: 9, MA: 9, BR: 11, AR: 10, CL: 9, CO: 10, MX: 10,
83
+ }
84
+
85
+ /**
86
+ * Whether `nationalDigits` is a plausible national number for `code` (length
87
+ * check). Unknown countries pass on any non-empty input. Pure.
88
+ */
89
+ export function phoneDigitsValid(code: string, nationalDigits: string): boolean {
90
+ const n = (nationalDigits.match(/\d/g) ?? []).length
91
+ const rule = PHONE_NATIONAL_LENGTHS[code.toUpperCase()]
92
+ if (rule == null) return n > 0
93
+ return typeof rule === 'number' ? n === rule : n >= rule[0] && n <= rule[1]
94
+ }
95
+
71
96
  export const COUNTRY_BY_CODE = new Map(COUNTRIES.map((c) => [c.code, c]))
@@ -0,0 +1,126 @@
1
+ /**
2
+ * createAccordion - the HEADLESS core behind <SvAccordion>: a WAI-ARIA accordion
3
+ * (collapsible sections) with single- or multiple-expand, roving header focus
4
+ * (Up/Down/Home/End) and full ARIA, exposed as **prop-getters** you spread onto
5
+ * YOUR OWN markup. No styles, no DOM. Parity: Smart `smart-accordion`.
6
+ *
7
+ * ```svelte
8
+ * <script lang="ts">
9
+ * import { createAccordion } from '@svgrid/grid'
10
+ * let expanded = $state<string[]>(['a'])
11
+ * const acc = createAccordion({ items: () => items, expanded: () => expanded, onChange: (ids) => (expanded = ids) })
12
+ * </script>
13
+ * {#each items as it, i (it.id)}
14
+ * <h3><button {...acc.headerProps(i)}>{it.label}</button></h3>
15
+ * {#if acc.isExpanded(it.id)}<div {...acc.panelProps(i)}>...</div>{/if}
16
+ * {/each}
17
+ * ```
18
+ *
19
+ * The styled <SvAccordion> is one renderer over this core. DOM focus movement is
20
+ * a render concern: the component watches `focusTick`/`focusIndex` and calls
21
+ * `.focus()` itself.
22
+ */
23
+ export type AccordionItem = { id: string; label: string; disabled?: boolean }
24
+ /** `single` keeps at most one panel open; `multiple` allows any number. */
25
+ export type AccordionExpandMode = 'single' | 'multiple'
26
+
27
+ /** Reactive inputs are passed as getters so the core tracks live prop changes. */
28
+ export type AccordionConfig = {
29
+ items: () => ReadonlyArray<AccordionItem>
30
+ /** Expanded item ids (controlled). */
31
+ expanded: () => string[]
32
+ onChange?: (expandedIds: string[]) => void
33
+ expandMode?: () => AccordionExpandMode
34
+ disabled?: () => boolean
35
+ dir?: () => 'ltr' | 'rtl' | 'auto' | undefined
36
+ }
37
+
38
+ let uid = 0
39
+
40
+ export function createAccordion(config: AccordionConfig) {
41
+ const id = `sv-acc-${uid++}`
42
+ const items = () => config.items()
43
+ const expandMode = () => config.expandMode?.() ?? 'single'
44
+ const disabled = () => config.disabled?.() ?? false
45
+
46
+ const expandedSet = $derived(new Set(config.expanded()))
47
+ const enabledIdx = $derived(items().map((it, i) => (it.disabled ? -1 : i)).filter((i) => i >= 0))
48
+
49
+ let focusIndex = $state(0)
50
+ let focusTick = $state(0)
51
+ const requestFocus = (i: number) => { focusIndex = i; focusTick++ }
52
+
53
+ const headerId = (i: number) => `${id}-h-${i}`
54
+ const panelId = (i: number) => `${id}-p-${i}`
55
+ const isExpanded = (itemId: string) => expandedSet.has(itemId)
56
+
57
+ function toggle(i: number) {
58
+ const it = items()[i]
59
+ if (!it || it.disabled || disabled()) return
60
+ const open = expandedSet.has(it.id)
61
+ if (expandMode() === 'single') {
62
+ config.onChange?.(open ? [] : [it.id])
63
+ } else {
64
+ const next = new Set(expandedSet)
65
+ open ? next.delete(it.id) : next.add(it.id)
66
+ config.onChange?.([...next])
67
+ }
68
+ }
69
+
70
+ function move(delta: number) {
71
+ if (!enabledIdx.length) return
72
+ const pos = enabledIdx.indexOf(focusIndex)
73
+ const base = pos < 0 ? 0 : pos
74
+ const next = enabledIdx[(base + delta + enabledIdx.length) % enabledIdx.length]
75
+ if (next != null) requestFocus(next)
76
+ }
77
+
78
+ function onHeaderKeydown(e: KeyboardEvent, i: number) {
79
+ if (disabled()) return
80
+ switch (e.key) {
81
+ case 'ArrowDown': e.preventDefault(); move(1); break
82
+ case 'ArrowUp': e.preventDefault(); move(-1); break
83
+ case 'Home': e.preventDefault(); requestFocus(enabledIdx[0] ?? 0); break
84
+ case 'End': e.preventDefault(); requestFocus(enabledIdx.at(-1) ?? 0); break
85
+ case 'Enter':
86
+ case ' ': e.preventDefault(); toggle(i); break
87
+ }
88
+ }
89
+
90
+ return {
91
+ /** Header index that should hold DOM focus after the latest keyboard move. */
92
+ get focusIndex() { return focusIndex },
93
+ /** Monotonic counter; changes only on keyboard navigation. */
94
+ get focusTick() { return focusTick },
95
+ /** Currently-expanded item ids. */
96
+ get expandedIds() { return [...expandedSet] },
97
+ isExpanded,
98
+ toggle,
99
+ /** Spread onto the accordion root container. */
100
+ rootProps: () => ({
101
+ 'data-orientation': 'vertical' as const,
102
+ }),
103
+ /** Spread onto the header <button> at `index`. */
104
+ headerProps: (index: number) => {
105
+ const it = items()[index]
106
+ return {
107
+ type: 'button' as const,
108
+ id: headerId(index),
109
+ 'data-idx': index,
110
+ 'aria-expanded': it ? isExpanded(it.id) : false,
111
+ 'aria-controls': panelId(index),
112
+ disabled: disabled() || it?.disabled || undefined,
113
+ onclick: () => toggle(index),
114
+ onkeydown: (e: KeyboardEvent) => onHeaderKeydown(e, index),
115
+ }
116
+ },
117
+ /** Spread onto the panel/region element at `index`. */
118
+ panelProps: (index: number) => ({
119
+ role: 'region' as const,
120
+ id: panelId(index),
121
+ 'aria-labelledby': headerId(index),
122
+ }),
123
+ }
124
+ }
125
+
126
+ export type Accordion = ReturnType<typeof createAccordion>
@@ -20,6 +20,7 @@
20
20
  * ```
21
21
  */
22
22
  import { filterOptions, type ListOption } from './list-option'
23
+ import { editorAria, type EditorAriaState } from './editor-contract'
23
24
 
24
25
  /** Reactive inputs are passed as getters so the core tracks live prop changes. */
25
26
  export type AutocompleteConfig = {
@@ -32,11 +33,26 @@ export type AutocompleteConfig = {
32
33
  ariaLabel?: () => string | undefined
33
34
  /** DOM focus hook provided by the renderer; the core never touches the DOM. */
34
35
  focusInput?: () => void
36
+ // Editor contract (ARIA + validation) - folded into inputProps().
37
+ id?: () => string | undefined
38
+ invalid?: () => boolean
39
+ required?: () => boolean
40
+ error?: () => string | undefined
41
+ hint?: () => string | undefined
35
42
  }
36
43
 
37
44
  export function createAutocomplete(config: AutocompleteConfig) {
38
45
  const minChars = () => config.minChars?.() ?? 1
39
46
 
47
+ const ariaState = (): EditorAriaState => ({
48
+ id: config.id?.(),
49
+ invalid: config.invalid?.(),
50
+ required: config.required?.(),
51
+ error: config.error?.(),
52
+ hint: config.hint?.(),
53
+ ariaLabel: config.ariaLabel?.(),
54
+ })
55
+
40
56
  let open = $state(false)
41
57
  let active = $state(0)
42
58
 
@@ -75,10 +91,11 @@ export function createAutocomplete(config: AutocompleteConfig) {
75
91
  onKeydown,
76
92
  /** Spread onto the text <input>. */
77
93
  inputProps: () => ({
94
+ id: config.id?.(),
78
95
  role: 'combobox' as const,
79
96
  'aria-expanded': open,
80
97
  'aria-autocomplete': 'list' as const,
81
- 'aria-label': config.ariaLabel?.(),
98
+ ...editorAria(ariaState()),
82
99
  value: config.value(),
83
100
  disabled: config.disabled?.() ?? false,
84
101
  oninput: onInput,
@@ -0,0 +1,166 @@
1
+ /**
2
+ * createButtonGroup - the HEADLESS core behind <SvButtonGroup>: a segmented
3
+ * button bar with single- or multi-select (or plain action buttons), roving
4
+ * tabindex + full keyboard, exposed as **prop-getters** you spread onto YOUR OWN
5
+ * markup. No styles, no DOM. Parity: Smart `smart-button-group`.
6
+ *
7
+ * ```svelte
8
+ * <script lang="ts">
9
+ * import { createButtonGroup } from '@svgrid/grid'
10
+ * let value = $state<string | null>('day')
11
+ * const g = createButtonGroup({ items: () => items, value: () => value, onChange: (v) => (value = v) })
12
+ * </script>
13
+ * <div {...g.rootProps()}>
14
+ * {#each items as it, i (it.value)}<button {...g.buttonProps(i)}>{it.label}</button>{/each}
15
+ * </div>
16
+ * ```
17
+ *
18
+ * The styled <SvButtonGroup> is one renderer over this core.
19
+ */
20
+ import { editorAria, type EditorAriaState } from './editor-contract'
21
+
22
+ export type ButtonGroupItem = { value: string | number; label?: string; disabled?: boolean }
23
+ /** `single` = radio semantics; `multiple` = toggle set; `none` = action buttons. */
24
+ export type ButtonGroupMode = 'single' | 'multiple' | 'none'
25
+ export type ButtonGroupValue = string | number | Array<string | number> | null
26
+
27
+ /** Reactive inputs are passed as getters so the core tracks live prop changes. */
28
+ export type ButtonGroupConfig = {
29
+ items: () => ReadonlyArray<ButtonGroupItem>
30
+ value: () => ButtonGroupValue
31
+ onChange?: (value: string | number | Array<string | number>) => void
32
+ mode?: () => ButtonGroupMode
33
+ disabled?: () => boolean
34
+ orientation?: () => 'horizontal' | 'vertical'
35
+ /** Under `'rtl'` the horizontal Left/Right arrow direction flips. */
36
+ dir?: () => 'ltr' | 'rtl' | 'auto' | undefined
37
+ // Editor contract (ARIA + validation) - folded into rootProps().
38
+ id?: () => string | undefined
39
+ invalid?: () => boolean
40
+ required?: () => boolean
41
+ error?: () => string | undefined
42
+ hint?: () => string | undefined
43
+ ariaLabel?: () => string | undefined
44
+ }
45
+
46
+ /** Normalize the value to an array (pure). */
47
+ export function toGroupArray(value: ButtonGroupValue): Array<string | number> {
48
+ if (value == null) return []
49
+ return Array.isArray(value) ? [...value] : [value]
50
+ }
51
+
52
+ export function createButtonGroup(config: ButtonGroupConfig) {
53
+ const items = () => config.items()
54
+ const mode = () => config.mode?.() ?? 'single'
55
+ const disabled = () => config.disabled?.() ?? false
56
+ const isVert = () => (config.orientation?.() ?? 'horizontal') === 'vertical'
57
+ const rtl = () => config.dir?.() === 'rtl'
58
+
59
+ const selected = $derived(toGroupArray(config.value()))
60
+ const enabledIdx = $derived(items().map((it, i) => (it.disabled ? -1 : i)).filter((i) => i >= 0))
61
+
62
+ const isSelected = (v: string | number) => selected.includes(v)
63
+ const selectedIdx = $derived(items().findIndex((it) => isSelected(it.value)))
64
+
65
+ // Focus cursor. In single (radio) mode moving the cursor also selects, so the
66
+ // roving-tabindex target follows the selection; multiple/none keep their own
67
+ // cursor in `active`.
68
+ let active = $state(0)
69
+ $effect(() => {
70
+ const it = items()[active]
71
+ if (!it || it.disabled) active = enabledIdx[0] ?? 0
72
+ })
73
+ const cursor = $derived(
74
+ mode() === 'single' ? (selectedIdx >= 0 ? selectedIdx : enabledIdx[0] ?? 0) : active,
75
+ )
76
+
77
+ function pick(i: number) {
78
+ const it = items()[i]
79
+ if (!it || it.disabled || disabled()) return
80
+ active = i
81
+ if (mode() === 'none') { config.onChange?.(it.value); return }
82
+ if (mode() === 'multiple') {
83
+ const set = new Set(selected)
84
+ set.has(it.value) ? set.delete(it.value) : set.add(it.value)
85
+ config.onChange?.([...set])
86
+ } else {
87
+ config.onChange?.(it.value)
88
+ }
89
+ }
90
+
91
+ function goto(i: number) {
92
+ // Radio semantics select on move; toggle/action only move focus.
93
+ if (mode() === 'single') pick(i)
94
+ else active = i
95
+ }
96
+ function move(delta: number) {
97
+ if (!enabledIdx.length) return
98
+ const pos = enabledIdx.indexOf(cursor)
99
+ const next = enabledIdx[(pos + delta + enabledIdx.length) % enabledIdx.length]
100
+ if (next != null) goto(next)
101
+ }
102
+
103
+ function onKeydown(e: KeyboardEvent) {
104
+ if (disabled()) return
105
+ const horiz = !isVert()
106
+ const rtlH = horiz && rtl()
107
+ const fwd = horiz ? (rtlH ? 'ArrowLeft' : 'ArrowRight') : 'ArrowDown'
108
+ const back = horiz ? (rtlH ? 'ArrowRight' : 'ArrowLeft') : 'ArrowUp'
109
+ if (e.key === fwd) { e.preventDefault(); move(1) }
110
+ else if (e.key === back) { e.preventDefault(); move(-1) }
111
+ else if (e.key === 'Home') { e.preventDefault(); goto(enabledIdx[0] ?? 0) }
112
+ else if (e.key === 'End') { e.preventDefault(); goto(enabledIdx.at(-1) ?? 0) }
113
+ else if (e.key === ' ' || e.key === 'Enter') { e.preventDefault(); pick(cursor) }
114
+ }
115
+
116
+ const ariaState = (): EditorAriaState => ({
117
+ id: config.id?.(),
118
+ invalid: config.invalid?.(),
119
+ required: config.required?.(),
120
+ error: config.error?.(),
121
+ hint: config.hint?.(),
122
+ ariaLabel: config.ariaLabel?.(),
123
+ })
124
+
125
+ const groupRole = () => (mode() === 'single' ? 'radiogroup' : 'group')
126
+
127
+ return {
128
+ /** Roving-focus target index (follows the selection in single mode). */
129
+ get activeIndex() { return cursor },
130
+ /** Selected values as an array (single mode -> 0 or 1 entry). */
131
+ get selectedValues() { return selected },
132
+ isSelected,
133
+ isActive: (i: number) => i === cursor,
134
+ pick,
135
+ move,
136
+ onKeydown,
137
+ /** Spread onto the group container element. */
138
+ rootProps: () => ({
139
+ role: groupRole(),
140
+ id: config.id?.(),
141
+ 'aria-orientation': isVert() ? ('vertical' as const) : ('horizontal' as const),
142
+ ...editorAria(ariaState()),
143
+ onkeydown: onKeydown,
144
+ }),
145
+ /** Spread onto the button element at `index`. */
146
+ buttonProps: (index: number) => {
147
+ const it = items()[index]
148
+ const on = it ? isSelected(it.value) : false
149
+ const single = mode() === 'single'
150
+ return {
151
+ type: 'button' as const,
152
+ role: single ? ('radio' as const) : undefined,
153
+ 'data-idx': index,
154
+ 'aria-checked': single ? on : undefined,
155
+ 'aria-pressed': mode() === 'multiple' ? on : undefined,
156
+ 'data-selected': on ? '' : undefined,
157
+ disabled: disabled() || it?.disabled || undefined,
158
+ // Roving tabindex: only the cursor (selection in single mode) is tabbable.
159
+ tabindex: mode() === 'none' ? 0 : index === cursor ? 0 : -1,
160
+ onclick: () => pick(index),
161
+ }
162
+ },
163
+ }
164
+ }
165
+
166
+ export type ButtonGroup = ReturnType<typeof createButtonGroup>
@@ -48,6 +48,7 @@ import {
48
48
  type SelectionState,
49
49
  } from './datetime/date-selection'
50
50
  import { isDisabledDay, isImportant, type RestrictOptions } from './datetime/date-restrict'
51
+ import { matchesRecurrence, type RecurrenceRule } from './recurrence'
51
52
 
52
53
  export type DisplayMode = 'month' | 'year' | 'decade'
53
54
  export type CalendarNameFormat = 'narrow' | 'short' | 'long'
@@ -82,6 +83,8 @@ export type CalendarDayState = {
82
83
  outside: boolean
83
84
  focused: boolean
84
85
  preview: boolean
86
+ /** Matches one of the `recurrence` rules (repeat pattern). */
87
+ recurring: boolean
85
88
  }
86
89
 
87
90
  /** Reactive inputs are passed as getters so the core tracks live prop changes;
@@ -105,6 +108,8 @@ export type CalendarConfig = {
105
108
  dayNameFormat?: () => CalendarNameFormat
106
109
  monthNameFormat?: () => CalendarNameFormat
107
110
  dateTooltip?: () => ((date: Date) => string | null | undefined) | undefined
111
+ /** Repeat pattern(s) - matching days get `dayState().recurring = true`. */
112
+ recurrence?: () => RecurrenceRule | ReadonlyArray<RecurrenceRule> | null
108
113
  }
109
114
 
110
115
  const defaultLocale = () => (typeof navigator !== 'undefined' ? navigator.language : 'en-US')
@@ -216,6 +221,7 @@ export function createCalendar(config: CalendarConfig) {
216
221
  outside: !isSameMonth(d, panelMonth),
217
222
  focused: isSameDay(d, focusDate),
218
223
  preview: inPreview(d),
224
+ recurring: matchesRecurrence(d, config.recurrence?.() ?? null),
219
225
  }
220
226
  }
221
227
 
@@ -15,18 +15,30 @@
15
15
  *
16
16
  * The styled <SvCheckBox> is just one renderer over this core.
17
17
  */
18
+ import { editorAria, type EditorAriaState } from './editor-contract'
19
+
18
20
  export type CheckboxConfig = {
19
21
  checked: () => boolean
20
22
  indeterminate?: () => boolean
21
23
  onChange?: (checked: boolean) => void
22
24
  disabled?: () => boolean
25
+ // Editor contract (ARIA + validation) - folded into boxProps().
26
+ id?: () => string | undefined
27
+ invalid?: () => boolean
28
+ required?: () => boolean
29
+ error?: () => string | undefined
30
+ hint?: () => string | undefined
23
31
  ariaLabel?: () => string | undefined
24
32
  }
25
33
 
26
34
  export type CheckboxBoxProps = {
27
35
  type: 'button'
28
36
  role: 'checkbox'
37
+ id: string | undefined
29
38
  'aria-checked': boolean | 'mixed'
39
+ 'aria-invalid': 'true' | undefined
40
+ 'aria-required': 'true' | undefined
41
+ 'aria-describedby': string | undefined
30
42
  'aria-label': string | undefined
31
43
  disabled: boolean
32
44
  'data-checked': '' | undefined
@@ -55,6 +67,15 @@ export function createCheckbox(config: CheckboxConfig): Checkbox {
55
67
  config.onChange?.(indeterminate() ? true : !checked())
56
68
  }
57
69
 
70
+ const ariaState = (): EditorAriaState => ({
71
+ id: config.id?.(),
72
+ invalid: config.invalid?.(),
73
+ required: config.required?.(),
74
+ error: config.error?.(),
75
+ hint: config.hint?.(),
76
+ ariaLabel: config.ariaLabel?.(),
77
+ })
78
+
58
79
  return {
59
80
  get checked() { return checked() },
60
81
  get indeterminate() { return indeterminate() },
@@ -63,11 +84,12 @@ export function createCheckbox(config: CheckboxConfig): Checkbox {
63
84
  boxProps: () => ({
64
85
  type: 'button',
65
86
  role: 'checkbox',
87
+ id: config.id?.(),
66
88
  'aria-checked': indeterminate() ? 'mixed' : checked(),
67
- 'aria-label': config.ariaLabel?.(),
68
89
  disabled: disabled(),
69
90
  'data-checked': checked() && !indeterminate() ? '' : undefined,
70
91
  'data-indeterminate': indeterminate() ? '' : undefined,
92
+ ...editorAria(ariaState()),
71
93
  onclick: toggle,
72
94
  }),
73
95
  }
@@ -17,6 +17,7 @@
17
17
  * The styled <SvColorInput> is just one renderer over this core (it adds the
18
18
  * portal + anchored positioning).
19
19
  */
20
+ import { editorAria, type EditorAriaState } from './editor-contract'
20
21
 
21
22
  export const DEFAULT_PALETTE = [
22
23
  '#ef4444', '#f97316', '#f59e0b', '#eab308', '#84cc16', '#22c55e', '#10b981', '#14b8a6',
@@ -39,6 +40,12 @@ export type ColorInputConfig = {
39
40
  disabled?: () => boolean
40
41
  readonly?: () => boolean
41
42
  palette?: () => string[]
43
+ // Editor contract (ARIA + validation) - folded into swatchProps().
44
+ id?: () => string | undefined
45
+ invalid?: () => boolean
46
+ required?: () => boolean
47
+ error?: () => string | undefined
48
+ hint?: () => string | undefined
42
49
  ariaLabel?: () => string | undefined
43
50
  }
44
51
 
@@ -71,6 +78,15 @@ export function createColorInput(config: ColorInputConfig) {
71
78
  }
72
79
  const isActive = (c: string) => normalizeHex(c) === normalized
73
80
 
81
+ const ariaState = (): EditorAriaState => ({
82
+ id: config.id?.(),
83
+ invalid: config.invalid?.(),
84
+ required: config.required?.(),
85
+ error: config.error?.(),
86
+ hint: config.hint?.(),
87
+ ariaLabel: config.ariaLabel?.(),
88
+ })
89
+
74
90
  // Popover control surface (open state + show/close/toggle), no DOM measurement.
75
91
  const popover = {
76
92
  get open() { return open },
@@ -96,9 +112,13 @@ export function createColorInput(config: ColorInputConfig) {
96
112
  isActive,
97
113
  /** Spread onto the swatch trigger button. */
98
114
  swatchProps: () => ({
115
+ id: config.id?.(),
99
116
  type: 'button' as const,
100
117
  'aria-haspopup': 'dialog' as const,
101
118
  'aria-expanded': open,
119
+ // aria-invalid/required/describedby come from the editor contract; the
120
+ // aria-label default names the swatch by its current color when unset.
121
+ ...editorAria(ariaState()),
102
122
  'aria-label': config.ariaLabel?.() ?? `Color ${normalized}`,
103
123
  disabled: !isInteractive,
104
124
  onclick: toggle,
@@ -20,6 +20,7 @@
20
20
  * ```
21
21
  */
22
22
  import { filterOptions, type ListOption } from './list-option'
23
+ import { editorAria, type EditorAriaState } from './editor-contract'
23
24
 
24
25
  export type ComboboxValue = string | number | null
25
26
 
@@ -30,9 +31,17 @@ export type ComboboxConfig = {
30
31
  onChange?: (value: ComboboxValue) => void
31
32
  disabled?: () => boolean
32
33
  ariaLabel?: () => string | undefined
34
+ /** Filter the options locally by the query. Set false for server-side search. */
35
+ localFilter?: () => boolean
33
36
  /** DOM focus hooks provided by the renderer; the core itself never touches the DOM. */
34
37
  focusInput?: () => void
35
38
  blurInput?: () => void
39
+ // Editor contract (ARIA + validation) - folded into inputProps().
40
+ id?: () => string | undefined
41
+ invalid?: () => boolean
42
+ required?: () => boolean
43
+ error?: () => string | undefined
44
+ hint?: () => string | undefined
36
45
  }
37
46
 
38
47
  let uid = 0
@@ -48,8 +57,9 @@ export function createCombobox(config: ComboboxConfig) {
48
57
  let active = $state(0)
49
58
  let editing = $state(false)
50
59
 
60
+ const localFilter = () => config.localFilter?.() ?? true
51
61
  const selected = $derived(opts().find((o) => o.value === config.value()) ?? null)
52
- const filtered = $derived(editing ? filterOptions(opts(), query) : [...opts()])
62
+ const filtered = $derived(editing && localFilter() ? filterOptions(opts(), query) : [...opts()])
53
63
  const shownText = $derived(editing ? query : selected?.label ?? '')
54
64
 
55
65
  // While not actively editing, keep the shown text in sync with the selection.
@@ -57,6 +67,15 @@ export function createCombobox(config: ComboboxConfig) {
57
67
 
58
68
  const optionId = (i: number) => `${listId}-opt-${i}`
59
69
 
70
+ const ariaState = (): EditorAriaState => ({
71
+ id: config.id?.(),
72
+ invalid: config.invalid?.(),
73
+ required: config.required?.(),
74
+ error: config.error?.(),
75
+ hint: config.hint?.(),
76
+ ariaLabel: config.ariaLabel?.(),
77
+ })
78
+
60
79
  function openPanel() { if (disabled() || open) return; open = true; active = 0 }
61
80
  function close(revert = true) {
62
81
  open = false; editing = false
@@ -114,11 +133,12 @@ export function createCombobox(config: ComboboxConfig) {
114
133
  onKeydown,
115
134
  /** Spread onto the editable <input>. */
116
135
  inputProps: () => ({
136
+ id: config.id?.(),
117
137
  role: 'combobox' as const,
118
138
  'aria-expanded': open,
119
139
  'aria-controls': listId,
120
140
  'aria-autocomplete': 'list' as const,
121
- 'aria-label': config.ariaLabel?.(),
141
+ ...editorAria(ariaState()),
122
142
  value: shownText,
123
143
  disabled: disabled(),
124
144
  oninput: onInput,
@@ -21,6 +21,7 @@
21
21
  * ```
22
22
  */
23
23
  import { COUNTRIES, COUNTRY_BY_CODE, type Country } from './countries'
24
+ import { editorAria, type EditorAriaState } from './editor-contract'
24
25
 
25
26
  /** Reactive inputs are passed as getters so the core tracks live prop changes. */
26
27
  export type CountryInputConfig = {
@@ -28,13 +29,30 @@ export type CountryInputConfig = {
28
29
  onChange?: (code: string) => void
29
30
  disabled?: () => boolean
30
31
  ariaLabel?: () => string | undefined
32
+ /** Accessible label for the in-panel search field (localizable). */
33
+ searchLabel?: () => string | undefined
31
34
  /** DOM focus hook provided by the renderer; the core never touches the DOM. */
32
35
  focusTrigger?: () => void
36
+ // Editor contract (ARIA + validation) - folded into triggerProps().
37
+ id?: () => string | undefined
38
+ invalid?: () => boolean
39
+ required?: () => boolean
40
+ error?: () => string | undefined
41
+ hint?: () => string | undefined
33
42
  }
34
43
 
35
44
  export function createCountryInput(config: CountryInputConfig) {
36
45
  const disabled = () => config.disabled?.() ?? false
37
46
 
47
+ const ariaState = (): EditorAriaState => ({
48
+ id: config.id?.(),
49
+ invalid: config.invalid?.(),
50
+ required: config.required?.(),
51
+ error: config.error?.(),
52
+ hint: config.hint?.(),
53
+ ariaLabel: config.ariaLabel?.(),
54
+ })
55
+
38
56
  let open = $state(false)
39
57
  let query = $state('')
40
58
  let active = $state(0)
@@ -89,10 +107,11 @@ export function createCountryInput(config: CountryInputConfig) {
89
107
  onKeydown,
90
108
  /** Spread onto the trigger <button>. */
91
109
  triggerProps: () => ({
110
+ id: config.id?.(),
92
111
  type: 'button' as const,
93
112
  'aria-haspopup': 'listbox' as const,
94
113
  'aria-expanded': open,
95
- 'aria-label': config.ariaLabel?.(),
114
+ ...editorAria(ariaState()),
96
115
  disabled: disabled(),
97
116
  onclick: toggle,
98
117
  }),
@@ -100,7 +119,7 @@ export function createCountryInput(config: CountryInputConfig) {
100
119
  searchProps: () => ({
101
120
  type: 'text' as const,
102
121
  value: query,
103
- 'aria-label': 'Search countries',
122
+ 'aria-label': config.searchLabel?.() ?? 'Search countries',
104
123
  oninput: onSearchInput,
105
124
  onkeydown: onKeydown,
106
125
  }),