@thomasfosterau/components 0.0.0 → 0.2.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 (484) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +241 -56
  3. package/dist/canvas/VectorCanvas.svelte +320 -0
  4. package/dist/canvas/VectorCanvas.svelte.d.ts +105 -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 +40 -0
  57. package/dist/context.svelte.d.ts +46 -0
  58. package/dist/context.svelte.js +51 -0
  59. package/dist/data/ChatBubble.svelte +48 -0
  60. package/dist/data/ChatBubble.svelte.d.ts +33 -0
  61. package/dist/data/DataGrid.svelte +150 -0
  62. package/dist/data/DataGrid.svelte.d.ts +118 -0
  63. package/dist/data/DescriptionList.svelte +53 -0
  64. package/dist/data/DescriptionList.svelte.d.ts +26 -0
  65. package/dist/data/DescriptionRow.svelte +56 -0
  66. package/dist/data/DescriptionRow.svelte.d.ts +21 -0
  67. package/dist/data/DiffText.svelte +29 -0
  68. package/dist/data/DiffText.svelte.d.ts +19 -0
  69. package/dist/data/ItemList.svelte +80 -0
  70. package/dist/data/ItemList.svelte.d.ts +52 -0
  71. package/dist/data/LinkListItem.svelte +46 -0
  72. package/dist/data/LinkListItem.svelte.d.ts +21 -0
  73. package/dist/data/MessageThread.svelte +29 -0
  74. package/dist/data/MessageThread.svelte.d.ts +17 -0
  75. package/dist/data/Pagination.svelte +136 -0
  76. package/dist/data/Pagination.svelte.d.ts +79 -0
  77. package/dist/data/PaginationFooter.svelte +42 -0
  78. package/dist/data/PaginationFooter.svelte.d.ts +61 -0
  79. package/dist/data/ResultCount.svelte +27 -0
  80. package/dist/data/ResultCount.svelte.d.ts +18 -0
  81. package/dist/data/Table.svelte +151 -0
  82. package/dist/data/Table.svelte.d.ts +75 -0
  83. package/dist/data/TreeList.svelte +41 -0
  84. package/dist/data/TreeList.svelte.d.ts +17 -0
  85. package/dist/data/TreeListItem.svelte +29 -0
  86. package/dist/data/TreeListItem.svelte.d.ts +10 -0
  87. package/dist/data/description-context.d.ts +39 -0
  88. package/dist/data/description-context.js +36 -0
  89. package/dist/data/diff.d.ts +14 -0
  90. package/dist/data/diff.js +10 -0
  91. package/dist/data/index.d.ts +37 -0
  92. package/dist/data/index.js +37 -0
  93. package/dist/data/list-item.d.ts +30 -0
  94. package/dist/data/list-item.js +19 -0
  95. package/dist/data/pagination.d.ts +81 -0
  96. package/dist/data/pagination.js +79 -0
  97. package/dist/data/table-types.d.ts +71 -0
  98. package/dist/data/table-types.js +35 -0
  99. package/dist/feedback/ConfirmDialog.svelte +79 -0
  100. package/dist/feedback/ConfirmDialog.svelte.d.ts +82 -0
  101. package/dist/feedback/Dialog.svelte +47 -0
  102. package/dist/feedback/Dialog.svelte.d.ts +36 -0
  103. package/dist/feedback/DialogFrame.svelte +64 -0
  104. package/dist/feedback/DialogFrame.svelte.d.ts +23 -0
  105. package/dist/feedback/Drawer.svelte +43 -0
  106. package/dist/feedback/Drawer.svelte.d.ts +43 -0
  107. package/dist/feedback/LoadingBoundary.svelte +61 -0
  108. package/dist/feedback/LoadingBoundary.svelte.d.ts +34 -0
  109. package/dist/feedback/ProgressBar.svelte +46 -0
  110. package/dist/feedback/ProgressBar.svelte.d.ts +28 -0
  111. package/dist/feedback/SegmentBar.svelte +83 -0
  112. package/dist/feedback/SegmentBar.svelte.d.ts +37 -0
  113. package/dist/feedback/StatTile.svelte +77 -0
  114. package/dist/feedback/StatTile.svelte.d.ts +50 -0
  115. package/dist/feedback/Stats.svelte +18 -0
  116. package/dist/feedback/Stats.svelte.d.ts +16 -0
  117. package/dist/feedback/Steps.svelte +72 -0
  118. package/dist/feedback/Steps.svelte.d.ts +61 -0
  119. package/dist/feedback/Timeline.svelte +86 -0
  120. package/dist/feedback/Timeline.svelte.d.ts +55 -0
  121. package/dist/feedback/Toaster.svelte +78 -0
  122. package/dist/feedback/Toaster.svelte.d.ts +32 -0
  123. package/dist/feedback/index.d.ts +34 -0
  124. package/dist/feedback/index.js +29 -0
  125. package/dist/feedback/sizes.d.ts +41 -0
  126. package/dist/feedback/sizes.js +69 -0
  127. package/dist/feedback/toaster.d.ts +48 -0
  128. package/dist/feedback/toaster.js +29 -0
  129. package/dist/fields/Calendar.svelte +60 -0
  130. package/dist/fields/Calendar.svelte.d.ts +11 -0
  131. package/dist/fields/CalendarMonths.svelte +86 -0
  132. package/dist/fields/CalendarMonths.svelte.d.ts +30 -0
  133. package/dist/fields/Checkbox.svelte +33 -0
  134. package/dist/fields/Checkbox.svelte.d.ts +30 -0
  135. package/dist/fields/CheckboxGroup.svelte +41 -0
  136. package/dist/fields/CheckboxGroup.svelte.d.ts +40 -0
  137. package/dist/fields/Combobox.svelte +229 -0
  138. package/dist/fields/Combobox.svelte.d.ts +133 -0
  139. package/dist/fields/DateField.svelte +37 -0
  140. package/dist/fields/DateField.svelte.d.ts +20 -0
  141. package/dist/fields/DateInput.svelte +31 -0
  142. package/dist/fields/DateInput.svelte.d.ts +35 -0
  143. package/dist/fields/DatePicker.svelte +47 -0
  144. package/dist/fields/DatePicker.svelte.d.ts +25 -0
  145. package/dist/fields/DateRangePicker.svelte +70 -0
  146. package/dist/fields/DateRangePicker.svelte.d.ts +43 -0
  147. package/dist/fields/Dropzone.svelte +232 -0
  148. package/dist/fields/Dropzone.svelte.d.ts +103 -0
  149. package/dist/fields/FieldErrors.svelte +30 -0
  150. package/dist/fields/FieldErrors.svelte.d.ts +11 -0
  151. package/dist/fields/Fieldset.svelte +53 -0
  152. package/dist/fields/Fieldset.svelte.d.ts +27 -0
  153. package/dist/fields/FileInput.svelte +68 -0
  154. package/dist/fields/FileInput.svelte.d.ts +39 -0
  155. package/dist/fields/FormField.svelte +78 -0
  156. package/dist/fields/FormField.svelte.d.ts +29 -0
  157. package/dist/fields/ImageInput.svelte +87 -0
  158. package/dist/fields/ImageInput.svelte.d.ts +45 -0
  159. package/dist/fields/MultiSelect.svelte +112 -0
  160. package/dist/fields/MultiSelect.svelte.d.ts +75 -0
  161. package/dist/fields/NumberField.svelte +190 -0
  162. package/dist/fields/NumberField.svelte.d.ts +76 -0
  163. package/dist/fields/PasswordInput.svelte +95 -0
  164. package/dist/fields/PasswordInput.svelte.d.ts +58 -0
  165. package/dist/fields/PickerPopover.svelte +47 -0
  166. package/dist/fields/PickerPopover.svelte.d.ts +16 -0
  167. package/dist/fields/PinInput.svelte +83 -0
  168. package/dist/fields/PinInput.svelte.d.ts +66 -0
  169. package/dist/fields/PushToggle.svelte +93 -0
  170. package/dist/fields/PushToggle.svelte.d.ts +55 -0
  171. package/dist/fields/RadioGroup.svelte +88 -0
  172. package/dist/fields/RadioGroup.svelte.d.ts +41 -0
  173. package/dist/fields/RangeCalendar.svelte +51 -0
  174. package/dist/fields/RangeCalendar.svelte.d.ts +23 -0
  175. package/dist/fields/Rating.svelte +127 -0
  176. package/dist/fields/Rating.svelte.d.ts +86 -0
  177. package/dist/fields/SearchInput.svelte +58 -0
  178. package/dist/fields/SearchInput.svelte.d.ts +33 -0
  179. package/dist/fields/SegmentGroup.svelte +91 -0
  180. package/dist/fields/SegmentGroup.svelte.d.ts +20 -0
  181. package/dist/fields/Select.svelte +36 -0
  182. package/dist/fields/Select.svelte.d.ts +35 -0
  183. package/dist/fields/Slider.svelte +194 -0
  184. package/dist/fields/Slider.svelte.d.ts +100 -0
  185. package/dist/fields/TagsInput.svelte +256 -0
  186. package/dist/fields/TagsInput.svelte.d.ts +102 -0
  187. package/dist/fields/TextInput.svelte +26 -0
  188. package/dist/fields/TextInput.svelte.d.ts +36 -0
  189. package/dist/fields/Textarea.svelte +22 -0
  190. package/dist/fields/Textarea.svelte.d.ts +31 -0
  191. package/dist/fields/TimeField.svelte +30 -0
  192. package/dist/fields/TimeField.svelte.d.ts +19 -0
  193. package/dist/fields/Toggle.svelte +72 -0
  194. package/dist/fields/Toggle.svelte.d.ts +38 -0
  195. package/dist/fields/VideoInput.svelte +35 -0
  196. package/dist/fields/VideoInput.svelte.d.ts +46 -0
  197. package/dist/fields/calendar-props.d.ts +119 -0
  198. package/dist/fields/calendar-props.js +51 -0
  199. package/dist/fields/dates.d.ts +28 -0
  200. package/dist/fields/dates.js +26 -0
  201. package/dist/fields/field-types.d.ts +80 -0
  202. package/dist/fields/field-types.js +92 -0
  203. package/dist/fields/file-validation.d.ts +30 -0
  204. package/dist/fields/file-validation.js +51 -0
  205. package/dist/fields/index.d.ts +43 -0
  206. package/dist/fields/index.js +42 -0
  207. package/dist/fields/latest-search.svelte.d.ts +38 -0
  208. package/dist/fields/latest-search.svelte.js +85 -0
  209. package/dist/fields/number-format.d.ts +52 -0
  210. package/dist/fields/number-format.js +89 -0
  211. package/dist/fields/segmented-field.d.ts +128 -0
  212. package/dist/fields/segmented-field.js +52 -0
  213. package/dist/fields/slider-keys.d.ts +21 -0
  214. package/dist/fields/slider-keys.js +37 -0
  215. package/dist/formatting/Countdown.svelte +110 -0
  216. package/dist/formatting/Countdown.svelte.d.ts +35 -0
  217. package/dist/formatting/DateTime.svelte +35 -0
  218. package/dist/formatting/DateTime.svelte.d.ts +26 -0
  219. package/dist/formatting/Duration.svelte +18 -0
  220. package/dist/formatting/Duration.svelte.d.ts +20 -0
  221. package/dist/formatting/FormatList.svelte +31 -0
  222. package/dist/formatting/FormatList.svelte.d.ts +46 -0
  223. package/dist/formatting/Highlight.svelte +51 -0
  224. package/dist/formatting/Highlight.svelte.d.ts +19 -0
  225. package/dist/formatting/Number.svelte +35 -0
  226. package/dist/formatting/Number.svelte.d.ts +43 -0
  227. package/dist/formatting/RelativeTime.svelte +69 -0
  228. package/dist/formatting/RelativeTime.svelte.d.ts +26 -0
  229. package/dist/formatting/Year.svelte +39 -0
  230. package/dist/formatting/Year.svelte.d.ts +21 -0
  231. package/dist/formatting/duration.d.ts +44 -0
  232. package/dist/formatting/duration.js +125 -0
  233. package/dist/formatting/index.d.ts +15 -0
  234. package/dist/formatting/index.js +17 -0
  235. package/dist/forms/EntityForm.svelte +185 -0
  236. package/dist/forms/EntityForm.svelte.d.ts +66 -0
  237. package/dist/forms/FormActions.svelte +29 -0
  238. package/dist/forms/FormActions.svelte.d.ts +37 -0
  239. package/dist/forms/FormErrors.svelte +21 -0
  240. package/dist/forms/FormErrors.svelte.d.ts +16 -0
  241. package/dist/forms/HiddenField.svelte +22 -0
  242. package/dist/forms/HiddenField.svelte.d.ts +11 -0
  243. package/dist/forms/SearchForm.svelte +43 -0
  244. package/dist/forms/SearchForm.svelte.d.ts +36 -0
  245. package/dist/forms/entity-form.d.ts +12 -0
  246. package/dist/forms/entity-form.js +12 -0
  247. package/dist/forms/index.d.ts +14 -0
  248. package/dist/forms/index.js +14 -0
  249. package/dist/hotkeys/HotkeyBadge.svelte +39 -0
  250. package/dist/hotkeys/HotkeyBadge.svelte.d.ts +25 -0
  251. package/dist/hotkeys/HotkeyHelpDialog.svelte +68 -0
  252. package/dist/hotkeys/HotkeyHelpDialog.svelte.d.ts +35 -0
  253. package/dist/hotkeys/HotkeyProvider.svelte +92 -0
  254. package/dist/hotkeys/HotkeyProvider.svelte.d.ts +4 -0
  255. package/dist/hotkeys/context.svelte.d.ts +47 -0
  256. package/dist/hotkeys/context.svelte.js +72 -0
  257. package/dist/hotkeys/index.d.ts +28 -0
  258. package/dist/hotkeys/index.js +27 -0
  259. package/dist/hotkeys/match.d.ts +65 -0
  260. package/dist/hotkeys/match.js +135 -0
  261. package/dist/hotkeys/registry.svelte.d.ts +55 -0
  262. package/dist/hotkeys/registry.svelte.js +110 -0
  263. package/dist/hotkeys/types.d.ts +74 -0
  264. package/dist/hotkeys/types.js +18 -0
  265. package/dist/index.d.ts +37 -0
  266. package/dist/index.js +37 -0
  267. package/dist/infobox/Infobox.svelte +79 -0
  268. package/dist/infobox/Infobox.svelte.d.ts +33 -0
  269. package/dist/infobox/InfoboxHeader.svelte +35 -0
  270. package/dist/infobox/InfoboxHeader.svelte.d.ts +18 -0
  271. package/dist/infobox/InfoboxImage.svelte +38 -0
  272. package/dist/infobox/InfoboxImage.svelte.d.ts +21 -0
  273. package/dist/infobox/InfoboxRow.svelte +50 -0
  274. package/dist/infobox/InfoboxRow.svelte.d.ts +25 -0
  275. package/dist/infobox/InfoboxSection.svelte +24 -0
  276. package/dist/infobox/InfoboxSection.svelte.d.ts +12 -0
  277. package/dist/infobox/index.d.ts +21 -0
  278. package/dist/infobox/index.js +20 -0
  279. package/dist/infobox/types.d.ts +86 -0
  280. package/dist/infobox/types.js +27 -0
  281. package/dist/layout/AppShell.svelte +50 -0
  282. package/dist/layout/AppShell.svelte.d.ts +28 -0
  283. package/dist/layout/Breadcrumbs.svelte +52 -0
  284. package/dist/layout/Breadcrumbs.svelte.d.ts +18 -0
  285. package/dist/layout/Cluster.svelte +28 -0
  286. package/dist/layout/Cluster.svelte.d.ts +27 -0
  287. package/dist/layout/DetailLayout.svelte +50 -0
  288. package/dist/layout/DetailLayout.svelte.d.ts +19 -0
  289. package/dist/layout/FilterBar.svelte +40 -0
  290. package/dist/layout/FilterBar.svelte.d.ts +12 -0
  291. package/dist/layout/Fits.svelte +27 -0
  292. package/dist/layout/Fits.svelte.d.ts +13 -0
  293. package/dist/layout/Flow.svelte +46 -0
  294. package/dist/layout/Flow.svelte.d.ts +16 -0
  295. package/dist/layout/Footer.svelte +32 -0
  296. package/dist/layout/Footer.svelte.d.ts +12 -0
  297. package/dist/layout/Grid.svelte +23 -0
  298. package/dist/layout/Grid.svelte.d.ts +26 -0
  299. package/dist/layout/HStack.svelte +12 -0
  300. package/dist/layout/HStack.svelte.d.ts +34 -0
  301. package/dist/layout/Hero.svelte +76 -0
  302. package/dist/layout/Hero.svelte.d.ts +36 -0
  303. package/dist/layout/Main.svelte +30 -0
  304. package/dist/layout/Main.svelte.d.ts +15 -0
  305. package/dist/layout/Navbar.svelte +68 -0
  306. package/dist/layout/Navbar.svelte.d.ts +31 -0
  307. package/dist/layout/PageHeader.svelte +77 -0
  308. package/dist/layout/PageHeader.svelte.d.ts +33 -0
  309. package/dist/layout/Section.svelte +72 -0
  310. package/dist/layout/Section.svelte.d.ts +40 -0
  311. package/dist/layout/Sidebar.svelte +54 -0
  312. package/dist/layout/Sidebar.svelte.d.ts +27 -0
  313. package/dist/layout/Spacer.svelte +12 -0
  314. package/dist/layout/Spacer.svelte.d.ts +6 -0
  315. package/dist/layout/Tabs.svelte +85 -0
  316. package/dist/layout/Tabs.svelte.d.ts +42 -0
  317. package/dist/layout/VStack.svelte +12 -0
  318. package/dist/layout/VStack.svelte.d.ts +29 -0
  319. package/dist/layout/index.d.ts +38 -0
  320. package/dist/layout/index.js +38 -0
  321. package/dist/layout/space.d.ts +44 -0
  322. package/dist/layout/space.js +76 -0
  323. package/dist/layout/types.d.ts +52 -0
  324. package/dist/layout/types.js +27 -0
  325. package/dist/links/ContextLinks.svelte +28 -0
  326. package/dist/links/ContextLinks.svelte.d.ts +20 -0
  327. package/dist/links/EmailLink.svelte +26 -0
  328. package/dist/links/EmailLink.svelte.d.ts +20 -0
  329. package/dist/links/index.d.ts +7 -0
  330. package/dist/links/index.js +7 -0
  331. package/dist/media/Figure.svelte +26 -0
  332. package/dist/media/Figure.svelte.d.ts +12 -0
  333. package/dist/media/Image.svelte +76 -0
  334. package/dist/media/Image.svelte.d.ts +41 -0
  335. package/dist/media/Video.svelte +65 -0
  336. package/dist/media/Video.svelte.d.ts +29 -0
  337. package/dist/media/image-dimensions.d.ts +28 -0
  338. package/dist/media/image-dimensions.js +34 -0
  339. package/dist/media/index.d.ts +10 -0
  340. package/dist/media/index.js +10 -0
  341. package/dist/search/CommandPalette.svelte +257 -0
  342. package/dist/search/CommandPalette.svelte.d.ts +59 -0
  343. package/dist/search/index.d.ts +13 -0
  344. package/dist/search/index.js +13 -0
  345. package/dist/search/types.d.ts +40 -0
  346. package/dist/search/types.js +20 -0
  347. package/dist/seo/Head.svelte +190 -0
  348. package/dist/seo/Head.svelte.d.ts +86 -0
  349. package/dist/seo/JsonLd.svelte +30 -0
  350. package/dist/seo/JsonLd.svelte.d.ts +20 -0
  351. package/dist/seo/index.d.ts +18 -0
  352. package/dist/seo/index.js +17 -0
  353. package/dist/seo/json-ld.d.ts +33 -0
  354. package/dist/seo/json-ld.js +29 -0
  355. package/dist/seo/og-image.d.ts +72 -0
  356. package/dist/seo/og-image.js +82 -0
  357. package/dist/seo/types.d.ts +43 -0
  358. package/dist/seo/types.js +6 -0
  359. package/dist/theme.css +161 -0
  360. package/dist/tips/PopoverTip.svelte +80 -0
  361. package/dist/tips/PopoverTip.svelte.d.ts +33 -0
  362. package/dist/tips/Tip.svelte +91 -0
  363. package/dist/tips/Tip.svelte.d.ts +25 -0
  364. package/dist/tips/index.d.ts +15 -0
  365. package/dist/tips/index.js +14 -0
  366. package/dist/tips/tip-stack.d.ts +26 -0
  367. package/dist/tips/tip-stack.js +37 -0
  368. package/dist/tips/tip-store.svelte.d.ts +75 -0
  369. package/dist/tips/tip-store.svelte.js +158 -0
  370. package/dist/tips/types.d.ts +46 -0
  371. package/dist/tips/types.js +6 -0
  372. package/dist/ui/Accordion.svelte +75 -0
  373. package/dist/ui/Accordion.svelte.d.ts +50 -0
  374. package/dist/ui/AccordionItem.svelte +73 -0
  375. package/dist/ui/AccordionItem.svelte.d.ts +18 -0
  376. package/dist/ui/Alert.svelte +61 -0
  377. package/dist/ui/Alert.svelte.d.ts +37 -0
  378. package/dist/ui/Badge.svelte +39 -0
  379. package/dist/ui/Badge.svelte.d.ts +32 -0
  380. package/dist/ui/Button.svelte +44 -0
  381. package/dist/ui/Button.svelte.d.ts +29 -0
  382. package/dist/ui/ButtonGroup.svelte +24 -0
  383. package/dist/ui/ButtonGroup.svelte.d.ts +17 -0
  384. package/dist/ui/Card.svelte +49 -0
  385. package/dist/ui/Card.svelte.d.ts +44 -0
  386. package/dist/ui/Chip.svelte +58 -0
  387. package/dist/ui/Chip.svelte.d.ts +21 -0
  388. package/dist/ui/ConfirmButton.svelte +75 -0
  389. package/dist/ui/ConfirmButton.svelte.d.ts +48 -0
  390. package/dist/ui/ContextMenu.svelte +75 -0
  391. package/dist/ui/ContextMenu.svelte.d.ts +25 -0
  392. package/dist/ui/Disclosure.svelte +46 -0
  393. package/dist/ui/Disclosure.svelte.d.ts +22 -0
  394. package/dist/ui/Divider.svelte +47 -0
  395. package/dist/ui/Divider.svelte.d.ts +28 -0
  396. package/dist/ui/DropdownMenu.svelte +44 -0
  397. package/dist/ui/DropdownMenu.svelte.d.ts +30 -0
  398. package/dist/ui/EmptyState.svelte +37 -0
  399. package/dist/ui/EmptyState.svelte.d.ts +22 -0
  400. package/dist/ui/ExternalLink.svelte +38 -0
  401. package/dist/ui/ExternalLink.svelte.d.ts +34 -0
  402. package/dist/ui/Heading.svelte +58 -0
  403. package/dist/ui/Heading.svelte.d.ts +22 -0
  404. package/dist/ui/Link.svelte +34 -0
  405. package/dist/ui/Link.svelte.d.ts +20 -0
  406. package/dist/ui/LinkButton.svelte +51 -0
  407. package/dist/ui/LinkButton.svelte.d.ts +33 -0
  408. package/dist/ui/Menu.svelte +24 -0
  409. package/dist/ui/Menu.svelte.d.ts +13 -0
  410. package/dist/ui/MenuCheckboxItem.svelte +33 -0
  411. package/dist/ui/MenuCheckboxItem.svelte.d.ts +20 -0
  412. package/dist/ui/MenuItem.svelte +73 -0
  413. package/dist/ui/MenuItem.svelte.d.ts +21 -0
  414. package/dist/ui/MenuRadioGroup.svelte +25 -0
  415. package/dist/ui/MenuRadioGroup.svelte.d.ts +20 -0
  416. package/dist/ui/MenuRadioItem.svelte +20 -0
  417. package/dist/ui/MenuRadioItem.svelte.d.ts +15 -0
  418. package/dist/ui/MenuSeparator.svelte +27 -0
  419. package/dist/ui/MenuSeparator.svelte.d.ts +8 -0
  420. package/dist/ui/MenuSub.svelte +24 -0
  421. package/dist/ui/MenuSub.svelte.d.ts +15 -0
  422. package/dist/ui/Popover.svelte +63 -0
  423. package/dist/ui/Popover.svelte.d.ts +40 -0
  424. package/dist/ui/ShareButton.svelte +64 -0
  425. package/dist/ui/ShareButton.svelte.d.ts +39 -0
  426. package/dist/ui/Skeleton.svelte +43 -0
  427. package/dist/ui/Skeleton.svelte.d.ts +22 -0
  428. package/dist/ui/Spinner.svelte +41 -0
  429. package/dist/ui/Spinner.svelte.d.ts +35 -0
  430. package/dist/ui/Stack.svelte +36 -0
  431. package/dist/ui/Stack.svelte.d.ts +48 -0
  432. package/dist/ui/ThemeToggle.svelte +65 -0
  433. package/dist/ui/ThemeToggle.svelte.d.ts +24 -0
  434. package/dist/ui/ToggleButton.svelte +58 -0
  435. package/dist/ui/ToggleButton.svelte.d.ts +45 -0
  436. package/dist/ui/ToggleGroup.svelte +142 -0
  437. package/dist/ui/ToggleGroup.svelte.d.ts +86 -0
  438. package/dist/ui/Toolbar.svelte +40 -0
  439. package/dist/ui/Toolbar.svelte.d.ts +20 -0
  440. package/dist/ui/ToolbarButton.svelte +29 -0
  441. package/dist/ui/ToolbarButton.svelte.d.ts +21 -0
  442. package/dist/ui/ToolbarLink.svelte +21 -0
  443. package/dist/ui/ToolbarLink.svelte.d.ts +12 -0
  444. package/dist/ui/ToolbarSeparator.svelte +22 -0
  445. package/dist/ui/ToolbarSeparator.svelte.d.ts +12 -0
  446. package/dist/ui/ToolbarToggleGroup.svelte +66 -0
  447. package/dist/ui/ToolbarToggleGroup.svelte.d.ts +52 -0
  448. package/dist/ui/Tooltip.svelte +54 -0
  449. package/dist/ui/Tooltip.svelte.d.ts +35 -0
  450. package/dist/ui/VisuallyHidden.svelte +23 -0
  451. package/dist/ui/VisuallyHidden.svelte.d.ts +10 -0
  452. package/dist/ui/accordion-context.d.ts +26 -0
  453. package/dist/ui/accordion-context.js +23 -0
  454. package/dist/ui/colour-token.d.ts +41 -0
  455. package/dist/ui/colour-token.js +49 -0
  456. package/dist/ui/context-menu.d.ts +13 -0
  457. package/dist/ui/context-menu.js +13 -0
  458. package/dist/ui/index.d.ts +63 -0
  459. package/dist/ui/index.js +62 -0
  460. package/dist/ui/menu-context.d.ts +20 -0
  461. package/dist/ui/menu-context.js +22 -0
  462. package/dist/ui/menu-parts/CheckboxItemPart.svelte +26 -0
  463. package/dist/ui/menu-parts/CheckboxItemPart.svelte.d.ts +5 -0
  464. package/dist/ui/menu-parts/ItemPart.svelte +29 -0
  465. package/dist/ui/menu-parts/ItemPart.svelte.d.ts +5 -0
  466. package/dist/ui/menu-parts/RadioGroupPart.svelte +24 -0
  467. package/dist/ui/menu-parts/RadioGroupPart.svelte.d.ts +5 -0
  468. package/dist/ui/menu-parts/RadioItemPart.svelte +22 -0
  469. package/dist/ui/menu-parts/RadioItemPart.svelte.d.ts +5 -0
  470. package/dist/ui/menu-parts/SeparatorPart.svelte +16 -0
  471. package/dist/ui/menu-parts/SeparatorPart.svelte.d.ts +5 -0
  472. package/dist/ui/menu-parts/SubPart.svelte +28 -0
  473. package/dist/ui/menu-parts/SubPart.svelte.d.ts +5 -0
  474. package/dist/ui/menu-parts/menu-parts.d.ts +104 -0
  475. package/dist/ui/menu-parts/menu-parts.js +31 -0
  476. package/dist/ui/menu-parts/parts.d.ts +12 -0
  477. package/dist/ui/menu-parts/parts.js +27 -0
  478. package/dist/ui/slug.d.ts +19 -0
  479. package/dist/ui/slug.js +25 -0
  480. package/dist/ui/toggle-classes.d.ts +39 -0
  481. package/dist/ui/toggle-classes.js +36 -0
  482. package/dist/ui/tones.d.ts +103 -0
  483. package/dist/ui/tones.js +240 -0
  484. package/package.json +41 -25
@@ -0,0 +1,78 @@
1
+ <!-- @component
2
+ The label, hint and errors frame around one control, handing it the `FieldControl` wiring.
3
+ -->
4
+ <!--
5
+ FormField — the label + hint + errors frame around one control.
6
+
7
+ The control is a snippet that receives a `FieldControl` (`id`, `labelId`,
8
+ `describedBy`, `invalid`, `required`) to spread onto the primitive, so the
9
+ label's `for`, the control's `aria-describedby` and `aria-invalid`, and the
10
+ error region's `role="alert"` all stay wired together from a single `id`.
11
+ Neither side has to know the other's markup, which is what lets a caller
12
+ drop any of the field primitives — or their own `<input>` — into the slot.
13
+
14
+ ARIA contract: `<label for>` names the control (so clicking the text focuses
15
+ it). `<label for>` only reaches a labelable element (`input`, `select`,
16
+ `textarea`, `button`, `meter`, `output`, `progress`), so the label also
17
+ carries an id — `labelId` — for a role-based control (a slider thumb, a
18
+ `group` / `radiogroup` root) to point `aria-labelledby` at, and a click on
19
+ the label is forwarded to such a control: it focuses the control itself if
20
+ it is focusable, else its first focusable descendant (a roving group's
21
+ current tab stop). `describedBy` lists the hint's id then the error region's id, in that
22
+ reading order, and is empty when there is neither; `invalid` is true exactly
23
+ when there is at least one error. The required marker is a `*` that is
24
+ `aria-hidden` beside a visually-hidden `requiredLabel`, both inside the
25
+ `<label>`, so the accessible name reads "Scientific name (required)" rather
26
+ than "Scientific name star".
27
+
28
+ `errors` is a LIST. A field that fails several rules says so; collapsing
29
+ them to the first message is a decision the call site should make, not one
30
+ this frame makes for it.
31
+ -->
32
+ <script lang="ts">import FieldErrors from "./FieldErrors.svelte";
33
+ let { id: idProp, label, hint, errors = [], required = false, requiredLabel = "(required)", children, class: className, ...rest } = $props();
34
+ const uid = $props.id();
35
+ const id = $derived(idProp ?? uid);
36
+ const labelId = $derived(`${id}-label`);
37
+ const hintId = $derived(`${id}-hint`);
38
+ const errorId = $derived(`${id}-error`);
39
+ const invalid = $derived(errors.length > 0);
40
+ const control = $derived({
41
+ id,
42
+ labelId,
43
+ describedBy: [hint === undefined ? "" : hintId, invalid ? errorId : ""].filter((part) => part !== "").join(" "),
44
+ invalid,
45
+ required
46
+ });
47
+ // The elements a `<label for>` can name; the platform handles those.
48
+ const LABELABLE = "button, input, meter, output, progress, select, textarea";
49
+ // `<label for>` does nothing for a role-based control, so forward the click:
50
+ // focus the control if it is focusable, else its first focusable descendant.
51
+ const forwardClick = () => {
52
+ const control = document.getElementById(id);
53
+ if (control === null || control.matches(LABELABLE)) return;
54
+ const focusable = (element) => element.tabIndex >= 0 && !element.hasAttribute("disabled");
55
+ const target = focusable(control) ? control : Array.from(control.querySelectorAll("*")).find(focusable);
56
+ target?.focus();
57
+ };
58
+ </script>
59
+
60
+ <div {...rest} class={['fieldset', className]}>
61
+ <!-- The click handler restores what `<label for>` does natively — a pointer
62
+ shortcut to the control — for a control `for` cannot reach. Keyboard
63
+ users reach the control itself with Tab, as with any label, so there is
64
+ no key handler to add and the label stays non-interactive. -->
65
+ <!-- svelte-ignore a11y_click_events_have_key_events, a11y_no_noninteractive_element_interactions -->
66
+ <label class="label" for={id} id={labelId} onclick={forwardClick}>
67
+ <span>{label}</span>
68
+ {#if required}
69
+ <span class="text-error" aria-hidden="true">*</span>
70
+ <span class="sr-only">{requiredLabel}</span>
71
+ {/if}
72
+ </label>
73
+ {@render children(control)}
74
+ {#if hint !== undefined}
75
+ <p class="fieldset-label" id={hintId}>{hint}</p>
76
+ {/if}
77
+ <FieldErrors {errors} id={errorId} />
78
+ </div>
@@ -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,45 @@
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
+ * `FieldControl`: the visible label's id. Not used — the file input is
37
+ * labelable, so `<label for>` names it; passed through to `FileInput`,
38
+ * which drops it.
39
+ */
40
+ labelId?: string | undefined;
41
+ }
42
+ /** A `FileInput` that also measures the chosen image and previews it. */
43
+ declare const ImageInput: import("svelte").Component<Props, {}, "height" | "width" | "file">;
44
+ type ImageInput = ReturnType<typeof ImageInput>;
45
+ export default ImageInput;
@@ -0,0 +1,112 @@
1
+ <!-- @component
2
+ Several choices from a known list in a compact trigger that summarises them, on bits-ui's `Select` (`type="multiple"`).
3
+ -->
4
+ <!--
5
+ MultiSelect — any number of options from a medium-sized known list: the
6
+ trigger summarises the selection ("Full sun, Well-drained, +1"), a listbox
7
+ popover holds the options with checkmarks. Built on bits-ui's `Select`
8
+ with `type="multiple"`, bound to a `string[]`.
9
+
10
+ When to use which: `CheckboxGroup` puts every option on screen (right for
11
+ about seven or fewer); `Select` is the native single-choice `<select>`;
12
+ `MultiSelect` is for a longer list where several may be chosen. It takes
13
+ the same `options` shape as `Select`, so the two swap at the call site. It
14
+ is a separate component rather than a mode on `Select` because `Select`'s
15
+ point is to be the native control, with no script and the platform picker.
16
+
17
+ ARIA contract (APG select-only combobox / listbox): bits-ui gives the
18
+ trigger `role="combobox"`, `aria-haspopup="listbox"`, `aria-expanded` and
19
+ `aria-controls`; the content `role="listbox"` with `aria-multiselectable`;
20
+ each option `role="option"` with `aria-selected` (and `aria-disabled`,
21
+ which bits-ui 2.20 leaves out, added here); typeahead (fed from
22
+ `options` through bits-ui's `items`), Home / End, and Escape / Tab closing
23
+ with focus back on the trigger. Selecting an option keeps the listbox open.
24
+ This file adds the trigger's name (the `FieldControl` id goes on the
25
+ trigger, so `FormField`'s `<label for>` names it), `aria-describedby`,
26
+ `aria-invalid`, `aria-required`, the summary text and the `aria-hidden`
27
+ check icon.
28
+
29
+ The summary is plain text, not removable badges: focusable children inside
30
+ a button-like trigger are an accessibility trap.
31
+
32
+ `values` is always kept in `options` order, whatever order the reader
33
+ clicked in, so the summary is stable. `name` posts one hidden input per
34
+ value, in option order (and nothing at all while none is selected).
35
+ -->
36
+ <script lang="ts">import { Check } from "@lucide/svelte";
37
+ import { Select } from "bits-ui";
38
+ import { selectSize, toFieldOptions } from "./field-types.js";
39
+ let { options, values = $bindable([]), placeholder = "Select…", name, maxShown = 2, formatOverflow = (hidden) => `+${hidden}`, option, portal = true, disabled = false, size = "md", id, labelId: _labelId, describedBy = "", invalid = false, required = false, class: className, ...rest } = $props();
40
+ const uid = $props.id();
41
+ const triggerId = $derived(id ?? uid);
42
+ const entries = $derived(toFieldOptions(options));
43
+ const items = $derived(entries.map((entry) => ({
44
+ ...entry,
45
+ disabled: entry.disabled === true
46
+ })));
47
+ const selectedEntries = $derived(entries.filter((entry) => values.includes(entry.value)));
48
+ const selectedLabels = $derived(selectedEntries.map((entry) => entry.label));
49
+ const summary = $derived(selectedLabels.length <= maxShown ? selectedLabels.join(", ") : [...selectedLabels.slice(0, maxShown), formatOverflow(selectedLabels.length - maxShown)].join(", "));
50
+ const triggerText = $derived(selectedLabels.length === 0 ? placeholder : summary);
51
+ const ariaDescribedBy = $derived(describedBy === "" ? undefined : describedBy);
52
+ /** Keep `values` in option order, whatever order they were clicked in. */
53
+ function setValues(next) {
54
+ values = entries.map((entry) => entry.value).filter((value) => next.includes(value));
55
+ }
56
+ </script>
57
+
58
+ {#snippet optionBody(entry: FieldOption, selected: boolean)}
59
+ <Check class={['size-4 shrink-0', !selected && 'invisible']} aria-hidden="true" />
60
+ {#if option}
61
+ {@render option(entry, { selected })}
62
+ {:else}
63
+ <span>{entry.label}</span>
64
+ {/if}
65
+ {/snippet}
66
+
67
+ <div {...rest} class={['w-full', className]}>
68
+ <Select.Root
69
+ type="multiple"
70
+ bind:value={() => values, setValues}
71
+ {items}
72
+ {disabled}
73
+ {required}
74
+ >
75
+ <Select.Trigger
76
+ id={triggerId}
77
+ aria-describedby={ariaDescribedBy}
78
+ aria-invalid={invalid ? 'true' : undefined}
79
+ aria-required={required ? 'true' : undefined}
80
+ class={['select w-full text-start', selectSize[size], invalid && 'select-error']}
81
+ >
82
+ <span class={['truncate', selectedLabels.length === 0 && 'text-base-content/60']}>
83
+ {triggerText}
84
+ </span>
85
+ </Select.Trigger>
86
+ <Select.Portal disabled={!portal}>
87
+ <Select.Content
88
+ sideOffset={4}
89
+ class="menu bg-base-100 rounded-box border-base-300 z-50 max-h-72 w-(--bits-select-anchor-width) min-w-48 overflow-auto border p-2 shadow-lg"
90
+ >
91
+ <Select.Viewport>
92
+ {#each entries as entry (entry.value)}
93
+ <Select.Item
94
+ value={entry.value}
95
+ label={entry.label}
96
+ disabled={entry.disabled === true}
97
+ aria-disabled={entry.disabled === true ? 'true' : undefined}
98
+ class="rounded-field data-highlighted:bg-base-200 data-disabled:opacity-50 flex cursor-pointer items-center gap-2 px-3 py-2"
99
+ >
100
+ {#snippet children({ selected })}{@render optionBody(entry, selected)}{/snippet}
101
+ </Select.Item>
102
+ {/each}
103
+ </Select.Viewport>
104
+ </Select.Content>
105
+ </Select.Portal>
106
+ </Select.Root>
107
+ {#if name !== undefined}
108
+ {#each selectedEntries as entry (entry.value)}
109
+ <input type="hidden" {name} value={entry.value} {disabled} />
110
+ {/each}
111
+ {/if}
112
+ </div>
@@ -0,0 +1,75 @@
1
+ import type { Snippet } from 'svelte';
2
+ import type { HTMLAttributes } from 'svelte/elements';
3
+ import { type FieldOption, type FieldOptionInput, type FieldSize } from './field-types.ts';
4
+ interface Props extends Omit<HTMLAttributes<HTMLDivElement>, 'children' | 'id'> {
5
+ /** The options; bare strings are both value and label. */
6
+ options: readonly FieldOptionInput[];
7
+ /**
8
+ * The selected values, in option order. Bindable.
9
+ * @defaultValue `[]`
10
+ */
11
+ values?: string[] | undefined;
12
+ /**
13
+ * Trigger text while nothing is selected.
14
+ * @defaultValue `'Select…'`
15
+ */
16
+ placeholder?: string | undefined;
17
+ /** Posts each selected value under this name, one hidden input per value. */
18
+ name?: string | undefined;
19
+ /**
20
+ * Show at most this many labels in the trigger, then the overflow count.
21
+ * @defaultValue `2`
22
+ */
23
+ maxShown?: number | undefined;
24
+ /**
25
+ * Formats the overflow count, e.g. `(n) => \`+${n} more\``.
26
+ * @defaultValue `(n) => \`+${n}\``
27
+ */
28
+ formatOverflow?: ((hidden: number) => string) | undefined;
29
+ /** Renders one option's body (an icon, a description); defaults to its label. */
30
+ option?: Snippet<[FieldOption, {
31
+ selected: boolean;
32
+ }]> | undefined;
33
+ /**
34
+ * Render the listbox into `<body>`.
35
+ * @defaultValue `true`
36
+ */
37
+ portal?: boolean | undefined;
38
+ /**
39
+ * Disables the trigger.
40
+ * @defaultValue `false`
41
+ */
42
+ disabled?: boolean | undefined;
43
+ /**
44
+ * daisyUI size step of the trigger.
45
+ * @defaultValue `'md'`
46
+ */
47
+ size?: FieldSize | undefined;
48
+ /** `FieldControl`: the trigger's `id`, which `FormField`'s label points at. Minted when omitted. */
49
+ id?: string | undefined;
50
+ /**
51
+ * `FieldControl`: the visible label's id. Not used — the trigger is a
52
+ * `<button>`, so the `FormField`'s `<label for>` already names it; taken
53
+ * here so a spread `FieldControl` does not leak it as an attribute.
54
+ */
55
+ labelId?: string | undefined;
56
+ /**
57
+ * `FieldControl`: space-separated ids for `aria-describedby`.
58
+ * @defaultValue `''`
59
+ */
60
+ describedBy?: string | undefined;
61
+ /**
62
+ * `FieldControl`: the field is in error — sets `aria-invalid` and `select-error`.
63
+ * @defaultValue `false`
64
+ */
65
+ invalid?: boolean | undefined;
66
+ /**
67
+ * `FieldControl`: sets `aria-required` on the trigger.
68
+ * @defaultValue `false`
69
+ */
70
+ required?: boolean | undefined;
71
+ }
72
+ /** Several choices from a known list in a compact trigger that summarises them, on bits-ui's `Select` (`type="multiple"`). */
73
+ declare const MultiSelect: import("svelte").Component<Props, {}, "values">;
74
+ type MultiSelect = ReturnType<typeof MultiSelect>;
75
+ export default MultiSelect;
@@ -0,0 +1,190 @@
1
+ <!-- @component
2
+ An exact number the reader types or nudges, shown locale-formatted (units, percent, currency) with − / + stepper buttons: the APG spinbutton.
3
+ -->
4
+ <!--
5
+ NumberField — a `role="spinbutton"` text input showing its value through
6
+ `Intl.NumberFormat`, plus − / + buttons, stepping clamped to `min` / `max`.
7
+
8
+ Hand-rolled, and why that is acceptable here (AGENTS.md asks for the
9
+ reason): bits-ui 2.20 has no number field, and the APG spinbutton pattern is
10
+ one input plus two buttons — no popover, no roving focus, no ARIA
11
+ relationship beyond `aria-controls` — so every part of the contract is
12
+ small and pinned by `tests/fields/NumberField.test.ts`. It replaces the
13
+ native `<input type="number">` for callers who need what that cannot do:
14
+ locale formatting and parsing ("1.234,5" in `de-DE`), a `number` value
15
+ rather than a string, no accidental scroll-wheel changes (there is no wheel
16
+ handling at all), and stepper buttons that are labelled and styleable.
17
+
18
+ Editing: while focused the input holds the text as typed. On blur or Enter
19
+ it is parsed with the locale's separators (`number-format.ts`), clamped to
20
+ `min` / `max`, rounded to the step's or the format's precision, written to
21
+ `value`, re-formatted, and passed to `onCommit`. Unparseable text reverts
22
+ to the last valid value; clearing the field commits `undefined`.
23
+
24
+ ARIA contract (APG spinbutton): the input is `role="spinbutton"` with
25
+ `aria-valuenow`, `aria-valuemin` / `aria-valuemax` when set, and
26
+ `aria-valuetext` — the formatted string, so "45%" is read rather than
27
+ "0.45" — plus `inputmode` (`numeric` when the format has no fraction
28
+ digits, else `decimal`) and the `FieldControl` wiring. The − / + are real
29
+ buttons named by `decrementLabel` / `incrementLabel`, `aria-controls` the
30
+ input, `tabindex="-1"` so the field stays one tab stop, and disabled at the
31
+ bounds. Keys: Arrow Up / Down ± `step`, Page Up / Down ± `largeStep`,
32
+ Home / End to `min` / `max` (when set), Enter commits.
33
+
34
+ Press-and-hold on − / + auto-repeats: one step on press, then after
35
+ `REPEAT_DELAY_MS` it repeats, speeding up to `REPEAT_MIN_MS`, as GTK's spin
36
+ button, WinUI's NumberBox and SwiftUI's Stepper do. The pointer is captured,
37
+ so moving off the button keeps the capture and `pointerup` /
38
+ `pointercancel` / `lostpointercapture` (or reaching a bound) stops it. A
39
+ pointer press never moves focus off the input. A click with no pointer
40
+ press before it (assistive technology activating the button) steps once.
41
+
42
+ `name` posts the raw number through a hidden input, never the formatted
43
+ text, so a server never parses "1.234,5 cm".
44
+ -->
45
+ <script lang="ts">import { Minus, Plus } from "@lucide/svelte";
46
+ import { getComponentsContext } from "../context.svelte.js";
47
+ import { inputButtonSize, inputSize } from "./field-types.js";
48
+ import { clamp, formatNumber, parseNumber, roundTo, valuePrecision } from "./number-format.js";
49
+ /** How long a press is held before it starts repeating. */
50
+ const REPEAT_DELAY_MS = 400;
51
+ /** The first repeat interval; each repeat shortens it towards `REPEAT_MIN_MS`. */
52
+ const REPEAT_START_MS = 150;
53
+ const REPEAT_MIN_MS = 40;
54
+ let { value = $bindable(), min, max, step = 1, largeStep, formatOptions, locales = getComponentsContext().locale(), showStepper = true, decrementLabel = "Decrease", incrementLabel = "Increase", onCommit, name, size = "md", disabled = false, id: idProp, describedBy = "", labelId: _labelId, invalid = false, class: className, onkeydown, onblur, ...rest } = $props();
55
+ const uid = $props.id();
56
+ const id = $derived(idProp ?? uid);
57
+ const formatted = $derived(formatNumber(value, locales, formatOptions));
58
+ /** The input's text: the formatted value, overwritten by typing until the next commit. */
59
+ let text = $derived(formatted);
60
+ const precision = $derived(valuePrecision(step, locales, formatOptions));
61
+ const atMin = $derived(value !== undefined && min !== undefined && value <= min);
62
+ const atMax = $derived(value !== undefined && max !== undefined && value >= max);
63
+ let input = $state(null);
64
+ let repeat;
65
+ /** Write a new value: clamp, round, re-format, and tell `onCommit`. */
66
+ function commit(next) {
67
+ value = next === undefined ? undefined : roundTo(clamp(next, min, max), precision);
68
+ text = formatNumber(value, locales, formatOptions);
69
+ onCommit?.(value);
70
+ }
71
+ function commitText() {
72
+ if (text === formatted) return;
73
+ const parsed = parseNumber(text, locales, formatOptions);
74
+ if (parsed !== undefined && Number.isNaN(parsed)) {
75
+ text = formatted;
76
+ return;
77
+ }
78
+ commit(parsed);
79
+ }
80
+ /** Move by `delta`; from empty, land on `min` (or 0) rather than stepping. */
81
+ function stepBy(delta) {
82
+ commit(value === undefined ? min ?? 0 : value + delta);
83
+ }
84
+ const keySteps = {
85
+ ArrowUp: () => step,
86
+ ArrowDown: () => -step,
87
+ PageUp: () => largeStep ?? step * 10,
88
+ PageDown: () => -(largeStep ?? step * 10)
89
+ };
90
+ function handleKeydown(event) {
91
+ const delta = keySteps[event.key]?.();
92
+ if (delta !== undefined) {
93
+ event.preventDefault();
94
+ stepBy(delta);
95
+ } else if (event.key === "Home" && min !== undefined) {
96
+ event.preventDefault();
97
+ commit(min);
98
+ } else if (event.key === "End" && max !== undefined) {
99
+ event.preventDefault();
100
+ commit(max);
101
+ } else if (event.key === "Enter") {
102
+ commitText();
103
+ }
104
+ }
105
+ function stopRepeat() {
106
+ clearTimeout(repeat);
107
+ repeat = undefined;
108
+ }
109
+ /** Step now, then keep stepping while held, faster each time, until a bound. */
110
+ function startRepeat(event, delta) {
111
+ if (event.button !== 0 || disabled) return;
112
+ event.preventDefault();
113
+ input?.focus();
114
+ try {
115
+ event.currentTarget.setPointerCapture(event.pointerId);
116
+ } catch {}
117
+ stepBy(delta);
118
+ const tick = (interval) => {
119
+ repeat = setTimeout(() => {
120
+ if (delta > 0 && atMax || delta < 0 && atMin) return stopRepeat();
121
+ stepBy(delta);
122
+ tick(Math.max(REPEAT_MIN_MS, interval * .8));
123
+ }, interval);
124
+ };
125
+ stopRepeat();
126
+ repeat = setTimeout(() => tick(REPEAT_START_MS), REPEAT_DELAY_MS - REPEAT_START_MS);
127
+ }
128
+ /** A click that no pointer press preceded (`detail === 0`): assistive technology, step once. */
129
+ function clickStep(event, delta) {
130
+ if (event.detail === 0) stepBy(delta);
131
+ }
132
+ </script>
133
+
134
+ {#snippet stepper(delta: number, label: string, bounded: boolean)}
135
+ <button
136
+ type="button"
137
+ tabindex="-1"
138
+ aria-controls={id}
139
+ disabled={disabled || bounded}
140
+ class={['btn btn-square btn-ghost', inputButtonSize[size]]}
141
+ onpointerdown={(event) => startRepeat(event, delta)}
142
+ onpointerup={stopRepeat}
143
+ onpointercancel={stopRepeat}
144
+ onlostpointercapture={stopRepeat}
145
+ onclick={(event) => clickStep(event, delta)}
146
+ >
147
+ {#if delta < 0}
148
+ <Minus class="size-4" aria-hidden="true" />
149
+ {:else}
150
+ <Plus class="size-4" aria-hidden="true" />
151
+ {/if}
152
+ <span class="sr-only">{label}</span>
153
+ </button>
154
+ {/snippet}
155
+
156
+ <div class={['input w-full pe-1', inputSize[size], invalid && 'input-error', className]}>
157
+ <input
158
+ {...rest}
159
+ bind:this={input}
160
+ {id}
161
+ type="text"
162
+ role="spinbutton"
163
+ autocomplete="off"
164
+ inputmode={formatOptions?.maximumFractionDigits === 0 ? 'numeric' : 'decimal'}
165
+ {disabled}
166
+ aria-valuenow={value}
167
+ aria-valuemin={min}
168
+ aria-valuemax={max}
169
+ aria-valuetext={value === undefined ? undefined : formatted}
170
+ aria-describedby={describedBy === '' ? undefined : describedBy}
171
+ aria-invalid={invalid ? 'true' : undefined}
172
+ bind:value={text}
173
+ onkeydown={(event) => {
174
+ handleKeydown(event);
175
+ onkeydown?.(event);
176
+ }}
177
+ onblur={(event) => {
178
+ stopRepeat();
179
+ commitText();
180
+ onblur?.(event);
181
+ }}
182
+ />
183
+ {#if showStepper}
184
+ {@render stepper(-step, decrementLabel, atMin)}
185
+ {@render stepper(step, incrementLabel, atMax)}
186
+ {/if}
187
+ {#if name !== undefined}
188
+ <input type="hidden" {name} value={value === undefined ? '' : String(value)} {disabled} />
189
+ {/if}
190
+ </div>