zabi-components 8.0.0 → 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 (364) hide show
  1. package/CHANGELOG.md +1924 -0
  2. package/README.md +243 -10
  3. package/THEME.md +149 -332
  4. package/THEMING.md +1192 -266
  5. package/dist/atoms/ActionPanel.svelte +15 -5
  6. package/dist/atoms/Avatar.svelte +104 -0
  7. package/dist/atoms/Avatar.svelte.d.ts +34 -0
  8. package/dist/atoms/Badge.svelte +36 -7
  9. package/dist/atoms/Badge.svelte.d.ts +2 -2
  10. package/dist/atoms/Button.svelte +177 -27
  11. package/dist/atoms/Button.svelte.d.ts +30 -2
  12. package/dist/atoms/Card.svelte +4 -4
  13. package/dist/atoms/CardHeader.svelte +16 -4
  14. package/dist/atoms/Checkbox.svelte +48 -21
  15. package/dist/atoms/Checkbox.svelte.d.ts +4 -2
  16. package/dist/atoms/CodeBlock.svelte +20 -2
  17. package/dist/atoms/CodeBlock.svelte.d.ts +4 -0
  18. package/dist/atoms/ColorPicker.svelte +259 -49
  19. package/dist/atoms/ColorPicker.svelte.d.ts +3 -0
  20. package/dist/atoms/Container.svelte +16 -8
  21. package/dist/atoms/DateField.svelte +107 -0
  22. package/dist/atoms/DateField.svelte.d.ts +53 -0
  23. package/dist/atoms/FieldMessages.svelte +69 -0
  24. package/dist/atoms/FieldMessages.svelte.d.ts +23 -0
  25. package/dist/atoms/FloatingActionButton.svelte +174 -0
  26. package/dist/atoms/FloatingActionButton.svelte.d.ts +51 -0
  27. package/dist/atoms/Heading.svelte +37 -5
  28. package/dist/atoms/Heading.svelte.d.ts +7 -0
  29. package/dist/atoms/IconButton.svelte +220 -24
  30. package/dist/atoms/IconButton.svelte.d.ts +49 -4
  31. package/dist/atoms/Input.svelte +253 -52
  32. package/dist/atoms/Input.svelte.d.ts +32 -2
  33. package/dist/atoms/List.svelte +4 -1
  34. package/dist/atoms/ListItem.svelte +44 -11
  35. package/dist/atoms/ListItem.svelte.d.ts +5 -0
  36. package/dist/atoms/Progress.svelte +27 -8
  37. package/dist/atoms/Progress.svelte.d.ts +4 -0
  38. package/dist/atoms/Radio.svelte +19 -3
  39. package/dist/atoms/Radio.svelte.d.ts +4 -2
  40. package/dist/atoms/Rating.svelte +494 -0
  41. package/dist/atoms/Rating.svelte.d.ts +56 -0
  42. package/dist/atoms/Select.svelte +433 -91
  43. package/dist/atoms/Select.svelte.d.ts +49 -3
  44. package/dist/atoms/SelectionControl.svelte +94 -17
  45. package/dist/atoms/SelectionControl.svelte.d.ts +4 -3
  46. package/dist/atoms/Skeleton.svelte +1 -1
  47. package/dist/atoms/Slider.svelte +309 -0
  48. package/dist/atoms/Slider.svelte.d.ts +40 -0
  49. package/dist/atoms/Spinner.svelte +57 -0
  50. package/dist/atoms/Spinner.svelte.d.ts +19 -0
  51. package/dist/atoms/Table.svelte +151 -6
  52. package/dist/atoms/Table.svelte.d.ts +15 -3
  53. package/dist/atoms/TemporalField.svelte +140 -0
  54. package/dist/atoms/TemporalField.svelte.d.ts +32 -0
  55. package/dist/atoms/Text.svelte +23 -11
  56. package/dist/atoms/Text.svelte.d.ts +6 -3
  57. package/dist/atoms/Textarea.svelte +55 -44
  58. package/dist/atoms/Textarea.svelte.d.ts +14 -2
  59. package/dist/atoms/ThemeToggle.svelte +223 -44
  60. package/dist/atoms/ThemeToggle.svelte.d.ts +26 -3
  61. package/dist/atoms/TimeField.svelte +108 -0
  62. package/dist/atoms/TimeField.svelte.d.ts +54 -0
  63. package/dist/atoms/Toast.svelte +17 -4
  64. package/dist/atoms/Toast.svelte.d.ts +2 -0
  65. package/dist/atoms/Toggle.svelte +41 -8
  66. package/dist/atoms/Toggle.svelte.d.ts +11 -2
  67. package/dist/atoms/Tooltip.svelte +508 -23
  68. package/dist/atoms/Tooltip.svelte.d.ts +11 -0
  69. package/dist/atoms/ZabiStringsProvider.svelte +45 -0
  70. package/dist/atoms/ZabiStringsProvider.svelte.d.ts +27 -0
  71. package/dist/atoms/index.d.ts +17 -0
  72. package/dist/atoms/index.js +12 -0
  73. package/dist/atoms/selection-control.styles.d.ts +36 -5
  74. package/dist/atoms/selection-control.styles.js +40 -9
  75. package/dist/components/atoms/index.d.ts +17 -0
  76. package/dist/components/atoms/index.d.ts.map +1 -1
  77. package/dist/components/atoms/selection-control.styles.d.ts +36 -5
  78. package/dist/components/atoms/selection-control.styles.d.ts.map +1 -1
  79. package/dist/components/index.d.ts +80 -2
  80. package/dist/components/index.d.ts.map +1 -1
  81. package/dist/components/molecules/index.d.ts +46 -0
  82. package/dist/components/molecules/index.d.ts.map +1 -1
  83. package/dist/components/molecules/toast-store.d.ts +53 -7
  84. package/dist/components/molecules/toast-store.d.ts.map +1 -1
  85. package/dist/components/organisms/index.d.ts +5 -0
  86. package/dist/components/organisms/index.d.ts.map +1 -1
  87. package/dist/components/types/variants.d.ts +15 -1
  88. package/dist/components/types/variants.d.ts.map +1 -1
  89. package/dist/components/util/app-shell.d.ts +28 -0
  90. package/dist/components/util/app-shell.d.ts.map +1 -0
  91. package/dist/components/util/avatar.d.ts +36 -0
  92. package/dist/components/util/avatar.d.ts.map +1 -0
  93. package/dist/components/util/bottom-sheet.d.ts +30 -0
  94. package/dist/components/util/bottom-sheet.d.ts.map +1 -0
  95. package/dist/components/util/bottom-tab-bar.d.ts +35 -0
  96. package/dist/components/util/bottom-tab-bar.d.ts.map +1 -0
  97. package/dist/components/util/calendar.d.ts +56 -0
  98. package/dist/components/util/calendar.d.ts.map +1 -0
  99. package/dist/components/util/collapsible.d.ts +58 -0
  100. package/dist/components/util/collapsible.d.ts.map +1 -0
  101. package/dist/components/util/confirm-dialog.d.ts +12 -0
  102. package/dist/components/util/confirm-dialog.d.ts.map +1 -0
  103. package/dist/components/util/date.d.ts +96 -0
  104. package/dist/components/util/date.d.ts.map +1 -0
  105. package/dist/components/util/drawer.d.ts +13 -0
  106. package/dist/components/util/drawer.d.ts.map +1 -0
  107. package/dist/components/util/dropdown.d.ts +47 -0
  108. package/dist/components/util/dropdown.d.ts.map +1 -0
  109. package/dist/components/util/field.d.ts +39 -0
  110. package/dist/components/util/field.d.ts.map +1 -0
  111. package/dist/components/util/fit-in-viewport.d.ts +201 -0
  112. package/dist/components/util/fit-in-viewport.d.ts.map +1 -0
  113. package/dist/components/util/focus-utils.d.ts +75 -2
  114. package/dist/components/util/focus-utils.d.ts.map +1 -1
  115. package/dist/components/util/hydration.d.ts +45 -0
  116. package/dist/components/util/hydration.d.ts.map +1 -0
  117. package/dist/components/util/keyboard-inset.d.ts +18 -0
  118. package/dist/components/util/keyboard-inset.d.ts.map +1 -0
  119. package/dist/components/util/media-grid.d.ts +51 -0
  120. package/dist/components/util/media-grid.d.ts.map +1 -0
  121. package/dist/components/util/media.d.ts +6 -0
  122. package/dist/components/util/media.d.ts.map +1 -0
  123. package/dist/components/util/overlay-chrome.d.ts +53 -0
  124. package/dist/components/util/overlay-chrome.d.ts.map +1 -0
  125. package/dist/components/util/overlay.d.ts +72 -0
  126. package/dist/components/util/overlay.d.ts.map +1 -0
  127. package/dist/components/util/photo.d.ts +215 -0
  128. package/dist/components/util/photo.d.ts.map +1 -0
  129. package/dist/components/util/portal.d.ts +23 -0
  130. package/dist/components/util/portal.d.ts.map +1 -0
  131. package/dist/components/util/pull-to-refresh.d.ts +27 -0
  132. package/dist/components/util/pull-to-refresh.d.ts.map +1 -0
  133. package/dist/components/util/radio-keys.d.ts +25 -0
  134. package/dist/components/util/radio-keys.d.ts.map +1 -0
  135. package/dist/components/util/rating.d.ts +23 -0
  136. package/dist/components/util/rating.d.ts.map +1 -0
  137. package/dist/components/util/ready-made-strings.d.ts +77 -0
  138. package/dist/components/util/ready-made-strings.d.ts.map +1 -0
  139. package/dist/components/util/scroll-padding.d.ts +20 -0
  140. package/dist/components/util/scroll-padding.d.ts.map +1 -0
  141. package/dist/components/util/segmented-control.d.ts +16 -0
  142. package/dist/components/util/segmented-control.d.ts.map +1 -0
  143. package/dist/components/util/select.d.ts +41 -0
  144. package/dist/components/util/select.d.ts.map +1 -0
  145. package/dist/components/util/sheet-drag.d.ts +60 -0
  146. package/dist/components/util/sheet-drag.d.ts.map +1 -0
  147. package/dist/components/util/sidebar.d.ts +63 -0
  148. package/dist/components/util/sidebar.d.ts.map +1 -0
  149. package/dist/components/util/sortable-list.d.ts +65 -0
  150. package/dist/components/util/sortable-list.d.ts.map +1 -0
  151. package/dist/components/util/stepper.d.ts +40 -0
  152. package/dist/components/util/stepper.d.ts.map +1 -0
  153. package/dist/components/util/swipeable-list-item.d.ts +36 -0
  154. package/dist/components/util/swipeable-list-item.d.ts.map +1 -0
  155. package/dist/components/util/theme-mode.d.ts +80 -0
  156. package/dist/components/util/theme-mode.d.ts.map +1 -0
  157. package/dist/components/util/toaster.d.ts +93 -0
  158. package/dist/components/util/toaster.d.ts.map +1 -0
  159. package/dist/components/util/tooltip.d.ts +10 -0
  160. package/dist/components/util/tooltip.d.ts.map +1 -0
  161. package/dist/components/util/top-navbar.d.ts +11 -0
  162. package/dist/components/util/top-navbar.d.ts.map +1 -0
  163. package/dist/components/util/touch-target.d.ts +31 -0
  164. package/dist/components/util/touch-target.d.ts.map +1 -0
  165. package/dist/components/util/zabi-strings.d.ts +133 -0
  166. package/dist/components/util/zabi-strings.d.ts.map +1 -0
  167. package/dist/create-theme/cli.js +169 -0
  168. package/dist/create-theme/index.d.ts +176 -0
  169. package/dist/create-theme/index.js +817 -0
  170. package/dist/create-theme/lib/ramp-math.js +128 -0
  171. package/dist/create-theme/lib/resolve.js +141 -0
  172. package/dist/create-theme/theme-data.js +1116 -0
  173. package/dist/index.d.ts +80 -2
  174. package/dist/index.js +57 -2
  175. package/dist/molecules/Alert.svelte +33 -5
  176. package/dist/molecules/Alert.svelte.d.ts +2 -0
  177. package/dist/molecules/AppBar.svelte +415 -0
  178. package/dist/molecules/AppBar.svelte.d.ts +69 -0
  179. package/dist/molecules/AvatarGroup.svelte +116 -0
  180. package/dist/molecules/AvatarGroup.svelte.d.ts +40 -0
  181. package/dist/molecules/BottomSheet.svelte +540 -0
  182. package/dist/molecules/BottomSheet.svelte.d.ts +93 -0
  183. package/dist/molecules/BottomTabBar.svelte +297 -0
  184. package/dist/molecules/BottomTabBar.svelte.d.ts +64 -0
  185. package/dist/molecules/Calendar.svelte +597 -0
  186. package/dist/molecules/Calendar.svelte.d.ts +72 -0
  187. package/dist/molecules/Collapsible.svelte +243 -0
  188. package/dist/molecules/Collapsible.svelte.d.ts +44 -0
  189. package/dist/molecules/CollapsibleGroup.svelte +126 -0
  190. package/dist/molecules/CollapsibleGroup.svelte.d.ts +14 -0
  191. package/dist/molecules/ComponentDemo.svelte +16 -4
  192. package/dist/molecules/ComponentDemo.svelte.d.ts +3 -0
  193. package/dist/molecules/ConfirmDialog.svelte +253 -0
  194. package/dist/molecules/ConfirmDialog.svelte.d.ts +55 -0
  195. package/dist/molecules/ContactForm.svelte +34 -17
  196. package/dist/molecules/ContactForm.svelte.d.ts +8 -0
  197. package/dist/molecules/Drawer.svelte +394 -0
  198. package/dist/molecules/Drawer.svelte.d.ts +55 -0
  199. package/dist/molecules/Dropdown.svelte +509 -48
  200. package/dist/molecules/Dropdown.svelte.d.ts +45 -6
  201. package/dist/molecules/DropdownItem.svelte +122 -0
  202. package/dist/molecules/DropdownItem.svelte.d.ts +28 -0
  203. package/dist/molecules/EmptyState.svelte +55 -10
  204. package/dist/molecules/EmptyState.svelte.d.ts +8 -0
  205. package/dist/molecules/FormField.svelte +9 -1
  206. package/dist/molecules/FormField.svelte.d.ts +2 -0
  207. package/dist/molecules/Header.svelte +1 -1
  208. package/dist/molecules/ImageUpload.svelte +383 -70
  209. package/dist/molecules/ImageUpload.svelte.d.ts +47 -2
  210. package/dist/molecules/MediaGrid.svelte +527 -0
  211. package/dist/molecules/MediaGrid.svelte.d.ts +77 -0
  212. package/dist/molecules/Modal.svelte +288 -72
  213. package/dist/molecules/Modal.svelte.d.ts +51 -2
  214. package/dist/molecules/NavigationMenuContent.svelte +58 -1
  215. package/dist/molecules/NavigationMenuItem.svelte +0 -1
  216. package/dist/molecules/NavigationMenuLink.svelte +1 -1
  217. package/dist/molecules/NavigationMenuList.svelte +10 -3
  218. package/dist/molecules/NavigationMenuTrigger.svelte +2 -2
  219. package/dist/molecules/Page.svelte +32 -2
  220. package/dist/molecules/Page.svelte.d.ts +11 -0
  221. package/dist/molecules/PhotoGrid.svelte +433 -0
  222. package/dist/molecules/PhotoGrid.svelte.d.ts +62 -0
  223. package/dist/molecules/PhotoViewer.svelte +1116 -0
  224. package/dist/molecules/PhotoViewer.svelte.d.ts +56 -0
  225. package/dist/molecules/PropsTable.svelte +22 -10
  226. package/dist/molecules/PropsTable.svelte.d.ts +3 -0
  227. package/dist/molecules/PullToRefresh.svelte +263 -0
  228. package/dist/molecules/PullToRefresh.svelte.d.ts +52 -0
  229. package/dist/molecules/RadioGroup.svelte +52 -14
  230. package/dist/molecules/RadioGroup.svelte.d.ts +14 -3
  231. package/dist/molecules/Section.svelte +1 -1
  232. package/dist/molecules/SegmentedControl.svelte +323 -0
  233. package/dist/molecules/SegmentedControl.svelte.d.ts +34 -0
  234. package/dist/molecules/SidebarBrandHeader.svelte +11 -1
  235. package/dist/molecules/SidebarBrandHeader.svelte.d.ts +3 -0
  236. package/dist/molecules/SidebarFooter.svelte +29 -6
  237. package/dist/molecules/SidebarFooter.svelte.d.ts +3 -0
  238. package/dist/molecules/SlideUp.svelte +220 -61
  239. package/dist/molecules/SlideUp.svelte.d.ts +26 -0
  240. package/dist/molecules/SortableList.svelte +632 -0
  241. package/dist/molecules/SortableList.svelte.d.ts +69 -0
  242. package/dist/molecules/Stepper.svelte +548 -0
  243. package/dist/molecules/Stepper.svelte.d.ts +39 -0
  244. package/dist/molecules/StickyActionBar.svelte +191 -0
  245. package/dist/molecules/StickyActionBar.svelte.d.ts +65 -0
  246. package/dist/molecules/SwipeableListItem.svelte +334 -0
  247. package/dist/molecules/SwipeableListItem.svelte.d.ts +54 -0
  248. package/dist/molecules/Tabs.svelte +233 -9
  249. package/dist/molecules/Tabs.svelte.d.ts +7 -0
  250. package/dist/molecules/Toaster.svelte +350 -5
  251. package/dist/molecules/Toaster.svelte.d.ts +95 -2
  252. package/dist/molecules/ToasterToast.svelte +253 -67
  253. package/dist/molecules/ToasterToast.svelte.d.ts +9 -0
  254. package/dist/molecules/UnsavedChangesBar.svelte +212 -0
  255. package/dist/molecules/UnsavedChangesBar.svelte.d.ts +43 -0
  256. package/dist/molecules/index.d.ts +46 -0
  257. package/dist/molecules/index.js +27 -0
  258. package/dist/molecules/toast-store.d.ts +53 -7
  259. package/dist/molecules/toast-store.js +21 -0
  260. package/dist/organisms/AppShell.svelte +196 -0
  261. package/dist/organisms/AppShell.svelte.d.ts +61 -0
  262. package/dist/organisms/SidebarAccountPanel.svelte +107 -12
  263. package/dist/organisms/SidebarAccountPanel.svelte.d.ts +19 -0
  264. package/dist/organisms/SidebarNavigation.svelte +96 -15
  265. package/dist/organisms/SidebarNavigation.svelte.d.ts +29 -1
  266. package/dist/organisms/SidebarPanel.svelte +31 -10
  267. package/dist/organisms/SidebarShell.svelte +162 -11
  268. package/dist/organisms/SidebarShell.svelte.d.ts +56 -2
  269. package/dist/organisms/TopNavbar.svelte +256 -31
  270. package/dist/organisms/TopNavbar.svelte.d.ts +26 -0
  271. package/dist/organisms/index.d.ts +5 -0
  272. package/dist/organisms/index.js +3 -0
  273. package/dist/types/components.d.ts +4 -0
  274. package/dist/types/components.d.ts.map +1 -1
  275. package/dist/types/index.d.ts +605 -25
  276. package/dist/types/index.d.ts.map +1 -1
  277. package/dist/types/variants.ts +17 -1
  278. package/dist/util/app-shell.d.ts +27 -0
  279. package/dist/util/app-shell.js +72 -0
  280. package/dist/util/avatar.d.ts +35 -0
  281. package/dist/util/avatar.js +52 -0
  282. package/dist/util/bottom-sheet.d.ts +29 -0
  283. package/dist/util/bottom-sheet.js +43 -0
  284. package/dist/util/bottom-tab-bar.d.ts +34 -0
  285. package/dist/util/bottom-tab-bar.js +54 -0
  286. package/dist/util/calendar.d.ts +55 -0
  287. package/dist/util/calendar.js +74 -0
  288. package/dist/util/collapsible.d.ts +57 -0
  289. package/dist/util/collapsible.js +23 -0
  290. package/dist/util/confirm-dialog.d.ts +11 -0
  291. package/dist/util/confirm-dialog.js +2 -0
  292. package/dist/util/date.d.ts +95 -0
  293. package/dist/util/date.js +237 -0
  294. package/dist/util/drawer.d.ts +12 -0
  295. package/dist/util/drawer.js +7 -0
  296. package/dist/util/dropdown.d.ts +46 -0
  297. package/dist/util/dropdown.js +21 -0
  298. package/dist/util/field.d.ts +38 -0
  299. package/dist/util/field.js +38 -0
  300. package/dist/util/fit-in-viewport.d.ts +200 -0
  301. package/dist/util/fit-in-viewport.js +399 -0
  302. package/dist/util/focus-utils.d.ts +75 -2
  303. package/dist/util/focus-utils.js +182 -9
  304. package/dist/util/hydration.d.ts +44 -0
  305. package/dist/util/hydration.js +58 -0
  306. package/dist/util/keyboard-inset.d.ts +17 -0
  307. package/dist/util/keyboard-inset.js +53 -0
  308. package/dist/util/media-grid.d.ts +50 -0
  309. package/dist/util/media-grid.js +81 -0
  310. package/dist/util/media.d.ts +5 -0
  311. package/dist/util/media.js +10 -0
  312. package/dist/util/overlay-chrome.d.ts +52 -0
  313. package/dist/util/overlay-chrome.js +56 -0
  314. package/dist/util/overlay.d.ts +71 -0
  315. package/dist/util/overlay.js +240 -0
  316. package/dist/util/photo.d.ts +214 -0
  317. package/dist/util/photo.js +206 -0
  318. package/dist/util/portal.d.ts +22 -0
  319. package/dist/util/portal.js +50 -0
  320. package/dist/util/pull-to-refresh.d.ts +26 -0
  321. package/dist/util/pull-to-refresh.js +30 -0
  322. package/dist/util/radio-keys.d.ts +24 -0
  323. package/dist/util/radio-keys.js +49 -0
  324. package/dist/util/rating.d.ts +22 -0
  325. package/dist/util/rating.js +26 -0
  326. package/dist/util/ready-made-strings.d.ts +76 -0
  327. package/dist/util/ready-made-strings.js +67 -0
  328. package/dist/util/scroll-padding.d.ts +19 -0
  329. package/dist/util/scroll-padding.js +44 -0
  330. package/dist/util/segmented-control.d.ts +15 -0
  331. package/dist/util/segmented-control.js +1 -0
  332. package/dist/util/select.d.ts +40 -0
  333. package/dist/util/select.js +63 -0
  334. package/dist/util/sheet-drag.d.ts +59 -0
  335. package/dist/util/sheet-drag.js +241 -0
  336. package/dist/util/sidebar.d.ts +62 -0
  337. package/dist/util/sidebar.js +38 -0
  338. package/dist/util/sortable-list.d.ts +64 -0
  339. package/dist/util/sortable-list.js +53 -0
  340. package/dist/util/stepper.d.ts +39 -0
  341. package/dist/util/stepper.js +38 -0
  342. package/dist/util/swipeable-list-item.d.ts +35 -0
  343. package/dist/util/swipeable-list-item.js +32 -0
  344. package/dist/util/theme-mode.d.ts +79 -0
  345. package/dist/util/theme-mode.js +121 -0
  346. package/dist/util/toaster.d.ts +92 -0
  347. package/dist/util/toaster.js +75 -0
  348. package/dist/util/tooltip.d.ts +9 -0
  349. package/dist/util/tooltip.js +18 -0
  350. package/dist/util/top-navbar.d.ts +10 -0
  351. package/dist/util/top-navbar.js +5 -0
  352. package/dist/util/touch-target.d.ts +30 -0
  353. package/dist/util/touch-target.js +30 -0
  354. package/dist/util/zabi-strings.d.ts +132 -0
  355. package/dist/util/zabi-strings.js +105 -0
  356. package/dist/zabi-components-colors.css +5 -2
  357. package/dist/zabi-components-theme-dark-only.css +543 -241
  358. package/dist/zabi-components-theme-dark.css +543 -241
  359. package/dist/zabi-components-theme-only.css +1584 -367
  360. package/dist/zabi-components-theme.css +1584 -367
  361. package/dist/zabi-components.css +3885 -518
  362. package/docs/lucide-icons.md +40 -17
  363. package/docs/theme-imports.md +81 -11
  364. package/package.json +35 -8
@@ -1,7 +1,10 @@
1
1
  <script lang="ts">
2
+ import { zabiCommonStrings } from "../util/zabi-strings.js";
2
3
  import type { Snippet } from "svelte";
3
4
  import type { HTMLAttributes } from "svelte/elements";
5
+ import Drawer from "../molecules/Drawer.svelte";
4
6
  import { cn } from "../util/cn.js";
7
+ import type { DrawerCloseReason } from "../util/drawer.js";
5
8
 
6
9
  /**
7
10
  * The chrome of a sidebar and nothing else: width, surface, collapse
@@ -28,8 +31,37 @@
28
31
  * {/snippet}
29
32
  * </SidebarShell>
30
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.
31
52
  */
32
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
+
33
65
  /** Passed to every region so it can match the shell's collapse and inset. */
34
66
  export interface SidebarRegionContext {
35
67
  collapsed: boolean;
@@ -37,11 +69,34 @@
37
69
  insetX: string;
38
70
  }
39
71
 
40
- type Props = Omit<HTMLAttributes<HTMLElement>, "class"> & {
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"> & {
41
75
  mode?: "expanded" | "collapsed";
42
76
  /** `rail` attaches to the viewport edge; `card` is a floating panel. */
43
77
  layout?: "rail" | "card";
44
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]>;
45
100
  class?: string;
46
101
  /** @deprecated use `class`. */
47
102
  className?: string;
@@ -57,6 +112,13 @@
57
112
  mode = "expanded",
58
113
  layout = "rail",
59
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,
60
122
  class: classAttr = "",
61
123
  className: legacyClass = "",
62
124
  header,
@@ -65,6 +127,21 @@
65
127
  ...restProps
66
128
  }: Props = $props();
67
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
+
68
145
  const className = $derived(cn(classAttr, legacyClass));
69
146
  const isCollapsed = $derived(mode === "collapsed");
70
147
  const isCard = $derived(layout === "card");
@@ -78,35 +155,109 @@
78
155
  "border-r border-border bg-background text-headline shadow-sm";
79
156
  const surfaceClasses = isCard ? cardSurface : railSurface;
80
157
  const verticalPad = isCard ? "py-4" : "py-5";
81
- const baseClasses = `flex h-full min-h-0 max-h-full flex-col overflow-visible ${verticalPad}`;
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}`;
82
162
 
83
163
  return cn(`${baseClasses} ${widthClass} ${surfaceClasses} ${className}`);
84
164
  });
85
165
 
86
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
+ });
87
201
  </script>
88
202
 
89
- <nav class={containerClasses} aria-label={ariaLabel} {...restProps}>
90
- <div class="flex min-h-0 w-full min-w-0 flex-1 flex-col">
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")}>
91
205
  {#if header}
92
- <div class={cn("flex w-full shrink-0 flex-col gap-5 pb-3", insetX)}>
93
- {@render header(region)}
206
+ <div class={cn("flex w-full shrink-0 flex-col gap-5 pb-3", context.insetX)}>
207
+ {@render header(context)}
94
208
  </div>
95
209
  {/if}
96
210
 
211
+ <!-- In the drawer the drawer's body scrolls, so this region does not. -->
97
212
  <div
98
213
  class={cn(
99
- "flex min-h-0 min-w-0 flex-1 flex-col gap-3 overflow-y-auto overflow-x-hidden overscroll-y-contain pt-4 pb-2",
100
- insetX,
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,
101
218
  )}
102
219
  role="region"
103
- aria-label="Navigation links"
220
+ aria-label={label}
104
221
  >
105
- {@render children?.(region)}
222
+ {@render children?.(context)}
106
223
  </div>
107
224
  </div>
108
225
 
109
226
  {#if footer}
110
- {@render footer(region)}
227
+ {@render footer(context)}
111
228
  {/if}
229
+ {/snippet}
230
+
231
+ <nav class={containerClasses} aria-label={ariaLabel} {...restProps}>
232
+ {@render regions(region, false)}
112
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}
@@ -1,5 +1,6 @@
1
1
  import type { Snippet } from "svelte";
2
2
  import type { HTMLAttributes } from "svelte/elements";
3
+ import type { DrawerCloseReason } from "../util/drawer.js";
3
4
  /**
4
5
  * The chrome of a sidebar and nothing else: width, surface, collapse
5
6
  * behaviour, the inset its regions share, and a scroll container for the
@@ -25,18 +26,71 @@ import type { HTMLAttributes } from "svelte/elements";
25
26
  * {/snippet}
26
27
  * </SidebarShell>
27
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.
28
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";
29
60
  /** Passed to every region so it can match the shell's collapse and inset. */
30
61
  export interface SidebarRegionContext {
31
62
  collapsed: boolean;
32
63
  /** Horizontal padding utility the shell's own regions use. */
33
64
  insetX: string;
34
65
  }
35
- type Props = Omit<HTMLAttributes<HTMLElement>, "class"> & {
66
+ type Props = Omit<HTMLAttributes<HTMLElement>, "class" | "onclose" | "children"> & {
36
67
  mode?: "expanded" | "collapsed";
37
68
  /** `rail` attaches to the viewport edge; `card` is a floating panel. */
38
69
  layout?: "rail" | "card";
39
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]>;
40
94
  class?: string;
41
95
  /** @deprecated use `class`. */
42
96
  className?: string;
@@ -47,6 +101,6 @@ type Props = Omit<HTMLAttributes<HTMLElement>, "class"> & {
47
101
  /** Account row, logout, theme toggle. Outside the scroll container. */
48
102
  footer?: Snippet<[SidebarRegionContext]>;
49
103
  };
50
- declare const SidebarShell: import("svelte").Component<Props, {}, "">;
104
+ declare const SidebarShell: import("svelte").Component<Props, {}, "isOpen">;
51
105
  type SidebarShell = ReturnType<typeof SidebarShell>;
52
106
  export default SidebarShell;