@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,29 @@
1
+ import type { Snippet } from 'svelte';
2
+ import type { HTMLAttributes } from 'svelte/elements';
3
+ import type { FieldControl } from './field-types.ts';
4
+ interface Props extends Omit<HTMLAttributes<HTMLDivElement>, 'children'> {
5
+ /** The control's `id`. Minted when omitted. */
6
+ id?: string | undefined;
7
+ /** Visible label; becomes the control's accessible name. */
8
+ label: string;
9
+ /** Guidance rendered under the control. */
10
+ hint?: string | undefined;
11
+ /**
12
+ * Validation messages; any at all marks the control invalid.
13
+ * @defaultValue `[]`
14
+ */
15
+ errors?: readonly string[] | undefined;
16
+ /** Marks the control required and reads `requiredLabel` after the label. */
17
+ required?: boolean | undefined;
18
+ /**
19
+ * Read out after the label when `required`.
20
+ * @defaultValue `'(required)'`
21
+ */
22
+ requiredLabel?: string | undefined;
23
+ /** The control; receives the `FieldControl` props to spread. */
24
+ children: Snippet<[FieldControl]>;
25
+ }
26
+ /** The label, hint and errors frame around one control, handing it the `FieldControl` wiring. */
27
+ declare const FormField: import("svelte").Component<Props, {}, "">;
28
+ type FormField = ReturnType<typeof FormField>;
29
+ export default FormField;
@@ -0,0 +1,87 @@
1
+ <!-- @component
2
+ A `FileInput` that also measures the chosen image and previews it.
3
+ -->
4
+ <!--
5
+ ImageInput — a `FileInput` that also measures the picture and previews it.
6
+
7
+ It exists for one thing a server cannot do cheaply: read a file's INTRINSIC
8
+ DIMENSIONS. `createImageBitmap` (via `media/image-dimensions.ts`) decodes it in the browser the moment it is
9
+ chosen, so the form has `width` / `height` to submit alongside the binary and
10
+ the page that later renders the image can reserve its box on first paint
11
+ instead of reflowing when it loads.
12
+
13
+ The preview is an object URL, so the file never leaves the browser until the
14
+ form is submitted. The URL is revoked as soon as it is replaced and again on
15
+ teardown — an un-revoked object URL pins the whole file in memory for the
16
+ life of the document.
17
+
18
+ A decode failure means the browser could not read the file as an image; the
19
+ server's own media-type gate will refuse it too, so the dimensions are left
20
+ unset rather than blocking the submission.
21
+
22
+ ARIA contract: `FileInput`'s. The preview `<img>` is named by `previewAlt`,
23
+ and the measured size is a `<figcaption>` rather than an `aria-label`,
24
+ because it is information about the file that a sighted reader gets too.
25
+ -->
26
+ <script lang="ts">import { imageDimensions } from "../media/image-dimensions.js";
27
+ import FileInput from "./FileInput.svelte";
28
+ let { file = $bindable(), width = $bindable(), height = $bindable(), previewAlt = "Preview of the selected image", accept = "image/jpeg,image/png,image/gif,image/webp,image/avif", name, class: className, ...rest } = $props();
29
+ let previewUrl = $state();
30
+ let measuring = $state(false);
31
+ // Revoke the PREVIOUS url whenever this one changes, and the last one when
32
+ // the component goes away. Reading `previewUrl` inside the effect is what
33
+ // makes the cleanup run on every change rather than only on teardown.
34
+ $effect(() => {
35
+ const url = previewUrl;
36
+ return () => {
37
+ if (url !== undefined) URL.revokeObjectURL(url);
38
+ };
39
+ });
40
+ const measure = async (chosen) => {
41
+ measuring = true;
42
+ try {
43
+ ({width, height} = await imageDimensions(chosen));
44
+ } catch {
45
+ width = undefined;
46
+ height = undefined;
47
+ } finally {
48
+ measuring = false;
49
+ }
50
+ };
51
+ const select = (chosen) => {
52
+ if (chosen === undefined) {
53
+ previewUrl = undefined;
54
+ width = undefined;
55
+ height = undefined;
56
+ return;
57
+ }
58
+ previewUrl = URL.createObjectURL(chosen);
59
+ void measure(chosen);
60
+ };
61
+ </script>
62
+
63
+ <div class={['flex flex-col gap-2', className]}>
64
+ <FileInput {...rest} {name} {accept} bind:file onSelect={select} />
65
+
66
+ {#if previewUrl !== undefined}
67
+ <figure>
68
+ <!-- The caller's own file, not a served image, so a plain `<img>`. -->
69
+ <img src={previewUrl} alt={previewAlt} class="rounded-box border-base-300 max-h-64 border" />
70
+ <figcaption class="fieldset-label mt-1">
71
+ {#if measuring}
72
+ &nbsp;
73
+ {:else if width !== undefined && height !== undefined}
74
+ {width} × {height}
75
+ {/if}
76
+ </figcaption>
77
+ </figure>
78
+ {/if}
79
+
80
+ <!-- The measured dimensions ride the same multipart body as the binary. -->
81
+ {#if name !== undefined && width !== undefined}
82
+ <input type="hidden" name="width" value={width} />
83
+ {/if}
84
+ {#if name !== undefined && height !== undefined}
85
+ <input type="hidden" name="height" value={height} />
86
+ {/if}
87
+ </div>
@@ -0,0 +1,39 @@
1
+ import type { HTMLInputAttributes } from 'svelte/elements';
2
+ import type { FieldSize } from './field-types.ts';
3
+ interface Props extends Omit<HTMLInputAttributes, 'type' | 'value' | 'files' | 'size'> {
4
+ /** The chosen file. */
5
+ file?: File | undefined;
6
+ /** Measured intrinsic width, in pixels. */
7
+ width?: number | undefined;
8
+ /** Measured intrinsic height, in pixels. */
9
+ height?: number | undefined;
10
+ /**
11
+ * Alternative text for the local preview.
12
+ * @defaultValue `'Preview of the selected image'`
13
+ */
14
+ previewAlt?: string | undefined;
15
+ /**
16
+ * The `accept` filter passed to the file input.
17
+ * @defaultValue `'image/jpeg,image/png,image/gif,image/webp,image/avif'`
18
+ */
19
+ accept?: string | undefined;
20
+ /** Largest accepted file, in bytes; a larger file is rejected with `tooLargeMessage`. */
21
+ maxBytes?: number | undefined;
22
+ /**
23
+ * Shown when the chosen file exceeds `maxBytes`.
24
+ * @defaultValue `'That file is too large.'` (from `FileInput`)
25
+ */
26
+ tooLargeMessage?: string | undefined;
27
+ /** Called with the accepted file, or `undefined` when the selection is cleared. */
28
+ onSelect?: ((file: File | undefined) => void) | undefined;
29
+ /** daisyUI size step. */
30
+ size?: FieldSize | undefined;
31
+ /** `FieldControl`: the field is in error — sets `aria-invalid` and the error style. */
32
+ invalid?: boolean | undefined;
33
+ /** `FieldControl`: space-separated ids for `aria-describedby`. */
34
+ describedBy?: string | undefined;
35
+ }
36
+ /** A `FileInput` that also measures the chosen image and previews it. */
37
+ declare const ImageInput: import("svelte").Component<Props, {}, "height" | "width" | "file">;
38
+ type ImageInput = ReturnType<typeof ImageInput>;
39
+ export default ImageInput;
@@ -0,0 +1,93 @@
1
+ <!-- @component
2
+ A `Toggle` that subscribes this browser to Web Push, or unsubscribes it, through the caller’s `enable` / `disable`.
3
+ -->
4
+ <!--
5
+ PushToggle — the browser half of Web Push behind one `Toggle`: it reads
6
+ whether THIS browser is subscribed, and turns the subscription on or off
7
+ through the caller's `enable` / `disable`.
8
+
9
+ The switch reflects this browser's registration, not the account's
10
+ preference — two different questions, and a settings page asks both.
11
+ `enable` / `disable` are props rather than imports because the network calls
12
+ (subscribing with the VAPID key, registering the endpoint with the app's
13
+ API) belong to the app's own client; injecting them keeps this package free
14
+ of it.
15
+
16
+ Every failure of `enable` is a different thing to tell the reader — the
17
+ browser cannot, the site cannot, or they said no — so `enable` RESOLVES with
18
+ an outcome instead of throwing, and each reason renders its own message.
19
+ The switch writes back what actually happened (see `Toggle`), so a denied
20
+ permission never reads as "on".
21
+
22
+ The subscription state is read on mount, from the browser, rather than
23
+ trusted from a prop: the page may be cached, and the reader may have revoked
24
+ the permission in browser settings since it rendered. `supported` and
25
+ `readSubscribed` default to the real browser checks and exist so a caller
26
+ (or a test) can supply its own.
27
+
28
+ ARIA contract: `Toggle`'s — a native checkbox with `role="switch"`, named by
29
+ `label`. The notice under it is a polite live region, so a refusal is
30
+ announced when it appears.
31
+ -->
32
+ <script lang="ts">import Toggle from "./Toggle.svelte";
33
+ let { available, enable, disable, supported = typeof navigator !== "undefined" && "serviceWorker" in navigator && "PushManager" in globalThis, readSubscribed = async () => {
34
+ const registration = await navigator.serviceWorker.ready;
35
+ return await registration.pushManager.getSubscription() !== null;
36
+ }, label = "Send push notifications to this browser", description, unavailableMessage = "Push notifications aren't available on this site yet.", unsupportedMessage = "This browser can't receive push notifications.", deniedMessage = "This browser is blocking notifications. Allow them in your browser settings to turn this on.", class: className } = $props();
37
+ let subscribed = $state(false);
38
+ let notice = $state(undefined);
39
+ // Aborted on unmount, so a request in flight when the reader navigates away
40
+ // is cancelled rather than resolving into a destroyed component.
41
+ const controller = new AbortController();
42
+ $effect(() => () => controller.abort());
43
+ $effect(() => {
44
+ if (!available) return;
45
+ if (!supported) {
46
+ notice = "unsupported";
47
+ return;
48
+ }
49
+ void readSubscribed(controller.signal).then((value) => {
50
+ subscribed = value;
51
+ }, () => {
52
+ // Unreadable is reported as off; the switch still works.
53
+ });
54
+ });
55
+ const apply = async (next) => {
56
+ notice = undefined;
57
+ if (!next) {
58
+ await disable(controller.signal);
59
+ return false;
60
+ }
61
+ const result = await enable(controller.signal);
62
+ if (result._tag === "enabled") return true;
63
+ notice = result._tag;
64
+ return false;
65
+ };
66
+ const messages = $derived({
67
+ unsupported: unsupportedMessage,
68
+ unavailable: unavailableMessage,
69
+ denied: deniedMessage
70
+ });
71
+ const noticeTone = {
72
+ unsupported: "text-base-content/70",
73
+ unavailable: "text-base-content/70",
74
+ denied: "text-warning"
75
+ };
76
+ </script>
77
+
78
+ {#if !available}
79
+ <p class={['text-base-content/70 text-sm', className]}>{unavailableMessage}</p>
80
+ {:else}
81
+ <div class={['flex flex-col gap-2', className]}>
82
+ <Toggle
83
+ {label}
84
+ {description}
85
+ checked={subscribed}
86
+ disabled={!supported}
87
+ onChange={apply}
88
+ />
89
+ <p class={['text-sm', notice === undefined ? '' : noticeTone[notice]]} aria-live="polite">
90
+ {notice === undefined ? '' : messages[notice]}
91
+ </p>
92
+ </div>
93
+ {/if}
@@ -0,0 +1,55 @@
1
+ /** Why `enable` did not subscribe this browser. */
2
+ export type PushDeclineReason = 'unsupported' | 'unavailable' | 'denied';
3
+ /**
4
+ * What `enable` resolves with: it worked, or why not. The `_tag` shape
5
+ * matches a tagged-union result an Effect client already returns.
6
+ */
7
+ export interface PushEnableResult {
8
+ readonly _tag: 'enabled' | PushDeclineReason;
9
+ }
10
+ interface Props {
11
+ /** False when the site has no push configuration (no VAPID key pair): the switch is not offered. */
12
+ available: boolean;
13
+ /** Subscribe this browser and register it. Resolves with the outcome rather than throwing. */
14
+ enable: (signal: AbortSignal) => Promise<PushEnableResult>;
15
+ /** Unsubscribe this browser. Its resolved value is ignored. */
16
+ disable: (signal: AbortSignal) => Promise<unknown>;
17
+ /**
18
+ * Whether this browser can do Web Push at all.
19
+ * @defaultValue feature detection (`serviceWorker` and `PushManager`)
20
+ */
21
+ supported?: boolean | undefined;
22
+ /**
23
+ * Whether this browser is subscribed now.
24
+ * @defaultValue asks the service worker's `PushManager`
25
+ */
26
+ readSubscribed?: ((signal: AbortSignal) => Promise<boolean>) | undefined;
27
+ /**
28
+ * Text beside the switch; its accessible name.
29
+ * @defaultValue `'Send push notifications to this browser'`
30
+ */
31
+ label?: string | undefined;
32
+ /** A sentence under the switch. */
33
+ description?: string | undefined;
34
+ /**
35
+ * Shown instead of the switch when `available` is false.
36
+ * @defaultValue `"Push notifications aren't available on this site yet."`
37
+ */
38
+ unavailableMessage?: string | undefined;
39
+ /**
40
+ * Shown instead of the switch when the browser cannot do Web Push.
41
+ * @defaultValue `"This browser can't receive push notifications."`
42
+ */
43
+ unsupportedMessage?: string | undefined;
44
+ /**
45
+ * Shown when the browser’s notification permission is denied.
46
+ * @defaultValue `'This browser is blocking notifications. Allow them in your browser settings to turn this on.'`
47
+ */
48
+ deniedMessage?: string | undefined;
49
+ /** Extra classes, merged last. */
50
+ class?: string | undefined;
51
+ }
52
+ /** A `Toggle` that subscribes this browser to Web Push, or unsubscribes it, through the caller’s `enable` / `disable`. */
53
+ declare const PushToggle: import("svelte").Component<Props, {}, "">;
54
+ type PushToggle = ReturnType<typeof PushToggle>;
55
+ export default PushToggle;
@@ -0,0 +1,85 @@
1
+ <!-- @component
2
+ One choice from a short, known list, on bits-ui’s `RadioGroup`.
3
+ -->
4
+ <!--
5
+ RadioGroup — one choice from a short, known list, on bits-ui's `RadioGroup`.
6
+
7
+ bits-ui owns what is genuinely hard here: the roving tabindex (the group is
8
+ ONE tab stop, and the arrow keys move within it), wrap-around, and the
9
+ `aria-checked` bookkeeping. Hand-rolled radio groups get the tab stop wrong
10
+ almost every time.
11
+
12
+ ARIA contract: bits-ui renders `role="radiogroup"` around `role="radio"`
13
+ buttons, and this adds `aria-orientation` beside the `data-orientation`
14
+ bits-ui sets, so the arrow keys a reader is told to use match the ones that
15
+ work. A `role` element cannot be named by a `<legend>` — the legend names
16
+ the `<fieldset>`, which is a different node — so the group renders its
17
+ question as a heading-ish `<span>` and points `aria-labelledby` at it. That
18
+ is why this is the one field group NOT built on `Fieldset`, and the hint and
19
+ errors hang off the radiogroup element itself through `aria-describedby`.
20
+
21
+ The marker is drawn rather than delegated to daisyUI's `.radio`, because
22
+ that class styles an `<input type="radio">` through `:checked` and bits-ui's
23
+ radio is a `<button>`. It is two spans of semantic tokens; `checked` comes
24
+ from bits-ui's own snippet prop, so the visual and the accessibility tree
25
+ cannot disagree.
26
+ -->
27
+ <script lang="ts">import { RadioGroup } from "bits-ui";
28
+ import FieldErrors from "./FieldErrors.svelte";
29
+ import { toFieldOptions } from "./field-types.js";
30
+ let { legend, options, value = $bindable(""), orientation = "vertical", name, hint, errors = [], required = false, requiredLabel = "(required)", disabled = false, class: className, ...rest } = $props();
31
+ const uid = $props.id();
32
+ const legendId = `${uid}-legend`;
33
+ const hintId = `${uid}-hint`;
34
+ const errorId = `${uid}-error`;
35
+ const entries = $derived(toFieldOptions(options));
36
+ const invalid = $derived(errors.length > 0);
37
+ const describedBy = $derived([hint === undefined ? "" : hintId, invalid ? errorId : ""].filter((part) => part !== "").join(" "));
38
+ </script>
39
+
40
+ <div {...rest} class={['fieldset', className]}>
41
+ <span id={legendId} class="fieldset-legend">
42
+ {legend}
43
+ {#if required}
44
+ <span class="text-error" aria-hidden="true">*</span>
45
+ <span class="sr-only">{requiredLabel}</span>
46
+ {/if}
47
+ </span>
48
+ <RadioGroup.Root
49
+ bind:value
50
+ {orientation}
51
+ {...name === undefined ? {} : { name }}
52
+ {required}
53
+ {disabled}
54
+ aria-labelledby={legendId}
55
+ aria-orientation={orientation}
56
+ aria-describedby={describedBy === '' ? undefined : describedBy}
57
+ aria-invalid={invalid ? 'true' : undefined}
58
+ class={['flex gap-3', orientation === 'horizontal' ? 'flex-row flex-wrap' : 'flex-col']}
59
+ >
60
+ {#each entries as option (option.value)}
61
+ <RadioGroup.Item
62
+ value={option.value}
63
+ disabled={option.disabled === true}
64
+ class="rounded-field focus-visible:outline-primary flex cursor-pointer items-center gap-3 text-left focus-visible:outline-2 focus-visible:outline-offset-2 disabled:cursor-not-allowed disabled:opacity-50"
65
+ >
66
+ {#snippet children({ checked })}
67
+ <span
68
+ aria-hidden="true"
69
+ class={[
70
+ 'grid size-4 shrink-0 place-items-center rounded-full border-2',
71
+ checked ? 'border-primary' : 'border-base-content/40',
72
+ ]}
73
+ >
74
+ {#if checked}<span class="bg-primary size-2 rounded-full"></span>{/if}
75
+ </span>
76
+ <span>{option.label}</span>
77
+ {/snippet}
78
+ </RadioGroup.Item>
79
+ {/each}
80
+ </RadioGroup.Root>
81
+ {#if hint !== undefined}
82
+ <p class="fieldset-label" id={hintId}>{hint}</p>
83
+ {/if}
84
+ <FieldErrors {errors} id={errorId} />
85
+ </div>
@@ -0,0 +1,41 @@
1
+ import { RadioGroup } from 'bits-ui';
2
+ import type { HTMLAttributes } from 'svelte/elements';
3
+ import { type FieldOptionInput } from './field-types.ts';
4
+ interface Props extends Omit<HTMLAttributes<HTMLDivElement>, 'children'> {
5
+ /** The question the options answer; names the group. */
6
+ legend: string;
7
+ /** Full options, or bare strings when value and label are the same. */
8
+ options: readonly FieldOptionInput[];
9
+ /**
10
+ * The chosen value; empty string when nothing is chosen.
11
+ * @defaultValue `''`
12
+ */
13
+ value?: string | undefined;
14
+ /**
15
+ * Lay the options out in a column or a row.
16
+ * @defaultValue `'vertical'`
17
+ */
18
+ orientation?: 'vertical' | 'horizontal' | undefined;
19
+ /** Posted field name — bits-ui renders the hidden input when it is set. */
20
+ name?: string | undefined;
21
+ /** Guidance rendered under the legend. */
22
+ hint?: string | undefined;
23
+ /**
24
+ * Validation messages; any at all marks the group invalid.
25
+ * @defaultValue `[]`
26
+ */
27
+ errors?: readonly string[] | undefined;
28
+ /** Marks the group required and reads `requiredLabel` after the legend. */
29
+ required?: boolean | undefined;
30
+ /**
31
+ * Read out after the legend when `required`.
32
+ * @defaultValue `'(required)'`
33
+ */
34
+ requiredLabel?: string | undefined;
35
+ /** Disables every option. */
36
+ disabled?: boolean | undefined;
37
+ }
38
+ /** One choice from a short, known list, on bits-ui’s `RadioGroup`. */
39
+ declare const RadioGroup: import("svelte").Component<Props, {}, "value">;
40
+ type RadioGroup = ReturnType<typeof RadioGroup>;
41
+ export default RadioGroup;
@@ -0,0 +1,57 @@
1
+ <!-- @component
2
+ A `type="search"` field with a leading icon, a clear button while there is text, and a spinner while `pending`. `name` defaults to `'q'` and `placeholder` to `'Search'`.
3
+ -->
4
+ <!--
5
+ SearchInput — a `type="search"` field in daisyUI's composite `input`
6
+ wrapper, with a leading icon, a clear button while there is text, and a
7
+ spinner while `pending`.
8
+
9
+ The wrapper is a `<div>`, not a `<label>`: the clear button lives inside it,
10
+ and a button inside a label is both mis-named (the label's text becomes part
11
+ of ITS name) and label-activated (clicking it also focuses the field). The
12
+ visible label is optional — `label` always supplies the accessible name.
13
+
14
+ ARIA contract: the leading magnifier is `aria-hidden`, because the field's
15
+ own name already says what it is; the clear button is a real button with a
16
+ visually-hidden name rather than a bare glyph; the spinner is `aria-hidden`,
17
+ because a busy state must not change the field's accessible name.
18
+
19
+ `onInput` fires on every keystroke (feed it to whatever debounce the app
20
+ uses) and `onSubmit` on Enter.
21
+ -->
22
+ <script lang="ts">import { Search, X } from "@lucide/svelte";
23
+ import { inputSize } from "./field-types.js";
24
+ let { label, value = $bindable(""), onInput, onSubmit, onClear, pending = false, clearLabel = "Clear search", size = "md", name = "q", placeholder = "Search", class: className, ...rest } = $props();
25
+ const clear = () => {
26
+ value = "";
27
+ onInput?.("");
28
+ onClear?.();
29
+ };
30
+ </script>
31
+
32
+ <div class={['input w-full', inputSize[size], className]}>
33
+ <Search class="size-4 opacity-60" aria-hidden="true" />
34
+ <input
35
+ {...rest}
36
+ type="search"
37
+ {name}
38
+ {placeholder}
39
+ aria-label={label}
40
+ bind:value
41
+ oninput={() => onInput?.(value)}
42
+ onkeydown={(event) => {
43
+ if (event.key === 'Enter') {
44
+ event.preventDefault();
45
+ onSubmit?.(value);
46
+ }
47
+ }}
48
+ />
49
+ {#if pending}
50
+ <span class="loading loading-spinner loading-xs" aria-hidden="true"></span>
51
+ {:else if value !== ''}
52
+ <button type="button" class="btn btn-ghost btn-xs btn-circle" onclick={clear}>
53
+ <X class="size-3.5" aria-hidden="true" />
54
+ <span class="sr-only">{clearLabel}</span>
55
+ </button>
56
+ {/if}
57
+ </div>
@@ -0,0 +1,33 @@
1
+ import type { HTMLInputAttributes } from 'svelte/elements';
2
+ import { type FieldSize } from './field-types.ts';
3
+ interface Props extends Omit<HTMLInputAttributes, 'type' | 'value' | 'size'> {
4
+ /** Accessible name of the field. */
5
+ label: string;
6
+ /**
7
+ * The query. Bindable.
8
+ * @defaultValue `''`
9
+ */
10
+ value?: string | undefined;
11
+ /** Fires on every change with the current text. */
12
+ onInput?: ((query: string) => void) | undefined;
13
+ /** Fires on Enter with the current text. */
14
+ onSubmit?: ((query: string) => void) | undefined;
15
+ /** Fires after the clear button empties the field. */
16
+ onClear?: (() => void) | undefined;
17
+ /** A search is in flight. */
18
+ pending?: boolean | undefined;
19
+ /**
20
+ * Accessible name of the clear button.
21
+ * @defaultValue `'Clear search'`
22
+ */
23
+ clearLabel?: string | undefined;
24
+ /**
25
+ * daisyUI size step.
26
+ * @defaultValue `'md'`
27
+ */
28
+ size?: FieldSize | undefined;
29
+ }
30
+ /** A `type="search"` field with a leading icon, a clear button while there is text, and a spinner while `pending`. `name` defaults to `'q'` and `placeholder` to `'Search'`. */
31
+ declare const SearchInput: import("svelte").Component<Props, {}, "value">;
32
+ type SearchInput = ReturnType<typeof SearchInput>;
33
+ export default SearchInput;
@@ -0,0 +1,36 @@
1
+ <!-- @component
2
+ A native `<select>` in daisyUI styling over a list of options, with an optional placeholder and `FieldControl` wiring.
3
+ -->
4
+ <!--
5
+ Select — a native `<select>` in daisyUI `select` styling over a list of
6
+ options, with a bindable `value`, an optional placeholder option (disabled,
7
+ selected while `value` is empty) and the `FieldControl` wiring.
8
+
9
+ Native, not bits-ui, and deliberately: a native select is keyboard-complete
10
+ and typeahead-complete without a line of script, and on a phone it opens the
11
+ platform picker. The rich alternative is `Combobox`, which exists for the
12
+ case a select genuinely cannot serve — a list too long to scroll, or one
13
+ that has to be fetched.
14
+
15
+ ARIA contract: the platform's. `describedBy` and `invalid` are the only
16
+ attributes added.
17
+ -->
18
+ <script lang="ts">import { selectSize, toFieldOptions } from "./field-types.js";
19
+ let { options, value = $bindable(""), placeholder, size = "md", invalid = false, describedBy = "", class: className, ...rest } = $props();
20
+ const entries = $derived(toFieldOptions(options));
21
+ </script>
22
+
23
+ <select
24
+ {...rest}
25
+ bind:value
26
+ aria-invalid={invalid ? 'true' : undefined}
27
+ aria-describedby={describedBy === '' ? undefined : describedBy}
28
+ class={['select w-full', selectSize[size], invalid && 'select-error', className]}
29
+ >
30
+ {#if placeholder !== undefined}
31
+ <option value="" disabled>{placeholder}</option>
32
+ {/if}
33
+ {#each entries as option (option.value)}
34
+ <option value={option.value} disabled={option.disabled === true}>{option.label}</option>
35
+ {/each}
36
+ </select>
@@ -0,0 +1,29 @@
1
+ import type { HTMLSelectAttributes } from 'svelte/elements';
2
+ import { type FieldOptionInput, type FieldSize } from './field-types.ts';
3
+ interface Props extends Omit<HTMLSelectAttributes, 'value' | 'size'> {
4
+ /** Full options, or bare strings when value and label are the same. */
5
+ options: readonly FieldOptionInput[];
6
+ /**
7
+ * The selected option’s value; `''` selects the placeholder. Bindable.
8
+ * @defaultValue `''`
9
+ */
10
+ value?: string | undefined;
11
+ /** Disabled first option, shown while nothing is selected. */
12
+ placeholder?: string | undefined;
13
+ /**
14
+ * daisyUI size step.
15
+ * @defaultValue `'md'`
16
+ */
17
+ size?: FieldSize | undefined;
18
+ /** `FieldControl`: the field is in error — sets `aria-invalid` and the error style. */
19
+ invalid?: boolean | undefined;
20
+ /**
21
+ * `FieldControl`: space-separated ids for `aria-describedby`.
22
+ * @defaultValue `''`
23
+ */
24
+ describedBy?: string | undefined;
25
+ }
26
+ /** A native `<select>` in daisyUI styling over a list of options, with an optional placeholder and `FieldControl` wiring. */
27
+ declare const Select: import("svelte").Component<Props, {}, "value">;
28
+ type Select = ReturnType<typeof Select>;
29
+ export default Select;
@@ -0,0 +1,26 @@
1
+ <!-- @component
2
+ A daisyUI `input` over a native `<input>`, with a bindable `value` and `FieldControl` wiring.
3
+ -->
4
+ <!--
5
+ TextInput — daisyUI `input` over a native `<input>`, with a bindable `value`
6
+ and the `FieldControl` wiring (`describedBy` → `aria-describedby`, `invalid`
7
+ → `aria-invalid` + `input-error`).
8
+
9
+ ARIA contract: a native input, so the role, the keyboard and the validation
10
+ semantics are the platform's. The component adds only the two attributes
11
+ that tie it back to its `FormField`; everything else — `name`, `autocomplete`,
12
+ `maxlength`, `pattern`, `data-*` — passes straight through, because a
13
+ control that swallows those cannot be submitted, tested or hooked into.
14
+ -->
15
+ <script lang="ts">import { inputSize } from "./field-types.js";
16
+ let { value = $bindable(""), type = "text", size = "md", invalid = false, describedBy = "", class: className, ...rest } = $props();
17
+ </script>
18
+
19
+ <input
20
+ {...rest}
21
+ {type}
22
+ bind:value
23
+ aria-invalid={invalid ? 'true' : undefined}
24
+ aria-describedby={describedBy === '' ? undefined : describedBy}
25
+ class={['input w-full', inputSize[size], invalid && 'input-error', className]}
26
+ />
@@ -0,0 +1,30 @@
1
+ import type { HTMLInputAttributes } from 'svelte/elements';
2
+ import { type FieldSize } from './field-types.ts';
3
+ interface Props extends Omit<HTMLInputAttributes, 'type' | 'value' | 'size'> {
4
+ /**
5
+ * The text. Bindable.
6
+ * @defaultValue `''`
7
+ */
8
+ value?: string | undefined;
9
+ /**
10
+ * The text-ish `type`s. Dates have `DateInput`, files have `FileInput`.
11
+ * @defaultValue `'text'`
12
+ */
13
+ type?: 'text' | 'email' | 'url' | 'password' | 'tel' | 'number' | 'search' | undefined;
14
+ /**
15
+ * daisyUI size step.
16
+ * @defaultValue `'md'`
17
+ */
18
+ size?: FieldSize | undefined;
19
+ /** `FieldControl`: the field is in error — sets `aria-invalid` and the error style. */
20
+ invalid?: boolean | undefined;
21
+ /**
22
+ * Ids for `aria-describedby`; empty means none.
23
+ * @defaultValue `''`
24
+ */
25
+ describedBy?: string | undefined;
26
+ }
27
+ /** A daisyUI `input` over a native `<input>`, with a bindable `value` and `FieldControl` wiring. */
28
+ declare const TextInput: import("svelte").Component<Props, {}, "value">;
29
+ type TextInput = ReturnType<typeof TextInput>;
30
+ export default TextInput;