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,347 @@
1
+ <script lang="ts">
2
+ import { getContext, onDestroy, setContext, untrack, type Snippet } from "svelte";
3
+ import type { HTMLAttributes } from "svelte/elements";
4
+ import ChevronDown from "@lucide/svelte/icons/chevron-down";
5
+ import { cn } from "../util/cn.js";
6
+ import { generateId, isBrowser } from "../util/ssr-safe.js";
7
+ import {
8
+ COLLAPSIBLE_GROUP,
9
+ type CollapsibleGroupContext,
10
+ type CollapsibleGroupMember,
11
+ type CollapsibleHeadingLevel,
12
+ type CollapsibleTriggerProps,
13
+ type CollapsibleTriggerState,
14
+ } from "../util/collapsible.js";
15
+
16
+ /**
17
+ * A trigger, then a panel of its own that it opens. It is not for a "show
18
+ * all" under a list: there the control comes after the content, and what
19
+ * it shows is more items of a list that is already on the screen. For
20
+ * that, put a `Button` with `aria-expanded` and `aria-controls` under the
21
+ * list and let the list draw the rest (Z-127).
22
+ *
23
+ * Other attributes (`id`, `data-*`, ...) land on the host `<div>`.
24
+ */
25
+ type Props = Omit<HTMLAttributes<HTMLDivElement>, "class" | "title"> & {
26
+ /** Extra classes for the host element. */
27
+ class?: string;
28
+ /** Extra classes for the default trigger (a `<summary>`). */
29
+ triggerClass?: string;
30
+ /** Extra classes for the panel. */
31
+ panelClass?: string;
32
+ /** Whether the panel is shown; supports `bind:open`. */
33
+ open?: boolean;
34
+ /** Called when the user toggles the panel, or a group closes it. */
35
+ onopenchange?: (open: boolean) => void;
36
+ /**
37
+ * The trigger cannot be activated. The panel keeps its current state:
38
+ * a group does not close a disabled panel either.
39
+ */
40
+ disabled?: boolean;
41
+ /**
42
+ * Text of the default trigger, a full-width header with a chevron. It is
43
+ * a native `<details>` / `<summary>`, so it opens and closes without
44
+ * scripts: before hydration, and for a visitor who has none.
45
+ */
46
+ title?: string;
47
+ /**
48
+ * Your own header, in place of the default trigger. Spread the first
49
+ * argument on a `<button>`; the second carries the open state. This
50
+ * path needs scripts: the button and the `hidden` panel are yours to
51
+ * wire, and nothing opens until the page has hydrated. Use `title` for
52
+ * content that must open without scripts.
53
+ */
54
+ trigger?: Snippet<[CollapsibleTriggerProps, CollapsibleTriggerState]>;
55
+ /** Wraps the default trigger in `<h1>`–`<h6>`. No heading when omitted. */
56
+ headingLevel?: CollapsibleHeadingLevel;
57
+ /**
58
+ * Closed content stays in the DOM, inside the closed `<details>`, so a
59
+ * form inside it keeps its values and still submits them: closed
60
+ * details content is part of the form. Set this to remove the content
61
+ * while closed instead.
62
+ */
63
+ unmountOnClose?: boolean;
64
+ /**
65
+ * Gives the panel `role="region"`, which makes it a landmark. Defaults
66
+ * to true only inside a single-open `CollapsibleGroup`.
67
+ */
68
+ region?: boolean;
69
+ children?: Snippet;
70
+ };
71
+
72
+ let {
73
+ class: className = "",
74
+ triggerClass = "",
75
+ panelClass = "",
76
+ open = $bindable<Exclude<Props["open"], undefined>>(),
77
+ onopenchange,
78
+ disabled = false,
79
+ title,
80
+ trigger,
81
+ headingLevel,
82
+ unmountOnClose = false,
83
+ region,
84
+ children,
85
+ ...restProps
86
+ }: Props = $props();
87
+
88
+ // No fallback on a bindable prop: Svelte refuses `bind:…={undefined}` on
89
+ // one that has a fallback (`props_invalid_value`), and a page that throws
90
+ // while it hydrates never becomes interactive. The default is applied
91
+ // here instead: at once, for the server and the first render, and again
92
+ // whenever a parent hands back `undefined`.
93
+ const applyDefaults = () => {
94
+ if (open === undefined) open = false;
95
+ };
96
+ applyDefaults();
97
+ $effect.pre(applyDefaults);
98
+
99
+ const triggerId = generateId("collapsible-trigger");
100
+ const panelId = generateId("collapsible-panel");
101
+
102
+ const group = getContext<CollapsibleGroupContext | undefined>(COLLAPSIBLE_GROUP);
103
+ // A Collapsible inside this panel is not a member of the group around it:
104
+ // opening it must not close the panel it lives in.
105
+ setContext(COLLAPSIBLE_GROUP, undefined);
106
+
107
+ /**
108
+ * True for a panel that asked to start open in a single-open group that
109
+ * already had one. It renders closed from the first render, the server's
110
+ * included, and `open` is brought in line after mount. Nobody changed
111
+ * anything, so `onopenchange` is not called for it.
112
+ */
113
+ let startClosed = $state(false);
114
+ /** What is rendered. Everything below reads this, not `open`. */
115
+ const isOpen = $derived(open && !startClosed);
116
+
117
+ let panelElement: HTMLDivElement | undefined = $state();
118
+
119
+ function setOpen(next: boolean) {
120
+ if (isOpen === next) return;
121
+ open = next;
122
+ onopenchange?.(next);
123
+ }
124
+
125
+ function toggle() {
126
+ if (disabled) return;
127
+ setOpen(!isOpen);
128
+ }
129
+
130
+ const member: CollapsibleGroupMember = {
131
+ triggerId,
132
+ // Spelled out rather than `isOpen`: the group asks while this
133
+ // component is still initialising, before `startClosed` is known, and
134
+ // on the server a derived value keeps its first answer.
135
+ isOpen: () => open && !startClosed,
136
+ isDisabled: () => disabled,
137
+ close: () => setOpen(false),
138
+ };
139
+
140
+ if (group) {
141
+ const registration = group.register(member);
142
+ startClosed = registration.startClosed;
143
+ onDestroy(registration.unregister);
144
+ }
145
+
146
+ // The bound prop cannot be written while the component initialises, so
147
+ // the settled state is written back here, once.
148
+ $effect(() => {
149
+ if (!startClosed) return;
150
+ open = false;
151
+ startClosed = false;
152
+ });
153
+
154
+ // An effect rather than a call in `toggle`, so a panel opened from outside
155
+ // through `bind:open` closes its siblings as well. Untracked, because the
156
+ // group reads the siblings' state: tracking that would re-run this effect
157
+ // when a sibling opens, and the two would close each other.
158
+ $effect(() => {
159
+ if (isOpen) untrack(() => group?.opened(member));
160
+ });
161
+
162
+ // A panel can close while focus is inside it: the parent sets `open`, or
163
+ // a group closes it. Hidden content cannot hold focus, which would fall
164
+ // to `<body>`, so the trigger takes it. This runs before the panel is
165
+ // hidden, while the focused element can still be found; focus is not
166
+ // touched in any other case.
167
+ $effect.pre(() => {
168
+ if (isOpen || !panelElement) return;
169
+ const panel = panelElement;
170
+ untrack(() => {
171
+ if (panel.contains(document.activeElement)) {
172
+ document.getElementById(triggerId)?.focus();
173
+ }
174
+ });
175
+ });
176
+
177
+ const triggerProps = $derived({
178
+ id: triggerId,
179
+ type: "button",
180
+ "aria-expanded": isOpen,
181
+ "aria-controls": panelId,
182
+ disabled,
183
+ onclick: toggle,
184
+ "data-collapsible-trigger": "",
185
+ } satisfies CollapsibleTriggerProps);
186
+
187
+ let detailsElement: HTMLDetailsElement | undefined = $state();
188
+
189
+ /**
190
+ * What only the server writes on the `<details>`: the `open` attribute and
191
+ * a mark that says the element came from the server. The client never
192
+ * writes `open` through the template: hydration would set it from the
193
+ * state and undo a toggle the visitor made before the scripts arrived.
194
+ * The effects below adopt that choice, then keep `open` in line.
195
+ */
196
+ const SERVER_MARK = "data-collapsible-ssr";
197
+ const serverAttributes = () =>
198
+ isBrowser() ? {} : { open: isOpen, [SERVER_MARK]: "" };
199
+
200
+ // Once, after mount: a panel toggled before hydration keeps that state.
201
+ $effect(() => {
202
+ const element = detailsElement;
203
+ if (!element?.hasAttribute(SERVER_MARK)) return;
204
+ element.removeAttribute(SERVER_MARK);
205
+ untrack(() => {
206
+ if (!disabled && element.open !== isOpen) setOpen(element.open);
207
+ });
208
+ });
209
+
210
+ // `open` follows the state: an `open` bound from outside, a group closing
211
+ // a sibling. Runs after the adoption above on the first pass.
212
+ $effect(() => {
213
+ const element = detailsElement;
214
+ if (element && element.open !== isOpen) element.open = isOpen;
215
+ });
216
+
217
+ /**
218
+ * The browser's `toggle` event is where a change made on the `<details>`
219
+ * enters: a click on the summary, Enter or Space, find-in-page opening it,
220
+ * or the native exclusive group closing it. It also fires, a task later,
221
+ * for a change this component made itself; `setOpen` then sees no change.
222
+ */
223
+ function handleToggle() {
224
+ const element = detailsElement;
225
+ if (!element) return;
226
+ if (disabled) {
227
+ // Find-in-page can still open a disabled panel: undo it.
228
+ if (element.open !== isOpen) element.open = isOpen;
229
+ return;
230
+ }
231
+ setOpen(element.open);
232
+ }
233
+
234
+ /** A summary cannot be disabled, so its click is cancelled instead. */
235
+ function handleSummaryClick(event: MouseEvent) {
236
+ if (disabled) event.preventDefault();
237
+ }
238
+
239
+ /**
240
+ * Native exclusivity (the `name` attribute) for a single-open group. A
241
+ * disabled panel stays out of it: the group neither closes nor counts it.
242
+ */
243
+ const detailsName = $derived(disabled ? undefined : group?.exclusiveName);
244
+
245
+ const rowClass =
246
+ "m-0 flex items-center justify-between gap-2 px-3 py-2 text-sm font-medium pointer-coarse:min-h-11";
247
+
248
+ /**
249
+ * A region is a landmark, and a page of them is noise (the WAI-ARIA
250
+ * accordion pattern warns against it). In a single-open group at most one
251
+ * panel is exposed at a time, so a region is safe there. Everywhere else
252
+ * the panel is a plain named group unless the caller asks for a region.
253
+ */
254
+ const panelRole = $derived(
255
+ (region ?? (group ? !group.multiple : false)) ? "region" : "group",
256
+ );
257
+ </script>
258
+
259
+ {#snippet defaultRow()}
260
+ <span class="min-w-0 flex-1">{title}</span>
261
+ <ChevronDown
262
+ size={16}
263
+ aria-hidden="true"
264
+ class={cn(
265
+ "shrink-0 text-description transition-transform duration-150 motion-reduce:transition-none",
266
+ isOpen && "rotate-180",
267
+ )}
268
+ />
269
+ {/snippet}
270
+
271
+ {#snippet panel()}
272
+ <div
273
+ bind:this={panelElement}
274
+ id={panelId}
275
+ role={panelRole}
276
+ aria-labelledby={triggerId}
277
+ hidden={trigger ? !isOpen : undefined}
278
+ class={cn(!trigger && "px-3 pb-3 pt-1", panelClass)}
279
+ >
280
+ {#if isOpen || !unmountOnClose}
281
+ {@render children?.()}
282
+ {/if}
283
+ </div>
284
+ {/snippet}
285
+
286
+ <div
287
+ class={cn(className)}
288
+ data-state={isOpen ? "open" : "closed"}
289
+ data-disabled={disabled ? "" : undefined}
290
+ {...restProps}
291
+ >
292
+ {#if trigger}
293
+ {@render trigger(triggerProps, { open: isOpen, disabled })}
294
+ <!-- `hidden` is `display: none`: closed content takes no focus and is not
295
+ in the accessibility tree. The element itself stays, so `aria-controls`
296
+ always points at something. -->
297
+ {@render panel()}
298
+ {:else}
299
+ <!-- A native disclosure: the browser opens and closes it, so it works
300
+ before hydration and without scripts. `open` follows `isOpen` (see
301
+ `serverAttributes`); user changes come back through `toggle`. -->
302
+ <details
303
+ bind:this={detailsElement}
304
+ {...serverAttributes()}
305
+ name={detailsName}
306
+ ontoggle={handleToggle}
307
+ >
308
+ <summary
309
+ id={triggerId}
310
+ aria-controls={panelId}
311
+ aria-disabled={disabled ? "true" : undefined}
312
+ tabindex={disabled ? -1 : undefined}
313
+ data-collapsible-trigger=""
314
+ onclick={handleSummaryClick}
315
+ class={cn(
316
+ "focus-ring block w-full cursor-pointer list-none rounded-control text-start text-sm font-medium text-headline transition-colors duration-150 hover:bg-surface-hover active:bg-surface-active motion-reduce:transition-none [&::-webkit-details-marker]:hidden",
317
+ disabled &&
318
+ "pointer-events-none cursor-not-allowed opacity-50 hover:bg-transparent active:bg-transparent",
319
+ triggerClass,
320
+ )}
321
+ >
322
+ <!-- One branch per level, not `<svelte:element>`: hydration takes
323
+ a dynamic element out and puts it back, which blurs the trigger
324
+ inside it if the user had already tabbed to it. The heading goes
325
+ inside the summary: a summary inside a heading is not valid. The
326
+ flex row is on the inner element, not on the summary, which
327
+ older Safari does not lay out as flex. -->
328
+ {#if headingLevel === 1}
329
+ <h1 class={rowClass}>{@render defaultRow()}</h1>
330
+ {:else if headingLevel === 2}
331
+ <h2 class={rowClass}>{@render defaultRow()}</h2>
332
+ {:else if headingLevel === 3}
333
+ <h3 class={rowClass}>{@render defaultRow()}</h3>
334
+ {:else if headingLevel === 4}
335
+ <h4 class={rowClass}>{@render defaultRow()}</h4>
336
+ {:else if headingLevel === 5}
337
+ <h5 class={rowClass}>{@render defaultRow()}</h5>
338
+ {:else if headingLevel === 6}
339
+ <h6 class={rowClass}>{@render defaultRow()}</h6>
340
+ {:else}
341
+ <span class={rowClass}>{@render defaultRow()}</span>
342
+ {/if}
343
+ </summary>
344
+ {@render panel()}
345
+ </details>
346
+ {/if}
347
+ </div>
@@ -0,0 +1,61 @@
1
+ import { type Snippet } from "svelte";
2
+ import type { HTMLAttributes } from "svelte/elements";
3
+ import { type CollapsibleHeadingLevel, type CollapsibleTriggerProps, type CollapsibleTriggerState } from "../util/collapsible.js";
4
+ /**
5
+ * A trigger, then a panel of its own that it opens. It is not for a "show
6
+ * all" under a list: there the control comes after the content, and what
7
+ * it shows is more items of a list that is already on the screen. For
8
+ * that, put a `Button` with `aria-expanded` and `aria-controls` under the
9
+ * list and let the list draw the rest (Z-127).
10
+ *
11
+ * Other attributes (`id`, `data-*`, ...) land on the host `<div>`.
12
+ */
13
+ type Props = Omit<HTMLAttributes<HTMLDivElement>, "class" | "title"> & {
14
+ /** Extra classes for the host element. */
15
+ class?: string;
16
+ /** Extra classes for the default trigger (a `<summary>`). */
17
+ triggerClass?: string;
18
+ /** Extra classes for the panel. */
19
+ panelClass?: string;
20
+ /** Whether the panel is shown; supports `bind:open`. */
21
+ open?: boolean;
22
+ /** Called when the user toggles the panel, or a group closes it. */
23
+ onopenchange?: (open: boolean) => void;
24
+ /**
25
+ * The trigger cannot be activated. The panel keeps its current state:
26
+ * a group does not close a disabled panel either.
27
+ */
28
+ disabled?: boolean;
29
+ /**
30
+ * Text of the default trigger, a full-width header with a chevron. It is
31
+ * a native `<details>` / `<summary>`, so it opens and closes without
32
+ * scripts: before hydration, and for a visitor who has none.
33
+ */
34
+ title?: string;
35
+ /**
36
+ * Your own header, in place of the default trigger. Spread the first
37
+ * argument on a `<button>`; the second carries the open state. This
38
+ * path needs scripts: the button and the `hidden` panel are yours to
39
+ * wire, and nothing opens until the page has hydrated. Use `title` for
40
+ * content that must open without scripts.
41
+ */
42
+ trigger?: Snippet<[CollapsibleTriggerProps, CollapsibleTriggerState]>;
43
+ /** Wraps the default trigger in `<h1>`–`<h6>`. No heading when omitted. */
44
+ headingLevel?: CollapsibleHeadingLevel;
45
+ /**
46
+ * Closed content stays in the DOM, inside the closed `<details>`, so a
47
+ * form inside it keeps its values and still submits them: closed
48
+ * details content is part of the form. Set this to remove the content
49
+ * while closed instead.
50
+ */
51
+ unmountOnClose?: boolean;
52
+ /**
53
+ * Gives the panel `role="region"`, which makes it a landmark. Defaults
54
+ * to true only inside a single-open `CollapsibleGroup`.
55
+ */
56
+ region?: boolean;
57
+ children?: Snippet;
58
+ };
59
+ declare const Collapsible: import("svelte").Component<Props, {}, "open">;
60
+ type Collapsible = ReturnType<typeof Collapsible>;
61
+ export default Collapsible;
@@ -0,0 +1,136 @@
1
+ <script lang="ts">
2
+ import { setContext, untrack, type Snippet } from "svelte";
3
+ import type { HTMLAttributes } from "svelte/elements";
4
+ import { cn } from "../util/cn.js";
5
+ import { generateId } from "../util/ssr-safe.js";
6
+ import {
7
+ COLLAPSIBLE_GROUP,
8
+ nextTriggerIndex,
9
+ type CollapsibleGroupContext,
10
+ type CollapsibleGroupMember,
11
+ } from "../util/collapsible.js";
12
+
13
+ /** Other attributes (`id`, `aria-label`, `data-*`, ...) land on the host `<div>`. */
14
+ type Props = Omit<HTMLAttributes<HTMLDivElement>, "class"> & {
15
+ /** Extra classes for the host element. */
16
+ class?: string;
17
+ /** Lets several panels be open at once. By default opening one closes the others. */
18
+ multiple?: boolean;
19
+ /** The `Collapsible` components to coordinate. */
20
+ children?: Snippet;
21
+ };
22
+
23
+ let {
24
+ class: className = "",
25
+ multiple = false,
26
+ onkeydown,
27
+ children,
28
+ ...restProps
29
+ }: Props = $props();
30
+
31
+ let hostElement: HTMLDivElement | undefined = $state();
32
+
33
+ /** Names the native exclusive accordion of a single-open group. */
34
+ const exclusiveName = generateId("collapsible-group");
35
+
36
+ /** In the order the Collapsibles were created. */
37
+ const members = new Set<CollapsibleGroupMember>();
38
+
39
+ /** A disabled panel keeps its state, so it neither holds the one open slot nor loses it. */
40
+ function holdsTheSlot(member: CollapsibleGroupMember): boolean {
41
+ return member.isOpen() && !member.isDisabled();
42
+ }
43
+
44
+ function closeOthers(keep: CollapsibleGroupMember) {
45
+ for (const member of members) {
46
+ if (member !== keep && holdsTheSlot(member)) member.close();
47
+ }
48
+ }
49
+
50
+ setContext<CollapsibleGroupContext>(COLLAPSIBLE_GROUP, {
51
+ get multiple() {
52
+ return multiple;
53
+ },
54
+ get exclusiveName() {
55
+ return multiple ? undefined : exclusiveName;
56
+ },
57
+ register(member) {
58
+ // Settled here, while the members initialise, so the first render
59
+ // (the server's included) already shows one open panel.
60
+ const startClosed =
61
+ !multiple &&
62
+ holdsTheSlot(member) &&
63
+ [...members].some(holdsTheSlot);
64
+ members.add(member);
65
+ return { unregister: () => members.delete(member), startClosed };
66
+ },
67
+ opened(member) {
68
+ // A disabled panel is outside the one-open rule in both
69
+ // directions: it is not closed, and it closes nothing.
70
+ if (!multiple && !member.isDisabled()) closeOthers(member);
71
+ },
72
+ });
73
+
74
+ // Going from `multiple` back to single: the first open panel stays.
75
+ $effect(() => {
76
+ if (multiple) return;
77
+ untrack(() => {
78
+ const first = [...members].find(holdsTheSlot);
79
+ if (first) closeOthers(first);
80
+ });
81
+ });
82
+
83
+ /**
84
+ * The optional keys of the accordion pattern, between this group's own
85
+ * header buttons. A trigger nested in a panel belongs to no group, or to
86
+ * an inner one, and is left alone; Tab is never touched.
87
+ */
88
+ function handleKeydown(
89
+ event: KeyboardEvent & { currentTarget: EventTarget & HTMLDivElement },
90
+ ) {
91
+ onkeydown?.(event);
92
+ if (event.defaultPrevented) return;
93
+ if (event.altKey || event.ctrlKey || event.metaKey || event.shiftKey) return;
94
+ const target = event.target;
95
+ if (!(target instanceof HTMLElement) || !hostElement) return;
96
+
97
+ const ids = new Set([...members].map((member) => member.triggerId));
98
+ if (!ids.has(target.id)) return;
99
+
100
+ // Read from the DOM so the order is the visual one. A disabled button
101
+ // (or a summary marked `aria-disabled`) cannot take focus, so it is skipped.
102
+ const triggers = Array.from(
103
+ hostElement.querySelectorAll<HTMLElement>("[data-collapsible-trigger]"),
104
+ ).filter(
105
+ (element) =>
106
+ ids.has(element.id) &&
107
+ !(element as HTMLButtonElement).disabled &&
108
+ element.getAttribute("aria-disabled") !== "true",
109
+ );
110
+
111
+ const next = nextTriggerIndex(
112
+ event.key,
113
+ triggers.indexOf(target),
114
+ triggers.length,
115
+ );
116
+ if (next === null) return;
117
+
118
+ // Also keeps the arrow keys from scrolling the page under the header.
119
+ event.preventDefault();
120
+ triggers[next]?.focus();
121
+ }
122
+ </script>
123
+
124
+ <!-- The listener only moves focus between buttons that are already in the
125
+ tab order; the group itself is not interactive. -->
126
+ <!-- svelte-ignore a11y_no_static_element_interactions -->
127
+ <div
128
+ bind:this={hostElement}
129
+ class={cn("flex flex-col", className)}
130
+ data-collapsible-group=""
131
+ data-multiple={multiple ? "" : undefined}
132
+ {...restProps}
133
+ onkeydown={handleKeydown}
134
+ >
135
+ {@render children?.()}
136
+ </div>
@@ -0,0 +1,14 @@
1
+ import { type Snippet } from "svelte";
2
+ import type { HTMLAttributes } from "svelte/elements";
3
+ /** Other attributes (`id`, `aria-label`, `data-*`, ...) land on the host `<div>`. */
4
+ type Props = Omit<HTMLAttributes<HTMLDivElement>, "class"> & {
5
+ /** Extra classes for the host element. */
6
+ class?: string;
7
+ /** Lets several panels be open at once. By default opening one closes the others. */
8
+ multiple?: boolean;
9
+ /** The `Collapsible` components to coordinate. */
10
+ children?: Snippet;
11
+ };
12
+ declare const CollapsibleGroup: import("svelte").Component<Props, {}, "">;
13
+ type CollapsibleGroup = ReturnType<typeof CollapsibleGroup>;
14
+ export default CollapsibleGroup;
@@ -1,15 +1,23 @@
1
1
  <script lang="ts">
2
+ import { mergeStrings } from "../util/ready-made-strings.js";
3
+ import { zabiStringsFor } from "../util/zabi-strings.js";
2
4
  import CodeBlock from "../atoms/CodeBlock.svelte";
3
5
  import Card from "../atoms/Card.svelte";
4
6
  import CardHeader from "../atoms/CardHeader.svelte";
5
7
  import CardContent from "../atoms/CardContent.svelte";
6
8
  import { cn } from "../util/cn.js";
9
+ import {
10
+ DEFAULT_COMPONENT_DEMO_STRINGS,
11
+ type ComponentDemoStrings,
12
+ } from "../util/ready-made-strings.js";
7
13
 
8
14
  interface Props {
9
15
  title: string;
10
16
  description?: string;
11
17
  code: string;
12
18
  language?: string;
19
+ /** The words of the switch between the preview and the code. */
20
+ strings?: Partial<ComponentDemoStrings>;
13
21
  class?: string;
14
22
  /** @deprecated use `class`. */
15
23
  className?: string;
@@ -20,6 +28,7 @@
20
28
  description = "",
21
29
  code,
22
30
  language = "svelte",
31
+ strings,
23
32
  class: classAttr = "",
24
33
  className: legacyClass = "",
25
34
  children,
@@ -31,6 +40,9 @@
31
40
  const className = $derived(cn(`${classAttr} ${legacyClass}`));
32
41
 
33
42
  let showCode = $state(false);
43
+
44
+ const provided = zabiStringsFor("componentDemo");
45
+ const text = $derived(mergeStrings(DEFAULT_COMPONENT_DEMO_STRINGS, provided(), (strings as Partial<ComponentDemoStrings> | undefined)));
34
46
  </script>
35
47
 
36
48
  <div class="relative">
@@ -44,8 +56,8 @@
44
56
  <CardContent>
45
57
  <button
46
58
  onclick={() => (showCode = !showCode)}
47
- class="absolute top-8 right-8 z-10 flex cursor-pointer items-center gap-2 rounded-control px-3 py-2 text-sm text-description transition-colors duration-200 hover:bg-surface-hover hover:text-body"
48
- aria-label={showCode ? "Show preview" : "Show code"}
59
+ class="absolute top-8 right-8 z-10 flex cursor-pointer items-center gap-2 rounded-control px-3 py-2 pointer-coarse:min-h-11 text-sm text-description transition-colors duration-200 hover:bg-surface-hover hover:text-body active:bg-surface-active"
60
+ aria-label={showCode ? text.showPreview : text.showCode}
49
61
  >
50
62
  {#if showCode}
51
63
  <svg
@@ -67,7 +79,7 @@
67
79
  d="M2.458 12C3.732 7.943 7.523 5 12 5c4.478 0 8.268 2.943 9.542 7-1.274 4.057-5.064 7-9.542 7-4.477 0-8.268-2.943-9.542-7z"
68
80
  />
69
81
  </svg>
70
- Preview
82
+ {text.preview}
71
83
  {:else}
72
84
  <svg
73
85
  class="w-4 h-4"
@@ -82,7 +94,7 @@
82
94
  d="M10 20l4-16m4 4l4 4-4 4M6 16l-4-4 4-4"
83
95
  />
84
96
  </svg>
85
- Code
97
+ {text.code}
86
98
  {/if}
87
99
  </button>
88
100
 
@@ -1,8 +1,11 @@
1
+ import { type ComponentDemoStrings } from "../util/ready-made-strings.js";
1
2
  interface Props {
2
3
  title: string;
3
4
  description?: string;
4
5
  code: string;
5
6
  language?: string;
7
+ /** The words of the switch between the preview and the code. */
8
+ strings?: Partial<ComponentDemoStrings>;
6
9
  class?: string;
7
10
  /** @deprecated use `class`. */
8
11
  className?: string;