zabi-components 8.0.0 → 8.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (364) hide show
  1. package/CHANGELOG.md +1924 -0
  2. package/README.md +243 -10
  3. package/THEME.md +149 -332
  4. package/THEMING.md +1192 -266
  5. package/dist/atoms/ActionPanel.svelte +15 -5
  6. package/dist/atoms/Avatar.svelte +104 -0
  7. package/dist/atoms/Avatar.svelte.d.ts +34 -0
  8. package/dist/atoms/Badge.svelte +36 -7
  9. package/dist/atoms/Badge.svelte.d.ts +2 -2
  10. package/dist/atoms/Button.svelte +177 -27
  11. package/dist/atoms/Button.svelte.d.ts +30 -2
  12. package/dist/atoms/Card.svelte +4 -4
  13. package/dist/atoms/CardHeader.svelte +16 -4
  14. package/dist/atoms/Checkbox.svelte +48 -21
  15. package/dist/atoms/Checkbox.svelte.d.ts +4 -2
  16. package/dist/atoms/CodeBlock.svelte +20 -2
  17. package/dist/atoms/CodeBlock.svelte.d.ts +4 -0
  18. package/dist/atoms/ColorPicker.svelte +259 -49
  19. package/dist/atoms/ColorPicker.svelte.d.ts +3 -0
  20. package/dist/atoms/Container.svelte +16 -8
  21. package/dist/atoms/DateField.svelte +107 -0
  22. package/dist/atoms/DateField.svelte.d.ts +53 -0
  23. package/dist/atoms/FieldMessages.svelte +69 -0
  24. package/dist/atoms/FieldMessages.svelte.d.ts +23 -0
  25. package/dist/atoms/FloatingActionButton.svelte +174 -0
  26. package/dist/atoms/FloatingActionButton.svelte.d.ts +51 -0
  27. package/dist/atoms/Heading.svelte +37 -5
  28. package/dist/atoms/Heading.svelte.d.ts +7 -0
  29. package/dist/atoms/IconButton.svelte +220 -24
  30. package/dist/atoms/IconButton.svelte.d.ts +49 -4
  31. package/dist/atoms/Input.svelte +253 -52
  32. package/dist/atoms/Input.svelte.d.ts +32 -2
  33. package/dist/atoms/List.svelte +4 -1
  34. package/dist/atoms/ListItem.svelte +44 -11
  35. package/dist/atoms/ListItem.svelte.d.ts +5 -0
  36. package/dist/atoms/Progress.svelte +27 -8
  37. package/dist/atoms/Progress.svelte.d.ts +4 -0
  38. package/dist/atoms/Radio.svelte +19 -3
  39. package/dist/atoms/Radio.svelte.d.ts +4 -2
  40. package/dist/atoms/Rating.svelte +494 -0
  41. package/dist/atoms/Rating.svelte.d.ts +56 -0
  42. package/dist/atoms/Select.svelte +433 -91
  43. package/dist/atoms/Select.svelte.d.ts +49 -3
  44. package/dist/atoms/SelectionControl.svelte +94 -17
  45. package/dist/atoms/SelectionControl.svelte.d.ts +4 -3
  46. package/dist/atoms/Skeleton.svelte +1 -1
  47. package/dist/atoms/Slider.svelte +309 -0
  48. package/dist/atoms/Slider.svelte.d.ts +40 -0
  49. package/dist/atoms/Spinner.svelte +57 -0
  50. package/dist/atoms/Spinner.svelte.d.ts +19 -0
  51. package/dist/atoms/Table.svelte +151 -6
  52. package/dist/atoms/Table.svelte.d.ts +15 -3
  53. package/dist/atoms/TemporalField.svelte +140 -0
  54. package/dist/atoms/TemporalField.svelte.d.ts +32 -0
  55. package/dist/atoms/Text.svelte +23 -11
  56. package/dist/atoms/Text.svelte.d.ts +6 -3
  57. package/dist/atoms/Textarea.svelte +55 -44
  58. package/dist/atoms/Textarea.svelte.d.ts +14 -2
  59. package/dist/atoms/ThemeToggle.svelte +223 -44
  60. package/dist/atoms/ThemeToggle.svelte.d.ts +26 -3
  61. package/dist/atoms/TimeField.svelte +108 -0
  62. package/dist/atoms/TimeField.svelte.d.ts +54 -0
  63. package/dist/atoms/Toast.svelte +17 -4
  64. package/dist/atoms/Toast.svelte.d.ts +2 -0
  65. package/dist/atoms/Toggle.svelte +41 -8
  66. package/dist/atoms/Toggle.svelte.d.ts +11 -2
  67. package/dist/atoms/Tooltip.svelte +508 -23
  68. package/dist/atoms/Tooltip.svelte.d.ts +11 -0
  69. package/dist/atoms/ZabiStringsProvider.svelte +45 -0
  70. package/dist/atoms/ZabiStringsProvider.svelte.d.ts +27 -0
  71. package/dist/atoms/index.d.ts +17 -0
  72. package/dist/atoms/index.js +12 -0
  73. package/dist/atoms/selection-control.styles.d.ts +36 -5
  74. package/dist/atoms/selection-control.styles.js +40 -9
  75. package/dist/components/atoms/index.d.ts +17 -0
  76. package/dist/components/atoms/index.d.ts.map +1 -1
  77. package/dist/components/atoms/selection-control.styles.d.ts +36 -5
  78. package/dist/components/atoms/selection-control.styles.d.ts.map +1 -1
  79. package/dist/components/index.d.ts +80 -2
  80. package/dist/components/index.d.ts.map +1 -1
  81. package/dist/components/molecules/index.d.ts +46 -0
  82. package/dist/components/molecules/index.d.ts.map +1 -1
  83. package/dist/components/molecules/toast-store.d.ts +53 -7
  84. package/dist/components/molecules/toast-store.d.ts.map +1 -1
  85. package/dist/components/organisms/index.d.ts +5 -0
  86. package/dist/components/organisms/index.d.ts.map +1 -1
  87. package/dist/components/types/variants.d.ts +15 -1
  88. package/dist/components/types/variants.d.ts.map +1 -1
  89. package/dist/components/util/app-shell.d.ts +28 -0
  90. package/dist/components/util/app-shell.d.ts.map +1 -0
  91. package/dist/components/util/avatar.d.ts +36 -0
  92. package/dist/components/util/avatar.d.ts.map +1 -0
  93. package/dist/components/util/bottom-sheet.d.ts +30 -0
  94. package/dist/components/util/bottom-sheet.d.ts.map +1 -0
  95. package/dist/components/util/bottom-tab-bar.d.ts +35 -0
  96. package/dist/components/util/bottom-tab-bar.d.ts.map +1 -0
  97. package/dist/components/util/calendar.d.ts +56 -0
  98. package/dist/components/util/calendar.d.ts.map +1 -0
  99. package/dist/components/util/collapsible.d.ts +58 -0
  100. package/dist/components/util/collapsible.d.ts.map +1 -0
  101. package/dist/components/util/confirm-dialog.d.ts +12 -0
  102. package/dist/components/util/confirm-dialog.d.ts.map +1 -0
  103. package/dist/components/util/date.d.ts +96 -0
  104. package/dist/components/util/date.d.ts.map +1 -0
  105. package/dist/components/util/drawer.d.ts +13 -0
  106. package/dist/components/util/drawer.d.ts.map +1 -0
  107. package/dist/components/util/dropdown.d.ts +47 -0
  108. package/dist/components/util/dropdown.d.ts.map +1 -0
  109. package/dist/components/util/field.d.ts +39 -0
  110. package/dist/components/util/field.d.ts.map +1 -0
  111. package/dist/components/util/fit-in-viewport.d.ts +201 -0
  112. package/dist/components/util/fit-in-viewport.d.ts.map +1 -0
  113. package/dist/components/util/focus-utils.d.ts +75 -2
  114. package/dist/components/util/focus-utils.d.ts.map +1 -1
  115. package/dist/components/util/hydration.d.ts +45 -0
  116. package/dist/components/util/hydration.d.ts.map +1 -0
  117. package/dist/components/util/keyboard-inset.d.ts +18 -0
  118. package/dist/components/util/keyboard-inset.d.ts.map +1 -0
  119. package/dist/components/util/media-grid.d.ts +51 -0
  120. package/dist/components/util/media-grid.d.ts.map +1 -0
  121. package/dist/components/util/media.d.ts +6 -0
  122. package/dist/components/util/media.d.ts.map +1 -0
  123. package/dist/components/util/overlay-chrome.d.ts +53 -0
  124. package/dist/components/util/overlay-chrome.d.ts.map +1 -0
  125. package/dist/components/util/overlay.d.ts +72 -0
  126. package/dist/components/util/overlay.d.ts.map +1 -0
  127. package/dist/components/util/photo.d.ts +215 -0
  128. package/dist/components/util/photo.d.ts.map +1 -0
  129. package/dist/components/util/portal.d.ts +23 -0
  130. package/dist/components/util/portal.d.ts.map +1 -0
  131. package/dist/components/util/pull-to-refresh.d.ts +27 -0
  132. package/dist/components/util/pull-to-refresh.d.ts.map +1 -0
  133. package/dist/components/util/radio-keys.d.ts +25 -0
  134. package/dist/components/util/radio-keys.d.ts.map +1 -0
  135. package/dist/components/util/rating.d.ts +23 -0
  136. package/dist/components/util/rating.d.ts.map +1 -0
  137. package/dist/components/util/ready-made-strings.d.ts +77 -0
  138. package/dist/components/util/ready-made-strings.d.ts.map +1 -0
  139. package/dist/components/util/scroll-padding.d.ts +20 -0
  140. package/dist/components/util/scroll-padding.d.ts.map +1 -0
  141. package/dist/components/util/segmented-control.d.ts +16 -0
  142. package/dist/components/util/segmented-control.d.ts.map +1 -0
  143. package/dist/components/util/select.d.ts +41 -0
  144. package/dist/components/util/select.d.ts.map +1 -0
  145. package/dist/components/util/sheet-drag.d.ts +60 -0
  146. package/dist/components/util/sheet-drag.d.ts.map +1 -0
  147. package/dist/components/util/sidebar.d.ts +63 -0
  148. package/dist/components/util/sidebar.d.ts.map +1 -0
  149. package/dist/components/util/sortable-list.d.ts +65 -0
  150. package/dist/components/util/sortable-list.d.ts.map +1 -0
  151. package/dist/components/util/stepper.d.ts +40 -0
  152. package/dist/components/util/stepper.d.ts.map +1 -0
  153. package/dist/components/util/swipeable-list-item.d.ts +36 -0
  154. package/dist/components/util/swipeable-list-item.d.ts.map +1 -0
  155. package/dist/components/util/theme-mode.d.ts +80 -0
  156. package/dist/components/util/theme-mode.d.ts.map +1 -0
  157. package/dist/components/util/toaster.d.ts +93 -0
  158. package/dist/components/util/toaster.d.ts.map +1 -0
  159. package/dist/components/util/tooltip.d.ts +10 -0
  160. package/dist/components/util/tooltip.d.ts.map +1 -0
  161. package/dist/components/util/top-navbar.d.ts +11 -0
  162. package/dist/components/util/top-navbar.d.ts.map +1 -0
  163. package/dist/components/util/touch-target.d.ts +31 -0
  164. package/dist/components/util/touch-target.d.ts.map +1 -0
  165. package/dist/components/util/zabi-strings.d.ts +133 -0
  166. package/dist/components/util/zabi-strings.d.ts.map +1 -0
  167. package/dist/create-theme/cli.js +169 -0
  168. package/dist/create-theme/index.d.ts +176 -0
  169. package/dist/create-theme/index.js +817 -0
  170. package/dist/create-theme/lib/ramp-math.js +128 -0
  171. package/dist/create-theme/lib/resolve.js +141 -0
  172. package/dist/create-theme/theme-data.js +1116 -0
  173. package/dist/index.d.ts +80 -2
  174. package/dist/index.js +57 -2
  175. package/dist/molecules/Alert.svelte +33 -5
  176. package/dist/molecules/Alert.svelte.d.ts +2 -0
  177. package/dist/molecules/AppBar.svelte +415 -0
  178. package/dist/molecules/AppBar.svelte.d.ts +69 -0
  179. package/dist/molecules/AvatarGroup.svelte +116 -0
  180. package/dist/molecules/AvatarGroup.svelte.d.ts +40 -0
  181. package/dist/molecules/BottomSheet.svelte +540 -0
  182. package/dist/molecules/BottomSheet.svelte.d.ts +93 -0
  183. package/dist/molecules/BottomTabBar.svelte +297 -0
  184. package/dist/molecules/BottomTabBar.svelte.d.ts +64 -0
  185. package/dist/molecules/Calendar.svelte +597 -0
  186. package/dist/molecules/Calendar.svelte.d.ts +72 -0
  187. package/dist/molecules/Collapsible.svelte +243 -0
  188. package/dist/molecules/Collapsible.svelte.d.ts +44 -0
  189. package/dist/molecules/CollapsibleGroup.svelte +126 -0
  190. package/dist/molecules/CollapsibleGroup.svelte.d.ts +14 -0
  191. package/dist/molecules/ComponentDemo.svelte +16 -4
  192. package/dist/molecules/ComponentDemo.svelte.d.ts +3 -0
  193. package/dist/molecules/ConfirmDialog.svelte +253 -0
  194. package/dist/molecules/ConfirmDialog.svelte.d.ts +55 -0
  195. package/dist/molecules/ContactForm.svelte +34 -17
  196. package/dist/molecules/ContactForm.svelte.d.ts +8 -0
  197. package/dist/molecules/Drawer.svelte +394 -0
  198. package/dist/molecules/Drawer.svelte.d.ts +55 -0
  199. package/dist/molecules/Dropdown.svelte +509 -48
  200. package/dist/molecules/Dropdown.svelte.d.ts +45 -6
  201. package/dist/molecules/DropdownItem.svelte +122 -0
  202. package/dist/molecules/DropdownItem.svelte.d.ts +28 -0
  203. package/dist/molecules/EmptyState.svelte +55 -10
  204. package/dist/molecules/EmptyState.svelte.d.ts +8 -0
  205. package/dist/molecules/FormField.svelte +9 -1
  206. package/dist/molecules/FormField.svelte.d.ts +2 -0
  207. package/dist/molecules/Header.svelte +1 -1
  208. package/dist/molecules/ImageUpload.svelte +383 -70
  209. package/dist/molecules/ImageUpload.svelte.d.ts +47 -2
  210. package/dist/molecules/MediaGrid.svelte +527 -0
  211. package/dist/molecules/MediaGrid.svelte.d.ts +77 -0
  212. package/dist/molecules/Modal.svelte +288 -72
  213. package/dist/molecules/Modal.svelte.d.ts +51 -2
  214. package/dist/molecules/NavigationMenuContent.svelte +58 -1
  215. package/dist/molecules/NavigationMenuItem.svelte +0 -1
  216. package/dist/molecules/NavigationMenuLink.svelte +1 -1
  217. package/dist/molecules/NavigationMenuList.svelte +10 -3
  218. package/dist/molecules/NavigationMenuTrigger.svelte +2 -2
  219. package/dist/molecules/Page.svelte +32 -2
  220. package/dist/molecules/Page.svelte.d.ts +11 -0
  221. package/dist/molecules/PhotoGrid.svelte +433 -0
  222. package/dist/molecules/PhotoGrid.svelte.d.ts +62 -0
  223. package/dist/molecules/PhotoViewer.svelte +1116 -0
  224. package/dist/molecules/PhotoViewer.svelte.d.ts +56 -0
  225. package/dist/molecules/PropsTable.svelte +22 -10
  226. package/dist/molecules/PropsTable.svelte.d.ts +3 -0
  227. package/dist/molecules/PullToRefresh.svelte +263 -0
  228. package/dist/molecules/PullToRefresh.svelte.d.ts +52 -0
  229. package/dist/molecules/RadioGroup.svelte +52 -14
  230. package/dist/molecules/RadioGroup.svelte.d.ts +14 -3
  231. package/dist/molecules/Section.svelte +1 -1
  232. package/dist/molecules/SegmentedControl.svelte +323 -0
  233. package/dist/molecules/SegmentedControl.svelte.d.ts +34 -0
  234. package/dist/molecules/SidebarBrandHeader.svelte +11 -1
  235. package/dist/molecules/SidebarBrandHeader.svelte.d.ts +3 -0
  236. package/dist/molecules/SidebarFooter.svelte +29 -6
  237. package/dist/molecules/SidebarFooter.svelte.d.ts +3 -0
  238. package/dist/molecules/SlideUp.svelte +220 -61
  239. package/dist/molecules/SlideUp.svelte.d.ts +26 -0
  240. package/dist/molecules/SortableList.svelte +632 -0
  241. package/dist/molecules/SortableList.svelte.d.ts +69 -0
  242. package/dist/molecules/Stepper.svelte +548 -0
  243. package/dist/molecules/Stepper.svelte.d.ts +39 -0
  244. package/dist/molecules/StickyActionBar.svelte +191 -0
  245. package/dist/molecules/StickyActionBar.svelte.d.ts +65 -0
  246. package/dist/molecules/SwipeableListItem.svelte +334 -0
  247. package/dist/molecules/SwipeableListItem.svelte.d.ts +54 -0
  248. package/dist/molecules/Tabs.svelte +233 -9
  249. package/dist/molecules/Tabs.svelte.d.ts +7 -0
  250. package/dist/molecules/Toaster.svelte +350 -5
  251. package/dist/molecules/Toaster.svelte.d.ts +95 -2
  252. package/dist/molecules/ToasterToast.svelte +253 -67
  253. package/dist/molecules/ToasterToast.svelte.d.ts +9 -0
  254. package/dist/molecules/UnsavedChangesBar.svelte +212 -0
  255. package/dist/molecules/UnsavedChangesBar.svelte.d.ts +43 -0
  256. package/dist/molecules/index.d.ts +46 -0
  257. package/dist/molecules/index.js +27 -0
  258. package/dist/molecules/toast-store.d.ts +53 -7
  259. package/dist/molecules/toast-store.js +21 -0
  260. package/dist/organisms/AppShell.svelte +196 -0
  261. package/dist/organisms/AppShell.svelte.d.ts +61 -0
  262. package/dist/organisms/SidebarAccountPanel.svelte +107 -12
  263. package/dist/organisms/SidebarAccountPanel.svelte.d.ts +19 -0
  264. package/dist/organisms/SidebarNavigation.svelte +96 -15
  265. package/dist/organisms/SidebarNavigation.svelte.d.ts +29 -1
  266. package/dist/organisms/SidebarPanel.svelte +31 -10
  267. package/dist/organisms/SidebarShell.svelte +162 -11
  268. package/dist/organisms/SidebarShell.svelte.d.ts +56 -2
  269. package/dist/organisms/TopNavbar.svelte +256 -31
  270. package/dist/organisms/TopNavbar.svelte.d.ts +26 -0
  271. package/dist/organisms/index.d.ts +5 -0
  272. package/dist/organisms/index.js +3 -0
  273. package/dist/types/components.d.ts +4 -0
  274. package/dist/types/components.d.ts.map +1 -1
  275. package/dist/types/index.d.ts +605 -25
  276. package/dist/types/index.d.ts.map +1 -1
  277. package/dist/types/variants.ts +17 -1
  278. package/dist/util/app-shell.d.ts +27 -0
  279. package/dist/util/app-shell.js +72 -0
  280. package/dist/util/avatar.d.ts +35 -0
  281. package/dist/util/avatar.js +52 -0
  282. package/dist/util/bottom-sheet.d.ts +29 -0
  283. package/dist/util/bottom-sheet.js +43 -0
  284. package/dist/util/bottom-tab-bar.d.ts +34 -0
  285. package/dist/util/bottom-tab-bar.js +54 -0
  286. package/dist/util/calendar.d.ts +55 -0
  287. package/dist/util/calendar.js +74 -0
  288. package/dist/util/collapsible.d.ts +57 -0
  289. package/dist/util/collapsible.js +23 -0
  290. package/dist/util/confirm-dialog.d.ts +11 -0
  291. package/dist/util/confirm-dialog.js +2 -0
  292. package/dist/util/date.d.ts +95 -0
  293. package/dist/util/date.js +237 -0
  294. package/dist/util/drawer.d.ts +12 -0
  295. package/dist/util/drawer.js +7 -0
  296. package/dist/util/dropdown.d.ts +46 -0
  297. package/dist/util/dropdown.js +21 -0
  298. package/dist/util/field.d.ts +38 -0
  299. package/dist/util/field.js +38 -0
  300. package/dist/util/fit-in-viewport.d.ts +200 -0
  301. package/dist/util/fit-in-viewport.js +399 -0
  302. package/dist/util/focus-utils.d.ts +75 -2
  303. package/dist/util/focus-utils.js +182 -9
  304. package/dist/util/hydration.d.ts +44 -0
  305. package/dist/util/hydration.js +58 -0
  306. package/dist/util/keyboard-inset.d.ts +17 -0
  307. package/dist/util/keyboard-inset.js +53 -0
  308. package/dist/util/media-grid.d.ts +50 -0
  309. package/dist/util/media-grid.js +81 -0
  310. package/dist/util/media.d.ts +5 -0
  311. package/dist/util/media.js +10 -0
  312. package/dist/util/overlay-chrome.d.ts +52 -0
  313. package/dist/util/overlay-chrome.js +56 -0
  314. package/dist/util/overlay.d.ts +71 -0
  315. package/dist/util/overlay.js +240 -0
  316. package/dist/util/photo.d.ts +214 -0
  317. package/dist/util/photo.js +206 -0
  318. package/dist/util/portal.d.ts +22 -0
  319. package/dist/util/portal.js +50 -0
  320. package/dist/util/pull-to-refresh.d.ts +26 -0
  321. package/dist/util/pull-to-refresh.js +30 -0
  322. package/dist/util/radio-keys.d.ts +24 -0
  323. package/dist/util/radio-keys.js +49 -0
  324. package/dist/util/rating.d.ts +22 -0
  325. package/dist/util/rating.js +26 -0
  326. package/dist/util/ready-made-strings.d.ts +76 -0
  327. package/dist/util/ready-made-strings.js +67 -0
  328. package/dist/util/scroll-padding.d.ts +19 -0
  329. package/dist/util/scroll-padding.js +44 -0
  330. package/dist/util/segmented-control.d.ts +15 -0
  331. package/dist/util/segmented-control.js +1 -0
  332. package/dist/util/select.d.ts +40 -0
  333. package/dist/util/select.js +63 -0
  334. package/dist/util/sheet-drag.d.ts +59 -0
  335. package/dist/util/sheet-drag.js +241 -0
  336. package/dist/util/sidebar.d.ts +62 -0
  337. package/dist/util/sidebar.js +38 -0
  338. package/dist/util/sortable-list.d.ts +64 -0
  339. package/dist/util/sortable-list.js +53 -0
  340. package/dist/util/stepper.d.ts +39 -0
  341. package/dist/util/stepper.js +38 -0
  342. package/dist/util/swipeable-list-item.d.ts +35 -0
  343. package/dist/util/swipeable-list-item.js +32 -0
  344. package/dist/util/theme-mode.d.ts +79 -0
  345. package/dist/util/theme-mode.js +121 -0
  346. package/dist/util/toaster.d.ts +92 -0
  347. package/dist/util/toaster.js +75 -0
  348. package/dist/util/tooltip.d.ts +9 -0
  349. package/dist/util/tooltip.js +18 -0
  350. package/dist/util/top-navbar.d.ts +10 -0
  351. package/dist/util/top-navbar.js +5 -0
  352. package/dist/util/touch-target.d.ts +30 -0
  353. package/dist/util/touch-target.js +30 -0
  354. package/dist/util/zabi-strings.d.ts +132 -0
  355. package/dist/util/zabi-strings.js +105 -0
  356. package/dist/zabi-components-colors.css +5 -2
  357. package/dist/zabi-components-theme-dark-only.css +543 -241
  358. package/dist/zabi-components-theme-dark.css +543 -241
  359. package/dist/zabi-components-theme-only.css +1584 -367
  360. package/dist/zabi-components-theme.css +1584 -367
  361. package/dist/zabi-components.css +3885 -518
  362. package/docs/lucide-icons.md +40 -17
  363. package/docs/theme-imports.md +81 -11
  364. package/package.json +35 -8
@@ -0,0 +1,206 @@
1
+ /**
2
+ * Public types and pure helpers for `PhotoGrid` and `PhotoViewer`.
3
+ *
4
+ * Everything a gesture means is worked out here, on numbers: where a pinch
5
+ * leaves the photo, how far it may be panned, whether a swipe turns the page.
6
+ * The components only feed pointers in and apply the result, so the
7
+ * arithmetic can be tested without a browser.
8
+ */
9
+ export function photoKey(photo) {
10
+ return photo.id ?? photo.src;
11
+ }
12
+ export const PHOTO_GRID_STRINGS = {
13
+ addPhoto: "Add photo",
14
+ photoName: (position, total) => `Photo ${position} of ${total}`,
15
+ morePhotos: (count) => `${count} more ${count === 1 ? "photo" : "photos"}`,
16
+ openPhoto: (name) => `Open ${name}`,
17
+ keyboardHint: "Use the arrow keys to move between photos.",
18
+ };
19
+ export const PHOTO_VIEWER_STRINGS = {
20
+ close: "Close",
21
+ previous: "Previous photo",
22
+ next: "Next photo",
23
+ counter: (position, total) => `${position} / ${total}`,
24
+ position: (position, total) => `${position} of ${total}`,
25
+ loading: "Loading photo",
26
+ loadError: "The photo could not be loaded.",
27
+ retry: "Try again",
28
+ moreActions: "More actions",
29
+ showMore: "More",
30
+ showLess: "Less",
31
+ photoName: (position, total) => `Photo ${position} of ${total}`,
32
+ };
33
+ /** How many actions the viewer's bar shows before the rest go into a menu. */
34
+ export const MAX_BAR_ACTIONS = 3;
35
+ /** The actions shown in the bar, and those that go into the menu. */
36
+ export function splitActions(actions, max = MAX_BAR_ACTIONS) {
37
+ if (actions.length <= max)
38
+ return { bar: [...actions], menu: [] };
39
+ // One place in the bar goes to the menu's own button.
40
+ return { bar: actions.slice(0, max - 1), menu: actions.slice(max - 1) };
41
+ }
42
+ /** An index held inside a list of `count` photos; -1 for an empty list. */
43
+ export function clampIndex(index, count) {
44
+ if (count <= 0)
45
+ return -1;
46
+ if (!Number.isFinite(index))
47
+ return 0;
48
+ return Math.min(Math.max(Math.trunc(index), 0), count - 1);
49
+ }
50
+ /** What the grid shows of `total` photos under `max`, and how many the last tile stands for. */
51
+ export function visibleCount(total, max) {
52
+ if (max === undefined || !Number.isFinite(max) || max < 1 || total <= max) {
53
+ return { shown: total, hidden: 0 };
54
+ }
55
+ const shown = Math.floor(max);
56
+ return { shown, hidden: total - shown };
57
+ }
58
+ export const REST = { scale: 1, x: 0, y: 0 };
59
+ export const MIN_SCALE = 1;
60
+ export const MAX_SCALE = 4;
61
+ /** What a double tap zooms to. */
62
+ export const DOUBLE_TAP_SCALE = 2.5;
63
+ /** Under this a released pinch goes back to the fitted photo. */
64
+ const SNAP_TO_FIT = 1.02;
65
+ export const isZoomed = (transform) => transform.scale > 1.001;
66
+ /** The largest size a photo of `photo` px takes inside `view` without being cropped. */
67
+ export function fitSize(photo, view) {
68
+ if (photo.width <= 0 || photo.height <= 0 || view.width <= 0 || view.height <= 0) {
69
+ return { width: 0, height: 0 };
70
+ }
71
+ const ratio = Math.min(view.width / photo.width, view.height / photo.height);
72
+ return { width: photo.width * ratio, height: photo.height * ratio };
73
+ }
74
+ const clamp = (value, min, max) => Math.min(Math.max(value, min), max);
75
+ /**
76
+ * The transform with its pan held to the photo's edges: an edge of the photo
77
+ * never comes further in than the edge of the screen, and a side that is
78
+ * narrower than the screen stays centred. `fit` is the photo's size at scale 1.
79
+ */
80
+ export function clampPan(transform, fit, view) {
81
+ const scale = clamp(transform.scale, MIN_SCALE, MAX_SCALE);
82
+ const maxX = Math.max(0, (fit.width * scale - view.width) / 2);
83
+ const maxY = Math.max(0, (fit.height * scale - view.height) / 2);
84
+ // `|| 0`: a clamp to a limit of zero can leave -0, which is not what "centred" should print as.
85
+ return {
86
+ scale,
87
+ x: clamp(transform.x, -maxX, maxX) || 0,
88
+ y: clamp(transform.y, -maxY, maxY) || 0,
89
+ };
90
+ }
91
+ /**
92
+ * Zooms to `scale` so that the point of the photo under `point` stays under
93
+ * it. `point` is measured from the centre of the screen.
94
+ */
95
+ export function zoomAbout(transform, scale, point, fit, view) {
96
+ const next = clamp(scale, MIN_SCALE, MAX_SCALE);
97
+ const ratio = next / transform.scale;
98
+ return clampPan({
99
+ scale: next,
100
+ x: point.x - (point.x - transform.x) * ratio,
101
+ y: point.y - (point.y - transform.y) * ratio,
102
+ }, fit, view);
103
+ }
104
+ export function pinchSample(first, second) {
105
+ return {
106
+ centre: { x: (first.x + second.x) / 2, y: (first.y + second.y) / 2 },
107
+ distance: Math.hypot(second.x - first.x, second.y - first.y),
108
+ };
109
+ }
110
+ /**
111
+ * Where a pinch has taken the photo: scaled by how much the fingers have
112
+ * spread, about the point that was between them when it began, and moved with
113
+ * that point. Points are measured from the centre of the screen.
114
+ */
115
+ export function pinchTransform(start, from, now, fit, view) {
116
+ if (from.distance <= 0)
117
+ return clampPan(start, fit, view);
118
+ const zoomed = zoomAbout(start, start.scale * (now.distance / from.distance), from.centre, fit, view);
119
+ return clampPan({
120
+ scale: zoomed.scale,
121
+ x: zoomed.x + (now.centre.x - from.centre.x),
122
+ y: zoomed.y + (now.centre.y - from.centre.y),
123
+ }, fit, view);
124
+ }
125
+ /** A pinch let go a hair above the fitted size is the fitted size. */
126
+ export function settleZoom(transform, fit, view) {
127
+ return transform.scale < SNAP_TO_FIT ? REST : clampPan(transform, fit, view);
128
+ }
129
+ /** A double tap: in to 2.5 times about the tap from the fitted photo, and back to fitted from any zoom. */
130
+ export function doubleTapTransform(transform, point, fit, view) {
131
+ return isZoomed(transform) ? REST : zoomAbout(transform, DOUBLE_TAP_SCALE, point, fit, view);
132
+ }
133
+ /** The photo moved by `delta` px, held to its edges. */
134
+ export function panBy(transform, delta, fit, view) {
135
+ return clampPan({ scale: transform.scale, x: transform.x + delta.x, y: transform.y + delta.y }, fit, view);
136
+ }
137
+ /** One step of the keyboard or the wheel: in or out by half again, about the centre. */
138
+ export const ZOOM_STEP = 1.5;
139
+ // ---------------------------------------------------------------------------
140
+ // Taps and swipes
141
+ // ---------------------------------------------------------------------------
142
+ /** Movement under this many px is a press, not a drag. */
143
+ export const TAP_SLOP = 8;
144
+ /** A press shorter than this is a tap, and two taps closer together than this are a double tap. */
145
+ export const TAP_MS = 300;
146
+ /** Two taps further apart than this are two taps. */
147
+ export const DOUBLE_TAP_RADIUS = 32;
148
+ export function isDoubleTap(previous, now) {
149
+ if (!previous)
150
+ return false;
151
+ return (now.time - previous.time <= TAP_MS &&
152
+ Math.hypot(now.point.x - previous.point.x, now.point.y - previous.point.y) <= DOUBLE_TAP_RADIUS);
153
+ }
154
+ /**
155
+ * Which way a drag on the fitted photo is going, once it has gone far enough
156
+ * to tell: `x` turns the page, `down` closes, and null is not yet known. A
157
+ * drag upwards is neither, and stays null.
158
+ */
159
+ export function dragAxis(delta, slop = TAP_SLOP) {
160
+ const horizontal = Math.abs(delta.x);
161
+ const vertical = Math.abs(delta.y);
162
+ if (Math.max(horizontal, vertical) < slop)
163
+ return null;
164
+ if (horizontal >= vertical)
165
+ return "x";
166
+ return delta.y > 0 ? "down" : null;
167
+ }
168
+ /** Part of the screen's width a slow swipe has to cross to turn the page. */
169
+ export const TURN_DISTANCE = 0.25;
170
+ /** Part of the screen's height a slow swipe down has to cross to close. */
171
+ export const CLOSE_DISTANCE = 0.2;
172
+ /**
173
+ * What a horizontal swipe does when the finger lifts. A flick decides by its
174
+ * direction; a slow one by how far it went. Swiping left shows the next
175
+ * photo, and the previous one in a right-to-left layout. At either end there
176
+ * is nothing to turn to.
177
+ */
178
+ export function swipeTurn(options) {
179
+ const { distance, velocity, width, index, count, rtl = false, flick } = options;
180
+ let direction = 0;
181
+ if (Math.abs(velocity) >= flick)
182
+ direction = velocity < 0 ? 1 : -1;
183
+ else if (Math.abs(distance) >= width * TURN_DISTANCE)
184
+ direction = distance < 0 ? 1 : -1;
185
+ // A flick against the way the photo was dragged is a change of mind.
186
+ if (direction !== 0 && distance !== 0 && Math.sign(distance) === direction)
187
+ direction = 0;
188
+ if (rtl && direction !== 0)
189
+ direction = direction === 1 ? -1 : 1;
190
+ const target = index + direction;
191
+ return target < 0 || target >= count ? 0 : direction;
192
+ }
193
+ /** Whether a swipe down closes the viewer when the finger lifts. */
194
+ export function swipeCloses(options) {
195
+ const { distance, velocity, height, flick } = options;
196
+ if (distance <= 0)
197
+ return false;
198
+ return velocity >= flick || (distance >= height * CLOSE_DISTANCE && velocity > -flick);
199
+ }
200
+ /**
201
+ * How far a drag past the first or last photo moves it: a third of the
202
+ * finger's travel, so it gives but does not come away.
203
+ */
204
+ export function resist(distance) {
205
+ return distance / 3;
206
+ }
@@ -0,0 +1,22 @@
1
+ import type { Action } from "svelte/action";
2
+ /**
3
+ * Where to render the node: `true` for `document.body`, an element, or a
4
+ * selector. `false` leaves the node where the template put it.
5
+ */
6
+ export type PortalTarget = boolean | HTMLElement | string;
7
+ /**
8
+ * Moves an overlay out of its ancestors so none of them can trap it: a
9
+ * `transform`, `filter` or `contain` ancestor becomes the containing block of a
10
+ * `position: fixed` child, and an `overflow: hidden` one then clips it.
11
+ *
12
+ * SSR-safe by construction: actions never run on the server, so the markup is
13
+ * rendered in place and moved on mount. The node is removed on destroy, and
14
+ * Svelte's own teardown of a node that has already gone is a no-op.
15
+ *
16
+ * Put it on the single root element of an `{#if}` block. The theme is a `.dark`
17
+ * class on an ancestor, so a node portalled to `body` keeps it only when the
18
+ * class sits on `<html>` or `<body>`.
19
+ *
20
+ * Usage: `<div use:portal>` or `<div use:portal={enabled}>`.
21
+ */
22
+ export declare const portal: Action<HTMLElement, PortalTarget | undefined>;
@@ -0,0 +1,50 @@
1
+ function resolveTarget(target) {
2
+ if (target === false)
3
+ return null;
4
+ if (target === true)
5
+ return document.body;
6
+ if (typeof target === "string") {
7
+ return document.querySelector(target);
8
+ }
9
+ return target;
10
+ }
11
+ /**
12
+ * Moves an overlay out of its ancestors so none of them can trap it: a
13
+ * `transform`, `filter` or `contain` ancestor becomes the containing block of a
14
+ * `position: fixed` child, and an `overflow: hidden` one then clips it.
15
+ *
16
+ * SSR-safe by construction: actions never run on the server, so the markup is
17
+ * rendered in place and moved on mount. The node is removed on destroy, and
18
+ * Svelte's own teardown of a node that has already gone is a no-op.
19
+ *
20
+ * Put it on the single root element of an `{#if}` block. The theme is a `.dark`
21
+ * class on an ancestor, so a node portalled to `body` keeps it only when the
22
+ * class sits on `<html>` or `<body>`.
23
+ *
24
+ * Usage: `<div use:portal>` or `<div use:portal={enabled}>`.
25
+ */
26
+ export const portal = (node, target = true) => {
27
+ const home = node.parentNode;
28
+ const homeNext = node.nextSibling;
29
+ function place(next) {
30
+ const destination = resolveTarget(next);
31
+ if (destination) {
32
+ if (node.parentNode !== destination)
33
+ destination.appendChild(node);
34
+ }
35
+ else if (next === false && home && node.parentNode !== home) {
36
+ // Back to where the template put it, if that place still exists.
37
+ home.insertBefore(node, homeNext?.parentNode === home ? homeNext : null);
38
+ }
39
+ }
40
+ place(target);
41
+ return {
42
+ update(next = true) {
43
+ place(next);
44
+ },
45
+ destroy() {
46
+ if (node.parentNode !== home)
47
+ node.remove();
48
+ },
49
+ };
50
+ };
@@ -0,0 +1,26 @@
1
+ /** Public types and pure helpers for `PullToRefresh`. */
2
+ /** The words a `PullToRefresh` says by itself. */
3
+ export interface PullToRefreshStrings {
4
+ /** Shown while the list is being pulled, short of the threshold. */
5
+ pull: string;
6
+ /** Shown once letting go would refresh. */
7
+ release: string;
8
+ /** Shown, and announced, while the refresh runs. */
9
+ refreshing: string;
10
+ /** Announced when it has finished. */
11
+ done: string;
12
+ /** The button that refreshes without the gesture. */
13
+ refresh: string;
14
+ }
15
+ export declare const DEFAULT_PULL_TO_REFRESH_STRINGS: PullToRefreshStrings;
16
+ /**
17
+ * How far the indicator has come out for a finger that has travelled
18
+ * `distance` px: half as far, so the list feels held, and never more than one
19
+ * and a half times the threshold.
20
+ */
21
+ export declare function pullDistance(distance: number, threshold: number): number;
22
+ /**
23
+ * What scrolls the element: its nearest ancestor that scrolls on the block
24
+ * axis, or the page.
25
+ */
26
+ export declare function scrollContainerOf(element: HTMLElement): HTMLElement;
@@ -0,0 +1,30 @@
1
+ /** Public types and pure helpers for `PullToRefresh`. */
2
+ export const DEFAULT_PULL_TO_REFRESH_STRINGS = {
3
+ pull: "Pull to refresh",
4
+ release: "Release to refresh",
5
+ refreshing: "Refreshing",
6
+ done: "Updated",
7
+ refresh: "Refresh",
8
+ };
9
+ /**
10
+ * How far the indicator has come out for a finger that has travelled
11
+ * `distance` px: half as far, so the list feels held, and never more than one
12
+ * and a half times the threshold.
13
+ */
14
+ export function pullDistance(distance, threshold) {
15
+ return Math.max(0, Math.min(distance / 2, threshold * 1.5));
16
+ }
17
+ /**
18
+ * What scrolls the element: its nearest ancestor that scrolls on the block
19
+ * axis, or the page.
20
+ */
21
+ export function scrollContainerOf(element) {
22
+ for (let parent = element.parentElement; parent; parent = parent.parentElement) {
23
+ if (parent === document.body || parent === document.documentElement)
24
+ break;
25
+ const overflow = getComputedStyle(parent).overflowY;
26
+ if (overflow === "auto" || overflow === "scroll" || overflow === "overlay")
27
+ return parent;
28
+ }
29
+ return document.scrollingElement ?? document.documentElement;
30
+ }
@@ -0,0 +1,24 @@
1
+ /**
2
+ * Where a key moves the selection in a radio group laid out in a row, as
3
+ * Rating and SegmentedControl are. Kept out of the components so it can be
4
+ * tested without a DOM.
5
+ *
6
+ * It is the native radio contract: Right and Down go to the next option, Left
7
+ * and Up to the previous one, both wrap, and Left and Right swap in a
8
+ * right-to-left layout.
9
+ *
10
+ * `current` is the option that has focus, which is not always the selected
11
+ * one: a screen reader's cursor can put focus on any radio, and the keys then
12
+ * move from where the user is. It is -1 when focus is on none of them: the
13
+ * next option is then the first, and the previous one the last.
14
+ *
15
+ * `hasSelection` is false while the group is empty. A forward key then selects
16
+ * the focused option itself instead of stepping over it, so Right on an empty
17
+ * rating gives one star, not two.
18
+ *
19
+ * Returns -1 for a key that is not a navigation key, or when there is nothing
20
+ * to move to.
21
+ */
22
+ export declare function radioKeyIndex(key: string, current: number, count: number, rtl?: boolean, hasSelection?: boolean): number;
23
+ /** Whether `node` is laid out right to left. */
24
+ export declare function isRtl(node: Element): boolean;
@@ -0,0 +1,49 @@
1
+ /**
2
+ * Where a key moves the selection in a radio group laid out in a row, as
3
+ * Rating and SegmentedControl are. Kept out of the components so it can be
4
+ * tested without a DOM.
5
+ *
6
+ * It is the native radio contract: Right and Down go to the next option, Left
7
+ * and Up to the previous one, both wrap, and Left and Right swap in a
8
+ * right-to-left layout.
9
+ *
10
+ * `current` is the option that has focus, which is not always the selected
11
+ * one: a screen reader's cursor can put focus on any radio, and the keys then
12
+ * move from where the user is. It is -1 when focus is on none of them: the
13
+ * next option is then the first, and the previous one the last.
14
+ *
15
+ * `hasSelection` is false while the group is empty. A forward key then selects
16
+ * the focused option itself instead of stepping over it, so Right on an empty
17
+ * rating gives one star, not two.
18
+ *
19
+ * Returns -1 for a key that is not a navigation key, or when there is nothing
20
+ * to move to.
21
+ */
22
+ export function radioKeyIndex(key, current, count, rtl = false, hasSelection = true) {
23
+ if (count <= 0)
24
+ return -1;
25
+ if (key === "Home")
26
+ return 0;
27
+ if (key === "End")
28
+ return count - 1;
29
+ let delta = 0;
30
+ if (key === "ArrowDown")
31
+ delta = 1;
32
+ else if (key === "ArrowUp")
33
+ delta = -1;
34
+ else if (key === "ArrowRight")
35
+ delta = rtl ? -1 : 1;
36
+ else if (key === "ArrowLeft")
37
+ delta = rtl ? 1 : -1;
38
+ if (delta === 0)
39
+ return -1;
40
+ if (current < 0 || current >= count)
41
+ return delta > 0 ? 0 : count - 1;
42
+ if (!hasSelection && delta > 0)
43
+ return current;
44
+ return (current + delta + count) % count;
45
+ }
46
+ /** Whether `node` is laid out right to left. */
47
+ export function isRtl(node) {
48
+ return getComputedStyle(node).direction === "rtl";
49
+ }
@@ -0,0 +1,22 @@
1
+ /** Types and pure helpers for `Rating`. Kept out of the component so they can be tested without a DOM. */
2
+ /** Every built-in string. Pass a partial object to `strings` to translate. */
3
+ export interface RatingStrings {
4
+ /**
5
+ * Accessible name of one star ("4 of 5 stars"), and of a read-only
6
+ * rating as a whole ("3.5 of 5 stars"). `value` is already formatted.
7
+ */
8
+ starLabel: (value: string, max: number) => string;
9
+ /** Accessible name of the clear button that `clearable` adds. */
10
+ clearLabel: string;
11
+ /** Shown, and read, for a read-only rating without a value. */
12
+ noRating: string;
13
+ }
14
+ export declare const RATING_STRINGS: RatingStrings;
15
+ /** Number of stars: a whole number, at least one. */
16
+ export declare function starCount(max: number): number;
17
+ /** `value` held within 0..max; `null` for no rating or a value that is not a number. */
18
+ export declare function clampRating(value: number | null | undefined, max: number): number | null;
19
+ /** How much of star `star` (counted from 1) is filled, 0 to 1. */
20
+ export declare function starFill(value: number | null, star: number): number;
21
+ /** The value with at most one decimal: 4, 3.5, 3.7. */
22
+ export declare function defaultRatingFormat(value: number): string;
@@ -0,0 +1,26 @@
1
+ /** Types and pure helpers for `Rating`. Kept out of the component so they can be tested without a DOM. */
2
+ export const RATING_STRINGS = {
3
+ starLabel: (value, max) => `${value} of ${max} stars`,
4
+ clearLabel: "Clear rating",
5
+ noRating: "No rating",
6
+ };
7
+ /** Number of stars: a whole number, at least one. */
8
+ export function starCount(max) {
9
+ return Number.isFinite(max) ? Math.max(1, Math.floor(max)) : 5;
10
+ }
11
+ /** `value` held within 0..max; `null` for no rating or a value that is not a number. */
12
+ export function clampRating(value, max) {
13
+ if (value === null || value === undefined || !Number.isFinite(value))
14
+ return null;
15
+ return Math.max(0, Math.min(max, value));
16
+ }
17
+ /** How much of star `star` (counted from 1) is filled, 0 to 1. */
18
+ export function starFill(value, star) {
19
+ if (value === null)
20
+ return 0;
21
+ return Math.max(0, Math.min(1, value - (star - 1)));
22
+ }
23
+ /** The value with at most one decimal: 4, 3.5, 3.7. */
24
+ export function defaultRatingFormat(value) {
25
+ return String(Math.round(value * 10) / 10);
26
+ }
@@ -0,0 +1,76 @@
1
+ /**
2
+ * The words of the components that come with text of their own: the colour
3
+ * picker, the ready-made contact form, and the two the docs site is built
4
+ * from. Each takes its own as `strings`; what is left out stays as it is here.
5
+ */
6
+ /** `ColorPicker`. */
7
+ export interface ColorPickerStrings {
8
+ /** Accessible name of the field the hex value is typed in. */
9
+ hexInput: string;
10
+ /** Accessible name of the swatch button that opens the picker. */
11
+ open: string;
12
+ /** Accessible name of the picker that opens. */
13
+ picker: string;
14
+ /** Accessible name of the hue slider in it. */
15
+ hue: string;
16
+ /** Accessible name of the colour map: the two sliders below, on one surface. */
17
+ area: string;
18
+ /** Accessible name of the map's slider that goes across. */
19
+ saturation: string;
20
+ /** Accessible name of the map's slider that goes up and down. */
21
+ lightness: string;
22
+ /** The message under the field while what is typed is not a hex colour. */
23
+ invalidHex: string;
24
+ }
25
+ export declare const DEFAULT_COLOR_PICKER_STRINGS: ColorPickerStrings;
26
+ /** `ContactForm`: every label, placeholder and message of the form. */
27
+ export interface ContactFormStrings {
28
+ heading: string;
29
+ nameLabel: string;
30
+ namePlaceholder: string;
31
+ emailLabel: string;
32
+ emailPlaceholder: string;
33
+ messageLabel: string;
34
+ messagePlaceholder: string;
35
+ subscribeLabel: string;
36
+ submit: string;
37
+ /** Under a field left empty or filled in wrongly. */
38
+ nameRequired: string;
39
+ emailRequired: string;
40
+ emailInvalid: string;
41
+ messageRequired: string;
42
+ /** The box above the fields when the form could not be sent: its three lines. */
43
+ errorTitle: string;
44
+ errorMessage: string;
45
+ errorRecovery: string;
46
+ }
47
+ export declare const DEFAULT_CONTACT_FORM_STRINGS: ContactFormStrings;
48
+ /** `PropsTable`. The caption is the `caption` prop. */
49
+ export interface PropsTableStrings {
50
+ name: string;
51
+ type: string;
52
+ required: string;
53
+ default: string;
54
+ description: string;
55
+ yes: string;
56
+ no: string;
57
+ /** Shown instead of the table when there are no props. */
58
+ empty: string;
59
+ }
60
+ export declare const DEFAULT_PROPS_TABLE_STRINGS: PropsTableStrings;
61
+ /** `ComponentDemo`. */
62
+ export interface ComponentDemoStrings {
63
+ /** The two tabs, and what the switch between them is called. */
64
+ preview: string;
65
+ code: string;
66
+ showPreview: string;
67
+ showCode: string;
68
+ }
69
+ export declare const DEFAULT_COMPONENT_DEMO_STRINGS: ComponentDemoStrings;
70
+ /**
71
+ * Lays `strings` layers over a component's defaults, later layers winning. A
72
+ * key whose value is `undefined` is skipped, so `strings={{ close: t.close }}`
73
+ * with `t.close` missing keeps the default instead of blanking it. `null` and
74
+ * the empty string are kept: those are choices.
75
+ */
76
+ export declare function mergeStrings<T extends object>(defaults: T, ...layers: Array<Partial<T> | null | undefined>): T;
@@ -0,0 +1,67 @@
1
+ /**
2
+ * The words of the components that come with text of their own: the colour
3
+ * picker, the ready-made contact form, and the two the docs site is built
4
+ * from. Each takes its own as `strings`; what is left out stays as it is here.
5
+ */
6
+ export const DEFAULT_COLOR_PICKER_STRINGS = {
7
+ hexInput: "Hex color input",
8
+ open: "Open color picker",
9
+ picker: "Color picker",
10
+ hue: "Hue slider",
11
+ area: "Saturation and lightness",
12
+ saturation: "Saturation",
13
+ lightness: "Lightness",
14
+ invalidHex: "Please enter a valid hex color (e.g., #ff0000 or #f00)",
15
+ };
16
+ export const DEFAULT_CONTACT_FORM_STRINGS = {
17
+ heading: "Get in Touch",
18
+ nameLabel: "Name",
19
+ namePlaceholder: "Enter your name",
20
+ emailLabel: "Email",
21
+ emailPlaceholder: "Enter your email",
22
+ messageLabel: "Message",
23
+ messagePlaceholder: "Enter your message",
24
+ subscribeLabel: "Subscribe to updates",
25
+ submit: "Send Message",
26
+ nameRequired: "Please enter your name.",
27
+ emailRequired: "Please enter your email address.",
28
+ emailInvalid: "Please enter a valid email address.",
29
+ messageRequired: "Please enter a message.",
30
+ errorTitle: "Something went wrong",
31
+ errorMessage: "We could not submit your request. Fix the highlighted fields and try again.",
32
+ errorRecovery: "Recovery action: review your inputs and resubmit.",
33
+ };
34
+ export const DEFAULT_PROPS_TABLE_STRINGS = {
35
+ name: "Name",
36
+ type: "Type",
37
+ required: "Required",
38
+ default: "Default",
39
+ description: "Description",
40
+ yes: "Yes",
41
+ no: "No",
42
+ empty: "No documented props.",
43
+ };
44
+ export const DEFAULT_COMPONENT_DEMO_STRINGS = {
45
+ preview: "Preview",
46
+ code: "Code",
47
+ showPreview: "Show preview",
48
+ showCode: "Show code",
49
+ };
50
+ /**
51
+ * Lays `strings` layers over a component's defaults, later layers winning. A
52
+ * key whose value is `undefined` is skipped, so `strings={{ close: t.close }}`
53
+ * with `t.close` missing keeps the default instead of blanking it. `null` and
54
+ * the empty string are kept: those are choices.
55
+ */
56
+ export function mergeStrings(defaults, ...layers) {
57
+ const merged = { ...defaults };
58
+ for (const layer of layers) {
59
+ if (!layer)
60
+ continue;
61
+ for (const [key, value] of Object.entries(layer)) {
62
+ if (value !== undefined)
63
+ merged[key] = value;
64
+ }
65
+ }
66
+ return merged;
67
+ }
@@ -0,0 +1,19 @@
1
+ /**
2
+ * `scroll-padding-bottom` on a scrolling box, shared between the things that
3
+ * lie over its bottom edge.
4
+ *
5
+ * Each of them saves what was there and puts it back when it leaves. Done one
6
+ * by one that goes wrong as soon as two overlap in time: the second saves the
7
+ * first one's value as "what was there", and if the first leaves first, the
8
+ * second later restores a value nobody owns any more. Here the box's own
9
+ * value is saved once, the largest request is what is set, and the box gets
10
+ * its own value back when the last request is released.
11
+ */
12
+ /**
13
+ * Asks for room at the bottom of `container`. `set` gives or changes the
14
+ * amount in px; `release` withdraws the request. Call it in the browser.
15
+ */
16
+ export declare function reserveScrollPaddingBottom(container: HTMLElement): {
17
+ set: (px: number) => void;
18
+ release: () => void;
19
+ };
@@ -0,0 +1,44 @@
1
+ /**
2
+ * `scroll-padding-bottom` on a scrolling box, shared between the things that
3
+ * lie over its bottom edge.
4
+ *
5
+ * Each of them saves what was there and puts it back when it leaves. Done one
6
+ * by one that goes wrong as soon as two overlap in time: the second saves the
7
+ * first one's value as "what was there", and if the first leaves first, the
8
+ * second later restores a value nobody owns any more. Here the box's own
9
+ * value is saved once, the largest request is what is set, and the box gets
10
+ * its own value back when the last request is released.
11
+ */
12
+ const reservations = new WeakMap();
13
+ function apply(container, reservation) {
14
+ const largest = Math.max(0, ...reservation.requests.values());
15
+ container.style.scrollPaddingBottom = `${largest}px`;
16
+ }
17
+ /**
18
+ * Asks for room at the bottom of `container`. `set` gives or changes the
19
+ * amount in px; `release` withdraws the request. Call it in the browser.
20
+ */
21
+ export function reserveScrollPaddingBottom(container) {
22
+ const key = Symbol("scroll-padding");
23
+ let reservation = reservations.get(container);
24
+ if (!reservation) {
25
+ reservation = { original: container.style.scrollPaddingBottom, requests: new Map() };
26
+ reservations.set(container, reservation);
27
+ }
28
+ const mine = reservation;
29
+ return {
30
+ set(px) {
31
+ mine.requests.set(key, px);
32
+ apply(container, mine);
33
+ },
34
+ release() {
35
+ mine.requests.delete(key);
36
+ if (mine.requests.size > 0) {
37
+ apply(container, mine);
38
+ return;
39
+ }
40
+ container.style.scrollPaddingBottom = mine.original;
41
+ reservations.delete(container);
42
+ },
43
+ };
44
+ }