@thomasfosterau/components 0.0.0-stage → 0.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 (340) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +817 -2
  3. package/dist/canvas/VectorCanvas.svelte +239 -0
  4. package/dist/canvas/VectorCanvas.svelte.d.ts +87 -0
  5. package/dist/canvas/history.d.ts +105 -0
  6. package/dist/canvas/history.js +147 -0
  7. package/dist/canvas/index.d.ts +15 -0
  8. package/dist/canvas/index.js +14 -0
  9. package/dist/canvas/types.d.ts +91 -0
  10. package/dist/canvas/types.js +13 -0
  11. package/dist/canvas/utils.d.ts +66 -0
  12. package/dist/canvas/utils.js +114 -0
  13. package/dist/charts/Chart.svelte +300 -0
  14. package/dist/charts/Chart.svelte.d.ts +50 -0
  15. package/dist/charts/index.d.ts +11 -0
  16. package/dist/charts/index.js +10 -0
  17. package/dist/charts/types.d.ts +28 -0
  18. package/dist/charts/types.js +6 -0
  19. package/dist/citations/BookCitation.svelte +53 -0
  20. package/dist/citations/BookCitation.svelte.d.ts +41 -0
  21. package/dist/citations/ChapterCitation.svelte +38 -0
  22. package/dist/citations/ChapterCitation.svelte.d.ts +50 -0
  23. package/dist/citations/Citation.svelte +47 -0
  24. package/dist/citations/Citation.svelte.d.ts +24 -0
  25. package/dist/citations/CreditList.svelte +69 -0
  26. package/dist/citations/CreditList.svelte.d.ts +47 -0
  27. package/dist/citations/DOILink.svelte +28 -0
  28. package/dist/citations/DOILink.svelte.d.ts +19 -0
  29. package/dist/citations/Footnote.svelte +39 -0
  30. package/dist/citations/Footnote.svelte.d.ts +17 -0
  31. package/dist/citations/FootnoteList.svelte +36 -0
  32. package/dist/citations/FootnoteList.svelte.d.ts +21 -0
  33. package/dist/citations/ISBNLink.svelte +29 -0
  34. package/dist/citations/ISBNLink.svelte.d.ts +19 -0
  35. package/dist/citations/ISSNLink.svelte +29 -0
  36. package/dist/citations/ISSNLink.svelte.d.ts +19 -0
  37. package/dist/citations/Publisher.svelte +34 -0
  38. package/dist/citations/Publisher.svelte.d.ts +14 -0
  39. package/dist/citations/ReferenceList.svelte +53 -0
  40. package/dist/citations/ReferenceList.svelte.d.ts +21 -0
  41. package/dist/citations/ReportCitation.svelte +41 -0
  42. package/dist/citations/ReportCitation.svelte.d.ts +36 -0
  43. package/dist/citations/ScholarlyArticleCitation.svelte +64 -0
  44. package/dist/citations/ScholarlyArticleCitation.svelte.d.ts +41 -0
  45. package/dist/citations/ThesisCitation.svelte +35 -0
  46. package/dist/citations/ThesisCitation.svelte.d.ts +29 -0
  47. package/dist/citations/bibliography.svelte.d.ts +55 -0
  48. package/dist/citations/bibliography.svelte.js +78 -0
  49. package/dist/citations/citation.d.ts +71 -0
  50. package/dist/citations/citation.js +121 -0
  51. package/dist/citations/csl.d.ts +102 -0
  52. package/dist/citations/csl.js +20 -0
  53. package/dist/citations/index.d.ts +31 -0
  54. package/dist/citations/index.js +36 -0
  55. package/dist/citations/list-format.d.ts +34 -0
  56. package/dist/citations/list-format.js +38 -0
  57. package/dist/context.svelte.d.ts +46 -0
  58. package/dist/context.svelte.js +51 -0
  59. package/dist/data/DataGrid.svelte +150 -0
  60. package/dist/data/DataGrid.svelte.d.ts +118 -0
  61. package/dist/data/DescriptionList.svelte +47 -0
  62. package/dist/data/DescriptionList.svelte.d.ts +26 -0
  63. package/dist/data/DescriptionRow.svelte +55 -0
  64. package/dist/data/DescriptionRow.svelte.d.ts +21 -0
  65. package/dist/data/DiffText.svelte +29 -0
  66. package/dist/data/DiffText.svelte.d.ts +19 -0
  67. package/dist/data/ItemList.svelte +80 -0
  68. package/dist/data/ItemList.svelte.d.ts +52 -0
  69. package/dist/data/LinkListItem.svelte +46 -0
  70. package/dist/data/LinkListItem.svelte.d.ts +21 -0
  71. package/dist/data/Pagination.svelte +136 -0
  72. package/dist/data/Pagination.svelte.d.ts +71 -0
  73. package/dist/data/PaginationFooter.svelte +41 -0
  74. package/dist/data/PaginationFooter.svelte.d.ts +59 -0
  75. package/dist/data/ResultCount.svelte +27 -0
  76. package/dist/data/ResultCount.svelte.d.ts +18 -0
  77. package/dist/data/Table.svelte +145 -0
  78. package/dist/data/Table.svelte.d.ts +75 -0
  79. package/dist/data/TreeList.svelte +41 -0
  80. package/dist/data/TreeList.svelte.d.ts +17 -0
  81. package/dist/data/TreeListItem.svelte +29 -0
  82. package/dist/data/TreeListItem.svelte.d.ts +10 -0
  83. package/dist/data/description-context.d.ts +33 -0
  84. package/dist/data/description-context.js +35 -0
  85. package/dist/data/diff.d.ts +14 -0
  86. package/dist/data/diff.js +10 -0
  87. package/dist/data/index.d.ts +35 -0
  88. package/dist/data/index.js +35 -0
  89. package/dist/data/list-item.d.ts +30 -0
  90. package/dist/data/list-item.js +19 -0
  91. package/dist/data/pagination.d.ts +81 -0
  92. package/dist/data/pagination.js +79 -0
  93. package/dist/data/table-types.d.ts +64 -0
  94. package/dist/data/table-types.js +35 -0
  95. package/dist/feedback/ConfirmDialog.svelte +79 -0
  96. package/dist/feedback/ConfirmDialog.svelte.d.ts +82 -0
  97. package/dist/feedback/Dialog.svelte +77 -0
  98. package/dist/feedback/Dialog.svelte.d.ts +37 -0
  99. package/dist/feedback/LoadingBoundary.svelte +61 -0
  100. package/dist/feedback/LoadingBoundary.svelte.d.ts +34 -0
  101. package/dist/feedback/ProgressBar.svelte +46 -0
  102. package/dist/feedback/ProgressBar.svelte.d.ts +28 -0
  103. package/dist/feedback/SegmentBar.svelte +83 -0
  104. package/dist/feedback/SegmentBar.svelte.d.ts +36 -0
  105. package/dist/feedback/StatTile.svelte +77 -0
  106. package/dist/feedback/StatTile.svelte.d.ts +49 -0
  107. package/dist/feedback/Stats.svelte +18 -0
  108. package/dist/feedback/Stats.svelte.d.ts +16 -0
  109. package/dist/feedback/Timeline.svelte +86 -0
  110. package/dist/feedback/Timeline.svelte.d.ts +52 -0
  111. package/dist/feedback/Toaster.svelte +78 -0
  112. package/dist/feedback/Toaster.svelte.d.ts +32 -0
  113. package/dist/feedback/index.d.ts +31 -0
  114. package/dist/feedback/index.js +27 -0
  115. package/dist/feedback/sizes.d.ts +27 -0
  116. package/dist/feedback/sizes.js +41 -0
  117. package/dist/feedback/toaster.d.ts +48 -0
  118. package/dist/feedback/toaster.js +29 -0
  119. package/dist/fields/Checkbox.svelte +33 -0
  120. package/dist/fields/Checkbox.svelte.d.ts +24 -0
  121. package/dist/fields/CheckboxGroup.svelte +41 -0
  122. package/dist/fields/CheckboxGroup.svelte.d.ts +40 -0
  123. package/dist/fields/Combobox.svelte +222 -0
  124. package/dist/fields/Combobox.svelte.d.ts +98 -0
  125. package/dist/fields/DateInput.svelte +31 -0
  126. package/dist/fields/DateInput.svelte.d.ts +29 -0
  127. package/dist/fields/FieldErrors.svelte +30 -0
  128. package/dist/fields/FieldErrors.svelte.d.ts +11 -0
  129. package/dist/fields/Fieldset.svelte +48 -0
  130. package/dist/fields/Fieldset.svelte.d.ts +26 -0
  131. package/dist/fields/FileInput.svelte +67 -0
  132. package/dist/fields/FileInput.svelte.d.ts +33 -0
  133. package/dist/fields/FormField.svelte +54 -0
  134. package/dist/fields/FormField.svelte.d.ts +29 -0
  135. package/dist/fields/ImageInput.svelte +87 -0
  136. package/dist/fields/ImageInput.svelte.d.ts +39 -0
  137. package/dist/fields/PushToggle.svelte +93 -0
  138. package/dist/fields/PushToggle.svelte.d.ts +55 -0
  139. package/dist/fields/RadioGroup.svelte +85 -0
  140. package/dist/fields/RadioGroup.svelte.d.ts +41 -0
  141. package/dist/fields/SearchInput.svelte +57 -0
  142. package/dist/fields/SearchInput.svelte.d.ts +33 -0
  143. package/dist/fields/Select.svelte +36 -0
  144. package/dist/fields/Select.svelte.d.ts +29 -0
  145. package/dist/fields/TextInput.svelte +26 -0
  146. package/dist/fields/TextInput.svelte.d.ts +30 -0
  147. package/dist/fields/Textarea.svelte +22 -0
  148. package/dist/fields/Textarea.svelte.d.ts +25 -0
  149. package/dist/fields/Toggle.svelte +72 -0
  150. package/dist/fields/Toggle.svelte.d.ts +32 -0
  151. package/dist/fields/VideoInput.svelte +35 -0
  152. package/dist/fields/VideoInput.svelte.d.ts +40 -0
  153. package/dist/fields/field-types.d.ts +51 -0
  154. package/dist/fields/field-types.js +60 -0
  155. package/dist/fields/index.d.ts +34 -0
  156. package/dist/fields/index.js +34 -0
  157. package/dist/formatting/DateTime.svelte +35 -0
  158. package/dist/formatting/DateTime.svelte.d.ts +26 -0
  159. package/dist/formatting/FormatList.svelte +31 -0
  160. package/dist/formatting/FormatList.svelte.d.ts +46 -0
  161. package/dist/formatting/Highlight.svelte +47 -0
  162. package/dist/formatting/Highlight.svelte.d.ts +15 -0
  163. package/dist/formatting/Number.svelte +35 -0
  164. package/dist/formatting/Number.svelte.d.ts +43 -0
  165. package/dist/formatting/RelativeTime.svelte +69 -0
  166. package/dist/formatting/RelativeTime.svelte.d.ts +26 -0
  167. package/dist/formatting/Year.svelte +39 -0
  168. package/dist/formatting/Year.svelte.d.ts +21 -0
  169. package/dist/formatting/index.d.ts +12 -0
  170. package/dist/formatting/index.js +14 -0
  171. package/dist/forms/EntityForm.svelte +177 -0
  172. package/dist/forms/EntityForm.svelte.d.ts +57 -0
  173. package/dist/forms/FormActions.svelte +29 -0
  174. package/dist/forms/FormActions.svelte.d.ts +37 -0
  175. package/dist/forms/FormErrors.svelte +21 -0
  176. package/dist/forms/FormErrors.svelte.d.ts +16 -0
  177. package/dist/forms/HiddenField.svelte +22 -0
  178. package/dist/forms/HiddenField.svelte.d.ts +11 -0
  179. package/dist/forms/SearchForm.svelte +43 -0
  180. package/dist/forms/SearchForm.svelte.d.ts +36 -0
  181. package/dist/forms/entity-form.d.ts +12 -0
  182. package/dist/forms/entity-form.js +12 -0
  183. package/dist/forms/index.d.ts +14 -0
  184. package/dist/forms/index.js +14 -0
  185. package/dist/hotkeys/HotkeyBadge.svelte +39 -0
  186. package/dist/hotkeys/HotkeyBadge.svelte.d.ts +25 -0
  187. package/dist/hotkeys/HotkeyHelpDialog.svelte +68 -0
  188. package/dist/hotkeys/HotkeyHelpDialog.svelte.d.ts +35 -0
  189. package/dist/hotkeys/HotkeyProvider.svelte +92 -0
  190. package/dist/hotkeys/HotkeyProvider.svelte.d.ts +4 -0
  191. package/dist/hotkeys/context.svelte.d.ts +47 -0
  192. package/dist/hotkeys/context.svelte.js +72 -0
  193. package/dist/hotkeys/index.d.ts +23 -0
  194. package/dist/hotkeys/index.js +22 -0
  195. package/dist/hotkeys/match.d.ts +65 -0
  196. package/dist/hotkeys/match.js +133 -0
  197. package/dist/hotkeys/registry.svelte.d.ts +55 -0
  198. package/dist/hotkeys/registry.svelte.js +110 -0
  199. package/dist/hotkeys/types.d.ts +72 -0
  200. package/dist/hotkeys/types.js +18 -0
  201. package/dist/index.d.ts +36 -0
  202. package/dist/index.js +36 -0
  203. package/dist/infobox/Infobox.svelte +79 -0
  204. package/dist/infobox/Infobox.svelte.d.ts +26 -0
  205. package/dist/infobox/InfoboxHeader.svelte +27 -0
  206. package/dist/infobox/InfoboxHeader.svelte.d.ts +11 -0
  207. package/dist/infobox/InfoboxImage.svelte +38 -0
  208. package/dist/infobox/InfoboxImage.svelte.d.ts +21 -0
  209. package/dist/infobox/InfoboxRow.svelte +50 -0
  210. package/dist/infobox/InfoboxRow.svelte.d.ts +25 -0
  211. package/dist/infobox/InfoboxSection.svelte +24 -0
  212. package/dist/infobox/InfoboxSection.svelte.d.ts +12 -0
  213. package/dist/infobox/index.d.ts +21 -0
  214. package/dist/infobox/index.js +20 -0
  215. package/dist/infobox/types.d.ts +86 -0
  216. package/dist/infobox/types.js +27 -0
  217. package/dist/layout/Breadcrumbs.svelte +52 -0
  218. package/dist/layout/Breadcrumbs.svelte.d.ts +18 -0
  219. package/dist/layout/DetailLayout.svelte +50 -0
  220. package/dist/layout/DetailLayout.svelte.d.ts +19 -0
  221. package/dist/layout/FilterBar.svelte +40 -0
  222. package/dist/layout/FilterBar.svelte.d.ts +12 -0
  223. package/dist/layout/Footer.svelte +32 -0
  224. package/dist/layout/Footer.svelte.d.ts +12 -0
  225. package/dist/layout/Main.svelte +30 -0
  226. package/dist/layout/Main.svelte.d.ts +15 -0
  227. package/dist/layout/Navbar.svelte +68 -0
  228. package/dist/layout/Navbar.svelte.d.ts +31 -0
  229. package/dist/layout/PageHeader.svelte +77 -0
  230. package/dist/layout/PageHeader.svelte.d.ts +33 -0
  231. package/dist/layout/Section.svelte +72 -0
  232. package/dist/layout/Section.svelte.d.ts +40 -0
  233. package/dist/layout/Sidebar.svelte +54 -0
  234. package/dist/layout/Sidebar.svelte.d.ts +27 -0
  235. package/dist/layout/Tabs.svelte +85 -0
  236. package/dist/layout/Tabs.svelte.d.ts +42 -0
  237. package/dist/layout/index.d.ts +29 -0
  238. package/dist/layout/index.js +29 -0
  239. package/dist/layout/types.d.ts +52 -0
  240. package/dist/layout/types.js +27 -0
  241. package/dist/links/ContextLinks.svelte +28 -0
  242. package/dist/links/ContextLinks.svelte.d.ts +20 -0
  243. package/dist/links/EmailLink.svelte +26 -0
  244. package/dist/links/EmailLink.svelte.d.ts +20 -0
  245. package/dist/links/index.d.ts +7 -0
  246. package/dist/links/index.js +7 -0
  247. package/dist/media/Figure.svelte +26 -0
  248. package/dist/media/Figure.svelte.d.ts +12 -0
  249. package/dist/media/Image.svelte +76 -0
  250. package/dist/media/Image.svelte.d.ts +41 -0
  251. package/dist/media/Video.svelte +65 -0
  252. package/dist/media/Video.svelte.d.ts +29 -0
  253. package/dist/media/image-dimensions.d.ts +28 -0
  254. package/dist/media/image-dimensions.js +34 -0
  255. package/dist/media/index.d.ts +10 -0
  256. package/dist/media/index.js +10 -0
  257. package/dist/search/CommandPalette.svelte +257 -0
  258. package/dist/search/CommandPalette.svelte.d.ts +59 -0
  259. package/dist/search/index.d.ts +13 -0
  260. package/dist/search/index.js +13 -0
  261. package/dist/search/types.d.ts +37 -0
  262. package/dist/search/types.js +20 -0
  263. package/dist/seo/Head.svelte +190 -0
  264. package/dist/seo/Head.svelte.d.ts +86 -0
  265. package/dist/seo/JsonLd.svelte +30 -0
  266. package/dist/seo/JsonLd.svelte.d.ts +20 -0
  267. package/dist/seo/index.d.ts +18 -0
  268. package/dist/seo/index.js +17 -0
  269. package/dist/seo/json-ld.d.ts +33 -0
  270. package/dist/seo/json-ld.js +29 -0
  271. package/dist/seo/og-image.d.ts +71 -0
  272. package/dist/seo/og-image.js +81 -0
  273. package/dist/seo/types.d.ts +43 -0
  274. package/dist/seo/types.js +6 -0
  275. package/dist/theme.css +161 -0
  276. package/dist/tips/PopoverTip.svelte +80 -0
  277. package/dist/tips/PopoverTip.svelte.d.ts +33 -0
  278. package/dist/tips/Tip.svelte +91 -0
  279. package/dist/tips/Tip.svelte.d.ts +25 -0
  280. package/dist/tips/index.d.ts +15 -0
  281. package/dist/tips/index.js +14 -0
  282. package/dist/tips/tip-stack.d.ts +25 -0
  283. package/dist/tips/tip-stack.js +37 -0
  284. package/dist/tips/tip-store.svelte.d.ts +74 -0
  285. package/dist/tips/tip-store.svelte.js +158 -0
  286. package/dist/tips/types.d.ts +46 -0
  287. package/dist/tips/types.js +6 -0
  288. package/dist/ui/Alert.svelte +61 -0
  289. package/dist/ui/Alert.svelte.d.ts +37 -0
  290. package/dist/ui/Badge.svelte +39 -0
  291. package/dist/ui/Badge.svelte.d.ts +32 -0
  292. package/dist/ui/Button.svelte +43 -0
  293. package/dist/ui/Button.svelte.d.ts +29 -0
  294. package/dist/ui/Card.svelte +49 -0
  295. package/dist/ui/Card.svelte.d.ts +44 -0
  296. package/dist/ui/Chip.svelte +58 -0
  297. package/dist/ui/Chip.svelte.d.ts +21 -0
  298. package/dist/ui/ConfirmButton.svelte +75 -0
  299. package/dist/ui/ConfirmButton.svelte.d.ts +48 -0
  300. package/dist/ui/Disclosure.svelte +39 -0
  301. package/dist/ui/Disclosure.svelte.d.ts +22 -0
  302. package/dist/ui/EmptyState.svelte +37 -0
  303. package/dist/ui/EmptyState.svelte.d.ts +22 -0
  304. package/dist/ui/ExternalLink.svelte +38 -0
  305. package/dist/ui/ExternalLink.svelte.d.ts +34 -0
  306. package/dist/ui/Heading.svelte +58 -0
  307. package/dist/ui/Heading.svelte.d.ts +22 -0
  308. package/dist/ui/Link.svelte +34 -0
  309. package/dist/ui/Link.svelte.d.ts +20 -0
  310. package/dist/ui/LinkButton.svelte +51 -0
  311. package/dist/ui/LinkButton.svelte.d.ts +33 -0
  312. package/dist/ui/Menu.svelte +54 -0
  313. package/dist/ui/Menu.svelte.d.ts +34 -0
  314. package/dist/ui/MenuItem.svelte +75 -0
  315. package/dist/ui/MenuItem.svelte.d.ts +21 -0
  316. package/dist/ui/MenuSeparator.svelte +26 -0
  317. package/dist/ui/MenuSeparator.svelte.d.ts +8 -0
  318. package/dist/ui/Popover.svelte +63 -0
  319. package/dist/ui/Popover.svelte.d.ts +40 -0
  320. package/dist/ui/ShareButton.svelte +64 -0
  321. package/dist/ui/ShareButton.svelte.d.ts +39 -0
  322. package/dist/ui/Skeleton.svelte +43 -0
  323. package/dist/ui/Skeleton.svelte.d.ts +22 -0
  324. package/dist/ui/ThemeToggle.svelte +65 -0
  325. package/dist/ui/ThemeToggle.svelte.d.ts +24 -0
  326. package/dist/ui/Tooltip.svelte +54 -0
  327. package/dist/ui/Tooltip.svelte.d.ts +35 -0
  328. package/dist/ui/VisuallyHidden.svelte +23 -0
  329. package/dist/ui/VisuallyHidden.svelte.d.ts +10 -0
  330. package/dist/ui/colour-token.d.ts +41 -0
  331. package/dist/ui/colour-token.js +49 -0
  332. package/dist/ui/index.d.ts +44 -0
  333. package/dist/ui/index.js +44 -0
  334. package/dist/ui/menu-context.d.ts +22 -0
  335. package/dist/ui/menu-context.js +26 -0
  336. package/dist/ui/slug.d.ts +19 -0
  337. package/dist/ui/slug.js +25 -0
  338. package/dist/ui/tones.d.ts +68 -0
  339. package/dist/ui/tones.js +150 -0
  340. package/package.json +101 -3
@@ -0,0 +1,78 @@
1
+ <!-- @component
2
+ The notification region, plus the context that lets any descendant push a toast into it (`getToaster().add({ title: 'Saved' })`).
3
+ -->
4
+ <!--
5
+ Toaster — the notification region, plus the context that lets any descendant
6
+ push into it (`getToaster().add({ title: 'Saved' })`).
7
+
8
+ Hand-rolled rather than on a bits-ui primitive, deliberately: there is no
9
+ interaction to get right. A toast takes no focus, traps nothing and has no
10
+ keyboard contract of its own — it is a live region with a queue and a timer,
11
+ and the queue is a plain rune.
12
+
13
+ ARIA contract: ONE `aria-live="polite"` region, named by `regionLabel`, and
14
+ nothing live inside it. The tempting `role="status"` on each toast is the
15
+ bug this replaces: a status inside a live region makes assistive technology
16
+ announce the same text twice, and a region that is only created when the
17
+ first toast arrives is often not announced at all — so the region is in the
18
+ document from mount, empty, and only its contents change. Dismiss is a
19
+ labelled text button, not a bare glyph.
20
+
21
+ Timers are held in a map and cleared on destroy: a toast that outlives its
22
+ page would otherwise write to a component that is gone.
23
+ -->
24
+ <script lang="ts">import { alertTone } from "../ui/tones.js";
25
+ import Button from "../ui/Button.svelte";
26
+ import { toastPosition } from "./sizes.js";
27
+ import { setToaster } from "./toaster.js";
28
+ let { position = "bottom-end", regionLabel = "Notifications", dismissLabel = "Dismiss", durationMs = 6e3, class: className, children } = $props();
29
+ let toasts = $state([]);
30
+ let nextId = 1;
31
+ const timers = new Map();
32
+ const dismiss = (id) => {
33
+ const timer = timers.get(id);
34
+ if (timer !== undefined) {
35
+ clearTimeout(timer);
36
+ timers.delete(id);
37
+ }
38
+ toasts = toasts.filter((toast) => toast.id !== id);
39
+ };
40
+ const api = {
41
+ add(toast, duration = durationMs) {
42
+ const id = nextId++;
43
+ toasts = [...toasts, {
44
+ ...toast,
45
+ tone: toast.tone ?? "info",
46
+ id
47
+ }];
48
+ if (duration > 0) timers.set(id, setTimeout(() => dismiss(id), duration));
49
+ return id;
50
+ },
51
+ dismiss
52
+ };
53
+ setToaster(api);
54
+ $effect(() => () => {
55
+ for (const timer of timers.values()) clearTimeout(timer);
56
+ timers.clear();
57
+ });
58
+ </script>
59
+
60
+ {@render children()}
61
+
62
+ <div
63
+ class={['toast z-50', toastPosition[position], className]}
64
+ aria-live="polite"
65
+ aria-label={regionLabel}
66
+ >
67
+ {#each toasts as toast (toast.id)}
68
+ <div class={['alert items-start shadow-lg', alertTone[toast.tone]]}>
69
+ <div>
70
+ <p class="font-semibold">{toast.title}</p>
71
+ {#if toast.description !== undefined}
72
+ <p class="text-sm">{toast.description}</p>
73
+ {/if}
74
+ </div>
75
+ <Button variant="ghost" size="sm" onclick={() => dismiss(toast.id)}>{dismissLabel}</Button>
76
+ </div>
77
+ {/each}
78
+ </div>
@@ -0,0 +1,32 @@
1
+ import type { Snippet } from 'svelte';
2
+ import { type ToastPosition } from './sizes.ts';
3
+ interface Props {
4
+ /**
5
+ * Which corner the stack grows from.
6
+ * @defaultValue `'bottom-end'`
7
+ */
8
+ position?: ToastPosition | undefined;
9
+ /**
10
+ * Accessible name of the live region.
11
+ * @defaultValue `'Notifications'`
12
+ */
13
+ regionLabel?: string | undefined;
14
+ /**
15
+ * Accessible name of every toast's dismiss button.
16
+ * @defaultValue `'Dismiss'`
17
+ */
18
+ dismissLabel?: string | undefined;
19
+ /**
20
+ * How long a toast lives when `add` is given no duration. `0` = sticky.
21
+ * @defaultValue `6_000`
22
+ */
23
+ durationMs?: number | undefined;
24
+ /** Extra classes on the region. */
25
+ class?: string | undefined;
26
+ /** The application. Rendered above the region. */
27
+ children: Snippet<[]>;
28
+ }
29
+ /** The notification region, plus the context that lets any descendant push a toast into it (`getToaster().add({ title: 'Saved' })`). */
30
+ declare const Toaster: import("svelte").Component<Props, {}, "">;
31
+ type Toaster = ReturnType<typeof Toaster>;
32
+ export default Toaster;
@@ -0,0 +1,31 @@
1
+ /**
2
+ * feedback — dialogs, toasts, progress and the chrome around a pending read.
3
+ *
4
+ * What these have in common is that they speak to the reader about something
5
+ * that just happened, or is happening now: a confirmation to give, a result
6
+ * to notice, a number to compare, a wait to explain. That makes the live
7
+ * regions and the announcement order the contract — see each component's
8
+ * header comment — rather than the markup.
9
+ *
10
+ * As in `ui/`, every optional prop is declared `T | undefined`: the workspace
11
+ * compiles with `exactOptionalPropertyTypes`, under which `prop?: T` rejects
12
+ * an explicitly passed `undefined`, and these components are composed by
13
+ * callers forwarding their own optional props straight through.
14
+ *
15
+ * @module
16
+ */
17
+ export { default as ConfirmDialog } from './ConfirmDialog.svelte';
18
+ export { default as Dialog } from './Dialog.svelte';
19
+ export { default as LoadingBoundary } from './LoadingBoundary.svelte';
20
+ export { default as ProgressBar } from './ProgressBar.svelte';
21
+ export { default as SegmentBar } from './SegmentBar.svelte';
22
+ export { default as StatTile } from './StatTile.svelte';
23
+ export { default as Stats } from './Stats.svelte';
24
+ export { default as Timeline } from './Timeline.svelte';
25
+ export { default as Toaster } from './Toaster.svelte';
26
+ export type { ConfirmForm } from './ConfirmDialog.svelte';
27
+ export type { BarSegment } from './SegmentBar.svelte';
28
+ export type { StatDelta } from './StatTile.svelte';
29
+ export type { TimelineDate, TimelineEntry } from './Timeline.svelte';
30
+ export * from './sizes.ts';
31
+ export * from './toaster.ts';
@@ -0,0 +1,27 @@
1
+ /**
2
+ * feedback — dialogs, toasts, progress and the chrome around a pending read.
3
+ *
4
+ * What these have in common is that they speak to the reader about something
5
+ * that just happened, or is happening now: a confirmation to give, a result
6
+ * to notice, a number to compare, a wait to explain. That makes the live
7
+ * regions and the announcement order the contract — see each component's
8
+ * header comment — rather than the markup.
9
+ *
10
+ * As in `ui/`, every optional prop is declared `T | undefined`: the workspace
11
+ * compiles with `exactOptionalPropertyTypes`, under which `prop?: T` rejects
12
+ * an explicitly passed `undefined`, and these components are composed by
13
+ * callers forwarding their own optional props straight through.
14
+ *
15
+ * @module
16
+ */
17
+ export { default as ConfirmDialog } from './ConfirmDialog.svelte';
18
+ export { default as Dialog } from './Dialog.svelte';
19
+ export { default as LoadingBoundary } from './LoadingBoundary.svelte';
20
+ export { default as ProgressBar } from './ProgressBar.svelte';
21
+ export { default as SegmentBar } from './SegmentBar.svelte';
22
+ export { default as StatTile } from './StatTile.svelte';
23
+ export { default as Stats } from './Stats.svelte';
24
+ export { default as Timeline } from './Timeline.svelte';
25
+ export { default as Toaster } from './Toaster.svelte';
26
+ export * from "./sizes.js";
27
+ export * from "./toaster.js";
@@ -0,0 +1,27 @@
1
+ /**
2
+ * The literal class tables this family looks its variants up in.
3
+ *
4
+ * Tailwind only generates classes it can see verbatim in source, so a
5
+ * template string (`max-w-${size}`, `toast-${position}`) emits nothing at
6
+ * build time. Every step is spelled out once here as a `Record<K, string>`
7
+ * and looked up — the same discipline as `ui/tones.ts`, kept local because
8
+ * these steps are this family's, not the whole library's.
9
+ *
10
+ * @module
11
+ */
12
+ /** Width steps of a `Dialog`'s `modal-box`. */
13
+ export type DialogSize = 'sm' | 'md' | 'lg' | 'xl';
14
+ /** Literal `max-w-*` class per `DialogSize`; a lookup table so Tailwind's scanner sees every class. */
15
+ export declare const dialogSize: Record<DialogSize, string>;
16
+ /** Where a `Toaster` stacks its notifications. */
17
+ export type ToastPosition = 'top-start' | 'top-center' | 'top-end' | 'bottom-start' | 'bottom-center' | 'bottom-end';
18
+ /** Literal daisyUI class per `ToastPosition` for `Toaster`; a lookup table so Tailwind's scanner sees every class. */
19
+ export declare const toastPosition: Record<ToastPosition, string>;
20
+ /** Height steps of a `SegmentBar`. */
21
+ export type BarSize = 'sm' | 'md' | 'lg';
22
+ /** Literal height class per `BarSize`; a lookup table so Tailwind's scanner sees every class. */
23
+ export declare const barSize: Record<BarSize, string>;
24
+ /** How a `Stats` row lays its tiles out. */
25
+ export type StatsOrientation = 'horizontal' | 'vertical' | 'responsive';
26
+ /** Literal daisyUI class per `StatsOrientation` for `Stats`; a lookup table so Tailwind's scanner sees every class. */
27
+ export declare const statsOrientation: Record<StatsOrientation, string>;
@@ -0,0 +1,41 @@
1
+ /**
2
+ * The literal class tables this family looks its variants up in.
3
+ *
4
+ * Tailwind only generates classes it can see verbatim in source, so a
5
+ * template string (`max-w-${size}`, `toast-${position}`) emits nothing at
6
+ * build time. Every step is spelled out once here as a `Record<K, string>`
7
+ * and looked up — the same discipline as `ui/tones.ts`, kept local because
8
+ * these steps are this family's, not the whole library's.
9
+ *
10
+ * @module
11
+ */
12
+ /** Literal `max-w-*` class per `DialogSize`; a lookup table so Tailwind's scanner sees every class. */
13
+ export const dialogSize = {
14
+ sm: 'max-w-sm',
15
+ md: 'max-w-lg',
16
+ lg: 'max-w-2xl',
17
+ xl: 'max-w-4xl',
18
+ };
19
+ /** Literal daisyUI class per `ToastPosition` for `Toaster`; a lookup table so Tailwind's scanner sees every class. */
20
+ export const toastPosition = {
21
+ 'top-start': 'toast-top toast-start',
22
+ 'top-center': 'toast-top toast-center',
23
+ 'top-end': 'toast-top toast-end',
24
+ 'bottom-start': 'toast-bottom toast-start',
25
+ 'bottom-center': 'toast-bottom toast-center',
26
+ 'bottom-end': 'toast-bottom toast-end',
27
+ };
28
+ /** Literal height class per `BarSize`; a lookup table so Tailwind's scanner sees every class. */
29
+ export const barSize = {
30
+ sm: 'h-2',
31
+ md: 'h-4',
32
+ lg: 'h-6',
33
+ };
34
+ /** Literal daisyUI class per `StatsOrientation` for `Stats`; a lookup table so Tailwind's scanner sees every class. */
35
+ export const statsOrientation = {
36
+ horizontal: 'stats-horizontal',
37
+ vertical: 'stats-vertical',
38
+ // Stacked on a phone, in a row from `sm` up — the shape every app's
39
+ // dashboard reached for by hand.
40
+ responsive: 'stats-vertical sm:stats-horizontal',
41
+ };
@@ -0,0 +1,48 @@
1
+ /**
2
+ * The toaster seam: what a toast is, and how anything below a `<Toaster>`
3
+ * reaches the one region on the page.
4
+ *
5
+ * Kept out of `Toaster.svelte` so a module that only wants to *push* a toast
6
+ * — a store, a form helper, another component — imports a `.ts` file rather
7
+ * than pulling a component (and its icons) onto its own path.
8
+ *
9
+ * @module
10
+ */
11
+ import type { StatusTone } from '../ui/tones.ts';
12
+ /** The colour a toast is drawn in; the same four states an `Alert` has. */
13
+ export type ToastTone = StatusTone;
14
+ /** One notification as the `Toaster` holds it, after it has been given an id. */
15
+ export interface Toast {
16
+ /** Assigned by the toaster; unique for the life of the page. */
17
+ readonly id: number;
18
+ /** The line that carries the message. */
19
+ readonly title: string;
20
+ /** Optional second line. */
21
+ readonly description?: string | undefined;
22
+ readonly tone: ToastTone;
23
+ }
24
+ /** What a toast looks like before the toaster has given it an id. */
25
+ export type NewToast = Omit<Toast, 'id' | 'tone'> & {
26
+ readonly tone?: ToastTone | undefined;
27
+ };
28
+ /** What `getToaster()` returns: push a toast or dismiss one. */
29
+ export interface ToasterApi {
30
+ /**
31
+ * Push a toast and return its id. It dismisses itself after `durationMs`
32
+ * (the `<Toaster>`'s own default when omitted); `0` makes it sticky, for a
33
+ * message the reader must act on.
34
+ */
35
+ add(toast: NewToast, durationMs?: number): number;
36
+ /** Remove a toast early — the dismiss button, or the app itself. */
37
+ dismiss(id: number): void;
38
+ }
39
+ /** Publish the region's API to its descendants. Called by `Toaster`. */
40
+ export declare function setToaster(api: ToasterApi): void;
41
+ /**
42
+ * The nearest `<Toaster>`'s API.
43
+ *
44
+ * Throws when there is no provider: a silently dropped notification is worse
45
+ * than a loud failure in development, and every call site is inside the app's
46
+ * own layout where the provider is mounted once.
47
+ */
48
+ export declare function getToaster(): ToasterApi;
@@ -0,0 +1,29 @@
1
+ /**
2
+ * The toaster seam: what a toast is, and how anything below a `<Toaster>`
3
+ * reaches the one region on the page.
4
+ *
5
+ * Kept out of `Toaster.svelte` so a module that only wants to *push* a toast
6
+ * — a store, a form helper, another component — imports a `.ts` file rather
7
+ * than pulling a component (and its icons) onto its own path.
8
+ *
9
+ * @module
10
+ */
11
+ import { getContext, setContext } from 'svelte';
12
+ const TOASTER_KEY = Symbol('@thomasfosterau/components:toaster');
13
+ /** Publish the region's API to its descendants. Called by `Toaster`. */
14
+ export function setToaster(api) {
15
+ setContext(TOASTER_KEY, api);
16
+ }
17
+ /**
18
+ * The nearest `<Toaster>`'s API.
19
+ *
20
+ * Throws when there is no provider: a silently dropped notification is worse
21
+ * than a loud failure in development, and every call site is inside the app's
22
+ * own layout where the provider is mounted once.
23
+ */
24
+ export function getToaster() {
25
+ const api = getContext(TOASTER_KEY);
26
+ if (api === undefined)
27
+ throw new Error('getToaster() was called outside a <Toaster>');
28
+ return api;
29
+ }
@@ -0,0 +1,33 @@
1
+ <!-- @component
2
+ A daisyUI `checkbox` with its label in the same `<label>`, so the whole row is the hit target.
3
+ -->
4
+ <!--
5
+ Checkbox — daisyUI `checkbox` with its label in the same `<label>`, so the
6
+ whole row is the hit target and the input is named without an `id`/`for`
7
+ pair at all.
8
+
9
+ Unlike the text fields this one carries its OWN label: a checkbox's label
10
+ sits beside it rather than above it, and a `FormField` wrapped around a
11
+ single checkbox would put the question in two places. Use `CheckboxGroup`
12
+ (or `Fieldset`) when several checkboxes answer one question.
13
+
14
+ ARIA contract: a native `<input type="checkbox">` wrapped in its `<label>`,
15
+ so `checked` is the state assistive technology reads and Space toggles it
16
+ with no script. `describedBy` and `invalid` tie it to an enclosing group's
17
+ hint and errors when there is one.
18
+ -->
19
+ <script lang="ts">import { checkboxSize } from "./field-types.js";
20
+ let { label, checked = $bindable(false), size = "md", invalid = false, describedBy = "", class: className, ...rest } = $props();
21
+ </script>
22
+
23
+ <label class={['label cursor-pointer justify-start gap-3', className]}>
24
+ <input
25
+ {...rest}
26
+ type="checkbox"
27
+ bind:checked
28
+ aria-invalid={invalid ? 'true' : undefined}
29
+ aria-describedby={describedBy === '' ? undefined : describedBy}
30
+ class={['checkbox', checkboxSize[size], invalid && 'checkbox-error']}
31
+ />
32
+ <span>{label}</span>
33
+ </label>
@@ -0,0 +1,24 @@
1
+ import type { HTMLInputAttributes } from 'svelte/elements';
2
+ import { type FieldSize } from './field-types.ts';
3
+ interface Props extends Omit<HTMLInputAttributes, 'type' | 'checked' | 'size'> {
4
+ /** Text beside the box; becomes the input's accessible name. */
5
+ label: string;
6
+ /** Whether the box is ticked. Bindable. */
7
+ checked?: boolean | undefined;
8
+ /**
9
+ * daisyUI size step.
10
+ * @defaultValue `'md'`
11
+ */
12
+ size?: FieldSize | undefined;
13
+ /** `FieldControl`: the field is in error — sets `aria-invalid` and the error style. */
14
+ invalid?: boolean | undefined;
15
+ /**
16
+ * `FieldControl`: space-separated ids for `aria-describedby`.
17
+ * @defaultValue `''`
18
+ */
19
+ describedBy?: string | undefined;
20
+ }
21
+ /** A daisyUI `checkbox` with its label in the same `<label>`, so the whole row is the hit target. */
22
+ declare const Checkbox: import("svelte").Component<Props, {}, "checked">;
23
+ type Checkbox = ReturnType<typeof Checkbox>;
24
+ export default Checkbox;
@@ -0,0 +1,41 @@
1
+ <!-- @component
2
+ Several checkboxes answering one question, inside one `Fieldset`.
3
+ -->
4
+ <!--
5
+ CheckboxGroup — several checkboxes answering one question, inside one
6
+ `Fieldset`.
7
+
8
+ ARIA contract: the `<legend>` names the group and is repeated as focus moves
9
+ between the boxes; each box is named by its own inline label; the group's
10
+ hint and errors are on the `<fieldset>`, so they are read once for the group
11
+ rather than once per box.
12
+
13
+ `values` is the list of TICKED values, bindable. It is a plain array rather
14
+ than a `Set` because that is what a form's decoded body and a JSON payload
15
+ both look like, and because Svelte's deep reactivity tracks it.
16
+ -->
17
+ <script lang="ts">import Checkbox from "./Checkbox.svelte";
18
+ import Fieldset from "./Fieldset.svelte";
19
+ import { toFieldOptions } from "./field-types.js";
20
+ let { legend, options, values = $bindable([]), name, hint, errors = [], required = false, requiredLabel = "(required)", disabled = false, size = "md", ...rest } = $props();
21
+ const entries = $derived(toFieldOptions(options));
22
+ const invalid = $derived(errors.length > 0);
23
+ const toggle = (value, ticked) => {
24
+ values = ticked ? [...values, value] : values.filter((entry) => entry !== value);
25
+ };
26
+ </script>
27
+
28
+ <Fieldset {...rest} {legend} {hint} {errors} {required} {requiredLabel}>
29
+ {#each entries as option (option.value)}
30
+ <Checkbox
31
+ label={option.label}
32
+ {name}
33
+ {size}
34
+ {invalid}
35
+ value={option.value}
36
+ disabled={disabled || option.disabled === true}
37
+ checked={values.includes(option.value)}
38
+ onchange={(event) => toggle(option.value, event.currentTarget.checked)}
39
+ />
40
+ {/each}
41
+ </Fieldset>
@@ -0,0 +1,40 @@
1
+ import type { HTMLAttributes } from 'svelte/elements';
2
+ import { type FieldOptionInput, type FieldSize } from './field-types.ts';
3
+ interface Props extends Omit<HTMLAttributes<HTMLFieldSetElement>, 'children'> {
4
+ /** The question the boxes answer. */
5
+ legend: string;
6
+ /** Full options, or bare strings when value and label are the same. */
7
+ options: readonly FieldOptionInput[];
8
+ /**
9
+ * The ticked values.
10
+ * @defaultValue `[]`
11
+ */
12
+ values?: string[] | undefined;
13
+ /** Posted field name; every box shares it, as a multi-value field. */
14
+ name?: string | undefined;
15
+ /** Guidance rendered under the legend. */
16
+ hint?: string | undefined;
17
+ /**
18
+ * Validation messages; any at all marks the group invalid.
19
+ * @defaultValue `[]`
20
+ */
21
+ errors?: readonly string[] | undefined;
22
+ /** Marks the group required and reads `requiredLabel` after the legend. */
23
+ required?: boolean | undefined;
24
+ /**
25
+ * Read out after the legend when `required`.
26
+ * @defaultValue `'(required)'`
27
+ */
28
+ requiredLabel?: string | undefined;
29
+ /** Disables every option. */
30
+ disabled?: boolean | undefined;
31
+ /**
32
+ * daisyUI size step.
33
+ * @defaultValue `'md'`
34
+ */
35
+ size?: FieldSize | undefined;
36
+ }
37
+ /** Several checkboxes answering one question, inside one `Fieldset`. */
38
+ declare const CheckboxGroup: import("svelte").Component<Props, {}, "values">;
39
+ type CheckboxGroup = ReturnType<typeof CheckboxGroup>;
40
+ export default CheckboxGroup;