zabi-components 8.0.0 → 9.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (456) hide show
  1. package/CHANGELOG.md +2935 -0
  2. package/README.md +243 -10
  3. package/THEME.md +162 -332
  4. package/THEMING.md +1419 -259
  5. package/dist/atoms/ActionPanel.svelte +15 -5
  6. package/dist/atoms/Avatar.svelte +120 -0
  7. package/dist/atoms/Avatar.svelte.d.ts +46 -0
  8. package/dist/atoms/Badge.svelte +71 -13
  9. package/dist/atoms/Badge.svelte.d.ts +8 -2
  10. package/dist/atoms/Block.svelte +74 -0
  11. package/dist/atoms/Block.svelte.d.ts +29 -0
  12. package/dist/atoms/Button.svelte +217 -30
  13. package/dist/atoms/Button.svelte.d.ts +33 -4
  14. package/dist/atoms/Card.svelte +182 -26
  15. package/dist/atoms/Card.svelte.d.ts +55 -3
  16. package/dist/atoms/CardHeader.svelte +16 -4
  17. package/dist/atoms/Checkbox.svelte +81 -21
  18. package/dist/atoms/Checkbox.svelte.d.ts +31 -2
  19. package/dist/atoms/Chip.svelte +351 -0
  20. package/dist/atoms/Chip.svelte.d.ts +57 -0
  21. package/dist/atoms/CodeBlock.svelte +20 -2
  22. package/dist/atoms/CodeBlock.svelte.d.ts +4 -0
  23. package/dist/atoms/CodeField.svelte +385 -0
  24. package/dist/atoms/CodeField.svelte.d.ts +61 -0
  25. package/dist/atoms/ColorPicker.svelte +260 -49
  26. package/dist/atoms/ColorPicker.svelte.d.ts +3 -0
  27. package/dist/atoms/Container.svelte +16 -8
  28. package/dist/atoms/DateField.svelte +145 -0
  29. package/dist/atoms/DateField.svelte.d.ts +81 -0
  30. package/dist/atoms/FieldMessages.svelte +69 -0
  31. package/dist/atoms/FieldMessages.svelte.d.ts +23 -0
  32. package/dist/atoms/FloatingActionButton.svelte +176 -0
  33. package/dist/atoms/FloatingActionButton.svelte.d.ts +51 -0
  34. package/dist/atoms/Heading.svelte +63 -5
  35. package/dist/atoms/Heading.svelte.d.ts +14 -0
  36. package/dist/atoms/IconButton.svelte +294 -26
  37. package/dist/atoms/IconButton.svelte.d.ts +62 -4
  38. package/dist/atoms/Input.svelte +259 -53
  39. package/dist/atoms/Input.svelte.d.ts +33 -2
  40. package/dist/atoms/List.svelte +5 -1
  41. package/dist/atoms/ListItem.svelte +70 -13
  42. package/dist/atoms/ListItem.svelte.d.ts +11 -0
  43. package/dist/atoms/PickerField.svelte +239 -0
  44. package/dist/atoms/PickerField.svelte.d.ts +67 -0
  45. package/dist/atoms/Progress.svelte +99 -19
  46. package/dist/atoms/Progress.svelte.d.ts +15 -1
  47. package/dist/atoms/Radio.svelte +51 -3
  48. package/dist/atoms/Radio.svelte.d.ts +30 -2
  49. package/dist/atoms/Rating.svelte +620 -0
  50. package/dist/atoms/Rating.svelte.d.ts +77 -0
  51. package/dist/atoms/Select.svelte +438 -98
  52. package/dist/atoms/Select.svelte.d.ts +61 -3
  53. package/dist/atoms/SelectionControl.svelte +193 -23
  54. package/dist/atoms/SelectionControl.svelte.d.ts +29 -3
  55. package/dist/atoms/Skeleton.svelte +114 -18
  56. package/dist/atoms/Skeleton.svelte.d.ts +30 -3
  57. package/dist/atoms/SkeletonGroup.svelte +43 -0
  58. package/dist/atoms/SkeletonGroup.svelte.d.ts +20 -0
  59. package/dist/atoms/Slider.svelte +344 -0
  60. package/dist/atoms/Slider.svelte.d.ts +40 -0
  61. package/dist/atoms/Sparkline.svelte +109 -0
  62. package/dist/atoms/Sparkline.svelte.d.ts +27 -0
  63. package/dist/atoms/SpeechBubble.svelte +187 -0
  64. package/dist/atoms/SpeechBubble.svelte.d.ts +33 -0
  65. package/dist/atoms/Spinner.svelte +57 -0
  66. package/dist/atoms/Spinner.svelte.d.ts +19 -0
  67. package/dist/atoms/Stat.svelte +72 -0
  68. package/dist/atoms/Stat.svelte.d.ts +19 -0
  69. package/dist/atoms/Table.svelte +151 -6
  70. package/dist/atoms/Table.svelte.d.ts +15 -3
  71. package/dist/atoms/TemporalField.svelte +472 -0
  72. package/dist/atoms/TemporalField.svelte.d.ts +48 -0
  73. package/dist/atoms/Text.svelte +23 -11
  74. package/dist/atoms/Text.svelte.d.ts +6 -3
  75. package/dist/atoms/Textarea.svelte +150 -44
  76. package/dist/atoms/Textarea.svelte.d.ts +35 -2
  77. package/dist/atoms/ThemeToggle.svelte +223 -44
  78. package/dist/atoms/ThemeToggle.svelte.d.ts +26 -3
  79. package/dist/atoms/TimeColumn.svelte +150 -0
  80. package/dist/atoms/TimeColumn.svelte.d.ts +25 -0
  81. package/dist/atoms/TimeField.svelte +151 -0
  82. package/dist/atoms/TimeField.svelte.d.ts +85 -0
  83. package/dist/atoms/Toast.svelte +22 -8
  84. package/dist/atoms/Toast.svelte.d.ts +2 -0
  85. package/dist/atoms/Toggle.svelte +98 -14
  86. package/dist/atoms/Toggle.svelte.d.ts +26 -2
  87. package/dist/atoms/Tooltip.svelte +528 -24
  88. package/dist/atoms/Tooltip.svelte.d.ts +18 -0
  89. package/dist/atoms/ZabiStringsProvider.svelte +45 -0
  90. package/dist/atoms/ZabiStringsProvider.svelte.d.ts +27 -0
  91. package/dist/atoms/index.d.ts +38 -0
  92. package/dist/atoms/index.js +29 -0
  93. package/dist/atoms/selection-control.styles.d.ts +47 -5
  94. package/dist/atoms/selection-control.styles.js +52 -10
  95. package/dist/components/atoms/index.d.ts +38 -0
  96. package/dist/components/atoms/index.d.ts.map +1 -1
  97. package/dist/components/atoms/selection-control.styles.d.ts +47 -5
  98. package/dist/components/atoms/selection-control.styles.d.ts.map +1 -1
  99. package/dist/components/index.d.ts +104 -2
  100. package/dist/components/index.d.ts.map +1 -1
  101. package/dist/components/map.d.ts +11 -0
  102. package/dist/components/map.d.ts.map +1 -0
  103. package/dist/components/molecules/index.d.ts +51 -0
  104. package/dist/components/molecules/index.d.ts.map +1 -1
  105. package/dist/components/molecules/toast-store.d.ts +83 -7
  106. package/dist/components/molecules/toast-store.d.ts.map +1 -1
  107. package/dist/components/organisms/index.d.ts +10 -0
  108. package/dist/components/organisms/index.d.ts.map +1 -1
  109. package/dist/components/types/variants.d.ts +24 -1
  110. package/dist/components/types/variants.d.ts.map +1 -1
  111. package/dist/components/util/app-shell.d.ts +79 -0
  112. package/dist/components/util/app-shell.d.ts.map +1 -0
  113. package/dist/components/util/avatar.d.ts +39 -0
  114. package/dist/components/util/avatar.d.ts.map +1 -0
  115. package/dist/components/util/bottom-sheet.d.ts +30 -0
  116. package/dist/components/util/bottom-sheet.d.ts.map +1 -0
  117. package/dist/components/util/bottom-tab-bar.d.ts +41 -0
  118. package/dist/components/util/bottom-tab-bar.d.ts.map +1 -0
  119. package/dist/components/util/calendar.d.ts +56 -0
  120. package/dist/components/util/calendar.d.ts.map +1 -0
  121. package/dist/components/util/chart.d.ts +140 -0
  122. package/dist/components/util/chart.d.ts.map +1 -0
  123. package/dist/components/util/chip.d.ts +66 -0
  124. package/dist/components/util/chip.d.ts.map +1 -0
  125. package/dist/components/util/cn.d.ts.map +1 -1
  126. package/dist/components/util/code-field.d.ts +63 -0
  127. package/dist/components/util/code-field.d.ts.map +1 -0
  128. package/dist/components/util/collapsible.d.ts +64 -0
  129. package/dist/components/util/collapsible.d.ts.map +1 -0
  130. package/dist/components/util/confirm-dialog.d.ts +12 -0
  131. package/dist/components/util/confirm-dialog.d.ts.map +1 -0
  132. package/dist/components/util/date.d.ts +96 -0
  133. package/dist/components/util/date.d.ts.map +1 -0
  134. package/dist/components/util/display-voice.d.ts +10 -0
  135. package/dist/components/util/display-voice.d.ts.map +1 -0
  136. package/dist/components/util/drawer.d.ts +13 -0
  137. package/dist/components/util/drawer.d.ts.map +1 -0
  138. package/dist/components/util/dropdown.d.ts +47 -0
  139. package/dist/components/util/dropdown.d.ts.map +1 -0
  140. package/dist/components/util/field-control.d.ts +30 -0
  141. package/dist/components/util/field-control.d.ts.map +1 -0
  142. package/dist/components/util/field.d.ts +45 -0
  143. package/dist/components/util/field.d.ts.map +1 -0
  144. package/dist/components/util/fit-in-viewport.d.ts +201 -0
  145. package/dist/components/util/fit-in-viewport.d.ts.map +1 -0
  146. package/dist/components/util/focus-utils.d.ts +77 -2
  147. package/dist/components/util/focus-utils.d.ts.map +1 -1
  148. package/dist/components/util/hydration.d.ts +45 -0
  149. package/dist/components/util/hydration.d.ts.map +1 -0
  150. package/dist/components/util/keyboard-inset.d.ts +18 -0
  151. package/dist/components/util/keyboard-inset.d.ts.map +1 -0
  152. package/dist/components/util/map-view.d.ts +144 -0
  153. package/dist/components/util/map-view.d.ts.map +1 -0
  154. package/dist/components/util/media-grid.d.ts +51 -0
  155. package/dist/components/util/media-grid.d.ts.map +1 -0
  156. package/dist/components/util/media.d.ts +6 -0
  157. package/dist/components/util/media.d.ts.map +1 -0
  158. package/dist/components/util/overlay-chrome.d.ts +53 -0
  159. package/dist/components/util/overlay-chrome.d.ts.map +1 -0
  160. package/dist/components/util/overlay.d.ts +72 -0
  161. package/dist/components/util/overlay.d.ts.map +1 -0
  162. package/dist/components/util/photo.d.ts +234 -0
  163. package/dist/components/util/photo.d.ts.map +1 -0
  164. package/dist/components/util/portal.d.ts +23 -0
  165. package/dist/components/util/portal.d.ts.map +1 -0
  166. package/dist/components/util/progress.d.ts +19 -0
  167. package/dist/components/util/progress.d.ts.map +1 -0
  168. package/dist/components/util/pull-to-refresh.d.ts +27 -0
  169. package/dist/components/util/pull-to-refresh.d.ts.map +1 -0
  170. package/dist/components/util/radio-keys.d.ts +25 -0
  171. package/dist/components/util/radio-keys.d.ts.map +1 -0
  172. package/dist/components/util/rating.d.ts +23 -0
  173. package/dist/components/util/rating.d.ts.map +1 -0
  174. package/dist/components/util/ready-made-strings.d.ts +77 -0
  175. package/dist/components/util/ready-made-strings.d.ts.map +1 -0
  176. package/dist/components/util/scroll-edge.d.ts +33 -0
  177. package/dist/components/util/scroll-edge.d.ts.map +1 -0
  178. package/dist/components/util/scroll-padding.d.ts +20 -0
  179. package/dist/components/util/scroll-padding.d.ts.map +1 -0
  180. package/dist/components/util/segmented-control.d.ts +16 -0
  181. package/dist/components/util/segmented-control.d.ts.map +1 -0
  182. package/dist/components/util/select.d.ts +41 -0
  183. package/dist/components/util/select.d.ts.map +1 -0
  184. package/dist/components/util/sheet-drag.d.ts +84 -0
  185. package/dist/components/util/sheet-drag.d.ts.map +1 -0
  186. package/dist/components/util/sidebar.d.ts +63 -0
  187. package/dist/components/util/sidebar.d.ts.map +1 -0
  188. package/dist/components/util/skeleton.d.ts +15 -0
  189. package/dist/components/util/skeleton.d.ts.map +1 -0
  190. package/dist/components/util/sliding-indicator.d.ts +25 -0
  191. package/dist/components/util/sliding-indicator.d.ts.map +1 -0
  192. package/dist/components/util/sortable-list.d.ts +65 -0
  193. package/dist/components/util/sortable-list.d.ts.map +1 -0
  194. package/dist/components/util/stepper.d.ts +40 -0
  195. package/dist/components/util/stepper.d.ts.map +1 -0
  196. package/dist/components/util/swipeable-list-item.d.ts +36 -0
  197. package/dist/components/util/swipeable-list-item.d.ts.map +1 -0
  198. package/dist/components/util/temporal-field.d.ts +89 -0
  199. package/dist/components/util/temporal-field.d.ts.map +1 -0
  200. package/dist/components/util/theme-mode.d.ts +80 -0
  201. package/dist/components/util/theme-mode.d.ts.map +1 -0
  202. package/dist/components/util/toaster.d.ts +133 -0
  203. package/dist/components/util/toaster.d.ts.map +1 -0
  204. package/dist/components/util/tone-surface.d.ts +34 -0
  205. package/dist/components/util/tone-surface.d.ts.map +1 -0
  206. package/dist/components/util/tooltip.d.ts +10 -0
  207. package/dist/components/util/tooltip.d.ts.map +1 -0
  208. package/dist/components/util/top-navbar.d.ts +35 -0
  209. package/dist/components/util/top-navbar.d.ts.map +1 -0
  210. package/dist/components/util/touch-target.d.ts +31 -0
  211. package/dist/components/util/touch-target.d.ts.map +1 -0
  212. package/dist/components/util/zabi-strings.d.ts +142 -0
  213. package/dist/components/util/zabi-strings.d.ts.map +1 -0
  214. package/dist/create-theme/cli.js +173 -0
  215. package/dist/create-theme/index.d.ts +190 -0
  216. package/dist/create-theme/index.js +1034 -0
  217. package/dist/create-theme/lib/ramp-math.js +128 -0
  218. package/dist/create-theme/lib/resolve.js +238 -0
  219. package/dist/create-theme/theme-data.js +2644 -0
  220. package/dist/index.d.ts +104 -2
  221. package/dist/index.js +75 -2
  222. package/dist/map.d.ts +10 -0
  223. package/dist/map.js +9 -0
  224. package/dist/molecules/Alert.svelte +63 -16
  225. package/dist/molecules/Alert.svelte.d.ts +14 -1
  226. package/dist/molecules/AppBar.svelte +674 -0
  227. package/dist/molecules/AppBar.svelte.d.ts +131 -0
  228. package/dist/molecules/AvatarGroup.svelte +116 -0
  229. package/dist/molecules/AvatarGroup.svelte.d.ts +40 -0
  230. package/dist/molecules/BarChart.svelte +381 -0
  231. package/dist/molecules/BarChart.svelte.d.ts +45 -0
  232. package/dist/molecules/BottomSheet.svelte +608 -0
  233. package/dist/molecules/BottomSheet.svelte.d.ts +109 -0
  234. package/dist/molecules/BottomTabBar.svelte +403 -0
  235. package/dist/molecules/BottomTabBar.svelte.d.ts +98 -0
  236. package/dist/molecules/Calendar.svelte +597 -0
  237. package/dist/molecules/Calendar.svelte.d.ts +72 -0
  238. package/dist/molecules/ChipGroup.svelte +325 -0
  239. package/dist/molecules/ChipGroup.svelte.d.ts +41 -0
  240. package/dist/molecules/Collapsible.svelte +347 -0
  241. package/dist/molecules/Collapsible.svelte.d.ts +61 -0
  242. package/dist/molecules/CollapsibleGroup.svelte +136 -0
  243. package/dist/molecules/CollapsibleGroup.svelte.d.ts +14 -0
  244. package/dist/molecules/ComponentDemo.svelte +16 -4
  245. package/dist/molecules/ComponentDemo.svelte.d.ts +3 -0
  246. package/dist/molecules/ConfirmDialog.svelte +257 -0
  247. package/dist/molecules/ConfirmDialog.svelte.d.ts +58 -0
  248. package/dist/molecules/ContactForm.svelte +34 -17
  249. package/dist/molecules/ContactForm.svelte.d.ts +8 -0
  250. package/dist/molecules/Drawer.svelte +415 -0
  251. package/dist/molecules/Drawer.svelte.d.ts +55 -0
  252. package/dist/molecules/Dropdown.svelte +517 -48
  253. package/dist/molecules/Dropdown.svelte.d.ts +47 -6
  254. package/dist/molecules/DropdownItem.svelte +122 -0
  255. package/dist/molecules/DropdownItem.svelte.d.ts +28 -0
  256. package/dist/molecules/EmptyState.svelte +55 -10
  257. package/dist/molecules/EmptyState.svelte.d.ts +8 -0
  258. package/dist/molecules/FormField.svelte +9 -1
  259. package/dist/molecules/FormField.svelte.d.ts +2 -0
  260. package/dist/molecules/Header.svelte +1 -1
  261. package/dist/molecules/ImageUpload.svelte +492 -69
  262. package/dist/molecules/ImageUpload.svelte.d.ts +82 -2
  263. package/dist/molecules/LineChart.svelte +459 -0
  264. package/dist/molecules/LineChart.svelte.d.ts +53 -0
  265. package/dist/molecules/MapView.svelte +565 -0
  266. package/dist/molecules/MapView.svelte.d.ts +88 -0
  267. package/dist/molecules/MediaGrid.svelte +527 -0
  268. package/dist/molecules/MediaGrid.svelte.d.ts +77 -0
  269. package/dist/molecules/Modal.svelte +293 -72
  270. package/dist/molecules/Modal.svelte.d.ts +51 -2
  271. package/dist/molecules/NavigationMenuContent.svelte +58 -1
  272. package/dist/molecules/NavigationMenuItem.svelte +0 -1
  273. package/dist/molecules/NavigationMenuLink.svelte +1 -1
  274. package/dist/molecules/NavigationMenuList.svelte +10 -3
  275. package/dist/molecules/NavigationMenuTrigger.svelte +2 -2
  276. package/dist/molecules/Page.svelte +32 -2
  277. package/dist/molecules/Page.svelte.d.ts +11 -0
  278. package/dist/molecules/PhotoGrid.svelte +523 -0
  279. package/dist/molecules/PhotoGrid.svelte.d.ts +73 -0
  280. package/dist/molecules/PhotoViewer.svelte +1213 -0
  281. package/dist/molecules/PhotoViewer.svelte.d.ts +74 -0
  282. package/dist/molecules/PieChart.svelte +421 -0
  283. package/dist/molecules/PieChart.svelte.d.ts +51 -0
  284. package/dist/molecules/PropsTable.svelte +22 -10
  285. package/dist/molecules/PropsTable.svelte.d.ts +3 -0
  286. package/dist/molecules/PullToRefresh.svelte +263 -0
  287. package/dist/molecules/PullToRefresh.svelte.d.ts +52 -0
  288. package/dist/molecules/RadioGroup.svelte +72 -18
  289. package/dist/molecules/RadioGroup.svelte.d.ts +19 -3
  290. package/dist/molecules/Section.svelte +1 -1
  291. package/dist/molecules/SegmentedControl.svelte +456 -0
  292. package/dist/molecules/SegmentedControl.svelte.d.ts +40 -0
  293. package/dist/molecules/SidebarBrandHeader.svelte +11 -1
  294. package/dist/molecules/SidebarBrandHeader.svelte.d.ts +3 -0
  295. package/dist/molecules/SidebarFooter.svelte +30 -7
  296. package/dist/molecules/SidebarFooter.svelte.d.ts +3 -0
  297. package/dist/molecules/SidebarNavSection.svelte +1 -1
  298. package/dist/molecules/SlideUp.svelte +220 -61
  299. package/dist/molecules/SlideUp.svelte.d.ts +26 -0
  300. package/dist/molecules/SortableList.svelte +633 -0
  301. package/dist/molecules/SortableList.svelte.d.ts +69 -0
  302. package/dist/molecules/Stepper.svelte +562 -0
  303. package/dist/molecules/Stepper.svelte.d.ts +39 -0
  304. package/dist/molecules/StickyActionBar.svelte +250 -0
  305. package/dist/molecules/StickyActionBar.svelte.d.ts +78 -0
  306. package/dist/molecules/SwipeableListItem.svelte +334 -0
  307. package/dist/molecules/SwipeableListItem.svelte.d.ts +54 -0
  308. package/dist/molecules/Tabs.svelte +310 -9
  309. package/dist/molecules/Tabs.svelte.d.ts +7 -0
  310. package/dist/molecules/Toaster.svelte +514 -5
  311. package/dist/molecules/Toaster.svelte.d.ts +153 -2
  312. package/dist/molecules/ToasterToast.svelte +495 -77
  313. package/dist/molecules/ToasterToast.svelte.d.ts +15 -0
  314. package/dist/molecules/UnsavedChangesBar.svelte +223 -0
  315. package/dist/molecules/UnsavedChangesBar.svelte.d.ts +43 -0
  316. package/dist/molecules/index.d.ts +51 -0
  317. package/dist/molecules/index.js +31 -0
  318. package/dist/molecules/toast-store.d.ts +83 -7
  319. package/dist/molecules/toast-store.js +25 -1
  320. package/dist/organisms/AppNavigation.svelte +440 -0
  321. package/dist/organisms/AppNavigation.svelte.d.ts +72 -0
  322. package/dist/organisms/AppShell.svelte +696 -0
  323. package/dist/organisms/AppShell.svelte.d.ts +157 -0
  324. package/dist/organisms/AppShellHeaderPart.svelte +163 -0
  325. package/dist/organisms/AppShellHeaderPart.svelte.d.ts +57 -0
  326. package/dist/organisms/SidebarAccountPanel.svelte +107 -12
  327. package/dist/organisms/SidebarAccountPanel.svelte.d.ts +19 -0
  328. package/dist/organisms/SidebarNavigation.svelte +111 -43
  329. package/dist/organisms/SidebarNavigation.svelte.d.ts +29 -1
  330. package/dist/organisms/SidebarPanel.svelte +34 -17
  331. package/dist/organisms/SidebarPanel.svelte.d.ts +1 -0
  332. package/dist/organisms/SidebarShell.svelte +164 -14
  333. package/dist/organisms/SidebarShell.svelte.d.ts +56 -2
  334. package/dist/organisms/TopNavbar.svelte +353 -39
  335. package/dist/organisms/TopNavbar.svelte.d.ts +47 -0
  336. package/dist/organisms/TopNavbarSheetContent.svelte +145 -0
  337. package/dist/organisms/TopNavbarSheetContent.svelte.d.ts +24 -0
  338. package/dist/organisms/index.d.ts +10 -0
  339. package/dist/organisms/index.js +7 -0
  340. package/dist/types/components.d.ts +21 -1
  341. package/dist/types/components.d.ts.map +1 -1
  342. package/dist/types/index.d.ts +1093 -26
  343. package/dist/types/index.d.ts.map +1 -1
  344. package/dist/types/variants.ts +28 -1
  345. package/dist/util/app-shell.d.ts +78 -0
  346. package/dist/util/app-shell.js +97 -0
  347. package/dist/util/avatar.d.ts +38 -0
  348. package/dist/util/avatar.js +52 -0
  349. package/dist/util/bottom-sheet.d.ts +29 -0
  350. package/dist/util/bottom-sheet.js +43 -0
  351. package/dist/util/bottom-tab-bar.d.ts +40 -0
  352. package/dist/util/bottom-tab-bar.js +54 -0
  353. package/dist/util/calendar.d.ts +55 -0
  354. package/dist/util/calendar.js +74 -0
  355. package/dist/util/chart.d.ts +139 -0
  356. package/dist/util/chart.js +305 -0
  357. package/dist/util/chip.d.ts +65 -0
  358. package/dist/util/chip.js +58 -0
  359. package/dist/util/cn.js +28 -2
  360. package/dist/util/code-field.d.ts +62 -0
  361. package/dist/util/code-field.js +112 -0
  362. package/dist/util/collapsible.d.ts +63 -0
  363. package/dist/util/collapsible.js +23 -0
  364. package/dist/util/confirm-dialog.d.ts +11 -0
  365. package/dist/util/confirm-dialog.js +2 -0
  366. package/dist/util/date.d.ts +95 -0
  367. package/dist/util/date.js +237 -0
  368. package/dist/util/display-voice.d.ts +9 -0
  369. package/dist/util/display-voice.js +9 -0
  370. package/dist/util/drawer.d.ts +12 -0
  371. package/dist/util/drawer.js +7 -0
  372. package/dist/util/dropdown.d.ts +46 -0
  373. package/dist/util/dropdown.js +21 -0
  374. package/dist/util/field-control.d.ts +29 -0
  375. package/dist/util/field-control.js +32 -0
  376. package/dist/util/field.d.ts +44 -0
  377. package/dist/util/field.js +38 -0
  378. package/dist/util/fit-in-viewport.d.ts +200 -0
  379. package/dist/util/fit-in-viewport.js +399 -0
  380. package/dist/util/focus-utils.d.ts +77 -2
  381. package/dist/util/focus-utils.js +186 -9
  382. package/dist/util/hydration.d.ts +44 -0
  383. package/dist/util/hydration.js +58 -0
  384. package/dist/util/keyboard-inset.d.ts +17 -0
  385. package/dist/util/keyboard-inset.js +53 -0
  386. package/dist/util/map-view.d.ts +143 -0
  387. package/dist/util/map-view.js +121 -0
  388. package/dist/util/media-grid.d.ts +50 -0
  389. package/dist/util/media-grid.js +81 -0
  390. package/dist/util/media.d.ts +5 -0
  391. package/dist/util/media.js +10 -0
  392. package/dist/util/overlay-chrome.d.ts +52 -0
  393. package/dist/util/overlay-chrome.js +56 -0
  394. package/dist/util/overlay.d.ts +71 -0
  395. package/dist/util/overlay.js +240 -0
  396. package/dist/util/photo.d.ts +233 -0
  397. package/dist/util/photo.js +209 -0
  398. package/dist/util/portal.d.ts +22 -0
  399. package/dist/util/portal.js +50 -0
  400. package/dist/util/progress.d.ts +18 -0
  401. package/dist/util/progress.js +17 -0
  402. package/dist/util/pull-to-refresh.d.ts +26 -0
  403. package/dist/util/pull-to-refresh.js +30 -0
  404. package/dist/util/radio-keys.d.ts +24 -0
  405. package/dist/util/radio-keys.js +49 -0
  406. package/dist/util/rating.d.ts +22 -0
  407. package/dist/util/rating.js +26 -0
  408. package/dist/util/ready-made-strings.d.ts +76 -0
  409. package/dist/util/ready-made-strings.js +67 -0
  410. package/dist/util/scroll-edge.d.ts +32 -0
  411. package/dist/util/scroll-edge.js +87 -0
  412. package/dist/util/scroll-padding.d.ts +19 -0
  413. package/dist/util/scroll-padding.js +44 -0
  414. package/dist/util/segmented-control.d.ts +15 -0
  415. package/dist/util/segmented-control.js +1 -0
  416. package/dist/util/select.d.ts +40 -0
  417. package/dist/util/select.js +63 -0
  418. package/dist/util/sheet-drag.d.ts +83 -0
  419. package/dist/util/sheet-drag.js +311 -0
  420. package/dist/util/sidebar.d.ts +62 -0
  421. package/dist/util/sidebar.js +38 -0
  422. package/dist/util/skeleton.d.ts +14 -0
  423. package/dist/util/skeleton.js +22 -0
  424. package/dist/util/sliding-indicator.d.ts +24 -0
  425. package/dist/util/sliding-indicator.js +44 -0
  426. package/dist/util/sortable-list.d.ts +64 -0
  427. package/dist/util/sortable-list.js +53 -0
  428. package/dist/util/stepper.d.ts +39 -0
  429. package/dist/util/stepper.js +38 -0
  430. package/dist/util/swipeable-list-item.d.ts +35 -0
  431. package/dist/util/swipeable-list-item.js +32 -0
  432. package/dist/util/temporal-field.d.ts +88 -0
  433. package/dist/util/temporal-field.js +136 -0
  434. package/dist/util/theme-mode.d.ts +79 -0
  435. package/dist/util/theme-mode.js +121 -0
  436. package/dist/util/toaster.d.ts +132 -0
  437. package/dist/util/toaster.js +130 -0
  438. package/dist/util/tone-surface.d.ts +33 -0
  439. package/dist/util/tone-surface.js +35 -0
  440. package/dist/util/tooltip.d.ts +9 -0
  441. package/dist/util/tooltip.js +18 -0
  442. package/dist/util/top-navbar.d.ts +34 -0
  443. package/dist/util/top-navbar.js +28 -0
  444. package/dist/util/touch-target.d.ts +30 -0
  445. package/dist/util/touch-target.js +30 -0
  446. package/dist/util/zabi-strings.d.ts +141 -0
  447. package/dist/util/zabi-strings.js +105 -0
  448. package/dist/zabi-components-colors.css +11 -2
  449. package/dist/zabi-components-theme-dark-only.css +870 -258
  450. package/dist/zabi-components-theme-dark.css +870 -258
  451. package/dist/zabi-components-theme-only.css +2419 -368
  452. package/dist/zabi-components-theme.css +2419 -368
  453. package/dist/zabi-components.css +6032 -711
  454. package/docs/lucide-icons.md +40 -17
  455. package/docs/theme-imports.md +81 -11
  456. package/package.json +45 -8
@@ -0,0 +1,131 @@
1
+ import type { Snippet } from "svelte";
2
+ import type { HTMLAttributes } from "svelte/elements";
3
+ import { type ScrollEdgeMode } from "../util/scroll-edge.js";
4
+ /**
5
+ * The bar at the top of a phone screen: where you are, the way back, and
6
+ * at most two things to do here.
7
+ *
8
+ * It is one row, 56px tall, whenever the title has 72px or more beside
9
+ * the controls, at every text size. The bar is chrome: when the reader
10
+ * enlarges text, the room belongs to the page under it. So the controls
11
+ * are sized in px (48px targets), the title grows with the text only up
12
+ * to 1.3 times its size, and a title too long for the room it has is cut
13
+ * with an ellipsis (after two lines with `titleLines={2}`). The heading
14
+ * keeps its full text for a screen reader; keep the visible start of it
15
+ * enough to name the screen.
16
+ *
17
+ * The title never has less than 72px. Where the back control, `leading`
18
+ * and the actions leave it less (a wide chip in `leading`, a very narrow
19
+ * bar), it takes a second row of its own, as wide as the bar, and they
20
+ * stay together on the first.
21
+ *
22
+ * The bar is flush with the page and page-coloured at rest. Once content
23
+ * has scrolled under it, it turns to frosted glass with a hairline below
24
+ * it. Inside an `AppShell` the shell says when; on a page of its own the
25
+ * bar watches the window or its nearest scrolling ancestor.
26
+ *
27
+ * With `largeTitle` the title is also drawn large on a second row under
28
+ * the bar. That row scrolls away with the content (nothing is animated),
29
+ * the 56px row stays, and its own small title fades in once the large
30
+ * one has gone under it. The heading is the large one.
31
+ */
32
+ type Props = Omit<HTMLAttributes<HTMLElement>, "class" | "title"> & {
33
+ /** The name of the screen, as a heading. */
34
+ title?: string;
35
+ /** Heading level of the title. 1 when the bar names the page. */
36
+ headingLevel?: 1 | 2 | 3 | 4 | 5 | 6;
37
+ /** Shows the back control as a link to this address. */
38
+ backHref?: string;
39
+ /**
40
+ * Called when the back control is activated. On its own it makes the
41
+ * control a button; with `backHref` it runs before the link is
42
+ * followed, and `event.preventDefault()` stops that.
43
+ */
44
+ onback?: (event: MouseEvent) => void;
45
+ /** Accessible name of the back control. */
46
+ backLabel?: string;
47
+ /**
48
+ * Hides the bar while the page scrolls down and brings it back as
49
+ * soon as it scrolls up. It follows the nearest scrolling ancestor
50
+ * (the one in `AppShell`), or the window. The bar never hides while
51
+ * keyboard focus is inside it, and focus arriving from the keyboard
52
+ * brings it back. Focus left behind by a tap or a click does not hold
53
+ * it: it hides as usual, and the next key press brings it back.
54
+ */
55
+ collapseOnScroll?: boolean;
56
+ /**
57
+ * How many lines the title may take before it is cut with an
58
+ * ellipsis. The bar is taller only while the second line is in use.
59
+ */
60
+ titleLines?: 1 | 2;
61
+ /**
62
+ * Before the title, after the back control: a logo, an avatar, a
63
+ * chip. Rendered as it is, a child of the bar's row. It keeps its
64
+ * width; when that leaves the title under 72px the title moves to a
65
+ * row of its own, and a `leading` wider than the row is held to the
66
+ * row's width.
67
+ * The bar's row is on a fixed pixel scale (`--spacing: 4px` on the
68
+ * row), so the bar's own controls do not grow with the text size.
69
+ * Size what you place here on the same scale (Tailwind numeric
70
+ * classes such as `size-9`, or `calc(var(--spacing) * 9)`), not in
71
+ * rem, or it grows at 200% text while the bar's controls do not.
72
+ */
73
+ leading?: Snippet;
74
+ /**
75
+ * After the title. At most two icon buttons, at `size="lg"` so each is
76
+ * a 48px target; put anything more in a menu. Their size does not
77
+ * grow with the text size here: inside the bar the spacing scale is
78
+ * in px.
79
+ * The bar's row is on a fixed pixel scale (`--spacing: 4px` on the
80
+ * row), so the bar's own controls do not grow with the text size.
81
+ * Size what you place here on the same scale (Tailwind numeric
82
+ * classes such as `size-9`, or `calc(var(--spacing) * 9)`), not in
83
+ * rem, or it grows at 200% text while the bar's controls do not.
84
+ */
85
+ actions?: Snippet;
86
+ /**
87
+ * When the bar shows the glass and the hairline. `auto` follows the
88
+ * scroll position: flush at the top, glass once content is under it.
89
+ * `always` is glass all the time, `never` flush all the time.
90
+ */
91
+ scrollEdge?: ScrollEdgeMode;
92
+ /**
93
+ * Draws the title large (30px bold, two lines at most) on a second row
94
+ * under the bar. It scrolls away with the content; the bar keeps its
95
+ * 56px row, which shows the title small once the large one is gone.
96
+ * The heading is the large title; the small one is hidden from
97
+ * assistive technology. Needs `title`. `collapseOnScroll` is ignored
98
+ * with it. Before the bar has been measured (the server, no scripts)
99
+ * the whole header simply sticks and the large title stays.
100
+ */
101
+ largeTitle?: boolean;
102
+ /**
103
+ * The bar's fill. `default` is the page colour at rest. `transparent`
104
+ * has none at rest, for a canvas, an image or a colour block laid
105
+ * behind the bar, and takes the glass once content is under it.
106
+ * `brand` is the brand colour with on-brand text and controls, at rest
107
+ * and scrolled, with no glass and no hairline, so a brand block
108
+ * directly under the bar joins it. With `largeTitle` the large row is
109
+ * part of the brand block.
110
+ * `inherit` is for a bar inside a block of another colour: no fill of
111
+ * its own, no glass and no hairline at any scroll position, and the
112
+ * title, back control, actions, links, borders and focus rings take
113
+ * the colour the block gives the text. A sticky bar that has to cover
114
+ * scrolling content takes its fill from the app:
115
+ * `class="[--color-bar:var(--my-block-colour)]"`.
116
+ */
117
+ tone?: "default" | "transparent" | "brand" | "inherit";
118
+ /**
119
+ * `sticky` (default) keeps the bar at the top of what scrolls.
120
+ * `static` leaves it where it is in the page, for a bar that sits
121
+ * inside a block of the content: it scrolls away with the block, is
122
+ * never glass, and `collapseOnScroll` and the condensing of
123
+ * `largeTitle` do nothing. `class="static"`, the 8.1 way to say this,
124
+ * is read as `position="static"`.
125
+ */
126
+ position?: "sticky" | "static";
127
+ class?: string;
128
+ };
129
+ declare const AppBar: import("svelte").Component<Props, {}, "">;
130
+ type AppBar = ReturnType<typeof AppBar>;
131
+ export default AppBar;
@@ -0,0 +1,116 @@
1
+ <script lang="ts">
2
+ import { mergeStrings } from "../util/ready-made-strings.js";
3
+ import { zabiStringsFor } from "../util/zabi-strings.js";
4
+ import type { HTMLAttributes } from "svelte/elements";
5
+ import Avatar from "../atoms/Avatar.svelte";
6
+ import {
7
+ DEFAULT_AVATAR_GROUP_STRINGS,
8
+ splitGroup,
9
+ type AvatarGroupStrings,
10
+ type AvatarPerson,
11
+ type AvatarSize,
12
+ } from "../util/avatar.js";
13
+ import { cn } from "../util/cn.js";
14
+
15
+ /**
16
+ * A row of overlapping avatars: who is going, the members, who rated.
17
+ *
18
+ * It is a list, each avatar named by its person. Past `max` the rest
19
+ * stand behind a "+3" of the same size and shape. Not interactive.
20
+ *
21
+ * Between two avatars is a ring in the colour of the surface under the
22
+ * group. It cannot read that colour, so it is the raised surface (a
23
+ * card) unless the group is told otherwise:
24
+ *
25
+ * ```svelte
26
+ * <AvatarGroup {people} style="--zabi-avatar-ring: var(--color-surface-base)" />
27
+ * ```
28
+ *
29
+ * Other attributes (`id`, `data-*`, `style`, ...) land on the list.
30
+ */
31
+ type Props = Omit<HTMLAttributes<HTMLUListElement>, "class" | "aria-label"> & {
32
+ /** The people, in the order they are shown. */
33
+ people: AvatarPerson[];
34
+ /**
35
+ * How many are shown before the rest become "+N". Below 1 is 1. One
36
+ * person too many is shown and not counted: there is no "+1".
37
+ */
38
+ max?: number;
39
+ /** Diameter of each avatar: 24, 32 and 48px. */
40
+ size?: "sm" | "md" | "lg";
41
+ /** Accessible name of the list ("Who's going"). */
42
+ label?: string;
43
+ /** Overrides for the built-in strings. */
44
+ strings?: Partial<AvatarGroupStrings>;
45
+ /** The language the initials are worked out in; passed to each Avatar. Without it, the page's. */
46
+ locale?: string;
47
+ /** Extra classes for the list. */
48
+ class?: string;
49
+ };
50
+
51
+ let {
52
+ people,
53
+ max = 4,
54
+ size = "md",
55
+ label,
56
+ strings,
57
+ locale,
58
+ class: className = "",
59
+ ...restProps
60
+ }: Props = $props();
61
+
62
+ /** The app-wide words for this component, from a `ZabiStringsProvider` above it, if there is one. */
63
+ const provided = zabiStringsFor("avatarGroup");
64
+ const text = $derived(mergeStrings(DEFAULT_AVATAR_GROUP_STRINGS, provided(), strings));
65
+ const split = $derived(splitGroup(people.length, max));
66
+ const shown = $derived(people.slice(0, split.shown));
67
+ const hiddenNames = $derived(people.slice(split.shown).map((person) => person.name));
68
+
69
+ // A quarter of the circle lies under the next one.
70
+ const OVERLAP: Record<AvatarSize, string> = {
71
+ sm: "-ms-[6px]",
72
+ md: "-ms-[8px]",
73
+ lg: "-ms-[12px]",
74
+ };
75
+ const CHIP: Record<AvatarSize, string> = {
76
+ sm: "size-[24px] text-[10px]",
77
+ md: "size-[32px] text-[12px]",
78
+ lg: "size-[48px] text-[16px]",
79
+ };
80
+
81
+ /** The ring that parts one circle from the next. A shadow, so it takes no room; forced colours draw the avatars' own borders instead. */
82
+ const ring = "rounded-pill shadow-[0_0_0_2px_var(--zabi-avatar-ring,var(--color-surface-raised))]";
83
+ </script>
84
+
85
+ <!-- `role`, because a list without bullets loses its role in Safari. -->
86
+ <ul
87
+ class={cn("m-0 flex list-none items-center p-0", className)}
88
+ role="list"
89
+ aria-label={label || undefined}
90
+ data-avatar-group
91
+ data-size={size}
92
+ {...restProps}
93
+ >
94
+ {#each shown as person, index (index)}
95
+ <li class={cn("flex", index > 0 && (OVERLAP[size] ?? OVERLAP.md))}>
96
+ <Avatar name={person.name} src={person.src} children={person.picture} {size} {locale} class={ring} />
97
+ </li>
98
+ {/each}
99
+ {#if split.hidden > 0}
100
+ <li class={cn("flex", OVERLAP[size] ?? OVERLAP.md)}>
101
+ <!-- The same circle as an avatar, in the neutral pair: it is a count, not a person. -->
102
+ <span
103
+ class={cn(
104
+ "inline-flex shrink-0 items-center justify-center border border-transparent bg-neutral-subtle leading-none font-semibold text-neutral-text tabular-nums select-none",
105
+ CHIP[size] ?? CHIP.md,
106
+ ring,
107
+ )}
108
+ role="img"
109
+ aria-label={text.more(split.hidden, hiddenNames)}
110
+ data-avatar-more
111
+ >
112
+ <span aria-hidden="true">+{split.hidden}</span>
113
+ </span>
114
+ </li>
115
+ {/if}
116
+ </ul>
@@ -0,0 +1,40 @@
1
+ import type { HTMLAttributes } from "svelte/elements";
2
+ import { type AvatarGroupStrings, type AvatarPerson } from "../util/avatar.js";
3
+ /**
4
+ * A row of overlapping avatars: who is going, the members, who rated.
5
+ *
6
+ * It is a list, each avatar named by its person. Past `max` the rest
7
+ * stand behind a "+3" of the same size and shape. Not interactive.
8
+ *
9
+ * Between two avatars is a ring in the colour of the surface under the
10
+ * group. It cannot read that colour, so it is the raised surface (a
11
+ * card) unless the group is told otherwise:
12
+ *
13
+ * ```svelte
14
+ * <AvatarGroup {people} style="--zabi-avatar-ring: var(--color-surface-base)" />
15
+ * ```
16
+ *
17
+ * Other attributes (`id`, `data-*`, `style`, ...) land on the list.
18
+ */
19
+ type Props = Omit<HTMLAttributes<HTMLUListElement>, "class" | "aria-label"> & {
20
+ /** The people, in the order they are shown. */
21
+ people: AvatarPerson[];
22
+ /**
23
+ * How many are shown before the rest become "+N". Below 1 is 1. One
24
+ * person too many is shown and not counted: there is no "+1".
25
+ */
26
+ max?: number;
27
+ /** Diameter of each avatar: 24, 32 and 48px. */
28
+ size?: "sm" | "md" | "lg";
29
+ /** Accessible name of the list ("Who's going"). */
30
+ label?: string;
31
+ /** Overrides for the built-in strings. */
32
+ strings?: Partial<AvatarGroupStrings>;
33
+ /** The language the initials are worked out in; passed to each Avatar. Without it, the page's. */
34
+ locale?: string;
35
+ /** Extra classes for the list. */
36
+ class?: string;
37
+ };
38
+ declare const AvatarGroup: import("svelte").Component<Props, {}, "">;
39
+ type AvatarGroup = ReturnType<typeof AvatarGroup>;
40
+ export default AvatarGroup;
@@ -0,0 +1,381 @@
1
+ <script lang="ts">
2
+ import type { HTMLAttributes } from "svelte/elements";
3
+ import Table from "../atoms/Table.svelte";
4
+ import { cn } from "../util/cn.js";
5
+ import {
6
+ CHART_GLASS,
7
+ chartColor,
8
+ chartFormatter,
9
+ barPath,
10
+ chartScale,
11
+ chartTextScale,
12
+ labelEvery,
13
+ measureWidth,
14
+ widestText,
15
+ type ChartColor,
16
+ type ChartDatum,
17
+ type ChartFormat,
18
+ } from "../util/chart.js";
19
+
20
+ /**
21
+ * A bar chart of one set of values, drawn as plain SVG: upright bars from
22
+ * a zero line, a label under each and its value over it. A bar is drawn
23
+ * the way `Progress` draws its fill, and with a `yMax` each bar stands in
24
+ * a track up to it, as a `Progress` does: so many of so many.
25
+ *
26
+ * It is a `figure`: the `label` is its caption, the drawing is hidden
27
+ * from assistive technology, and the same numbers are in a real table
28
+ * (off screen unless `showTable`). The bars are one colour, since the
29
+ * labels tell them apart; a datum may carry its own.
30
+ */
31
+ type Props = Omit<HTMLAttributes<HTMLElement>, "class"> & {
32
+ /** The bars, in order: a `label`, a `value`, and a `color` of its own if it needs one. */
33
+ data: ChartDatum[];
34
+ /** The colour of the bars: a theme colour by name or any CSS colour. Without it, the first series colour. */
35
+ color?: ChartColor;
36
+ /** The top of the value axis, and the ceiling each bar is measured against: with it every bar stands in a track that reaches it. Without it, the highest value rounded up to a tick, and no tracks. */
37
+ yMax?: number;
38
+ /** How a value is written: `number` (default), `percent` (72 is "72 %"), or a function. */
39
+ yFormat?: ChartFormat;
40
+ /** Height of the drawing in px. The width is the container's. */
41
+ height?: number;
42
+ /** The caption, and what the chart is called. */
43
+ label: string;
44
+ /** Keeps the caption for assistive technology and takes it off the screen. */
45
+ labelHidden?: boolean;
46
+ /** Writes each value over its bar. */
47
+ showValues?: boolean;
48
+ /** Shows the data table under the chart; otherwise it is there for assistive technology only. */
49
+ showTable?: boolean;
50
+ /** The heading of the table's first column. */
51
+ categoryTitle?: string;
52
+ /** The heading of the table's value column. */
53
+ valueTitle?: string;
54
+ /** The language of the built-in number formats. Without it, the runtime's. */
55
+ locale?: string;
56
+ /** Stands the chart on a panel of the thick material (frosted glass), for a chart that floats over a wash, a tint or a picture. On a plain card it shows nothing: leave it off there. */
57
+ glass?: boolean;
58
+ class?: string;
59
+ };
60
+
61
+ let {
62
+ data,
63
+ color,
64
+ yMax,
65
+ yFormat = "number",
66
+ height = 240,
67
+ label,
68
+ labelHidden = false,
69
+ showValues = true,
70
+ showTable = false,
71
+ categoryTitle = "Category",
72
+ valueTitle = "Value",
73
+ locale,
74
+ glass = false,
75
+ class: className = "",
76
+ ...restProps
77
+ }: Props = $props();
78
+
79
+ /** Before it is measured (on the server) the drawing is laid out this wide and scaled to fit. */
80
+ const FALLBACK_WIDTH = 600;
81
+ /** The widest a bar gets: a bar is a `Progress` stood on end, a rod and not a block. */
82
+ const WIDEST = 24;
83
+
84
+ const uid = $props.id();
85
+ const clipId = `${uid}-bars`;
86
+ const veilId = `${uid}-veil`;
87
+ const shineId = `${uid}-shine`;
88
+ const streakId = `${uid}-streak`;
89
+ const softId = `${uid}-soft`;
90
+
91
+ let measured = $state(0);
92
+ let tickWidth = $state(0);
93
+
94
+ const width = $derived(measured > 0 ? measured : FALLBACK_WIDTH);
95
+ /** Chart text is set in rem; the room for it grows with the reader's text size. */
96
+ const rem = $derived(measured > 0 ? chartTextScale() : 1);
97
+ const plotHeight = $derived(Math.max(height, 80));
98
+ const top = $derived(showValues ? Math.round(24 * rem) : 8);
99
+ const bottom = $derived(Math.round(28 * rem));
100
+ const format = $derived(chartFormatter(yFormat, locale));
101
+ const isValue = (value: unknown): value is number => typeof value === "number" && Number.isFinite(value);
102
+ const scale = $derived(chartScale(data.map((datum) => datum.value), { max: yMax, zero: true }));
103
+ const tickLabels = $derived(scale.ticks.map((tick) => format(tick)));
104
+ /** The value axis takes the width of its widest label as drawn; until that is known, an estimate. */
105
+ const left = $derived((tickWidth || Math.max(...tickLabels.map((text) => text.length), 1) * 7 * rem) + 12);
106
+ const innerWidth = $derived(Math.max(width - left, 10));
107
+ const innerHeight = $derived(Math.max(plotHeight - top - bottom, 10));
108
+ const band = $derived(innerWidth / Math.max(data.length, 1));
109
+ const barWidth = $derived(Math.max(1, Math.min(band * 0.5, WIDEST)));
110
+ /** A ceiling the app gave, and nothing below zero: each bar has a track up to it. */
111
+ const tracks = $derived(isValue(yMax) && scale.min >= 0 && scale.max > 0);
112
+ const every = $derived(labelEvery(data.map((datum) => datum.label), innerWidth, 7 * rem));
113
+ /** A value over every bar only while each has room for it. */
114
+ const valuesFit = $derived(showValues && band >= Math.max(...tickLabels.map((text) => text.length), 1) * 7 * rem);
115
+ /** Where the bars stand: zero, or the end of the axis nearest to it. */
116
+ const zero = $derived(Math.min(Math.max(0, scale.min), scale.max));
117
+
118
+ const y = (value: number) => top + innerHeight - ((value - scale.min) / (scale.max - scale.min)) * innerHeight;
119
+
120
+ const bars = $derived(
121
+ data.map((datum, index) => {
122
+ const value = isValue(datum.value) ? Math.min(Math.max(datum.value, scale.min), scale.max) : 0;
123
+ const from = y(zero);
124
+ const to = y(value);
125
+ return {
126
+ label: datum.label,
127
+ text: isValue(datum.value) ? format(datum.value) : "",
128
+ color: datum.color ? chartColor(datum.color, index) : chartColor(color, 0),
129
+ centre: left + band * (index + 0.5),
130
+ top: Math.min(from, to),
131
+ height: Math.abs(from - to),
132
+ path: barPath(left + band * (index + 0.5) - barWidth / 2, barWidth, from, to, barWidth / 2),
133
+ track: barPath(left + band * (index + 0.5) - barWidth / 2, barWidth, from, y(scale.max), barWidth / 2),
134
+ below: value < zero,
135
+ };
136
+ }),
137
+ );
138
+ </script>
139
+
140
+ <figure class={cn("zabi-chart relative m-0 flex flex-col gap-3", glass && CHART_GLASS, className)} {...restProps}>
141
+ <figcaption class={cn("text-label text-sm font-medium", labelHidden && "sr-only")}>{label}</figcaption>
142
+
143
+ <div class="w-full" use:measureWidth={(value) => (measured = value)}>
144
+ <svg class="plot block w-full" viewBox="0 0 {width} {plotHeight}" height={plotHeight} aria-hidden="true" focusable="false">
145
+ <g use:widestText={{ onwidth: (value) => (tickWidth = value), key: [tickLabels, measured] }}>
146
+ {#each scale.ticks as tick, index (tick)}
147
+ {#if tick !== zero}
148
+ <line class="grid" x1={left} x2={left + innerWidth} y1={y(tick)} y2={y(tick)} />
149
+ {/if}
150
+ <text class="tick" x={left - 12} y={y(tick)} text-anchor="end" dominant-baseline="central">
151
+ {tickLabels[index]}
152
+ </text>
153
+ {/each}
154
+ </g>
155
+
156
+ {#each bars as bar, index (index)}
157
+ {#if tracks}<path class="track" d={bar.track} />{/if}
158
+ <path class="bar" style:--_c={bar.color} d={bar.path}>
159
+ <title>{bar.label}: {bar.text}</title>
160
+ </path>
161
+ {#if valuesFit && bar.text}
162
+ <text
163
+ class="value"
164
+ x={bar.centre}
165
+ y={bar.below ? bar.top + bar.height + 16 * rem : (tracks ? top : bar.top) - 8}
166
+ text-anchor="middle"
167
+ >
168
+ {bar.text}
169
+ </text>
170
+ {/if}
171
+ {#if index % every === 0}
172
+ <text class="label" x={bar.centre} y={plotHeight - bottom + 20 * rem} text-anchor="middle">{bar.label}</text>
173
+ {/if}
174
+ {/each}
175
+
176
+ <!--
177
+ The glass, as on a PieChart: the bars keep their colour, opaque, and over them
178
+ lie the veil of a solid control, a hairline of light on the round end of each
179
+ bar and a soft sheen down its near side. Decoration only; not drawn where the materials are opaque.
180
+ -->
181
+ <defs>
182
+ <clipPath id={clipId}>
183
+ {#each bars as bar, index (index)}<path d={bar.path} />{/each}
184
+ </clipPath>
185
+ <linearGradient id={veilId} x1="0" y1="0" x2="0" y2="1">
186
+ <stop class="veil-top" offset="0" />
187
+ <stop class="veil-bottom" offset="1" />
188
+ </linearGradient>
189
+ <linearGradient id={shineId} x1="0" y1="0" x2="0" y2="1">
190
+ <stop class="light" offset="0" />
191
+ <stop class="light off" offset="0.5" />
192
+ </linearGradient>
193
+ <linearGradient id={streakId} gradientUnits="userSpaceOnUse" x1="0" y1={top} x2="0" y2={top + innerHeight}>
194
+ <stop class="light" offset="0" />
195
+ <stop class="light off" offset="1" />
196
+ </linearGradient>
197
+ <filter id={softId} filterUnits="userSpaceOnUse" x="0" y="0" {width} height={plotHeight}>
198
+ <feGaussianBlur stdDeviation="1.2" />
199
+ </filter>
200
+ </defs>
201
+ <g class="glass" clip-path="url(#{clipId})">
202
+ {#each bars as bar, index (index)}
203
+ {#if bar.height > 0}
204
+ <path class="veil" d={bar.path} fill="url(#{veilId})" />
205
+ <path class="rim" d={bar.path} stroke="url(#{shineId})" />
206
+ {#if barWidth >= 12 && bar.height > barWidth}
207
+ <line
208
+ class="sheen"
209
+ filter="url(#{softId})"
210
+ x1={bar.centre - barWidth / 2 + 4.5}
211
+ x2={bar.centre - barWidth / 2 + 4.5}
212
+ y1={bar.top + barWidth / 2}
213
+ y2={bar.top + bar.height - barWidth / 2}
214
+ stroke="url(#{streakId})"
215
+ />
216
+ {/if}
217
+ {/if}
218
+ {/each}
219
+ </g>
220
+
221
+ <!-- The zero line is drawn last, so every bar stands on it. -->
222
+ <line class="grid axis" x1={left} x2={left + innerWidth} y1={y(zero)} y2={y(zero)} />
223
+ </svg>
224
+ </div>
225
+
226
+ <!-- The library's own Table, so the numbers look like every other table in the app. -->
227
+ <Table class={showTable ? "" : "sr-only"}>
228
+ <thead class="border-border bg-surface-elevated border-b">
229
+ <tr>
230
+ <th class="text-headline px-4 py-3 text-start font-medium" scope="col">{categoryTitle}</th>
231
+ <th class="text-headline px-4 py-3 text-end font-medium" scope="col">{valueTitle}</th>
232
+ </tr>
233
+ </thead>
234
+ <tbody class="divide-border divide-y">
235
+ {#each bars as bar, index (index)}
236
+ <tr>
237
+ <th class="text-body px-4 py-3 text-start font-normal" scope="row">{bar.label}</th>
238
+ <td class="text-body px-4 py-3 text-end tabular-nums">{bar.text}</td>
239
+ </tr>
240
+ {/each}
241
+ </tbody>
242
+ </Table>
243
+ </figure>
244
+
245
+ <style>
246
+ /*
247
+ * The series colours an app may set, on the chart or on anything around
248
+ * it; read with a fallback (in util/chart.ts) and never declared here:
249
+ * --zabi-chart-1 … --zabi-chart-6
250
+ */
251
+ .plot {
252
+ overflow: visible;
253
+ }
254
+
255
+ /* The library's hairline, as between the rows of a list; the line the bars
256
+ stand on is the edge a Progress track has. */
257
+ .grid {
258
+ stroke: var(--color-border);
259
+ stroke-width: 1;
260
+ shape-rendering: crispEdges;
261
+ }
262
+
263
+ .grid.axis {
264
+ stroke: var(--color-border-strong);
265
+ }
266
+
267
+ /* The track of a Progress, stood on end: same fill, same edge. */
268
+ .track {
269
+ fill: var(--color-progress-track);
270
+ stroke: var(--color-progress-track-border);
271
+ stroke-width: 1;
272
+ }
273
+
274
+ .tick,
275
+ .label {
276
+ fill: var(--color-caption);
277
+ font-size: 0.75rem;
278
+ font-variant-numeric: tabular-nums;
279
+ }
280
+
281
+ /* What a bar is called reads a step firmer than the scale beside it. */
282
+ .label {
283
+ fill: var(--color-description);
284
+ }
285
+
286
+ .value {
287
+ fill: var(--color-headline);
288
+ font-size: 0.75rem;
289
+ font-weight: 600;
290
+ font-variant-numeric: tabular-nums;
291
+ }
292
+
293
+ .bar {
294
+ fill: var(--_c);
295
+ }
296
+
297
+ /*
298
+ * The bars are glass, drawn, in the same terms as the slices of a PieChart (which has the
299
+ * list): the veil of a solid control, one pixel of light on the round end fading down the
300
+ * sides, a soft sheen down the near side, and the contact shadow.
301
+ */
302
+ .bar {
303
+ /* The contact shadow only: a wider one is a smudge under a rod this thin. */
304
+ filter: drop-shadow(0 1px 2px rgb(var(--shadow-color) / var(--shadow-opacity-contact)));
305
+ }
306
+
307
+ .glass {
308
+ pointer-events: none;
309
+ }
310
+
311
+ .veil-top {
312
+ stop-color: var(--color-control-gradient-accent-start);
313
+ }
314
+
315
+ .veil-bottom {
316
+ stop-color: var(--color-control-gradient-accent-end);
317
+ }
318
+
319
+ .light {
320
+ stop-color: color-mix(in srgb, var(--zabi-base-50) 30%, transparent);
321
+ }
322
+
323
+ .light.off {
324
+ stop-opacity: 0;
325
+ }
326
+
327
+ .rim {
328
+ fill: none;
329
+ stroke-width: 2px;
330
+ vector-effect: non-scaling-stroke;
331
+ }
332
+
333
+ .sheen {
334
+ stroke-width: 3;
335
+ stroke-linecap: round;
336
+ opacity: 0.45;
337
+ }
338
+
339
+ /* Flat where the library's materials are opaque: the same switches, the same reasons. */
340
+ @media (prefers-reduced-transparency: reduce), (prefers-contrast: more), (forced-colors: active) {
341
+ .glass {
342
+ display: none;
343
+ }
344
+
345
+ .bar {
346
+ filter: none;
347
+ }
348
+ }
349
+
350
+ :global([data-materials="opaque"]) .glass {
351
+ display: none;
352
+ }
353
+
354
+ :global([data-materials="opaque"]) .bar {
355
+ filter: none;
356
+ }
357
+
358
+ /* Forced colours: bars in the system's text colour; the labels tell them apart. */
359
+ @media (forced-colors: active) {
360
+ .bar,
361
+ .tick,
362
+ .label,
363
+ .value {
364
+ fill: CanvasText;
365
+ }
366
+
367
+ .grid,
368
+ .grid.axis {
369
+ stroke: GrayText;
370
+ }
371
+
372
+ .track {
373
+ fill: Canvas;
374
+ stroke: CanvasText;
375
+ }
376
+
377
+ .bar {
378
+ fill: Highlight;
379
+ }
380
+ }
381
+ </style>