@thomasfosterau/components 0.0.0 → 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 +2 -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 +25 -25
@@ -0,0 +1,35 @@
1
+ /**
2
+ * data — the tables, lists and pagers an index page and a detail page are
3
+ * built from.
4
+ *
5
+ * Two shapes of list run through this family, and the split is worth stating
6
+ * once. `Table` and `ItemList` present a window a caller already chose:
7
+ * they never decide which rows exist, so they serve a server-paged collection
8
+ * of 40,000 as happily as a list of four. `DataGrid` is the other shape —
9
+ * it sorts, filters and pages a bounded set the page already holds, entirely
10
+ * on the client — and it composes the first two rather than duplicating them.
11
+ *
12
+ * Every optional prop in this family is declared `T | undefined`, not bare
13
+ * `T`: the workspace compiles with `exactOptionalPropertyTypes`, under which
14
+ * `prop?: T` rejects an explicitly passed `undefined`, and a caller
15
+ * forwarding its own optional prop straight through would not compile.
16
+ *
17
+ * @module
18
+ */
19
+ export { default as DataGrid } from './DataGrid.svelte';
20
+ export { default as DescriptionList } from './DescriptionList.svelte';
21
+ export { default as DescriptionRow } from './DescriptionRow.svelte';
22
+ export { default as DiffText } from './DiffText.svelte';
23
+ export { default as ItemList } from './ItemList.svelte';
24
+ export { default as LinkListItem } from './LinkListItem.svelte';
25
+ export { default as Pagination } from './Pagination.svelte';
26
+ export { default as PaginationFooter } from './PaginationFooter.svelte';
27
+ export { default as ResultCount } from './ResultCount.svelte';
28
+ export { default as Table } from './Table.svelte';
29
+ export { default as TreeList } from './TreeList.svelte';
30
+ export { default as TreeListItem } from './TreeListItem.svelte';
31
+ export * from './description-context.ts';
32
+ export * from './diff.ts';
33
+ export * from './list-item.ts';
34
+ export * from './pagination.ts';
35
+ export * from './table-types.ts';
@@ -0,0 +1,35 @@
1
+ /**
2
+ * data — the tables, lists and pagers an index page and a detail page are
3
+ * built from.
4
+ *
5
+ * Two shapes of list run through this family, and the split is worth stating
6
+ * once. `Table` and `ItemList` present a window a caller already chose:
7
+ * they never decide which rows exist, so they serve a server-paged collection
8
+ * of 40,000 as happily as a list of four. `DataGrid` is the other shape —
9
+ * it sorts, filters and pages a bounded set the page already holds, entirely
10
+ * on the client — and it composes the first two rather than duplicating them.
11
+ *
12
+ * Every optional prop in this family is declared `T | undefined`, not bare
13
+ * `T`: the workspace compiles with `exactOptionalPropertyTypes`, under which
14
+ * `prop?: T` rejects an explicitly passed `undefined`, and a caller
15
+ * forwarding its own optional prop straight through would not compile.
16
+ *
17
+ * @module
18
+ */
19
+ export { default as DataGrid } from './DataGrid.svelte';
20
+ export { default as DescriptionList } from './DescriptionList.svelte';
21
+ export { default as DescriptionRow } from './DescriptionRow.svelte';
22
+ export { default as DiffText } from './DiffText.svelte';
23
+ export { default as ItemList } from './ItemList.svelte';
24
+ export { default as LinkListItem } from './LinkListItem.svelte';
25
+ export { default as Pagination } from './Pagination.svelte';
26
+ export { default as PaginationFooter } from './PaginationFooter.svelte';
27
+ export { default as ResultCount } from './ResultCount.svelte';
28
+ export { default as Table } from './Table.svelte';
29
+ export { default as TreeList } from './TreeList.svelte';
30
+ export { default as TreeListItem } from './TreeListItem.svelte';
31
+ export * from "./description-context.js";
32
+ export * from "./diff.js";
33
+ export * from "./list-item.js";
34
+ export * from "./pagination.js";
35
+ export * from "./table-types.js";
@@ -0,0 +1,30 @@
1
+ /**
2
+ * The row shape an index page maps its data into before handing it to
3
+ * `ItemList`.
4
+ *
5
+ * A type, not a resource: the caller's own mappers turn a wire document into
6
+ * it, which is what keeps every API's shape out of this package. It lives in
7
+ * a `.ts` module rather than a `<script module>` block so a mapper — and the
8
+ * mapper's unit test — can import it without reaching through a component.
9
+ *
10
+ * Both optional members are declared `?: T | undefined` rather than bare
11
+ * `?: T`. The workspace compiles under `exactOptionalPropertyTypes`, where
12
+ * `{ subtitle: undefined }` is not the same thing as an absent key: with the
13
+ * narrow declaration every mapper reading a possibly-absent attribute has to
14
+ * spread the member conditionally, which is three lines and a comment at
15
+ * every call site. Widening it here is what removes them.
16
+ *
17
+ * @module
18
+ */
19
+ /** One row of a `LinkListItem`-shaped list. */
20
+ export interface LinkListItemData {
21
+ /** Stable identity — the list's `{#each}` key. */
22
+ readonly id: string;
23
+ /** Where the row's title links to; resolved through the components context. */
24
+ readonly href: string;
25
+ readonly title: string;
26
+ /** A line of context under the title — an owner, a parent, a date. */
27
+ readonly subtitle?: string | undefined;
28
+ /** Short markers rendered as badges (a visibility state, a kind). */
29
+ readonly badges?: readonly string[] | undefined;
30
+ }
@@ -0,0 +1,19 @@
1
+ /**
2
+ * The row shape an index page maps its data into before handing it to
3
+ * `ItemList`.
4
+ *
5
+ * A type, not a resource: the caller's own mappers turn a wire document into
6
+ * it, which is what keeps every API's shape out of this package. It lives in
7
+ * a `.ts` module rather than a `<script module>` block so a mapper — and the
8
+ * mapper's unit test — can import it without reaching through a component.
9
+ *
10
+ * Both optional members are declared `?: T | undefined` rather than bare
11
+ * `?: T`. The workspace compiles under `exactOptionalPropertyTypes`, where
12
+ * `{ subtitle: undefined }` is not the same thing as an absent key: with the
13
+ * narrow declaration every mapper reading a possibly-absent attribute has to
14
+ * spread the member conditionally, which is three lines and a comment at
15
+ * every call site. Widening it here is what removes them.
16
+ *
17
+ * @module
18
+ */
19
+ export {};
@@ -0,0 +1,81 @@
1
+ /**
2
+ * Offset / limit pagination arithmetic, and the href that carries it.
3
+ *
4
+ * Pure functions, deliberately: the window a pager renders (first page, last
5
+ * page, the current page's neighbours, the gaps between them) is arithmetic
6
+ * with a dozen edge cases — an offset past the end, a zero limit, a total of
7
+ * zero — and arithmetic is far cheaper to pin in a unit test than in a
8
+ * rendered component. `Pagination` is then markup over {@link pageWindow}.
9
+ *
10
+ * Ported from an Effect implementation with `Option` replaced by
11
+ * `undefined` and the query Schema left behind in the app that needs it:
12
+ * decoding an app's own URL is the app's job, and a component library that
13
+ * owned it would drag a validation library onto every consumer's page.
14
+ *
15
+ * @module
16
+ */
17
+ /** The JSON:API offset query parameter — the default {@link pageHref} writes. */
18
+ export declare const PAGE_OFFSET_PARAM = "page[offset]";
19
+ /** The JSON:API limit query parameter — the default {@link pageHref} writes. */
20
+ export declare const PAGE_LIMIT_PARAM = "page[limit]";
21
+ /** A resolved page position. */
22
+ export interface Page {
23
+ readonly offset: number;
24
+ readonly limit: number;
25
+ }
26
+ /** The query-parameter names {@link pageHref} writes the position into. */
27
+ export interface PageParamNames {
28
+ readonly offset?: string | undefined;
29
+ readonly limit?: string | undefined;
30
+ }
31
+ /**
32
+ * Build the href for `page` from `base` (a path, optionally with a query
33
+ * string and a fragment).
34
+ *
35
+ * Every other query parameter is preserved and the page parameters are
36
+ * replaced — paging through a filtered list that silently dropped the filter
37
+ * is the bug this shape exists to prevent. A relative `base` yields a
38
+ * relative href: the absolute origin below is a parsing scaffold, never part
39
+ * of the result.
40
+ */
41
+ export declare function pageHref(base: string, page: Page, names?: PageParamNames): string;
42
+ /** One entry in a rendered page window: a page link, or the gap before one. */
43
+ export type PageWindowItem = {
44
+ readonly kind: 'page';
45
+ /** 1-based page number, as rendered. */
46
+ readonly page: number;
47
+ /** The offset that page starts at. */
48
+ readonly offset: number;
49
+ readonly current: boolean;
50
+ } | {
51
+ readonly kind: 'gap'; /** The page number the gap follows. */
52
+ readonly after: number;
53
+ };
54
+ /** Everything a pager needs to draw itself, from one position and a total. */
55
+ export interface PageWindow {
56
+ /** 1-based current page. */
57
+ readonly page: number;
58
+ /** Total pages; at least 1, even with no rows. */
59
+ readonly pageCount: number;
60
+ /** 1-based index of the first row shown, or 0 when there are no rows. */
61
+ readonly from: number;
62
+ /** 1-based index of the last row shown, or 0 when there are no rows. */
63
+ readonly to: number;
64
+ /** Offset of the previous page, or `undefined` on the first. */
65
+ readonly previous: number | undefined;
66
+ /** Offset of the next page, or `undefined` on the last. */
67
+ readonly next: number | undefined;
68
+ /** First page, last page, and `siblings` pages either side of the current. */
69
+ readonly items: readonly PageWindowItem[];
70
+ }
71
+ /**
72
+ * Compute the window of page links to render around `page`: the first and
73
+ * last pages always, plus `siblings` pages either side of the current one,
74
+ * with a gap marker wherever the sequence skips.
75
+ *
76
+ * Tolerant by construction — a zero or negative limit, a negative offset and
77
+ * an offset past the end all resolve to a real page rather than throwing,
78
+ * because every one of them arrives from a hand-edited URL sooner or later
79
+ * and a 500 is a worse answer than the first page.
80
+ */
81
+ export declare function pageWindow(page: Page, total: number, siblings?: number): PageWindow;
@@ -0,0 +1,79 @@
1
+ /**
2
+ * Offset / limit pagination arithmetic, and the href that carries it.
3
+ *
4
+ * Pure functions, deliberately: the window a pager renders (first page, last
5
+ * page, the current page's neighbours, the gaps between them) is arithmetic
6
+ * with a dozen edge cases — an offset past the end, a zero limit, a total of
7
+ * zero — and arithmetic is far cheaper to pin in a unit test than in a
8
+ * rendered component. `Pagination` is then markup over {@link pageWindow}.
9
+ *
10
+ * Ported from an Effect implementation with `Option` replaced by
11
+ * `undefined` and the query Schema left behind in the app that needs it:
12
+ * decoding an app's own URL is the app's job, and a component library that
13
+ * owned it would drag a validation library onto every consumer's page.
14
+ *
15
+ * @module
16
+ */
17
+ /** The JSON:API offset query parameter — the default {@link pageHref} writes. */
18
+ export const PAGE_OFFSET_PARAM = 'page[offset]';
19
+ /** The JSON:API limit query parameter — the default {@link pageHref} writes. */
20
+ export const PAGE_LIMIT_PARAM = 'page[limit]';
21
+ /**
22
+ * Build the href for `page` from `base` (a path, optionally with a query
23
+ * string and a fragment).
24
+ *
25
+ * Every other query parameter is preserved and the page parameters are
26
+ * replaced — paging through a filtered list that silently dropped the filter
27
+ * is the bug this shape exists to prevent. A relative `base` yields a
28
+ * relative href: the absolute origin below is a parsing scaffold, never part
29
+ * of the result.
30
+ */
31
+ export function pageHref(base, page, names = {}) {
32
+ const url = new URL(base, 'http://pagination.invalid');
33
+ url.searchParams.set(names.offset ?? PAGE_OFFSET_PARAM, String(page.offset));
34
+ url.searchParams.set(names.limit ?? PAGE_LIMIT_PARAM, String(page.limit));
35
+ return `${url.pathname}${url.search}${url.hash}`;
36
+ }
37
+ /**
38
+ * Compute the window of page links to render around `page`: the first and
39
+ * last pages always, plus `siblings` pages either side of the current one,
40
+ * with a gap marker wherever the sequence skips.
41
+ *
42
+ * Tolerant by construction — a zero or negative limit, a negative offset and
43
+ * an offset past the end all resolve to a real page rather than throwing,
44
+ * because every one of them arrives from a hand-edited URL sooner or later
45
+ * and a 500 is a worse answer than the first page.
46
+ */
47
+ export function pageWindow(page, total, siblings = 1) {
48
+ const limit = Math.max(1, Math.floor(page.limit));
49
+ const offset = Math.max(0, Math.floor(page.offset));
50
+ const rows = Math.max(0, total);
51
+ const pageCount = Math.max(1, Math.ceil(rows / limit));
52
+ const current = Math.min(pageCount, Math.floor(offset / limit) + 1);
53
+ const wanted = [];
54
+ const want = (n) => {
55
+ if (n >= 1 && n <= pageCount && !wanted.includes(n))
56
+ wanted.push(n);
57
+ };
58
+ want(1);
59
+ for (let n = current - siblings; n <= current + siblings; n += 1)
60
+ want(n);
61
+ want(pageCount);
62
+ wanted.sort((left, right) => left - right);
63
+ const items = [];
64
+ for (const [index, n] of wanted.entries()) {
65
+ const previous = index > 0 ? wanted[index - 1] : undefined;
66
+ if (previous !== undefined && n - previous > 1)
67
+ items.push({ kind: 'gap', after: previous });
68
+ items.push({ kind: 'page', page: n, offset: (n - 1) * limit, current: n === current });
69
+ }
70
+ return {
71
+ page: current,
72
+ pageCount,
73
+ from: rows > 0 ? (current - 1) * limit + 1 : 0,
74
+ to: rows > 0 ? Math.min(rows, current * limit) : 0,
75
+ previous: current > 1 ? (current - 2) * limit : undefined,
76
+ next: current < pageCount ? current * limit : undefined,
77
+ items,
78
+ };
79
+ }
@@ -0,0 +1,64 @@
1
+ /**
2
+ * The column spec `Table` and `DataGrid` share.
3
+ *
4
+ * Declarative data rather than a table engine. The apps this is consolidated
5
+ * from were built on `@tanstack/svelte-table` — a column helper, a
6
+ * `createTable` instance, four row-model factories and a `FlexRender`
7
+ * indirection — which is ~40 kB of client JavaScript for tables whose whole
8
+ * job is to show rows, sort one column and page. The spec below is data, and
9
+ * the sorting / filtering / paging live in `DataGrid`'s own derivations.
10
+ * Reach for an engine again when something needs column resizing, grouping
11
+ * or virtualisation; none of those are on the map.
12
+ *
13
+ * @module
14
+ */
15
+ import type { Snippet } from 'svelte';
16
+ /**
17
+ * One column of a table.
18
+ *
19
+ * `value` is required and does triple duty: it is the cell's text, the key
20
+ * the column sorts on, and the corpus `DataGrid`'s filter searches. One
21
+ * accessor for all three is what stops a grid that sorts a column by one
22
+ * thing and shows another — the failure mode of a spec where they are
23
+ * separate. `cell` layers richer markup (a link, a badge, a formatted date)
24
+ * over that same value.
25
+ */
26
+ export interface TableColumn<T> {
27
+ /** Stable id — the `{#each}` key and the sort state's handle. */
28
+ readonly key: string;
29
+ /** The `<th>` text, already in the caller's language. */
30
+ readonly header: string;
31
+ /** The cell's text, its sort key, and what a filter matches against. */
32
+ readonly value: (row: T) => string | number;
33
+ /** Richer cell markup; `value`'s result is rendered when omitted. */
34
+ readonly cell?: Snippet<[T]> | undefined;
35
+ /** Whether the header offers a sort control on this column. */
36
+ readonly sortable?: boolean | undefined;
37
+ /** Right-align numeric columns. */
38
+ readonly align?: 'start' | 'end' | undefined;
39
+ /** Extra classes on this column's header and body cells. */
40
+ readonly class?: string | undefined;
41
+ }
42
+ /**
43
+ * Which column a table is sorted by, and which way.
44
+ *
45
+ * Unsorted is `undefined`, not a `SortState` with an empty key: "no column"
46
+ * is the absence of a sort, and modelling it as a sentinel key means every
47
+ * reader of the state has to know the sentinel.
48
+ */
49
+ export interface SortState {
50
+ /** The `key` of the sorted {@link TableColumn}. */
51
+ readonly key: string;
52
+ readonly direction: 'asc' | 'desc';
53
+ }
54
+ /**
55
+ * The next state in the three-step cycle a sortable header runs:
56
+ * unsorted → ascending → descending → unsorted.
57
+ *
58
+ * A pure function rather than a method on the component so `DataGrid` and a
59
+ * server-sorted caller cycle identically, and so the cycle is unit-testable
60
+ * without a DOM.
61
+ */
62
+ export declare function nextSortState(current: SortState | undefined, key: string): SortState | undefined;
63
+ /** What a column's `<th>` announces in `aria-sort`. */
64
+ export declare function columnAriaSort(sort: SortState | undefined, key: string): 'ascending' | 'descending' | 'none';
@@ -0,0 +1,35 @@
1
+ /**
2
+ * The column spec `Table` and `DataGrid` share.
3
+ *
4
+ * Declarative data rather than a table engine. The apps this is consolidated
5
+ * from were built on `@tanstack/svelte-table` — a column helper, a
6
+ * `createTable` instance, four row-model factories and a `FlexRender`
7
+ * indirection — which is ~40 kB of client JavaScript for tables whose whole
8
+ * job is to show rows, sort one column and page. The spec below is data, and
9
+ * the sorting / filtering / paging live in `DataGrid`'s own derivations.
10
+ * Reach for an engine again when something needs column resizing, grouping
11
+ * or virtualisation; none of those are on the map.
12
+ *
13
+ * @module
14
+ */
15
+ /**
16
+ * The next state in the three-step cycle a sortable header runs:
17
+ * unsorted → ascending → descending → unsorted.
18
+ *
19
+ * A pure function rather than a method on the component so `DataGrid` and a
20
+ * server-sorted caller cycle identically, and so the cycle is unit-testable
21
+ * without a DOM.
22
+ */
23
+ export function nextSortState(current, key) {
24
+ if (current === undefined || current.key !== key)
25
+ return { key, direction: 'asc' };
26
+ if (current.direction === 'asc')
27
+ return { key, direction: 'desc' };
28
+ return undefined;
29
+ }
30
+ /** What a column's `<th>` announces in `aria-sort`. */
31
+ export function columnAriaSort(sort, key) {
32
+ if (sort === undefined || sort.key !== key)
33
+ return 'none';
34
+ return sort.direction === 'asc' ? 'ascending' : 'descending';
35
+ }
@@ -0,0 +1,79 @@
1
+ <!-- @component
2
+ A destructive action behind a modal confirmation, run either as a callback (`onConfirm`) or as a progressive-enhancement POST (`form`).
3
+ -->
4
+ <!--
5
+ ConfirmDialog — a destructive action behind a modal confirmation, in the two
6
+ shapes the app repos actually needed.
7
+
8
+ **Callback mode** (`onConfirm`) is the client-side one: the promise runs,
9
+ the confirm button goes busy, a rejection is shown inside the still-open
10
+ dialog so the reader can retry or cancel, and a resolution closes it and
11
+ optionally navigates through the components context.
12
+
13
+ **Form mode** (`form`) is the progressive-enhancement one: the confirm
14
+ button is a real `type="submit"` inside a real `<form method="POST">`, so
15
+ the action posts and the server redirects even with scripting off. The SvelteKit `enhance` action is passed IN by the
16
+ app (`enhance: (node) => enhance(node)`); importing `$app/forms` here would
17
+ tie this package to SvelteKit. It is applied as an attachment, so a caller
18
+ that omits it simply gets the unenhanced form.
19
+
20
+ Pass exactly one of the two. With neither, the confirm button does nothing
21
+ — there is no action to take.
22
+
23
+ ARIA contract: `Dialog`'s, plus a failure surfaced as an `Alert` with
24
+ `live="assertive"` inside the dialog (the reader pressed confirm and
25
+ nothing visible happened, so the reason has to interrupt). Both buttons are
26
+ `disabled` while the callback is in flight and the confirm button swaps its
27
+ text to `pendingLabel` rather than emptying itself, so its accessible name
28
+ never disappears mid-press.
29
+ -->
30
+ <script lang="ts">import { getComponentsContext } from "../context.svelte.js";
31
+ import Alert from "../ui/Alert.svelte";
32
+ import Button from "../ui/Button.svelte";
33
+ import Dialog from "./Dialog.svelte";
34
+ let { open = $bindable(false), title, description, confirmLabel = "Delete", cancelLabel = "Cancel", pendingLabel = "Working…", failureMessage = "That didn’t work. Try again.", closeLabel = "Close", tone = "error", size = "md", onConfirm, form, redirectTo, class: className, trigger, children } = $props();
35
+ const context = getComponentsContext();
36
+ let pending = $state(false);
37
+ let errorMessage = $state(undefined);
38
+ const confirm = async () => {
39
+ if (onConfirm === undefined) return;
40
+ pending = true;
41
+ errorMessage = undefined;
42
+ try {
43
+ await onConfirm();
44
+ open = false;
45
+ if (redirectTo !== undefined) await context.navigate(context.href(redirectTo));
46
+ } catch (error) {
47
+ errorMessage = error instanceof Error ? error.message : failureMessage;
48
+ } finally {
49
+ pending = false;
50
+ }
51
+ };
52
+ /** The app's `enhance`, as an attachment so omitting it is a no-op. */
53
+ const enhanceForm = (node) => form?.enhance?.(node);
54
+ </script>
55
+
56
+ <Dialog bind:open {title} {description} {closeLabel} {size} class={className} {trigger}>
57
+ {#if children}
58
+ {@render children()}
59
+ {/if}
60
+ {#if errorMessage !== undefined}
61
+ <Alert tone="error" live="assertive">{errorMessage}</Alert>
62
+ {/if}
63
+
64
+ {#snippet actions({ close })}
65
+ <Button variant="ghost" disabled={pending} onclick={close}>{cancelLabel}</Button>
66
+ {#if form === undefined}
67
+ <Button {tone} loading={pending} onclick={confirm}>
68
+ {pending ? pendingLabel : confirmLabel}
69
+ </Button>
70
+ {:else}
71
+ <form method={form.method ?? 'POST'} action={form.action} {@attach enhanceForm}>
72
+ {#each Object.entries(form.fields ?? {}) as [name, value] (name)}
73
+ <input type="hidden" {name} {value} />
74
+ {/each}
75
+ <Button {tone} type="submit">{confirmLabel}</Button>
76
+ </form>
77
+ {/if}
78
+ {/snippet}
79
+ </Dialog>
@@ -0,0 +1,82 @@
1
+ import type { Snippet } from 'svelte';
2
+ import type { Tone } from '../ui/tones.ts';
3
+ import type { DialogSize } from './sizes.ts';
4
+ /** A progressive-enhancement POST: the confirm button submits this form. */
5
+ export interface ConfirmForm {
6
+ /** The form's `action` — a SvelteKit named action (`?/delete`) or a path. */
7
+ readonly action: string;
8
+ /** Always `POST`; spelled out because a destructive GET is a bug. */
9
+ readonly method?: 'POST' | undefined;
10
+ /**
11
+ * Extra fields to post, rendered as hidden inputs INSIDE the form. They
12
+ * live here rather than in `children` because `children` renders in the
13
+ * dialog body, which is outside the form element.
14
+ */
15
+ readonly fields?: Readonly<Record<string, string>> | undefined;
16
+ /**
17
+ * The app's own `use:enhance`, applied to the `<form>` element. Returns
18
+ * its teardown, if it has one. Omit for a plain, unenhanced POST.
19
+ */
20
+ readonly enhance?: ((form: HTMLFormElement) => void | (() => void)) | undefined;
21
+ }
22
+ interface Props {
23
+ /** Whether the dialog is shown. Bindable. */
24
+ open?: boolean | undefined;
25
+ /** Heading — the dialog's accessible name. */
26
+ title: string;
27
+ /** What confirming does, in the reader's terms. */
28
+ description?: string | undefined;
29
+ /**
30
+ * Text of the confirm button.
31
+ * @defaultValue `'Delete'`
32
+ */
33
+ confirmLabel?: string | undefined;
34
+ /**
35
+ * Text of the cancel button.
36
+ * @defaultValue `'Cancel'`
37
+ */
38
+ cancelLabel?: string | undefined;
39
+ /**
40
+ * The confirm button's text while the callback is in flight.
41
+ * @defaultValue `'Working…'`
42
+ */
43
+ pendingLabel?: string | undefined;
44
+ /**
45
+ * Shown when the callback rejects with something that is not an `Error`.
46
+ * @defaultValue `'That didn’t work. Try again.'`
47
+ */
48
+ failureMessage?: string | undefined;
49
+ /**
50
+ * Accessible name of the corner close button.
51
+ * @defaultValue `'Close'`
52
+ */
53
+ closeLabel?: string | undefined;
54
+ /**
55
+ * Colour of the confirm button.
56
+ * @defaultValue `'error'`
57
+ */
58
+ tone?: Tone | undefined;
59
+ /**
60
+ * Width of the dialog.
61
+ * @defaultValue `'md'`
62
+ */
63
+ size?: DialogSize | undefined;
64
+ /** Client-side confirmation. Mutually exclusive with `form`. */
65
+ onConfirm?: (() => Promise<unknown>) | undefined;
66
+ /** Progressive-enhancement POST. Mutually exclusive with `onConfirm`. */
67
+ form?: ConfirmForm | undefined;
68
+ /** Navigated to through the components context once `onConfirm` resolves. */
69
+ redirectTo?: string | undefined;
70
+ /** Extra classes, merged last. */
71
+ class?: string | undefined;
72
+ /** The opening control — the caller's own element, spreading `props`. */
73
+ trigger?: Snippet<[{
74
+ props: Record<string, unknown>;
75
+ }]> | undefined;
76
+ /** Extra body content: a list of what will be destroyed, a reason field. */
77
+ children?: Snippet<[]> | undefined;
78
+ }
79
+ /** A destructive action behind a modal confirmation, run either as a callback (`onConfirm`) or as a progressive-enhancement POST (`form`). */
80
+ declare const ConfirmDialog: import("svelte").Component<Props, {}, "open">;
81
+ type ConfirmDialog = ReturnType<typeof ConfirmDialog>;
82
+ export default ConfirmDialog;
@@ -0,0 +1,77 @@
1
+ <!-- @component
2
+ A modal on bits-ui’s `Dialog`, wearing daisyUI’s `modal-box`.
3
+ -->
4
+ <!--
5
+ Dialog — a modal on bits-ui's `Dialog`, wearing daisyUI's `modal-box`.
6
+
7
+ ARIA contract: everything that makes a modal a modal is bits-ui's — the
8
+ focus trap, the initial focus move, `aria-modal`, the `aria-labelledby` /
9
+ `aria-describedby` wiring to `Dialog.Title` and `Dialog.Description`,
10
+ Escape and outside-click dismissal, the scroll lock, and returning focus
11
+ to whatever opened it. None of that is re-implemented here, because the
12
+ hand-rolled version is the one that is always subtly wrong: the native
13
+ `<dialog>` the app repos used gets the trap right but leaves the title
14
+ wiring and the focus return to the call site, and every call site did it
15
+ differently.
16
+
17
+ `title` is required: a dialog with no accessible name is announced as
18
+ "dialog" and nothing else. `description` is optional but is what turns
19
+ "Delete garden?" into a sentence an assistive-technology user can act on.
20
+
21
+ `trigger` receives the props to spread, so an existing `Button` keeps its
22
+ own markup and its own accessible name; `actions` receives `close`, so the
23
+ action row's buttons can dismiss without reaching for the bindable `open`.
24
+ -->
25
+ <script lang="ts">import { X } from "@lucide/svelte";
26
+ import { Dialog } from "bits-ui";
27
+ import Button from "../ui/Button.svelte";
28
+ import VisuallyHidden from "../ui/VisuallyHidden.svelte";
29
+ import { dialogSize } from "./sizes.js";
30
+ let { open = $bindable(false), title, description, closeLabel = "Close", size = "md", class: className, trigger, actions, children } = $props();
31
+ const close = () => {
32
+ open = false;
33
+ };
34
+ </script>
35
+
36
+ <Dialog.Root bind:open>
37
+ {#if trigger}
38
+ <Dialog.Trigger>
39
+ {#snippet child({ props })}
40
+ {@render trigger({ props })}
41
+ {/snippet}
42
+ </Dialog.Trigger>
43
+ {/if}
44
+ <Dialog.Portal>
45
+ <!-- daisyUI's `modal-backdrop` is a child of an open `.modal`; this is
46
+ the same scrim as an element bits-ui can animate and dismiss from. -->
47
+ <Dialog.Overlay class="bg-base-300/60 fixed inset-0 z-40" />
48
+ <Dialog.Content
49
+ class={[
50
+ 'modal-box fixed top-1/2 left-1/2 z-50 max-h-[calc(100svh-2rem)] w-[calc(100vw-2rem)] -translate-x-1/2 -translate-y-1/2',
51
+ dialogSize[size],
52
+ className,
53
+ ]}
54
+ >
55
+ <div class="flex items-start justify-between gap-4">
56
+ <Dialog.Title class="text-lg font-semibold">{title}</Dialog.Title>
57
+ <Dialog.Close>
58
+ {#snippet child({ props })}
59
+ <Button {...props} variant="ghost" size="sm" class="btn-square -mt-1 -mr-1">
60
+ <X class="size-4" aria-hidden="true" />
61
+ <VisuallyHidden>{closeLabel}</VisuallyHidden>
62
+ </Button>
63
+ {/snippet}
64
+ </Dialog.Close>
65
+ </div>
66
+ {#if description !== undefined}
67
+ <Dialog.Description class="text-base-content/70 mt-1 text-sm leading-6">
68
+ {description}
69
+ </Dialog.Description>
70
+ {/if}
71
+ <div class="py-4">{@render children()}</div>
72
+ {#if actions}
73
+ <div class="modal-action">{@render actions({ close })}</div>
74
+ {/if}
75
+ </Dialog.Content>
76
+ </Dialog.Portal>
77
+ </Dialog.Root>
@@ -0,0 +1,37 @@
1
+ import { Dialog } from 'bits-ui';
2
+ import type { Snippet } from 'svelte';
3
+ import { type DialogSize } from './sizes.ts';
4
+ interface Props {
5
+ /** Whether the dialog is shown. Bindable. */
6
+ open?: boolean | undefined;
7
+ /** Heading — the dialog's accessible name. */
8
+ title: string;
9
+ /** Supporting line under the heading — the accessible description. */
10
+ description?: string | undefined;
11
+ /**
12
+ * Accessible name of the corner close button.
13
+ * @defaultValue `'Close'`
14
+ */
15
+ closeLabel?: string | undefined;
16
+ /**
17
+ * Width step of the `modal-box`.
18
+ * @defaultValue `'md'`
19
+ */
20
+ size?: DialogSize | undefined;
21
+ /** Extra classes on the `modal-box`. */
22
+ class?: string | undefined;
23
+ /** The opening control — the caller's own element, spreading `props`. */
24
+ trigger?: Snippet<[{
25
+ props: Record<string, unknown>;
26
+ }]> | undefined;
27
+ /** Buttons for the `modal-action` row. */
28
+ actions?: Snippet<[{
29
+ close: () => void;
30
+ }]> | undefined;
31
+ /** The dialog body, under the title and description. */
32
+ children: Snippet<[]>;
33
+ }
34
+ /** A modal on bits-ui’s `Dialog`, wearing daisyUI’s `modal-box`. */
35
+ declare const Dialog: import("svelte").Component<Props, {}, "open">;
36
+ type Dialog = ReturnType<typeof Dialog>;
37
+ export default Dialog;