zabi-components 7.0.2 → 8.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (433) hide show
  1. package/CHANGELOG.md +1924 -0
  2. package/README.md +287 -11
  3. package/THEME.md +149 -332
  4. package/THEMING.md +1329 -163
  5. package/dist/atoms/ActionPanel.svelte +24 -9
  6. package/dist/atoms/ActionPanel.svelte.d.ts +2 -0
  7. package/dist/atoms/Avatar.svelte +104 -0
  8. package/dist/atoms/Avatar.svelte.d.ts +34 -0
  9. package/dist/atoms/Badge.svelte +124 -58
  10. package/dist/atoms/Badge.svelte.d.ts +17 -5
  11. package/dist/atoms/Button.svelte +212 -62
  12. package/dist/atoms/Button.svelte.d.ts +33 -2
  13. package/dist/atoms/Card.svelte +31 -28
  14. package/dist/atoms/Card.svelte.d.ts +5 -2
  15. package/dist/atoms/CardContent.svelte +10 -2
  16. package/dist/atoms/CardContent.svelte.d.ts +2 -0
  17. package/dist/atoms/CardFooter.svelte +10 -2
  18. package/dist/atoms/CardFooter.svelte.d.ts +2 -0
  19. package/dist/atoms/CardHeader.svelte +26 -6
  20. package/dist/atoms/CardHeader.svelte.d.ts +2 -0
  21. package/dist/atoms/Checkbox.svelte +52 -21
  22. package/dist/atoms/Checkbox.svelte.d.ts +6 -2
  23. package/dist/atoms/CodeBlock.svelte +35 -9
  24. package/dist/atoms/CodeBlock.svelte.d.ts +13 -3
  25. package/dist/atoms/ColorPicker.svelte +272 -52
  26. package/dist/atoms/ColorPicker.svelte.d.ts +6 -1
  27. package/dist/atoms/Container.svelte +17 -10
  28. package/dist/atoms/DateField.svelte +107 -0
  29. package/dist/atoms/DateField.svelte.d.ts +53 -0
  30. package/dist/atoms/Divider.svelte +4 -4
  31. package/dist/atoms/FeatureCard.svelte +4 -3
  32. package/dist/atoms/FieldMessages.svelte +69 -0
  33. package/dist/atoms/FieldMessages.svelte.d.ts +23 -0
  34. package/dist/atoms/FloatingActionButton.svelte +174 -0
  35. package/dist/atoms/FloatingActionButton.svelte.d.ts +51 -0
  36. package/dist/atoms/Heading.svelte +93 -32
  37. package/dist/atoms/Heading.svelte.d.ts +24 -4
  38. package/dist/atoms/IconButton.svelte +242 -53
  39. package/dist/atoms/IconButton.svelte.d.ts +49 -4
  40. package/dist/atoms/Input.svelte +267 -74
  41. package/dist/atoms/Input.svelte.d.ts +32 -2
  42. package/dist/atoms/List.svelte +13 -2
  43. package/dist/atoms/List.svelte.d.ts +2 -0
  44. package/dist/atoms/ListItem.svelte +44 -11
  45. package/dist/atoms/ListItem.svelte.d.ts +5 -0
  46. package/dist/atoms/ListItemLeading.svelte +4 -2
  47. package/dist/atoms/ListItemLeading.svelte.d.ts +1 -1
  48. package/dist/atoms/OptimizedImage.svelte +11 -2
  49. package/dist/atoms/OptimizedImage.svelte.d.ts +2 -0
  50. package/dist/atoms/Progress.svelte +31 -9
  51. package/dist/atoms/Progress.svelte.d.ts +6 -0
  52. package/dist/atoms/Radio.svelte +23 -3
  53. package/dist/atoms/Radio.svelte.d.ts +6 -2
  54. package/dist/atoms/Rating.svelte +494 -0
  55. package/dist/atoms/Rating.svelte.d.ts +56 -0
  56. package/dist/atoms/Select.svelte +475 -135
  57. package/dist/atoms/Select.svelte.d.ts +52 -2
  58. package/dist/atoms/SelectionControl.svelte +99 -18
  59. package/dist/atoms/SelectionControl.svelte.d.ts +6 -3
  60. package/dist/atoms/Skeleton.svelte +5 -3
  61. package/dist/atoms/Slider.svelte +309 -0
  62. package/dist/atoms/Slider.svelte.d.ts +40 -0
  63. package/dist/atoms/Spinner.svelte +57 -0
  64. package/dist/atoms/Spinner.svelte.d.ts +19 -0
  65. package/dist/atoms/Table.svelte +152 -6
  66. package/dist/atoms/Table.svelte.d.ts +15 -3
  67. package/dist/atoms/TemporalField.svelte +140 -0
  68. package/dist/atoms/TemporalField.svelte.d.ts +32 -0
  69. package/dist/atoms/Text.svelte +56 -15
  70. package/dist/atoms/Text.svelte.d.ts +13 -4
  71. package/dist/atoms/Textarea.svelte +57 -45
  72. package/dist/atoms/Textarea.svelte.d.ts +14 -2
  73. package/dist/atoms/ThemeToggle.svelte +238 -56
  74. package/dist/atoms/ThemeToggle.svelte.d.ts +28 -3
  75. package/dist/atoms/TimeField.svelte +108 -0
  76. package/dist/atoms/TimeField.svelte.d.ts +54 -0
  77. package/dist/atoms/Toast.svelte +25 -10
  78. package/dist/atoms/Toast.svelte.d.ts +2 -0
  79. package/dist/atoms/Toggle.svelte +60 -19
  80. package/dist/atoms/Toggle.svelte.d.ts +18 -2
  81. package/dist/atoms/Tooltip.svelte +583 -40
  82. package/dist/atoms/Tooltip.svelte.d.ts +26 -0
  83. package/dist/atoms/ZabiStringsProvider.svelte +45 -0
  84. package/dist/atoms/ZabiStringsProvider.svelte.d.ts +27 -0
  85. package/dist/atoms/index.d.ts +17 -0
  86. package/dist/atoms/index.js +12 -0
  87. package/dist/atoms/selection-control.styles.d.ts +36 -5
  88. package/dist/atoms/selection-control.styles.js +40 -9
  89. package/dist/components/atoms/index.d.ts +17 -0
  90. package/dist/components/atoms/index.d.ts.map +1 -1
  91. package/dist/components/atoms/selection-control.styles.d.ts +36 -5
  92. package/dist/components/atoms/selection-control.styles.d.ts.map +1 -1
  93. package/dist/components/index.d.ts +83 -3
  94. package/dist/components/index.d.ts.map +1 -1
  95. package/dist/components/molecules/index.d.ts +46 -0
  96. package/dist/components/molecules/index.d.ts.map +1 -1
  97. package/dist/components/molecules/toast-store.d.ts +53 -7
  98. package/dist/components/molecules/toast-store.d.ts.map +1 -1
  99. package/dist/components/organisms/index.d.ts +6 -0
  100. package/dist/components/organisms/index.d.ts.map +1 -1
  101. package/dist/components/types/variants.d.ts +15 -1
  102. package/dist/components/types/variants.d.ts.map +1 -1
  103. package/dist/components/util/app-shell.d.ts +28 -0
  104. package/dist/components/util/app-shell.d.ts.map +1 -0
  105. package/dist/components/util/avatar.d.ts +36 -0
  106. package/dist/components/util/avatar.d.ts.map +1 -0
  107. package/dist/components/util/bottom-sheet.d.ts +30 -0
  108. package/dist/components/util/bottom-sheet.d.ts.map +1 -0
  109. package/dist/components/util/bottom-tab-bar.d.ts +35 -0
  110. package/dist/components/util/bottom-tab-bar.d.ts.map +1 -0
  111. package/dist/components/util/calendar.d.ts +56 -0
  112. package/dist/components/util/calendar.d.ts.map +1 -0
  113. package/dist/components/util/cn.d.ts +8 -0
  114. package/dist/components/util/cn.d.ts.map +1 -0
  115. package/dist/components/util/collapsible.d.ts +58 -0
  116. package/dist/components/util/collapsible.d.ts.map +1 -0
  117. package/dist/components/util/confirm-dialog.d.ts +12 -0
  118. package/dist/components/util/confirm-dialog.d.ts.map +1 -0
  119. package/dist/components/util/date.d.ts +96 -0
  120. package/dist/components/util/date.d.ts.map +1 -0
  121. package/dist/components/util/drawer.d.ts +13 -0
  122. package/dist/components/util/drawer.d.ts.map +1 -0
  123. package/dist/components/util/dropdown.d.ts +47 -0
  124. package/dist/components/util/dropdown.d.ts.map +1 -0
  125. package/dist/components/util/field.d.ts +39 -0
  126. package/dist/components/util/field.d.ts.map +1 -0
  127. package/dist/components/util/fit-in-viewport.d.ts +201 -0
  128. package/dist/components/util/fit-in-viewport.d.ts.map +1 -0
  129. package/dist/components/util/focus-utils.d.ts +75 -2
  130. package/dist/components/util/focus-utils.d.ts.map +1 -1
  131. package/dist/components/util/hydration.d.ts +45 -0
  132. package/dist/components/util/hydration.d.ts.map +1 -0
  133. package/dist/components/util/keyboard-inset.d.ts +18 -0
  134. package/dist/components/util/keyboard-inset.d.ts.map +1 -0
  135. package/dist/components/util/media-grid.d.ts +51 -0
  136. package/dist/components/util/media-grid.d.ts.map +1 -0
  137. package/dist/components/util/media.d.ts +6 -0
  138. package/dist/components/util/media.d.ts.map +1 -0
  139. package/dist/components/util/overlay-chrome.d.ts +53 -0
  140. package/dist/components/util/overlay-chrome.d.ts.map +1 -0
  141. package/dist/components/util/overlay.d.ts +72 -0
  142. package/dist/components/util/overlay.d.ts.map +1 -0
  143. package/dist/components/util/photo.d.ts +215 -0
  144. package/dist/components/util/photo.d.ts.map +1 -0
  145. package/dist/components/util/portal.d.ts +23 -0
  146. package/dist/components/util/portal.d.ts.map +1 -0
  147. package/dist/components/util/pull-to-refresh.d.ts +27 -0
  148. package/dist/components/util/pull-to-refresh.d.ts.map +1 -0
  149. package/dist/components/util/radio-keys.d.ts +25 -0
  150. package/dist/components/util/radio-keys.d.ts.map +1 -0
  151. package/dist/components/util/rating.d.ts +23 -0
  152. package/dist/components/util/rating.d.ts.map +1 -0
  153. package/dist/components/util/ready-made-strings.d.ts +77 -0
  154. package/dist/components/util/ready-made-strings.d.ts.map +1 -0
  155. package/dist/components/util/scroll-padding.d.ts +20 -0
  156. package/dist/components/util/scroll-padding.d.ts.map +1 -0
  157. package/dist/components/util/segmented-control.d.ts +16 -0
  158. package/dist/components/util/segmented-control.d.ts.map +1 -0
  159. package/dist/components/util/select.d.ts +41 -0
  160. package/dist/components/util/select.d.ts.map +1 -0
  161. package/dist/components/util/sheet-drag.d.ts +60 -0
  162. package/dist/components/util/sheet-drag.d.ts.map +1 -0
  163. package/dist/components/util/sidebar.d.ts +63 -0
  164. package/dist/components/util/sidebar.d.ts.map +1 -0
  165. package/dist/components/util/sortable-list.d.ts +65 -0
  166. package/dist/components/util/sortable-list.d.ts.map +1 -0
  167. package/dist/components/util/ssr-safe.d.ts +4 -4
  168. package/dist/components/util/ssr-safe.d.ts.map +1 -1
  169. package/dist/components/util/stepper.d.ts +40 -0
  170. package/dist/components/util/stepper.d.ts.map +1 -0
  171. package/dist/components/util/swipeable-list-item.d.ts +36 -0
  172. package/dist/components/util/swipeable-list-item.d.ts.map +1 -0
  173. package/dist/components/util/theme-mode.d.ts +80 -0
  174. package/dist/components/util/theme-mode.d.ts.map +1 -0
  175. package/dist/components/util/toaster.d.ts +93 -0
  176. package/dist/components/util/toaster.d.ts.map +1 -0
  177. package/dist/components/util/tooltip.d.ts +10 -0
  178. package/dist/components/util/tooltip.d.ts.map +1 -0
  179. package/dist/components/util/top-navbar.d.ts +11 -0
  180. package/dist/components/util/top-navbar.d.ts.map +1 -0
  181. package/dist/components/util/touch-target.d.ts +31 -0
  182. package/dist/components/util/touch-target.d.ts.map +1 -0
  183. package/dist/components/util/zabi-strings.d.ts +133 -0
  184. package/dist/components/util/zabi-strings.d.ts.map +1 -0
  185. package/dist/create-theme/cli.js +169 -0
  186. package/dist/create-theme/index.d.ts +176 -0
  187. package/dist/create-theme/index.js +817 -0
  188. package/dist/create-theme/lib/ramp-math.js +128 -0
  189. package/dist/create-theme/lib/resolve.js +141 -0
  190. package/dist/create-theme/theme-data.js +1116 -0
  191. package/dist/index.d.ts +83 -3
  192. package/dist/index.js +60 -8
  193. package/dist/molecules/Alert.svelte +73 -66
  194. package/dist/molecules/Alert.svelte.d.ts +9 -1
  195. package/dist/molecules/AppBar.svelte +415 -0
  196. package/dist/molecules/AppBar.svelte.d.ts +69 -0
  197. package/dist/molecules/AvatarGroup.svelte +116 -0
  198. package/dist/molecules/AvatarGroup.svelte.d.ts +40 -0
  199. package/dist/molecules/BottomSheet.svelte +540 -0
  200. package/dist/molecules/BottomSheet.svelte.d.ts +93 -0
  201. package/dist/molecules/BottomTabBar.svelte +297 -0
  202. package/dist/molecules/BottomTabBar.svelte.d.ts +64 -0
  203. package/dist/molecules/Calendar.svelte +597 -0
  204. package/dist/molecules/Calendar.svelte.d.ts +72 -0
  205. package/dist/molecules/Collapsible.svelte +243 -0
  206. package/dist/molecules/Collapsible.svelte.d.ts +44 -0
  207. package/dist/molecules/CollapsibleGroup.svelte +126 -0
  208. package/dist/molecules/CollapsibleGroup.svelte.d.ts +14 -0
  209. package/dist/molecules/ComponentDemo.svelte +25 -5
  210. package/dist/molecules/ComponentDemo.svelte.d.ts +5 -0
  211. package/dist/molecules/ConfirmDialog.svelte +253 -0
  212. package/dist/molecules/ConfirmDialog.svelte.d.ts +55 -0
  213. package/dist/molecules/ContactForm.svelte +46 -19
  214. package/dist/molecules/ContactForm.svelte.d.ts +10 -0
  215. package/dist/molecules/Drawer.svelte +394 -0
  216. package/dist/molecules/Drawer.svelte.d.ts +55 -0
  217. package/dist/molecules/Dropdown.svelte +540 -50
  218. package/dist/molecules/Dropdown.svelte.d.ts +49 -6
  219. package/dist/molecules/DropdownItem.svelte +122 -0
  220. package/dist/molecules/DropdownItem.svelte.d.ts +28 -0
  221. package/dist/molecules/EmptyState.svelte +56 -10
  222. package/dist/molecules/EmptyState.svelte.d.ts +8 -0
  223. package/dist/molecules/Form.svelte +25 -14
  224. package/dist/molecules/Form.svelte.d.ts +13 -5
  225. package/dist/molecules/FormField.svelte +11 -2
  226. package/dist/molecules/FormField.svelte.d.ts +2 -0
  227. package/dist/molecules/Header.svelte +13 -3
  228. package/dist/molecules/Header.svelte.d.ts +2 -0
  229. package/dist/molecules/ImageUpload.svelte +422 -70
  230. package/dist/molecules/ImageUpload.svelte.d.ts +61 -5
  231. package/dist/molecules/MediaGrid.svelte +527 -0
  232. package/dist/molecules/MediaGrid.svelte.d.ts +77 -0
  233. package/dist/molecules/Modal.svelte +340 -45
  234. package/dist/molecules/Modal.svelte.d.ts +59 -7
  235. package/dist/molecules/NavigationMenu.svelte +15 -3
  236. package/dist/molecules/NavigationMenu.svelte.d.ts +4 -0
  237. package/dist/molecules/NavigationMenuContent.svelte +91 -36
  238. package/dist/molecules/NavigationMenuContent.svelte.d.ts +2 -0
  239. package/dist/molecules/NavigationMenuItem.svelte +10 -3
  240. package/dist/molecules/NavigationMenuItem.svelte.d.ts +2 -0
  241. package/dist/molecules/NavigationMenuLink.svelte +10 -2
  242. package/dist/molecules/NavigationMenuLink.svelte.d.ts +2 -0
  243. package/dist/molecules/NavigationMenuList.svelte +19 -4
  244. package/dist/molecules/NavigationMenuList.svelte.d.ts +2 -0
  245. package/dist/molecules/NavigationMenuTrigger.svelte +16 -3
  246. package/dist/molecules/NavigationMenuTrigger.svelte.d.ts +2 -0
  247. package/dist/molecules/Page.svelte +38 -2
  248. package/dist/molecules/Page.svelte.d.ts +13 -0
  249. package/dist/molecules/PhotoGrid.svelte +433 -0
  250. package/dist/molecules/PhotoGrid.svelte.d.ts +62 -0
  251. package/dist/molecules/PhotoViewer.svelte +1116 -0
  252. package/dist/molecules/PhotoViewer.svelte.d.ts +56 -0
  253. package/dist/molecules/PropsTable.svelte +26 -11
  254. package/dist/molecules/PropsTable.svelte.d.ts +5 -0
  255. package/dist/molecules/PullToRefresh.svelte +263 -0
  256. package/dist/molecules/PullToRefresh.svelte.d.ts +52 -0
  257. package/dist/molecules/RadioGroup.svelte +57 -15
  258. package/dist/molecules/RadioGroup.svelte.d.ts +16 -3
  259. package/dist/molecules/Section.svelte +13 -5
  260. package/dist/molecules/Section.svelte.d.ts +3 -1
  261. package/dist/molecules/SegmentedControl.svelte +323 -0
  262. package/dist/molecules/SegmentedControl.svelte.d.ts +34 -0
  263. package/dist/molecules/SidebarBrandHeader.svelte +24 -5
  264. package/dist/molecules/SidebarBrandHeader.svelte.d.ts +5 -0
  265. package/dist/molecules/SidebarFooter.svelte +39 -8
  266. package/dist/molecules/SidebarFooter.svelte.d.ts +5 -0
  267. package/dist/molecules/SidebarNavSection.svelte +12 -4
  268. package/dist/molecules/SidebarNavSection.svelte.d.ts +2 -0
  269. package/dist/molecules/SlideUp.svelte +253 -30
  270. package/dist/molecules/SlideUp.svelte.d.ts +31 -4
  271. package/dist/molecules/SortableList.svelte +632 -0
  272. package/dist/molecules/SortableList.svelte.d.ts +69 -0
  273. package/dist/molecules/Stepper.svelte +548 -0
  274. package/dist/molecules/Stepper.svelte.d.ts +39 -0
  275. package/dist/molecules/StickyActionBar.svelte +191 -0
  276. package/dist/molecules/StickyActionBar.svelte.d.ts +65 -0
  277. package/dist/molecules/SwipeableListItem.svelte +334 -0
  278. package/dist/molecules/SwipeableListItem.svelte.d.ts +54 -0
  279. package/dist/molecules/Tabs.svelte +254 -24
  280. package/dist/molecules/Tabs.svelte.d.ts +15 -4
  281. package/dist/molecules/Toaster.svelte +352 -6
  282. package/dist/molecules/Toaster.svelte.d.ts +95 -2
  283. package/dist/molecules/ToasterToast.svelte +279 -62
  284. package/dist/molecules/ToasterToast.svelte.d.ts +11 -0
  285. package/dist/molecules/UnsavedChangesBar.svelte +212 -0
  286. package/dist/molecules/UnsavedChangesBar.svelte.d.ts +43 -0
  287. package/dist/molecules/index.d.ts +46 -0
  288. package/dist/molecules/index.js +27 -0
  289. package/dist/molecules/toast-store.d.ts +53 -7
  290. package/dist/molecules/toast-store.js +21 -0
  291. package/dist/organisms/AppShell.svelte +196 -0
  292. package/dist/organisms/AppShell.svelte.d.ts +61 -0
  293. package/dist/organisms/SidebarAccountPanel.svelte +111 -12
  294. package/dist/organisms/SidebarAccountPanel.svelte.d.ts +21 -0
  295. package/dist/organisms/SidebarNavigation.svelte +193 -69
  296. package/dist/organisms/SidebarNavigation.svelte.d.ts +31 -1
  297. package/dist/organisms/SidebarPanel.svelte +45 -16
  298. package/dist/organisms/SidebarPanel.svelte.d.ts +2 -0
  299. package/dist/organisms/SidebarShell.svelte +263 -0
  300. package/dist/organisms/SidebarShell.svelte.d.ts +106 -0
  301. package/dist/organisms/TopNavbar.svelte +329 -33
  302. package/dist/organisms/TopNavbar.svelte.d.ts +28 -0
  303. package/dist/organisms/index.d.ts +6 -0
  304. package/dist/organisms/index.js +4 -0
  305. package/dist/routes/lib/variant-utils.d.ts +26 -0
  306. package/dist/routes/lib/{variant-utils.ts → variant-utils.js} +15 -46
  307. package/dist/types/components.d.ts +4 -0
  308. package/dist/types/components.d.ts.map +1 -1
  309. package/dist/types/events.js +1 -0
  310. package/dist/types/index.d.ts +605 -25
  311. package/dist/types/index.d.ts.map +1 -1
  312. package/dist/types/index.js +3 -0
  313. package/dist/types/variants.ts +17 -1
  314. package/dist/util/app-shell.d.ts +27 -0
  315. package/dist/util/app-shell.js +72 -0
  316. package/dist/util/avatar.d.ts +35 -0
  317. package/dist/util/avatar.js +52 -0
  318. package/dist/util/bottom-sheet.d.ts +29 -0
  319. package/dist/util/bottom-sheet.js +43 -0
  320. package/dist/util/bottom-tab-bar.d.ts +34 -0
  321. package/dist/util/bottom-tab-bar.js +54 -0
  322. package/dist/util/calendar.d.ts +55 -0
  323. package/dist/util/calendar.js +74 -0
  324. package/dist/util/cn.d.ts +7 -0
  325. package/dist/util/cn.js +48 -0
  326. package/dist/util/collapsible.d.ts +57 -0
  327. package/dist/util/collapsible.js +23 -0
  328. package/dist/util/confirm-dialog.d.ts +11 -0
  329. package/dist/util/confirm-dialog.js +2 -0
  330. package/dist/util/date.d.ts +95 -0
  331. package/dist/util/date.js +237 -0
  332. package/dist/util/drawer.d.ts +12 -0
  333. package/dist/util/drawer.js +7 -0
  334. package/dist/util/dropdown.d.ts +46 -0
  335. package/dist/util/dropdown.js +21 -0
  336. package/dist/util/field.d.ts +38 -0
  337. package/dist/util/field.js +38 -0
  338. package/dist/util/fit-in-viewport.d.ts +200 -0
  339. package/dist/util/fit-in-viewport.js +399 -0
  340. package/dist/util/focus-utils.d.ts +75 -2
  341. package/dist/util/focus-utils.js +182 -9
  342. package/dist/util/hydration.d.ts +44 -0
  343. package/dist/util/hydration.js +58 -0
  344. package/dist/util/keyboard-inset.d.ts +17 -0
  345. package/dist/util/keyboard-inset.js +53 -0
  346. package/dist/util/media-grid.d.ts +50 -0
  347. package/dist/util/media-grid.js +81 -0
  348. package/dist/util/media.d.ts +5 -0
  349. package/dist/util/media.js +10 -0
  350. package/dist/util/overlay-chrome.d.ts +52 -0
  351. package/dist/util/overlay-chrome.js +56 -0
  352. package/dist/util/overlay.d.ts +71 -0
  353. package/dist/util/overlay.js +240 -0
  354. package/dist/util/photo.d.ts +214 -0
  355. package/dist/util/photo.js +206 -0
  356. package/dist/util/portal.d.ts +22 -0
  357. package/dist/util/portal.js +50 -0
  358. package/dist/util/pull-to-refresh.d.ts +26 -0
  359. package/dist/util/pull-to-refresh.js +30 -0
  360. package/dist/util/radio-keys.d.ts +24 -0
  361. package/dist/util/radio-keys.js +49 -0
  362. package/dist/util/rating.d.ts +22 -0
  363. package/dist/util/rating.js +26 -0
  364. package/dist/util/ready-made-strings.d.ts +76 -0
  365. package/dist/util/ready-made-strings.js +67 -0
  366. package/dist/util/scroll-padding.d.ts +19 -0
  367. package/dist/util/scroll-padding.js +44 -0
  368. package/dist/util/segmented-control.d.ts +15 -0
  369. package/dist/util/segmented-control.js +1 -0
  370. package/dist/util/select.d.ts +40 -0
  371. package/dist/util/select.js +63 -0
  372. package/dist/util/sheet-drag.d.ts +59 -0
  373. package/dist/util/sheet-drag.js +241 -0
  374. package/dist/util/sidebar.d.ts +62 -0
  375. package/dist/util/sidebar.js +38 -0
  376. package/dist/util/sortable-list.d.ts +64 -0
  377. package/dist/util/sortable-list.js +53 -0
  378. package/dist/util/ssr-safe.d.ts +4 -4
  379. package/dist/util/stepper.d.ts +39 -0
  380. package/dist/util/stepper.js +38 -0
  381. package/dist/util/swipeable-list-item.d.ts +35 -0
  382. package/dist/util/swipeable-list-item.js +32 -0
  383. package/dist/util/theme-mode.d.ts +79 -0
  384. package/dist/util/theme-mode.js +121 -0
  385. package/dist/util/toaster.d.ts +92 -0
  386. package/dist/util/toaster.js +75 -0
  387. package/dist/util/tooltip.d.ts +9 -0
  388. package/dist/util/tooltip.js +18 -0
  389. package/dist/util/top-navbar.d.ts +10 -0
  390. package/dist/util/top-navbar.js +5 -0
  391. package/dist/util/touch-target.d.ts +30 -0
  392. package/dist/util/touch-target.js +30 -0
  393. package/dist/util/zabi-strings.d.ts +132 -0
  394. package/dist/util/zabi-strings.js +105 -0
  395. package/dist/zabi-components-colors.css +5 -2
  396. package/dist/zabi-components-theme-dark-only.css +574 -225
  397. package/dist/zabi-components-theme-dark.css +574 -225
  398. package/dist/zabi-components-theme-only.css +1680 -317
  399. package/dist/zabi-components-theme.css +1680 -317
  400. package/dist/zabi-components.css +4473 -1158
  401. package/docs/lucide-icons.md +41 -16
  402. package/docs/theme-imports.md +81 -11
  403. package/package.json +51 -29
  404. package/dist/lib/layout-width-tokens.d.ts +0 -17
  405. package/dist/lib/layout-width-tokens.d.ts.map +0 -1
  406. package/dist/lib/showcase/component-docs/Button.d.ts +0 -3
  407. package/dist/lib/showcase/component-docs/Button.d.ts.map +0 -1
  408. package/dist/lib/showcase/component-docs/Input.d.ts +0 -3
  409. package/dist/lib/showcase/component-docs/Input.d.ts.map +0 -1
  410. package/dist/lib/showcase/component-docs/List.d.ts +0 -3
  411. package/dist/lib/showcase/component-docs/List.d.ts.map +0 -1
  412. package/dist/lib/showcase/component-docs/Modal.d.ts +0 -3
  413. package/dist/lib/showcase/component-docs/Modal.d.ts.map +0 -1
  414. package/dist/lib/showcase/component-docs/Radio.d.ts +0 -3
  415. package/dist/lib/showcase/component-docs/Radio.d.ts.map +0 -1
  416. package/dist/lib/showcase/component-docs/SidebarNavigation.d.ts +0 -3
  417. package/dist/lib/showcase/component-docs/SidebarNavigation.d.ts.map +0 -1
  418. package/dist/lib/showcase/component-docs/Skeleton.d.ts +0 -3
  419. package/dist/lib/showcase/component-docs/Skeleton.d.ts.map +0 -1
  420. package/dist/lib/showcase/component-docs/_shared.d.ts +0 -12
  421. package/dist/lib/showcase/component-docs/_shared.d.ts.map +0 -1
  422. package/dist/lib/showcase/component-docs/index.d.ts +0 -4
  423. package/dist/lib/showcase/component-docs/index.d.ts.map +0 -1
  424. package/dist/lib/showcase/components-catalog.d.ts +0 -3
  425. package/dist/lib/showcase/components-catalog.d.ts.map +0 -1
  426. package/dist/lib/showcase/components-showcase-constants.d.ts +0 -89
  427. package/dist/lib/showcase/components-showcase-constants.d.ts.map +0 -1
  428. package/dist/lib/showcase/docs-sidebar-helpers.d.ts +0 -15
  429. package/dist/lib/showcase/docs-sidebar-helpers.d.ts.map +0 -1
  430. package/dist/routes/lib/focus-utils.d.ts +0 -12
  431. package/dist/routes/lib/focus-utils.ts +0 -110
  432. package/dist/routes/lib/ssr-safe.ts +0 -58
  433. package/scripts/fix-lucide-svelte-icon-dts.js +0 -35
@@ -0,0 +1,433 @@
1
+ <script lang="ts">
2
+ import { mergeStrings } from "../util/ready-made-strings.js";
3
+ import { zabiStringsFor } from "../util/zabi-strings.js";
4
+ import { untrack } from "svelte";
5
+ import type { HTMLAttributes } from "svelte/elements";
6
+ import Check from "@lucide/svelte/icons/check";
7
+ import ImageOff from "@lucide/svelte/icons/image-off";
8
+ import Maximize2 from "@lucide/svelte/icons/maximize-2";
9
+ import Plus from "@lucide/svelte/icons/plus";
10
+ import Skeleton from "../atoms/Skeleton.svelte";
11
+ import { isDevBuild } from "../util/app-shell.js";
12
+ import { cn } from "../util/cn.js";
13
+ import { columnCount, moveForKey, moveIndex } from "../util/media-grid.js";
14
+ import {
15
+ PHOTO_GRID_STRINGS,
16
+ photoKey,
17
+ visibleCount,
18
+ type Photo,
19
+ type PhotoGridSelectDetail,
20
+ type PhotoGridStrings,
21
+ type PhotoKey,
22
+ } from "../util/photo.js";
23
+ import { generateId } from "../util/ssr-safe.js";
24
+ import { TOUCH_HIT_AREA } from "../util/touch-target.js";
25
+
26
+ /**
27
+ * A grid of square photo thumbnails. A press on one tells you which, so
28
+ * you can open it in a `PhotoViewer`:
29
+ *
30
+ * ```svelte
31
+ * <PhotoGrid {photos} max={9} onopen={(at) => { index = at; open = true; }} onadd={pickFiles} />
32
+ * <PhotoViewer {photos} bind:index bind:isOpen={open} />
33
+ * ```
34
+ *
35
+ * With `selectable` a press selects instead, for choosing a cover photo
36
+ * (`single`) or photos to delete (`multiple`); each tile then has a small
37
+ * button of its own that opens it. In `single` a press on the selected
38
+ * photo changes nothing: the selection is never cleared by pressing it
39
+ * again.
40
+ *
41
+ * The grid is one Tab stop; the arrow keys move between the tiles.
42
+ *
43
+ * Other attributes (`id`, `data-*`, ...) land on the host `<div>`; the
44
+ * two ARIA names go to the list.
45
+ */
46
+ type Props = Omit<
47
+ HTMLAttributes<HTMLDivElement>,
48
+ // `onselect` is also a native event (text selection); ours replaces it.
49
+ "class" | "aria-label" | "aria-labelledby" | "onselect"
50
+ > & {
51
+ photos: Photo[];
52
+ /**
53
+ * How many columns. Left out, it follows the width of the grid: 3
54
+ * under 480px, 4 from there and 5 from 768px.
55
+ */
56
+ columns?: number;
57
+ /**
58
+ * Shows no more than this many photos. The last tile then says how
59
+ * many more there are ("+12") and opens the viewer at its photo.
60
+ */
61
+ max?: number;
62
+ /** Called with the photo's place in `photos` when one is opened. */
63
+ onopen?: (index: number) => void;
64
+ /**
65
+ * Makes a press select: `single` for one photo, `multiple` for any
66
+ * number, with a check mark on each selected tile.
67
+ */
68
+ selectable?: "single" | "multiple";
69
+ /** Key of the selected photo, or `null`; supports `bind:selected`. Used with `selectable="single"`. */
70
+ selected?: PhotoKey | null;
71
+ /** Keys of the selected photos; supports `bind:selectedKeys`. Used with `selectable="multiple"`. */
72
+ selectedKeys?: PhotoKey[];
73
+ /** Called when a photo is selected or, in `multiple`, its selection is cleared. */
74
+ onselect?: (detail: PhotoGridSelectDetail) => void;
75
+ /**
76
+ * Puts an "Add photo" tile first and is called when it is pressed.
77
+ * Open your own file picker or an ImageUpload from it.
78
+ */
79
+ onadd?: () => void;
80
+ /** Overrides for the built-in strings. */
81
+ strings?: Partial<PhotoGridStrings>;
82
+ /** Extra classes for the host element. */
83
+ class?: string;
84
+ "aria-label"?: string;
85
+ "aria-labelledby"?: string;
86
+ };
87
+
88
+ let {
89
+ photos,
90
+ columns,
91
+ max,
92
+ onopen,
93
+ selectable,
94
+ selected = $bindable<Exclude<Props["selected"], undefined>>(),
95
+ selectedKeys = $bindable<Exclude<Props["selectedKeys"], undefined>>(),
96
+ onselect,
97
+ onadd,
98
+ strings,
99
+ class: className = "",
100
+ "aria-label": ariaLabel,
101
+ "aria-labelledby": ariaLabelledby,
102
+ ...restProps
103
+ }: Props = $props();
104
+
105
+ // No fallback on a bindable prop: Svelte refuses `bind:…={undefined}` on
106
+ // one that has a fallback (`props_invalid_value`), and a page that throws
107
+ // while it hydrates never becomes interactive. The default is applied
108
+ // here instead: at once, for the server and the first render, and again
109
+ // whenever a parent hands back `undefined`.
110
+ const applyDefaults = () => {
111
+ if (selected === undefined) selected = null;
112
+ if (selectedKeys === undefined) selectedKeys = [];
113
+ };
114
+ applyDefaults();
115
+ $effect.pre(applyDefaults);
116
+
117
+ /** The add tile's place among the tile keys. No photo has this key: it is not an address. */
118
+ const ADD_KEY = "\u0000add";
119
+ type TileKey = PhotoKey;
120
+
121
+ /** The app-wide words for this component, from a `ZabiStringsProvider` above it, if there is one. */
122
+ const provided = zabiStringsFor("photoGrid");
123
+ const text = $derived(mergeStrings(PHOTO_GRID_STRINGS, provided(), strings));
124
+ const hintId = generateId("photo-grid-hint");
125
+
126
+ const counts = $derived(visibleCount(photos.length, max));
127
+ const shown = $derived(photos.slice(0, counts.shown));
128
+ const multiple = $derived(selectable === "multiple");
129
+
130
+ /** Every tile in order, by key: the add tile, then the photos shown. */
131
+ const tileKeys = $derived<TileKey[]>([...(onadd ? [ADD_KEY] : []), ...shown.map(photoKey)]);
132
+
133
+ $effect(() => {
134
+ if (!isDevBuild()) return;
135
+ const unnamed = photos.filter((photo) => !photo.alt?.trim()).length;
136
+ if (unnamed > 0) {
137
+ console.warn(
138
+ `[zabi-components] PhotoGrid: ${unnamed} of ${photos.length} photos have no alt text. ` +
139
+ 'Each is named "Photo 3 of 12" until it has one.',
140
+ );
141
+ }
142
+ });
143
+
144
+ function nameOf(photo: Photo, index: number): string {
145
+ return photo.alt?.trim() || text.photoName(index + 1, photos.length);
146
+ }
147
+
148
+ let host: HTMLDivElement | undefined = $state();
149
+ /** Thumbnails that have loaded, and those that failed; a changed address is tried again. */
150
+ let loaded = $state<string[]>([]);
151
+ let failed = $state<string[]>([]);
152
+
153
+ /**
154
+ * An image that was already there when the page started running (the
155
+ * server sent it, the cache had it) fired its `load` before anything was
156
+ * listening.
157
+ */
158
+ function settled(image: HTMLImageElement) {
159
+ if (!image.complete) return;
160
+ const address = image.getAttribute("src") ?? "";
161
+ // Untracked: this runs as an attachment, which would otherwise run
162
+ // again for every image each time one of the lists changes.
163
+ untrack(() => {
164
+ if (image.naturalWidth > 0) markLoaded(address);
165
+ else markFailed(address);
166
+ });
167
+ }
168
+
169
+ function markLoaded(address: string) {
170
+ if (!loaded.includes(address)) loaded = [...loaded, address];
171
+ }
172
+
173
+ function markFailed(address: string) {
174
+ if (!failed.includes(address)) failed = [...failed, address];
175
+ }
176
+
177
+ /**
178
+ * The tile the arrow keys last reached. Only its buttons are in the Tab
179
+ * order (a roving tabindex), so the grid is one stop however many photos
180
+ * it holds.
181
+ */
182
+ let activeKey = $state<TileKey | null>(null);
183
+ const tabbableKey = $derived.by(() => {
184
+ if (activeKey !== null && tileKeys.includes(activeKey)) return activeKey;
185
+ if (selectable === "single" && selected !== null && tileKeys.includes(selected)) return selected;
186
+ return tileKeys[0] ?? null;
187
+ });
188
+
189
+ /** As in MediaGrid: the hint describes the tile focus arrives on, and only that one. */
190
+ let focusInside = $state(false);
191
+ let entryKey = $state<TileKey | null>(null);
192
+ const describedKey = $derived(focusInside ? entryKey : tabbableKey);
193
+
194
+ function handleTileFocus(key: TileKey) {
195
+ // Runs before the host's own focusin, which sets `focusInside`.
196
+ if (!focusInside) entryKey = key;
197
+ activeKey = key;
198
+ }
199
+
200
+ function handleHostFocusOut(event: FocusEvent) {
201
+ const next = event.relatedTarget;
202
+ if (!(next instanceof Node) || !host?.contains(next)) focusInside = false;
203
+ }
204
+
205
+ function isSelected(key: PhotoKey): boolean {
206
+ if (!selectable) return false;
207
+ return multiple ? selectedKeys.includes(key) : selected === key;
208
+ }
209
+
210
+ function toggle(photo: Photo, index: number) {
211
+ const key = photoKey(photo);
212
+ const on = !isSelected(key);
213
+ if (multiple) {
214
+ selectedKeys = on
215
+ ? [...selectedKeys, key]
216
+ : selectedKeys.filter((candidate) => candidate !== key);
217
+ onselect?.({ photo, index, selected: on, keys: selectedKeys });
218
+ } else {
219
+ // A single choice is never cleared by pressing it again; only the
220
+ // checkbox-like `multiple` mode toggles off.
221
+ if (!on) return;
222
+ selected = key;
223
+ onselect?.({ photo, index, selected: true, keys: [key] });
224
+ }
225
+ }
226
+
227
+ /** The tile that stands for the photos not shown always opens: it is not one photo to select. */
228
+ function press(photo: Photo, index: number, standsForMore: boolean) {
229
+ if (selectable && !standsForMore) toggle(photo, index);
230
+ else onopen?.(index);
231
+ }
232
+
233
+ function mainButtons(): HTMLElement[] {
234
+ return host ? Array.from(host.querySelectorAll<HTMLElement>("[data-photo-grid-item]")) : [];
235
+ }
236
+
237
+ function handleKeydown(event: KeyboardEvent) {
238
+ const tile = (event.target as HTMLElement).closest<HTMLElement>("[data-photo-grid-tile]");
239
+ if (!tile || event.altKey || event.metaKey) return;
240
+ const buttons = mainButtons();
241
+ const index = buttons.findIndex((button) => tile.contains(button));
242
+ if (index === -1) return;
243
+ // The arrows follow what is on screen, which is mirrored in RTL.
244
+ const move = moveForKey(event, getComputedStyle(tile).direction === "rtl");
245
+ if (!move) return;
246
+ // Also keeps the arrow keys from scrolling the page under the grid.
247
+ event.preventDefault();
248
+ const perRow = columnCount(buttons.map((button) => button.getBoundingClientRect().top));
249
+ const target = moveIndex(index, move, perRow, buttons.length);
250
+ if (target !== index) buttons[target]?.focus();
251
+ }
252
+
253
+ /**
254
+ * The tile's radius is the container role's. What sits 4px inside a
255
+ * corner of it (the check mark, the open button) takes that radius less
256
+ * the 4px, so the two curves stay parallel.
257
+ */
258
+ const cornerMark =
259
+ "absolute flex items-center justify-center rounded-[calc(var(--radius-container)-4px)]";
260
+
261
+ /**
262
+ * The tile's edge is an outline drawn inside it, not a border: a border
263
+ * would move everything in the tile in by its own width, and by another
264
+ * pixel when the tile is selected, and the marks would no longer be 4px
265
+ * from the corner they are rounded for.
266
+ */
267
+ const tileButton =
268
+ "focus-ring relative block size-full cursor-pointer overflow-hidden rounded-container bg-(--color-surface-2) transition-[outline-color,scale] duration-150 motion-reduce:transition-none";
269
+ </script>
270
+
271
+ <div
272
+ bind:this={host}
273
+ class={cn("group/photos @container relative", className)}
274
+ data-photo-grid
275
+ onfocusin={() => (focusInside = true)}
276
+ onfocusout={handleHostFocusOut}
277
+ {...restProps}
278
+ >
279
+ <!-- The reset removes the markers, and Safari then drops the list role. -->
280
+ <!-- The gap is in px: it is the line between two photos, not text spacing. -->
281
+ <!-- svelte-ignore a11y_no_redundant_roles, a11y_no_noninteractive_element_interactions -->
282
+ <ul
283
+ role="list"
284
+ class={cn(
285
+ "m-0 grid list-none gap-[4px] p-0",
286
+ // By the width of the grid itself, so it is right in a sheet or a
287
+ // sidebar too. In px: the count must not change with the text size.
288
+ columns === undefined && "grid-cols-3 @[480px]:grid-cols-4 @[768px]:grid-cols-5",
289
+ )}
290
+ style:grid-template-columns={columns === undefined
291
+ ? undefined
292
+ : `repeat(${Math.max(1, Math.floor(columns))}, minmax(0, 1fr))`}
293
+ aria-label={ariaLabel}
294
+ aria-labelledby={ariaLabelledby}
295
+ onkeydown={handleKeydown}
296
+ >
297
+ {#if onadd}
298
+ <li class="relative aspect-square" data-photo-grid-tile onfocusin={() => handleTileFocus(ADD_KEY)}>
299
+ <!-- A dashed edge the tile depends on to be seen, so in the
300
+ control-border colour; the label is shown, not only read out. -->
301
+ <button
302
+ type="button"
303
+ class="focus-ring flex size-full cursor-pointer flex-col items-center justify-center gap-1 rounded-container border border-dashed border-control-border bg-transparent p-1 text-center text-xs font-medium text-body transition-colors duration-150 hover:bg-surface-hover active:bg-surface-active motion-reduce:transition-none"
304
+ aria-describedby={describedKey === ADD_KEY ? hintId : undefined}
305
+ tabindex={tabbableKey === ADD_KEY ? 0 : -1}
306
+ data-photo-grid-item
307
+ data-photo-grid-add
308
+ onclick={() => onadd?.()}
309
+ >
310
+ <Plus size={24} class="shrink-0" aria-hidden="true" />
311
+ <span class="[overflow-wrap:anywhere]">{text.addPhoto}</span>
312
+ </button>
313
+ </li>
314
+ {/if}
315
+ {#each shown as photo, index (photoKey(photo))}
316
+ {@const key = photoKey(photo)}
317
+ {@const name = nameOf(photo, index)}
318
+ {@const source = photo.thumbSrc ?? photo.src}
319
+ {@const more = counts.hidden > 0 && index === shown.length - 1}
320
+ {@const on = isSelected(key)}
321
+ {@const tabindex = key === tabbableKey ? 0 : -1}
322
+ {@const selects = !!selectable && !more}
323
+ <li
324
+ class="relative aspect-square"
325
+ data-photo-grid-tile
326
+ data-selected={on ? "" : undefined}
327
+ onfocusin={() => handleTileFocus(key)}
328
+ >
329
+ <!-- Selection is a thicker edge and a check mark, not a colour
330
+ alone. `data-photo-key` is how a PhotoViewer finds the tile of
331
+ the photo it shows, to give focus back to it. -->
332
+ <button
333
+ type="button"
334
+ class={cn(
335
+ tileButton,
336
+ on
337
+ ? "outline-2 -outline-offset-2 outline-(--color-action-primary)"
338
+ : "outline-1 -outline-offset-1 outline-(--color-border) hover:outline-(--color-border-medium) active:scale-[0.98] motion-reduce:active:scale-100",
339
+ )}
340
+ aria-label={more ? `${name}, ${text.morePhotos(counts.hidden)}` : name}
341
+ aria-pressed={selects ? on : undefined}
342
+ aria-describedby={key === describedKey ? hintId : undefined}
343
+ {tabindex}
344
+ data-photo-grid-item
345
+ data-photo-key={key}
346
+ onclick={() => press(photo, index, more)}
347
+ >
348
+ {#if failed.includes(source)}
349
+ <span
350
+ class="flex size-full items-center justify-center text-description"
351
+ data-photo-grid-fallback
352
+ >
353
+ <ImageOff size={24} aria-hidden="true" />
354
+ </span>
355
+ {:else}
356
+ {#if !loaded.includes(source)}
357
+ <span class="absolute inset-0" aria-hidden="true" data-photo-grid-skeleton>
358
+ <Skeleton variant="block" class="h-full rounded-none" />
359
+ </span>
360
+ {/if}
361
+ <!-- The button carries the name; the image would only repeat it. -->
362
+ <img
363
+ src={source}
364
+ alt=""
365
+ loading="lazy"
366
+ decoding="async"
367
+ draggable="false"
368
+ class={cn(
369
+ "relative size-full object-cover transition-opacity duration-200 motion-reduce:transition-none",
370
+ loaded.includes(source) ? "opacity-100" : "opacity-0",
371
+ )}
372
+ onload={() => markLoaded(source)}
373
+ onerror={() => markFailed(source)}
374
+ {@attach settled}
375
+ />
376
+ {/if}
377
+ {#if on}
378
+ <span
379
+ class={cn(cornerMark, "start-[4px] top-[4px] size-6 bg-action-primary text-action-primary")}
380
+ data-photo-grid-check
381
+ >
382
+ <Check size={14} aria-hidden="true" />
383
+ </span>
384
+ {/if}
385
+ {#if more}
386
+ <!-- The photo is dimmed and the number sits on an opaque
387
+ plate, so it reads on any photo in either theme. The
388
+ name says it in words. -->
389
+ <span
390
+ class="absolute inset-0 flex items-center justify-center bg-overlay"
391
+ aria-hidden="true"
392
+ data-photo-grid-more
393
+ >
394
+ <span
395
+ class="rounded-control border border-control-border bg-surface-overlay px-2 py-1 text-base font-semibold text-headline"
396
+ >
397
+ +{counts.hidden}
398
+ </span>
399
+ </span>
400
+ {/if}
401
+ </button>
402
+ {#if selects && onopen}
403
+ <!-- Beside the tile's button, not inside it. An opaque
404
+ plate, so it reads over any photo; on a coarse pointer an
405
+ invisible layer widens what takes the tap to 44px. -->
406
+ <button
407
+ type="button"
408
+ class={cn(
409
+ cornerMark,
410
+ TOUCH_HIT_AREA,
411
+ "focus-ring bottom-[4px] end-[4px] size-7 cursor-pointer border border-control-border bg-surface-overlay text-body transition-colors duration-150 hover:text-headline motion-reduce:transition-none",
412
+ )}
413
+ aria-label={text.openPhoto(name)}
414
+ {tabindex}
415
+ data-photo-grid-open
416
+ onclick={() => onopen?.(index)}
417
+ >
418
+ <Maximize2 size={14} aria-hidden="true" />
419
+ </button>
420
+ {/if}
421
+ </li>
422
+ {/each}
423
+ </ul>
424
+ <!-- Hidden until the grid has keyboard focus: a sighted keyboard user
425
+ needs it as much as a screen reader user, and a pointer user does not. -->
426
+ <p
427
+ id={hintId}
428
+ class="mt-2 hidden text-xs text-description group-has-[:focus-visible]/photos:block"
429
+ data-photo-grid-hint
430
+ >
431
+ {text.keyboardHint}
432
+ </p>
433
+ </div>
@@ -0,0 +1,62 @@
1
+ import type { HTMLAttributes } from "svelte/elements";
2
+ import { type Photo, type PhotoGridSelectDetail, type PhotoGridStrings, type PhotoKey } from "../util/photo.js";
3
+ /**
4
+ * A grid of square photo thumbnails. A press on one tells you which, so
5
+ * you can open it in a `PhotoViewer`:
6
+ *
7
+ * ```svelte
8
+ * <PhotoGrid {photos} max={9} onopen={(at) => { index = at; open = true; }} onadd={pickFiles} />
9
+ * <PhotoViewer {photos} bind:index bind:isOpen={open} />
10
+ * ```
11
+ *
12
+ * With `selectable` a press selects instead, for choosing a cover photo
13
+ * (`single`) or photos to delete (`multiple`); each tile then has a small
14
+ * button of its own that opens it. In `single` a press on the selected
15
+ * photo changes nothing: the selection is never cleared by pressing it
16
+ * again.
17
+ *
18
+ * The grid is one Tab stop; the arrow keys move between the tiles.
19
+ *
20
+ * Other attributes (`id`, `data-*`, ...) land on the host `<div>`; the
21
+ * two ARIA names go to the list.
22
+ */
23
+ type Props = Omit<HTMLAttributes<HTMLDivElement>, "class" | "aria-label" | "aria-labelledby" | "onselect"> & {
24
+ photos: Photo[];
25
+ /**
26
+ * How many columns. Left out, it follows the width of the grid: 3
27
+ * under 480px, 4 from there and 5 from 768px.
28
+ */
29
+ columns?: number;
30
+ /**
31
+ * Shows no more than this many photos. The last tile then says how
32
+ * many more there are ("+12") and opens the viewer at its photo.
33
+ */
34
+ max?: number;
35
+ /** Called with the photo's place in `photos` when one is opened. */
36
+ onopen?: (index: number) => void;
37
+ /**
38
+ * Makes a press select: `single` for one photo, `multiple` for any
39
+ * number, with a check mark on each selected tile.
40
+ */
41
+ selectable?: "single" | "multiple";
42
+ /** Key of the selected photo, or `null`; supports `bind:selected`. Used with `selectable="single"`. */
43
+ selected?: PhotoKey | null;
44
+ /** Keys of the selected photos; supports `bind:selectedKeys`. Used with `selectable="multiple"`. */
45
+ selectedKeys?: PhotoKey[];
46
+ /** Called when a photo is selected or, in `multiple`, its selection is cleared. */
47
+ onselect?: (detail: PhotoGridSelectDetail) => void;
48
+ /**
49
+ * Puts an "Add photo" tile first and is called when it is pressed.
50
+ * Open your own file picker or an ImageUpload from it.
51
+ */
52
+ onadd?: () => void;
53
+ /** Overrides for the built-in strings. */
54
+ strings?: Partial<PhotoGridStrings>;
55
+ /** Extra classes for the host element. */
56
+ class?: string;
57
+ "aria-label"?: string;
58
+ "aria-labelledby"?: string;
59
+ };
60
+ declare const PhotoGrid: import("svelte").Component<Props, {}, "selected" | "selectedKeys">;
61
+ type PhotoGrid = ReturnType<typeof PhotoGrid>;
62
+ export default PhotoGrid;