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,43 @@
1
+ import { type Snippet } from "svelte";
2
+ import type { HTMLAttributes } from "svelte/elements";
3
+ type Props = Omit<HTMLAttributes<HTMLDivElement>, "class" | "role"> & {
4
+ /** Shows the bar. Set it while the form differs from what is saved. */
5
+ dirty?: boolean;
6
+ /** Shown in the bar, and announced once when it appears. */
7
+ message?: string;
8
+ /** Accessible name of the bar as a landmark. */
9
+ label?: string;
10
+ saveLabel?: string;
11
+ discardLabel?: string;
12
+ /**
13
+ * Save shows its loading state and Discard is disabled. Set by the
14
+ * bar itself while a promise from `onsave` is pending.
15
+ */
16
+ saving?: boolean;
17
+ /**
18
+ * Called when Save is activated. Return a promise to keep the bar in
19
+ * its saving state until it settles. The bar does not hide itself:
20
+ * clear `dirty` once the save has gone through.
21
+ */
22
+ onsave?: () => void | Promise<unknown>;
23
+ /** Called when Discard is activated. */
24
+ ondiscard?: () => void;
25
+ /**
26
+ * Called when the promise from `onsave` rejects; the bar stays.
27
+ * Without it the error goes to the global error handler.
28
+ */
29
+ onerror?: (error: unknown) => void;
30
+ /**
31
+ * Which edge of its scroll container the bar sticks to. It is sticky,
32
+ * not fixed, so it keeps its place in the flow: put it after the
33
+ * form's last field (`bottom`) or before its first (`top`).
34
+ */
35
+ position?: "bottom" | "top";
36
+ /** Extra buttons, placed before Discard. */
37
+ actions?: Snippet;
38
+ /** Extra classes for the bar. */
39
+ class?: string;
40
+ };
41
+ declare const UnsavedChangesBar: import("svelte").Component<Props, {}, "">;
42
+ type UnsavedChangesBar = ReturnType<typeof UnsavedChangesBar>;
43
+ export default UnsavedChangesBar;
@@ -2,8 +2,12 @@ export { default as Alert } from './Alert.svelte';
2
2
  export { default as ComponentDemo } from './ComponentDemo.svelte';
3
3
  export { default as ContactForm } from './ContactForm.svelte';
4
4
  export { default as Dropdown } from './Dropdown.svelte';
5
+ export { default as DropdownItem } from './DropdownItem.svelte';
6
+ export type { DropdownItemIcon, DropdownItemTone, DropdownOption, } from '../util/dropdown.js';
5
7
  export { default as Form } from './Form.svelte';
6
8
  export { default as ImageUpload } from './ImageUpload.svelte';
9
+ export { default as MediaGrid } from './MediaGrid.svelte';
10
+ export type { MediaGridItemType, MediaGridKey, MediaGridSelectDetail, MediaGridStrings, } from '../util/media-grid.js';
7
11
  export { default as Modal } from './Modal.svelte';
8
12
  export { default as NavigationMenu } from './NavigationMenu.svelte';
9
13
  export { default as NavigationMenuList } from './NavigationMenuList.svelte';
@@ -23,4 +27,46 @@ export { default as Header } from './Header.svelte';
23
27
  export { default as EmptyState } from './EmptyState.svelte';
24
28
  export { default as Toaster } from './Toaster.svelte';
25
29
  export { default as RadioGroup } from './RadioGroup.svelte';
30
+ export { default as SegmentedControl } from './SegmentedControl.svelte';
31
+ export type { SegmentedControlIcon, SegmentedControlOption, } from '../util/segmented-control.js';
32
+ export { default as SortableList } from './SortableList.svelte';
33
+ export type { SortableListAnnouncement, SortableListReorderDetail, SortableListRow, SortableListStrings, } from '../util/sortable-list.js';
34
+ export { default as Collapsible } from './Collapsible.svelte';
35
+ export { default as SwipeableListItem } from './SwipeableListItem.svelte';
36
+ export { DEFAULT_SWIPEABLE_LIST_ITEM_STRINGS } from '../util/swipeable-list-item.js';
37
+ export type { SwipeableListItemAction, SwipeableListItemStrings } from '../util/swipeable-list-item.js';
38
+ export { default as PullToRefresh } from './PullToRefresh.svelte';
39
+ export { DEFAULT_PULL_TO_REFRESH_STRINGS } from '../util/pull-to-refresh.js';
40
+ export type { PullToRefreshStrings } from '../util/pull-to-refresh.js';
41
+ export { default as CollapsibleGroup } from './CollapsibleGroup.svelte';
42
+ export type { CollapsibleHeadingLevel, CollapsibleTriggerProps, CollapsibleTriggerState, } from '../util/collapsible.js';
43
+ export { default as ConfirmDialog } from './ConfirmDialog.svelte';
44
+ export type { ConfirmDialogCancelReason, ConfirmDialogResult, ConfirmDialogVariant, } from '../util/confirm-dialog.js';
45
+ export { default as Drawer } from './Drawer.svelte';
46
+ export type { DrawerCloseReason, DrawerSide, DrawerSize, } from '../util/drawer.js';
47
+ export { default as UnsavedChangesBar } from './UnsavedChangesBar.svelte';
48
+ export { default as AppBar } from './AppBar.svelte';
49
+ export { default as BottomTabBar } from './BottomTabBar.svelte';
50
+ export type { BottomTabBarIcon, BottomTabBarItem, } from '../util/bottom-tab-bar.js';
51
+ export { default as BottomSheet } from './BottomSheet.svelte';
52
+ export type { BottomSheetCloseReason, BottomSheetSnap, } from '../util/bottom-sheet.js';
53
+ export { default as StickyActionBar } from './StickyActionBar.svelte';
54
+ export { default as Calendar } from './Calendar.svelte';
55
+ export type { CalendarEvent, CalendarStrings, CalendarTone, } from '../util/calendar.js';
56
+ export { default as PhotoGrid } from './PhotoGrid.svelte';
57
+ export { default as PhotoViewer } from './PhotoViewer.svelte';
58
+ export type { Photo, PhotoGridSelectDetail, PhotoGridStrings, PhotoKey, PhotoViewerAction, PhotoViewerCloseReason, PhotoViewerStrings, } from '../util/photo.js';
59
+ export { default as AvatarGroup } from './AvatarGroup.svelte';
60
+ export { DEFAULT_AVATAR_GROUP_STRINGS } from '../util/avatar.js';
61
+ export type { AvatarGroupStrings, AvatarPerson } from '../util/avatar.js';
62
+ export { default as Stepper } from './Stepper.svelte';
63
+ export type { StepperItem, StepperLayout, StepperStep, StepperStepState, StepperStrings, } from '../util/stepper.js';
26
64
  export * from './toast-store.js';
65
+ export { DEFAULT_TOASTER_STRINGS, TOAST_DURATIONS } from '../util/toaster.js';
66
+ export type { ToasterStrings, ToastPauseChange, ToastDuration } from '../util/toaster.js';
67
+ export { DEFAULT_SELECT_STRINGS } from '../util/select.js';
68
+ export type { SelectStrings, SelectPresentation } from '../util/select.js';
69
+ export { DEFAULT_SIDEBAR_BRAND_HEADER_STRINGS, DEFAULT_SIDEBAR_FOOTER_STRINGS, } from '../util/sidebar.js';
70
+ export type { SidebarBrandHeaderStrings, SidebarFooterStrings } from '../util/sidebar.js';
71
+ export { DEFAULT_COMPONENT_DEMO_STRINGS, DEFAULT_CONTACT_FORM_STRINGS, DEFAULT_PROPS_TABLE_STRINGS, } from '../util/ready-made-strings.js';
72
+ export type { ComponentDemoStrings, ContactFormStrings, PropsTableStrings, } from '../util/ready-made-strings.js';
@@ -2,8 +2,10 @@ export { default as Alert } from './Alert.svelte';
2
2
  export { default as ComponentDemo } from './ComponentDemo.svelte';
3
3
  export { default as ContactForm } from './ContactForm.svelte';
4
4
  export { default as Dropdown } from './Dropdown.svelte';
5
+ export { default as DropdownItem } from './DropdownItem.svelte';
5
6
  export { default as Form } from './Form.svelte';
6
7
  export { default as ImageUpload } from './ImageUpload.svelte';
8
+ export { default as MediaGrid } from './MediaGrid.svelte';
7
9
  export { default as Modal } from './Modal.svelte';
8
10
  export { default as NavigationMenu } from './NavigationMenu.svelte';
9
11
  export { default as NavigationMenuList } from './NavigationMenuList.svelte';
@@ -23,4 +25,29 @@ export { default as Header } from './Header.svelte';
23
25
  export { default as EmptyState } from './EmptyState.svelte';
24
26
  export { default as Toaster } from './Toaster.svelte';
25
27
  export { default as RadioGroup } from './RadioGroup.svelte';
28
+ export { default as SegmentedControl } from './SegmentedControl.svelte';
29
+ export { default as SortableList } from './SortableList.svelte';
30
+ export { default as Collapsible } from './Collapsible.svelte';
31
+ export { default as SwipeableListItem } from './SwipeableListItem.svelte';
32
+ export { DEFAULT_SWIPEABLE_LIST_ITEM_STRINGS } from '../util/swipeable-list-item.js';
33
+ export { default as PullToRefresh } from './PullToRefresh.svelte';
34
+ export { DEFAULT_PULL_TO_REFRESH_STRINGS } from '../util/pull-to-refresh.js';
35
+ export { default as CollapsibleGroup } from './CollapsibleGroup.svelte';
36
+ export { default as ConfirmDialog } from './ConfirmDialog.svelte';
37
+ export { default as Drawer } from './Drawer.svelte';
38
+ export { default as UnsavedChangesBar } from './UnsavedChangesBar.svelte';
39
+ export { default as AppBar } from './AppBar.svelte';
40
+ export { default as BottomTabBar } from './BottomTabBar.svelte';
41
+ export { default as BottomSheet } from './BottomSheet.svelte';
42
+ export { default as StickyActionBar } from './StickyActionBar.svelte';
43
+ export { default as Calendar } from './Calendar.svelte';
44
+ export { default as PhotoGrid } from './PhotoGrid.svelte';
45
+ export { default as PhotoViewer } from './PhotoViewer.svelte';
46
+ export { default as AvatarGroup } from './AvatarGroup.svelte';
47
+ export { DEFAULT_AVATAR_GROUP_STRINGS } from '../util/avatar.js';
48
+ export { default as Stepper } from './Stepper.svelte';
26
49
  export * from './toast-store.js';
50
+ export { DEFAULT_TOASTER_STRINGS, TOAST_DURATIONS } from '../util/toaster.js';
51
+ export { DEFAULT_SELECT_STRINGS } from '../util/select.js';
52
+ export { DEFAULT_SIDEBAR_BRAND_HEADER_STRINGS, DEFAULT_SIDEBAR_FOOTER_STRINGS, } from '../util/sidebar.js';
53
+ export { DEFAULT_COMPONENT_DEMO_STRINGS, DEFAULT_CONTACT_FORM_STRINGS, DEFAULT_PROPS_TABLE_STRINGS, } from '../util/ready-made-strings.js';
@@ -1,17 +1,52 @@
1
+ import type { ToastDuration } from '../util/toaster.js';
1
2
  export type ToastLevel = 'success' | 'error' | 'warning' | 'info';
3
+ /**
4
+ * One action offered inside a toast, such as Undo.
5
+ *
6
+ * A toast with an action stays until it is dismissed, unless you give it a
7
+ * `duration` (a named length or milliseconds): a keyboard user has to reach the button first, and the toaster
8
+ * may be far away in the tab order (WCAG 2.2.1). `focusToasts()` moves focus
9
+ * there. The action must still not be the only way to do the thing: offer it
10
+ * elsewhere in the page as well.
11
+ */
12
+ export interface ToastAction {
13
+ /** Button text, and its accessible name. */
14
+ label: string;
15
+ onclick: () => void;
16
+ /** Close the toast after the handler runs. Defaults to `true`. */
17
+ dismissOnClick?: boolean;
18
+ }
2
19
  export interface ToastItem {
3
20
  id: string;
4
- /** Primary line (always shown). */
21
+ /**
22
+ * What the toast says. Always shown: as its one line, or under `title`
23
+ * when it has one.
24
+ */
5
25
  message: string;
6
26
  type: ToastLevel;
27
+ /** A heading over the message. Without it the message stands alone. */
7
28
  title?: string;
8
- /** Expandable body; UI can fall back to `message`. */
29
+ /**
30
+ * The long version, kept behind a button that opens it. Only a toast
31
+ * with `detail` has that button.
32
+ */
9
33
  detail?: string;
10
34
  /**
11
- * Auto-dismiss duration in milliseconds.
12
- * When omitted, the toaster uses 14s. Use `0` to keep the toast until dismissed manually.
35
+ * How long the toast stays: `"short"` (3 s, a few words that need no
36
+ * reading time), `"medium"` (7 s), `"long"` (14 s), `"persistent"`
37
+ * (until it is dismissed: what the user has to act on, and errors), or
38
+ * a number of milliseconds, where `0` is persistent too. What is given
39
+ * here always wins.
40
+ *
41
+ * When omitted, the toaster decides: an error and a toast with an
42
+ * `action` stay until dismissed. Everything else goes by its title and
43
+ * message together: up to 120 characters gets `medium` (or the
44
+ * Toaster's `defaultDuration`), 121 to 240 gets `long`, and more than
45
+ * 240 stays until dismissed.
13
46
  */
14
- duration?: number;
47
+ duration?: ToastDuration;
48
+ /** An action button in the toast, such as Undo. */
49
+ action?: ToastAction;
15
50
  }
16
51
  export declare const toastStore: {
17
52
  subscribe: (this: void, run: import("svelte/store").Subscriber<ToastItem[]>, invalidate?: () => void) => import("svelte/store").Unsubscriber;
@@ -19,10 +54,11 @@ export declare const toastStore: {
19
54
  message: string;
20
55
  type?: ToastLevel;
21
56
  /** Same semantics as {@link ToastItem.duration}. */
22
- duration?: number;
57
+ duration?: ToastDuration;
23
58
  id?: string;
24
59
  title?: string;
25
60
  detail?: string;
61
+ action?: ToastAction;
26
62
  }): string;
27
63
  dismiss: (id: string) => void;
28
64
  clear(): void;
@@ -31,8 +67,18 @@ export declare function pushToast(options: {
31
67
  message: string;
32
68
  type?: ToastLevel;
33
69
  /** Same semantics as {@link ToastItem.duration}. */
34
- duration?: number;
70
+ duration?: ToastDuration;
35
71
  title?: string;
36
72
  detail?: string;
73
+ /** An action button in the toast, such as Undo. See {@link ToastAction}. */
74
+ action?: ToastAction;
37
75
  }): string;
38
76
  export declare function dismissToast(id: string): void;
77
+ /**
78
+ * Moves keyboard focus to the newest toast, so its action can be reached
79
+ * without tabbing through the page. Bind it to a shortcut of your own; the
80
+ * library registers none. Returns false when there is no toast to focus.
81
+ *
82
+ * Focus returns to where it was when that toast is dismissed.
83
+ */
84
+ export declare function focusToasts(): boolean;
@@ -15,6 +15,7 @@ function createToastStore() {
15
15
  duration: options.duration,
16
16
  title: options.title,
17
17
  detail: options.detail,
18
+ action: options.action,
18
19
  };
19
20
  update((list) => [...list, item]);
20
21
  return id;
@@ -32,3 +33,23 @@ export function pushToast(options) {
32
33
  export function dismissToast(id) {
33
34
  toastStore.dismiss(id);
34
35
  }
36
+ /**
37
+ * Moves keyboard focus to the newest toast, so its action can be reached
38
+ * without tabbing through the page. Bind it to a shortcut of your own; the
39
+ * library registers none. Returns false when there is no toast to focus.
40
+ *
41
+ * Focus returns to where it was when that toast is dismissed.
42
+ */
43
+ export function focusToasts() {
44
+ if (typeof document === 'undefined')
45
+ return false;
46
+ const toasts = document.querySelectorAll('[data-zabi-toaster] [data-toast-id]');
47
+ const newest = toasts[toasts.length - 1];
48
+ // The action if the toast has one, else its first control.
49
+ const target = newest?.querySelector('[data-toast-action]') ??
50
+ newest?.querySelector('button');
51
+ if (!target)
52
+ return false;
53
+ target.focus();
54
+ return document.activeElement === target;
55
+ }
@@ -0,0 +1,196 @@
1
+ <script lang="ts">
2
+ import type { Snippet } from "svelte";
3
+ import type { HTMLAttributes } from "svelte/elements";
4
+ import { untrack } from "svelte";
5
+ import { markAppShell, publishAppShellInsets } from "../util/app-shell.js";
6
+ import { cn } from "../util/cn.js";
7
+
8
+ /**
9
+ * The layout of a phone app: a bar at the top, content that scrolls, a
10
+ * tab bar at the bottom, and none of it under the notch or the home
11
+ * indicator.
12
+ *
13
+ * The shell is exactly as tall as the visible screen (`100dvh`, so the
14
+ * browser's own bars are accounted for) and only the middle scrolls. The
15
+ * header stays at the top of that scrolling area; the footer sits below it.
16
+ *
17
+ * ```svelte
18
+ * <AppShell>
19
+ * {#snippet header()}
20
+ * <AppBar title="Quizrundan" collapseOnScroll />
21
+ * {/snippet}
22
+ * <div class="p-4">…</div>
23
+ * {#snippet footer()}
24
+ * <BottomTabBar {items} active={page.url.pathname} />
25
+ * {/snippet}
26
+ * </AppShell>
27
+ * ```
28
+ *
29
+ * Two custom properties on the host say how much of the screen the bars
30
+ * take, safe areas included, so anything inside the shell can stay clear
31
+ * of them:
32
+ *
33
+ * - `--app-shell-top-inset`: the height of the header;
34
+ * - `--app-shell-bottom-inset`: the height of the footer.
35
+ *
36
+ * Without a bar the value is the safe-area inset alone. The host is the
37
+ * containing block for anything positioned inside it, and such an element
38
+ * does not scroll with the content, so a floating button above the tab bar
39
+ * is `absolute` with `bottom: calc(var(--app-shell-bottom-inset) + 1rem)`.
40
+ *
41
+ * While the shell is mounted the same two properties are also set on
42
+ * `<html>`, so an overlay that is moved to `document.body` (a sheet, a
43
+ * toast) can read them too. With more than one shell on the page, the one
44
+ * mounted last is the one mirrored there.
45
+ */
46
+ type Props = Omit<HTMLAttributes<HTMLDivElement>, "class" | "style"> & {
47
+ /** The top bar, an `AppBar`. It handles the safe area above it. */
48
+ header?: Snippet;
49
+ /** The scrolling content, rendered in the element `contentElement` names. */
50
+ children?: Snippet;
51
+ /**
52
+ * The element around the content. `main` when the shell is the page,
53
+ * which is what it is for. A page has one `<main>`: use `div` for a
54
+ * shell that sits inside another one, such as a preview in a page of
55
+ * its own.
56
+ */
57
+ contentElement?: "main" | "div";
58
+ /** The bottom bar, a `BottomTabBar`. It handles the safe area below it. */
59
+ footer?: Snippet;
60
+ class?: string;
61
+ /** Added after the two custom properties the shell sets. */
62
+ style?: string;
63
+ };
64
+
65
+ let {
66
+ header,
67
+ children,
68
+ contentElement = "main",
69
+ footer,
70
+ class: className = "",
71
+ style = "",
72
+ ...restProps
73
+ }: Props = $props();
74
+
75
+ markAppShell();
76
+
77
+ let headerRegion: HTMLDivElement | undefined = $state();
78
+ let footerRegion: HTMLDivElement | undefined = $state();
79
+ /** Measured heights in px; null on the server and until the first measurement. */
80
+ let headerHeight = $state<number | null>(null);
81
+ let footerHeight = $state<number | null>(null);
82
+
83
+ /**
84
+ * What the bars measure before they have been measured: the default
85
+ * heights of AppBar and BottomTabBar. The real size replaces it once the
86
+ * shell runs, which matters when the text is enlarged or a label wraps.
87
+ */
88
+ // In px: AppBar and BottomTabBar keep their height when the text is enlarged.
89
+ const HEADER_ESTIMATE = "calc(57px + env(safe-area-inset-top, 0px))";
90
+ const FOOTER_ESTIMATE = "calc(65px + env(safe-area-inset-bottom, 0px))";
91
+
92
+ function track(region: HTMLElement | undefined, set: (height: number | null) => void) {
93
+ if (!region) {
94
+ set(null);
95
+ return;
96
+ }
97
+ const measure = () => {
98
+ const height = region.getBoundingClientRect().height;
99
+ // No layout (a hidden shell, a test DOM): keep the estimate.
100
+ set(height > 0 ? height : null);
101
+ };
102
+ measure();
103
+ if (typeof ResizeObserver === "undefined") return;
104
+ const observer = new ResizeObserver(measure);
105
+ observer.observe(region);
106
+ return () => observer.disconnect();
107
+ }
108
+
109
+ $effect(() => track(headerRegion, (height) => (headerHeight = height)));
110
+ $effect(() => track(footerRegion, (height) => (footerHeight = height)));
111
+
112
+ const topInset = $derived(
113
+ !header
114
+ ? "env(safe-area-inset-top, 0px)"
115
+ : headerHeight === null
116
+ ? HEADER_ESTIMATE
117
+ : `${headerHeight}px`,
118
+ );
119
+ const bottomInset = $derived(
120
+ !footer
121
+ ? "env(safe-area-inset-bottom, 0px)"
122
+ : footerHeight === null
123
+ ? FOOTER_ESTIMATE
124
+ : `${footerHeight}px`,
125
+ );
126
+
127
+ // On `<html>` as well, for what is rendered outside the shell.
128
+ let published: ReturnType<typeof publishAppShellInsets> | undefined;
129
+ $effect(() => {
130
+ published = untrack(() => publishAppShellInsets(topInset, bottomInset));
131
+ return () => {
132
+ published?.leave();
133
+ published = undefined;
134
+ };
135
+ });
136
+ $effect(() => {
137
+ published?.update(topInset, bottomInset);
138
+ });
139
+
140
+ const contentClasses = $derived(
141
+ cn(
142
+ "min-w-0 flex-1 pl-[env(safe-area-inset-left)] pr-[env(safe-area-inset-right)]",
143
+ !header && "pt-[env(safe-area-inset-top)]",
144
+ !footer && "pb-[env(safe-area-inset-bottom)]",
145
+ ),
146
+ );
147
+ </script>
148
+
149
+ <div
150
+ data-app-shell
151
+ class={cn(
152
+ "relative flex h-dvh w-full flex-col overflow-hidden bg-background text-body",
153
+ className,
154
+ )}
155
+ style="--app-shell-top-inset: {topInset}; --app-shell-bottom-inset: {bottomInset}; {style}"
156
+ {...restProps}
157
+ >
158
+ <!--
159
+ The header is inside the scrolling area and sticks to its top. It keeps
160
+ its place in the flow, so the content starts below it with nothing to
161
+ measure, and it can slide away (AppBar's collapseOnScroll) without the
162
+ content moving. scroll-padding keeps a focused field from landing under it.
163
+ -->
164
+ <div
165
+ data-app-shell-scroller
166
+ class="flex min-h-0 flex-1 flex-col overflow-x-hidden overflow-y-auto overscroll-y-contain scroll-pt-(--app-shell-top-inset)"
167
+ >
168
+ {#if header}
169
+ <!-- An empty strip is left when the bar slides away; it must not take taps. -->
170
+ <div
171
+ bind:this={headerRegion}
172
+ data-app-shell-header
173
+ class="pointer-events-none sticky top-0 z-sticky shrink-0 *:pointer-events-auto"
174
+ >
175
+ {@render header()}
176
+ </div>
177
+ {/if}
178
+ <!-- Two branches, not `<svelte:element>`: hydration takes a dynamic
179
+ element out and puts it back, which blurs a control inside it that the
180
+ user had already tabbed to, and the whole page is inside this one. -->
181
+ {#if contentElement === "div"}
182
+ <div data-app-shell-content class={contentClasses}>
183
+ {@render children?.()}
184
+ </div>
185
+ {:else}
186
+ <main data-app-shell-content class={contentClasses}>
187
+ {@render children?.()}
188
+ </main>
189
+ {/if}
190
+ </div>
191
+ {#if footer}
192
+ <div bind:this={footerRegion} data-app-shell-footer class="shrink-0">
193
+ {@render footer()}
194
+ </div>
195
+ {/if}
196
+ </div>
@@ -0,0 +1,61 @@
1
+ import type { Snippet } from "svelte";
2
+ import type { HTMLAttributes } from "svelte/elements";
3
+ /**
4
+ * The layout of a phone app: a bar at the top, content that scrolls, a
5
+ * tab bar at the bottom, and none of it under the notch or the home
6
+ * indicator.
7
+ *
8
+ * The shell is exactly as tall as the visible screen (`100dvh`, so the
9
+ * browser's own bars are accounted for) and only the middle scrolls. The
10
+ * header stays at the top of that scrolling area; the footer sits below it.
11
+ *
12
+ * ```svelte
13
+ * <AppShell>
14
+ * {#snippet header()}
15
+ * <AppBar title="Quizrundan" collapseOnScroll />
16
+ * {/snippet}
17
+ * <div class="p-4">…</div>
18
+ * {#snippet footer()}
19
+ * <BottomTabBar {items} active={page.url.pathname} />
20
+ * {/snippet}
21
+ * </AppShell>
22
+ * ```
23
+ *
24
+ * Two custom properties on the host say how much of the screen the bars
25
+ * take, safe areas included, so anything inside the shell can stay clear
26
+ * of them:
27
+ *
28
+ * - `--app-shell-top-inset`: the height of the header;
29
+ * - `--app-shell-bottom-inset`: the height of the footer.
30
+ *
31
+ * Without a bar the value is the safe-area inset alone. The host is the
32
+ * containing block for anything positioned inside it, and such an element
33
+ * does not scroll with the content, so a floating button above the tab bar
34
+ * is `absolute` with `bottom: calc(var(--app-shell-bottom-inset) + 1rem)`.
35
+ *
36
+ * While the shell is mounted the same two properties are also set on
37
+ * `<html>`, so an overlay that is moved to `document.body` (a sheet, a
38
+ * toast) can read them too. With more than one shell on the page, the one
39
+ * mounted last is the one mirrored there.
40
+ */
41
+ type Props = Omit<HTMLAttributes<HTMLDivElement>, "class" | "style"> & {
42
+ /** The top bar, an `AppBar`. It handles the safe area above it. */
43
+ header?: Snippet;
44
+ /** The scrolling content, rendered in the element `contentElement` names. */
45
+ children?: Snippet;
46
+ /**
47
+ * The element around the content. `main` when the shell is the page,
48
+ * which is what it is for. A page has one `<main>`: use `div` for a
49
+ * shell that sits inside another one, such as a preview in a page of
50
+ * its own.
51
+ */
52
+ contentElement?: "main" | "div";
53
+ /** The bottom bar, a `BottomTabBar`. It handles the safe area below it. */
54
+ footer?: Snippet;
55
+ class?: string;
56
+ /** Added after the two custom properties the shell sets. */
57
+ style?: string;
58
+ };
59
+ declare const AppShell: import("svelte").Component<Props, {}, "">;
60
+ type AppShell = ReturnType<typeof AppShell>;
61
+ export default AppShell;