zabi-components 8.0.0 → 9.0.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 (456) hide show
  1. package/CHANGELOG.md +2935 -0
  2. package/README.md +243 -10
  3. package/THEME.md +162 -332
  4. package/THEMING.md +1419 -259
  5. package/dist/atoms/ActionPanel.svelte +15 -5
  6. package/dist/atoms/Avatar.svelte +120 -0
  7. package/dist/atoms/Avatar.svelte.d.ts +46 -0
  8. package/dist/atoms/Badge.svelte +71 -13
  9. package/dist/atoms/Badge.svelte.d.ts +8 -2
  10. package/dist/atoms/Block.svelte +74 -0
  11. package/dist/atoms/Block.svelte.d.ts +29 -0
  12. package/dist/atoms/Button.svelte +217 -30
  13. package/dist/atoms/Button.svelte.d.ts +33 -4
  14. package/dist/atoms/Card.svelte +182 -26
  15. package/dist/atoms/Card.svelte.d.ts +55 -3
  16. package/dist/atoms/CardHeader.svelte +16 -4
  17. package/dist/atoms/Checkbox.svelte +81 -21
  18. package/dist/atoms/Checkbox.svelte.d.ts +31 -2
  19. package/dist/atoms/Chip.svelte +351 -0
  20. package/dist/atoms/Chip.svelte.d.ts +57 -0
  21. package/dist/atoms/CodeBlock.svelte +20 -2
  22. package/dist/atoms/CodeBlock.svelte.d.ts +4 -0
  23. package/dist/atoms/CodeField.svelte +385 -0
  24. package/dist/atoms/CodeField.svelte.d.ts +61 -0
  25. package/dist/atoms/ColorPicker.svelte +260 -49
  26. package/dist/atoms/ColorPicker.svelte.d.ts +3 -0
  27. package/dist/atoms/Container.svelte +16 -8
  28. package/dist/atoms/DateField.svelte +145 -0
  29. package/dist/atoms/DateField.svelte.d.ts +81 -0
  30. package/dist/atoms/FieldMessages.svelte +69 -0
  31. package/dist/atoms/FieldMessages.svelte.d.ts +23 -0
  32. package/dist/atoms/FloatingActionButton.svelte +176 -0
  33. package/dist/atoms/FloatingActionButton.svelte.d.ts +51 -0
  34. package/dist/atoms/Heading.svelte +63 -5
  35. package/dist/atoms/Heading.svelte.d.ts +14 -0
  36. package/dist/atoms/IconButton.svelte +294 -26
  37. package/dist/atoms/IconButton.svelte.d.ts +62 -4
  38. package/dist/atoms/Input.svelte +259 -53
  39. package/dist/atoms/Input.svelte.d.ts +33 -2
  40. package/dist/atoms/List.svelte +5 -1
  41. package/dist/atoms/ListItem.svelte +70 -13
  42. package/dist/atoms/ListItem.svelte.d.ts +11 -0
  43. package/dist/atoms/PickerField.svelte +239 -0
  44. package/dist/atoms/PickerField.svelte.d.ts +67 -0
  45. package/dist/atoms/Progress.svelte +99 -19
  46. package/dist/atoms/Progress.svelte.d.ts +15 -1
  47. package/dist/atoms/Radio.svelte +51 -3
  48. package/dist/atoms/Radio.svelte.d.ts +30 -2
  49. package/dist/atoms/Rating.svelte +620 -0
  50. package/dist/atoms/Rating.svelte.d.ts +77 -0
  51. package/dist/atoms/Select.svelte +438 -98
  52. package/dist/atoms/Select.svelte.d.ts +61 -3
  53. package/dist/atoms/SelectionControl.svelte +193 -23
  54. package/dist/atoms/SelectionControl.svelte.d.ts +29 -3
  55. package/dist/atoms/Skeleton.svelte +114 -18
  56. package/dist/atoms/Skeleton.svelte.d.ts +30 -3
  57. package/dist/atoms/SkeletonGroup.svelte +43 -0
  58. package/dist/atoms/SkeletonGroup.svelte.d.ts +20 -0
  59. package/dist/atoms/Slider.svelte +344 -0
  60. package/dist/atoms/Slider.svelte.d.ts +40 -0
  61. package/dist/atoms/Sparkline.svelte +109 -0
  62. package/dist/atoms/Sparkline.svelte.d.ts +27 -0
  63. package/dist/atoms/SpeechBubble.svelte +187 -0
  64. package/dist/atoms/SpeechBubble.svelte.d.ts +33 -0
  65. package/dist/atoms/Spinner.svelte +57 -0
  66. package/dist/atoms/Spinner.svelte.d.ts +19 -0
  67. package/dist/atoms/Stat.svelte +72 -0
  68. package/dist/atoms/Stat.svelte.d.ts +19 -0
  69. package/dist/atoms/Table.svelte +151 -6
  70. package/dist/atoms/Table.svelte.d.ts +15 -3
  71. package/dist/atoms/TemporalField.svelte +472 -0
  72. package/dist/atoms/TemporalField.svelte.d.ts +48 -0
  73. package/dist/atoms/Text.svelte +23 -11
  74. package/dist/atoms/Text.svelte.d.ts +6 -3
  75. package/dist/atoms/Textarea.svelte +150 -44
  76. package/dist/atoms/Textarea.svelte.d.ts +35 -2
  77. package/dist/atoms/ThemeToggle.svelte +223 -44
  78. package/dist/atoms/ThemeToggle.svelte.d.ts +26 -3
  79. package/dist/atoms/TimeColumn.svelte +150 -0
  80. package/dist/atoms/TimeColumn.svelte.d.ts +25 -0
  81. package/dist/atoms/TimeField.svelte +151 -0
  82. package/dist/atoms/TimeField.svelte.d.ts +85 -0
  83. package/dist/atoms/Toast.svelte +22 -8
  84. package/dist/atoms/Toast.svelte.d.ts +2 -0
  85. package/dist/atoms/Toggle.svelte +98 -14
  86. package/dist/atoms/Toggle.svelte.d.ts +26 -2
  87. package/dist/atoms/Tooltip.svelte +528 -24
  88. package/dist/atoms/Tooltip.svelte.d.ts +18 -0
  89. package/dist/atoms/ZabiStringsProvider.svelte +45 -0
  90. package/dist/atoms/ZabiStringsProvider.svelte.d.ts +27 -0
  91. package/dist/atoms/index.d.ts +38 -0
  92. package/dist/atoms/index.js +29 -0
  93. package/dist/atoms/selection-control.styles.d.ts +47 -5
  94. package/dist/atoms/selection-control.styles.js +52 -10
  95. package/dist/components/atoms/index.d.ts +38 -0
  96. package/dist/components/atoms/index.d.ts.map +1 -1
  97. package/dist/components/atoms/selection-control.styles.d.ts +47 -5
  98. package/dist/components/atoms/selection-control.styles.d.ts.map +1 -1
  99. package/dist/components/index.d.ts +104 -2
  100. package/dist/components/index.d.ts.map +1 -1
  101. package/dist/components/map.d.ts +11 -0
  102. package/dist/components/map.d.ts.map +1 -0
  103. package/dist/components/molecules/index.d.ts +51 -0
  104. package/dist/components/molecules/index.d.ts.map +1 -1
  105. package/dist/components/molecules/toast-store.d.ts +83 -7
  106. package/dist/components/molecules/toast-store.d.ts.map +1 -1
  107. package/dist/components/organisms/index.d.ts +10 -0
  108. package/dist/components/organisms/index.d.ts.map +1 -1
  109. package/dist/components/types/variants.d.ts +24 -1
  110. package/dist/components/types/variants.d.ts.map +1 -1
  111. package/dist/components/util/app-shell.d.ts +79 -0
  112. package/dist/components/util/app-shell.d.ts.map +1 -0
  113. package/dist/components/util/avatar.d.ts +39 -0
  114. package/dist/components/util/avatar.d.ts.map +1 -0
  115. package/dist/components/util/bottom-sheet.d.ts +30 -0
  116. package/dist/components/util/bottom-sheet.d.ts.map +1 -0
  117. package/dist/components/util/bottom-tab-bar.d.ts +41 -0
  118. package/dist/components/util/bottom-tab-bar.d.ts.map +1 -0
  119. package/dist/components/util/calendar.d.ts +56 -0
  120. package/dist/components/util/calendar.d.ts.map +1 -0
  121. package/dist/components/util/chart.d.ts +140 -0
  122. package/dist/components/util/chart.d.ts.map +1 -0
  123. package/dist/components/util/chip.d.ts +66 -0
  124. package/dist/components/util/chip.d.ts.map +1 -0
  125. package/dist/components/util/cn.d.ts.map +1 -1
  126. package/dist/components/util/code-field.d.ts +63 -0
  127. package/dist/components/util/code-field.d.ts.map +1 -0
  128. package/dist/components/util/collapsible.d.ts +64 -0
  129. package/dist/components/util/collapsible.d.ts.map +1 -0
  130. package/dist/components/util/confirm-dialog.d.ts +12 -0
  131. package/dist/components/util/confirm-dialog.d.ts.map +1 -0
  132. package/dist/components/util/date.d.ts +96 -0
  133. package/dist/components/util/date.d.ts.map +1 -0
  134. package/dist/components/util/display-voice.d.ts +10 -0
  135. package/dist/components/util/display-voice.d.ts.map +1 -0
  136. package/dist/components/util/drawer.d.ts +13 -0
  137. package/dist/components/util/drawer.d.ts.map +1 -0
  138. package/dist/components/util/dropdown.d.ts +47 -0
  139. package/dist/components/util/dropdown.d.ts.map +1 -0
  140. package/dist/components/util/field-control.d.ts +30 -0
  141. package/dist/components/util/field-control.d.ts.map +1 -0
  142. package/dist/components/util/field.d.ts +45 -0
  143. package/dist/components/util/field.d.ts.map +1 -0
  144. package/dist/components/util/fit-in-viewport.d.ts +201 -0
  145. package/dist/components/util/fit-in-viewport.d.ts.map +1 -0
  146. package/dist/components/util/focus-utils.d.ts +77 -2
  147. package/dist/components/util/focus-utils.d.ts.map +1 -1
  148. package/dist/components/util/hydration.d.ts +45 -0
  149. package/dist/components/util/hydration.d.ts.map +1 -0
  150. package/dist/components/util/keyboard-inset.d.ts +18 -0
  151. package/dist/components/util/keyboard-inset.d.ts.map +1 -0
  152. package/dist/components/util/map-view.d.ts +144 -0
  153. package/dist/components/util/map-view.d.ts.map +1 -0
  154. package/dist/components/util/media-grid.d.ts +51 -0
  155. package/dist/components/util/media-grid.d.ts.map +1 -0
  156. package/dist/components/util/media.d.ts +6 -0
  157. package/dist/components/util/media.d.ts.map +1 -0
  158. package/dist/components/util/overlay-chrome.d.ts +53 -0
  159. package/dist/components/util/overlay-chrome.d.ts.map +1 -0
  160. package/dist/components/util/overlay.d.ts +72 -0
  161. package/dist/components/util/overlay.d.ts.map +1 -0
  162. package/dist/components/util/photo.d.ts +234 -0
  163. package/dist/components/util/photo.d.ts.map +1 -0
  164. package/dist/components/util/portal.d.ts +23 -0
  165. package/dist/components/util/portal.d.ts.map +1 -0
  166. package/dist/components/util/progress.d.ts +19 -0
  167. package/dist/components/util/progress.d.ts.map +1 -0
  168. package/dist/components/util/pull-to-refresh.d.ts +27 -0
  169. package/dist/components/util/pull-to-refresh.d.ts.map +1 -0
  170. package/dist/components/util/radio-keys.d.ts +25 -0
  171. package/dist/components/util/radio-keys.d.ts.map +1 -0
  172. package/dist/components/util/rating.d.ts +23 -0
  173. package/dist/components/util/rating.d.ts.map +1 -0
  174. package/dist/components/util/ready-made-strings.d.ts +77 -0
  175. package/dist/components/util/ready-made-strings.d.ts.map +1 -0
  176. package/dist/components/util/scroll-edge.d.ts +33 -0
  177. package/dist/components/util/scroll-edge.d.ts.map +1 -0
  178. package/dist/components/util/scroll-padding.d.ts +20 -0
  179. package/dist/components/util/scroll-padding.d.ts.map +1 -0
  180. package/dist/components/util/segmented-control.d.ts +16 -0
  181. package/dist/components/util/segmented-control.d.ts.map +1 -0
  182. package/dist/components/util/select.d.ts +41 -0
  183. package/dist/components/util/select.d.ts.map +1 -0
  184. package/dist/components/util/sheet-drag.d.ts +84 -0
  185. package/dist/components/util/sheet-drag.d.ts.map +1 -0
  186. package/dist/components/util/sidebar.d.ts +63 -0
  187. package/dist/components/util/sidebar.d.ts.map +1 -0
  188. package/dist/components/util/skeleton.d.ts +15 -0
  189. package/dist/components/util/skeleton.d.ts.map +1 -0
  190. package/dist/components/util/sliding-indicator.d.ts +25 -0
  191. package/dist/components/util/sliding-indicator.d.ts.map +1 -0
  192. package/dist/components/util/sortable-list.d.ts +65 -0
  193. package/dist/components/util/sortable-list.d.ts.map +1 -0
  194. package/dist/components/util/stepper.d.ts +40 -0
  195. package/dist/components/util/stepper.d.ts.map +1 -0
  196. package/dist/components/util/swipeable-list-item.d.ts +36 -0
  197. package/dist/components/util/swipeable-list-item.d.ts.map +1 -0
  198. package/dist/components/util/temporal-field.d.ts +89 -0
  199. package/dist/components/util/temporal-field.d.ts.map +1 -0
  200. package/dist/components/util/theme-mode.d.ts +80 -0
  201. package/dist/components/util/theme-mode.d.ts.map +1 -0
  202. package/dist/components/util/toaster.d.ts +133 -0
  203. package/dist/components/util/toaster.d.ts.map +1 -0
  204. package/dist/components/util/tone-surface.d.ts +34 -0
  205. package/dist/components/util/tone-surface.d.ts.map +1 -0
  206. package/dist/components/util/tooltip.d.ts +10 -0
  207. package/dist/components/util/tooltip.d.ts.map +1 -0
  208. package/dist/components/util/top-navbar.d.ts +35 -0
  209. package/dist/components/util/top-navbar.d.ts.map +1 -0
  210. package/dist/components/util/touch-target.d.ts +31 -0
  211. package/dist/components/util/touch-target.d.ts.map +1 -0
  212. package/dist/components/util/zabi-strings.d.ts +142 -0
  213. package/dist/components/util/zabi-strings.d.ts.map +1 -0
  214. package/dist/create-theme/cli.js +173 -0
  215. package/dist/create-theme/index.d.ts +190 -0
  216. package/dist/create-theme/index.js +1034 -0
  217. package/dist/create-theme/lib/ramp-math.js +128 -0
  218. package/dist/create-theme/lib/resolve.js +238 -0
  219. package/dist/create-theme/theme-data.js +2644 -0
  220. package/dist/index.d.ts +104 -2
  221. package/dist/index.js +75 -2
  222. package/dist/map.d.ts +10 -0
  223. package/dist/map.js +9 -0
  224. package/dist/molecules/Alert.svelte +63 -16
  225. package/dist/molecules/Alert.svelte.d.ts +14 -1
  226. package/dist/molecules/AppBar.svelte +674 -0
  227. package/dist/molecules/AppBar.svelte.d.ts +131 -0
  228. package/dist/molecules/AvatarGroup.svelte +116 -0
  229. package/dist/molecules/AvatarGroup.svelte.d.ts +40 -0
  230. package/dist/molecules/BarChart.svelte +381 -0
  231. package/dist/molecules/BarChart.svelte.d.ts +45 -0
  232. package/dist/molecules/BottomSheet.svelte +608 -0
  233. package/dist/molecules/BottomSheet.svelte.d.ts +109 -0
  234. package/dist/molecules/BottomTabBar.svelte +403 -0
  235. package/dist/molecules/BottomTabBar.svelte.d.ts +98 -0
  236. package/dist/molecules/Calendar.svelte +597 -0
  237. package/dist/molecules/Calendar.svelte.d.ts +72 -0
  238. package/dist/molecules/ChipGroup.svelte +325 -0
  239. package/dist/molecules/ChipGroup.svelte.d.ts +41 -0
  240. package/dist/molecules/Collapsible.svelte +347 -0
  241. package/dist/molecules/Collapsible.svelte.d.ts +61 -0
  242. package/dist/molecules/CollapsibleGroup.svelte +136 -0
  243. package/dist/molecules/CollapsibleGroup.svelte.d.ts +14 -0
  244. package/dist/molecules/ComponentDemo.svelte +16 -4
  245. package/dist/molecules/ComponentDemo.svelte.d.ts +3 -0
  246. package/dist/molecules/ConfirmDialog.svelte +257 -0
  247. package/dist/molecules/ConfirmDialog.svelte.d.ts +58 -0
  248. package/dist/molecules/ContactForm.svelte +34 -17
  249. package/dist/molecules/ContactForm.svelte.d.ts +8 -0
  250. package/dist/molecules/Drawer.svelte +415 -0
  251. package/dist/molecules/Drawer.svelte.d.ts +55 -0
  252. package/dist/molecules/Dropdown.svelte +517 -48
  253. package/dist/molecules/Dropdown.svelte.d.ts +47 -6
  254. package/dist/molecules/DropdownItem.svelte +122 -0
  255. package/dist/molecules/DropdownItem.svelte.d.ts +28 -0
  256. package/dist/molecules/EmptyState.svelte +55 -10
  257. package/dist/molecules/EmptyState.svelte.d.ts +8 -0
  258. package/dist/molecules/FormField.svelte +9 -1
  259. package/dist/molecules/FormField.svelte.d.ts +2 -0
  260. package/dist/molecules/Header.svelte +1 -1
  261. package/dist/molecules/ImageUpload.svelte +492 -69
  262. package/dist/molecules/ImageUpload.svelte.d.ts +82 -2
  263. package/dist/molecules/LineChart.svelte +459 -0
  264. package/dist/molecules/LineChart.svelte.d.ts +53 -0
  265. package/dist/molecules/MapView.svelte +565 -0
  266. package/dist/molecules/MapView.svelte.d.ts +88 -0
  267. package/dist/molecules/MediaGrid.svelte +527 -0
  268. package/dist/molecules/MediaGrid.svelte.d.ts +77 -0
  269. package/dist/molecules/Modal.svelte +293 -72
  270. package/dist/molecules/Modal.svelte.d.ts +51 -2
  271. package/dist/molecules/NavigationMenuContent.svelte +58 -1
  272. package/dist/molecules/NavigationMenuItem.svelte +0 -1
  273. package/dist/molecules/NavigationMenuLink.svelte +1 -1
  274. package/dist/molecules/NavigationMenuList.svelte +10 -3
  275. package/dist/molecules/NavigationMenuTrigger.svelte +2 -2
  276. package/dist/molecules/Page.svelte +32 -2
  277. package/dist/molecules/Page.svelte.d.ts +11 -0
  278. package/dist/molecules/PhotoGrid.svelte +523 -0
  279. package/dist/molecules/PhotoGrid.svelte.d.ts +73 -0
  280. package/dist/molecules/PhotoViewer.svelte +1213 -0
  281. package/dist/molecules/PhotoViewer.svelte.d.ts +74 -0
  282. package/dist/molecules/PieChart.svelte +421 -0
  283. package/dist/molecules/PieChart.svelte.d.ts +51 -0
  284. package/dist/molecules/PropsTable.svelte +22 -10
  285. package/dist/molecules/PropsTable.svelte.d.ts +3 -0
  286. package/dist/molecules/PullToRefresh.svelte +263 -0
  287. package/dist/molecules/PullToRefresh.svelte.d.ts +52 -0
  288. package/dist/molecules/RadioGroup.svelte +72 -18
  289. package/dist/molecules/RadioGroup.svelte.d.ts +19 -3
  290. package/dist/molecules/Section.svelte +1 -1
  291. package/dist/molecules/SegmentedControl.svelte +456 -0
  292. package/dist/molecules/SegmentedControl.svelte.d.ts +40 -0
  293. package/dist/molecules/SidebarBrandHeader.svelte +11 -1
  294. package/dist/molecules/SidebarBrandHeader.svelte.d.ts +3 -0
  295. package/dist/molecules/SidebarFooter.svelte +30 -7
  296. package/dist/molecules/SidebarFooter.svelte.d.ts +3 -0
  297. package/dist/molecules/SidebarNavSection.svelte +1 -1
  298. package/dist/molecules/SlideUp.svelte +220 -61
  299. package/dist/molecules/SlideUp.svelte.d.ts +26 -0
  300. package/dist/molecules/SortableList.svelte +633 -0
  301. package/dist/molecules/SortableList.svelte.d.ts +69 -0
  302. package/dist/molecules/Stepper.svelte +562 -0
  303. package/dist/molecules/Stepper.svelte.d.ts +39 -0
  304. package/dist/molecules/StickyActionBar.svelte +250 -0
  305. package/dist/molecules/StickyActionBar.svelte.d.ts +78 -0
  306. package/dist/molecules/SwipeableListItem.svelte +334 -0
  307. package/dist/molecules/SwipeableListItem.svelte.d.ts +54 -0
  308. package/dist/molecules/Tabs.svelte +310 -9
  309. package/dist/molecules/Tabs.svelte.d.ts +7 -0
  310. package/dist/molecules/Toaster.svelte +514 -5
  311. package/dist/molecules/Toaster.svelte.d.ts +153 -2
  312. package/dist/molecules/ToasterToast.svelte +495 -77
  313. package/dist/molecules/ToasterToast.svelte.d.ts +15 -0
  314. package/dist/molecules/UnsavedChangesBar.svelte +223 -0
  315. package/dist/molecules/UnsavedChangesBar.svelte.d.ts +43 -0
  316. package/dist/molecules/index.d.ts +51 -0
  317. package/dist/molecules/index.js +31 -0
  318. package/dist/molecules/toast-store.d.ts +83 -7
  319. package/dist/molecules/toast-store.js +25 -1
  320. package/dist/organisms/AppNavigation.svelte +440 -0
  321. package/dist/organisms/AppNavigation.svelte.d.ts +72 -0
  322. package/dist/organisms/AppShell.svelte +696 -0
  323. package/dist/organisms/AppShell.svelte.d.ts +157 -0
  324. package/dist/organisms/AppShellHeaderPart.svelte +163 -0
  325. package/dist/organisms/AppShellHeaderPart.svelte.d.ts +57 -0
  326. package/dist/organisms/SidebarAccountPanel.svelte +107 -12
  327. package/dist/organisms/SidebarAccountPanel.svelte.d.ts +19 -0
  328. package/dist/organisms/SidebarNavigation.svelte +111 -43
  329. package/dist/organisms/SidebarNavigation.svelte.d.ts +29 -1
  330. package/dist/organisms/SidebarPanel.svelte +34 -17
  331. package/dist/organisms/SidebarPanel.svelte.d.ts +1 -0
  332. package/dist/organisms/SidebarShell.svelte +164 -14
  333. package/dist/organisms/SidebarShell.svelte.d.ts +56 -2
  334. package/dist/organisms/TopNavbar.svelte +353 -39
  335. package/dist/organisms/TopNavbar.svelte.d.ts +47 -0
  336. package/dist/organisms/TopNavbarSheetContent.svelte +145 -0
  337. package/dist/organisms/TopNavbarSheetContent.svelte.d.ts +24 -0
  338. package/dist/organisms/index.d.ts +10 -0
  339. package/dist/organisms/index.js +7 -0
  340. package/dist/types/components.d.ts +21 -1
  341. package/dist/types/components.d.ts.map +1 -1
  342. package/dist/types/index.d.ts +1093 -26
  343. package/dist/types/index.d.ts.map +1 -1
  344. package/dist/types/variants.ts +28 -1
  345. package/dist/util/app-shell.d.ts +78 -0
  346. package/dist/util/app-shell.js +97 -0
  347. package/dist/util/avatar.d.ts +38 -0
  348. package/dist/util/avatar.js +52 -0
  349. package/dist/util/bottom-sheet.d.ts +29 -0
  350. package/dist/util/bottom-sheet.js +43 -0
  351. package/dist/util/bottom-tab-bar.d.ts +40 -0
  352. package/dist/util/bottom-tab-bar.js +54 -0
  353. package/dist/util/calendar.d.ts +55 -0
  354. package/dist/util/calendar.js +74 -0
  355. package/dist/util/chart.d.ts +139 -0
  356. package/dist/util/chart.js +305 -0
  357. package/dist/util/chip.d.ts +65 -0
  358. package/dist/util/chip.js +58 -0
  359. package/dist/util/cn.js +28 -2
  360. package/dist/util/code-field.d.ts +62 -0
  361. package/dist/util/code-field.js +112 -0
  362. package/dist/util/collapsible.d.ts +63 -0
  363. package/dist/util/collapsible.js +23 -0
  364. package/dist/util/confirm-dialog.d.ts +11 -0
  365. package/dist/util/confirm-dialog.js +2 -0
  366. package/dist/util/date.d.ts +95 -0
  367. package/dist/util/date.js +237 -0
  368. package/dist/util/display-voice.d.ts +9 -0
  369. package/dist/util/display-voice.js +9 -0
  370. package/dist/util/drawer.d.ts +12 -0
  371. package/dist/util/drawer.js +7 -0
  372. package/dist/util/dropdown.d.ts +46 -0
  373. package/dist/util/dropdown.js +21 -0
  374. package/dist/util/field-control.d.ts +29 -0
  375. package/dist/util/field-control.js +32 -0
  376. package/dist/util/field.d.ts +44 -0
  377. package/dist/util/field.js +38 -0
  378. package/dist/util/fit-in-viewport.d.ts +200 -0
  379. package/dist/util/fit-in-viewport.js +399 -0
  380. package/dist/util/focus-utils.d.ts +77 -2
  381. package/dist/util/focus-utils.js +186 -9
  382. package/dist/util/hydration.d.ts +44 -0
  383. package/dist/util/hydration.js +58 -0
  384. package/dist/util/keyboard-inset.d.ts +17 -0
  385. package/dist/util/keyboard-inset.js +53 -0
  386. package/dist/util/map-view.d.ts +143 -0
  387. package/dist/util/map-view.js +121 -0
  388. package/dist/util/media-grid.d.ts +50 -0
  389. package/dist/util/media-grid.js +81 -0
  390. package/dist/util/media.d.ts +5 -0
  391. package/dist/util/media.js +10 -0
  392. package/dist/util/overlay-chrome.d.ts +52 -0
  393. package/dist/util/overlay-chrome.js +56 -0
  394. package/dist/util/overlay.d.ts +71 -0
  395. package/dist/util/overlay.js +240 -0
  396. package/dist/util/photo.d.ts +233 -0
  397. package/dist/util/photo.js +209 -0
  398. package/dist/util/portal.d.ts +22 -0
  399. package/dist/util/portal.js +50 -0
  400. package/dist/util/progress.d.ts +18 -0
  401. package/dist/util/progress.js +17 -0
  402. package/dist/util/pull-to-refresh.d.ts +26 -0
  403. package/dist/util/pull-to-refresh.js +30 -0
  404. package/dist/util/radio-keys.d.ts +24 -0
  405. package/dist/util/radio-keys.js +49 -0
  406. package/dist/util/rating.d.ts +22 -0
  407. package/dist/util/rating.js +26 -0
  408. package/dist/util/ready-made-strings.d.ts +76 -0
  409. package/dist/util/ready-made-strings.js +67 -0
  410. package/dist/util/scroll-edge.d.ts +32 -0
  411. package/dist/util/scroll-edge.js +87 -0
  412. package/dist/util/scroll-padding.d.ts +19 -0
  413. package/dist/util/scroll-padding.js +44 -0
  414. package/dist/util/segmented-control.d.ts +15 -0
  415. package/dist/util/segmented-control.js +1 -0
  416. package/dist/util/select.d.ts +40 -0
  417. package/dist/util/select.js +63 -0
  418. package/dist/util/sheet-drag.d.ts +83 -0
  419. package/dist/util/sheet-drag.js +311 -0
  420. package/dist/util/sidebar.d.ts +62 -0
  421. package/dist/util/sidebar.js +38 -0
  422. package/dist/util/skeleton.d.ts +14 -0
  423. package/dist/util/skeleton.js +22 -0
  424. package/dist/util/sliding-indicator.d.ts +24 -0
  425. package/dist/util/sliding-indicator.js +44 -0
  426. package/dist/util/sortable-list.d.ts +64 -0
  427. package/dist/util/sortable-list.js +53 -0
  428. package/dist/util/stepper.d.ts +39 -0
  429. package/dist/util/stepper.js +38 -0
  430. package/dist/util/swipeable-list-item.d.ts +35 -0
  431. package/dist/util/swipeable-list-item.js +32 -0
  432. package/dist/util/temporal-field.d.ts +88 -0
  433. package/dist/util/temporal-field.js +136 -0
  434. package/dist/util/theme-mode.d.ts +79 -0
  435. package/dist/util/theme-mode.js +121 -0
  436. package/dist/util/toaster.d.ts +132 -0
  437. package/dist/util/toaster.js +130 -0
  438. package/dist/util/tone-surface.d.ts +33 -0
  439. package/dist/util/tone-surface.js +35 -0
  440. package/dist/util/tooltip.d.ts +9 -0
  441. package/dist/util/tooltip.js +18 -0
  442. package/dist/util/top-navbar.d.ts +34 -0
  443. package/dist/util/top-navbar.js +28 -0
  444. package/dist/util/touch-target.d.ts +30 -0
  445. package/dist/util/touch-target.js +30 -0
  446. package/dist/util/zabi-strings.d.ts +141 -0
  447. package/dist/util/zabi-strings.js +105 -0
  448. package/dist/zabi-components-colors.css +11 -2
  449. package/dist/zabi-components-theme-dark-only.css +870 -258
  450. package/dist/zabi-components-theme-dark.css +870 -258
  451. package/dist/zabi-components-theme-only.css +2419 -368
  452. package/dist/zabi-components-theme.css +2419 -368
  453. package/dist/zabi-components.css +6032 -711
  454. package/docs/lucide-icons.md +40 -17
  455. package/docs/theme-imports.md +81 -11
  456. package/package.json +45 -8
@@ -0,0 +1,45 @@
1
+ import type { HTMLAttributes } from "svelte/elements";
2
+ import { type ChartColor, type ChartDatum, type ChartFormat } from "../util/chart.js";
3
+ /**
4
+ * A bar chart of one set of values, drawn as plain SVG: upright bars from
5
+ * a zero line, a label under each and its value over it. A bar is drawn
6
+ * the way `Progress` draws its fill, and with a `yMax` each bar stands in
7
+ * a track up to it, as a `Progress` does: so many of so many.
8
+ *
9
+ * It is a `figure`: the `label` is its caption, the drawing is hidden
10
+ * from assistive technology, and the same numbers are in a real table
11
+ * (off screen unless `showTable`). The bars are one colour, since the
12
+ * labels tell them apart; a datum may carry its own.
13
+ */
14
+ type Props = Omit<HTMLAttributes<HTMLElement>, "class"> & {
15
+ /** The bars, in order: a `label`, a `value`, and a `color` of its own if it needs one. */
16
+ data: ChartDatum[];
17
+ /** The colour of the bars: a theme colour by name or any CSS colour. Without it, the first series colour. */
18
+ color?: ChartColor;
19
+ /** The top of the value axis, and the ceiling each bar is measured against: with it every bar stands in a track that reaches it. Without it, the highest value rounded up to a tick, and no tracks. */
20
+ yMax?: number;
21
+ /** How a value is written: `number` (default), `percent` (72 is "72 %"), or a function. */
22
+ yFormat?: ChartFormat;
23
+ /** Height of the drawing in px. The width is the container's. */
24
+ height?: number;
25
+ /** The caption, and what the chart is called. */
26
+ label: string;
27
+ /** Keeps the caption for assistive technology and takes it off the screen. */
28
+ labelHidden?: boolean;
29
+ /** Writes each value over its bar. */
30
+ showValues?: boolean;
31
+ /** Shows the data table under the chart; otherwise it is there for assistive technology only. */
32
+ showTable?: boolean;
33
+ /** The heading of the table's first column. */
34
+ categoryTitle?: string;
35
+ /** The heading of the table's value column. */
36
+ valueTitle?: string;
37
+ /** The language of the built-in number formats. Without it, the runtime's. */
38
+ locale?: string;
39
+ /** Stands the chart on a panel of the thick material (frosted glass), for a chart that floats over a wash, a tint or a picture. On a plain card it shows nothing: leave it off there. */
40
+ glass?: boolean;
41
+ class?: string;
42
+ };
43
+ declare const BarChart: import("svelte").Component<Props, {}, "">;
44
+ type BarChart = ReturnType<typeof BarChart>;
45
+ export default BarChart;
@@ -0,0 +1,608 @@
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. A box inside the content that
63
+ * scrolls by itself (a long list, a text area) keeps every swipe that
64
+ * starts on it, at its top too, and so does anything carrying
65
+ * `data-sheet-no-drag` (a map, a wheel, a swipeable row) or a range input.
66
+ *
67
+ * From the `md` breakpoint up the sheet is still attached to the bottom
68
+ * edge, centred and no wider than 40rem.
69
+ *
70
+ * Other attributes (`id`, `data-*`, ...) land on the `role="dialog"` panel.
71
+ */
72
+ type Props = Omit<
73
+ HTMLAttributes<HTMLDivElement>,
74
+ // `onclose` is also a DOM event handler; ours carries a reason instead.
75
+ "class" | "title" | "onclose" | "onkeydown"
76
+ > & {
77
+ isOpen?: boolean;
78
+ /** Heading of the sheet, and its accessible name. */
79
+ title: string;
80
+ description?: string;
81
+ /**
82
+ * The heights the sheet can rest at: `half` of the screen, `full`
83
+ * down from the status bar. With one, the grip only drags.
84
+ */
85
+ snapPoints?: BottomSheetSnap[];
86
+ /**
87
+ * The snap point the sheet is at. Bindable. It opens at the one you
88
+ * pass, or at the lowest of `snapPoints`. It is kept while the sheet
89
+ * is closed: one left at `full` opens at `full` again, bound or not.
90
+ * Set it when you open the sheet to start somewhere else.
91
+ */
92
+ snap?: BottomSheetSnap;
93
+ /**
94
+ * The sheet is as tall as its content, up to half the screen, instead
95
+ * of half the screen whatever it holds. It has one resting height, so
96
+ * `snapPoints` and `snap` are ignored and the grip only drags. Off by
97
+ * default.
98
+ */
99
+ fit?: boolean;
100
+ /**
101
+ * With `fit`: the tallest the sheet grows before its content scrolls.
102
+ * `half` (the default) is half the screen; `full` is down from the
103
+ * status bar, like the `full` snap point. Ignored without `fit`.
104
+ */
105
+ fitMax?: "half" | "full";
106
+ /**
107
+ * Render the overlay in `document.body`, so an ancestor with a
108
+ * transform, filter or clipped overflow cannot trap it; pass `false`
109
+ * to render in place. The theme class belongs on `<html>` or `<body>`;
110
+ * set lower, it does not reach a portalled sheet.
111
+ */
112
+ portal?: boolean;
113
+ /**
114
+ * When false, Escape, a backdrop click, the close button and a swipe
115
+ * down do not close the sheet. Focus stays trapped, the grip still
116
+ * moves it between snap points, and setting `isOpen` yourself still
117
+ * closes it.
118
+ */
119
+ dismissible?: boolean;
120
+ /** Fired when the sheet closes itself, with what the user did. */
121
+ onclose?: (detail: { reason: BottomSheetCloseReason }) => void;
122
+ /**
123
+ * Hears every keydown in the sheet, after the sheet has handled
124
+ * Escape. The Tab cycle is kept either way.
125
+ */
126
+ onkeydown?: (event: KeyboardEvent) => void;
127
+ /** Accessible name of the close button. */
128
+ closeLabel?: string;
129
+ /** Accessible name of the grip while its button takes the sheet up a step. */
130
+ expandLabel?: string;
131
+ /** Accessible name of the grip while its button takes the sheet down a step. */
132
+ collapseLabel?: string;
133
+ /**
134
+ * CSS selector, looked up inside the panel, of the control that takes
135
+ * focus when the sheet opens (a search field, say). Without it, or
136
+ * with no match, the first control does: the grip, or the close button.
137
+ */
138
+ initialFocus?: string;
139
+ /** Extra classes for the dialog panel. */
140
+ class?: string;
141
+ children?: Snippet;
142
+ /** Pinned to the bottom of the panel, below the scrolling content. */
143
+ footer?: Snippet;
144
+ };
145
+
146
+ let {
147
+ isOpen = $bindable<Exclude<Props["isOpen"], undefined>>(),
148
+ title,
149
+ description = "",
150
+ snapPoints,
151
+ snap = $bindable(),
152
+ fit = false,
153
+ fitMax = "half",
154
+ portal = true,
155
+ dismissible = true,
156
+ onclose,
157
+ onkeydown,
158
+ closeLabel: closeLabelGiven,
159
+ expandLabel: expandLabelGiven,
160
+ collapseLabel: collapseLabelGiven,
161
+ initialFocus,
162
+ class: className = "",
163
+ children,
164
+ footer,
165
+ ...restProps
166
+ }: Props = $props();
167
+
168
+ /** Words many components share: a `ZabiStringsProvider` above this one may give them; else English. */
169
+ const common = zabiCommonStrings();
170
+ const closeLabel = $derived(closeLabelGiven ?? common().close);
171
+ const expandLabel = $derived(expandLabelGiven ?? common().expand);
172
+ const collapseLabel = $derived(collapseLabelGiven ?? common().collapse);
173
+
174
+ // No fallback on a bindable prop: Svelte refuses `bind:…={undefined}` on
175
+ // one that has a fallback (`props_invalid_value`), and a page that throws
176
+ // while it hydrates never becomes interactive. The default is applied
177
+ // here instead: at once, for the server and the first render, and again
178
+ // whenever a parent hands back `undefined`.
179
+ const applyDefaults = () => {
180
+ if (isOpen === undefined) isOpen = false;
181
+ };
182
+ applyDefaults();
183
+ $effect.pre(applyDefaults);
184
+
185
+ // Used only where --duration-slow cannot be read.
186
+ const SLIDE_MS = 300;
187
+
188
+ const titleId = generateId("bottom-sheet-title");
189
+ const descriptionId = generateId("bottom-sheet-description");
190
+
191
+ let root = $state<HTMLDivElement>();
192
+ let panel = $state<HTMLDivElement>();
193
+ let header = $state<HTMLDivElement>();
194
+ let scroller = $state<HTMLDivElement>();
195
+ let footerElement = $state<HTMLDivElement>();
196
+
197
+ /**
198
+ * Read as the overlay opens: with the text enlarged, a long word in the
199
+ * title is hyphenated before it is cut (util/overlay-chrome.ts).
200
+ */
201
+ let enlargedText = $state(false);
202
+ $effect(() => {
203
+ if (isOpen) enlargedText = textIsEnlarged();
204
+ });
205
+ let focusActive = false;
206
+ let scrollerNeedsFocus = $state(false);
207
+ /** Position among the open overlays; lifts a sheet opened later above the earlier ones. */
208
+ let depth = $state(0);
209
+
210
+ const points = $derived(fit ? normaliseSnapPoints(["half"]) : normaliseSnapPoints(snapPoints));
211
+ /** The snap point in effect: the one asked for if it is in use, else the lowest. */
212
+ const currentSnap = $derived(snap && points.includes(snap) ? snap : points[0]);
213
+ const stepTo = $derived(nextSnap(points, currentSnap));
214
+
215
+ /**
216
+ * `100%` is the overlay, which is the viewport. Full height stops at the
217
+ * status bar, so the close button stays in reach; half is never so low
218
+ * that a short screen leaves no room for content, and never above full.
219
+ */
220
+ const FULL = "calc(100% - env(safe-area-inset-top, 0px))";
221
+ const HALF = `min(max(50%, 18rem), ${FULL})`;
222
+ const snapHeight = $derived(currentSnap === "full" ? FULL : HALF);
223
+
224
+ /** While a drag is on: the height and offset it has moved the panel to, in px. */
225
+ let dragging = $state(false);
226
+ let dragHeight = $state<number | null>(null);
227
+ let dragOffset = $state(0);
228
+ /**
229
+ * False from opening until the slide-in has ended. The panel's height
230
+ * transition is for snapping and settling a drag; while the sheet arrives
231
+ * a late change of height (the overlay shrinking over a keyboard, a snap
232
+ * set as it opens) would ride the spring and move the content after the
233
+ * eye has landed on it. Until then the height is applied at once.
234
+ */
235
+ let arrived = $state(false);
236
+
237
+ function close(reason: BottomSheetCloseReason) {
238
+ if (!dismissible) return;
239
+ isOpen = false;
240
+ if (focusActive) {
241
+ focusActive = false;
242
+ returnFocus();
243
+ }
244
+ onclose?.({ reason });
245
+ }
246
+
247
+ /**
248
+ * Slides the panel up from the bottom edge. Skipped where motion is unwanted or unsupported.
249
+ * Returns how long the slide takes in ms: 0 when there is none.
250
+ */
251
+ function slideIn(container: HTMLElement): number {
252
+ if (typeof container.animate !== "function" || prefersReducedMotion()) return 0;
253
+ // Arriving is a smooth slide to rest on the standard curve, with no
254
+ // overshoot (the spring is for snapping and settling a drag). Read
255
+ // from the element so a theme that overrides the tokens is followed.
256
+ // Where the tokens cannot be read (no stylesheet) or the engine
257
+ // rejects the easing, plain ease-out.
258
+ const style = getComputedStyle(container);
259
+ const duration = parseDuration(style.getPropertyValue("--duration-slow"));
260
+ const standard = style.getPropertyValue("--ease-standard").trim();
261
+ const frames = [{ transform: "translateY(100%)" }, { transform: "translateY(0)" }];
262
+ try {
263
+ container.animate(frames, { duration, easing: standard || "ease-out" });
264
+ } catch {
265
+ container.animate(frames, { duration, easing: "ease-out" });
266
+ }
267
+ return duration;
268
+ }
269
+
270
+ /** `300ms`, `0.3s` or `0s` to milliseconds; the fallback when it is not a time. */
271
+ function parseDuration(value: string): number {
272
+ const match = /^\s*(-?\d*\.?\d+)(ms|s)\s*$/.exec(value);
273
+ if (!match) return SLIDE_MS;
274
+ const ms = parseFloat(match[1]) * (match[2] === "s" ? 1000 : 1);
275
+ return ms >= 0 ? ms : SLIDE_MS;
276
+ }
277
+
278
+ /** The snap points in px, lowest first, for the screen as it is now. */
279
+ function measureSnapHeights(): number[] {
280
+ if (!root) return [];
281
+ // A fitted sheet rests at the height its content gives it.
282
+ if (fit && panel) return [panel.getBoundingClientRect().height];
283
+ const available = root.clientHeight - parseFloat(getComputedStyle(root).paddingTop || "0");
284
+ const rem = parseFloat(getComputedStyle(document.documentElement).fontSize) || 16;
285
+ const half = Math.min(Math.max(root.clientHeight * 0.5, 18 * rem), available);
286
+ return points.map((point) => (point === "full" ? available : half));
287
+ }
288
+
289
+ function stepSnap() {
290
+ if (stepTo) snap = stepTo;
291
+ }
292
+
293
+ $effect(() => {
294
+ const box = scroller;
295
+ if (!isOpen || !box) {
296
+ scrollerNeedsFocus = false;
297
+ return;
298
+ }
299
+ return watchScrollerNeedsFocus(box, (needsFocus) => (scrollerNeedsFocus = needsFocus));
300
+ });
301
+
302
+ // Where the on-screen keyboard covers the page, the overlay keeps to what
303
+ // is left above it. The snap heights are shares of the overlay, so half
304
+ // is half of what can be seen, and the footer stays above the keyboard.
305
+ $effect(() => {
306
+ const overlay = root;
307
+ if (!isOpen || !overlay) return;
308
+ return followKeyboard(overlay);
309
+ });
310
+
311
+ // For the toast stack, which stays off a pinned footer.
312
+ $effect(() => {
313
+ const pinned = footerElement;
314
+ if (!isOpen || !pinned) return;
315
+ return registerOverlayFooter(pinned);
316
+ });
317
+
318
+ // And the header, with the close button, which the stack starts below.
319
+ $effect(() => {
320
+ const top = header;
321
+ if (!isOpen || !top) return;
322
+ return registerOverlayHeader(top);
323
+ });
324
+
325
+ $effect(() => {
326
+ const container = panel;
327
+ if (isOpen && container) {
328
+ saveFocus();
329
+ focusActive = true;
330
+ const unlockScroll = lockBodyScroll();
331
+ const overlay = joinOverlayStack(container);
332
+ depth = overlay.depth;
333
+ arrived = false;
334
+ const arrival = setTimeout(() => (arrived = true), slideIn(container));
335
+ const t = setTimeout(() => {
336
+ // Something inside has taken focus already (a field that
337
+ // focuses itself as it mounts): leave it there. `initialFocus`
338
+ // is the consumer saying where focus goes, so it still decides.
339
+ const active = document.activeElement;
340
+ const inside = !!active && active !== container && container.contains(active);
341
+ if (inside && !(initialFocus && container.querySelector(initialFocus))) return;
342
+ focusFirstElement(container, initialFocus);
343
+ }, 0);
344
+ // If the focused control is disabled or removed, focus lands on
345
+ // `<body>`; take Tab and Escape back from there.
346
+ const stopRecovery = recoverStrayFocus(container, {
347
+ onEscape: () => close("escape"),
348
+ });
349
+ return () => {
350
+ clearTimeout(t);
351
+ clearTimeout(arrival);
352
+ arrived = false;
353
+ stopRecovery();
354
+ overlay.leave();
355
+ unlockScroll();
356
+ dragging = false;
357
+ dragHeight = null;
358
+ dragOffset = 0;
359
+ if (focusActive) {
360
+ focusActive = false;
361
+ returnFocus();
362
+ }
363
+ };
364
+ }
365
+ });
366
+
367
+ $effect(() => {
368
+ const zone = header;
369
+ const container = panel;
370
+ if (!isOpen || !zone || !container) return;
371
+
372
+ let heights: number[] = [];
373
+ let startHeight = 0;
374
+ let startIndex = 0;
375
+
376
+ /** How much of the sheet shows after the pointer has moved `distance` down. */
377
+ const shown = (distance: number) =>
378
+ Math.min(Math.max(startHeight - distance, 0), heights[heights.length - 1]);
379
+
380
+ return attachSheetDrag(
381
+ { handleZone: zone, scroller },
382
+ {
383
+ onStart() {
384
+ heights = measureSnapHeights();
385
+ startHeight = container.getBoundingClientRect().height;
386
+ startIndex = points.indexOf(currentSnap);
387
+ dragging = true;
388
+ },
389
+ onMove(distance) {
390
+ const height = shown(distance);
391
+ const lowest = heights[0];
392
+ if (height >= lowest) {
393
+ dragHeight = height;
394
+ dragOffset = 0;
395
+ return;
396
+ }
397
+ // Below the lowest snap point the sheet keeps its size and
398
+ // slides off; one that cannot be closed only gives a little.
399
+ dragHeight = lowest;
400
+ dragOffset = (lowest - height) * (dismissible ? 1 : 0.2);
401
+ },
402
+ onEnd(distance, velocity) {
403
+ const index = settleSnap({
404
+ heights,
405
+ startIndex,
406
+ height: shown(distance),
407
+ velocity,
408
+ flick: FLICK_VELOCITY,
409
+ canClose: dismissible,
410
+ });
411
+ dragging = false;
412
+ dragHeight = null;
413
+ dragOffset = 0;
414
+ if (index === -1) close("swipe");
415
+ else snap = points[index];
416
+ },
417
+ onCancel() {
418
+ dragging = false;
419
+ dragHeight = null;
420
+ dragOffset = 0;
421
+ },
422
+ },
423
+ );
424
+ });
425
+
426
+ function handleBackdropClick(event: Event) {
427
+ if (event.target === event.currentTarget) close("backdrop");
428
+ }
429
+
430
+ function handleKeydown(event: KeyboardEvent) {
431
+ // `defaultPrevented` means a nested dialog already handled the key.
432
+ // Prevented here even when not dismissible, so a dialog underneath
433
+ // does not take the Escape as its own.
434
+ if (event.key === "Escape" && !event.defaultPrevented) {
435
+ event.preventDefault();
436
+ close("escape");
437
+ }
438
+ onkeydown?.(event);
439
+ }
440
+
441
+ /**
442
+ * 44px wide and tall in px: the target must not shrink, and need not grow
443
+ * with the text. It is flush with the top edge of the panel, so it has
444
+ * the panel's radius; that also clears the mark inside it, a 4px pill
445
+ * 10px in (2px + 10px).
446
+ */
447
+ const gripClasses =
448
+ "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";
449
+
450
+ /**
451
+ * The focus ring of the grip button, drawn inside its box. The usual ring
452
+ * reaches 4px outside it, and the title row starts 28px down, inside the
453
+ * 44px box: outside, the ring cut across the title. Same gap and ring,
454
+ * handed to the same property `.focus-ring` uses, only inset.
455
+ */
456
+ const gripFocusClasses =
457
+ "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)]";
458
+ </script>
459
+
460
+ <!-- The bar of the grip. Drawn in the text colour where colours are forced:
461
+ a background is dropped there, and the grip would be gone. -->
462
+ {#snippet gripMark()}
463
+ <span
464
+ class="block h-1 w-9 rounded-pill bg-current text-description forced-colors:bg-[CanvasText]"
465
+ aria-hidden="true"
466
+ ></span>
467
+ {/snippet}
468
+
469
+ {#if isOpen}
470
+ <!-- svelte-ignore a11y_click_events_have_key_events -->
471
+ <!-- `overflow-clip`, not `hidden`: a clipped box is a scroll container
472
+ nobody can scroll. The panel waits below the screen while it slides in, so
473
+ the root has overflow to scroll, and WebKit scrolls it to bring the
474
+ focused grip into view: the sheet rose to the top of the screen, and sat
475
+ 32px high (the filler under the panel) once the slide had ended. -->
476
+ <div
477
+ bind:this={root}
478
+ class="group/overlay fixed inset-0 z-modal overflow-clip pt-[env(safe-area-inset-top)] {dismissible
479
+ ? 'cursor-pointer'
480
+ : 'cursor-default'} bg-overlay"
481
+ style:z-index={depth > 0 ? `calc(var(--z-modal) + ${depth})` : undefined}
482
+ data-overlay-depth={depth}
483
+ use:portalTo={portal}
484
+ onclick={handleBackdropClick}
485
+ onkeydown={handleKeydown}
486
+ role="presentation"
487
+ >
488
+ <div
489
+ bind:this={panel}
490
+ class={cn(
491
+ // The glass is a layer behind the content (`::before`), not a filter on
492
+ // the panel: a backdrop-filter would make the panel the containing
493
+ // block of every fixed-position popover inside it.
494
+ "material-layer-thick absolute inset-x-0 bottom-0 mx-auto flex w-full cursor-default flex-col rounded-t-overlay",
495
+ "max-h-[calc(100dvh-env(safe-area-inset-top,0px))] pb-[env(safe-area-inset-bottom)]",
496
+ // Over the keyboard: no taller than what is left, and the home
497
+ // indicator it would clear is under the keyboard.
498
+ "group-data-keyboard-open/overlay:max-h-[calc(100%_-_env(safe-area-inset-top,0px))] group-data-keyboard-open/overlay:pb-0",
499
+ // Clear of the notch and the rounded corners in landscape.
500
+ "pl-[env(safe-area-inset-left)] pr-[env(safe-area-inset-right)]",
501
+ // Snapping and settling spring; the overshoot may lift the panel a
502
+ // few px, so the filler continues its fill under the screen edge.
503
+ // The panel's own background is transparent now (the fill is on its
504
+ // layer), so the filler paints the material token itself: it follows
505
+ // the same fallbacks.
506
+ "transition-[height,transform] duration-(--duration-slow) ease-spring motion-reduce:transition-none",
507
+ "after:pointer-events-none after:absolute after:inset-x-0 after:top-full after:h-8 after:content-[''] after:bg-(--color-material-thick)",
508
+ "md:w-[min(100%,40rem)]",
509
+ className,
510
+ )}
511
+ style:height={dragHeight !== null ? `${dragHeight}px` : fit ? "auto" : snapHeight}
512
+ style:max-height={fit ? (fitMax === "full" ? FULL : HALF) : undefined}
513
+ style:transform={dragOffset > 0 ? `translateY(${dragOffset}px)` : undefined}
514
+ style:transition={dragging || !arrived ? "none" : undefined}
515
+ role="dialog"
516
+ aria-modal="true"
517
+ aria-labelledby={titleId}
518
+ aria-describedby={description ? descriptionId : undefined}
519
+ tabindex="-1"
520
+ data-snap={currentSnap}
521
+ data-dragging={dragging ? "true" : undefined}
522
+ {...restProps}
523
+ onkeydown={(event) => trapTabKey(panel, event)}
524
+ >
525
+ <!-- The whole header drags the sheet, in both directions.
526
+ `touch-none`: otherwise the browser takes a touch here for itself.
527
+ Chrome: laid out in px, with a title that stops at 1.3 times its
528
+ size (util/overlay-chrome.ts). The gutters of the content and
529
+ the footer are px with it, so the three stay in line. -->
530
+ <div bind:this={header} class="relative shrink-0 touch-none px-4 pt-7 pb-3 {OVERLAY_CHROME}">
531
+ {#if stepTo}
532
+ <button
533
+ type="button"
534
+ data-sheet-grip
535
+ class={cn(gripFocusClasses, gripClasses)}
536
+ aria-label={stepTo === "full" ? expandLabel : collapseLabel}
537
+ onclick={stepSnap}
538
+ >
539
+ {@render gripMark()}
540
+ </button>
541
+ {:else}
542
+ <div data-sheet-grip class={gripClasses}>
543
+ {@render gripMark()}
544
+ </div>
545
+ {/if}
546
+ <div class="flex items-start justify-between gap-[8px]">
547
+ <div class="min-w-0 flex-1">
548
+ <h2
549
+ id={titleId}
550
+ class="{OVERLAY_TITLE_XL} font-semibold text-headline {OVERLAY_TITLE_WRAP} {enlargedText ? OVERLAY_TITLE_HYPHENS : ''}"
551
+ >
552
+ {title}
553
+ </h2>
554
+ </div>
555
+ <!-- `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. -->
556
+ <!-- 8px in from the end edge of the panel: its radius
557
+ (control) plus that is the panel's (overlay). -->
558
+ <button
559
+ type="button"
560
+ onclick={() => close("close-button")}
561
+ 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
562
+ ? 'cursor-pointer hover:bg-surface-overlay-hover hover:text-headline'
563
+ : 'cursor-not-allowed opacity-50'}"
564
+ aria-label={closeLabel}
565
+ aria-disabled={dismissible ? undefined : "true"}
566
+ >
567
+ <X size={20} aria-hidden="true" />
568
+ </button>
569
+ </div>
570
+ </div>
571
+
572
+ <!-- `pt-1`: a scroll container clips, and the focus ring of a
573
+ first control needs the room. `overscroll-contain`: the page
574
+ behind must not take over when the content reaches an end. -->
575
+ <!-- svelte-ignore a11y_no_noninteractive_tabindex -->
576
+ <div
577
+ bind:this={scroller}
578
+ data-bottom-sheet-content
579
+ class={cn(
580
+ "min-h-0 flex-1 overflow-y-auto overscroll-contain px-[16px] pt-[4px] pb-[16px]",
581
+ scrollerNeedsFocus &&
582
+ "focus-visible:outline-2 focus-visible:-outline-offset-2 focus-visible:outline-focus-ring",
583
+ )}
584
+ tabindex={scrollerNeedsFocus ? 0 : undefined}
585
+ role={scrollerNeedsFocus ? "group" : undefined}
586
+ aria-labelledby={scrollerNeedsFocus ? titleId : undefined}
587
+ >
588
+ <!-- With the content, not in the header: at half height on a
589
+ small screen every fixed line is taken from the content. -->
590
+ {#if description}
591
+ <p id={descriptionId} class="mb-3 text-sm text-description">
592
+ {description}
593
+ </p>
594
+ {/if}
595
+ {@render children?.()}
596
+ </div>
597
+
598
+ {#if footer}
599
+ <div
600
+ bind:this={footerElement}
601
+ class="flex shrink-0 flex-wrap justify-end gap-[8px] border-t border-border-overlay px-[16px] py-[12px]"
602
+ >
603
+ {@render footer()}
604
+ </div>
605
+ {/if}
606
+ </div>
607
+ </div>
608
+ {/if}