@svgrid/grid 1.2.33 → 2.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (490) hide show
  1. package/README.md +6 -5
  2. package/dist/FlexRender.svelte +96 -96
  3. package/dist/GridFooter.svelte +178 -178
  4. package/dist/SvAccordion.svelte +109 -0
  5. package/dist/SvAccordion.svelte.d.ts +28 -0
  6. package/dist/SvAlert.svelte +92 -0
  7. package/dist/SvAlert.svelte.d.ts +27 -0
  8. package/dist/SvAutoComplete.svelte +47 -18
  9. package/dist/SvAutoComplete.svelte.d.ts +2 -5
  10. package/dist/SvAvatar.svelte +75 -0
  11. package/dist/SvAvatar.svelte.d.ts +14 -0
  12. package/dist/SvAvatarGroup.svelte +48 -0
  13. package/dist/SvAvatarGroup.svelte.d.ts +16 -0
  14. package/dist/SvBadge.svelte +54 -0
  15. package/dist/SvBadge.svelte.d.ts +23 -0
  16. package/dist/SvBreadcrumb.svelte +88 -0
  17. package/dist/SvBreadcrumb.svelte.d.ts +23 -0
  18. package/dist/SvButtonGroup.svelte +121 -0
  19. package/dist/SvButtonGroup.svelte.d.ts +16 -0
  20. package/dist/SvCalendar.svelte +72 -9
  21. package/dist/SvCalendar.svelte.d.ts +34 -1
  22. package/dist/SvCard.svelte +71 -0
  23. package/dist/SvCard.svelte.d.ts +28 -0
  24. package/dist/SvCarousel.svelte +134 -0
  25. package/dist/SvCarousel.svelte.d.ts +28 -0
  26. package/dist/SvCheckBox.svelte +36 -21
  27. package/dist/SvCheckBox.svelte.d.ts +2 -5
  28. package/dist/SvChip.svelte +88 -0
  29. package/dist/SvChip.svelte.d.ts +31 -0
  30. package/dist/SvCircularProgress.svelte +109 -0
  31. package/dist/SvCircularProgress.svelte.d.ts +31 -0
  32. package/dist/SvColorInput.svelte +54 -27
  33. package/dist/SvColorInput.svelte.d.ts +10 -6
  34. package/dist/SvComboBox.svelte +119 -33
  35. package/dist/SvComboBox.svelte.d.ts +18 -6
  36. package/dist/SvCommand.svelte +197 -0
  37. package/dist/SvCommand.svelte.d.ts +26 -0
  38. package/dist/SvContextMenu.svelte +96 -0
  39. package/dist/SvContextMenu.svelte.d.ts +25 -0
  40. package/dist/SvCountryInput.svelte +56 -21
  41. package/dist/SvCountryInput.svelte.d.ts +9 -5
  42. package/dist/SvDateRangeInput.svelte +256 -0
  43. package/dist/SvDateRangeInput.svelte.d.ts +37 -0
  44. package/dist/SvDateTimePicker.svelte +57 -20
  45. package/dist/SvDateTimePicker.svelte.d.ts +12 -4
  46. package/dist/SvDivider.svelte +61 -0
  47. package/dist/SvDivider.svelte.d.ts +22 -0
  48. package/dist/SvDrawer.svelte +226 -0
  49. package/dist/SvDrawer.svelte.d.ts +43 -0
  50. package/dist/SvDropDownList.svelte +114 -35
  51. package/dist/SvDropDownList.svelte.d.ts +7 -6
  52. package/dist/SvDurationInput.svelte +108 -0
  53. package/dist/SvDurationInput.svelte.d.ts +15 -0
  54. package/dist/SvEmptyState.svelte +59 -0
  55. package/dist/SvEmptyState.svelte.d.ts +25 -0
  56. package/dist/SvField.svelte +85 -0
  57. package/dist/SvField.svelte.d.ts +30 -0
  58. package/dist/SvFileUpload.svelte +169 -0
  59. package/dist/SvFileUpload.svelte.d.ts +24 -0
  60. package/dist/SvForm.svelte +7 -1
  61. package/dist/SvForm.svelte.d.ts +3 -0
  62. package/dist/SvGauge.svelte +7 -1
  63. package/dist/SvGauge.svelte.d.ts +11 -0
  64. package/dist/SvGrid.controller.svelte.d.ts +6 -0
  65. package/dist/SvGrid.controller.svelte.js +64 -9
  66. package/dist/SvGrid.css +2376 -2343
  67. package/dist/SvGrid.svelte +2764 -2651
  68. package/dist/SvGrid.types.d.ts +366 -0
  69. package/dist/SvGridBoard.svelte +2281 -0
  70. package/dist/SvGridBoard.svelte.d.ts +31 -0
  71. package/dist/SvGridDropdown.svelte +666 -665
  72. package/dist/SvGridSelect.svelte +255 -0
  73. package/dist/SvGridSelect.svelte.d.ts +32 -0
  74. package/dist/SvGroupCell.svelte +109 -0
  75. package/dist/SvGroupCell.svelte.d.ts +41 -0
  76. package/dist/SvListBox.svelte +123 -36
  77. package/dist/SvListBox.svelte.d.ts +12 -8
  78. package/dist/SvMaskedInput.svelte +53 -13
  79. package/dist/SvMaskedInput.svelte.d.ts +16 -6
  80. package/dist/SvMenu.svelte +101 -0
  81. package/dist/SvMenu.svelte.d.ts +26 -0
  82. package/dist/SvMenuList.svelte +143 -0
  83. package/dist/SvMenuList.svelte.d.ts +24 -0
  84. package/dist/SvModal.svelte +220 -0
  85. package/dist/SvModal.svelte.d.ts +31 -0
  86. package/dist/SvMultiSelect.svelte +277 -0
  87. package/dist/SvMultiSelect.svelte.d.ts +31 -0
  88. package/dist/SvNavPane.svelte +314 -0
  89. package/dist/SvNavPane.svelte.d.ts +59 -0
  90. package/dist/SvNumberInput.svelte +23 -9
  91. package/dist/SvNumberInput.svelte.d.ts +2 -0
  92. package/dist/SvOtpInput.svelte +157 -0
  93. package/dist/SvOtpInput.svelte.d.ts +16 -0
  94. package/dist/SvPagination.svelte +100 -0
  95. package/dist/SvPagination.svelte.d.ts +16 -0
  96. package/dist/SvPasswordInput.svelte +42 -12
  97. package/dist/SvPasswordInput.svelte.d.ts +13 -6
  98. package/dist/SvPhoneInput.svelte +43 -11
  99. package/dist/SvPhoneInput.svelte.d.ts +10 -11
  100. package/dist/SvPopover.svelte +127 -0
  101. package/dist/SvPopover.svelte.d.ts +35 -0
  102. package/dist/SvProgress.svelte +116 -0
  103. package/dist/SvProgress.svelte.d.ts +32 -0
  104. package/dist/SvRadioGroup.svelte +40 -22
  105. package/dist/SvRadioGroup.svelte.d.ts +2 -10
  106. package/dist/SvRating.svelte +51 -30
  107. package/dist/SvRating.svelte.d.ts +10 -7
  108. package/dist/SvRichText.svelte +192 -0
  109. package/dist/SvRichText.svelte.d.ts +16 -0
  110. package/dist/SvRowGroupPanel.svelte +147 -0
  111. package/dist/SvRowGroupPanel.svelte.d.ts +17 -0
  112. package/dist/SvScrollArea.svelte +50 -0
  113. package/dist/SvScrollArea.svelte.d.ts +22 -0
  114. package/dist/SvSkeleton.svelte +76 -0
  115. package/dist/SvSkeleton.svelte.d.ts +25 -0
  116. package/dist/SvSlider.svelte +41 -9
  117. package/dist/SvSlider.svelte.d.ts +4 -4
  118. package/dist/SvSparkline.svelte +77 -0
  119. package/dist/SvSparkline.svelte.d.ts +28 -0
  120. package/dist/SvSplitter.svelte +138 -0
  121. package/dist/SvSplitter.svelte.d.ts +32 -0
  122. package/dist/SvStat.svelte +86 -0
  123. package/dist/SvStat.svelte.d.ts +30 -0
  124. package/dist/SvStepper.svelte +91 -0
  125. package/dist/SvStepper.svelte.d.ts +24 -0
  126. package/dist/SvSwitchButton.svelte +39 -24
  127. package/dist/SvSwitchButton.svelte.d.ts +2 -5
  128. package/dist/SvTabs.svelte +147 -28
  129. package/dist/SvTabs.svelte.d.ts +16 -4
  130. package/dist/SvTagsInput.svelte +59 -22
  131. package/dist/SvTagsInput.svelte.d.ts +9 -4
  132. package/dist/SvTextArea.svelte +113 -0
  133. package/dist/SvTextArea.svelte.d.ts +18 -0
  134. package/dist/SvTextInput.svelte +115 -0
  135. package/dist/SvTextInput.svelte.d.ts +20 -0
  136. package/dist/SvTimePicker.svelte +19 -5
  137. package/dist/SvTimePicker.svelte.d.ts +12 -0
  138. package/dist/SvTimeline.svelte +75 -0
  139. package/dist/SvTimeline.svelte.d.ts +24 -0
  140. package/dist/SvToaster.svelte +124 -0
  141. package/dist/SvToaster.svelte.d.ts +10 -0
  142. package/dist/SvToggleButton.svelte +37 -12
  143. package/dist/SvToggleButton.svelte.d.ts +2 -4
  144. package/dist/SvTooltip.svelte +113 -0
  145. package/dist/SvTooltip.svelte.d.ts +22 -0
  146. package/dist/SvTour.svelte +151 -0
  147. package/dist/SvTour.svelte.d.ts +25 -0
  148. package/dist/SvTree.svelte +286 -44
  149. package/dist/SvTree.svelte.d.ts +26 -9
  150. package/dist/SvTreeSelect.svelte +222 -0
  151. package/dist/SvTreeSelect.svelte.d.ts +24 -0
  152. package/dist/a11y/dismissable.d.ts +32 -0
  153. package/dist/a11y/dismissable.js +54 -0
  154. package/dist/a11y/focus-trap.d.ts +39 -0
  155. package/dist/a11y/focus-trap.js +142 -0
  156. package/dist/a11y/live-region.d.ts +8 -0
  157. package/dist/a11y/live-region.js +60 -0
  158. package/dist/a11y/scroll-lock.d.ts +4 -0
  159. package/dist/a11y/scroll-lock.js +41 -0
  160. package/dist/a11y.d.ts +3 -1
  161. package/dist/a11y.js +1 -1
  162. package/dist/avatar.d.ts +10 -0
  163. package/dist/avatar.js +22 -0
  164. package/dist/builtin-editors.d.ts +5 -0
  165. package/dist/builtin-editors.js +55 -0
  166. package/dist/cdn/svgrid.js +16022 -8235
  167. package/dist/cdn/svgrid.svelte-external.js +14543 -6765
  168. package/dist/columns.js +3 -2
  169. package/dist/core.d.ts +7 -1
  170. package/dist/countries.d.ts +12 -0
  171. package/dist/countries.js +24 -0
  172. package/dist/createAccordion.svelte.d.ts +71 -0
  173. package/dist/createAccordion.svelte.js +99 -0
  174. package/dist/createAutocomplete.svelte.d.ts +13 -4
  175. package/dist/createAutocomplete.svelte.js +11 -1
  176. package/dist/createButtonGroup.svelte.d.ts +62 -0
  177. package/dist/createButtonGroup.svelte.js +154 -0
  178. package/dist/createCalendar.svelte.d.ts +5 -0
  179. package/dist/createCalendar.svelte.js +2 -0
  180. package/dist/createCheckbox.svelte.d.ts +9 -17
  181. package/dist/createCheckbox.svelte.js +28 -1
  182. package/dist/createColorInput.svelte.d.ts +12 -22
  183. package/dist/createColorInput.svelte.js +13 -0
  184. package/dist/createCombobox.svelte.d.ts +16 -5
  185. package/dist/createCombobox.svelte.js +13 -2
  186. package/dist/createCountryInput.svelte.d.ts +14 -3
  187. package/dist/createCountryInput.svelte.js +12 -2
  188. package/dist/createDateTimePicker.svelte.d.ts +17 -2
  189. package/dist/createDateTimePicker.svelte.js +12 -2
  190. package/dist/createDropdownList.svelte.d.ts +14 -5
  191. package/dist/createDropdownList.svelte.js +50 -1
  192. package/dist/createFileUpload.svelte.d.ts +68 -0
  193. package/dist/createFileUpload.svelte.js +122 -0
  194. package/dist/createListbox.svelte.d.ts +10 -1
  195. package/dist/createListbox.svelte.js +48 -1
  196. package/dist/createMaskedInput.svelte.d.ts +11 -2
  197. package/dist/createMaskedInput.svelte.js +11 -1
  198. package/dist/createNumberInput.svelte.d.ts +1 -0
  199. package/dist/createNumberInput.svelte.js +1 -0
  200. package/dist/createPasswordInput.svelte.d.ts +19 -21
  201. package/dist/createPasswordInput.svelte.js +16 -4
  202. package/dist/createPhoneInput.svelte.d.ts +26 -7
  203. package/dist/createPhoneInput.svelte.js +24 -3
  204. package/dist/createPopoverSelect.svelte.d.ts +86 -0
  205. package/dist/createPopoverSelect.svelte.js +196 -0
  206. package/dist/createRadioGroup.svelte.d.ts +9 -0
  207. package/dist/createRadioGroup.svelte.js +11 -1
  208. package/dist/createRating.svelte.d.ts +11 -0
  209. package/dist/createRating.svelte.js +12 -1
  210. package/dist/createSlider.svelte.d.ts +2 -0
  211. package/dist/createSlider.svelte.js +16 -5
  212. package/dist/createSplitter.svelte.d.ts +67 -0
  213. package/dist/createSplitter.svelte.js +99 -0
  214. package/dist/createSwitch.svelte.d.ts +9 -16
  215. package/dist/createSwitch.svelte.js +28 -1
  216. package/dist/createTabs.svelte.d.ts +7 -0
  217. package/dist/createTabs.svelte.js +17 -2
  218. package/dist/createTagsInput.svelte.d.ts +13 -1
  219. package/dist/createTagsInput.svelte.js +12 -3
  220. package/dist/createToggle.svelte.d.ts +9 -0
  221. package/dist/createToggle.svelte.js +12 -1
  222. package/dist/createTree.svelte.d.ts +19 -0
  223. package/dist/createTree.svelte.js +108 -15
  224. package/dist/debounce.d.ts +11 -0
  225. package/dist/debounce.js +30 -0
  226. package/dist/duration.d.ts +15 -0
  227. package/dist/duration.js +51 -0
  228. package/dist/editor-contract.d.ts +62 -7
  229. package/dist/editor-contract.js +45 -5
  230. package/dist/editor-registry.d.ts +67 -0
  231. package/dist/editor-registry.js +38 -0
  232. package/dist/editors/cell-editors.d.ts +1 -1
  233. package/dist/editors/cell-editors.js +5 -0
  234. package/dist/fuzzy.d.ts +3 -0
  235. package/dist/fuzzy.js +41 -0
  236. package/dist/grid-select.d.ts +9 -0
  237. package/dist/grid-select.js +8 -0
  238. package/dist/index.d.ts +79 -6
  239. package/dist/index.js +78 -4
  240. package/dist/keyboard-handlers.js +19 -0
  241. package/dist/list-option.d.ts +36 -0
  242. package/dist/list-option.js +68 -0
  243. package/dist/otp.d.ts +16 -0
  244. package/dist/otp.js +26 -0
  245. package/dist/paginate.d.ts +20 -0
  246. package/dist/paginate.js +40 -0
  247. package/dist/popover.d.ts +15 -1
  248. package/dist/popover.js +29 -3
  249. package/dist/recurrence.d.ts +36 -0
  250. package/dist/recurrence.js +90 -0
  251. package/dist/server-data-source.d.ts +89 -0
  252. package/dist/server-data-source.js +4 -0
  253. package/dist/server-group-model.d.ts +98 -0
  254. package/dist/server-group-model.js +263 -0
  255. package/dist/toast-store.svelte.d.ts +41 -0
  256. package/dist/toast-store.svelte.js +113 -0
  257. package/dist/tree-select.d.ts +19 -0
  258. package/dist/tree-select.js +40 -0
  259. package/dist/ui-app.types.d.ts +80 -0
  260. package/dist/ui-app.types.js +7 -0
  261. package/dist/validators.d.ts +43 -0
  262. package/dist/validators.js +53 -0
  263. package/dist/virtual.d.ts +35 -0
  264. package/dist/virtual.js +34 -0
  265. package/package.json +85 -85
  266. package/src/FlexRender.svelte +96 -96
  267. package/src/GridFooter.svelte +178 -178
  268. package/src/SvAccordion.svelte +109 -0
  269. package/src/SvAccordion.test.ts +66 -0
  270. package/src/SvAlert.svelte +92 -0
  271. package/src/SvAutoComplete.svelte +47 -18
  272. package/src/SvAvatar.svelte +75 -0
  273. package/src/SvAvatarGroup.svelte +48 -0
  274. package/src/SvBadge.svelte +54 -0
  275. package/src/SvBreadcrumb.svelte +88 -0
  276. package/src/SvButtonGroup.svelte +121 -0
  277. package/src/SvButtonGroup.test.ts +72 -0
  278. package/src/SvCalendar.svelte +72 -9
  279. package/src/SvCard.svelte +71 -0
  280. package/src/SvCarousel.svelte +134 -0
  281. package/src/SvCheckBox.svelte +36 -21
  282. package/src/SvChip.svelte +88 -0
  283. package/src/SvCircularProgress.svelte +109 -0
  284. package/src/SvColorInput.svelte +54 -27
  285. package/src/SvComboBox.svelte +119 -33
  286. package/src/SvCommand.svelte +197 -0
  287. package/src/SvContextMenu.svelte +96 -0
  288. package/src/SvCountryInput.svelte +56 -21
  289. package/src/SvDateRangeInput.svelte +256 -0
  290. package/src/SvDateRangeInput.test.ts +80 -0
  291. package/src/SvDateTimePicker.svelte +57 -20
  292. package/src/SvDivider.svelte +61 -0
  293. package/src/SvDrawer.svelte +226 -0
  294. package/src/SvDrawer.test.ts +125 -0
  295. package/src/SvDropDownList.svelte +114 -35
  296. package/src/SvDurationInput.svelte +108 -0
  297. package/src/SvEmptyState.svelte +59 -0
  298. package/src/SvField.svelte +85 -0
  299. package/src/SvFileUpload.svelte +169 -0
  300. package/src/SvFileUpload.test.ts +59 -0
  301. package/src/SvForm.svelte +7 -1
  302. package/src/SvGauge.svelte +7 -1
  303. package/src/SvGrid.controller.svelte.ts +2553 -2494
  304. package/src/SvGrid.css +2376 -2343
  305. package/src/SvGrid.svelte +2764 -2651
  306. package/src/SvGrid.types.ts +944 -570
  307. package/src/SvGridBoard.svelte +2281 -0
  308. package/src/SvGridDropdown.svelte +666 -665
  309. package/src/SvGridSelect.svelte +255 -0
  310. package/src/SvGridSelect.test.ts +100 -0
  311. package/src/SvGroupCell.svelte +109 -0
  312. package/src/SvListBox.svelte +123 -36
  313. package/src/SvMaskedInput.svelte +53 -13
  314. package/src/SvMenu.svelte +101 -0
  315. package/src/SvMenu.test.ts +73 -0
  316. package/src/SvMenuList.svelte +143 -0
  317. package/src/SvModal.svelte +220 -0
  318. package/src/SvMultiSelect.svelte +277 -0
  319. package/src/SvMultiSelect.test.ts +167 -0
  320. package/src/SvNavPane.svelte +314 -0
  321. package/src/SvNavPane.test.ts +70 -0
  322. package/src/SvNumberInput.svelte +23 -9
  323. package/src/SvOtpInput.svelte +157 -0
  324. package/src/SvPagination.svelte +100 -0
  325. package/src/SvPasswordInput.svelte +42 -12
  326. package/src/SvPhoneInput.svelte +43 -11
  327. package/src/SvPopover.svelte +127 -0
  328. package/src/SvProgress.svelte +116 -0
  329. package/src/SvRadioGroup.svelte +40 -22
  330. package/src/SvRating.svelte +51 -30
  331. package/src/SvRichText.svelte +192 -0
  332. package/src/SvRowGroupPanel.svelte +147 -0
  333. package/src/SvScrollArea.svelte +50 -0
  334. package/src/SvSkeleton.svelte +76 -0
  335. package/src/SvSlider.svelte +41 -9
  336. package/src/SvSparkline.svelte +77 -0
  337. package/src/SvSplitter.svelte +138 -0
  338. package/src/SvSplitter.test.ts +55 -0
  339. package/src/SvStat.svelte +86 -0
  340. package/src/SvStepper.svelte +91 -0
  341. package/src/SvSwitchButton.svelte +39 -24
  342. package/src/SvTabs.svelte +147 -28
  343. package/src/SvTagsInput.svelte +59 -22
  344. package/src/SvTextArea.svelte +113 -0
  345. package/src/SvTextInput.svelte +115 -0
  346. package/src/SvTimePicker.svelte +19 -5
  347. package/src/SvTimeline.svelte +75 -0
  348. package/src/SvToaster.svelte +124 -0
  349. package/src/SvToaster.test.ts +63 -0
  350. package/src/SvToggleButton.svelte +37 -12
  351. package/src/SvTooltip.svelte +113 -0
  352. package/src/SvTour.svelte +151 -0
  353. package/src/SvTree.svelte +286 -44
  354. package/src/SvTreeSelect.svelte +222 -0
  355. package/src/SvTreeSelect.test.ts +87 -0
  356. package/src/a11y/dismissable.test.ts +95 -0
  357. package/src/a11y/dismissable.ts +94 -0
  358. package/src/a11y/focus-trap.test.ts +110 -0
  359. package/src/a11y/focus-trap.ts +134 -0
  360. package/src/a11y/live-region.test.ts +40 -0
  361. package/src/a11y/live-region.ts +65 -0
  362. package/src/a11y/scroll-lock.test.ts +38 -0
  363. package/src/a11y/scroll-lock.ts +41 -0
  364. package/src/a11y.contract.test.ts +49 -49
  365. package/src/a11y.test.ts +59 -59
  366. package/src/a11y.ts +61 -59
  367. package/src/avatar.test.ts +32 -0
  368. package/src/avatar.ts +21 -0
  369. package/src/board.test.ts +927 -0
  370. package/src/build-api.ts +798 -798
  371. package/src/builtin-editors.grid.test.ts +85 -0
  372. package/src/builtin-editors.test.ts +58 -0
  373. package/src/builtin-editors.ts +56 -0
  374. package/src/cell-formatting.ts +169 -169
  375. package/src/cell-render.ts +469 -469
  376. package/src/collaboration.test.ts +104 -104
  377. package/src/collaboration.ts +167 -167
  378. package/src/columns.ts +3 -2
  379. package/src/core.performance.test.ts +30 -30
  380. package/src/core.ts +1111 -1101
  381. package/src/countries.ts +25 -0
  382. package/src/createAccordion.svelte.ts +126 -0
  383. package/src/createAutocomplete.svelte.ts +18 -1
  384. package/src/createButtonGroup.svelte.ts +166 -0
  385. package/src/createCalendar.svelte.ts +6 -0
  386. package/src/createCheckbox.svelte.ts +23 -1
  387. package/src/createColorInput.svelte.ts +20 -0
  388. package/src/createCombobox.svelte.ts +22 -2
  389. package/src/createCountryInput.svelte.ts +21 -2
  390. package/src/createDateTimePicker.svelte.ts +23 -2
  391. package/src/createDropdownList.svelte.ts +31 -2
  392. package/src/createFileUpload.svelte.ts +144 -0
  393. package/src/createGrid.svelte.ts +42 -42
  394. package/src/createGrid.test.ts +10 -10
  395. package/src/createGridState.svelte.ts +17 -17
  396. package/src/createListbox.svelte.ts +35 -3
  397. package/src/createMaskedInput.svelte.ts +18 -1
  398. package/src/createNumberInput.svelte.ts +2 -0
  399. package/src/createPasswordInput.svelte.ts +31 -5
  400. package/src/createPhoneInput.svelte.ts +38 -4
  401. package/src/createPopoverSelect.svelte.ts +208 -0
  402. package/src/createRadioGroup.svelte.ts +23 -1
  403. package/src/createRating.svelte.ts +26 -1
  404. package/src/createSlider.svelte.ts +14 -3
  405. package/src/createSplitter.svelte.ts +119 -0
  406. package/src/createSwitch.svelte.ts +26 -3
  407. package/src/createTabs.svelte.ts +20 -3
  408. package/src/createTagsInput.svelte.ts +24 -3
  409. package/src/createToggle.svelte.ts +25 -2
  410. package/src/createTree.svelte.ts +104 -14
  411. package/src/debounce.test.ts +50 -0
  412. package/src/debounce.ts +41 -0
  413. package/src/duration.test.ts +41 -0
  414. package/src/duration.ts +51 -0
  415. package/src/editing.test.ts +859 -859
  416. package/src/editing.ts +675 -675
  417. package/src/editor-contract.ts +87 -8
  418. package/src/editor-registry.grid.test.ts +98 -0
  419. package/src/editor-registry.test.ts +86 -0
  420. package/src/editor-registry.ts +106 -0
  421. package/src/editors/cell-editors.ts +10 -1
  422. package/src/export-data-api.test.ts +126 -126
  423. package/src/export-format.test.ts +107 -107
  424. package/src/export-format.ts +598 -598
  425. package/src/flex-render.ts +3 -3
  426. package/src/fuzzy.test.ts +40 -0
  427. package/src/fuzzy.ts +39 -0
  428. package/src/grid-select.test.ts +28 -0
  429. package/src/grid-select.ts +20 -0
  430. package/src/index.ts +463 -318
  431. package/src/keyboard-handlers.ts +20 -0
  432. package/src/keyboard.test.ts +59 -59
  433. package/src/keyboard.ts +97 -97
  434. package/src/list-option.ts +74 -0
  435. package/src/list-power.test.ts +71 -0
  436. package/src/menus.ts +582 -582
  437. package/src/merge-objects.ts +48 -48
  438. package/src/otp.test.ts +28 -0
  439. package/src/otp.ts +28 -0
  440. package/src/overlays.test.ts +85 -0
  441. package/src/paginate.test.ts +41 -0
  442. package/src/paginate.ts +61 -0
  443. package/src/phone-valid.test.ts +26 -0
  444. package/src/popover.ts +31 -3
  445. package/src/progress.test.ts +55 -0
  446. package/src/recurrence.test.ts +91 -0
  447. package/src/recurrence.ts +111 -0
  448. package/src/render-component.ts +28 -28
  449. package/src/selection.test.ts +754 -754
  450. package/src/selection.ts +600 -600
  451. package/src/server-data-source.test.ts +289 -289
  452. package/src/server-data-source.ts +413 -311
  453. package/src/server-group-model.test.ts +294 -0
  454. package/src/server-group-model.ts +370 -0
  455. package/src/sparkline.test.ts +68 -68
  456. package/src/sparkline.ts +169 -169
  457. package/src/spreadsheet.test.ts +489 -489
  458. package/src/spreadsheet.ts +304 -304
  459. package/src/static-functions.ts +11 -11
  460. package/src/subscribe.ts +38 -38
  461. package/src/svgrid-wrapper.types.ts +439 -439
  462. package/src/svgrid.behavior.test.ts +706 -706
  463. package/src/svgrid.features.test.ts +157 -157
  464. package/src/svgrid.new-features.wrapper.test.ts +251 -251
  465. package/src/svgrid.responsive.test.ts +72 -0
  466. package/src/svgrid.wrapper.test.ts +40 -40
  467. package/src/toast-store.svelte.ts +148 -0
  468. package/src/toast-store.test.ts +86 -0
  469. package/src/tree-edit.test.ts +49 -0
  470. package/src/tree-filter.test.ts +56 -0
  471. package/src/tree-select.test.ts +48 -0
  472. package/src/tree-select.ts +62 -0
  473. package/src/ui-app.test.ts +130 -0
  474. package/src/ui-app.types.ts +88 -0
  475. package/src/ui-display.test.ts +79 -0
  476. package/src/ui-inputs.test.ts +1 -1
  477. package/src/ui-status.test.ts +141 -0
  478. package/src/ui-tier1.test.ts +142 -0
  479. package/src/ui-tier2.test.ts +124 -0
  480. package/src/ui-tier3.test.ts +139 -0
  481. package/src/validators.test.ts +58 -0
  482. package/src/validators.ts +105 -0
  483. package/src/virtual.test.ts +72 -0
  484. package/src/virtual.ts +67 -0
  485. package/src/virtualization/column-virtualizer.test.ts +27 -27
  486. package/src/virtualization/column-virtualizer.ts +30 -30
  487. package/src/virtualization/svelte-virtualizer.svelte.ts +26 -26
  488. package/src/virtualization/types.ts +30 -30
  489. package/src/virtualization/virtualizer.test.ts +47 -47
  490. package/src/virtualization/virtualizer.ts +296 -296
@@ -0,0 +1,134 @@
1
+ /**
2
+ * focus-trap - keep Tab / Shift+Tab focus inside a container while it is active,
3
+ * move focus in on activate, and restore it on release. Shared by SvModal,
4
+ * SvDrawer, SvMenu and any dialog-like overlay so the WAI-ARIA dialog focus
5
+ * behaviour lives in ONE place instead of being re-hand-rolled per component.
6
+ *
7
+ * Framework-free (DOM only): call `createFocusTrap(el).activate()` from a
8
+ * component `$effect` and `release()` from its cleanup.
9
+ *
10
+ * ```ts
11
+ * $effect(() => {
12
+ * if (!open || !panel) return
13
+ * const trap = createFocusTrap(panel)
14
+ * trap.activate()
15
+ * return () => trap.release()
16
+ * })
17
+ * ```
18
+ */
19
+
20
+ /** Selector for elements that can, in principle, hold keyboard focus. */
21
+ export const FOCUSABLE_SELECTOR = [
22
+ 'a[href]',
23
+ 'area[href]',
24
+ 'button:not([disabled])',
25
+ 'input:not([disabled])',
26
+ 'select:not([disabled])',
27
+ 'textarea:not([disabled])',
28
+ 'iframe',
29
+ 'audio[controls]',
30
+ 'video[controls]',
31
+ '[contenteditable]:not([contenteditable="false"])',
32
+ '[tabindex]',
33
+ ].join(',')
34
+
35
+ /**
36
+ * Whether `el` is visible enough to be Tab-focusable. Detects the common,
37
+ * layout-independent ways of hiding (disabled / hidden attr / aria-hidden /
38
+ * inline display:none|visibility:hidden) so the check is deterministic in both
39
+ * real browsers and jsdom. Class-based `display:none` in a real browser is
40
+ * additionally caught via client rects when layout is available.
41
+ */
42
+ function isVisible(el: HTMLElement): boolean {
43
+ if (el.hasAttribute('disabled')) return false
44
+ if (el.getAttribute('aria-hidden') === 'true') return false
45
+ if (el.hidden || el.closest('[hidden]')) return false
46
+ const style = el.style
47
+ if (style.display === 'none' || style.visibility === 'hidden') return false
48
+ // Only trust layout when the environment actually lays out (real browsers).
49
+ if (typeof el.getClientRects === 'function') {
50
+ const rects = el.getClientRects()
51
+ // jsdom reports 0 rects for everything, so 0 alone is not "hidden" there;
52
+ // treat 0 rects as hidden only when SOME element in the doc has layout.
53
+ if (rects.length === 0 && document.body.getClientRects().length > 0) return false
54
+ }
55
+ return true
56
+ }
57
+
58
+ /** Visible, Tab-focusable descendants of `container`, in DOM order. Elements
59
+ * with `tabindex="-1"` are excluded (script-focusable, not Tab-reachable). */
60
+ export function getFocusable(container: HTMLElement): HTMLElement[] {
61
+ const nodes = Array.from(container.querySelectorAll<HTMLElement>(FOCUSABLE_SELECTOR))
62
+ return nodes.filter((el) => {
63
+ const ti = el.getAttribute('tabindex')
64
+ if (ti !== null && Number(ti) < 0) return false
65
+ return isVisible(el)
66
+ })
67
+ }
68
+
69
+ export type FocusTrapOptions = {
70
+ /** Where focus goes on activate: the first focusable (default), the container
71
+ * itself, a specific element, or a getter resolved at activate time. */
72
+ initialFocus?: 'first' | 'container' | HTMLElement | (() => HTMLElement | null)
73
+ /** Restore focus to the previously-focused element on release. Default true. */
74
+ returnFocus?: boolean
75
+ /** Called on Escape while active. Dismissal is usually owned by the layer
76
+ * manager, so most consumers leave this unset. */
77
+ onEscape?: (e: KeyboardEvent) => void
78
+ }
79
+
80
+ export type FocusTrap = { activate: () => void; release: () => void }
81
+
82
+ /** Create a focus trap for `container`. Inert until `activate()`. */
83
+ export function createFocusTrap(
84
+ container: HTMLElement,
85
+ options: FocusTrapOptions = {},
86
+ ): FocusTrap {
87
+ const { initialFocus = 'first', returnFocus = true } = options
88
+ let previouslyFocused: HTMLElement | null = null
89
+ let active = false
90
+
91
+ function resolveInitial(): HTMLElement | null {
92
+ const f = initialFocus
93
+ if (f === 'container') return container
94
+ if (f === 'first') return getFocusable(container)[0] ?? container
95
+ if (typeof f === 'function') return f()
96
+ return f
97
+ }
98
+
99
+ function onKeydown(e: KeyboardEvent) {
100
+ if (!active) return
101
+ if (e.key === 'Escape' && options.onEscape) { options.onEscape(e); return }
102
+ if (e.key !== 'Tab') return
103
+ const f = getFocusable(container)
104
+ if (!f.length) { e.preventDefault(); container.focus(); return }
105
+ const first = f[0]!
106
+ const last = f[f.length - 1]!
107
+ const activeEl = document.activeElement
108
+ // Focus escaped the container entirely (programmatic blur, etc.): pull back.
109
+ if (!container.contains(activeEl)) { e.preventDefault(); first.focus(); return }
110
+ if (e.shiftKey && activeEl === first) { e.preventDefault(); last.focus() }
111
+ else if (!e.shiftKey && activeEl === last) { e.preventDefault(); first.focus() }
112
+ }
113
+
114
+ function activate() {
115
+ if (active) return
116
+ active = true
117
+ previouslyFocused = document.activeElement as HTMLElement | null
118
+ // Guarantee the container is a focus fallback even without focusable children.
119
+ if (!container.hasAttribute('tabindex')) container.setAttribute('tabindex', '-1')
120
+ document.addEventListener('keydown', onKeydown, true)
121
+ // Focus on the next microtask so a just-opened panel is in the DOM first.
122
+ queueMicrotask(() => { if (active) resolveInitial()?.focus() })
123
+ }
124
+
125
+ function release() {
126
+ if (!active) return
127
+ active = false
128
+ document.removeEventListener('keydown', onKeydown, true)
129
+ if (returnFocus) previouslyFocused?.focus?.()
130
+ previouslyFocused = null
131
+ }
132
+
133
+ return { activate, release }
134
+ }
@@ -0,0 +1,40 @@
1
+ import { afterEach, describe, expect, it } from 'vitest'
2
+ import { _resetLiveRegions, announce } from './live-region'
3
+
4
+ afterEach(() => {
5
+ _resetLiveRegions()
6
+ })
7
+
8
+ describe('announce', () => {
9
+ it('creates a polite status region and sets its text', async () => {
10
+ announce('3 rows selected')
11
+ const region = document.querySelector('[aria-live="polite"]')
12
+ expect(region).not.toBeNull()
13
+ expect(region!.getAttribute('role')).toBe('status')
14
+ await Promise.resolve()
15
+ expect(region!.textContent).toBe('3 rows selected')
16
+ })
17
+
18
+ it('creates a separate assertive alert region', async () => {
19
+ announce('error!', { assertive: true })
20
+ const region = document.querySelector('[aria-live="assertive"]')
21
+ expect(region).not.toBeNull()
22
+ expect(region!.getAttribute('role')).toBe('alert')
23
+ await Promise.resolve()
24
+ expect(region!.textContent).toBe('error!')
25
+ })
26
+
27
+ it('reuses the same region across calls', async () => {
28
+ announce('first')
29
+ announce('second')
30
+ const regions = document.querySelectorAll('[aria-live="polite"]')
31
+ expect(regions).toHaveLength(1)
32
+ await Promise.resolve()
33
+ expect(regions[0]!.textContent).toBe('second')
34
+ })
35
+
36
+ it('ignores empty messages', () => {
37
+ announce('')
38
+ expect(document.querySelector('[aria-live]')).toBeNull()
39
+ })
40
+ })
@@ -0,0 +1,65 @@
1
+ /**
2
+ * live-region - a single shared pair of visually-hidden ARIA live regions
3
+ * (polite + assertive) appended to <body> on first use, so any component can
4
+ * push a transient message to screen readers via `announce()` without each one
5
+ * shipping its own live region. Used for things like "3 rows selected",
6
+ * "copied", "no results".
7
+ */
8
+ let politeEl: HTMLElement | null = null
9
+ let assertiveEl: HTMLElement | null = null
10
+
11
+ function make(mode: 'polite' | 'assertive'): HTMLElement {
12
+ const el = document.createElement('div')
13
+ el.setAttribute('aria-live', mode)
14
+ el.setAttribute('aria-atomic', 'true')
15
+ el.setAttribute('role', mode === 'assertive' ? 'alert' : 'status')
16
+ // Visually hidden, but still exposed to assistive technology.
17
+ Object.assign(el.style, {
18
+ position: 'absolute',
19
+ width: '1px',
20
+ height: '1px',
21
+ margin: '-1px',
22
+ border: '0',
23
+ padding: '0',
24
+ overflow: 'hidden',
25
+ clip: 'rect(0 0 0 0)',
26
+ clipPath: 'inset(50%)',
27
+ whiteSpace: 'nowrap',
28
+ })
29
+ document.body.appendChild(el)
30
+ return el
31
+ }
32
+
33
+ export type AnnounceOptions = {
34
+ /** Interrupt the user immediately (role="alert"). Default polite. */
35
+ assertive?: boolean
36
+ }
37
+
38
+ /** Announce `message` to screen readers. Empty messages are ignored. */
39
+ export function announce(message: string, options: AnnounceOptions = {}): void {
40
+ if (typeof document === 'undefined' || !message) return
41
+ const assertive = options.assertive ?? false
42
+ let el = assertive ? assertiveEl : politeEl
43
+ // Recreate when missing OR when a prior DOM reset detached the cached node,
44
+ // so a stale reference can never swallow an announcement silently.
45
+ if (!el || !el.isConnected) {
46
+ el = make(assertive ? 'assertive' : 'polite')
47
+ if (assertive) assertiveEl = el
48
+ else politeEl = el
49
+ }
50
+ // Clear, then set on the next microtask, so an identical consecutive message
51
+ // still triggers a fresh announcement.
52
+ el.textContent = ''
53
+ const target = el
54
+ queueMicrotask(() => {
55
+ target.textContent = message
56
+ })
57
+ }
58
+
59
+ /** Test helper: tear the shared live regions down. */
60
+ export function _resetLiveRegions(): void {
61
+ politeEl?.remove()
62
+ assertiveEl?.remove()
63
+ politeEl = null
64
+ assertiveEl = null
65
+ }
@@ -0,0 +1,38 @@
1
+ import { afterEach, describe, expect, it } from 'vitest'
2
+ import { lockScroll, scrollLockDepth } from './scroll-lock'
3
+
4
+ afterEach(() => {
5
+ // Each test balances its own locks; just reset the inline styles.
6
+ document.body.style.overflow = ''
7
+ document.body.style.paddingRight = ''
8
+ })
9
+
10
+ describe('lockScroll', () => {
11
+ it('hides body overflow while locked and restores it on unlock', () => {
12
+ document.body.style.overflow = 'auto'
13
+ const unlock = lockScroll()
14
+ expect(document.body.style.overflow).toBe('hidden')
15
+ unlock()
16
+ expect(document.body.style.overflow).toBe('auto')
17
+ })
18
+
19
+ it('ref-counts nested locks: only the last unlock restores', () => {
20
+ document.body.style.overflow = 'scroll'
21
+ const a = lockScroll()
22
+ const b = lockScroll()
23
+ expect(scrollLockDepth()).toBe(2)
24
+ a()
25
+ expect(document.body.style.overflow).toBe('hidden') // still locked by b
26
+ b()
27
+ expect(document.body.style.overflow).toBe('scroll')
28
+ })
29
+
30
+ it('unlock is idempotent', () => {
31
+ document.body.style.overflow = 'auto'
32
+ const unlock = lockScroll()
33
+ unlock()
34
+ unlock()
35
+ expect(scrollLockDepth()).toBe(0)
36
+ expect(document.body.style.overflow).toBe('auto')
37
+ })
38
+ })
@@ -0,0 +1,41 @@
1
+ /**
2
+ * scroll-lock - stop the document scrolling behind an overlay, with REF-COUNTING
3
+ * so nested overlays (a dialog that opens a menu) never fight over
4
+ * `body.style.overflow`. The first lock records the original values; the last
5
+ * unlock restores them. Also pads for the vanishing scrollbar so the page
6
+ * doesn't shift when the lock engages.
7
+ */
8
+ let locks = 0
9
+ let previousOverflow = ''
10
+ let previousPaddingRight = ''
11
+
12
+ /** Lock document scroll. Returns an idempotent unlock function. */
13
+ export function lockScroll(): () => void {
14
+ if (typeof document === 'undefined') return () => {}
15
+ const body = document.body
16
+ if (locks === 0) {
17
+ previousOverflow = body.style.overflow
18
+ previousPaddingRight = body.style.paddingRight
19
+ // Compensate for the scrollbar width so fixed/full-width layout stays put.
20
+ const sbw = window.innerWidth - document.documentElement.clientWidth
21
+ if (sbw > 0) {
22
+ const current = parseFloat(getComputedStyle(body).paddingRight) || 0
23
+ body.style.paddingRight = `${current + sbw}px`
24
+ }
25
+ body.style.overflow = 'hidden'
26
+ }
27
+ locks++
28
+ let released = false
29
+ return () => {
30
+ if (released) return
31
+ released = true
32
+ locks = Math.max(0, locks - 1)
33
+ if (locks === 0) {
34
+ body.style.overflow = previousOverflow
35
+ body.style.paddingRight = previousPaddingRight
36
+ }
37
+ }
38
+ }
39
+
40
+ /** Test helper: current lock depth. */
41
+ export function scrollLockDepth(): number { return locks }
@@ -1,49 +1,49 @@
1
- import { describe, expect, it } from 'vitest'
2
- import {
3
- getGridCellA11yProps,
4
- getGridHeaderA11yProps,
5
- getGridRootA11yProps,
6
- getGridRowA11yProps,
7
- } from './a11y'
8
-
9
- function attrsToHtml(attrs: Record<string, string | number | boolean>) {
10
- return Object.entries(attrs)
11
- .map(([key, value]) => `${key}="${String(value)}"`)
12
- .join(' ')
13
- }
14
-
15
- describe('a11y contract rendering', () => {
16
- it('builds expected headless markup attributes', () => {
17
- const root = getGridRootA11yProps()
18
- const header = getGridHeaderA11yProps({
19
- sortable: true,
20
- sortDirection: 'descending',
21
- })
22
- const row = getGridRowA11yProps(1)
23
- const cell = getGridCellA11yProps({
24
- rowIndex: 1,
25
- colIndex: 2,
26
- selected: true,
27
- })
28
-
29
- const html = [
30
- `<table ${attrsToHtml(root)}><thead>`,
31
- `<tr ${attrsToHtml(row)}>`,
32
- `<th ${attrsToHtml(header)}>Age</th>`,
33
- `</tr></thead><tbody>`,
34
- `<tr ${attrsToHtml(row)}><td ${attrsToHtml(cell)}>42</td></tr>`,
35
- `</tbody></table>`,
36
- ].join('')
37
-
38
- expect(html).toContain('role="grid"')
39
- expect(html).toContain('role="columnheader"')
40
- expect(html).toContain('aria-sort="descending"')
41
- expect(html).toContain('aria-selected="true"')
42
- expect(html).toContain('aria-colindex="2"')
43
- })
44
-
45
- it('creates stable active descendant ids', async () => {
46
- const { getGridCellDomId } = await import('./a11y')
47
- expect(getGridCellDomId('svgrid', 3, 4)).toBe('svgrid_cell_3_4')
48
- })
49
- })
1
+ import { describe, expect, it } from 'vitest'
2
+ import {
3
+ getGridCellA11yProps,
4
+ getGridHeaderA11yProps,
5
+ getGridRootA11yProps,
6
+ getGridRowA11yProps,
7
+ } from './a11y'
8
+
9
+ function attrsToHtml(attrs: Record<string, string | number | boolean>) {
10
+ return Object.entries(attrs)
11
+ .map(([key, value]) => `${key}="${String(value)}"`)
12
+ .join(' ')
13
+ }
14
+
15
+ describe('a11y contract rendering', () => {
16
+ it('builds expected headless markup attributes', () => {
17
+ const root = getGridRootA11yProps()
18
+ const header = getGridHeaderA11yProps({
19
+ sortable: true,
20
+ sortDirection: 'descending',
21
+ })
22
+ const row = getGridRowA11yProps(1)
23
+ const cell = getGridCellA11yProps({
24
+ rowIndex: 1,
25
+ colIndex: 2,
26
+ selected: true,
27
+ })
28
+
29
+ const html = [
30
+ `<table ${attrsToHtml(root)}><thead>`,
31
+ `<tr ${attrsToHtml(row)}>`,
32
+ `<th ${attrsToHtml(header)}>Age</th>`,
33
+ `</tr></thead><tbody>`,
34
+ `<tr ${attrsToHtml(row)}><td ${attrsToHtml(cell)}>42</td></tr>`,
35
+ `</tbody></table>`,
36
+ ].join('')
37
+
38
+ expect(html).toContain('role="grid"')
39
+ expect(html).toContain('role="columnheader"')
40
+ expect(html).toContain('aria-sort="descending"')
41
+ expect(html).toContain('aria-selected="true"')
42
+ expect(html).toContain('aria-colindex="2"')
43
+ })
44
+
45
+ it('creates stable active descendant ids', async () => {
46
+ const { getGridCellDomId } = await import('./a11y')
47
+ expect(getGridCellDomId('svgrid', 3, 4)).toBe('svgrid_cell_3_4')
48
+ })
49
+ })
package/src/a11y.test.ts CHANGED
@@ -1,59 +1,59 @@
1
- import { describe, expect, it } from 'vitest'
2
- import {
3
- getGridCellA11yProps,
4
- getGridHeaderA11yProps,
5
- getGridRootA11yProps,
6
- getGridRowA11yProps,
7
- } from './a11y'
8
-
9
- describe('a11y helpers', () => {
10
- it('returns grid role props', () => {
11
- expect(
12
- getGridRootA11yProps({
13
- activeDescendantId: 'svgrid_cell_0_0',
14
- rowCount: 10,
15
- colCount: 5,
16
- }),
17
- ).toEqual({
18
- role: 'grid',
19
- tabindex: 0,
20
- 'aria-activedescendant': 'svgrid_cell_0_0',
21
- 'aria-rowcount': 10,
22
- 'aria-colcount': 5,
23
- })
24
- })
25
-
26
- it('returns header sort semantics', () => {
27
- expect(
28
- getGridHeaderA11yProps({
29
- sortable: true,
30
- sortDirection: 'ascending',
31
- }),
32
- ).toEqual({
33
- role: 'columnheader',
34
- 'aria-sort': 'ascending',
35
- })
36
- })
37
-
38
- it('returns row and cell indices for accessibility metadata', () => {
39
- expect(getGridRowA11yProps(2)).toEqual({
40
- role: 'row',
41
- 'aria-rowindex': 2,
42
- })
43
-
44
- expect(
45
- getGridCellA11yProps({
46
- id: 'svgrid_cell_2_3',
47
- rowIndex: 2,
48
- colIndex: 3,
49
- selected: true,
50
- }),
51
- ).toEqual({
52
- role: 'gridcell',
53
- id: 'svgrid_cell_2_3',
54
- 'aria-colindex': 3,
55
- 'aria-rowindex': 2,
56
- 'aria-selected': true,
57
- })
58
- })
59
- })
1
+ import { describe, expect, it } from 'vitest'
2
+ import {
3
+ getGridCellA11yProps,
4
+ getGridHeaderA11yProps,
5
+ getGridRootA11yProps,
6
+ getGridRowA11yProps,
7
+ } from './a11y'
8
+
9
+ describe('a11y helpers', () => {
10
+ it('returns grid role props', () => {
11
+ expect(
12
+ getGridRootA11yProps({
13
+ activeDescendantId: 'svgrid_cell_0_0',
14
+ rowCount: 10,
15
+ colCount: 5,
16
+ }),
17
+ ).toEqual({
18
+ role: 'grid',
19
+ tabindex: 0,
20
+ 'aria-activedescendant': 'svgrid_cell_0_0',
21
+ 'aria-rowcount': 10,
22
+ 'aria-colcount': 5,
23
+ })
24
+ })
25
+
26
+ it('returns header sort semantics', () => {
27
+ expect(
28
+ getGridHeaderA11yProps({
29
+ sortable: true,
30
+ sortDirection: 'ascending',
31
+ }),
32
+ ).toEqual({
33
+ role: 'columnheader',
34
+ 'aria-sort': 'ascending',
35
+ })
36
+ })
37
+
38
+ it('returns row and cell indices for accessibility metadata', () => {
39
+ expect(getGridRowA11yProps(2)).toEqual({
40
+ role: 'row',
41
+ 'aria-rowindex': 2,
42
+ })
43
+
44
+ expect(
45
+ getGridCellA11yProps({
46
+ id: 'svgrid_cell_2_3',
47
+ rowIndex: 2,
48
+ colIndex: 3,
49
+ selected: true,
50
+ }),
51
+ ).toEqual({
52
+ role: 'gridcell',
53
+ id: 'svgrid_cell_2_3',
54
+ 'aria-colindex': 3,
55
+ 'aria-rowindex': 2,
56
+ 'aria-selected': true,
57
+ })
58
+ })
59
+ })
package/src/a11y.ts CHANGED
@@ -1,59 +1,61 @@
1
- export type GridSortDirection = 'ascending' | 'descending' | 'none' | 'other'
2
-
3
- export type GridColumnA11yInput = {
4
- sortable?: boolean
5
- sortDirection?: GridSortDirection
6
- }
7
-
8
- export type GridCellA11yInput = {
9
- colIndex?: number
10
- rowIndex?: number
11
- selected?: boolean
12
- id?: string
13
- }
14
-
15
- export function getGridRootA11yProps(input: {
16
- activeDescendantId?: string | null
17
- rowCount?: number
18
- colCount?: number
19
- } = {}) {
20
- return {
21
- role: 'grid',
22
- tabindex: 0,
23
- ...(input.activeDescendantId
24
- ? { 'aria-activedescendant': input.activeDescendantId }
25
- : {}),
26
- ...(input.rowCount !== undefined ? { 'aria-rowcount': input.rowCount } : {}),
27
- ...(input.colCount !== undefined ? { 'aria-colcount': input.colCount } : {}),
28
- } as const
29
- }
30
-
31
- export function getGridHeaderA11yProps(input: GridColumnA11yInput = {}) {
32
- const { sortable = false, sortDirection = 'none' } = input
33
- return {
34
- role: 'columnheader',
35
- ...(sortable ? { 'aria-sort': sortDirection } : {}),
36
- } as const
37
- }
38
-
39
- export function getGridRowA11yProps(rowIndex?: number) {
40
- return {
41
- role: 'row',
42
- ...(rowIndex !== undefined ? { 'aria-rowindex': rowIndex } : {}),
43
- } as const
44
- }
45
-
46
- export function getGridCellA11yProps(input: GridCellA11yInput = {}) {
47
- const { colIndex, rowIndex, selected = false, id } = input
48
- return {
49
- role: 'gridcell',
50
- ...(id ? { id } : {}),
51
- ...(colIndex !== undefined ? { 'aria-colindex': colIndex } : {}),
52
- ...(rowIndex !== undefined ? { 'aria-rowindex': rowIndex } : {}),
53
- 'aria-selected': selected,
54
- } as const
55
- }
56
-
57
- export function getGridCellDomId(baseId: string, rowIndex: number, colIndex: number) {
58
- return `${baseId}_cell_${rowIndex}_${colIndex}`
59
- }
1
+ export type GridSortDirection = 'ascending' | 'descending' | 'none' | 'other'
2
+
3
+ export type GridColumnA11yInput = {
4
+ sortable?: boolean
5
+ sortDirection?: GridSortDirection
6
+ }
7
+
8
+ export type GridCellA11yInput = {
9
+ colIndex?: number
10
+ rowIndex?: number
11
+ selected?: boolean
12
+ id?: string
13
+ }
14
+
15
+ export function getGridRootA11yProps(input: {
16
+ activeDescendantId?: string | null
17
+ rowCount?: number
18
+ colCount?: number
19
+ /** Use the treegrid role (expandable server group / tree rows are present). */
20
+ treegrid?: boolean
21
+ } = {}) {
22
+ return {
23
+ role: input.treegrid ? 'treegrid' : 'grid',
24
+ tabindex: 0,
25
+ ...(input.activeDescendantId
26
+ ? { 'aria-activedescendant': input.activeDescendantId }
27
+ : {}),
28
+ ...(input.rowCount !== undefined ? { 'aria-rowcount': input.rowCount } : {}),
29
+ ...(input.colCount !== undefined ? { 'aria-colcount': input.colCount } : {}),
30
+ } as const
31
+ }
32
+
33
+ export function getGridHeaderA11yProps(input: GridColumnA11yInput = {}) {
34
+ const { sortable = false, sortDirection = 'none' } = input
35
+ return {
36
+ role: 'columnheader',
37
+ ...(sortable ? { 'aria-sort': sortDirection } : {}),
38
+ } as const
39
+ }
40
+
41
+ export function getGridRowA11yProps(rowIndex?: number) {
42
+ return {
43
+ role: 'row',
44
+ ...(rowIndex !== undefined ? { 'aria-rowindex': rowIndex } : {}),
45
+ } as const
46
+ }
47
+
48
+ export function getGridCellA11yProps(input: GridCellA11yInput = {}) {
49
+ const { colIndex, rowIndex, selected = false, id } = input
50
+ return {
51
+ role: 'gridcell',
52
+ ...(id ? { id } : {}),
53
+ ...(colIndex !== undefined ? { 'aria-colindex': colIndex } : {}),
54
+ ...(rowIndex !== undefined ? { 'aria-rowindex': rowIndex } : {}),
55
+ 'aria-selected': selected,
56
+ } as const
57
+ }
58
+
59
+ export function getGridCellDomId(baseId: string, rowIndex: number, colIndex: number) {
60
+ return `${baseId}_cell_${rowIndex}_${colIndex}`
61
+ }