zabi-components 7.0.2 → 8.1.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 (433) hide show
  1. package/CHANGELOG.md +1924 -0
  2. package/README.md +287 -11
  3. package/THEME.md +149 -332
  4. package/THEMING.md +1329 -163
  5. package/dist/atoms/ActionPanel.svelte +24 -9
  6. package/dist/atoms/ActionPanel.svelte.d.ts +2 -0
  7. package/dist/atoms/Avatar.svelte +104 -0
  8. package/dist/atoms/Avatar.svelte.d.ts +34 -0
  9. package/dist/atoms/Badge.svelte +124 -58
  10. package/dist/atoms/Badge.svelte.d.ts +17 -5
  11. package/dist/atoms/Button.svelte +212 -62
  12. package/dist/atoms/Button.svelte.d.ts +33 -2
  13. package/dist/atoms/Card.svelte +31 -28
  14. package/dist/atoms/Card.svelte.d.ts +5 -2
  15. package/dist/atoms/CardContent.svelte +10 -2
  16. package/dist/atoms/CardContent.svelte.d.ts +2 -0
  17. package/dist/atoms/CardFooter.svelte +10 -2
  18. package/dist/atoms/CardFooter.svelte.d.ts +2 -0
  19. package/dist/atoms/CardHeader.svelte +26 -6
  20. package/dist/atoms/CardHeader.svelte.d.ts +2 -0
  21. package/dist/atoms/Checkbox.svelte +52 -21
  22. package/dist/atoms/Checkbox.svelte.d.ts +6 -2
  23. package/dist/atoms/CodeBlock.svelte +35 -9
  24. package/dist/atoms/CodeBlock.svelte.d.ts +13 -3
  25. package/dist/atoms/ColorPicker.svelte +272 -52
  26. package/dist/atoms/ColorPicker.svelte.d.ts +6 -1
  27. package/dist/atoms/Container.svelte +17 -10
  28. package/dist/atoms/DateField.svelte +107 -0
  29. package/dist/atoms/DateField.svelte.d.ts +53 -0
  30. package/dist/atoms/Divider.svelte +4 -4
  31. package/dist/atoms/FeatureCard.svelte +4 -3
  32. package/dist/atoms/FieldMessages.svelte +69 -0
  33. package/dist/atoms/FieldMessages.svelte.d.ts +23 -0
  34. package/dist/atoms/FloatingActionButton.svelte +174 -0
  35. package/dist/atoms/FloatingActionButton.svelte.d.ts +51 -0
  36. package/dist/atoms/Heading.svelte +93 -32
  37. package/dist/atoms/Heading.svelte.d.ts +24 -4
  38. package/dist/atoms/IconButton.svelte +242 -53
  39. package/dist/atoms/IconButton.svelte.d.ts +49 -4
  40. package/dist/atoms/Input.svelte +267 -74
  41. package/dist/atoms/Input.svelte.d.ts +32 -2
  42. package/dist/atoms/List.svelte +13 -2
  43. package/dist/atoms/List.svelte.d.ts +2 -0
  44. package/dist/atoms/ListItem.svelte +44 -11
  45. package/dist/atoms/ListItem.svelte.d.ts +5 -0
  46. package/dist/atoms/ListItemLeading.svelte +4 -2
  47. package/dist/atoms/ListItemLeading.svelte.d.ts +1 -1
  48. package/dist/atoms/OptimizedImage.svelte +11 -2
  49. package/dist/atoms/OptimizedImage.svelte.d.ts +2 -0
  50. package/dist/atoms/Progress.svelte +31 -9
  51. package/dist/atoms/Progress.svelte.d.ts +6 -0
  52. package/dist/atoms/Radio.svelte +23 -3
  53. package/dist/atoms/Radio.svelte.d.ts +6 -2
  54. package/dist/atoms/Rating.svelte +494 -0
  55. package/dist/atoms/Rating.svelte.d.ts +56 -0
  56. package/dist/atoms/Select.svelte +475 -135
  57. package/dist/atoms/Select.svelte.d.ts +52 -2
  58. package/dist/atoms/SelectionControl.svelte +99 -18
  59. package/dist/atoms/SelectionControl.svelte.d.ts +6 -3
  60. package/dist/atoms/Skeleton.svelte +5 -3
  61. package/dist/atoms/Slider.svelte +309 -0
  62. package/dist/atoms/Slider.svelte.d.ts +40 -0
  63. package/dist/atoms/Spinner.svelte +57 -0
  64. package/dist/atoms/Spinner.svelte.d.ts +19 -0
  65. package/dist/atoms/Table.svelte +152 -6
  66. package/dist/atoms/Table.svelte.d.ts +15 -3
  67. package/dist/atoms/TemporalField.svelte +140 -0
  68. package/dist/atoms/TemporalField.svelte.d.ts +32 -0
  69. package/dist/atoms/Text.svelte +56 -15
  70. package/dist/atoms/Text.svelte.d.ts +13 -4
  71. package/dist/atoms/Textarea.svelte +57 -45
  72. package/dist/atoms/Textarea.svelte.d.ts +14 -2
  73. package/dist/atoms/ThemeToggle.svelte +238 -56
  74. package/dist/atoms/ThemeToggle.svelte.d.ts +28 -3
  75. package/dist/atoms/TimeField.svelte +108 -0
  76. package/dist/atoms/TimeField.svelte.d.ts +54 -0
  77. package/dist/atoms/Toast.svelte +25 -10
  78. package/dist/atoms/Toast.svelte.d.ts +2 -0
  79. package/dist/atoms/Toggle.svelte +60 -19
  80. package/dist/atoms/Toggle.svelte.d.ts +18 -2
  81. package/dist/atoms/Tooltip.svelte +583 -40
  82. package/dist/atoms/Tooltip.svelte.d.ts +26 -0
  83. package/dist/atoms/ZabiStringsProvider.svelte +45 -0
  84. package/dist/atoms/ZabiStringsProvider.svelte.d.ts +27 -0
  85. package/dist/atoms/index.d.ts +17 -0
  86. package/dist/atoms/index.js +12 -0
  87. package/dist/atoms/selection-control.styles.d.ts +36 -5
  88. package/dist/atoms/selection-control.styles.js +40 -9
  89. package/dist/components/atoms/index.d.ts +17 -0
  90. package/dist/components/atoms/index.d.ts.map +1 -1
  91. package/dist/components/atoms/selection-control.styles.d.ts +36 -5
  92. package/dist/components/atoms/selection-control.styles.d.ts.map +1 -1
  93. package/dist/components/index.d.ts +83 -3
  94. package/dist/components/index.d.ts.map +1 -1
  95. package/dist/components/molecules/index.d.ts +46 -0
  96. package/dist/components/molecules/index.d.ts.map +1 -1
  97. package/dist/components/molecules/toast-store.d.ts +53 -7
  98. package/dist/components/molecules/toast-store.d.ts.map +1 -1
  99. package/dist/components/organisms/index.d.ts +6 -0
  100. package/dist/components/organisms/index.d.ts.map +1 -1
  101. package/dist/components/types/variants.d.ts +15 -1
  102. package/dist/components/types/variants.d.ts.map +1 -1
  103. package/dist/components/util/app-shell.d.ts +28 -0
  104. package/dist/components/util/app-shell.d.ts.map +1 -0
  105. package/dist/components/util/avatar.d.ts +36 -0
  106. package/dist/components/util/avatar.d.ts.map +1 -0
  107. package/dist/components/util/bottom-sheet.d.ts +30 -0
  108. package/dist/components/util/bottom-sheet.d.ts.map +1 -0
  109. package/dist/components/util/bottom-tab-bar.d.ts +35 -0
  110. package/dist/components/util/bottom-tab-bar.d.ts.map +1 -0
  111. package/dist/components/util/calendar.d.ts +56 -0
  112. package/dist/components/util/calendar.d.ts.map +1 -0
  113. package/dist/components/util/cn.d.ts +8 -0
  114. package/dist/components/util/cn.d.ts.map +1 -0
  115. package/dist/components/util/collapsible.d.ts +58 -0
  116. package/dist/components/util/collapsible.d.ts.map +1 -0
  117. package/dist/components/util/confirm-dialog.d.ts +12 -0
  118. package/dist/components/util/confirm-dialog.d.ts.map +1 -0
  119. package/dist/components/util/date.d.ts +96 -0
  120. package/dist/components/util/date.d.ts.map +1 -0
  121. package/dist/components/util/drawer.d.ts +13 -0
  122. package/dist/components/util/drawer.d.ts.map +1 -0
  123. package/dist/components/util/dropdown.d.ts +47 -0
  124. package/dist/components/util/dropdown.d.ts.map +1 -0
  125. package/dist/components/util/field.d.ts +39 -0
  126. package/dist/components/util/field.d.ts.map +1 -0
  127. package/dist/components/util/fit-in-viewport.d.ts +201 -0
  128. package/dist/components/util/fit-in-viewport.d.ts.map +1 -0
  129. package/dist/components/util/focus-utils.d.ts +75 -2
  130. package/dist/components/util/focus-utils.d.ts.map +1 -1
  131. package/dist/components/util/hydration.d.ts +45 -0
  132. package/dist/components/util/hydration.d.ts.map +1 -0
  133. package/dist/components/util/keyboard-inset.d.ts +18 -0
  134. package/dist/components/util/keyboard-inset.d.ts.map +1 -0
  135. package/dist/components/util/media-grid.d.ts +51 -0
  136. package/dist/components/util/media-grid.d.ts.map +1 -0
  137. package/dist/components/util/media.d.ts +6 -0
  138. package/dist/components/util/media.d.ts.map +1 -0
  139. package/dist/components/util/overlay-chrome.d.ts +53 -0
  140. package/dist/components/util/overlay-chrome.d.ts.map +1 -0
  141. package/dist/components/util/overlay.d.ts +72 -0
  142. package/dist/components/util/overlay.d.ts.map +1 -0
  143. package/dist/components/util/photo.d.ts +215 -0
  144. package/dist/components/util/photo.d.ts.map +1 -0
  145. package/dist/components/util/portal.d.ts +23 -0
  146. package/dist/components/util/portal.d.ts.map +1 -0
  147. package/dist/components/util/pull-to-refresh.d.ts +27 -0
  148. package/dist/components/util/pull-to-refresh.d.ts.map +1 -0
  149. package/dist/components/util/radio-keys.d.ts +25 -0
  150. package/dist/components/util/radio-keys.d.ts.map +1 -0
  151. package/dist/components/util/rating.d.ts +23 -0
  152. package/dist/components/util/rating.d.ts.map +1 -0
  153. package/dist/components/util/ready-made-strings.d.ts +77 -0
  154. package/dist/components/util/ready-made-strings.d.ts.map +1 -0
  155. package/dist/components/util/scroll-padding.d.ts +20 -0
  156. package/dist/components/util/scroll-padding.d.ts.map +1 -0
  157. package/dist/components/util/segmented-control.d.ts +16 -0
  158. package/dist/components/util/segmented-control.d.ts.map +1 -0
  159. package/dist/components/util/select.d.ts +41 -0
  160. package/dist/components/util/select.d.ts.map +1 -0
  161. package/dist/components/util/sheet-drag.d.ts +60 -0
  162. package/dist/components/util/sheet-drag.d.ts.map +1 -0
  163. package/dist/components/util/sidebar.d.ts +63 -0
  164. package/dist/components/util/sidebar.d.ts.map +1 -0
  165. package/dist/components/util/sortable-list.d.ts +65 -0
  166. package/dist/components/util/sortable-list.d.ts.map +1 -0
  167. package/dist/components/util/ssr-safe.d.ts +4 -4
  168. package/dist/components/util/ssr-safe.d.ts.map +1 -1
  169. package/dist/components/util/stepper.d.ts +40 -0
  170. package/dist/components/util/stepper.d.ts.map +1 -0
  171. package/dist/components/util/swipeable-list-item.d.ts +36 -0
  172. package/dist/components/util/swipeable-list-item.d.ts.map +1 -0
  173. package/dist/components/util/theme-mode.d.ts +80 -0
  174. package/dist/components/util/theme-mode.d.ts.map +1 -0
  175. package/dist/components/util/toaster.d.ts +93 -0
  176. package/dist/components/util/toaster.d.ts.map +1 -0
  177. package/dist/components/util/tooltip.d.ts +10 -0
  178. package/dist/components/util/tooltip.d.ts.map +1 -0
  179. package/dist/components/util/top-navbar.d.ts +11 -0
  180. package/dist/components/util/top-navbar.d.ts.map +1 -0
  181. package/dist/components/util/touch-target.d.ts +31 -0
  182. package/dist/components/util/touch-target.d.ts.map +1 -0
  183. package/dist/components/util/zabi-strings.d.ts +133 -0
  184. package/dist/components/util/zabi-strings.d.ts.map +1 -0
  185. package/dist/create-theme/cli.js +169 -0
  186. package/dist/create-theme/index.d.ts +176 -0
  187. package/dist/create-theme/index.js +817 -0
  188. package/dist/create-theme/lib/ramp-math.js +128 -0
  189. package/dist/create-theme/lib/resolve.js +141 -0
  190. package/dist/create-theme/theme-data.js +1116 -0
  191. package/dist/index.d.ts +83 -3
  192. package/dist/index.js +60 -8
  193. package/dist/molecules/Alert.svelte +73 -66
  194. package/dist/molecules/Alert.svelte.d.ts +9 -1
  195. package/dist/molecules/AppBar.svelte +415 -0
  196. package/dist/molecules/AppBar.svelte.d.ts +69 -0
  197. package/dist/molecules/AvatarGroup.svelte +116 -0
  198. package/dist/molecules/AvatarGroup.svelte.d.ts +40 -0
  199. package/dist/molecules/BottomSheet.svelte +540 -0
  200. package/dist/molecules/BottomSheet.svelte.d.ts +93 -0
  201. package/dist/molecules/BottomTabBar.svelte +297 -0
  202. package/dist/molecules/BottomTabBar.svelte.d.ts +64 -0
  203. package/dist/molecules/Calendar.svelte +597 -0
  204. package/dist/molecules/Calendar.svelte.d.ts +72 -0
  205. package/dist/molecules/Collapsible.svelte +243 -0
  206. package/dist/molecules/Collapsible.svelte.d.ts +44 -0
  207. package/dist/molecules/CollapsibleGroup.svelte +126 -0
  208. package/dist/molecules/CollapsibleGroup.svelte.d.ts +14 -0
  209. package/dist/molecules/ComponentDemo.svelte +25 -5
  210. package/dist/molecules/ComponentDemo.svelte.d.ts +5 -0
  211. package/dist/molecules/ConfirmDialog.svelte +253 -0
  212. package/dist/molecules/ConfirmDialog.svelte.d.ts +55 -0
  213. package/dist/molecules/ContactForm.svelte +46 -19
  214. package/dist/molecules/ContactForm.svelte.d.ts +10 -0
  215. package/dist/molecules/Drawer.svelte +394 -0
  216. package/dist/molecules/Drawer.svelte.d.ts +55 -0
  217. package/dist/molecules/Dropdown.svelte +540 -50
  218. package/dist/molecules/Dropdown.svelte.d.ts +49 -6
  219. package/dist/molecules/DropdownItem.svelte +122 -0
  220. package/dist/molecules/DropdownItem.svelte.d.ts +28 -0
  221. package/dist/molecules/EmptyState.svelte +56 -10
  222. package/dist/molecules/EmptyState.svelte.d.ts +8 -0
  223. package/dist/molecules/Form.svelte +25 -14
  224. package/dist/molecules/Form.svelte.d.ts +13 -5
  225. package/dist/molecules/FormField.svelte +11 -2
  226. package/dist/molecules/FormField.svelte.d.ts +2 -0
  227. package/dist/molecules/Header.svelte +13 -3
  228. package/dist/molecules/Header.svelte.d.ts +2 -0
  229. package/dist/molecules/ImageUpload.svelte +422 -70
  230. package/dist/molecules/ImageUpload.svelte.d.ts +61 -5
  231. package/dist/molecules/MediaGrid.svelte +527 -0
  232. package/dist/molecules/MediaGrid.svelte.d.ts +77 -0
  233. package/dist/molecules/Modal.svelte +340 -45
  234. package/dist/molecules/Modal.svelte.d.ts +59 -7
  235. package/dist/molecules/NavigationMenu.svelte +15 -3
  236. package/dist/molecules/NavigationMenu.svelte.d.ts +4 -0
  237. package/dist/molecules/NavigationMenuContent.svelte +91 -36
  238. package/dist/molecules/NavigationMenuContent.svelte.d.ts +2 -0
  239. package/dist/molecules/NavigationMenuItem.svelte +10 -3
  240. package/dist/molecules/NavigationMenuItem.svelte.d.ts +2 -0
  241. package/dist/molecules/NavigationMenuLink.svelte +10 -2
  242. package/dist/molecules/NavigationMenuLink.svelte.d.ts +2 -0
  243. package/dist/molecules/NavigationMenuList.svelte +19 -4
  244. package/dist/molecules/NavigationMenuList.svelte.d.ts +2 -0
  245. package/dist/molecules/NavigationMenuTrigger.svelte +16 -3
  246. package/dist/molecules/NavigationMenuTrigger.svelte.d.ts +2 -0
  247. package/dist/molecules/Page.svelte +38 -2
  248. package/dist/molecules/Page.svelte.d.ts +13 -0
  249. package/dist/molecules/PhotoGrid.svelte +433 -0
  250. package/dist/molecules/PhotoGrid.svelte.d.ts +62 -0
  251. package/dist/molecules/PhotoViewer.svelte +1116 -0
  252. package/dist/molecules/PhotoViewer.svelte.d.ts +56 -0
  253. package/dist/molecules/PropsTable.svelte +26 -11
  254. package/dist/molecules/PropsTable.svelte.d.ts +5 -0
  255. package/dist/molecules/PullToRefresh.svelte +263 -0
  256. package/dist/molecules/PullToRefresh.svelte.d.ts +52 -0
  257. package/dist/molecules/RadioGroup.svelte +57 -15
  258. package/dist/molecules/RadioGroup.svelte.d.ts +16 -3
  259. package/dist/molecules/Section.svelte +13 -5
  260. package/dist/molecules/Section.svelte.d.ts +3 -1
  261. package/dist/molecules/SegmentedControl.svelte +323 -0
  262. package/dist/molecules/SegmentedControl.svelte.d.ts +34 -0
  263. package/dist/molecules/SidebarBrandHeader.svelte +24 -5
  264. package/dist/molecules/SidebarBrandHeader.svelte.d.ts +5 -0
  265. package/dist/molecules/SidebarFooter.svelte +39 -8
  266. package/dist/molecules/SidebarFooter.svelte.d.ts +5 -0
  267. package/dist/molecules/SidebarNavSection.svelte +12 -4
  268. package/dist/molecules/SidebarNavSection.svelte.d.ts +2 -0
  269. package/dist/molecules/SlideUp.svelte +253 -30
  270. package/dist/molecules/SlideUp.svelte.d.ts +31 -4
  271. package/dist/molecules/SortableList.svelte +632 -0
  272. package/dist/molecules/SortableList.svelte.d.ts +69 -0
  273. package/dist/molecules/Stepper.svelte +548 -0
  274. package/dist/molecules/Stepper.svelte.d.ts +39 -0
  275. package/dist/molecules/StickyActionBar.svelte +191 -0
  276. package/dist/molecules/StickyActionBar.svelte.d.ts +65 -0
  277. package/dist/molecules/SwipeableListItem.svelte +334 -0
  278. package/dist/molecules/SwipeableListItem.svelte.d.ts +54 -0
  279. package/dist/molecules/Tabs.svelte +254 -24
  280. package/dist/molecules/Tabs.svelte.d.ts +15 -4
  281. package/dist/molecules/Toaster.svelte +352 -6
  282. package/dist/molecules/Toaster.svelte.d.ts +95 -2
  283. package/dist/molecules/ToasterToast.svelte +279 -62
  284. package/dist/molecules/ToasterToast.svelte.d.ts +11 -0
  285. package/dist/molecules/UnsavedChangesBar.svelte +212 -0
  286. package/dist/molecules/UnsavedChangesBar.svelte.d.ts +43 -0
  287. package/dist/molecules/index.d.ts +46 -0
  288. package/dist/molecules/index.js +27 -0
  289. package/dist/molecules/toast-store.d.ts +53 -7
  290. package/dist/molecules/toast-store.js +21 -0
  291. package/dist/organisms/AppShell.svelte +196 -0
  292. package/dist/organisms/AppShell.svelte.d.ts +61 -0
  293. package/dist/organisms/SidebarAccountPanel.svelte +111 -12
  294. package/dist/organisms/SidebarAccountPanel.svelte.d.ts +21 -0
  295. package/dist/organisms/SidebarNavigation.svelte +193 -69
  296. package/dist/organisms/SidebarNavigation.svelte.d.ts +31 -1
  297. package/dist/organisms/SidebarPanel.svelte +45 -16
  298. package/dist/organisms/SidebarPanel.svelte.d.ts +2 -0
  299. package/dist/organisms/SidebarShell.svelte +263 -0
  300. package/dist/organisms/SidebarShell.svelte.d.ts +106 -0
  301. package/dist/organisms/TopNavbar.svelte +329 -33
  302. package/dist/organisms/TopNavbar.svelte.d.ts +28 -0
  303. package/dist/organisms/index.d.ts +6 -0
  304. package/dist/organisms/index.js +4 -0
  305. package/dist/routes/lib/variant-utils.d.ts +26 -0
  306. package/dist/routes/lib/{variant-utils.ts → variant-utils.js} +15 -46
  307. package/dist/types/components.d.ts +4 -0
  308. package/dist/types/components.d.ts.map +1 -1
  309. package/dist/types/events.js +1 -0
  310. package/dist/types/index.d.ts +605 -25
  311. package/dist/types/index.d.ts.map +1 -1
  312. package/dist/types/index.js +3 -0
  313. package/dist/types/variants.ts +17 -1
  314. package/dist/util/app-shell.d.ts +27 -0
  315. package/dist/util/app-shell.js +72 -0
  316. package/dist/util/avatar.d.ts +35 -0
  317. package/dist/util/avatar.js +52 -0
  318. package/dist/util/bottom-sheet.d.ts +29 -0
  319. package/dist/util/bottom-sheet.js +43 -0
  320. package/dist/util/bottom-tab-bar.d.ts +34 -0
  321. package/dist/util/bottom-tab-bar.js +54 -0
  322. package/dist/util/calendar.d.ts +55 -0
  323. package/dist/util/calendar.js +74 -0
  324. package/dist/util/cn.d.ts +7 -0
  325. package/dist/util/cn.js +48 -0
  326. package/dist/util/collapsible.d.ts +57 -0
  327. package/dist/util/collapsible.js +23 -0
  328. package/dist/util/confirm-dialog.d.ts +11 -0
  329. package/dist/util/confirm-dialog.js +2 -0
  330. package/dist/util/date.d.ts +95 -0
  331. package/dist/util/date.js +237 -0
  332. package/dist/util/drawer.d.ts +12 -0
  333. package/dist/util/drawer.js +7 -0
  334. package/dist/util/dropdown.d.ts +46 -0
  335. package/dist/util/dropdown.js +21 -0
  336. package/dist/util/field.d.ts +38 -0
  337. package/dist/util/field.js +38 -0
  338. package/dist/util/fit-in-viewport.d.ts +200 -0
  339. package/dist/util/fit-in-viewport.js +399 -0
  340. package/dist/util/focus-utils.d.ts +75 -2
  341. package/dist/util/focus-utils.js +182 -9
  342. package/dist/util/hydration.d.ts +44 -0
  343. package/dist/util/hydration.js +58 -0
  344. package/dist/util/keyboard-inset.d.ts +17 -0
  345. package/dist/util/keyboard-inset.js +53 -0
  346. package/dist/util/media-grid.d.ts +50 -0
  347. package/dist/util/media-grid.js +81 -0
  348. package/dist/util/media.d.ts +5 -0
  349. package/dist/util/media.js +10 -0
  350. package/dist/util/overlay-chrome.d.ts +52 -0
  351. package/dist/util/overlay-chrome.js +56 -0
  352. package/dist/util/overlay.d.ts +71 -0
  353. package/dist/util/overlay.js +240 -0
  354. package/dist/util/photo.d.ts +214 -0
  355. package/dist/util/photo.js +206 -0
  356. package/dist/util/portal.d.ts +22 -0
  357. package/dist/util/portal.js +50 -0
  358. package/dist/util/pull-to-refresh.d.ts +26 -0
  359. package/dist/util/pull-to-refresh.js +30 -0
  360. package/dist/util/radio-keys.d.ts +24 -0
  361. package/dist/util/radio-keys.js +49 -0
  362. package/dist/util/rating.d.ts +22 -0
  363. package/dist/util/rating.js +26 -0
  364. package/dist/util/ready-made-strings.d.ts +76 -0
  365. package/dist/util/ready-made-strings.js +67 -0
  366. package/dist/util/scroll-padding.d.ts +19 -0
  367. package/dist/util/scroll-padding.js +44 -0
  368. package/dist/util/segmented-control.d.ts +15 -0
  369. package/dist/util/segmented-control.js +1 -0
  370. package/dist/util/select.d.ts +40 -0
  371. package/dist/util/select.js +63 -0
  372. package/dist/util/sheet-drag.d.ts +59 -0
  373. package/dist/util/sheet-drag.js +241 -0
  374. package/dist/util/sidebar.d.ts +62 -0
  375. package/dist/util/sidebar.js +38 -0
  376. package/dist/util/sortable-list.d.ts +64 -0
  377. package/dist/util/sortable-list.js +53 -0
  378. package/dist/util/ssr-safe.d.ts +4 -4
  379. package/dist/util/stepper.d.ts +39 -0
  380. package/dist/util/stepper.js +38 -0
  381. package/dist/util/swipeable-list-item.d.ts +35 -0
  382. package/dist/util/swipeable-list-item.js +32 -0
  383. package/dist/util/theme-mode.d.ts +79 -0
  384. package/dist/util/theme-mode.js +121 -0
  385. package/dist/util/toaster.d.ts +92 -0
  386. package/dist/util/toaster.js +75 -0
  387. package/dist/util/tooltip.d.ts +9 -0
  388. package/dist/util/tooltip.js +18 -0
  389. package/dist/util/top-navbar.d.ts +10 -0
  390. package/dist/util/top-navbar.js +5 -0
  391. package/dist/util/touch-target.d.ts +30 -0
  392. package/dist/util/touch-target.js +30 -0
  393. package/dist/util/zabi-strings.d.ts +132 -0
  394. package/dist/util/zabi-strings.js +105 -0
  395. package/dist/zabi-components-colors.css +5 -2
  396. package/dist/zabi-components-theme-dark-only.css +574 -225
  397. package/dist/zabi-components-theme-dark.css +574 -225
  398. package/dist/zabi-components-theme-only.css +1680 -317
  399. package/dist/zabi-components-theme.css +1680 -317
  400. package/dist/zabi-components.css +4473 -1158
  401. package/docs/lucide-icons.md +41 -16
  402. package/docs/theme-imports.md +81 -11
  403. package/package.json +51 -29
  404. package/dist/lib/layout-width-tokens.d.ts +0 -17
  405. package/dist/lib/layout-width-tokens.d.ts.map +0 -1
  406. package/dist/lib/showcase/component-docs/Button.d.ts +0 -3
  407. package/dist/lib/showcase/component-docs/Button.d.ts.map +0 -1
  408. package/dist/lib/showcase/component-docs/Input.d.ts +0 -3
  409. package/dist/lib/showcase/component-docs/Input.d.ts.map +0 -1
  410. package/dist/lib/showcase/component-docs/List.d.ts +0 -3
  411. package/dist/lib/showcase/component-docs/List.d.ts.map +0 -1
  412. package/dist/lib/showcase/component-docs/Modal.d.ts +0 -3
  413. package/dist/lib/showcase/component-docs/Modal.d.ts.map +0 -1
  414. package/dist/lib/showcase/component-docs/Radio.d.ts +0 -3
  415. package/dist/lib/showcase/component-docs/Radio.d.ts.map +0 -1
  416. package/dist/lib/showcase/component-docs/SidebarNavigation.d.ts +0 -3
  417. package/dist/lib/showcase/component-docs/SidebarNavigation.d.ts.map +0 -1
  418. package/dist/lib/showcase/component-docs/Skeleton.d.ts +0 -3
  419. package/dist/lib/showcase/component-docs/Skeleton.d.ts.map +0 -1
  420. package/dist/lib/showcase/component-docs/_shared.d.ts +0 -12
  421. package/dist/lib/showcase/component-docs/_shared.d.ts.map +0 -1
  422. package/dist/lib/showcase/component-docs/index.d.ts +0 -4
  423. package/dist/lib/showcase/component-docs/index.d.ts.map +0 -1
  424. package/dist/lib/showcase/components-catalog.d.ts +0 -3
  425. package/dist/lib/showcase/components-catalog.d.ts.map +0 -1
  426. package/dist/lib/showcase/components-showcase-constants.d.ts +0 -89
  427. package/dist/lib/showcase/components-showcase-constants.d.ts.map +0 -1
  428. package/dist/lib/showcase/docs-sidebar-helpers.d.ts +0 -15
  429. package/dist/lib/showcase/docs-sidebar-helpers.d.ts.map +0 -1
  430. package/dist/routes/lib/focus-utils.d.ts +0 -12
  431. package/dist/routes/lib/focus-utils.ts +0 -110
  432. package/dist/routes/lib/ssr-safe.ts +0 -58
  433. package/scripts/fix-lucide-svelte-icon-dts.js +0 -35
@@ -0,0 +1,597 @@
1
+ <script lang="ts">
2
+ import { mergeStrings } from "../util/ready-made-strings.js";
3
+ import { zabiStringsFor } from "../util/zabi-strings.js";
4
+ import { tick } from "svelte";
5
+ import type { HTMLAttributes } from "svelte/elements";
6
+ import ChevronLeft from "@lucide/svelte/icons/chevron-left";
7
+ import ChevronRight from "@lucide/svelte/icons/chevron-right";
8
+ import IconButton from "../atoms/IconButton.svelte";
9
+ import {
10
+ calendarDayName,
11
+ calendarKeyTarget,
12
+ DEFAULT_CALENDAR_STRINGS,
13
+ groupEventsByDate,
14
+ MAX_EVENT_DOTS,
15
+ type CalendarEvent,
16
+ type CalendarStrings,
17
+ } from "../util/calendar.js";
18
+ import { cn } from "../util/cn.js";
19
+ import {
20
+ addMonthsToMonth,
21
+ clampDate,
22
+ daysInMonth,
23
+ formatDate,
24
+ isIsoDate,
25
+ monthGrid,
26
+ monthOf,
27
+ normaliseWeekStart,
28
+ parseIsoDate,
29
+ parseIsoMonth,
30
+ todayIso,
31
+ todayIsoUtc,
32
+ toIsoDate,
33
+ weekdayOrder,
34
+ } from "../util/date.js";
35
+ import { isRtl } from "../util/radio-keys.js";
36
+ import { generateId } from "../util/ssr-safe.js";
37
+
38
+ /**
39
+ * One month as a grid of days, with a dot on the days that have events.
40
+ * Pick a day and list what happens on it below:
41
+ *
42
+ * ```svelte
43
+ * <Calendar bind:selected {events} locale="sv" />
44
+ * {#each events.filter((event) => event.date === selected) as event}
45
+ * <p>{event.label}</p>
46
+ * {/each}
47
+ * ```
48
+ *
49
+ * Dates are strings: `YYYY-MM-DD` for a day, `YYYY-MM` for a month. A
50
+ * press on the selected day changes nothing: the selection is never
51
+ * cleared by pressing it again.
52
+ *
53
+ * It is as wide as its container, seven equal columns: give it a width
54
+ * where the container is wide. Days are at least 44px tall, and at least
55
+ * 40px wide down to a 320px screen with 16px margins.
56
+ * The weekday names are three letters ("Mon"), or one where the text is
57
+ * enlarged so far that three would not fit their column.
58
+ *
59
+ * On the server, and until it runs in the browser, it does not know what
60
+ * day it is: nothing is marked as today, and a calendar given neither
61
+ * `month` nor `selected` shows the month it is in UTC. Pass `month` to
62
+ * decide the first month yourself, and `locale` so the names are the same
63
+ * on the server and in the browser.
64
+ *
65
+ * Other attributes (`id`, `data-*`, ...) land on the host element.
66
+ */
67
+ type Props = Omit<HTMLAttributes<HTMLDivElement>, "class" | "onselect"> & {
68
+ /**
69
+ * The month shown, as `YYYY-MM`. Supports `bind:month`. Left out, it
70
+ * starts at the month of `selected`, or of today.
71
+ */
72
+ month?: string;
73
+ /** The selected day, as `YYYY-MM-DD`, or null. Supports `bind:selected`. */
74
+ selected?: string | null;
75
+ /** What happens on which day. A day shows a dot per event, three at most. */
76
+ events?: CalendarEvent[];
77
+ /** First day of the week: 0 for Sunday to 6 for Saturday. */
78
+ weekStartsOn?: number;
79
+ /**
80
+ * Language of the month and day names, as a BCP 47 tag (`sv`, `en-GB`).
81
+ * Left out, it is the page's language (`<html lang>`), then the
82
+ * browser's; on the server, English.
83
+ *
84
+ * It translates only what comes from the calendar of that language:
85
+ * the month, the weekdays and the dates. The words the component
86
+ * adds ("Previous month", "today", "selected", "2 events: ...") stay
87
+ * English until you pass them in `strings`. Translate both.
88
+ */
89
+ locale?: string;
90
+ /** Earliest day that can be selected, as `YYYY-MM-DD`. Months before it cannot be reached. */
91
+ min?: string;
92
+ /** Latest day that can be selected, as `YYYY-MM-DD`. Months after it cannot be reached. */
93
+ max?: string;
94
+ /** Return true for a day that cannot be selected. It can still be focused and read. */
95
+ isDateDisabled?: (date: string) => boolean;
96
+ /** The words the calendar says; replace any of them to translate. */
97
+ strings?: Partial<CalendarStrings>;
98
+ /** Called with the day when the user selects another one. */
99
+ onselect?: (date: string) => void;
100
+ /** Called with the month when the user moves to another one. */
101
+ onmonthchange?: (month: string) => void;
102
+ /** Extra classes for the host element. */
103
+ class?: string;
104
+ };
105
+
106
+ let {
107
+ month = $bindable(),
108
+ selected = $bindable<Exclude<Props["selected"], undefined>>(),
109
+ events = [],
110
+ weekStartsOn = 1,
111
+ locale,
112
+ min,
113
+ max,
114
+ isDateDisabled,
115
+ strings,
116
+ onselect,
117
+ onmonthchange,
118
+ class: className = "",
119
+ ...restProps
120
+ }: Props = $props();
121
+
122
+ // No fallback on a bindable prop: Svelte refuses `bind:…={undefined}` on
123
+ // one that has a fallback (`props_invalid_value`), and a page that throws
124
+ // while it hydrates never becomes interactive. The default is applied
125
+ // here instead: at once, for the server and the first render, and again
126
+ // whenever a parent hands back `undefined`.
127
+ const applyDefaults = () => {
128
+ if (selected === undefined) selected = null;
129
+ };
130
+ applyDefaults();
131
+ $effect.pre(applyDefaults);
132
+
133
+ const titleId = generateId("calendar-title");
134
+ /** The app-wide words for this component, from a `ZabiStringsProvider` above it, if there is one. */
135
+ const provided = zabiStringsFor("calendar");
136
+ const text = $derived(mergeStrings(DEFAULT_CALENDAR_STRINGS, provided(), strings));
137
+
138
+ let grid: HTMLTableElement | undefined = $state();
139
+
140
+ /**
141
+ * Null on the server and in the first render in the browser, so the two
142
+ * agree whatever the time zones; the real day arrives once mounted.
143
+ */
144
+ let today = $state<string | null>(null);
145
+ /** The page's or the browser's language, known once mounted. */
146
+ let detectedLocale = $state<string | undefined>(undefined);
147
+ /**
148
+ * The month to show when neither `month` nor `selected` says. UTC first:
149
+ * the server and the browser compute the same one. The user's own month
150
+ * replaces it once mounted, which differs only around the turn of a month.
151
+ */
152
+ let fallbackMonth = $state(monthOf(todayIsoUtc())!);
153
+
154
+ $effect(() => {
155
+ const now = todayIso();
156
+ today = now;
157
+ fallbackMonth = monthOf(now)!;
158
+ detectedLocale = document.documentElement.lang || navigator.language || undefined;
159
+ // A calendar left open over midnight: look again when it is looked at again.
160
+ const refresh = () => {
161
+ if (document.visibilityState === "visible") today = todayIso();
162
+ };
163
+ document.addEventListener("visibilitychange", refresh);
164
+ return () => document.removeEventListener("visibilitychange", refresh);
165
+ });
166
+
167
+ const language = $derived(locale ?? detectedLocale ?? "en");
168
+ const weekStart = $derived(normaliseWeekStart(weekStartsOn));
169
+ const selectedDate = $derived(isIsoDate(selected) ? selected : null);
170
+ const viewMonth = $derived(
171
+ parseIsoMonth(month) ? (month as string) : (monthOf(selectedDate) ?? fallbackMonth),
172
+ );
173
+ const weeks = $derived(monthGrid(viewMonth, weekStart));
174
+ const eventsByDate = $derived(groupEventsByDate(events));
175
+
176
+ const title = $derived(
177
+ formatDate(`${viewMonth}-01`, language, { month: "long", year: "numeric" }),
178
+ );
179
+
180
+ /** 1 January 2023 was a Sunday: that week gives the names of the seven days. */
181
+ const weekdays = $derived(
182
+ weekdayOrder(weekStart).map((weekday) => {
183
+ const sample = toIsoDate(2023, 1, 1 + weekday);
184
+ return {
185
+ short: formatDate(sample, language, { weekday: "short" }),
186
+ narrow: formatDate(sample, language, { weekday: "narrow" }),
187
+ long: formatDate(sample, language, { weekday: "long" }),
188
+ };
189
+ }),
190
+ );
191
+
192
+ function isDisabled(date: string): boolean {
193
+ if (isIsoDate(min) && date < min) return true;
194
+ if (isIsoDate(max) && date > max) return true;
195
+ return isDateDisabled?.(date) ?? false;
196
+ }
197
+
198
+ const previous = $derived(addMonthsToMonth(viewMonth, -1));
199
+ const next = $derived(addMonthsToMonth(viewMonth, 1));
200
+ /** A month can be reached when at least one of its days is within `min` and `max`. */
201
+ const canGoPrevious = $derived.by(() => {
202
+ const parts = parseIsoMonth(previous);
203
+ if (!parts) return false;
204
+ const last = toIsoDate(parts.year, parts.month, daysInMonth(parts.year, parts.month));
205
+ return !(isIsoDate(min) && last < min);
206
+ });
207
+ const canGoNext = $derived(!!next && !(isIsoDate(max) && `${next}-01` > max));
208
+
209
+ /** The day the keyboard was last on. It keeps the one Tab stop where the user left it. */
210
+ let focused = $state<string | null>(null);
211
+
212
+ /**
213
+ * The one day in the grid that takes Tab: where the keyboard was, else
214
+ * the selected day, else today, else the first day that can be selected;
215
+ * always a day of the month shown.
216
+ */
217
+ const tabStop = $derived.by(() => {
218
+ const inView = (date: string | null) => !!date && monthOf(date) === viewMonth;
219
+ if (inView(focused)) return focused;
220
+ if (inView(selectedDate)) return selectedDate;
221
+ if (inView(today)) return today;
222
+ const days = weeks.flat().filter((date): date is string => date !== null);
223
+ return days.find((date) => !isDisabled(date)) ?? days[0] ?? null;
224
+ });
225
+
226
+ function showMonth(nextMonth: string | null) {
227
+ if (!nextMonth || nextMonth === viewMonth) return;
228
+ month = nextMonth;
229
+ onmonthchange?.(nextMonth);
230
+ }
231
+
232
+ /** A press on the selected day is not a change: a single selection never clears. */
233
+ function select(date: string) {
234
+ if (isDisabled(date) || date === selectedDate) return;
235
+ selected = date;
236
+ onselect?.(date);
237
+ }
238
+
239
+ async function focusDay(date: string) {
240
+ focused = date;
241
+ showMonth(monthOf(date));
242
+ await tick();
243
+ grid?.querySelector<HTMLElement>(`[data-date="${date}"]`)?.focus();
244
+ }
245
+
246
+ function handleKeydown(event: KeyboardEvent) {
247
+ const origin = (event.target as Element | null)?.closest<HTMLElement>("[data-date]");
248
+ const from = origin?.dataset.date;
249
+ if (!from || event.altKey || event.ctrlKey || event.metaKey) return;
250
+ const target = calendarKeyTarget(event.key, from, {
251
+ shift: event.shiftKey,
252
+ rtl: isRtl(event.currentTarget as Element),
253
+ weekStartsOn: weekStart,
254
+ });
255
+ if (!target) return;
256
+ // The key is the grid's either way: the page must not scroll under it.
257
+ event.preventDefault();
258
+ // Not past the first or last day that may be chosen.
259
+ const allowed = clampDate(target, min, max);
260
+ if (allowed !== from) void focusDay(allowed);
261
+ }
262
+
263
+ const fullDate = (date: string) =>
264
+ formatDate(date, language, {
265
+ weekday: "long",
266
+ day: "numeric",
267
+ month: "long",
268
+ year: "numeric",
269
+ });
270
+
271
+ /** `aria-disabled`, not `disabled`: a button that turns disabled under the finger or the key would drop focus. */
272
+ const stepClasses = (available: boolean) =>
273
+ available ? "" : "cursor-not-allowed opacity-50 hover:bg-transparent active:bg-transparent active:scale-100";
274
+ </script>
275
+
276
+ <div class={cn("zabi-calendar w-full", className)} data-month={viewMonth} {...restProps}>
277
+ <div class="flex items-center justify-between gap-[8px] pb-2">
278
+ <IconButton
279
+ variant="ghost"
280
+ label={text.previousMonth}
281
+ aria-disabled={canGoPrevious ? undefined : "true"}
282
+ class={stepClasses(canGoPrevious)}
283
+ onclick={() => canGoPrevious && showMonth(previous)}
284
+ >
285
+ <ChevronLeft size={20} class="rtl:rotate-180" aria-hidden="true" />
286
+ </IconButton>
287
+ <!-- A live region: a change of month is read out, also when it comes
288
+ from the arrow keys in the grid. -->
289
+ <div
290
+ id={titleId}
291
+ class="min-w-0 text-center text-base font-semibold text-headline first-letter:uppercase"
292
+ aria-live="polite"
293
+ aria-atomic="true"
294
+ >
295
+ {title}
296
+ </div>
297
+ <IconButton
298
+ variant="ghost"
299
+ label={text.nextMonth}
300
+ aria-disabled={canGoNext ? undefined : "true"}
301
+ class={stepClasses(canGoNext)}
302
+ onclick={() => canGoNext && showMonth(next)}
303
+ >
304
+ <ChevronRight size={20} class="rtl:rotate-180" aria-hidden="true" />
305
+ </IconButton>
306
+ </div>
307
+
308
+ <!-- svelte-ignore a11y_no_noninteractive_element_to_interactive_role -->
309
+ <table
310
+ bind:this={grid}
311
+ role="grid"
312
+ aria-labelledby={titleId}
313
+ class="w-full table-fixed border-collapse"
314
+ onkeydown={handleKeydown}
315
+ >
316
+ <thead>
317
+ <tr>
318
+ {#each weekdays as weekday (weekday.long)}
319
+ <th scope="col" abbr={weekday.long} class="h-8 p-0 text-xs font-medium text-description">
320
+ <span class="weekday" aria-hidden="true">
321
+ <span class="weekday-short">{weekday.short}</span>
322
+ <span class="weekday-narrow">{weekday.narrow}</span>
323
+ </span>
324
+ <span class="sr-only">{weekday.long}</span>
325
+ </th>
326
+ {/each}
327
+ </tr>
328
+ </thead>
329
+ <tbody>
330
+ {#each weeks as week, index (index)}
331
+ <tr>
332
+ {#each week as date, column (column)}
333
+ {#if date}
334
+ {@const dayEvents = eventsByDate.get(date) ?? []}
335
+ {@const isToday = date === today}
336
+ {@const isSelected = date === selectedDate}
337
+ {@const disabled = isDisabled(date)}
338
+ <td role="gridcell" class="p-0" aria-selected={isSelected ? "true" : undefined}>
339
+ <button
340
+ type="button"
341
+ class="day"
342
+ data-date={date}
343
+ data-today={isToday ? "" : undefined}
344
+ data-selected={isSelected ? "" : undefined}
345
+ tabindex={date === tabStop ? 0 : -1}
346
+ aria-label={calendarDayName(
347
+ fullDate(date),
348
+ {
349
+ today: isToday,
350
+ selected: isSelected,
351
+ disabled,
352
+ events: dayEvents,
353
+ },
354
+ text,
355
+ )}
356
+ aria-current={isToday ? "date" : undefined}
357
+ aria-disabled={disabled ? "true" : undefined}
358
+ onclick={() => select(date)}
359
+ onfocus={() => (focused = date)}
360
+ >
361
+ <span class="face">
362
+ <span>{parseIsoDate(date)?.day}</span>
363
+ {#if dayEvents.length > 0}
364
+ <span class="dots" aria-hidden="true">
365
+ {#each dayEvents.slice(0, MAX_EVENT_DOTS) as event, dot (dot)}
366
+ <span class="dot" data-tone={event.tone ?? "default"}></span>
367
+ {/each}
368
+ </span>
369
+ {/if}
370
+ </span>
371
+ </button>
372
+ </td>
373
+ {:else}
374
+ <!-- A place before the 1st or after the last day: days of
375
+ other months are not shown, so each day is in the grid once. -->
376
+ <td role="gridcell" class="p-0"></td>
377
+ {/if}
378
+ {/each}
379
+ </tr>
380
+ {/each}
381
+ </tbody>
382
+ </table>
383
+ </div>
384
+
385
+ <style>
386
+ /*
387
+ * The states of a day hang off the button (`data-selected`, `data-today`,
388
+ * `aria-disabled`) and are drawn on the face inside it, which utilities
389
+ * could only reach through a group variant per declaration.
390
+ *
391
+ * The button fills its cell and has square corners, so the cells take
392
+ * touches edge to edge, corners included, with no gaps between them. The
393
+ * face is drawn 2px inside it, so two marked days side by side do not run
394
+ * into each other. The focus ring is drawn around the face as well: on
395
+ * the square button it would not follow the shape that is seen.
396
+ */
397
+ /*
398
+ * The weekday names follow the text size, the columns do not: with the
399
+ * text enlarged to 200% on a 320px screen "Wed" is wider than its column
400
+ * and runs into "Thu". In a column under 1.9rem the name is one letter
401
+ * instead. The full name is read out either way.
402
+ *
403
+ * The query is on the name's own box, as wide as the column: the table
404
+ * sets that width, not the name, so containing it changes no layout.
405
+ */
406
+ .weekday {
407
+ display: block;
408
+ container-type: inline-size;
409
+ }
410
+ .weekday-narrow {
411
+ display: none;
412
+ }
413
+ @container (max-width: 1.9rem) {
414
+ .weekday-short {
415
+ display: none;
416
+ }
417
+ .weekday-narrow {
418
+ display: inline;
419
+ }
420
+ }
421
+
422
+ .day {
423
+ position: relative;
424
+ display: block;
425
+ width: 100%;
426
+ /* 44px for the finger, in px: it must not shrink. The rem part lets
427
+ the number and the dots under it both fit when the text is enlarged. */
428
+ min-height: max(44px, calc(1.25rem + 24px));
429
+ padding: 0;
430
+ border-radius: 0;
431
+ outline: none;
432
+ background: transparent;
433
+ color: var(--color-body);
434
+ font-size: 0.875rem;
435
+ line-height: 1.25rem;
436
+ cursor: pointer;
437
+ -webkit-tap-highlight-color: transparent;
438
+ }
439
+
440
+ /* Above its neighbours while focused: the ring reaches over their edges. */
441
+ .day:focus-visible {
442
+ z-index: 1;
443
+ }
444
+
445
+ /* The library's focus ring (see `.focus-ring` in app.css), on the face. */
446
+ .day:focus-visible .face {
447
+ box-shadow:
448
+ 0 0 0 2px var(--color-focus-ring-offset),
449
+ 0 0 0 4px var(--color-focus-ring);
450
+ }
451
+
452
+ .face {
453
+ position: absolute;
454
+ inset: 2px;
455
+ display: flex;
456
+ align-items: center;
457
+ justify-content: center;
458
+ border-radius: var(--radius-control);
459
+ transition: background-color 150ms;
460
+ }
461
+
462
+ @media (hover: hover) {
463
+ .day:not([aria-disabled="true"]):hover .face {
464
+ background-color: var(--color-surface-hover);
465
+ }
466
+ .day[data-selected]:not([aria-disabled="true"]):hover .face {
467
+ background-color: var(--color-action-primary-hover);
468
+ }
469
+ }
470
+
471
+ .day:not([aria-disabled="true"]):active .face {
472
+ background-color: var(--color-surface-active);
473
+ }
474
+
475
+ .day[data-selected] .face,
476
+ .day[data-selected]:active .face {
477
+ background-color: var(--color-action-primary);
478
+ color: var(--color-action-primary-text);
479
+ font-weight: 600;
480
+ }
481
+
482
+ /* Today: a ring and a heavier number, so it does not rest on colour. On
483
+ the selected day the ring moves inside the fill and takes its text colour. */
484
+ .day[data-today] .face {
485
+ outline: 2px solid var(--color-action-primary);
486
+ outline-offset: -2px;
487
+ font-weight: 700;
488
+ }
489
+ .day[data-today][data-selected] .face {
490
+ outline-color: currentColor;
491
+ outline-offset: -4px;
492
+ }
493
+
494
+ /* Struck through as well as grey: the grey alone is a thin difference
495
+ (3:1 against the page), and colour must not be the only sign. */
496
+ .day[aria-disabled="true"] {
497
+ color: var(--color-action-disabled-text);
498
+ cursor: not-allowed;
499
+ }
500
+
501
+ /* On the number itself: the face is positioned out of the flow, and a
502
+ line set on the button is not drawn through what is out of the flow. */
503
+ .day[aria-disabled="true"] .face > span:first-child {
504
+ text-decoration: line-through;
505
+ }
506
+
507
+ /* 5px above the bottom of the face: 4px dots, so 2px at their ends, plus
508
+ those 5px stay within the face's 8px radius. */
509
+ .dots {
510
+ position: absolute;
511
+ bottom: 5px;
512
+ left: 0;
513
+ right: 0;
514
+ display: flex;
515
+ justify-content: center;
516
+ gap: 2px;
517
+ }
518
+
519
+ .dot {
520
+ width: 4px;
521
+ height: 4px;
522
+ border-radius: var(--radius-pill);
523
+ background-color: var(--color-action-primary);
524
+ }
525
+ .dot[data-tone="success"] {
526
+ background-color: var(--color-success);
527
+ }
528
+ .dot[data-tone="warning"] {
529
+ background-color: var(--color-warning);
530
+ }
531
+ .dot[data-tone="danger"] {
532
+ background-color: var(--color-error);
533
+ }
534
+ .dot[data-tone="accent"] {
535
+ background-color: var(--color-accent);
536
+ }
537
+ /* On the selected day's fill every dot takes the text colour: a tone
538
+ would not show against it. The events are named with the day anyway. */
539
+ .day[data-selected] .dot {
540
+ background-color: currentColor;
541
+ }
542
+
543
+ @media (prefers-reduced-motion: reduce) {
544
+ .face {
545
+ transition: none;
546
+ }
547
+ }
548
+
549
+ /* Fills are dropped where colours are forced: the selected day and the
550
+ dots are drawn in system colours instead. */
551
+ @media (forced-colors: active) {
552
+ .day[data-selected] .face,
553
+ .day[data-selected]:active .face {
554
+ forced-color-adjust: none;
555
+ background-color: Highlight;
556
+ color: HighlightText;
557
+ }
558
+ /* A button's text is forced to ButtonText: without this a day that
559
+ cannot be selected looks like one that can. */
560
+ .day[aria-disabled="true"] {
561
+ color: GrayText;
562
+ }
563
+ .day[data-today] .face {
564
+ outline-color: CanvasText;
565
+ }
566
+ .day[data-today][data-selected] .face {
567
+ outline-color: HighlightText;
568
+ }
569
+ /* The face's outline is today's mark, so focus is drawn on the
570
+ button here, inside its edge. */
571
+ .day:focus-visible {
572
+ outline: 2px solid Highlight;
573
+ outline-offset: -2px;
574
+ }
575
+ .day:focus-visible .face {
576
+ box-shadow: none;
577
+ }
578
+ /* `[data-tone]` as well: a tone's own rule would otherwise win. */
579
+ .dot,
580
+ .dot[data-tone] {
581
+ forced-color-adjust: none;
582
+ background-color: CanvasText;
583
+ }
584
+ .day[data-selected] .dot,
585
+ .day[data-selected] .dot[data-tone] {
586
+ background-color: HighlightText;
587
+ }
588
+ }
589
+
590
+ /* The selected day's hover rule is more specific than the one above, and
591
+ with the adjustment off its own blue would show under the pointer. */
592
+ @media (forced-colors: active) and (hover: hover) {
593
+ .day[data-selected]:not([aria-disabled="true"]):hover .face {
594
+ background-color: Highlight;
595
+ }
596
+ }
597
+ </style>
@@ -0,0 +1,72 @@
1
+ import type { HTMLAttributes } from "svelte/elements";
2
+ import { type CalendarEvent, type CalendarStrings } from "../util/calendar.js";
3
+ /**
4
+ * One month as a grid of days, with a dot on the days that have events.
5
+ * Pick a day and list what happens on it below:
6
+ *
7
+ * ```svelte
8
+ * <Calendar bind:selected {events} locale="sv" />
9
+ * {#each events.filter((event) => event.date === selected) as event}
10
+ * <p>{event.label}</p>
11
+ * {/each}
12
+ * ```
13
+ *
14
+ * Dates are strings: `YYYY-MM-DD` for a day, `YYYY-MM` for a month. A
15
+ * press on the selected day changes nothing: the selection is never
16
+ * cleared by pressing it again.
17
+ *
18
+ * It is as wide as its container, seven equal columns: give it a width
19
+ * where the container is wide. Days are at least 44px tall, and at least
20
+ * 40px wide down to a 320px screen with 16px margins.
21
+ * The weekday names are three letters ("Mon"), or one where the text is
22
+ * enlarged so far that three would not fit their column.
23
+ *
24
+ * On the server, and until it runs in the browser, it does not know what
25
+ * day it is: nothing is marked as today, and a calendar given neither
26
+ * `month` nor `selected` shows the month it is in UTC. Pass `month` to
27
+ * decide the first month yourself, and `locale` so the names are the same
28
+ * on the server and in the browser.
29
+ *
30
+ * Other attributes (`id`, `data-*`, ...) land on the host element.
31
+ */
32
+ type Props = Omit<HTMLAttributes<HTMLDivElement>, "class" | "onselect"> & {
33
+ /**
34
+ * The month shown, as `YYYY-MM`. Supports `bind:month`. Left out, it
35
+ * starts at the month of `selected`, or of today.
36
+ */
37
+ month?: string;
38
+ /** The selected day, as `YYYY-MM-DD`, or null. Supports `bind:selected`. */
39
+ selected?: string | null;
40
+ /** What happens on which day. A day shows a dot per event, three at most. */
41
+ events?: CalendarEvent[];
42
+ /** First day of the week: 0 for Sunday to 6 for Saturday. */
43
+ weekStartsOn?: number;
44
+ /**
45
+ * Language of the month and day names, as a BCP 47 tag (`sv`, `en-GB`).
46
+ * Left out, it is the page's language (`<html lang>`), then the
47
+ * browser's; on the server, English.
48
+ *
49
+ * It translates only what comes from the calendar of that language:
50
+ * the month, the weekdays and the dates. The words the component
51
+ * adds ("Previous month", "today", "selected", "2 events: ...") stay
52
+ * English until you pass them in `strings`. Translate both.
53
+ */
54
+ locale?: string;
55
+ /** Earliest day that can be selected, as `YYYY-MM-DD`. Months before it cannot be reached. */
56
+ min?: string;
57
+ /** Latest day that can be selected, as `YYYY-MM-DD`. Months after it cannot be reached. */
58
+ max?: string;
59
+ /** Return true for a day that cannot be selected. It can still be focused and read. */
60
+ isDateDisabled?: (date: string) => boolean;
61
+ /** The words the calendar says; replace any of them to translate. */
62
+ strings?: Partial<CalendarStrings>;
63
+ /** Called with the day when the user selects another one. */
64
+ onselect?: (date: string) => void;
65
+ /** Called with the month when the user moves to another one. */
66
+ onmonthchange?: (month: string) => void;
67
+ /** Extra classes for the host element. */
68
+ class?: string;
69
+ };
70
+ declare const Calendar: import("svelte").Component<Props, {}, "month" | "selected">;
71
+ type Calendar = ReturnType<typeof Calendar>;
72
+ export default Calendar;