@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
@@ -7,9 +7,11 @@
7
7
  * The behavior lives in the headless `createSlider` core; this component is
8
8
  * just one styled renderer over it (it only owns pointer/DOM measurement).
9
9
  */
10
+ import SvField from './SvField.svelte'
11
+ import { nextEditorId, type SvEditorProps } from './editor-contract'
10
12
  import { createSlider } from './createSlider.svelte'
11
13
 
12
- type Props = {
14
+ type Props = SvEditorProps & {
13
15
  value?: number | [number, number]
14
16
  onChange?: (value: any) => void
15
17
  min?: number
@@ -18,12 +20,11 @@
18
20
  range?: boolean
19
21
  /** Show evenly spaced tick marks (count or explicit values). */
20
22
  ticks?: number | number[]
23
+ /** Numeric scale labels under the track: 'endpoints' (min/max), 'all' (per tick), or 'none'. */
24
+ labels?: 'none' | 'endpoints' | 'all'
21
25
  /** Show the value bubble above the thumb. */
22
26
  showValue?: boolean
23
- disabled?: boolean
24
27
  orientation?: 'horizontal' | 'vertical'
25
- name?: string
26
- ariaLabel?: string
27
28
  formatValue?: (v: number) => string
28
29
  }
29
30
 
@@ -35,14 +36,26 @@
35
36
  step = 1,
36
37
  range = false,
37
38
  ticks,
39
+ labels = 'none',
38
40
  showValue = false,
39
41
  disabled = false,
40
42
  orientation = 'horizontal',
41
43
  name,
42
44
  ariaLabel,
45
+ invalid = false,
46
+ required = false,
47
+ error,
48
+ label,
49
+ hint,
50
+ dir,
51
+ id,
43
52
  formatValue,
44
53
  }: Props = $props()
45
54
 
55
+ const autoId = nextEditorId('sv-slider')
56
+ const uid = $derived(id ?? autoId)
57
+ const resolvedDir = $derived(dir === 'ltr' || dir === 'rtl' ? dir : undefined)
58
+
46
59
  // Behavior (value<->position math, stepping, keyboard, drag state, ARIA) lives
47
60
  // in the headless `createSlider` core; the component keeps the DOM measuring.
48
61
  const slider = createSlider({
@@ -54,7 +67,8 @@
54
67
  range: () => range,
55
68
  orientation: () => orientation,
56
69
  disabled: () => disabled,
57
- ariaLabel: () => ariaLabel,
70
+ ariaLabel: () => ariaLabel ?? label,
71
+ rtl: () => resolvedDir === 'rtl',
58
72
  })
59
73
  const lo = $derived(slider.lo)
60
74
  const hi = $derived(slider.hi)
@@ -65,6 +79,10 @@
65
79
  if (Array.isArray(ticks)) return ticks
66
80
  return Array.from({ length: ticks }, (_, i) => min + ((max - min) / (ticks - 1)) * i)
67
81
  })
82
+ const labelValues = $derived.by<number[]>(() => {
83
+ if (labels === 'all') return tickValues.length ? tickValues : [min, max]
84
+ return [] // 'endpoints' renders min/max directly; 'none' renders nothing
85
+ })
68
86
 
69
87
  // The rect is measured here (a DOM concern) and passed INTO the core.
70
88
  let trackEl: HTMLDivElement | null = null
@@ -82,10 +100,11 @@
82
100
  const fmt = (v: number) => (formatValue ? formatValue(v) : String(v))
83
101
  </script>
84
102
 
103
+ <SvField id={uid} {label} {hint} {error} {required} {dir}>
85
104
  <div
86
105
  class="sv-slider sv-slider--{orientation}"
87
106
  class:is-disabled={disabled}
88
- aria-label={ariaLabel}
107
+ dir={resolvedDir}
89
108
  >
90
109
  <!-- svelte-ignore a11y_no_static_element_interactions -->
91
110
  <div
@@ -119,8 +138,18 @@
119
138
  </div>
120
139
  {/if}
121
140
  </div>
141
+ {#if labels !== 'none' && orientation === 'horizontal'}
142
+ {#if labels === 'endpoints'}
143
+ <div class="sv-slider__scale sv-slider__scale--ends" aria-hidden="true"><span>{fmt(min)}</span><span>{fmt(max)}</span></div>
144
+ {:else}
145
+ <div class="sv-slider__scale" aria-hidden="true">
146
+ {#each labelValues as lv (lv)}<span class="sv-slider__scale-lbl" style:--p={`${pct(lv)}%`}>{fmt(lv)}</span>{/each}
147
+ </div>
148
+ {/if}
149
+ {/if}
122
150
  {#if name}<input type="hidden" {name} value={range ? `${lo},${hi}` : hi} />{/if}
123
151
  </div>
152
+ </SvField>
124
153
 
125
154
  <style>
126
155
  .sv-slider { --_accent: var(--sg-accent, #2563eb); --_track: var(--sg-border, #e2e8f0); padding: 10px; }
@@ -132,15 +161,18 @@
132
161
  }
133
162
  .sv-slider--horizontal .sv-slider__track { height: 6px; }
134
163
  .sv-slider--vertical .sv-slider__track { width: 6px; height: 100%; margin: 0 auto; }
164
+ .sv-slider__scale { position: relative; height: 15px; margin-top: 8px; font-size: 11px; color: var(--sg-muted, #64748b); font-variant-numeric: tabular-nums; }
165
+ .sv-slider__scale--ends { display: flex; justify-content: space-between; }
166
+ .sv-slider__scale-lbl { position: absolute; inset-inline-start: var(--p); transform: translateX(-50%); white-space: nowrap; }
135
167
  .sv-slider__fill { position: absolute; background: var(--_accent); border-radius: 999px; }
136
- .sv-slider--horizontal .sv-slider__fill { left: var(--a); right: calc(100% - var(--b)); top: 0; bottom: 0; }
168
+ .sv-slider--horizontal .sv-slider__fill { inset-inline-start: var(--a); inset-inline-end: calc(100% - var(--b)); top: 0; bottom: 0; }
137
169
  .sv-slider--vertical .sv-slider__fill { bottom: var(--a); top: calc(100% - var(--b)); left: 0; right: 0; }
138
170
  .sv-slider__thumb {
139
171
  position: absolute; width: 16px; height: 16px; border-radius: 50%;
140
172
  background: #fff; border: 2px solid var(--_accent); box-shadow: 0 1px 3px rgba(0,0,0,0.25);
141
173
  cursor: grab; touch-action: none;
142
174
  }
143
- .sv-slider--horizontal .sv-slider__thumb { left: var(--p); top: 50%; transform: translate(-50%, -50%); }
175
+ .sv-slider--horizontal .sv-slider__thumb { inset-inline-start: var(--p); top: 50%; transform: translate(-50%, -50%); }
144
176
  .sv-slider--vertical .sv-slider__thumb { bottom: var(--p); left: 50%; transform: translate(-50%, 50%); }
145
177
  .sv-slider__thumb:focus-visible { outline: 2px solid var(--sg-focus-ring, var(--_accent)); outline-offset: 2px; }
146
178
  .sv-slider__thumb:active { cursor: grabbing; }
@@ -150,6 +182,6 @@
150
182
  padding: 2px 6px; border-radius: 5px; white-space: nowrap;
151
183
  }
152
184
  .sv-slider__tick { position: absolute; width: 2px; height: 2px; border-radius: 50%; background: var(--sg-muted, #94a3b8); }
153
- .sv-slider--horizontal .sv-slider__tick { left: var(--p); top: 50%; transform: translate(-50%, -50%); }
185
+ .sv-slider--horizontal .sv-slider__tick { inset-inline-start: var(--p); top: 50%; transform: translate(-50%, -50%); }
154
186
  .sv-slider--vertical .sv-slider__tick { bottom: var(--p); left: 50%; transform: translate(-50%, 50%); }
155
187
  </style>
@@ -0,0 +1,77 @@
1
+ <script lang="ts">
2
+ /**
3
+ * SvSparkline - a tiny inline chart (line / area / bar / win-loss) from a number
4
+ * array. A thin SVG renderer over the pure `buildSparkline` geometry (shared
5
+ * with the grid's sparkline cell). Theme-driven (defaults to `--sg-accent`).
6
+ *
7
+ * ```svelte
8
+ * <SvSparkline data={[3, 7, 4, 9, 6, 11, 8]} type="area" />
9
+ * ```
10
+ */
11
+ import { buildSparkline, type SparklineType } from './sparkline'
12
+
13
+ type Props = {
14
+ data: ReadonlyArray<number>
15
+ type?: SparklineType
16
+ width?: number
17
+ height?: number
18
+ color?: string
19
+ negativeColor?: string
20
+ lineWidth?: number
21
+ /** Fix the value scale instead of deriving it from the data. */
22
+ min?: number
23
+ max?: number
24
+ /** End-cap dot on the last point (line / area). Default true. */
25
+ lastPoint?: boolean
26
+ ariaLabel?: string
27
+ }
28
+
29
+ let {
30
+ data,
31
+ type = 'line',
32
+ width = 88,
33
+ height = 22,
34
+ color,
35
+ negativeColor,
36
+ lineWidth,
37
+ min,
38
+ max,
39
+ lastPoint = true,
40
+ ariaLabel,
41
+ }: Props = $props()
42
+
43
+ const geo = $derived(
44
+ buildSparkline([...data], { type, width, height, color, negativeColor, lineWidth, min, max, lastPoint }),
45
+ )
46
+ </script>
47
+
48
+ {#if geo}
49
+ <svg
50
+ class="sv-spark"
51
+ width={geo.width}
52
+ height={geo.height}
53
+ viewBox={`0 0 ${geo.width} ${geo.height}`}
54
+ preserveAspectRatio="none"
55
+ role="img"
56
+ aria-label={ariaLabel ?? 'sparkline'}
57
+ >
58
+ {#if type === 'area' && geo.areaPath}
59
+ <path d={geo.areaPath} fill={geo.color} fill-opacity="0.15" stroke="none" />
60
+ {/if}
61
+ {#if (type === 'line' || type === 'area') && geo.linePath}
62
+ <path d={geo.linePath} fill="none" stroke={geo.color} stroke-width={geo.lineWidth} stroke-linejoin="round" stroke-linecap="round" />
63
+ {/if}
64
+ {#if type === 'bar' || type === 'winloss'}
65
+ {#each geo.bars as b, i (i)}
66
+ <rect x={b.x} y={b.y} width={b.w} height={b.h} rx="0.5" fill={b.negative ? geo.negativeColor : geo.color} />
67
+ {/each}
68
+ {/if}
69
+ {#if lastPoint && geo.lastPoint && (type === 'line' || type === 'area')}
70
+ <circle cx={geo.lastPoint.x} cy={geo.lastPoint.y} r={geo.lineWidth + 0.8} fill={geo.color} />
71
+ {/if}
72
+ </svg>
73
+ {/if}
74
+
75
+ <style>
76
+ .sv-spark { display: inline-block; vertical-align: middle; overflow: visible; }
77
+ </style>
@@ -0,0 +1,138 @@
1
+ <script lang="ts" module>
2
+ export type { SplitterOrientation } from './createSplitter.svelte'
3
+ </script>
4
+
5
+ <script lang="ts">
6
+ /**
7
+ * SvSplitter - two resizable panes with a draggable separator (WAI-ARIA
8
+ * `separator`), pointer drag + keyboard resize. Parity: Smart `smart-splitter`.
9
+ * Controlled via `fraction` (0..1 of the first pane) + `onChange`; the two panes
10
+ * are the `start` and `end` snippets.
11
+ *
12
+ * Behavior (clamp, keyboard, ARIA, RTL) lives in the headless `createSplitter`
13
+ * core; this component owns only the container measurement for pointer drags.
14
+ */
15
+ import type { Snippet } from 'svelte'
16
+ import { type EditorDir } from './editor-contract'
17
+ import { createSplitter, type SplitterOrientation } from './createSplitter.svelte'
18
+
19
+ type Props = {
20
+ /** Size of the first pane as a fraction of the container (0..1). */
21
+ fraction?: number
22
+ onChange?: (fraction: number) => void
23
+ /** 'horizontal' = left/right panes (default); 'vertical' = top/bottom. */
24
+ orientation?: SplitterOrientation
25
+ min?: number
26
+ max?: number
27
+ /** Keyboard resize step as a fraction (default 0.02 = 2%). */
28
+ step?: number
29
+ disabled?: boolean
30
+ dir?: EditorDir
31
+ ariaLabel?: string
32
+ start?: Snippet
33
+ end?: Snippet
34
+ }
35
+
36
+ let {
37
+ fraction = $bindable(0.5),
38
+ onChange,
39
+ orientation = 'horizontal',
40
+ min = 0.1,
41
+ max = 0.9,
42
+ step = 0.02,
43
+ disabled = false,
44
+ dir,
45
+ ariaLabel,
46
+ start,
47
+ end,
48
+ }: Props = $props()
49
+
50
+ const resolvedDir = $derived(dir === 'ltr' || dir === 'rtl' ? dir : undefined)
51
+
52
+ const sp = createSplitter({
53
+ fraction: () => fraction,
54
+ onChange: (f) => { fraction = f; onChange?.(f) },
55
+ orientation: () => orientation,
56
+ min: () => min,
57
+ max: () => max,
58
+ step: () => step,
59
+ disabled: () => disabled,
60
+ dir: () => dir,
61
+ ariaLabel: () => ariaLabel,
62
+ })
63
+
64
+ // The container rect is measured here (a DOM concern) and passed INTO the core.
65
+ let rootEl: HTMLDivElement | null = null
66
+ let sepEl: HTMLDivElement | null = null
67
+
68
+ function onSepDown(e: PointerEvent) {
69
+ if (disabled) return
70
+ sp.pointerDown()
71
+ sepEl?.setPointerCapture(e.pointerId)
72
+ }
73
+ function onMove(e: PointerEvent) {
74
+ if (!sp.dragging || !rootEl) return
75
+ sp.setFromPointer({ x: e.clientX, y: e.clientY }, rootEl.getBoundingClientRect())
76
+ }
77
+ function onUp(e: PointerEvent) {
78
+ if (!sp.dragging) return
79
+ sp.endDrag()
80
+ sepEl?.releasePointerCapture(e.pointerId)
81
+ }
82
+ </script>
83
+
84
+ <div
85
+ bind:this={rootEl}
86
+ class="sv-split sv-split--{orientation}"
87
+ class:is-disabled={disabled}
88
+ class:is-dragging={sp.dragging}
89
+ dir={resolvedDir}
90
+ >
91
+ <div class="sv-split__pane" style:flex-basis={`${sp.percent}%`}>
92
+ {#if start}{@render start()}{/if}
93
+ </div>
94
+ <!-- svelte-ignore a11y_no_static_element_interactions -->
95
+ <div
96
+ bind:this={sepEl}
97
+ class="sv-split__sep"
98
+ {...sp.separatorProps()}
99
+ onpointerdown={onSepDown}
100
+ onpointermove={onMove}
101
+ onpointerup={onUp}
102
+ onpointercancel={onUp}
103
+ ><span class="sv-split__grip" aria-hidden="true"></span></div>
104
+ <div class="sv-split__pane sv-split__pane--end">
105
+ {#if end}{@render end()}{/if}
106
+ </div>
107
+ </div>
108
+
109
+ <style>
110
+ .sv-split {
111
+ --_accent: var(--sg-accent, #2563eb);
112
+ --_border: var(--sg-border, #e2e8f0);
113
+ display: flex; width: 100%; height: 100%; min-height: 0; overflow: hidden;
114
+ }
115
+ .sv-split--vertical { flex-direction: column; }
116
+ .sv-split.is-disabled { opacity: 0.7; }
117
+ .sv-split__pane { flex-grow: 0; flex-shrink: 0; min-width: 0; min-height: 0; overflow: auto; }
118
+ .sv-split__pane--end { flex: 1 1 0; }
119
+ /* The gutter is a solid divider-toned bar (--sg-border is the one token that
120
+ reliably contrasts against panes in BOTH light and dark themes - it draws the
121
+ visible card outline too). It turns accent on hover / drag. */
122
+ .sv-split__sep {
123
+ flex: 0 0 auto; position: relative; background: var(--sg-border, #cbd5e1);
124
+ display: grid; place-items: center; touch-action: none; transition: background 0.12s;
125
+ }
126
+ /* Orientation rules MUST target the splitter's OWN separator only (direct
127
+ child). With a descendant combinator a nested splitter of the opposite
128
+ orientation inherits the wrong sizing - e.g. a vertical split inside a
129
+ horizontal one would get width:8px and collapse its horizontal bar. */
130
+ .sv-split--horizontal > .sv-split__sep { width: 8px; min-width: 8px; cursor: col-resize; }
131
+ .sv-split--vertical > .sv-split__sep { height: 8px; min-height: 8px; cursor: row-resize; }
132
+ .sv-split__sep:hover, .sv-split.is-dragging > .sv-split__sep { background: var(--_accent); }
133
+ .sv-split__sep:focus-visible { outline: 2px solid var(--sg-focus-ring, var(--_accent)); outline-offset: 1px; z-index: 1; }
134
+ /* Grip dots: the pane background color, so they read as notches cut into the bar. */
135
+ .sv-split__grip { background: var(--sg-bg, #fff); border-radius: 3px; opacity: 0.9; transition: background 0.12s; }
136
+ .sv-split--horizontal > .sv-split__sep > .sv-split__grip { width: 2px; height: 24px; }
137
+ .sv-split--vertical > .sv-split__sep > .sv-split__grip { width: 24px; height: 2px; }
138
+ </style>
@@ -0,0 +1,55 @@
1
+ /**
2
+ * Component tests for SvSplitter: the separator carries WAI-ARIA separator
3
+ * semantics, keyboard resize clamps to [min, max], and RTL flips the horizontal
4
+ * arrow direction.
5
+ */
6
+ import { describe, expect, it } from 'vitest'
7
+ import { mount, unmount, flushSync } from 'svelte'
8
+ import SvSplitter from './SvSplitter.svelte'
9
+
10
+ function mountSp(props: Record<string, unknown>) {
11
+ const target = document.createElement('div')
12
+ document.body.appendChild(target)
13
+ const app = mount(SvSplitter, { target, props: props as any })
14
+ flushSync()
15
+ const sep = target.querySelector<HTMLElement>('.sv-split__sep')!
16
+ return { target, sep, destroy: () => { unmount(app); target.remove() } }
17
+ }
18
+ const key = (sep: HTMLElement, k: string) => sep.dispatchEvent(new KeyboardEvent('keydown', { key: k, bubbles: true }))
19
+
20
+ describe('SvSplitter', () => {
21
+ it('exposes separator semantics with valuemin/max/now', () => {
22
+ const { sep, destroy } = mountSp({ fraction: 0.5, min: 0.2, max: 0.8 })
23
+ try {
24
+ expect(sep.getAttribute('role')).toBe('separator')
25
+ // Horizontal split -> a vertical separator line.
26
+ expect(sep.getAttribute('aria-orientation')).toBe('vertical')
27
+ expect(sep.getAttribute('aria-valuemin')).toBe('20')
28
+ expect(sep.getAttribute('aria-valuemax')).toBe('80')
29
+ expect(sep.getAttribute('aria-valuenow')).toBe('50')
30
+ expect(sep.tabIndex).toBe(0)
31
+ } finally { destroy() }
32
+ })
33
+
34
+ it('ArrowRight grows the first pane; Home/End jump to the bounds', () => {
35
+ let got: number | undefined
36
+ const { sep, destroy } = mountSp({ fraction: 0.5, step: 0.1, min: 0.2, max: 0.8, onChange: (f: number) => (got = f) })
37
+ try {
38
+ key(sep, 'ArrowRight'); flushSync()
39
+ expect(got).toBeCloseTo(0.6, 5)
40
+ key(sep, 'End'); flushSync()
41
+ expect(got).toBeCloseTo(0.8, 5)
42
+ key(sep, 'Home'); flushSync()
43
+ expect(got).toBeCloseTo(0.2, 5)
44
+ } finally { destroy() }
45
+ })
46
+
47
+ it('RTL flips the horizontal arrow direction', () => {
48
+ let got: number | undefined
49
+ const { sep, destroy } = mountSp({ fraction: 0.5, step: 0.1, dir: 'rtl', onChange: (f: number) => (got = f) })
50
+ try {
51
+ key(sep, 'ArrowRight'); flushSync()
52
+ expect(got).toBeCloseTo(0.4, 5) // Right shrinks the first (right-anchored) pane under RTL
53
+ } finally { destroy() }
54
+ })
55
+ })
@@ -0,0 +1,86 @@
1
+ <script lang="ts">
2
+ /**
3
+ * SvStat - a KPI / metric card: a label, a big value, and an optional delta with
4
+ * an up/down trend (auto-coloured; green up / red down, or inverted for
5
+ * "lower is better" metrics), plus slots for an icon and an inline chart
6
+ * (e.g. a sparkline). Theme-driven. The dashboard building block.
7
+ *
8
+ * ```svelte
9
+ * <SvStat label="Revenue" value="$48.2k" delta={12.4} hint="vs last month" />
10
+ * ```
11
+ */
12
+ import type { Snippet } from 'svelte'
13
+
14
+ type Trend = 'up' | 'down' | 'flat'
15
+
16
+ type Props = {
17
+ label: string
18
+ value: string | number
19
+ /** A number (auto-signs + infers trend) or a preformatted string. */
20
+ delta?: string | number
21
+ /** Override the inferred trend. */
22
+ trend?: Trend
23
+ /** Text after the delta (e.g. "vs last month"). */
24
+ hint?: string
25
+ /** Treat a downward delta as good (green) - e.g. error rate, latency. */
26
+ invert?: boolean
27
+ icon?: Snippet
28
+ /** Inline chart / sparkline slot. */
29
+ chart?: Snippet
30
+ }
31
+
32
+ let { label, value, delta, trend, hint, invert = false, icon, chart }: Props = $props()
33
+
34
+ const resolvedTrend = $derived<Trend | undefined>(
35
+ trend ?? (typeof delta === 'number' ? (delta > 0 ? 'up' : delta < 0 ? 'down' : 'flat') : undefined),
36
+ )
37
+ const deltaText = $derived(
38
+ delta == null ? null : typeof delta === 'number' ? `${delta > 0 ? '+' : ''}${delta}%` : delta,
39
+ )
40
+ const arrow = $derived(resolvedTrend === 'up' ? '▲' : resolvedTrend === 'down' ? '▼' : '→')
41
+ // Which trend is "positive" (green): up, unless inverted.
42
+ const tone = $derived(
43
+ resolvedTrend === 'flat' || !resolvedTrend
44
+ ? 'flat'
45
+ : (resolvedTrend === 'up') !== invert
46
+ ? 'good'
47
+ : 'bad',
48
+ )
49
+ </script>
50
+
51
+ <div class="sv-stat">
52
+ <div class="sv-stat__head">
53
+ <span class="sv-stat__label">{label}</span>
54
+ {#if icon}<span class="sv-stat__icon" aria-hidden="true">{@render icon()}</span>{/if}
55
+ </div>
56
+ <div class="sv-stat__value">{value}</div>
57
+ {#if (deltaText != null) || hint || chart}
58
+ <div class="sv-stat__foot">
59
+ {#if deltaText != null && resolvedTrend}
60
+ <span class="sv-stat__delta is-{tone}"><span class="sv-stat__arrow" aria-hidden="true">{arrow}</span>{deltaText}</span>
61
+ {/if}
62
+ {#if hint}<span class="sv-stat__hint">{hint}</span>{/if}
63
+ {#if chart}<span class="sv-stat__chart">{@render chart()}</span>{/if}
64
+ </div>
65
+ {/if}
66
+ </div>
67
+
68
+ <style>
69
+ .sv-stat {
70
+ display: flex; flex-direction: column; gap: 5px;
71
+ padding: 14px 16px; border: 1px solid var(--sg-border, #e2e8f0); border-radius: var(--sg-radius-lg, 12px);
72
+ background: var(--sg-bg, #fff); color: var(--sg-fg, #0f172a); min-width: 0;
73
+ }
74
+ .sv-stat__head { display: flex; align-items: center; justify-content: space-between; gap: 8px; }
75
+ .sv-stat__label { font-size: 12.5px; font-weight: 600; color: var(--sg-muted, #64748b); }
76
+ .sv-stat__icon { color: var(--sg-muted, #94a3b8); display: inline-flex; }
77
+ .sv-stat__value { font-size: 26px; font-weight: 700; line-height: 1.1; font-variant-numeric: tabular-nums; }
78
+ .sv-stat__foot { display: flex; align-items: center; gap: 8px; font-size: 12px; }
79
+ .sv-stat__delta { display: inline-flex; align-items: center; gap: 3px; font-weight: 650; font-variant-numeric: tabular-nums; }
80
+ .sv-stat__arrow { font-size: 9px; }
81
+ .sv-stat__delta.is-good { color: var(--sg-success, #16a34a); }
82
+ .sv-stat__delta.is-bad { color: var(--sg-danger, #dc2626); }
83
+ .sv-stat__delta.is-flat { color: var(--sg-muted, #64748b); }
84
+ .sv-stat__hint { color: var(--sg-muted, #94a3b8); }
85
+ .sv-stat__chart { margin-inline-start: auto; }
86
+ </style>
@@ -0,0 +1,91 @@
1
+ <script lang="ts" module>
2
+ export type { StepItem } from './ui-app.types'
3
+ </script>
4
+
5
+ <script lang="ts">
6
+ /**
7
+ * SvStepper - a progress stepper for wizards/multi-step forms. Shows completed
8
+ * / active / upcoming steps horizontally or vertically. In `linear` mode only
9
+ * completed steps (and the active one) are clickable; otherwise any step is.
10
+ * Parity: Smart steps / wizard nav.
11
+ *
12
+ * ```svelte
13
+ * <SvStepper steps={steps} current={i} onChange={(n) => (i = n)} />
14
+ * ```
15
+ */
16
+ import type { StepItem } from './ui-app.types'
17
+
18
+ type Props = {
19
+ steps: ReadonlyArray<StepItem>
20
+ /** 0-based index of the active step. */
21
+ current: number
22
+ onChange?: (index: number) => void
23
+ /** Only allow navigating to already-reached steps. Default true. */
24
+ linear?: boolean
25
+ orientation?: 'horizontal' | 'vertical'
26
+ }
27
+
28
+ let { steps, current, onChange, linear = true, orientation = 'horizontal' }: Props = $props()
29
+
30
+ const statusOf = (i: number): 'complete' | 'active' | 'upcoming' =>
31
+ i < current ? 'complete' : i === current ? 'active' : 'upcoming'
32
+ const clickable = (i: number) => (linear ? i <= current : true)
33
+ const go = (i: number) => { if (clickable(i) && i !== current) onChange?.(i) }
34
+ </script>
35
+
36
+ <ol class="sv-step sv-step--{orientation}">
37
+ {#each steps as step, i (i)}
38
+ {@const status = statusOf(i)}
39
+ <li class="sv-step__item is-{status}" class:is-clickable={clickable(i)}>
40
+ <button
41
+ type="button"
42
+ class="sv-step__btn"
43
+ aria-current={status === 'active' ? 'step' : undefined}
44
+ disabled={!clickable(i)}
45
+ onclick={() => go(i)}
46
+ >
47
+ <span class="sv-step__marker" aria-hidden="true">
48
+ {#if status === 'complete'}✓{:else}{i + 1}{/if}
49
+ </span>
50
+ <span class="sv-step__text">
51
+ <span class="sv-step__label">{step.label}{#if step.optional}<span class="sv-step__optional"> (optional)</span>{/if}</span>
52
+ {#if step.description}<span class="sv-step__desc">{step.description}</span>{/if}
53
+ </span>
54
+ </button>
55
+ {#if i < steps.length - 1}<span class="sv-step__line" aria-hidden="true"></span>{/if}
56
+ </li>
57
+ {/each}
58
+ </ol>
59
+
60
+ <style>
61
+ .sv-step { --_accent: var(--sg-accent, #2563eb); list-style: none; margin: 0; padding: 0; display: flex; }
62
+ .sv-step--horizontal { flex-direction: row; align-items: flex-start; }
63
+ .sv-step--vertical { flex-direction: column; }
64
+ .sv-step__item { display: flex; position: relative; flex: 1; }
65
+ .sv-step--vertical .sv-step__item { flex-direction: column; }
66
+
67
+ .sv-step__btn {
68
+ display: flex; align-items: center; gap: 10px; background: none; border: 0; padding: 4px; margin: 0;
69
+ font: inherit; text-align: start; cursor: default; color: var(--sg-muted, #64748b);
70
+ }
71
+ .sv-step__item.is-clickable .sv-step__btn { cursor: pointer; }
72
+ .sv-step__marker {
73
+ flex: none; width: 28px; height: 28px; display: grid; place-items: center; border-radius: 50%;
74
+ font-size: 13px; font-weight: 650;
75
+ background: var(--sg-row-hover-bg, #eef2f7); color: var(--sg-muted, #64748b);
76
+ border: 2px solid transparent; transition: background 0.15s, color 0.15s, border-color 0.15s;
77
+ }
78
+ .sv-step__item.is-active .sv-step__marker { background: var(--_accent); color: var(--sg-on-accent, #fff); }
79
+ .sv-step__item.is-complete .sv-step__marker { background: color-mix(in srgb, var(--_accent) 16%, transparent); color: var(--_accent); border-color: color-mix(in srgb, var(--_accent) 45%, transparent); }
80
+ .sv-step__item.is-active .sv-step__label, .sv-step__item.is-complete .sv-step__label { color: var(--sg-fg, #0f172a); }
81
+
82
+ .sv-step__text { display: flex; flex-direction: column; }
83
+ .sv-step__label { font-size: 13px; font-weight: 600; }
84
+ .sv-step__optional { font-weight: 400; color: var(--sg-muted, #94a3b8); font-size: 11.5px; }
85
+ .sv-step__desc { font-size: 11.5px; color: var(--sg-muted, #94a3b8); }
86
+
87
+ .sv-step__line { flex: 1; height: 2px; align-self: center; margin: 0 8px; background: var(--sg-border, #e2e8f0); }
88
+ .sv-step__item.is-complete .sv-step__line { background: color-mix(in srgb, var(--_accent) 45%, var(--sg-border, #e2e8f0)); }
89
+ .sv-step--vertical .sv-step__line { width: 2px; height: auto; min-height: 16px; margin: 4px 0 4px 17px; align-self: flex-start; }
90
+ .sv-step--vertical .sv-step__item { padding-bottom: 4px; }
91
+ </style>