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,540 @@
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 X from "@lucide/svelte/icons/x";
6
+ import {
7
+ nextSnap,
8
+ normaliseSnapPoints,
9
+ settleSnap,
10
+ type BottomSheetCloseReason,
11
+ type BottomSheetSnap,
12
+ } from "../util/bottom-sheet.js";
13
+ import { cn } from "../util/cn.js";
14
+ import {
15
+ OVERLAY_CHROME,
16
+ OVERLAY_TITLE_HYPHENS,
17
+ OVERLAY_TITLE_WRAP,
18
+ OVERLAY_TITLE_XL,
19
+ textIsEnlarged,
20
+ } from "../util/overlay-chrome.js";
21
+ import {
22
+ focusFirstElement,
23
+ joinOverlayStack,
24
+ recoverStrayFocus,
25
+ returnFocus,
26
+ saveFocus,
27
+ } from "../util/focus-utils.js";
28
+ import {
29
+ followKeyboard,
30
+ lockBodyScroll,
31
+ registerOverlayFooter,
32
+ registerOverlayHeader,
33
+ trapTabKey,
34
+ watchScrollerNeedsFocus,
35
+ } from "../util/overlay.js";
36
+ import { portal as portalTo } from "../util/portal.js";
37
+ import { attachSheetDrag, FLICK_VELOCITY, prefersReducedMotion } from "../util/sheet-drag.js";
38
+ import { generateId } from "../util/ssr-safe.js";
39
+
40
+ /**
41
+ * A panel that slides up from the bottom of the screen for a picker, a
42
+ * set of filters or a short form, and rests at half or full height.
43
+ *
44
+ * It is a modal dialog, on the same footing as Modal, SlideUp and Drawer:
45
+ * focus moves in and stays in, the page behind does not scroll, and
46
+ * Escape, the backdrop, the close button or a swipe down close it and
47
+ * return focus to what opened it.
48
+ *
49
+ * ```svelte
50
+ * <BottomSheet bind:isOpen title="Filters" bind:snap>
51
+ * …
52
+ * {#snippet footer()}
53
+ * <Button onclick={apply}>Show results</Button>
54
+ * {/snippet}
55
+ * </BottomSheet>
56
+ * ```
57
+ *
58
+ * The grip at the top drags the sheet between its snap points and, past
59
+ * the lowest, closed. It is also a button that moves the sheet a step, so
60
+ * the same is there for a keyboard and a screen reader. A swipe on the
61
+ * content scrolls the content; it moves the sheet only when the content
62
+ * is at its top and the swipe goes down.
63
+ *
64
+ * From the `md` breakpoint up the sheet is still attached to the bottom
65
+ * edge, centred and no wider than 40rem.
66
+ *
67
+ * Other attributes (`id`, `data-*`, ...) land on the `role="dialog"` panel.
68
+ */
69
+ type Props = Omit<
70
+ HTMLAttributes<HTMLDivElement>,
71
+ // `onclose` is also a DOM event handler; ours carries a reason instead.
72
+ "class" | "title" | "onclose" | "onkeydown"
73
+ > & {
74
+ isOpen?: boolean;
75
+ /** Heading of the sheet, and its accessible name. */
76
+ title: string;
77
+ description?: string;
78
+ /**
79
+ * The heights the sheet can rest at: `half` of the screen, `full`
80
+ * down from the status bar. With one, the grip only drags.
81
+ */
82
+ snapPoints?: BottomSheetSnap[];
83
+ /**
84
+ * The snap point the sheet is at. Bindable. It opens at the one you
85
+ * pass, or at the lowest of `snapPoints`. It is kept while the sheet
86
+ * is closed: one left at `full` opens at `full` again, bound or not.
87
+ * Set it when you open the sheet to start somewhere else.
88
+ */
89
+ snap?: BottomSheetSnap;
90
+ /**
91
+ * Render the overlay in `document.body`, so an ancestor with a
92
+ * transform, filter or clipped overflow cannot trap it; pass `false`
93
+ * to render in place. The theme class belongs on `<html>` or `<body>`;
94
+ * set lower, it does not reach a portalled sheet.
95
+ */
96
+ portal?: boolean;
97
+ /**
98
+ * When false, Escape, a backdrop click, the close button and a swipe
99
+ * down do not close the sheet. Focus stays trapped, the grip still
100
+ * moves it between snap points, and setting `isOpen` yourself still
101
+ * closes it.
102
+ */
103
+ dismissible?: boolean;
104
+ /** Fired when the sheet closes itself, with what the user did. */
105
+ onclose?: (detail: { reason: BottomSheetCloseReason }) => void;
106
+ /**
107
+ * Hears every keydown in the sheet, after the sheet has handled
108
+ * Escape. The Tab cycle is kept either way.
109
+ */
110
+ onkeydown?: (event: KeyboardEvent) => void;
111
+ /** Accessible name of the close button. */
112
+ closeLabel?: string;
113
+ /** Accessible name of the grip while its button takes the sheet up a step. */
114
+ expandLabel?: string;
115
+ /** Accessible name of the grip while its button takes the sheet down a step. */
116
+ collapseLabel?: string;
117
+ /**
118
+ * CSS selector, looked up inside the panel, of the control that takes
119
+ * focus when the sheet opens (a search field, say). Without it, or
120
+ * with no match, the first control does: the grip, or the close button.
121
+ */
122
+ initialFocus?: string;
123
+ /** Extra classes for the dialog panel. */
124
+ class?: string;
125
+ children?: Snippet;
126
+ /** Pinned to the bottom of the panel, below the scrolling content. */
127
+ footer?: Snippet;
128
+ };
129
+
130
+ let {
131
+ isOpen = $bindable<Exclude<Props["isOpen"], undefined>>(),
132
+ title,
133
+ description = "",
134
+ snapPoints,
135
+ snap = $bindable(),
136
+ portal = true,
137
+ dismissible = true,
138
+ onclose,
139
+ onkeydown,
140
+ closeLabel: closeLabelGiven,
141
+ expandLabel: expandLabelGiven,
142
+ collapseLabel: collapseLabelGiven,
143
+ initialFocus,
144
+ class: className = "",
145
+ children,
146
+ footer,
147
+ ...restProps
148
+ }: Props = $props();
149
+
150
+ /** Words many components share: a `ZabiStringsProvider` above this one may give them; else English. */
151
+ const common = zabiCommonStrings();
152
+ const closeLabel = $derived(closeLabelGiven ?? common().close);
153
+ const expandLabel = $derived(expandLabelGiven ?? common().expand);
154
+ const collapseLabel = $derived(collapseLabelGiven ?? common().collapse);
155
+
156
+ // No fallback on a bindable prop: Svelte refuses `bind:…={undefined}` on
157
+ // one that has a fallback (`props_invalid_value`), and a page that throws
158
+ // while it hydrates never becomes interactive. The default is applied
159
+ // here instead: at once, for the server and the first render, and again
160
+ // whenever a parent hands back `undefined`.
161
+ const applyDefaults = () => {
162
+ if (isOpen === undefined) isOpen = false;
163
+ };
164
+ applyDefaults();
165
+ $effect.pre(applyDefaults);
166
+
167
+ const SLIDE_MS = 200;
168
+
169
+ const titleId = generateId("bottom-sheet-title");
170
+ const descriptionId = generateId("bottom-sheet-description");
171
+
172
+ let root = $state<HTMLDivElement>();
173
+ let panel = $state<HTMLDivElement>();
174
+ let header = $state<HTMLDivElement>();
175
+ let scroller = $state<HTMLDivElement>();
176
+ let footerElement = $state<HTMLDivElement>();
177
+
178
+ /**
179
+ * Read as the overlay opens: with the text enlarged, a long word in the
180
+ * title is hyphenated before it is cut (util/overlay-chrome.ts).
181
+ */
182
+ let enlargedText = $state(false);
183
+ $effect(() => {
184
+ if (isOpen) enlargedText = textIsEnlarged();
185
+ });
186
+ let focusActive = false;
187
+ let scrollerNeedsFocus = $state(false);
188
+ /** Position among the open overlays; lifts a sheet opened later above the earlier ones. */
189
+ let depth = $state(0);
190
+
191
+ const points = $derived(normaliseSnapPoints(snapPoints));
192
+ /** The snap point in effect: the one asked for if it is in use, else the lowest. */
193
+ const currentSnap = $derived(snap && points.includes(snap) ? snap : points[0]);
194
+ const stepTo = $derived(nextSnap(points, currentSnap));
195
+
196
+ /**
197
+ * `100%` is the overlay, which is the viewport. Full height stops at the
198
+ * status bar, so the close button stays in reach; half is never so low
199
+ * that a short screen leaves no room for content, and never above full.
200
+ */
201
+ const FULL = "calc(100% - env(safe-area-inset-top, 0px))";
202
+ const snapHeight = $derived(
203
+ currentSnap === "full" ? FULL : `min(max(50%, 18rem), ${FULL})`,
204
+ );
205
+
206
+ /** While a drag is on: the height and offset it has moved the panel to, in px. */
207
+ let dragging = $state(false);
208
+ let dragHeight = $state<number | null>(null);
209
+ let dragOffset = $state(0);
210
+
211
+ function close(reason: BottomSheetCloseReason) {
212
+ if (!dismissible) return;
213
+ isOpen = false;
214
+ if (focusActive) {
215
+ focusActive = false;
216
+ returnFocus();
217
+ }
218
+ onclose?.({ reason });
219
+ }
220
+
221
+ /** Slides the panel up from the bottom edge. Skipped where motion is unwanted or unsupported. */
222
+ function slideIn(container: HTMLElement) {
223
+ if (typeof container.animate !== "function" || prefersReducedMotion()) return;
224
+ container.animate(
225
+ [{ transform: "translateY(100%)" }, { transform: "translateY(0)" }],
226
+ { duration: SLIDE_MS, easing: "ease-out" },
227
+ );
228
+ }
229
+
230
+ /** The snap points in px, lowest first, for the screen as it is now. */
231
+ function measureSnapHeights(): number[] {
232
+ if (!root) return [];
233
+ const available = root.clientHeight - parseFloat(getComputedStyle(root).paddingTop || "0");
234
+ const rem = parseFloat(getComputedStyle(document.documentElement).fontSize) || 16;
235
+ const half = Math.min(Math.max(root.clientHeight * 0.5, 18 * rem), available);
236
+ return points.map((point) => (point === "full" ? available : half));
237
+ }
238
+
239
+ function stepSnap() {
240
+ if (stepTo) snap = stepTo;
241
+ }
242
+
243
+ $effect(() => {
244
+ const box = scroller;
245
+ if (!isOpen || !box) {
246
+ scrollerNeedsFocus = false;
247
+ return;
248
+ }
249
+ return watchScrollerNeedsFocus(box, (needsFocus) => (scrollerNeedsFocus = needsFocus));
250
+ });
251
+
252
+ // Where the on-screen keyboard covers the page, the overlay keeps to what
253
+ // is left above it. The snap heights are shares of the overlay, so half
254
+ // is half of what can be seen, and the footer stays above the keyboard.
255
+ $effect(() => {
256
+ const overlay = root;
257
+ if (!isOpen || !overlay) return;
258
+ return followKeyboard(overlay);
259
+ });
260
+
261
+ // For the toast stack, which stays off a pinned footer.
262
+ $effect(() => {
263
+ const pinned = footerElement;
264
+ if (!isOpen || !pinned) return;
265
+ return registerOverlayFooter(pinned);
266
+ });
267
+
268
+ // And the header, with the close button, which the stack starts below.
269
+ $effect(() => {
270
+ const top = header;
271
+ if (!isOpen || !top) return;
272
+ return registerOverlayHeader(top);
273
+ });
274
+
275
+ $effect(() => {
276
+ const container = panel;
277
+ if (isOpen && container) {
278
+ saveFocus();
279
+ focusActive = true;
280
+ const unlockScroll = lockBodyScroll();
281
+ const overlay = joinOverlayStack(container);
282
+ depth = overlay.depth;
283
+ slideIn(container);
284
+ const t = setTimeout(() => {
285
+ // Something inside has taken focus already (a field that
286
+ // focuses itself as it mounts): leave it there. `initialFocus`
287
+ // is the consumer saying where focus goes, so it still decides.
288
+ const active = document.activeElement;
289
+ const inside = !!active && active !== container && container.contains(active);
290
+ if (inside && !(initialFocus && container.querySelector(initialFocus))) return;
291
+ focusFirstElement(container, initialFocus);
292
+ }, 0);
293
+ // If the focused control is disabled or removed, focus lands on
294
+ // `<body>`; take Tab and Escape back from there.
295
+ const stopRecovery = recoverStrayFocus(container, {
296
+ onEscape: () => close("escape"),
297
+ });
298
+ return () => {
299
+ clearTimeout(t);
300
+ stopRecovery();
301
+ overlay.leave();
302
+ unlockScroll();
303
+ dragging = false;
304
+ dragHeight = null;
305
+ dragOffset = 0;
306
+ if (focusActive) {
307
+ focusActive = false;
308
+ returnFocus();
309
+ }
310
+ };
311
+ }
312
+ });
313
+
314
+ $effect(() => {
315
+ const zone = header;
316
+ const container = panel;
317
+ if (!isOpen || !zone || !container) return;
318
+
319
+ let heights: number[] = [];
320
+ let startHeight = 0;
321
+ let startIndex = 0;
322
+
323
+ /** How much of the sheet shows after the pointer has moved `distance` down. */
324
+ const shown = (distance: number) =>
325
+ Math.min(Math.max(startHeight - distance, 0), heights[heights.length - 1]);
326
+
327
+ return attachSheetDrag(
328
+ { handleZone: zone, scroller },
329
+ {
330
+ onStart() {
331
+ heights = measureSnapHeights();
332
+ startHeight = container.getBoundingClientRect().height;
333
+ startIndex = points.indexOf(currentSnap);
334
+ dragging = true;
335
+ },
336
+ onMove(distance) {
337
+ const height = shown(distance);
338
+ const lowest = heights[0];
339
+ if (height >= lowest) {
340
+ dragHeight = height;
341
+ dragOffset = 0;
342
+ return;
343
+ }
344
+ // Below the lowest snap point the sheet keeps its size and
345
+ // slides off; one that cannot be closed only gives a little.
346
+ dragHeight = lowest;
347
+ dragOffset = (lowest - height) * (dismissible ? 1 : 0.2);
348
+ },
349
+ onEnd(distance, velocity) {
350
+ const index = settleSnap({
351
+ heights,
352
+ startIndex,
353
+ height: shown(distance),
354
+ velocity,
355
+ flick: FLICK_VELOCITY,
356
+ canClose: dismissible,
357
+ });
358
+ dragging = false;
359
+ dragHeight = null;
360
+ dragOffset = 0;
361
+ if (index === -1) close("swipe");
362
+ else snap = points[index];
363
+ },
364
+ onCancel() {
365
+ dragging = false;
366
+ dragHeight = null;
367
+ dragOffset = 0;
368
+ },
369
+ },
370
+ );
371
+ });
372
+
373
+ function handleBackdropClick(event: Event) {
374
+ if (event.target === event.currentTarget) close("backdrop");
375
+ }
376
+
377
+ function handleKeydown(event: KeyboardEvent) {
378
+ // `defaultPrevented` means a nested dialog already handled the key.
379
+ // Prevented here even when not dismissible, so a dialog underneath
380
+ // does not take the Escape as its own.
381
+ if (event.key === "Escape" && !event.defaultPrevented) {
382
+ event.preventDefault();
383
+ close("escape");
384
+ }
385
+ onkeydown?.(event);
386
+ }
387
+
388
+ /**
389
+ * 44px wide and tall in px: the target must not shrink, and need not grow
390
+ * with the text. It is flush with the top edge of the panel, so it has
391
+ * the panel's radius; that also clears the mark inside it, a 4px pill
392
+ * 10px in (2px + 10px).
393
+ */
394
+ const gripClasses =
395
+ "absolute top-0 left-1/2 flex h-[44px] w-[80px] -translate-x-1/2 cursor-grab items-start justify-center rounded-overlay pt-[10px] active:cursor-grabbing";
396
+
397
+ /**
398
+ * The focus ring of the grip button, drawn inside its box. The usual ring
399
+ * reaches 4px outside it, and the title row starts 28px down, inside the
400
+ * 44px box: outside, the ring cut across the title. Same gap and ring,
401
+ * handed to the same property `.focus-ring` uses, only inset.
402
+ */
403
+ const gripFocusClasses =
404
+ "focus-ring focus-visible:[--tw-ring-offset-shadow:inset_0_0_0_2px_var(--zabi-focus-ring-offset-color),inset_0_0_0_4px_var(--zabi-focus-ring-color)]";
405
+ </script>
406
+
407
+ <!-- The bar of the grip. Drawn in the text colour where colours are forced:
408
+ a background is dropped there, and the grip would be gone. -->
409
+ {#snippet gripMark()}
410
+ <span
411
+ class="block h-1 w-9 rounded-pill bg-current text-description forced-colors:bg-[CanvasText]"
412
+ aria-hidden="true"
413
+ ></span>
414
+ {/snippet}
415
+
416
+ {#if isOpen}
417
+ <!-- svelte-ignore a11y_click_events_have_key_events -->
418
+ <div
419
+ bind:this={root}
420
+ class="group/overlay fixed inset-0 z-modal overflow-hidden pt-[env(safe-area-inset-top)] {dismissible
421
+ ? 'cursor-pointer'
422
+ : 'cursor-default'} bg-overlay"
423
+ style:z-index={depth > 0 ? `calc(var(--z-modal) + ${depth})` : undefined}
424
+ data-overlay-depth={depth}
425
+ use:portalTo={portal}
426
+ onclick={handleBackdropClick}
427
+ onkeydown={handleKeydown}
428
+ role="presentation"
429
+ >
430
+ <div
431
+ bind:this={panel}
432
+ class={cn(
433
+ "absolute inset-x-0 bottom-0 mx-auto flex w-full cursor-default flex-col rounded-t-overlay border-t border-border-overlay bg-surface-overlay shadow-lg",
434
+ "max-h-[calc(100dvh-env(safe-area-inset-top,0px))] pb-[env(safe-area-inset-bottom)]",
435
+ // Over the keyboard: no taller than what is left, and the home
436
+ // indicator it would clear is under the keyboard.
437
+ "group-data-keyboard-open/overlay:max-h-[calc(100%_-_env(safe-area-inset-top,0px))] group-data-keyboard-open/overlay:pb-0",
438
+ // Clear of the notch and the rounded corners in landscape.
439
+ "pl-[env(safe-area-inset-left)] pr-[env(safe-area-inset-right)]",
440
+ "transition-[height,transform] duration-200 ease-out motion-reduce:transition-none",
441
+ "md:w-[min(100%,40rem)] md:border-x",
442
+ className,
443
+ )}
444
+ style:height={dragHeight !== null ? `${dragHeight}px` : snapHeight}
445
+ style:transform={dragOffset > 0 ? `translateY(${dragOffset}px)` : undefined}
446
+ style:transition={dragging ? "none" : undefined}
447
+ role="dialog"
448
+ aria-modal="true"
449
+ aria-labelledby={titleId}
450
+ aria-describedby={description ? descriptionId : undefined}
451
+ tabindex="-1"
452
+ data-snap={currentSnap}
453
+ data-dragging={dragging ? "true" : undefined}
454
+ {...restProps}
455
+ onkeydown={(event) => trapTabKey(panel, event)}
456
+ >
457
+ <!-- The whole header drags the sheet, in both directions.
458
+ `touch-none`: otherwise the browser takes a touch here for itself.
459
+ Chrome: laid out in px, with a title that stops at 1.3 times its
460
+ size (util/overlay-chrome.ts). The gutters of the content and
461
+ the footer are px with it, so the three stay in line. -->
462
+ <div bind:this={header} class="relative shrink-0 touch-none px-4 pt-7 pb-3 {OVERLAY_CHROME}">
463
+ {#if stepTo}
464
+ <button
465
+ type="button"
466
+ data-sheet-grip
467
+ class={cn(gripFocusClasses, gripClasses)}
468
+ aria-label={stepTo === "full" ? expandLabel : collapseLabel}
469
+ onclick={stepSnap}
470
+ >
471
+ {@render gripMark()}
472
+ </button>
473
+ {:else}
474
+ <div data-sheet-grip class={gripClasses}>
475
+ {@render gripMark()}
476
+ </div>
477
+ {/if}
478
+ <div class="flex items-start justify-between gap-[8px]">
479
+ <div class="min-w-0 flex-1">
480
+ <h2
481
+ id={titleId}
482
+ class="{OVERLAY_TITLE_XL} font-semibold text-headline {OVERLAY_TITLE_WRAP} {enlargedText ? OVERLAY_TITLE_HYPHENS : ''}"
483
+ >
484
+ {title}
485
+ </h2>
486
+ </div>
487
+ <!-- `aria-disabled`, not `disabled` or removed: the button may hold focus when `dismissible` turns false, and a disabled or missing element would drop focus out of the trap. -->
488
+ <!-- 8px in from the end edge of the panel: its radius
489
+ (control) plus that is the panel's (overlay). -->
490
+ <button
491
+ type="button"
492
+ onclick={() => close("close-button")}
493
+ class="focus-ring -mt-2 -me-2 flex size-11 shrink-0 items-center justify-center rounded-control text-description transition-colors motion-reduce:transition-none {dismissible
494
+ ? 'cursor-pointer hover:bg-surface-overlay-hover hover:text-headline'
495
+ : 'cursor-not-allowed opacity-50'}"
496
+ aria-label={closeLabel}
497
+ aria-disabled={dismissible ? undefined : "true"}
498
+ >
499
+ <X size={20} aria-hidden="true" />
500
+ </button>
501
+ </div>
502
+ </div>
503
+
504
+ <!-- `pt-1`: a scroll container clips, and the focus ring of a
505
+ first control needs the room. `overscroll-contain`: the page
506
+ behind must not take over when the content reaches an end. -->
507
+ <!-- svelte-ignore a11y_no_noninteractive_tabindex -->
508
+ <div
509
+ bind:this={scroller}
510
+ data-bottom-sheet-content
511
+ class={cn(
512
+ "min-h-0 flex-1 overflow-y-auto overscroll-contain px-[16px] pt-[4px] pb-[16px]",
513
+ scrollerNeedsFocus &&
514
+ "focus-visible:outline-2 focus-visible:-outline-offset-2 focus-visible:outline-focus-ring",
515
+ )}
516
+ tabindex={scrollerNeedsFocus ? 0 : undefined}
517
+ role={scrollerNeedsFocus ? "group" : undefined}
518
+ aria-labelledby={scrollerNeedsFocus ? titleId : undefined}
519
+ >
520
+ <!-- With the content, not in the header: at half height on a
521
+ small screen every fixed line is taken from the content. -->
522
+ {#if description}
523
+ <p id={descriptionId} class="mb-3 text-sm text-description">
524
+ {description}
525
+ </p>
526
+ {/if}
527
+ {@render children?.()}
528
+ </div>
529
+
530
+ {#if footer}
531
+ <div
532
+ bind:this={footerElement}
533
+ class="flex shrink-0 flex-wrap justify-end gap-[8px] border-t border-border-overlay px-[16px] py-[12px]"
534
+ >
535
+ {@render footer()}
536
+ </div>
537
+ {/if}
538
+ </div>
539
+ </div>
540
+ {/if}
@@ -0,0 +1,93 @@
1
+ import type { Snippet } from "svelte";
2
+ import type { HTMLAttributes } from "svelte/elements";
3
+ import { type BottomSheetCloseReason, type BottomSheetSnap } from "../util/bottom-sheet.js";
4
+ /**
5
+ * A panel that slides up from the bottom of the screen for a picker, a
6
+ * set of filters or a short form, and rests at half or full height.
7
+ *
8
+ * It is a modal dialog, on the same footing as Modal, SlideUp and Drawer:
9
+ * focus moves in and stays in, the page behind does not scroll, and
10
+ * Escape, the backdrop, the close button or a swipe down close it and
11
+ * return focus to what opened it.
12
+ *
13
+ * ```svelte
14
+ * <BottomSheet bind:isOpen title="Filters" bind:snap>
15
+ * …
16
+ * {#snippet footer()}
17
+ * <Button onclick={apply}>Show results</Button>
18
+ * {/snippet}
19
+ * </BottomSheet>
20
+ * ```
21
+ *
22
+ * The grip at the top drags the sheet between its snap points and, past
23
+ * the lowest, closed. It is also a button that moves the sheet a step, so
24
+ * the same is there for a keyboard and a screen reader. A swipe on the
25
+ * content scrolls the content; it moves the sheet only when the content
26
+ * is at its top and the swipe goes down.
27
+ *
28
+ * From the `md` breakpoint up the sheet is still attached to the bottom
29
+ * edge, centred and no wider than 40rem.
30
+ *
31
+ * Other attributes (`id`, `data-*`, ...) land on the `role="dialog"` panel.
32
+ */
33
+ type Props = Omit<HTMLAttributes<HTMLDivElement>, "class" | "title" | "onclose" | "onkeydown"> & {
34
+ isOpen?: boolean;
35
+ /** Heading of the sheet, and its accessible name. */
36
+ title: string;
37
+ description?: string;
38
+ /**
39
+ * The heights the sheet can rest at: `half` of the screen, `full`
40
+ * down from the status bar. With one, the grip only drags.
41
+ */
42
+ snapPoints?: BottomSheetSnap[];
43
+ /**
44
+ * The snap point the sheet is at. Bindable. It opens at the one you
45
+ * pass, or at the lowest of `snapPoints`. It is kept while the sheet
46
+ * is closed: one left at `full` opens at `full` again, bound or not.
47
+ * Set it when you open the sheet to start somewhere else.
48
+ */
49
+ snap?: BottomSheetSnap;
50
+ /**
51
+ * Render the overlay in `document.body`, so an ancestor with a
52
+ * transform, filter or clipped overflow cannot trap it; pass `false`
53
+ * to render in place. The theme class belongs on `<html>` or `<body>`;
54
+ * set lower, it does not reach a portalled sheet.
55
+ */
56
+ portal?: boolean;
57
+ /**
58
+ * When false, Escape, a backdrop click, the close button and a swipe
59
+ * down do not close the sheet. Focus stays trapped, the grip still
60
+ * moves it between snap points, and setting `isOpen` yourself still
61
+ * closes it.
62
+ */
63
+ dismissible?: boolean;
64
+ /** Fired when the sheet closes itself, with what the user did. */
65
+ onclose?: (detail: {
66
+ reason: BottomSheetCloseReason;
67
+ }) => void;
68
+ /**
69
+ * Hears every keydown in the sheet, after the sheet has handled
70
+ * Escape. The Tab cycle is kept either way.
71
+ */
72
+ onkeydown?: (event: KeyboardEvent) => void;
73
+ /** Accessible name of the close button. */
74
+ closeLabel?: string;
75
+ /** Accessible name of the grip while its button takes the sheet up a step. */
76
+ expandLabel?: string;
77
+ /** Accessible name of the grip while its button takes the sheet down a step. */
78
+ collapseLabel?: string;
79
+ /**
80
+ * CSS selector, looked up inside the panel, of the control that takes
81
+ * focus when the sheet opens (a search field, say). Without it, or
82
+ * with no match, the first control does: the grip, or the close button.
83
+ */
84
+ initialFocus?: string;
85
+ /** Extra classes for the dialog panel. */
86
+ class?: string;
87
+ children?: Snippet;
88
+ /** Pinned to the bottom of the panel, below the scrolling content. */
89
+ footer?: Snippet;
90
+ };
91
+ declare const BottomSheet: import("svelte").Component<Props, {}, "isOpen" | "snap">;
92
+ type BottomSheet = ReturnType<typeof BottomSheet>;
93
+ export default BottomSheet;