@svgrid/grid 1.2.34 → 2.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (444) hide show
  1. package/README.md +6 -5
  2. package/dist/SvAccordion.svelte +109 -0
  3. package/dist/SvAccordion.svelte.d.ts +28 -0
  4. package/dist/SvAlert.svelte +92 -0
  5. package/dist/SvAlert.svelte.d.ts +27 -0
  6. package/dist/SvAutoComplete.svelte +47 -18
  7. package/dist/SvAutoComplete.svelte.d.ts +2 -5
  8. package/dist/SvAvatar.svelte +75 -0
  9. package/dist/SvAvatar.svelte.d.ts +14 -0
  10. package/dist/SvAvatarGroup.svelte +48 -0
  11. package/dist/SvAvatarGroup.svelte.d.ts +16 -0
  12. package/dist/SvBadge.svelte +54 -0
  13. package/dist/SvBadge.svelte.d.ts +23 -0
  14. package/dist/SvBreadcrumb.svelte +88 -0
  15. package/dist/SvBreadcrumb.svelte.d.ts +23 -0
  16. package/dist/SvButtonGroup.svelte +121 -0
  17. package/dist/SvButtonGroup.svelte.d.ts +16 -0
  18. package/dist/SvCalendar.svelte +72 -9
  19. package/dist/SvCalendar.svelte.d.ts +34 -1
  20. package/dist/SvCard.svelte +71 -0
  21. package/dist/SvCard.svelte.d.ts +28 -0
  22. package/dist/SvCarousel.svelte +134 -0
  23. package/dist/SvCarousel.svelte.d.ts +28 -0
  24. package/dist/SvCheckBox.svelte +36 -21
  25. package/dist/SvCheckBox.svelte.d.ts +2 -5
  26. package/dist/SvChip.svelte +88 -0
  27. package/dist/SvChip.svelte.d.ts +31 -0
  28. package/dist/SvCircularProgress.svelte +109 -0
  29. package/dist/SvCircularProgress.svelte.d.ts +31 -0
  30. package/dist/SvColorInput.svelte +54 -27
  31. package/dist/SvColorInput.svelte.d.ts +10 -6
  32. package/dist/SvComboBox.svelte +119 -33
  33. package/dist/SvComboBox.svelte.d.ts +18 -6
  34. package/dist/SvCommand.svelte +197 -0
  35. package/dist/SvCommand.svelte.d.ts +26 -0
  36. package/dist/SvContextMenu.svelte +96 -0
  37. package/dist/SvContextMenu.svelte.d.ts +25 -0
  38. package/dist/SvCountryInput.svelte +56 -21
  39. package/dist/SvCountryInput.svelte.d.ts +9 -5
  40. package/dist/SvDateRangeInput.svelte +256 -0
  41. package/dist/SvDateRangeInput.svelte.d.ts +37 -0
  42. package/dist/SvDateTimePicker.svelte +57 -20
  43. package/dist/SvDateTimePicker.svelte.d.ts +12 -4
  44. package/dist/SvDivider.svelte +61 -0
  45. package/dist/SvDivider.svelte.d.ts +22 -0
  46. package/dist/SvDrawer.svelte +226 -0
  47. package/dist/SvDrawer.svelte.d.ts +43 -0
  48. package/dist/SvDropDownList.svelte +114 -35
  49. package/dist/SvDropDownList.svelte.d.ts +7 -6
  50. package/dist/SvDurationInput.svelte +108 -0
  51. package/dist/SvDurationInput.svelte.d.ts +15 -0
  52. package/dist/SvEmptyState.svelte +59 -0
  53. package/dist/SvEmptyState.svelte.d.ts +25 -0
  54. package/dist/SvField.svelte +85 -0
  55. package/dist/SvField.svelte.d.ts +30 -0
  56. package/dist/SvFileUpload.svelte +169 -0
  57. package/dist/SvFileUpload.svelte.d.ts +24 -0
  58. package/dist/SvForm.svelte +7 -1
  59. package/dist/SvForm.svelte.d.ts +3 -0
  60. package/dist/SvGauge.svelte +7 -1
  61. package/dist/SvGauge.svelte.d.ts +11 -0
  62. package/dist/SvGrid.controller.svelte.d.ts +6 -0
  63. package/dist/SvGrid.controller.svelte.js +64 -9
  64. package/dist/SvGrid.css +33 -0
  65. package/dist/SvGrid.svelte +116 -3
  66. package/dist/SvGrid.types.d.ts +366 -0
  67. package/dist/SvGridBoard.svelte +2281 -0
  68. package/dist/SvGridBoard.svelte.d.ts +31 -0
  69. package/dist/SvGridDropdown.svelte +2 -1
  70. package/dist/SvGridSelect.svelte +255 -0
  71. package/dist/SvGridSelect.svelte.d.ts +32 -0
  72. package/dist/SvGroupCell.svelte +109 -0
  73. package/dist/SvGroupCell.svelte.d.ts +41 -0
  74. package/dist/SvListBox.svelte +123 -36
  75. package/dist/SvListBox.svelte.d.ts +12 -8
  76. package/dist/SvMaskedInput.svelte +53 -13
  77. package/dist/SvMaskedInput.svelte.d.ts +16 -6
  78. package/dist/SvMenu.svelte +101 -0
  79. package/dist/SvMenu.svelte.d.ts +26 -0
  80. package/dist/SvMenuList.svelte +143 -0
  81. package/dist/SvMenuList.svelte.d.ts +24 -0
  82. package/dist/SvModal.svelte +220 -0
  83. package/dist/SvModal.svelte.d.ts +31 -0
  84. package/dist/SvMultiSelect.svelte +277 -0
  85. package/dist/SvMultiSelect.svelte.d.ts +31 -0
  86. package/dist/SvNavPane.svelte +314 -0
  87. package/dist/SvNavPane.svelte.d.ts +59 -0
  88. package/dist/SvNumberInput.svelte +23 -9
  89. package/dist/SvNumberInput.svelte.d.ts +2 -0
  90. package/dist/SvOtpInput.svelte +157 -0
  91. package/dist/SvOtpInput.svelte.d.ts +16 -0
  92. package/dist/SvPagination.svelte +100 -0
  93. package/dist/SvPagination.svelte.d.ts +16 -0
  94. package/dist/SvPasswordInput.svelte +42 -12
  95. package/dist/SvPasswordInput.svelte.d.ts +13 -6
  96. package/dist/SvPhoneInput.svelte +43 -11
  97. package/dist/SvPhoneInput.svelte.d.ts +10 -11
  98. package/dist/SvPopover.svelte +127 -0
  99. package/dist/SvPopover.svelte.d.ts +35 -0
  100. package/dist/SvProgress.svelte +116 -0
  101. package/dist/SvProgress.svelte.d.ts +32 -0
  102. package/dist/SvRadioGroup.svelte +40 -22
  103. package/dist/SvRadioGroup.svelte.d.ts +2 -10
  104. package/dist/SvRating.svelte +51 -30
  105. package/dist/SvRating.svelte.d.ts +10 -7
  106. package/dist/SvRichText.svelte +192 -0
  107. package/dist/SvRichText.svelte.d.ts +16 -0
  108. package/dist/SvRowGroupPanel.svelte +147 -0
  109. package/dist/SvRowGroupPanel.svelte.d.ts +17 -0
  110. package/dist/SvScrollArea.svelte +50 -0
  111. package/dist/SvScrollArea.svelte.d.ts +22 -0
  112. package/dist/SvSkeleton.svelte +76 -0
  113. package/dist/SvSkeleton.svelte.d.ts +25 -0
  114. package/dist/SvSlider.svelte +41 -9
  115. package/dist/SvSlider.svelte.d.ts +4 -4
  116. package/dist/SvSparkline.svelte +77 -0
  117. package/dist/SvSparkline.svelte.d.ts +28 -0
  118. package/dist/SvSplitter.svelte +138 -0
  119. package/dist/SvSplitter.svelte.d.ts +32 -0
  120. package/dist/SvStat.svelte +86 -0
  121. package/dist/SvStat.svelte.d.ts +30 -0
  122. package/dist/SvStepper.svelte +91 -0
  123. package/dist/SvStepper.svelte.d.ts +24 -0
  124. package/dist/SvSwitchButton.svelte +39 -24
  125. package/dist/SvSwitchButton.svelte.d.ts +2 -5
  126. package/dist/SvTabs.svelte +147 -28
  127. package/dist/SvTabs.svelte.d.ts +16 -4
  128. package/dist/SvTagsInput.svelte +59 -22
  129. package/dist/SvTagsInput.svelte.d.ts +9 -4
  130. package/dist/SvTextArea.svelte +113 -0
  131. package/dist/SvTextArea.svelte.d.ts +18 -0
  132. package/dist/SvTextInput.svelte +115 -0
  133. package/dist/SvTextInput.svelte.d.ts +20 -0
  134. package/dist/SvTimePicker.svelte +19 -5
  135. package/dist/SvTimePicker.svelte.d.ts +12 -0
  136. package/dist/SvTimeline.svelte +75 -0
  137. package/dist/SvTimeline.svelte.d.ts +24 -0
  138. package/dist/SvToaster.svelte +124 -0
  139. package/dist/SvToaster.svelte.d.ts +10 -0
  140. package/dist/SvToggleButton.svelte +37 -12
  141. package/dist/SvToggleButton.svelte.d.ts +2 -4
  142. package/dist/SvTooltip.svelte +113 -0
  143. package/dist/SvTooltip.svelte.d.ts +22 -0
  144. package/dist/SvTour.svelte +151 -0
  145. package/dist/SvTour.svelte.d.ts +25 -0
  146. package/dist/SvTree.svelte +286 -44
  147. package/dist/SvTree.svelte.d.ts +26 -9
  148. package/dist/SvTreeSelect.svelte +222 -0
  149. package/dist/SvTreeSelect.svelte.d.ts +24 -0
  150. package/dist/a11y/dismissable.d.ts +32 -0
  151. package/dist/a11y/dismissable.js +54 -0
  152. package/dist/a11y/focus-trap.d.ts +39 -0
  153. package/dist/a11y/focus-trap.js +142 -0
  154. package/dist/a11y/live-region.d.ts +8 -0
  155. package/dist/a11y/live-region.js +60 -0
  156. package/dist/a11y/scroll-lock.d.ts +4 -0
  157. package/dist/a11y/scroll-lock.js +41 -0
  158. package/dist/a11y.d.ts +3 -1
  159. package/dist/a11y.js +1 -1
  160. package/dist/avatar.d.ts +10 -0
  161. package/dist/avatar.js +22 -0
  162. package/dist/builtin-editors.d.ts +5 -0
  163. package/dist/builtin-editors.js +55 -0
  164. package/dist/cdn/svgrid.js +16021 -8234
  165. package/dist/cdn/svgrid.svelte-external.js +14542 -6764
  166. package/dist/columns.js +3 -2
  167. package/dist/core.d.ts +7 -1
  168. package/dist/countries.d.ts +12 -0
  169. package/dist/countries.js +24 -0
  170. package/dist/createAccordion.svelte.d.ts +71 -0
  171. package/dist/createAccordion.svelte.js +99 -0
  172. package/dist/createAutocomplete.svelte.d.ts +13 -4
  173. package/dist/createAutocomplete.svelte.js +11 -1
  174. package/dist/createButtonGroup.svelte.d.ts +62 -0
  175. package/dist/createButtonGroup.svelte.js +154 -0
  176. package/dist/createCalendar.svelte.d.ts +5 -0
  177. package/dist/createCalendar.svelte.js +2 -0
  178. package/dist/createCheckbox.svelte.d.ts +9 -17
  179. package/dist/createCheckbox.svelte.js +28 -1
  180. package/dist/createColorInput.svelte.d.ts +12 -22
  181. package/dist/createColorInput.svelte.js +13 -0
  182. package/dist/createCombobox.svelte.d.ts +16 -5
  183. package/dist/createCombobox.svelte.js +13 -2
  184. package/dist/createCountryInput.svelte.d.ts +14 -3
  185. package/dist/createCountryInput.svelte.js +12 -2
  186. package/dist/createDateTimePicker.svelte.d.ts +17 -2
  187. package/dist/createDateTimePicker.svelte.js +12 -2
  188. package/dist/createDropdownList.svelte.d.ts +14 -5
  189. package/dist/createDropdownList.svelte.js +50 -1
  190. package/dist/createFileUpload.svelte.d.ts +68 -0
  191. package/dist/createFileUpload.svelte.js +122 -0
  192. package/dist/createListbox.svelte.d.ts +10 -1
  193. package/dist/createListbox.svelte.js +48 -1
  194. package/dist/createMaskedInput.svelte.d.ts +11 -2
  195. package/dist/createMaskedInput.svelte.js +11 -1
  196. package/dist/createNumberInput.svelte.d.ts +1 -0
  197. package/dist/createNumberInput.svelte.js +1 -0
  198. package/dist/createPasswordInput.svelte.d.ts +19 -21
  199. package/dist/createPasswordInput.svelte.js +16 -4
  200. package/dist/createPhoneInput.svelte.d.ts +26 -7
  201. package/dist/createPhoneInput.svelte.js +24 -3
  202. package/dist/createPopoverSelect.svelte.d.ts +86 -0
  203. package/dist/createPopoverSelect.svelte.js +196 -0
  204. package/dist/createRadioGroup.svelte.d.ts +9 -0
  205. package/dist/createRadioGroup.svelte.js +11 -1
  206. package/dist/createRating.svelte.d.ts +11 -0
  207. package/dist/createRating.svelte.js +12 -1
  208. package/dist/createSlider.svelte.d.ts +2 -0
  209. package/dist/createSlider.svelte.js +16 -5
  210. package/dist/createSplitter.svelte.d.ts +67 -0
  211. package/dist/createSplitter.svelte.js +99 -0
  212. package/dist/createSwitch.svelte.d.ts +9 -16
  213. package/dist/createSwitch.svelte.js +28 -1
  214. package/dist/createTabs.svelte.d.ts +7 -0
  215. package/dist/createTabs.svelte.js +17 -2
  216. package/dist/createTagsInput.svelte.d.ts +13 -1
  217. package/dist/createTagsInput.svelte.js +12 -3
  218. package/dist/createToggle.svelte.d.ts +9 -0
  219. package/dist/createToggle.svelte.js +12 -1
  220. package/dist/createTree.svelte.d.ts +19 -0
  221. package/dist/createTree.svelte.js +108 -15
  222. package/dist/debounce.d.ts +11 -0
  223. package/dist/debounce.js +30 -0
  224. package/dist/duration.d.ts +15 -0
  225. package/dist/duration.js +51 -0
  226. package/dist/editor-contract.d.ts +62 -7
  227. package/dist/editor-contract.js +45 -5
  228. package/dist/editor-registry.d.ts +67 -0
  229. package/dist/editor-registry.js +38 -0
  230. package/dist/editors/cell-editors.d.ts +1 -1
  231. package/dist/editors/cell-editors.js +5 -0
  232. package/dist/fuzzy.d.ts +3 -0
  233. package/dist/fuzzy.js +41 -0
  234. package/dist/grid-select.d.ts +9 -0
  235. package/dist/grid-select.js +8 -0
  236. package/dist/index.d.ts +79 -6
  237. package/dist/index.js +78 -4
  238. package/dist/keyboard-handlers.js +19 -0
  239. package/dist/list-option.d.ts +36 -0
  240. package/dist/list-option.js +68 -0
  241. package/dist/otp.d.ts +16 -0
  242. package/dist/otp.js +26 -0
  243. package/dist/paginate.d.ts +20 -0
  244. package/dist/paginate.js +40 -0
  245. package/dist/popover.d.ts +15 -1
  246. package/dist/popover.js +29 -3
  247. package/dist/recurrence.d.ts +36 -0
  248. package/dist/recurrence.js +90 -0
  249. package/dist/server-data-source.d.ts +89 -0
  250. package/dist/server-data-source.js +4 -0
  251. package/dist/server-group-model.d.ts +98 -0
  252. package/dist/server-group-model.js +263 -0
  253. package/dist/toast-store.svelte.d.ts +41 -0
  254. package/dist/toast-store.svelte.js +113 -0
  255. package/dist/tree-select.d.ts +19 -0
  256. package/dist/tree-select.js +40 -0
  257. package/dist/ui-app.types.d.ts +80 -0
  258. package/dist/ui-app.types.js +7 -0
  259. package/dist/validators.d.ts +43 -0
  260. package/dist/validators.js +53 -0
  261. package/dist/virtual.d.ts +35 -0
  262. package/dist/virtual.js +34 -0
  263. package/package.json +1 -1
  264. package/src/SvAccordion.svelte +109 -0
  265. package/src/SvAccordion.test.ts +66 -0
  266. package/src/SvAlert.svelte +92 -0
  267. package/src/SvAutoComplete.svelte +47 -18
  268. package/src/SvAvatar.svelte +75 -0
  269. package/src/SvAvatarGroup.svelte +48 -0
  270. package/src/SvBadge.svelte +54 -0
  271. package/src/SvBreadcrumb.svelte +88 -0
  272. package/src/SvButtonGroup.svelte +121 -0
  273. package/src/SvButtonGroup.test.ts +72 -0
  274. package/src/SvCalendar.svelte +72 -9
  275. package/src/SvCard.svelte +71 -0
  276. package/src/SvCarousel.svelte +134 -0
  277. package/src/SvCheckBox.svelte +36 -21
  278. package/src/SvChip.svelte +88 -0
  279. package/src/SvCircularProgress.svelte +109 -0
  280. package/src/SvColorInput.svelte +54 -27
  281. package/src/SvComboBox.svelte +119 -33
  282. package/src/SvCommand.svelte +197 -0
  283. package/src/SvContextMenu.svelte +96 -0
  284. package/src/SvCountryInput.svelte +56 -21
  285. package/src/SvDateRangeInput.svelte +256 -0
  286. package/src/SvDateRangeInput.test.ts +80 -0
  287. package/src/SvDateTimePicker.svelte +57 -20
  288. package/src/SvDivider.svelte +61 -0
  289. package/src/SvDrawer.svelte +226 -0
  290. package/src/SvDrawer.test.ts +125 -0
  291. package/src/SvDropDownList.svelte +114 -35
  292. package/src/SvDurationInput.svelte +108 -0
  293. package/src/SvEmptyState.svelte +59 -0
  294. package/src/SvField.svelte +85 -0
  295. package/src/SvFileUpload.svelte +169 -0
  296. package/src/SvFileUpload.test.ts +59 -0
  297. package/src/SvForm.svelte +7 -1
  298. package/src/SvGauge.svelte +7 -1
  299. package/src/SvGrid.controller.svelte.ts +68 -9
  300. package/src/SvGrid.css +33 -0
  301. package/src/SvGrid.svelte +116 -3
  302. package/src/SvGrid.types.ts +374 -0
  303. package/src/SvGridBoard.svelte +2281 -0
  304. package/src/SvGridDropdown.svelte +2 -1
  305. package/src/SvGridSelect.svelte +255 -0
  306. package/src/SvGridSelect.test.ts +100 -0
  307. package/src/SvGroupCell.svelte +109 -0
  308. package/src/SvListBox.svelte +123 -36
  309. package/src/SvMaskedInput.svelte +53 -13
  310. package/src/SvMenu.svelte +101 -0
  311. package/src/SvMenu.test.ts +73 -0
  312. package/src/SvMenuList.svelte +143 -0
  313. package/src/SvModal.svelte +220 -0
  314. package/src/SvMultiSelect.svelte +277 -0
  315. package/src/SvMultiSelect.test.ts +167 -0
  316. package/src/SvNavPane.svelte +314 -0
  317. package/src/SvNavPane.test.ts +70 -0
  318. package/src/SvNumberInput.svelte +23 -9
  319. package/src/SvOtpInput.svelte +157 -0
  320. package/src/SvPagination.svelte +100 -0
  321. package/src/SvPasswordInput.svelte +42 -12
  322. package/src/SvPhoneInput.svelte +43 -11
  323. package/src/SvPopover.svelte +127 -0
  324. package/src/SvProgress.svelte +116 -0
  325. package/src/SvRadioGroup.svelte +40 -22
  326. package/src/SvRating.svelte +51 -30
  327. package/src/SvRichText.svelte +192 -0
  328. package/src/SvRowGroupPanel.svelte +147 -0
  329. package/src/SvScrollArea.svelte +50 -0
  330. package/src/SvSkeleton.svelte +76 -0
  331. package/src/SvSlider.svelte +41 -9
  332. package/src/SvSparkline.svelte +77 -0
  333. package/src/SvSplitter.svelte +138 -0
  334. package/src/SvSplitter.test.ts +55 -0
  335. package/src/SvStat.svelte +86 -0
  336. package/src/SvStepper.svelte +91 -0
  337. package/src/SvSwitchButton.svelte +39 -24
  338. package/src/SvTabs.svelte +147 -28
  339. package/src/SvTagsInput.svelte +59 -22
  340. package/src/SvTextArea.svelte +113 -0
  341. package/src/SvTextInput.svelte +115 -0
  342. package/src/SvTimePicker.svelte +19 -5
  343. package/src/SvTimeline.svelte +75 -0
  344. package/src/SvToaster.svelte +124 -0
  345. package/src/SvToaster.test.ts +63 -0
  346. package/src/SvToggleButton.svelte +37 -12
  347. package/src/SvTooltip.svelte +113 -0
  348. package/src/SvTour.svelte +151 -0
  349. package/src/SvTree.svelte +286 -44
  350. package/src/SvTreeSelect.svelte +222 -0
  351. package/src/SvTreeSelect.test.ts +87 -0
  352. package/src/a11y/dismissable.test.ts +95 -0
  353. package/src/a11y/dismissable.ts +94 -0
  354. package/src/a11y/focus-trap.test.ts +110 -0
  355. package/src/a11y/focus-trap.ts +134 -0
  356. package/src/a11y/live-region.test.ts +40 -0
  357. package/src/a11y/live-region.ts +65 -0
  358. package/src/a11y/scroll-lock.test.ts +38 -0
  359. package/src/a11y/scroll-lock.ts +41 -0
  360. package/src/a11y.ts +3 -1
  361. package/src/avatar.test.ts +32 -0
  362. package/src/avatar.ts +21 -0
  363. package/src/board.test.ts +927 -0
  364. package/src/builtin-editors.grid.test.ts +85 -0
  365. package/src/builtin-editors.test.ts +58 -0
  366. package/src/builtin-editors.ts +56 -0
  367. package/src/columns.ts +3 -2
  368. package/src/core.ts +10 -0
  369. package/src/countries.ts +25 -0
  370. package/src/createAccordion.svelte.ts +126 -0
  371. package/src/createAutocomplete.svelte.ts +18 -1
  372. package/src/createButtonGroup.svelte.ts +166 -0
  373. package/src/createCalendar.svelte.ts +6 -0
  374. package/src/createCheckbox.svelte.ts +23 -1
  375. package/src/createColorInput.svelte.ts +20 -0
  376. package/src/createCombobox.svelte.ts +22 -2
  377. package/src/createCountryInput.svelte.ts +21 -2
  378. package/src/createDateTimePicker.svelte.ts +23 -2
  379. package/src/createDropdownList.svelte.ts +31 -2
  380. package/src/createFileUpload.svelte.ts +144 -0
  381. package/src/createListbox.svelte.ts +35 -3
  382. package/src/createMaskedInput.svelte.ts +18 -1
  383. package/src/createNumberInput.svelte.ts +2 -0
  384. package/src/createPasswordInput.svelte.ts +31 -5
  385. package/src/createPhoneInput.svelte.ts +38 -4
  386. package/src/createPopoverSelect.svelte.ts +208 -0
  387. package/src/createRadioGroup.svelte.ts +23 -1
  388. package/src/createRating.svelte.ts +26 -1
  389. package/src/createSlider.svelte.ts +14 -3
  390. package/src/createSplitter.svelte.ts +119 -0
  391. package/src/createSwitch.svelte.ts +26 -3
  392. package/src/createTabs.svelte.ts +20 -3
  393. package/src/createTagsInput.svelte.ts +24 -3
  394. package/src/createToggle.svelte.ts +25 -2
  395. package/src/createTree.svelte.ts +104 -14
  396. package/src/debounce.test.ts +50 -0
  397. package/src/debounce.ts +41 -0
  398. package/src/duration.test.ts +41 -0
  399. package/src/duration.ts +51 -0
  400. package/src/editor-contract.ts +87 -8
  401. package/src/editor-registry.grid.test.ts +98 -0
  402. package/src/editor-registry.test.ts +86 -0
  403. package/src/editor-registry.ts +106 -0
  404. package/src/editors/cell-editors.ts +10 -1
  405. package/src/fuzzy.test.ts +40 -0
  406. package/src/fuzzy.ts +39 -0
  407. package/src/grid-select.test.ts +28 -0
  408. package/src/grid-select.ts +20 -0
  409. package/src/index.ts +150 -5
  410. package/src/keyboard-handlers.ts +20 -0
  411. package/src/list-option.ts +74 -0
  412. package/src/list-power.test.ts +71 -0
  413. package/src/otp.test.ts +28 -0
  414. package/src/otp.ts +28 -0
  415. package/src/overlays.test.ts +85 -0
  416. package/src/paginate.test.ts +41 -0
  417. package/src/paginate.ts +61 -0
  418. package/src/phone-valid.test.ts +26 -0
  419. package/src/popover.ts +31 -3
  420. package/src/progress.test.ts +55 -0
  421. package/src/recurrence.test.ts +91 -0
  422. package/src/recurrence.ts +111 -0
  423. package/src/server-data-source.ts +102 -0
  424. package/src/server-group-model.test.ts +294 -0
  425. package/src/server-group-model.ts +370 -0
  426. package/src/svgrid.responsive.test.ts +72 -0
  427. package/src/toast-store.svelte.ts +148 -0
  428. package/src/toast-store.test.ts +86 -0
  429. package/src/tree-edit.test.ts +49 -0
  430. package/src/tree-filter.test.ts +56 -0
  431. package/src/tree-select.test.ts +48 -0
  432. package/src/tree-select.ts +62 -0
  433. package/src/ui-app.test.ts +130 -0
  434. package/src/ui-app.types.ts +88 -0
  435. package/src/ui-display.test.ts +79 -0
  436. package/src/ui-inputs.test.ts +1 -1
  437. package/src/ui-status.test.ts +141 -0
  438. package/src/ui-tier1.test.ts +142 -0
  439. package/src/ui-tier2.test.ts +124 -0
  440. package/src/ui-tier3.test.ts +139 -0
  441. package/src/validators.test.ts +58 -0
  442. package/src/validators.ts +105 -0
  443. package/src/virtual.test.ts +72 -0
  444. package/src/virtual.ts +67 -0
@@ -0,0 +1,35 @@
1
+ /**
2
+ * virtual - a tiny, framework-free fixed-row windowing helper shared by the
3
+ * selection controls so their option lists scale to tens of thousands of rows
4
+ * without rendering them all. The renderer measures the viewport + tracks
5
+ * scrollTop (DOM concerns) and passes them in; this returns the slice to render
6
+ * plus the top/bottom spacer heights that keep the scrollbar honest.
7
+ */
8
+ export type VirtualRange = {
9
+ /** First item index to render (inclusive). */
10
+ start: number;
11
+ /** Last item index to render (exclusive). */
12
+ end: number;
13
+ /** Spacer height above the rendered slice (px). */
14
+ padTop: number;
15
+ /** Spacer height below the rendered slice (px). */
16
+ padBottom: number;
17
+ /** Total scrollable height of all rows (px). */
18
+ totalHeight: number;
19
+ };
20
+ export type VirtualParams = {
21
+ scrollTop: number;
22
+ viewportHeight: number;
23
+ rowHeight: number;
24
+ count: number;
25
+ /** Extra rows rendered above/below the viewport to avoid blank flashes. */
26
+ overscan?: number;
27
+ };
28
+ /** Compute the visible window for a fixed-row-height list (pure). */
29
+ export declare function virtualRange(params: VirtualParams): VirtualRange;
30
+ /**
31
+ * The scrollTop that brings item `index` fully into a fixed-row viewport,
32
+ * scrolling the least amount (returns the current scrollTop if already visible).
33
+ * Pure - the renderer applies it to the element.
34
+ */
35
+ export declare function scrollToIndex(index: number, scrollTop: number, viewportHeight: number, rowHeight: number): number;
@@ -0,0 +1,34 @@
1
+ /** Compute the visible window for a fixed-row-height list (pure). */
2
+ export function virtualRange(params) {
3
+ const { scrollTop, viewportHeight, rowHeight, count } = params;
4
+ const overscan = params.overscan ?? 6;
5
+ const total = Math.max(0, count) * Math.max(0, rowHeight);
6
+ if (rowHeight <= 0 || count <= 0) {
7
+ return { start: 0, end: Math.max(0, count), padTop: 0, padBottom: 0, totalHeight: total };
8
+ }
9
+ const first = Math.floor(Math.max(0, scrollTop) / rowHeight);
10
+ const start = Math.max(0, first - overscan);
11
+ const visible = Math.max(1, Math.ceil(Math.max(0, viewportHeight) / rowHeight));
12
+ const end = Math.min(count, first + visible + overscan);
13
+ return {
14
+ start,
15
+ end,
16
+ padTop: start * rowHeight,
17
+ padBottom: Math.max(0, (count - end) * rowHeight),
18
+ totalHeight: total,
19
+ };
20
+ }
21
+ /**
22
+ * The scrollTop that brings item `index` fully into a fixed-row viewport,
23
+ * scrolling the least amount (returns the current scrollTop if already visible).
24
+ * Pure - the renderer applies it to the element.
25
+ */
26
+ export function scrollToIndex(index, scrollTop, viewportHeight, rowHeight) {
27
+ const top = index * rowHeight;
28
+ const bottom = top + rowHeight;
29
+ if (top < scrollTop)
30
+ return top;
31
+ if (bottom > scrollTop + viewportHeight)
32
+ return bottom - viewportHeight;
33
+ return scrollTop;
34
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@svgrid/grid",
3
- "version": "1.2.34",
3
+ "version": "2.0.1",
4
4
  "description": "SvGrid - a headless-first, Svelte 5-native data grid engine and render component.",
5
5
  "author": "jQWidgets <boikom@jqwidgets.com>",
6
6
  "license": "MIT",
@@ -0,0 +1,109 @@
1
+ <script lang="ts" module>
2
+ export type { AccordionItem, AccordionExpandMode } from './createAccordion.svelte'
3
+ </script>
4
+
5
+ <script lang="ts">
6
+ /**
7
+ * SvAccordion - a WAI-ARIA accordion (collapsible sections) with single- or
8
+ * multiple-expand and roving header focus (Up/Down/Home/End). Parity: Smart
9
+ * `smart-accordion`. Controlled via `expanded` + `onChange`; each panel's body
10
+ * is rendered through the `panel` snippet (receives the item).
11
+ *
12
+ * Behavior (expand state, keyboard, ARIA) lives in the headless `createAccordion`
13
+ * core; this component is one styled renderer over it.
14
+ */
15
+ import type { Snippet } from 'svelte'
16
+ import { type EditorDir } from './editor-contract'
17
+ import { createAccordion, type AccordionItem, type AccordionExpandMode } from './createAccordion.svelte'
18
+
19
+ type Props = {
20
+ items: ReadonlyArray<AccordionItem>
21
+ /** Expanded item ids (controlled). */
22
+ expanded?: string[]
23
+ onChange?: (expandedIds: string[]) => void
24
+ /** 'single' (at most one open) or 'multiple'. Default single. */
25
+ expandMode?: AccordionExpandMode
26
+ disabled?: boolean
27
+ dir?: EditorDir
28
+ /** Panel body for an item. */
29
+ panel?: Snippet<[AccordionItem]>
30
+ }
31
+
32
+ let {
33
+ items,
34
+ expanded = $bindable([]),
35
+ onChange,
36
+ expandMode = 'single',
37
+ disabled = false,
38
+ dir,
39
+ panel,
40
+ }: Props = $props()
41
+
42
+ const resolvedDir = $derived(dir === 'ltr' || dir === 'rtl' ? dir : undefined)
43
+
44
+ const acc = createAccordion({
45
+ items: () => items,
46
+ expanded: () => expanded,
47
+ onChange: (ids) => { expanded = ids; onChange?.(ids) },
48
+ expandMode: () => expandMode,
49
+ disabled: () => disabled,
50
+ dir: () => dir,
51
+ })
52
+
53
+ // DOM focus movement is a render concern: follow the core's roving focus header.
54
+ let rootEl: HTMLDivElement | null = null
55
+ let lastFocusTick = 0
56
+ $effect(() => {
57
+ if (acc.focusTick !== lastFocusTick) {
58
+ lastFocusTick = acc.focusTick
59
+ const i = acc.focusIndex
60
+ queueMicrotask(() => rootEl?.querySelector<HTMLElement>(`[data-idx="${i}"]`)?.focus())
61
+ }
62
+ })
63
+ </script>
64
+
65
+ <div bind:this={rootEl} class="sv-acc" class:is-disabled={disabled} dir={resolvedDir} {...acc.rootProps()}>
66
+ {#each items as item, i (item.id)}
67
+ {@const open = acc.isExpanded(item.id)}
68
+ <div class="sv-acc__item" class:is-open={open}>
69
+ <h3 class="sv-acc__heading">
70
+ <button class="sv-acc__header" {...acc.headerProps(i)}>
71
+ <svg class="sv-acc__chevron" viewBox="0 0 24 24" width="16" height="16" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="m9 18 6-6-6-6" /></svg>
72
+ <span class="sv-acc__label">{item.label}</span>
73
+ </button>
74
+ </h3>
75
+ {#if open}
76
+ <div class="sv-acc__panel" {...acc.panelProps(i)}>
77
+ <div class="sv-acc__body">{#if panel}{@render panel(item)}{/if}</div>
78
+ </div>
79
+ {/if}
80
+ </div>
81
+ {/each}
82
+ </div>
83
+
84
+ <style>
85
+ .sv-acc {
86
+ --_accent: var(--sg-accent, #2563eb);
87
+ --_border: var(--sg-border, #e2e8f0);
88
+ --_radius: var(--sg-radius, 8px);
89
+ display: flex; flex-direction: column; width: 100%;
90
+ background: var(--sg-bg, #fff); color: var(--sg-fg, #0f172a);
91
+ border: 1px solid var(--_border); border-radius: var(--_radius); overflow: hidden;
92
+ }
93
+ .sv-acc.is-disabled { opacity: 0.6; }
94
+ .sv-acc__item + .sv-acc__item { border-top: 1px solid var(--_border); }
95
+ .sv-acc__heading { margin: 0; font: inherit; }
96
+ .sv-acc__header {
97
+ display: flex; align-items: center; gap: 8px; width: 100%;
98
+ padding: 12px 14px; background: none; border: 0; font: inherit; font-size: 14px; font-weight: 600;
99
+ color: inherit; cursor: pointer; text-align: start;
100
+ }
101
+ .sv-acc__header:hover:not(:disabled) { background: var(--sg-row-hover-bg, #f1f5f9); }
102
+ .sv-acc__header:disabled { opacity: 0.5; cursor: not-allowed; }
103
+ .sv-acc__header:focus-visible { outline: 2px solid var(--sg-focus-ring, var(--_accent)); outline-offset: -2px; }
104
+ .sv-acc__chevron { flex: none; color: var(--sg-muted, #64748b); transition: transform 0.16s; }
105
+ .sv-acc__item.is-open .sv-acc__chevron { transform: rotate(90deg); }
106
+ .sv-acc[dir='rtl'] .sv-acc__item:not(.is-open) .sv-acc__chevron { transform: scaleX(-1); }
107
+ .sv-acc__label { flex: 1; }
108
+ .sv-acc__body { padding: 4px 14px 16px; font-size: 13.5px; line-height: 1.6; color: var(--sg-fg, #0f172a); }
109
+ </style>
@@ -0,0 +1,66 @@
1
+ /**
2
+ * Component tests for SvAccordion: single vs multiple expand, ARIA wiring
3
+ * (aria-expanded / aria-controls / region), and toggle behavior.
4
+ */
5
+ import { describe, expect, it } from 'vitest'
6
+ import { mount, unmount, flushSync } from 'svelte'
7
+ import { createRawSnippet } from 'svelte'
8
+ import SvAccordion from './SvAccordion.svelte'
9
+
10
+ const items = [
11
+ { id: 'a', label: 'First' },
12
+ { id: 'b', label: 'Second' },
13
+ { id: 'c', label: 'Third' },
14
+ ]
15
+
16
+ // A trivial panel snippet so the body renders.
17
+ const panel = createRawSnippet((item: () => { id: string; label: string }) => ({
18
+ render: () => `<p>body-${item().id}</p>`,
19
+ }))
20
+
21
+ function mountAcc(props: Record<string, unknown>) {
22
+ const target = document.createElement('div')
23
+ document.body.appendChild(target)
24
+ const app = mount(SvAccordion, { target, props: { panel, ...props } as any })
25
+ flushSync()
26
+ return { target, destroy: () => { unmount(app); target.remove() } }
27
+ }
28
+
29
+ describe('SvAccordion', () => {
30
+ it('renders headers with aria-expanded + aria-controls and shows only the open panel', () => {
31
+ const { target, destroy } = mountAcc({ items, expanded: ['a'] })
32
+ try {
33
+ const headers = [...target.querySelectorAll<HTMLButtonElement>('.sv-acc__header')]
34
+ expect(headers).toHaveLength(3)
35
+ expect(headers[0].getAttribute('aria-expanded')).toBe('true')
36
+ expect(headers[1].getAttribute('aria-expanded')).toBe('false')
37
+ const region = target.querySelector<HTMLElement>('.sv-acc__panel')!
38
+ expect(region.getAttribute('role')).toBe('region')
39
+ expect(region.getAttribute('aria-labelledby')).toBe(headers[0].id)
40
+ // Only one panel region is rendered (the open one).
41
+ expect(target.querySelectorAll('.sv-acc__panel')).toHaveLength(1)
42
+ } finally { destroy() }
43
+ })
44
+
45
+ it('single mode: opening one closes the previous', () => {
46
+ let got: string[] | undefined
47
+ const { target, destroy } = mountAcc({ items, expanded: ['a'], expandMode: 'single', onChange: (ids: string[]) => (got = ids) })
48
+ try {
49
+ const headers = target.querySelectorAll<HTMLButtonElement>('.sv-acc__header')
50
+ headers[1].click()
51
+ flushSync()
52
+ expect(got).toEqual(['b'])
53
+ } finally { destroy() }
54
+ })
55
+
56
+ it('multiple mode: toggling accumulates open panels', () => {
57
+ let got: string[] | undefined
58
+ const { target, destroy } = mountAcc({ items, expanded: ['a'], expandMode: 'multiple', onChange: (ids: string[]) => (got = ids) })
59
+ try {
60
+ const headers = target.querySelectorAll<HTMLButtonElement>('.sv-acc__header')
61
+ headers[2].click()
62
+ flushSync()
63
+ expect(got).toEqual(['a', 'c'])
64
+ } finally { destroy() }
65
+ })
66
+ })
@@ -0,0 +1,92 @@
1
+ <script lang="ts">
2
+ /**
3
+ * SvAlert - an inline, persistent contextual message (info / success / warning
4
+ * / danger / neutral) with an icon, optional title, dismiss button and trailing
5
+ * actions. Unlike SvToast (transient, floating), an alert stays in the layout.
6
+ * Theme-driven via `--sg-*` semantic tokens. Parity: Smart alert / message.
7
+ */
8
+ import type { Snippet } from 'svelte'
9
+
10
+ type Variant = 'info' | 'success' | 'warning' | 'danger' | 'neutral'
11
+
12
+ type Props = {
13
+ variant?: Variant
14
+ title?: string
15
+ /** Show a close (x) button. */
16
+ dismissible?: boolean
17
+ onDismiss?: () => void
18
+ /** Hide the leading status icon. */
19
+ hideIcon?: boolean
20
+ /** Softer, borderless tint vs the default bordered card. */
21
+ soft?: boolean
22
+ children?: Snippet
23
+ /** Trailing action buttons / links. */
24
+ actions?: Snippet
25
+ /** Custom leading icon (overrides the default glyph). */
26
+ icon?: Snippet
27
+ }
28
+
29
+ let {
30
+ variant = 'info',
31
+ title,
32
+ dismissible = false,
33
+ onDismiss,
34
+ hideIcon = false,
35
+ soft = false,
36
+ children,
37
+ actions,
38
+ icon,
39
+ }: Props = $props()
40
+
41
+ let open = $state(true)
42
+ function dismiss() { open = false; onDismiss?.() }
43
+ const glyph = (v: Variant) =>
44
+ v === 'success' ? '✓' : v === 'warning' ? '⚠' : v === 'danger' ? '✕' : v === 'neutral' ? '•' : 'ℹ'
45
+ </script>
46
+
47
+ {#if open}
48
+ <div
49
+ class="sv-alert sv-alert--{variant}"
50
+ class:is-soft={soft}
51
+ role={variant === 'danger' || variant === 'warning' ? 'alert' : 'status'}
52
+ >
53
+ {#if !hideIcon}
54
+ <span class="sv-alert__icon" aria-hidden="true">{#if icon}{@render icon()}{:else}{glyph(variant)}{/if}</span>
55
+ {/if}
56
+ <div class="sv-alert__body">
57
+ {#if title}<div class="sv-alert__title">{title}</div>{/if}
58
+ {#if children}<div class="sv-alert__msg">{@render children()}</div>{/if}
59
+ {#if actions}<div class="sv-alert__actions">{@render actions()}</div>{/if}
60
+ </div>
61
+ {#if dismissible}
62
+ <button type="button" class="sv-alert__x" aria-label="Dismiss" onclick={dismiss}>&times;</button>
63
+ {/if}
64
+ </div>
65
+ {/if}
66
+
67
+ <style>
68
+ .sv-alert {
69
+ --_c: var(--sg-info, #0891b2);
70
+ display: flex; align-items: flex-start; gap: 10px; box-sizing: border-box;
71
+ padding: 11px 12px; border-radius: 10px; font-size: 13px; line-height: 1.5;
72
+ background: var(--sg-bg, #fff); color: var(--sg-fg, #0f172a);
73
+ border: 1px solid color-mix(in srgb, var(--_c) 30%, var(--sg-border, #e2e8f0));
74
+ border-inline-start: 3px solid var(--_c);
75
+ }
76
+ .sv-alert.is-soft { border: 0; background: color-mix(in srgb, var(--_c) 12%, transparent); }
77
+ .sv-alert--info { --_c: var(--sg-info, #0891b2); }
78
+ .sv-alert--success { --_c: var(--sg-success, #16a34a); }
79
+ .sv-alert--warning { --_c: var(--sg-warning, #d97706); }
80
+ .sv-alert--danger { --_c: var(--sg-danger, #dc2626); }
81
+ .sv-alert--neutral { --_c: var(--sg-muted, #64748b); }
82
+ .sv-alert__icon { flex: none; width: 18px; height: 18px; display: grid; place-items: center; color: var(--_c); font-weight: 700; }
83
+ .sv-alert__body { flex: 1; min-width: 0; }
84
+ .sv-alert__title { font-weight: 650; margin-bottom: 1px; }
85
+ .sv-alert__msg { color: var(--sg-fg, #0f172a); overflow-wrap: anywhere; }
86
+ .sv-alert__actions { display: flex; gap: 8px; margin-top: 8px; }
87
+ .sv-alert__x {
88
+ flex: none; width: 22px; height: 22px; display: grid; place-items: center; margin: -2px -2px 0 0;
89
+ background: none; border: 0; color: var(--sg-muted, #64748b); cursor: pointer; border-radius: 5px; font-size: 17px; line-height: 1;
90
+ }
91
+ .sv-alert__x:hover { background: var(--sg-row-hover-bg, #f1f5f9); color: var(--sg-fg, #0f172a); }
92
+ </style>
@@ -7,24 +7,43 @@
7
7
  * One styled renderer over the headless `createAutocomplete` core; only
8
8
  * portal/measure render concerns live here.
9
9
  */
10
- import { anchoredRect, portalToBody, type AnchoredRect } from './popover'
10
+ import { anchoredRect, portalToBody, popIn, type AnchoredRect } from './popover'
11
+ import { createDismissableLayer } from './a11y/dismissable'
11
12
  import { type ListOption } from './list-option'
13
+ import SvField from './SvField.svelte'
14
+ import { nextEditorId, type SvEditorProps } from './editor-contract'
12
15
  import { createAutocomplete } from './createAutocomplete.svelte'
13
16
 
14
- type Props = {
17
+ type Props = SvEditorProps & {
15
18
  value?: string
16
19
  onChange?: (value: string) => void
17
20
  /** Suggestions - strings or {value,label} (label is shown, value inserted). */
18
21
  suggestions?: ReadonlyArray<string | ListOption>
19
22
  minChars?: number
20
23
  placeholder?: string
21
- disabled?: boolean
22
- name?: string
23
- size?: 'sm' | 'md' | 'lg'
24
- ariaLabel?: string
25
24
  }
26
25
 
27
- let { value = '', onChange, suggestions = [], minChars = 1, placeholder, disabled = false, name, size = 'md', ariaLabel }: Props = $props()
26
+ let {
27
+ value = '',
28
+ onChange,
29
+ suggestions = [],
30
+ minChars = 1,
31
+ placeholder,
32
+ disabled = false,
33
+ name,
34
+ size = 'md',
35
+ ariaLabel,
36
+ invalid = false,
37
+ required = false,
38
+ error,
39
+ label,
40
+ hint,
41
+ dir,
42
+ id,
43
+ }: Props = $props()
44
+
45
+ const autoId = nextEditorId('sv-ac')
46
+ const uid = $derived(id ?? autoId)
28
47
 
29
48
  let fieldEl = $state<HTMLInputElement | null>(null)
30
49
  let panelEl = $state<HTMLDivElement | null>(null)
@@ -38,6 +57,11 @@
38
57
  disabled: () => disabled,
39
58
  ariaLabel: () => ariaLabel,
40
59
  focusInput: () => fieldEl?.focus(),
60
+ id: () => uid,
61
+ invalid: () => invalid,
62
+ required: () => required,
63
+ error: () => error,
64
+ hint: () => hint,
41
65
  })
42
66
 
43
67
  function updatePos() {
@@ -50,22 +74,25 @@
50
74
  updatePos()
51
75
  const rp = () => updatePos()
52
76
  window.addEventListener('scroll', rp, true); window.addEventListener('resize', rp)
53
- const od = (e: PointerEvent) => { const t = e.target as Node | null; if (t && (fieldEl?.contains(t) || panelEl?.contains(t))) return; ac.close() }
54
- document.addEventListener('pointerdown', od, true)
55
- return () => { window.removeEventListener('scroll', rp, true); window.removeEventListener('resize', rp); document.removeEventListener('pointerdown', od, true) }
77
+ const layer = createDismissableLayer({ element: () => [fieldEl, panelEl], onDismiss: () => ac.close(), closeOnEscape: false })
78
+ layer.activate()
79
+ return () => { window.removeEventListener('scroll', rp, true); window.removeEventListener('resize', rp); layer.release() }
56
80
  })
57
81
  </script>
58
82
 
59
- <input
60
- bind:this={fieldEl}
61
- class="sv-ac sv-ac--{size}"
62
- type="text"
63
- {placeholder}
64
- {...ac.inputProps()}
65
- />
83
+ <SvField id={uid} {label} {hint} {error} {required} {dir}>
84
+ <input
85
+ bind:this={fieldEl}
86
+ class="sv-ac sv-ac--{size}"
87
+ class:is-invalid={invalid}
88
+ type="text"
89
+ {placeholder}
90
+ {...ac.inputProps()}
91
+ />
92
+ </SvField>
66
93
 
67
94
  {#if ac.open}
68
- <div bind:this={panelEl} class="sv-ddl__panel" use:portalToBody style:position="fixed" style:top={`${rect.top}px`} style:left={`${rect.left}px`} style:min-width={`${rect.width}px`} {...ac.listboxProps()}>
95
+ <div bind:this={panelEl} class="sv-ddl__panel" use:portalToBody use:popIn={{ up: rect.openUpward }} style:position="fixed" style:top={`${rect.top}px`} style:left={`${rect.left}px`} style:min-width={`${rect.width}px`} {...ac.listboxProps()}>
69
96
  {#each ac.filtered as opt, i (opt.value)}
70
97
  <!-- svelte-ignore a11y_click_events_have_key_events a11y_interactive_supports_focus -->
71
98
  <div class="sv-ddl__opt" class:is-active={ac.isActive(i)} {...ac.optionProps(i)}>{opt.label}</div>
@@ -86,4 +113,6 @@
86
113
  .sv-ac--md { height: 34px; font-size: 13px; }
87
114
  .sv-ac--lg { height: 40px; font-size: 15px; }
88
115
  .sv-ac:focus { border-color: var(--_accent); box-shadow: 0 0 0 2px color-mix(in srgb, var(--_accent) 22%, transparent); }
116
+ .sv-ac.is-invalid { border-color: var(--sg-danger, #dc2626); }
117
+ .sv-ac.is-invalid:focus { box-shadow: 0 0 0 2px color-mix(in srgb, var(--sg-danger, #dc2626) 22%, transparent); }
89
118
  </style>
@@ -0,0 +1,75 @@
1
+ <script lang="ts">
2
+ /**
3
+ * SvAvatar - a user/entity avatar: an image with an initials + colour-hash
4
+ * fallback (used when there is no `src` or the image fails to load), plus an
5
+ * optional presence status dot. Parity: Smart avatar.
6
+ *
7
+ * ```svelte
8
+ * <SvAvatar name="Ada Lovelace" src="/ada.jpg" status="online" />
9
+ * ```
10
+ */
11
+ import { avatarInitials, avatarColorHue } from './avatar'
12
+
13
+ type Props = {
14
+ name?: string
15
+ src?: string
16
+ alt?: string
17
+ /** Preset (sm 28 / md 36 / lg 48) or an explicit pixel size. */
18
+ size?: 'sm' | 'md' | 'lg' | number
19
+ shape?: 'circle' | 'square'
20
+ status?: 'online' | 'offline' | 'busy' | 'away'
21
+ /** Override the hash-derived fallback colour (any CSS colour). */
22
+ color?: string
23
+ }
24
+
25
+ let { name = '', src, alt, size = 'md', shape = 'circle', status, color }: Props = $props()
26
+
27
+ const px = $derived(typeof size === 'number' ? size : size === 'sm' ? 28 : size === 'lg' ? 48 : 36)
28
+ const initials = $derived(avatarInitials(name))
29
+ const bg = $derived(color ?? `hsl(${avatarColorHue(name || '?')} 62% 45%)`)
30
+ // Fall back to initials when the image errors.
31
+ let broken = $state(false)
32
+ const showImg = $derived(!!src && !broken)
33
+ </script>
34
+
35
+ <span
36
+ class="sv-avatar sv-avatar--{shape}"
37
+ style:width={`${px}px`}
38
+ style:height={`${px}px`}
39
+ style:font-size={`${Math.round(px * 0.4)}px`}
40
+ style:background={showImg ? undefined : bg}
41
+ role="img"
42
+ aria-label={alt ?? (name || 'avatar')}
43
+ title={name || undefined}
44
+ >
45
+ {#if showImg}
46
+ <img class="sv-avatar__img" src={src} alt={alt ?? name} onerror={() => (broken = true)} />
47
+ {:else if initials}
48
+ <span class="sv-avatar__initials" aria-hidden="true">{initials}</span>
49
+ {:else}
50
+ <svg class="sv-avatar__ph" viewBox="0 0 24 24" fill="currentColor" aria-hidden="true"><path d="M12 12a5 5 0 1 0 0-10 5 5 0 0 0 0 10Zm0 2c-4.4 0-8 2.5-8 5.5V22h16v-2.5C20 16.5 16.4 14 12 14Z" /></svg>
51
+ {/if}
52
+ {#if status}<span class="sv-avatar__status is-{status}" aria-hidden="true"></span>{/if}
53
+ </span>
54
+
55
+ <style>
56
+ .sv-avatar {
57
+ position: relative; display: inline-grid; place-items: center; overflow: visible;
58
+ color: #fff; font-weight: 600; line-height: 1; user-select: none; flex: none;
59
+ vertical-align: middle;
60
+ }
61
+ .sv-avatar--circle { border-radius: 50%; }
62
+ .sv-avatar--square { border-radius: 22%; }
63
+ .sv-avatar__img { width: 100%; height: 100%; object-fit: cover; border-radius: inherit; display: block; }
64
+ .sv-avatar--circle .sv-avatar__img, .sv-avatar--circle { }
65
+ .sv-avatar__initials { letter-spacing: 0.02em; }
66
+ .sv-avatar__ph { width: 62%; height: 62%; opacity: 0.9; }
67
+ .sv-avatar__status {
68
+ position: absolute; right: -1px; bottom: -1px; width: 28%; height: 28%; min-width: 8px; min-height: 8px;
69
+ border-radius: 50%; border: 2px solid var(--sg-bg, #fff);
70
+ }
71
+ .sv-avatar__status.is-online { background: var(--sg-success, #16a34a); }
72
+ .sv-avatar__status.is-offline { background: var(--sg-muted, #94a3b8); }
73
+ .sv-avatar__status.is-busy { background: var(--sg-danger, #dc2626); }
74
+ .sv-avatar__status.is-away { background: var(--sg-warning, #f59e0b); }
75
+ </style>
@@ -0,0 +1,48 @@
1
+ <script lang="ts">
2
+ /**
3
+ * SvAvatarGroup - overlapping stacked avatars with a "+N" overflow pill, for
4
+ * "assigned to" / "members" rows. Each entry is a set of SvAvatar props.
5
+ *
6
+ * ```svelte
7
+ * <SvAvatarGroup max={3} avatars={[{ name: 'Ada' }, { name: 'Alan' }, { name: 'Grace' }, { name: 'Ken' }]} />
8
+ * ```
9
+ */
10
+ import SvAvatar from './SvAvatar.svelte'
11
+
12
+ type AvatarProps = { name?: string; src?: string; alt?: string; color?: string }
13
+ type Size = 'sm' | 'md' | 'lg' | number
14
+
15
+ type Props = {
16
+ avatars: ReadonlyArray<AvatarProps>
17
+ /** Max avatars shown before collapsing to "+N". */
18
+ max?: number
19
+ size?: Size
20
+ }
21
+
22
+ let { avatars, max = 4, size = 'md' }: Props = $props()
23
+
24
+ const shown = $derived(avatars.slice(0, max))
25
+ const overflow = $derived(Math.max(0, avatars.length - max))
26
+ const px = $derived(typeof size === 'number' ? size : size === 'sm' ? 28 : size === 'lg' ? 48 : 36)
27
+ const overlap = $derived(Math.round(px * 0.28))
28
+ </script>
29
+
30
+ <div class="sv-avgroup" role="group" style:--ov={`${overlap}px`}>
31
+ {#each shown as a, i (i)}
32
+ <span class="sv-avgroup__it" style:z-index={shown.length - i}><SvAvatar {...a} {size} /></span>
33
+ {/each}
34
+ {#if overflow}
35
+ <span class="sv-avgroup__more" style:width={`${px}px`} style:height={`${px}px`} style:font-size={`${Math.round(px * 0.36)}px`}>+{overflow}</span>
36
+ {/if}
37
+ </div>
38
+
39
+ <style>
40
+ .sv-avgroup { display: inline-flex; align-items: center; }
41
+ .sv-avgroup__it { display: inline-flex; margin-inline-start: calc(-1 * var(--ov, 10px)); border-radius: 50%; box-shadow: 0 0 0 2px var(--sg-bg, #fff); }
42
+ .sv-avgroup__it:first-child { margin-inline-start: 0; }
43
+ .sv-avgroup__more {
44
+ display: grid; place-items: center; margin-inline-start: calc(-1 * var(--ov, 10px)); border-radius: 50%;
45
+ background: var(--sg-row-hover-bg, #e2e8f0); color: var(--sg-muted, #64748b); font-weight: 650;
46
+ box-shadow: 0 0 0 2px var(--sg-bg, #fff);
47
+ }
48
+ </style>
@@ -0,0 +1,54 @@
1
+ <script lang="ts">
2
+ /**
3
+ * SvBadge - a small status pill / count label. Themed via `--sg-*` tokens with
4
+ * soft `color-mix` tints so it sits well on light and dark. Parity: Smart badge.
5
+ *
6
+ * ```svelte
7
+ * <SvBadge variant="success">Active</SvBadge>
8
+ * <SvBadge variant="danger" dot /> 3 failed
9
+ * ```
10
+ */
11
+ import type { Snippet } from 'svelte'
12
+
13
+ type Variant = 'neutral' | 'accent' | 'success' | 'warning' | 'danger' | 'info'
14
+
15
+ type Props = {
16
+ variant?: Variant
17
+ size?: 'sm' | 'md'
18
+ /** Rounded-pill shape (default) vs a softer rounded rect. */
19
+ pill?: boolean
20
+ /** Show a leading status dot instead of / before content. */
21
+ dot?: boolean
22
+ children?: Snippet
23
+ }
24
+
25
+ let { variant = 'neutral', size = 'md', pill = true, dot = false, children }: Props = $props()
26
+ </script>
27
+
28
+ <span class="sv-badge sv-badge--{variant} sv-badge--{size}" class:is-pill={pill} class:has-dot={dot}>
29
+ {#if dot}<span class="sv-badge__dot" aria-hidden="true"></span>{/if}
30
+ {@render children?.()}
31
+ </span>
32
+
33
+ <style>
34
+ .sv-badge {
35
+ --_c: var(--sg-muted, #64748b);
36
+ display: inline-flex; align-items: center; gap: 5px; box-sizing: border-box;
37
+ font-weight: 600; line-height: 1; white-space: nowrap;
38
+ color: var(--_c);
39
+ background: color-mix(in srgb, var(--_c) 14%, transparent);
40
+ border: 1px solid color-mix(in srgb, var(--_c) 26%, transparent);
41
+ border-radius: 6px;
42
+ }
43
+ .sv-badge.is-pill { border-radius: 999px; }
44
+ .sv-badge--sm { font-size: 10.5px; padding: 2px 7px; }
45
+ .sv-badge--md { font-size: 12px; padding: 3px 9px; }
46
+ .sv-badge__dot { width: 6px; height: 6px; border-radius: 50%; background: var(--_c); }
47
+
48
+ .sv-badge--neutral { --_c: var(--sg-muted, #64748b); }
49
+ .sv-badge--accent { --_c: var(--sg-accent, #2563eb); }
50
+ .sv-badge--success { --_c: var(--sg-success, #16a34a); }
51
+ .sv-badge--warning { --_c: var(--sg-warning, #d97706); }
52
+ .sv-badge--danger { --_c: var(--sg-danger, #dc2626); }
53
+ .sv-badge--info { --_c: var(--sg-info, #0891b2); }
54
+ </style>