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
@@ -1,6 +1,8 @@
1
+ import type { SidebarDrawerCloseReason, SidebarTriggerContext } from "./SidebarShell.svelte";
1
2
  import type { Snippet } from "svelte";
2
3
  import type { Component } from "svelte";
3
4
  import type { ButtonVariant, SizeVariant } from "../types/variants.js";
5
+ import { type SidebarNavigationStrings } from "../util/sidebar.js";
4
6
  export interface SidebarNavigationItem {
5
7
  id: string;
6
8
  label: string;
@@ -21,6 +23,14 @@ interface Props {
21
23
  items?: SidebarNavigationItem[];
22
24
  currentPath?: string;
23
25
  ariaLabel?: string;
26
+ /**
27
+ * The words the sidebar says by itself, for another language: the
28
+ * names of its lists, the two lines shown when a search finds
29
+ * nothing, and those of the footer and the brand header in it.
30
+ */
31
+ strings?: Partial<SidebarNavigationStrings>;
32
+ class?: string;
33
+ /** @deprecated use `class`. */
24
34
  className?: string;
25
35
  logoSrc?: string;
26
36
  logoAlt?: string;
@@ -59,7 +69,27 @@ interface Props {
59
69
  profilePanel?: Snippet;
60
70
  /** Parent nav href to highlight when `currentPath` is a deeper leaf (e.g. category row). */
61
71
  activePrimaryHref?: string;
72
+ /**
73
+ * What the sidebar is below the `lg` breakpoint. `drawer`: the rail is
74
+ * hidden there and the sidebar opens in a Drawer from the start edge.
75
+ * This and the six props after it are SidebarShell's; see there.
76
+ */
77
+ mobile?: "none" | "drawer";
78
+ /** Whether the drawer is open. Bindable. */
79
+ isOpen?: boolean;
80
+ /** The button that opens the drawer, rendered in the rail's place below `lg`. */
81
+ trigger?: Snippet<[SidebarTriggerContext]>;
82
+ /** Heading of the drawer. Without it, `ariaLabel` is. */
83
+ drawerTitle?: string;
84
+ /** Accessible name of the drawer's close button. */
85
+ closeLabel?: string;
86
+ /** Fired when the drawer closes itself, with why. */
87
+ onclose?: (detail: {
88
+ reason: SidebarDrawerCloseReason;
89
+ }) => void;
90
+ /** Accessible name of the scrolling region between header and footer. */
91
+ label?: string;
62
92
  }
63
- declare const SidebarNavigation: Component<Props, {}, "isLightMode" | "searchValue">;
93
+ declare const SidebarNavigation: Component<Props, {}, "isOpen" | "isLightMode" | "searchValue">;
64
94
  type SidebarNavigation = ReturnType<typeof SidebarNavigation>;
65
95
  export default SidebarNavigation;
@@ -1,8 +1,11 @@
1
1
  <script lang="ts">
2
+ import { zabiCommonStrings } from "../util/zabi-strings.js";
2
3
  import Input from "../atoms/Input.svelte";
3
4
  import Badge from "../atoms/Badge.svelte";
4
- import { Search, X } from "@lucide/svelte";
5
+ import Search from "@lucide/svelte/icons/search";
6
+ import X from "@lucide/svelte/icons/x";
5
7
  import type { Component } from "svelte";
8
+ import { cn } from "../util/cn.js";
6
9
 
7
10
  export interface SidebarPanelItem {
8
11
  id: string;
@@ -13,6 +16,8 @@
13
16
  }
14
17
 
15
18
  interface Props {
19
+ class?: string;
20
+ /** @deprecated use `class`. */
16
21
  className?: string;
17
22
  widthClass?: string;
18
23
  variant?: "plain" | "elevated";
@@ -35,17 +40,18 @@
35
40
  }
36
41
 
37
42
  let {
38
- className = "",
43
+ class: classAttr = "",
44
+ className: legacyClass = "",
39
45
  widthClass = "w-80",
40
46
  variant = "elevated",
41
47
  ariaLabel = "Picker panel",
42
48
  title = "Items",
43
49
  subtitle = "Choose an item to continue",
44
50
  showSearch = true,
45
- searchPlaceholder = "Search...",
46
- searchValue = $bindable(""),
51
+ searchPlaceholder: searchPlaceholderGiven,
52
+ searchValue = $bindable<Exclude<Props["searchValue"], undefined>>(),
47
53
  items = [],
48
- selectedItemId = $bindable(""),
54
+ selectedItemId = $bindable<Exclude<Props["selectedItemId"], undefined>>(),
49
55
  emptyStateTitle = "No matches",
50
56
  emptyStateDescription = "Try a different keyword.",
51
57
  emptyStateActionLabel = "",
@@ -57,6 +63,26 @@
57
63
  ...restProps
58
64
  }: Props = $props();
59
65
 
66
+ // No fallback on a bindable prop: Svelte refuses `bind:…={undefined}` on
67
+ // one that has a fallback (`props_invalid_value`), and a page that throws
68
+ // while it hydrates never becomes interactive. The default is applied
69
+ // here instead: at once, for the server and the first render, and again
70
+ // whenever a parent hands back `undefined`.
71
+ const applyDefaults = () => {
72
+ if (searchValue === undefined) searchValue = "";
73
+ if (selectedItemId === undefined) selectedItemId = "";
74
+ };
75
+ applyDefaults();
76
+ $effect.pre(applyDefaults);
77
+
78
+ /** Words many components share: a `ZabiStringsProvider` above this one may give them; else English. */
79
+ const common = zabiCommonStrings();
80
+ const searchPlaceholder = $derived(searchPlaceholderGiven ?? common().search);
81
+
82
+ /** `class` is the public prop; `className` is a deprecated alias.
83
+ * Both are merged here so existing call sites keep working. */
84
+ const className = $derived(cn(`${classAttr} ${legacyClass}`));
85
+
60
86
  const normalizedSearchTerm = $derived(searchValue.trim().toLowerCase());
61
87
  const filteredItems = $derived(
62
88
  normalizedSearchTerm
@@ -71,19 +97,22 @@
71
97
  const containerClasses = $derived.by(() => {
72
98
  const resolvedWidthClass = widthClass.trim() || "w-80";
73
99
  const shell = isElevated
74
- ? "rounded-3xl border border-border bg-card text-headline shadow-md ring-1 ring-border/50"
75
- : "rounded-3xl border border-border bg-card text-headline shadow-sm";
76
- return `${resolvedWidthClass} shrink-0 p-5 ${shell} ${className}`.trim();
100
+ ? "rounded-container border border-border bg-card text-headline shadow-sm ring-1 ring-border"
101
+ : "rounded-container border border-border bg-card text-headline shadow-sm";
102
+ return cn(`${resolvedWidthClass} shrink-0 p-5 ${shell} ${className}`);
77
103
  });
78
104
 
79
105
  function getItemClasses(itemId: string): string {
80
106
  const isActive = selectedItemId === itemId;
81
107
  const baseClasses =
82
- "focus-ring focus-ring--nav w-full cursor-pointer rounded-lg px-3 py-2.5 text-left transition-colors duration-150";
108
+ "focus-ring focus-ring--nav w-full cursor-pointer rounded-control px-3 py-2 text-left transition-colors duration-150";
83
109
  if (isActive) {
84
- return `${baseClasses} bg-nav-menu-active text-inherit shadow-sm ring-1 ring-border/80`;
110
+ // The fill, the shadow and the ring are all dropped in forced
111
+ // colours; an outline is drawn there, so the selected item keeps
112
+ // a shape of its own.
113
+ return `${baseClasses} bg-nav-menu-active text-inherit shadow-sm ring-1 ring-border forced-colors:outline-solid forced-colors:outline-2 forced-colors:-outline-offset-2`;
85
114
  }
86
- return `${baseClasses} text-nav-menu-item hover:bg-nav-menu-hover hover:text-nav-menu-item-hover active:bg-base-200`;
115
+ return `${baseClasses} text-nav-menu-item hover:bg-nav-menu-hover hover:text-nav-menu-item-hover active:bg-surface-active`;
87
116
  }
88
117
 
89
118
  function handleSelect(item: SidebarPanelItem): void {
@@ -116,7 +145,7 @@
116
145
  </div>
117
146
  <button
118
147
  type="button"
119
- class="-mr-0.5 -mt-0.5 inline-flex size-9 shrink-0 cursor-pointer items-center justify-center rounded-lg text-nav-menu-item transition-colors hover:bg-nav-menu-hover hover:text-nav-menu-item-hover focus-ring focus-ring--nav"
148
+ class="-mr-0.5 -mt-0.5 inline-flex size-9 pointer-coarse:min-h-11 pointer-coarse:min-w-11 shrink-0 cursor-pointer items-center justify-center rounded-control text-nav-menu-item transition-colors hover:bg-nav-menu-hover hover:text-nav-menu-item-hover active:bg-nav-menu-active focus-ring focus-ring--nav"
120
149
  aria-label={closeLabel}
121
150
  onclick={handleClose}
122
151
  >
@@ -137,7 +166,7 @@
137
166
  bind:value={searchValue}
138
167
  placeholder={searchPlaceholder}
139
168
  aria-label={searchPlaceholder}
140
- class="w-full min-w-0 min-h-10 rounded-xl border-transparent !bg-transparent py-2 pl-10 text-sm ring-1 ring-border/60 hover:!bg-nav-menu-hover focus:!bg-transparent focus-ring focus-ring--nav"
169
+ class="w-full min-w-0 min-h-10 pointer-coarse:min-h-11 rounded-container border-transparent !bg-transparent py-2 pl-10 text-sm ring-1 ring-input-border hover:!bg-nav-menu-hover focus:!bg-transparent focus-ring focus-ring--nav"
141
170
  />
142
171
  </div>
143
172
  {/if}
@@ -154,7 +183,7 @@
154
183
  aria-selected={selectedItemId === item.id}
155
184
  >
156
185
  <span class="flex items-center justify-between gap-3">
157
- <span class="flex min-w-0 items-start gap-2.5">
186
+ <span class="flex min-w-0 items-start gap-3">
158
187
  {#if item.icon}
159
188
  {@const Icon = item.icon}
160
189
  <span
@@ -189,14 +218,14 @@
189
218
  </ul>
190
219
  {:else}
191
220
  <div
192
- class="rounded-xl border border-dashed border-border bg-transparent px-3.5 py-4 ring-1 ring-border/40"
221
+ class="rounded-container border border-dashed border-border bg-transparent px-4 py-4"
193
222
  >
194
223
  <h4 class="text-sm font-semibold text-headline">{emptyStateTitle}</h4>
195
224
  <p class="mt-1 text-sm leading-relaxed text-description">{emptyStateDescription}</p>
196
225
  {#if emptyStateActionLabel.trim() && onEmptyStateAction}
197
226
  <button
198
227
  type="button"
199
- class="mt-3 inline-flex min-h-10 cursor-pointer items-center rounded-lg bg-action-primary px-3 py-2 text-sm font-medium text-action-primary transition-colors hover:bg-action-primary-hover focus-ring focus-ring--nav"
228
+ class="mt-3 inline-flex min-h-10 pointer-coarse:min-h-11 cursor-pointer items-center rounded-control bg-action-primary px-3 py-2 text-sm font-medium text-action-primary transition-colors hover:bg-action-primary-hover focus-ring focus-ring--nav"
200
229
  onclick={handleEmptyStateAction}
201
230
  >
202
231
  {emptyStateActionLabel.trim()}
@@ -10,6 +10,8 @@ export interface SidebarPanelItem {
10
10
  badgeText?: string;
11
11
  }
12
12
  interface Props {
13
+ class?: string;
14
+ /** @deprecated use `class`. */
13
15
  className?: string;
14
16
  widthClass?: string;
15
17
  variant?: "plain" | "elevated";
@@ -0,0 +1,263 @@
1
+ <script lang="ts">
2
+ import { zabiCommonStrings } from "../util/zabi-strings.js";
3
+ import type { Snippet } from "svelte";
4
+ import type { HTMLAttributes } from "svelte/elements";
5
+ import Drawer from "../molecules/Drawer.svelte";
6
+ import { cn } from "../util/cn.js";
7
+ import type { DrawerCloseReason } from "../util/drawer.js";
8
+
9
+ /**
10
+ * The chrome of a sidebar and nothing else: width, surface, collapse
11
+ * behaviour, the inset its regions share, and a scroll container for the
12
+ * middle.
13
+ *
14
+ * `SidebarNavigation` is a fully-specified sidebar driven by ~44 props —
15
+ * excellent when your sidebar looks like that one, useless when it does
16
+ * not, because the parts are not reachable. This is the same shell with
17
+ * the regions left open, so you can drop in `SidebarBrandHeader`,
18
+ * `SidebarNavSection` and `SidebarFooter` (or your own markup) and skip
19
+ * the props you would never set.
20
+ *
21
+ * `SidebarNavigation` is built on it, so the two cannot drift apart.
22
+ *
23
+ * ```svelte
24
+ * <SidebarShell mode="collapsed">
25
+ * {#snippet header({ collapsed })}
26
+ * <SidebarBrandHeader {collapsed} brandName="Zabi" />
27
+ * {/snippet}
28
+ * <SidebarNavSection title="Main" sectionKey="main">…</SidebarNavSection>
29
+ * {#snippet footer({ collapsed, insetX })}
30
+ * <SidebarFooter {collapsed} className={insetX} />
31
+ * {/snippet}
32
+ * </SidebarShell>
33
+ * ```
34
+ *
35
+ * A sidebar is 266px wide, which a phone does not have beside its
36
+ * content. With `mobile="drawer"` the rail is only there from the `lg`
37
+ * breakpoint (1024px) up; below it the same regions open in a Drawer from
38
+ * the start edge, with the Drawer's focus trap, Escape and backdrop:
39
+ *
40
+ * ```svelte
41
+ * <SidebarShell mobile="drawer" bind:isOpen drawerTitle="Menu">
42
+ * {#snippet trigger({ props, toggle })}
43
+ * <IconButton label="Menu" onclick={toggle} {...props}><Menu /></IconButton>
44
+ * {/snippet}
45
+ * …
46
+ * </SidebarShell>
47
+ * ```
48
+ *
49
+ * `trigger` is rendered where the rail would be, below `lg` only; leave
50
+ * it out and open the drawer from a button of your own by setting
51
+ * `isOpen`. The drawer closes when a link in it is followed.
52
+ */
53
+
54
+ /** What the `trigger` snippet gets. */
55
+ export interface SidebarTriggerContext {
56
+ isOpen: boolean;
57
+ toggle: () => void;
58
+ /** Spread on the button: it says what the button opens, and whether it is open. */
59
+ props: { "aria-haspopup": "dialog"; "aria-expanded": boolean };
60
+ }
61
+
62
+ /** What closed the drawer: the user, a link that was followed, or the screen reaching `lg`. */
63
+ export type SidebarDrawerCloseReason = DrawerCloseReason | "navigate" | "resize";
64
+
65
+ /** Passed to every region so it can match the shell's collapse and inset. */
66
+ export interface SidebarRegionContext {
67
+ collapsed: boolean;
68
+ /** Horizontal padding utility the shell's own regions use. */
69
+ insetX: string;
70
+ }
71
+
72
+ // `children` here is a snippet that takes the region context; the one in
73
+ // `HTMLAttributes` takes nothing, and the two together accepted neither.
74
+ type Props = Omit<HTMLAttributes<HTMLElement>, "class" | "onclose" | "children"> & {
75
+ mode?: "expanded" | "collapsed";
76
+ /** `rail` attaches to the viewport edge; `card` is a floating panel. */
77
+ layout?: "rail" | "card";
78
+ ariaLabel?: string;
79
+ /** Accessible name of the scrolling region between header and footer. */
80
+ label?: string;
81
+ /**
82
+ * What the sidebar is below the `lg` breakpoint. `none`: the rail, as
83
+ * everywhere. `drawer`: the rail is hidden there and its regions open
84
+ * in a Drawer from the start edge instead.
85
+ */
86
+ mobile?: "none" | "drawer";
87
+ /** Whether the drawer is open. Bindable. Only used with `mobile="drawer"`. */
88
+ isOpen?: boolean;
89
+ /** Heading of the drawer, and its accessible name. Without it, `ariaLabel` is. */
90
+ drawerTitle?: string;
91
+ /** Accessible name of the drawer's close button. */
92
+ closeLabel?: string;
93
+ /** Fired when the drawer closes itself, with why. */
94
+ onclose?: (detail: { reason: SidebarDrawerCloseReason }) => void;
95
+ /**
96
+ * The button that opens the drawer, rendered in the rail's place below
97
+ * `lg`. Optional: a button anywhere that sets `isOpen` does the same.
98
+ */
99
+ trigger?: Snippet<[SidebarTriggerContext]>;
100
+ class?: string;
101
+ /** @deprecated use `class`. */
102
+ className?: string;
103
+ /** Brand row, search — anything above the scrolling nav area. */
104
+ header?: Snippet<[SidebarRegionContext]>;
105
+ /** The scrolling middle. */
106
+ children?: Snippet<[SidebarRegionContext]>;
107
+ /** Account row, logout, theme toggle. Outside the scroll container. */
108
+ footer?: Snippet<[SidebarRegionContext]>;
109
+ };
110
+
111
+ let {
112
+ mode = "expanded",
113
+ layout = "rail",
114
+ ariaLabel = "Sidebar navigation",
115
+ label = "Navigation links",
116
+ mobile = "none",
117
+ isOpen = $bindable<Exclude<Props["isOpen"], undefined>>(),
118
+ drawerTitle = undefined,
119
+ closeLabel: closeLabelGiven,
120
+ onclose,
121
+ trigger,
122
+ class: classAttr = "",
123
+ className: legacyClass = "",
124
+ header,
125
+ children,
126
+ footer,
127
+ ...restProps
128
+ }: Props = $props();
129
+
130
+ // No fallback on a bindable prop: Svelte refuses `bind:…={undefined}` on
131
+ // one that has a fallback (`props_invalid_value`), and a page that throws
132
+ // while it hydrates never becomes interactive. The default is applied
133
+ // here instead: at once, for the server and the first render, and again
134
+ // whenever a parent hands back `undefined`.
135
+ const applyDefaults = () => {
136
+ if (isOpen === undefined) isOpen = false;
137
+ };
138
+ applyDefaults();
139
+ $effect.pre(applyDefaults);
140
+
141
+ /** Words many components share: a `ZabiStringsProvider` above this one may give them; else English. */
142
+ const common = zabiCommonStrings();
143
+ const closeLabel = $derived(closeLabelGiven ?? common().close);
144
+
145
+ const className = $derived(cn(classAttr, legacyClass));
146
+ const isCollapsed = $derived(mode === "collapsed");
147
+ const isCard = $derived(layout === "card");
148
+
149
+ const insetX = $derived(isCollapsed ? "px-2" : "px-4");
150
+
151
+ const containerClasses = $derived.by(() => {
152
+ const widthClass = isCollapsed ? "w-[72px]" : "w-[266px]";
153
+ const railSurface = "border-r border-border bg-background text-headline";
154
+ const cardSurface =
155
+ "border-r border-border bg-background text-headline shadow-sm";
156
+ const surfaceClasses = isCard ? cardSurface : railSurface;
157
+ const verticalPad = isCard ? "py-4" : "py-5";
158
+ // `flex` and `max-lg:hidden` must not both apply below `lg`: with
159
+ // the drawer mode the rail is a flex box from `lg` up only.
160
+ const display = asDrawer ? "hidden lg:flex" : "flex";
161
+ const baseClasses = `${display} h-full min-h-0 max-h-full flex-col overflow-visible ${verticalPad}`;
162
+
163
+ return cn(`${baseClasses} ${widthClass} ${surfaceClasses} ${className}`);
164
+ });
165
+
166
+ const region = $derived({ collapsed: isCollapsed, insetX });
167
+
168
+ const asDrawer = $derived(mobile === "drawer");
169
+ /** In the drawer the sidebar is always spelled out, and the drawer's own padding is the inset. */
170
+ const drawerRegion: SidebarRegionContext = { collapsed: false, insetX: "px-0" };
171
+
172
+ function closeDrawer(reason: SidebarDrawerCloseReason) {
173
+ if (!isOpen) return;
174
+ isOpen = false;
175
+ onclose?.({ reason });
176
+ }
177
+
178
+ // From `lg` up the rail is there: a drawer left open would cover it.
179
+ $effect(() => {
180
+ if (!asDrawer || typeof window.matchMedia !== "function") return;
181
+ const wide = window.matchMedia("(min-width: 1024px)");
182
+ const onChange = () => {
183
+ if (wide.matches) closeDrawer("resize");
184
+ };
185
+ onChange();
186
+ wide.addEventListener("change", onChange);
187
+ return () => wide.removeEventListener("change", onChange);
188
+ });
189
+
190
+ /** A link in the drawer was followed: the page it leads to is behind the drawer. */
191
+ function handleDrawerClick(event: MouseEvent) {
192
+ if (!(event.target instanceof Element)) return;
193
+ if (event.target.closest("a[href]")) closeDrawer("navigate");
194
+ }
195
+
196
+ const triggerContext = $derived<SidebarTriggerContext>({
197
+ isOpen,
198
+ toggle: () => (isOpen ? closeDrawer("close-button") : (isOpen = true)),
199
+ props: { "aria-haspopup": "dialog", "aria-expanded": isOpen },
200
+ });
201
+ </script>
202
+
203
+ {#snippet regions(context: SidebarRegionContext, inDrawer: boolean)}
204
+ <div class={cn("flex w-full min-w-0 flex-col", !inDrawer && "min-h-0 flex-1")}>
205
+ {#if header}
206
+ <div class={cn("flex w-full shrink-0 flex-col gap-5 pb-3", context.insetX)}>
207
+ {@render header(context)}
208
+ </div>
209
+ {/if}
210
+
211
+ <!-- In the drawer the drawer's body scrolls, so this region does not. -->
212
+ <div
213
+ class={cn(
214
+ "flex min-w-0 flex-col gap-3 pt-4 pb-2",
215
+ !inDrawer &&
216
+ "min-h-0 flex-1 overflow-y-auto overflow-x-hidden overscroll-y-contain",
217
+ context.insetX,
218
+ )}
219
+ role="region"
220
+ aria-label={label}
221
+ >
222
+ {@render children?.(context)}
223
+ </div>
224
+ </div>
225
+
226
+ {#if footer}
227
+ {@render footer(context)}
228
+ {/if}
229
+ {/snippet}
230
+
231
+ <nav class={containerClasses} aria-label={ariaLabel} {...restProps}>
232
+ {@render regions(region, false)}
233
+ </nav>
234
+
235
+ {#if asDrawer}
236
+ {#if trigger}
237
+ <div class="lg:hidden" data-sidebar-trigger>
238
+ {@render trigger(triggerContext)}
239
+ </div>
240
+ {/if}
241
+ <!-- The same regions, in a Drawer. Rendered only while it is open, so
242
+ nothing of the sidebar is in the page twice until then, and the server
243
+ renders the rail alone. -->
244
+ <Drawer
245
+ bind:isOpen
246
+ side="start"
247
+ size="sm"
248
+ title={drawerTitle || ariaLabel}
249
+ {closeLabel}
250
+ onclose={(detail) => onclose?.(detail)}
251
+ class="lg:hidden"
252
+ >
253
+ <!-- svelte-ignore a11y_click_events_have_key_events, a11y_no_noninteractive_element_interactions -->
254
+ <nav
255
+ class="flex w-full min-w-0 flex-col text-headline"
256
+ aria-label={ariaLabel}
257
+ data-sidebar-drawer
258
+ onclick={handleDrawerClick}
259
+ >
260
+ {@render regions(drawerRegion, true)}
261
+ </nav>
262
+ </Drawer>
263
+ {/if}
@@ -0,0 +1,106 @@
1
+ import type { Snippet } from "svelte";
2
+ import type { HTMLAttributes } from "svelte/elements";
3
+ import type { DrawerCloseReason } from "../util/drawer.js";
4
+ /**
5
+ * The chrome of a sidebar and nothing else: width, surface, collapse
6
+ * behaviour, the inset its regions share, and a scroll container for the
7
+ * middle.
8
+ *
9
+ * `SidebarNavigation` is a fully-specified sidebar driven by ~44 props —
10
+ * excellent when your sidebar looks like that one, useless when it does
11
+ * not, because the parts are not reachable. This is the same shell with
12
+ * the regions left open, so you can drop in `SidebarBrandHeader`,
13
+ * `SidebarNavSection` and `SidebarFooter` (or your own markup) and skip
14
+ * the props you would never set.
15
+ *
16
+ * `SidebarNavigation` is built on it, so the two cannot drift apart.
17
+ *
18
+ * ```svelte
19
+ * <SidebarShell mode="collapsed">
20
+ * {#snippet header({ collapsed })}
21
+ * <SidebarBrandHeader {collapsed} brandName="Zabi" />
22
+ * {/snippet}
23
+ * <SidebarNavSection title="Main" sectionKey="main">…</SidebarNavSection>
24
+ * {#snippet footer({ collapsed, insetX })}
25
+ * <SidebarFooter {collapsed} className={insetX} />
26
+ * {/snippet}
27
+ * </SidebarShell>
28
+ * ```
29
+ *
30
+ * A sidebar is 266px wide, which a phone does not have beside its
31
+ * content. With `mobile="drawer"` the rail is only there from the `lg`
32
+ * breakpoint (1024px) up; below it the same regions open in a Drawer from
33
+ * the start edge, with the Drawer's focus trap, Escape and backdrop:
34
+ *
35
+ * ```svelte
36
+ * <SidebarShell mobile="drawer" bind:isOpen drawerTitle="Menu">
37
+ * {#snippet trigger({ props, toggle })}
38
+ * <IconButton label="Menu" onclick={toggle} {...props}><Menu /></IconButton>
39
+ * {/snippet}
40
+ * …
41
+ * </SidebarShell>
42
+ * ```
43
+ *
44
+ * `trigger` is rendered where the rail would be, below `lg` only; leave
45
+ * it out and open the drawer from a button of your own by setting
46
+ * `isOpen`. The drawer closes when a link in it is followed.
47
+ */
48
+ /** What the `trigger` snippet gets. */
49
+ export interface SidebarTriggerContext {
50
+ isOpen: boolean;
51
+ toggle: () => void;
52
+ /** Spread on the button: it says what the button opens, and whether it is open. */
53
+ props: {
54
+ "aria-haspopup": "dialog";
55
+ "aria-expanded": boolean;
56
+ };
57
+ }
58
+ /** What closed the drawer: the user, a link that was followed, or the screen reaching `lg`. */
59
+ export type SidebarDrawerCloseReason = DrawerCloseReason | "navigate" | "resize";
60
+ /** Passed to every region so it can match the shell's collapse and inset. */
61
+ export interface SidebarRegionContext {
62
+ collapsed: boolean;
63
+ /** Horizontal padding utility the shell's own regions use. */
64
+ insetX: string;
65
+ }
66
+ type Props = Omit<HTMLAttributes<HTMLElement>, "class" | "onclose" | "children"> & {
67
+ mode?: "expanded" | "collapsed";
68
+ /** `rail` attaches to the viewport edge; `card` is a floating panel. */
69
+ layout?: "rail" | "card";
70
+ ariaLabel?: string;
71
+ /** Accessible name of the scrolling region between header and footer. */
72
+ label?: string;
73
+ /**
74
+ * What the sidebar is below the `lg` breakpoint. `none`: the rail, as
75
+ * everywhere. `drawer`: the rail is hidden there and its regions open
76
+ * in a Drawer from the start edge instead.
77
+ */
78
+ mobile?: "none" | "drawer";
79
+ /** Whether the drawer is open. Bindable. Only used with `mobile="drawer"`. */
80
+ isOpen?: boolean;
81
+ /** Heading of the drawer, and its accessible name. Without it, `ariaLabel` is. */
82
+ drawerTitle?: string;
83
+ /** Accessible name of the drawer's close button. */
84
+ closeLabel?: string;
85
+ /** Fired when the drawer closes itself, with why. */
86
+ onclose?: (detail: {
87
+ reason: SidebarDrawerCloseReason;
88
+ }) => void;
89
+ /**
90
+ * The button that opens the drawer, rendered in the rail's place below
91
+ * `lg`. Optional: a button anywhere that sets `isOpen` does the same.
92
+ */
93
+ trigger?: Snippet<[SidebarTriggerContext]>;
94
+ class?: string;
95
+ /** @deprecated use `class`. */
96
+ className?: string;
97
+ /** Brand row, search — anything above the scrolling nav area. */
98
+ header?: Snippet<[SidebarRegionContext]>;
99
+ /** The scrolling middle. */
100
+ children?: Snippet<[SidebarRegionContext]>;
101
+ /** Account row, logout, theme toggle. Outside the scroll container. */
102
+ footer?: Snippet<[SidebarRegionContext]>;
103
+ };
104
+ declare const SidebarShell: import("svelte").Component<Props, {}, "isOpen">;
105
+ type SidebarShell = ReturnType<typeof SidebarShell>;
106
+ export default SidebarShell;