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,620 @@
1
+ <script lang="ts">
2
+ import { mergeStrings } from "../util/ready-made-strings.js";
3
+ import { zabiStringsFor } from "../util/zabi-strings.js";
4
+ import Star from "@lucide/svelte/icons/star";
5
+ import X from "@lucide/svelte/icons/x";
6
+ import type { HTMLAttributes } from "svelte/elements";
7
+ import { isDevBuild } from "../util/app-shell.js";
8
+ import { cn } from "../util/cn.js";
9
+ import { resetValueOf, groupBinding } from "../util/hydration.js";
10
+ import { isRtl, radioKeyIndex } from "../util/radio-keys.js";
11
+ import {
12
+ RATING_STRINGS,
13
+ clampRating,
14
+ defaultRatingFormat,
15
+ starCount,
16
+ starFill,
17
+ type RatingStrings,
18
+ } from "../util/rating.js";
19
+ import { generateId } from "../util/ssr-safe.js";
20
+
21
+ /** Sizes of a rating. `xl` and `display` are for a large input or a compact score. */
22
+ type RatingSize = "sm" | "md" | "lg" | "xl" | "display";
23
+
24
+ /** Other attributes (`data-*`, `id`, ...) land on the host element. */
25
+ type Props = Omit<
26
+ HTMLAttributes<HTMLDivElement>,
27
+ "class" | "onchange" | "aria-label" | "aria-labelledby"
28
+ > & {
29
+ /** Extra classes for the host element. */
30
+ class?: string;
31
+ /** Stars given, or `null` for no rating; supports `bind:value`. */
32
+ value?: number | null;
33
+ /** Number of stars. */
34
+ max?: number;
35
+ /** Names the rating ("Quiz"). Shown above the stars unless `hideLabel`. */
36
+ label?: string;
37
+ /** Keeps `label` as the accessible name only. */
38
+ hideLabel?: boolean;
39
+ /** Names the rating when there is no `label`. */
40
+ "aria-label"?: string;
41
+ /** Id of the element that names the rating, when there is no `label`. */
42
+ "aria-labelledby"?: string;
43
+ /**
44
+ * Size of a star. An interactive star is a 44px target at every size.
45
+ * `xl` is 40px stars. `display` is a display-size score with `compact`
46
+ * (a 40px star and a 48px number); as a row of stars it is `xl`.
47
+ */
48
+ size?: RatingSize;
49
+ /**
50
+ * Colour of a filled star: the primary action colour, or the app's
51
+ * accent (`--color-accent`). An app can also set the colours itself:
52
+ * `--zabi-rating-on` and `--zabi-rating-off`, see THEMING.md.
53
+ */
54
+ tone?: "primary" | "accent";
55
+ /**
56
+ * Shows a score instead of asking for one: one image with one name,
57
+ * and a star can be partly filled (3.5 fills half of the fourth).
58
+ */
59
+ readonly?: boolean;
60
+ /**
61
+ * Adds a clear button beside the stars, and Delete or Backspace clears
62
+ * too. Pressing the selected star again never clears.
63
+ */
64
+ clearable?: boolean;
65
+ disabled?: boolean;
66
+ /**
67
+ * Name the value is submitted under in a form. Nothing is submitted
68
+ * without a rating. Without a name the rating is not part of the form
69
+ * around it: it submits nothing and a form reset leaves it alone.
70
+ */
71
+ name?: string;
72
+ /**
73
+ * A score for a list, not a control: one filled star and the number
74
+ * ("4,0"), from the real `value` and `max`, with one accessible name
75
+ * ("4 of 5 stars"). Implies `readonly`: `clearable`, `name` and
76
+ * `onchange` are ignored, and `showValue` too (the number is the point).
77
+ */
78
+ compact?: boolean;
79
+ /**
80
+ * What an empty star looks like: a hollow `outline` (default), or a
81
+ * `soft` fill (no outline) in `--zabi-rating-off-fill`, falling back to
82
+ * `--color-control-track`. The soft fill is below 3:1 against the page
83
+ * by default, so it suits a rating that also has a visible label and
84
+ * value. `--zabi-rating-off-fill` sets the colour; it is declared
85
+ * nowhere, like `--zabi-rating-on`. In forced colours it is an outline.
86
+ */
87
+ empty?: "outline" | "soft";
88
+ /** Shows the number beside the stars. On by default when `readonly`. */
89
+ showValue?: boolean;
90
+ /** Turns the value into text for a locale ("3,5"). At most one decimal by default. */
91
+ formatValue?: (value: number) => string;
92
+ /** Overrides for the built-in strings. */
93
+ strings?: Partial<RatingStrings>;
94
+ /** Called when the user changes the rating; `null` when it was cleared. */
95
+ onchange?: (value: number | null) => void;
96
+ };
97
+
98
+ let {
99
+ class: className = "",
100
+ value = $bindable<Exclude<Props["value"], undefined>>(),
101
+ max = 5,
102
+ label = "",
103
+ hideLabel = false,
104
+ "aria-label": ariaLabel,
105
+ "aria-labelledby": ariaLabelledby,
106
+ size = "md",
107
+ tone = "primary",
108
+ readonly = false,
109
+ clearable = false,
110
+ disabled = false,
111
+ name = "",
112
+ compact = false,
113
+ empty = "outline",
114
+ showValue,
115
+ formatValue,
116
+ strings,
117
+ onchange,
118
+ ...restProps
119
+ }: Props = $props();
120
+
121
+ // No fallback on a bindable prop: Svelte refuses `bind:…={undefined}` on
122
+ // one that has a fallback (`props_invalid_value`), and a page that throws
123
+ // while it hydrates never becomes interactive. The default is applied
124
+ // here instead: at once, for the server and the first render, and again
125
+ // whenever a parent hands back `undefined`.
126
+ const applyDefaults = () => {
127
+ if (value === undefined) value = null;
128
+ };
129
+ applyDefaults();
130
+ $effect.pre(applyDefaults);
131
+
132
+ $effect(() => {
133
+ if (!isDevBuild() || !compact || !(clearable || name)) return;
134
+ console.warn(
135
+ "[zabi-components] Rating: `compact` is a read-only score; `clearable` and `name` are ignored.",
136
+ );
137
+ });
138
+
139
+ const baseId = generateId("rating");
140
+ const labelId = `${baseId}-label`;
141
+ const valueId = `${baseId}-value`;
142
+ /**
143
+ * The radios always share a name, the caller's or one made here: without
144
+ * one they are not a group to the browser, and before the page hydrates a
145
+ * second choice would leave the first one checked beside it.
146
+ *
147
+ * With a name made here they are also kept out of any form around them
148
+ * (`form` names a form that does not exist), so nothing is submitted
149
+ * that the caller did not name, as before.
150
+ */
151
+ const groupName = $derived(name || `${baseId}-group`);
152
+
153
+ /** The app-wide words for this component, from a `ZabiStringsProvider` above it, if there is one. */
154
+ const provided = zabiStringsFor("rating");
155
+ const text = $derived(mergeStrings(RATING_STRINGS, provided(), strings));
156
+ const count = $derived(starCount(max));
157
+ const stars = $derived(Array.from({ length: count }, (_, index) => index + 1));
158
+ /** What the stars show: the value held within 0..max. */
159
+ const shown = $derived(clampRating(value, count));
160
+ const valueShown = $derived(showValue ?? readonly);
161
+ const labelVisible = $derived(!!label && !hideLabel);
162
+ /** A row of stars has no `display` size: it is `xl`. */
163
+ const starSize = $derived(size === "display" ? "xl" : size);
164
+ const softEmpty = $derived(empty === "soft" ? "soft" : undefined);
165
+
166
+ function format(current: number): string {
167
+ return formatValue ? formatValue(current) : defaultRatingFormat(current);
168
+ }
169
+
170
+ const valueText = $derived(shown === null ? "" : format(shown));
171
+ /** "3.5 of 5 stars", or "No rating". */
172
+ const valueLabel = $derived(
173
+ shown === null ? text.noRating : text.starLabel(valueText, count),
174
+ );
175
+
176
+ /** The star that takes Tab: the selected one, or the first when none is. */
177
+ const tabStop = $derived(value !== null && stars.includes(value) ? value : 1);
178
+
179
+ const inputs: (HTMLInputElement | undefined)[] = $state([]);
180
+
181
+ function select(next: number | null) {
182
+ if (disabled || next === value) return;
183
+ value = next;
184
+ onchange?.(next);
185
+ }
186
+
187
+ /**
188
+ * The radios are bound, so a star picked before the page hydrated is kept:
189
+ * Svelte's binding hands it over here instead of overwriting it (see
190
+ * util/hydration.ts), and it goes through `select` like any other choice,
191
+ * `onchange` included. A form reset empties the rating, as it empties
192
+ * a bound native input.
193
+ */
194
+ const chosen = groupBinding<number>(
195
+ () => value,
196
+ (star) => select(star),
197
+ () => {
198
+ if (!disabled) value = resetValueOf<number>(inputs) ?? null;
199
+ },
200
+ );
201
+
202
+ /** The clear button hides once there is nothing to clear, so focus goes back to the stars. */
203
+ function clear() {
204
+ if (disabled || value === null) return;
205
+ select(null);
206
+ inputs[0]?.focus();
207
+ }
208
+
209
+ /** `focused` is the index of the star the key was pressed on, which need not be the selected one. */
210
+ function handleKeydown(event: KeyboardEvent, focused: number) {
211
+ if (disabled) return;
212
+ if (event.key === "Delete" || event.key === "Backspace") {
213
+ if (!clearable) return;
214
+ event.preventDefault();
215
+ clear();
216
+ return;
217
+ }
218
+ const next = radioKeyIndex(
219
+ event.key,
220
+ focused,
221
+ count,
222
+ isRtl(event.currentTarget as Element),
223
+ value !== null && stars.includes(value),
224
+ );
225
+ if (next === -1) return;
226
+ event.preventDefault();
227
+ select(stars[next]);
228
+ inputs[next]?.focus();
229
+ }
230
+ </script>
231
+
232
+ {#snippet glyph(fill: number | undefined)}
233
+ <span class="rating-glyph" style:--zabi-rating-fill={fill} aria-hidden="true">
234
+ <Star class="rating-glyph-empty" />
235
+ <span class="rating-glyph-fill"><Star fill="currentColor" /></span>
236
+ </span>
237
+ {/snippet}
238
+
239
+ {#snippet valueOutput()}
240
+ <span
241
+ class={cn(
242
+ "shrink-0 text-sm tabular-nums",
243
+ shown === null ? "text-description" : "font-medium text-body",
244
+ )}
245
+ data-rating-value
246
+ aria-hidden={readonly ? undefined : "true"}
247
+ >
248
+ {shown === null ? (readonly ? text.noRating : "") : valueText}
249
+ </span>
250
+ {/snippet}
251
+
252
+ {#if compact}
253
+ <!-- A score: one star and the number, one image with one name as `readonly`. -->
254
+ <div
255
+ class={cn("rating inline-flex items-center", className)}
256
+ data-size={size}
257
+ data-tone={tone}
258
+ data-empty={softEmpty}
259
+ data-compact
260
+ data-readonly
261
+ role="img"
262
+ aria-label={ariaLabelledby
263
+ ? undefined
264
+ : [label || ariaLabel, valueLabel].filter(Boolean).join(", ")}
265
+ aria-labelledby={ariaLabelledby ? `${ariaLabelledby} ${valueId}` : undefined}
266
+ {...restProps}
267
+ >
268
+ {#if labelVisible}
269
+ <span class="me-2 text-sm font-medium text-label">{label}</span>
270
+ {/if}
271
+ {@render glyph(shown === null ? 0 : 1)}
272
+ <span
273
+ class={cn(
274
+ "rating-compact-value font-semibold tabular-nums",
275
+ size === "display" && "font-display font-bold",
276
+ shown === null ? "text-description" : "text-headline",
277
+ )}
278
+ data-rating-value
279
+ >
280
+ {shown === null ? text.noRating : valueText}
281
+ </span>
282
+ {#if ariaLabelledby}
283
+ <span id={valueId} class="sr-only">{valueLabel}</span>
284
+ {/if}
285
+ </div>
286
+ {:else if readonly}
287
+ <!-- One image with one name. Its children, the visible label and number
288
+ included, are presentational, so nothing is read twice. -->
289
+ <div
290
+ class={cn("rating", className)}
291
+ data-size={starSize}
292
+ data-tone={tone}
293
+ data-empty={softEmpty}
294
+ data-readonly
295
+ role="img"
296
+ aria-label={ariaLabelledby
297
+ ? undefined
298
+ : [label || ariaLabel, valueLabel].filter(Boolean).join(", ")}
299
+ aria-labelledby={ariaLabelledby ? `${ariaLabelledby} ${valueId}` : undefined}
300
+ {...restProps}
301
+ >
302
+ {#if labelVisible}
303
+ <span class="mb-1 block text-sm font-medium text-label">{label}</span>
304
+ {/if}
305
+ <span class="flex flex-wrap items-center gap-2">
306
+ <span class="rating-stars">
307
+ {#each stars as star (star)}
308
+ {@render glyph(starFill(shown, star))}
309
+ {/each}
310
+ </span>
311
+ {#if valueShown}
312
+ {@render valueOutput()}
313
+ {/if}
314
+ </span>
315
+ {#if ariaLabelledby}
316
+ <span id={valueId} class="sr-only">{valueLabel}</span>
317
+ {/if}
318
+ </div>
319
+ {:else}
320
+ <div
321
+ class={cn("rating", disabled && "opacity-50", className)}
322
+ data-size={starSize}
323
+ data-tone={tone}
324
+ data-empty={softEmpty}
325
+ {...restProps}
326
+ >
327
+ {#if labelVisible}
328
+ <span id={labelId} class="mb-1 block text-sm font-medium text-label">{label}</span>
329
+ {/if}
330
+ <div class="flex flex-wrap items-center gap-2">
331
+ <!-- Native radio inputs: one Tab stop, Space selects, a press on
332
+ the selected one changes nothing, and the value goes with a form. -->
333
+ <div
334
+ class="rating-stars"
335
+ role="radiogroup"
336
+ aria-labelledby={labelVisible ? labelId : label ? undefined : ariaLabelledby}
337
+ aria-label={labelVisible ? undefined : label || ariaLabel}
338
+ aria-disabled={disabled ? "true" : undefined}
339
+ >
340
+ {#each stars as star, index (star)}
341
+ <label class="rating-star" data-on={shown !== null && star <= shown ? "" : undefined}>
342
+ <input
343
+ bind:this={inputs[index]}
344
+ type="radio"
345
+ class="sr-only"
346
+ name={groupName}
347
+ form={name ? undefined : groupName}
348
+ value={star}
349
+ bind:group={chosen.value}
350
+ {disabled}
351
+ tabindex={star === tabStop ? 0 : -1}
352
+ aria-label={text.starLabel(format(star), count)}
353
+ onkeydown={(event) => handleKeydown(event, index)}
354
+ />
355
+ {@render glyph(undefined)}
356
+ </label>
357
+ {/each}
358
+ </div>
359
+ {#if clearable}
360
+ <!-- No `disabled:opacity-50`: the host dims a disabled rating
361
+ once, and the button is invisible while there is nothing to clear. -->
362
+ <button
363
+ type="button"
364
+ class={cn(
365
+ "focus-ring focus-ring--muted flex size-11 shrink-0 cursor-pointer items-center justify-center rounded-control text-description transition-colors duration-150 hover:bg-surface-hover hover:text-body active:bg-surface-active motion-reduce:transition-none disabled:cursor-not-allowed disabled:hover:bg-transparent disabled:hover:text-description",
366
+ value === null && "invisible",
367
+ )}
368
+ aria-label={text.clearLabel}
369
+ title={text.clearLabel}
370
+ disabled={disabled || value === null}
371
+ onclick={clear}
372
+ data-rating-clear
373
+ >
374
+ <X class="size-4" aria-hidden="true" />
375
+ </button>
376
+ {/if}
377
+ {#if valueShown}
378
+ {@render valueOutput()}
379
+ {/if}
380
+ </div>
381
+ </div>
382
+ {/if}
383
+
384
+ <style>
385
+ /*
386
+ * A star is two glyphs: an outline, and a filled one on top that is cut
387
+ * off at `--zabi-rating-fill` of its width. So a star can be half filled,
388
+ * and filled differs from empty by shape as well as by colour.
389
+ */
390
+ .rating {
391
+ --zabi-rating-star: 1.5rem;
392
+ /* A finger is the same size whatever the text size is, so the target
393
+ is in px; it only grows to keep room around a star enlarged by zoom. */
394
+ --zabi-rating-target: max(44px, var(--zabi-rating-star) + 8px);
395
+ /*
396
+ * The star colours an app may set, on the component or on anything
397
+ * around it (they inherit):
398
+ * --zabi-rating-on a filled star
399
+ * --zabi-rating-on-hover the same under the pointer; default: --zabi-rating-on
400
+ * --zabi-rating-on-active the same while pressed; default: --zabi-rating-on
401
+ * --zabi-rating-on-edge the outline of a filled star; default: its fill
402
+ * --zabi-rating-off the outline of an empty star
403
+ * They are read with a fallback and never declared here. Declared on
404
+ * `.rating`, they could only be overridden by an inline style: this
405
+ * rule would beat any class or ancestor an app set them on.
406
+ * The defaults below are the component's own and are not for apps.
407
+ */
408
+ --_rating-on: var(--color-action-primary);
409
+ --_rating-on-hover: var(--color-action-primary-hover);
410
+ --_rating-on-active: var(--color-action-primary-active);
411
+ --_rating-on-edge: currentColor;
412
+ /* The outline is all there is of an empty star, so it needs 3:1 against
413
+ what it sits on (WCAG 1.4.11). `--color-border-medium` is 2.2:1 on
414
+ the light page and 1.9:1 on a dark card, and `--color-border-strong`
415
+ 2.49:1 on the dark elevated surface; the control boundary is held to
416
+ 3:1 on every surface level of both themes. The fallback is for an
417
+ app whose own theme file predates the role. */
418
+ --_rating-off: var(--color-control-border, var(--color-border-strong));
419
+ }
420
+
421
+ /*
422
+ * The app's accent. An accent can be a light colour: a yellow fill is
423
+ * about 1.3:1 on a white card, and no star made of it alone can be seen
424
+ * there. So the filled star is outlined in the accent's text step, which
425
+ * is held to 4.5:1 on the page and a card and 3:1 on the inset surface in
426
+ * both themes. The outline carries the shape whatever the accent is; the
427
+ * fill carries the colour.
428
+ */
429
+ .rating[data-tone="accent"] {
430
+ --_rating-on: var(--color-accent);
431
+ --_rating-on-hover: var(--color-accent-hover);
432
+ --_rating-on-active: var(--color-accent-active);
433
+ --_rating-on-edge: var(--color-accent-text);
434
+ }
435
+
436
+ .rating[data-size="sm"] {
437
+ --zabi-rating-star: 1.25rem;
438
+ }
439
+
440
+ .rating[data-size="lg"] {
441
+ --zabi-rating-star: 2rem;
442
+ --zabi-rating-target: max(48px, var(--zabi-rating-star) + 8px);
443
+ }
444
+
445
+ .rating[data-size="xl"] {
446
+ --zabi-rating-star: 2.5rem;
447
+ --zabi-rating-target: max(48px, var(--zabi-rating-star) + 8px);
448
+ }
449
+
450
+ /* A compact score: the star and the number share one size per step. */
451
+ .rating[data-compact] {
452
+ --zabi-rating-text: 1.5rem;
453
+ gap: 4px;
454
+ }
455
+
456
+ .rating[data-compact][data-size="sm"] {
457
+ --zabi-rating-star: 14px;
458
+ --zabi-rating-text: 14px;
459
+ }
460
+
461
+ .rating[data-compact][data-size="md"] {
462
+ --zabi-rating-star: 16px;
463
+ --zabi-rating-text: 16px;
464
+ }
465
+
466
+ .rating[data-compact][data-size="lg"] {
467
+ --zabi-rating-star: 20px;
468
+ --zabi-rating-text: 20px;
469
+ }
470
+
471
+ .rating[data-compact][data-size="xl"] {
472
+ --zabi-rating-star: 28px;
473
+ --zabi-rating-text: 30px;
474
+ gap: 6px;
475
+ }
476
+
477
+ .rating[data-compact][data-size="display"] {
478
+ --zabi-rating-star: 40px;
479
+ --zabi-rating-text: 48px;
480
+ gap: 6px;
481
+ }
482
+
483
+ .rating-compact-value {
484
+ font-size: var(--zabi-rating-text);
485
+ line-height: 1;
486
+ }
487
+
488
+ .rating-stars {
489
+ display: inline-flex;
490
+ flex-wrap: wrap;
491
+ /* The 44px target reaches past the first glyph; pulled back so the
492
+ glyph lines up with the label above. Only at the start: overflow
493
+ there cannot make the page scroll sideways, at the end it could. */
494
+ --zabi-rating-inset: calc((var(--zabi-rating-target) - var(--zabi-rating-star)) / 2);
495
+ margin-inline-start: calc(var(--zabi-rating-inset) * -1);
496
+ max-width: calc(100% + var(--zabi-rating-inset));
497
+ }
498
+
499
+ .rating[data-readonly] .rating-stars {
500
+ gap: 0.125rem;
501
+ margin-inline-start: 0;
502
+ max-width: 100%;
503
+ }
504
+
505
+ .rating-star {
506
+ position: relative;
507
+ display: inline-flex;
508
+ align-items: center;
509
+ justify-content: center;
510
+ width: var(--zabi-rating-target);
511
+ height: var(--zabi-rating-target);
512
+ border-radius: var(--radius-control);
513
+ cursor: pointer;
514
+ -webkit-tap-highlight-color: transparent;
515
+ }
516
+
517
+ .rating-star[data-on] {
518
+ --zabi-rating-fill: 1;
519
+ }
520
+
521
+ .rating-glyph {
522
+ position: relative;
523
+ display: block;
524
+ flex: none;
525
+ width: var(--zabi-rating-star);
526
+ height: var(--zabi-rating-star);
527
+ transition: transform 150ms;
528
+ }
529
+
530
+ .rating-glyph :global(svg) {
531
+ display: block;
532
+ width: var(--zabi-rating-star);
533
+ height: var(--zabi-rating-star);
534
+ max-width: none;
535
+ }
536
+
537
+ .rating-glyph :global(.rating-glyph-empty) {
538
+ color: var(--zabi-rating-off, var(--_rating-off));
539
+ }
540
+
541
+ /* A soft empty star is a filled shape with no outline. */
542
+ .rating[data-empty="soft"] .rating-glyph :global(.rating-glyph-empty) {
543
+ fill: var(--zabi-rating-off-fill, var(--color-control-track));
544
+ stroke: none;
545
+ }
546
+
547
+ /* Anchored at the start, so the filled part is on the right in a right-to-left layout. */
548
+ .rating-glyph-fill {
549
+ position: absolute;
550
+ inset-block: 0;
551
+ inset-inline-start: 0;
552
+ width: calc(var(--zabi-rating-fill, 0) * 100%);
553
+ overflow: hidden;
554
+ color: var(--_rating-on-now, var(--zabi-rating-on, var(--_rating-on)));
555
+ }
556
+
557
+ .rating-glyph-fill :global(svg) {
558
+ stroke: var(--zabi-rating-on-edge, var(--_rating-on-edge));
559
+ }
560
+
561
+ /* A preview of the rating a click would give, where a pointer can hover. */
562
+ @media (hover: hover) {
563
+ .rating-stars:not([aria-disabled="true"]):hover .rating-star {
564
+ --zabi-rating-fill: 1;
565
+ /* An app that set only --zabi-rating-on keeps that colour here. */
566
+ --_rating-on-now: var(--zabi-rating-on-hover, var(--zabi-rating-on, var(--_rating-on-hover)));
567
+ }
568
+
569
+ .rating-stars:not([aria-disabled="true"]) .rating-star:hover ~ .rating-star {
570
+ --zabi-rating-fill: 0;
571
+ }
572
+ }
573
+
574
+ /* Pressed, on every pointer: the star under the finger fills and dips. */
575
+ .rating-stars:not([aria-disabled="true"]) .rating-star:active {
576
+ --zabi-rating-fill: 1;
577
+ --_rating-on-now: var(--zabi-rating-on-active, var(--zabi-rating-on, var(--_rating-on-active)));
578
+ }
579
+
580
+ .rating-stars:not([aria-disabled="true"]) .rating-star:active .rating-glyph {
581
+ transform: scale(0.85);
582
+ }
583
+
584
+ /* Same geometry as `.focus-ring`: a 2px gap, then a 2px ring. */
585
+ .rating-star:has(:focus-visible) {
586
+ z-index: 1;
587
+ box-shadow:
588
+ 0 0 0 2px var(--color-focus-ring-offset),
589
+ 0 0 0 4px var(--color-focus-ring);
590
+ }
591
+
592
+ .rating-stars[aria-disabled="true"] .rating-star {
593
+ cursor: not-allowed;
594
+ }
595
+
596
+ /* Forced colours drop every box-shadow; an outline is kept. As `.focus-ring` does in app.css. */
597
+ @media (forced-colors: active) {
598
+ .rating-star:has(:focus-visible) {
599
+ outline: 2px solid Highlight;
600
+ outline-offset: 2px;
601
+ }
602
+
603
+ /* An svg keeps its author colour in forced colours; the outline takes the text colour as the fill does. */
604
+ .rating-glyph :global(.rating-glyph-empty) {
605
+ color: CanvasText;
606
+ }
607
+
608
+ /* A soft star is hollow here, so it still differs from a filled one. */
609
+ .rating[data-empty="soft"] .rating-glyph :global(.rating-glyph-empty) {
610
+ fill: none;
611
+ stroke: CanvasText;
612
+ }
613
+ }
614
+
615
+ @media (prefers-reduced-motion: reduce) {
616
+ .rating-glyph {
617
+ transition: none;
618
+ }
619
+ }
620
+ </style>
@@ -0,0 +1,77 @@
1
+ import type { HTMLAttributes } from "svelte/elements";
2
+ import { type RatingStrings } from "../util/rating.js";
3
+ /** Sizes of a rating. `xl` and `display` are for a large input or a compact score. */
4
+ type RatingSize = "sm" | "md" | "lg" | "xl" | "display";
5
+ /** Other attributes (`data-*`, `id`, ...) land on the host element. */
6
+ type Props = Omit<HTMLAttributes<HTMLDivElement>, "class" | "onchange" | "aria-label" | "aria-labelledby"> & {
7
+ /** Extra classes for the host element. */
8
+ class?: string;
9
+ /** Stars given, or `null` for no rating; supports `bind:value`. */
10
+ value?: number | null;
11
+ /** Number of stars. */
12
+ max?: number;
13
+ /** Names the rating ("Quiz"). Shown above the stars unless `hideLabel`. */
14
+ label?: string;
15
+ /** Keeps `label` as the accessible name only. */
16
+ hideLabel?: boolean;
17
+ /** Names the rating when there is no `label`. */
18
+ "aria-label"?: string;
19
+ /** Id of the element that names the rating, when there is no `label`. */
20
+ "aria-labelledby"?: string;
21
+ /**
22
+ * Size of a star. An interactive star is a 44px target at every size.
23
+ * `xl` is 40px stars. `display` is a display-size score with `compact`
24
+ * (a 40px star and a 48px number); as a row of stars it is `xl`.
25
+ */
26
+ size?: RatingSize;
27
+ /**
28
+ * Colour of a filled star: the primary action colour, or the app's
29
+ * accent (`--color-accent`). An app can also set the colours itself:
30
+ * `--zabi-rating-on` and `--zabi-rating-off`, see THEMING.md.
31
+ */
32
+ tone?: "primary" | "accent";
33
+ /**
34
+ * Shows a score instead of asking for one: one image with one name,
35
+ * and a star can be partly filled (3.5 fills half of the fourth).
36
+ */
37
+ readonly?: boolean;
38
+ /**
39
+ * Adds a clear button beside the stars, and Delete or Backspace clears
40
+ * too. Pressing the selected star again never clears.
41
+ */
42
+ clearable?: boolean;
43
+ disabled?: boolean;
44
+ /**
45
+ * Name the value is submitted under in a form. Nothing is submitted
46
+ * without a rating. Without a name the rating is not part of the form
47
+ * around it: it submits nothing and a form reset leaves it alone.
48
+ */
49
+ name?: string;
50
+ /**
51
+ * A score for a list, not a control: one filled star and the number
52
+ * ("4,0"), from the real `value` and `max`, with one accessible name
53
+ * ("4 of 5 stars"). Implies `readonly`: `clearable`, `name` and
54
+ * `onchange` are ignored, and `showValue` too (the number is the point).
55
+ */
56
+ compact?: boolean;
57
+ /**
58
+ * What an empty star looks like: a hollow `outline` (default), or a
59
+ * `soft` fill (no outline) in `--zabi-rating-off-fill`, falling back to
60
+ * `--color-control-track`. The soft fill is below 3:1 against the page
61
+ * by default, so it suits a rating that also has a visible label and
62
+ * value. `--zabi-rating-off-fill` sets the colour; it is declared
63
+ * nowhere, like `--zabi-rating-on`. In forced colours it is an outline.
64
+ */
65
+ empty?: "outline" | "soft";
66
+ /** Shows the number beside the stars. On by default when `readonly`. */
67
+ showValue?: boolean;
68
+ /** Turns the value into text for a locale ("3,5"). At most one decimal by default. */
69
+ formatValue?: (value: number) => string;
70
+ /** Overrides for the built-in strings. */
71
+ strings?: Partial<RatingStrings>;
72
+ /** Called when the user changes the rating; `null` when it was cleared. */
73
+ onchange?: (value: number | null) => void;
74
+ };
75
+ declare const Rating: import("svelte").Component<Props, {}, "value">;
76
+ type Rating = ReturnType<typeof Rating>;
77
+ export default Rating;