@thomasfosterau/components 0.1.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 (240) hide show
  1. package/README.md +241 -56
  2. package/dist/canvas/VectorCanvas.svelte +89 -8
  3. package/dist/canvas/VectorCanvas.svelte.d.ts +19 -1
  4. package/dist/canvas/types.d.ts +1 -1
  5. package/dist/charts/types.d.ts +1 -1
  6. package/dist/citations/list-format.js +3 -1
  7. package/dist/data/ChatBubble.svelte +48 -0
  8. package/dist/data/ChatBubble.svelte.d.ts +33 -0
  9. package/dist/data/DescriptionList.svelte +9 -3
  10. package/dist/data/DescriptionList.svelte.d.ts +1 -1
  11. package/dist/data/DescriptionRow.svelte +6 -5
  12. package/dist/data/MessageThread.svelte +29 -0
  13. package/dist/data/MessageThread.svelte.d.ts +17 -0
  14. package/dist/data/Pagination.svelte +5 -5
  15. package/dist/data/Pagination.svelte.d.ts +8 -0
  16. package/dist/data/PaginationFooter.svelte +2 -1
  17. package/dist/data/PaginationFooter.svelte.d.ts +2 -0
  18. package/dist/data/Table.svelte +9 -3
  19. package/dist/data/description-context.d.ts +12 -6
  20. package/dist/data/description-context.js +6 -5
  21. package/dist/data/index.d.ts +2 -0
  22. package/dist/data/index.js +2 -0
  23. package/dist/data/table-types.d.ts +7 -0
  24. package/dist/feedback/Dialog.svelte +19 -49
  25. package/dist/feedback/Dialog.svelte.d.ts +0 -1
  26. package/dist/feedback/DialogFrame.svelte +64 -0
  27. package/dist/feedback/DialogFrame.svelte.d.ts +23 -0
  28. package/dist/feedback/Drawer.svelte +43 -0
  29. package/dist/feedback/Drawer.svelte.d.ts +43 -0
  30. package/dist/feedback/SegmentBar.svelte.d.ts +1 -0
  31. package/dist/feedback/StatTile.svelte.d.ts +1 -0
  32. package/dist/feedback/Steps.svelte +72 -0
  33. package/dist/feedback/Steps.svelte.d.ts +61 -0
  34. package/dist/feedback/Timeline.svelte.d.ts +3 -0
  35. package/dist/feedback/index.d.ts +3 -0
  36. package/dist/feedback/index.js +2 -0
  37. package/dist/feedback/sizes.d.ts +14 -0
  38. package/dist/feedback/sizes.js +28 -0
  39. package/dist/fields/Calendar.svelte +60 -0
  40. package/dist/fields/Calendar.svelte.d.ts +11 -0
  41. package/dist/fields/CalendarMonths.svelte +86 -0
  42. package/dist/fields/CalendarMonths.svelte.d.ts +30 -0
  43. package/dist/fields/Checkbox.svelte +1 -1
  44. package/dist/fields/Checkbox.svelte.d.ts +6 -0
  45. package/dist/fields/Combobox.svelte +61 -54
  46. package/dist/fields/Combobox.svelte.d.ts +38 -3
  47. package/dist/fields/DateField.svelte +37 -0
  48. package/dist/fields/DateField.svelte.d.ts +20 -0
  49. package/dist/fields/DateInput.svelte +1 -1
  50. package/dist/fields/DateInput.svelte.d.ts +6 -0
  51. package/dist/fields/DatePicker.svelte +47 -0
  52. package/dist/fields/DatePicker.svelte.d.ts +25 -0
  53. package/dist/fields/DateRangePicker.svelte +70 -0
  54. package/dist/fields/DateRangePicker.svelte.d.ts +43 -0
  55. package/dist/fields/Dropzone.svelte +232 -0
  56. package/dist/fields/Dropzone.svelte.d.ts +103 -0
  57. package/dist/fields/Fieldset.svelte +7 -2
  58. package/dist/fields/Fieldset.svelte.d.ts +3 -2
  59. package/dist/fields/FileInput.svelte +3 -2
  60. package/dist/fields/FileInput.svelte.d.ts +6 -0
  61. package/dist/fields/FormField.svelte +27 -3
  62. package/dist/fields/ImageInput.svelte.d.ts +6 -0
  63. package/dist/fields/MultiSelect.svelte +112 -0
  64. package/dist/fields/MultiSelect.svelte.d.ts +75 -0
  65. package/dist/fields/NumberField.svelte +190 -0
  66. package/dist/fields/NumberField.svelte.d.ts +76 -0
  67. package/dist/fields/PasswordInput.svelte +95 -0
  68. package/dist/fields/PasswordInput.svelte.d.ts +58 -0
  69. package/dist/fields/PickerPopover.svelte +47 -0
  70. package/dist/fields/PickerPopover.svelte.d.ts +16 -0
  71. package/dist/fields/PinInput.svelte +83 -0
  72. package/dist/fields/PinInput.svelte.d.ts +66 -0
  73. package/dist/fields/RadioGroup.svelte +3 -0
  74. package/dist/fields/RangeCalendar.svelte +51 -0
  75. package/dist/fields/RangeCalendar.svelte.d.ts +23 -0
  76. package/dist/fields/Rating.svelte +127 -0
  77. package/dist/fields/Rating.svelte.d.ts +86 -0
  78. package/dist/fields/SearchInput.svelte +2 -1
  79. package/dist/fields/SegmentGroup.svelte +91 -0
  80. package/dist/fields/SegmentGroup.svelte.d.ts +20 -0
  81. package/dist/fields/Select.svelte +1 -1
  82. package/dist/fields/Select.svelte.d.ts +6 -0
  83. package/dist/fields/Slider.svelte +194 -0
  84. package/dist/fields/Slider.svelte.d.ts +100 -0
  85. package/dist/fields/TagsInput.svelte +256 -0
  86. package/dist/fields/TagsInput.svelte.d.ts +102 -0
  87. package/dist/fields/TextInput.svelte +1 -1
  88. package/dist/fields/TextInput.svelte.d.ts +6 -0
  89. package/dist/fields/Textarea.svelte +1 -1
  90. package/dist/fields/Textarea.svelte.d.ts +6 -0
  91. package/dist/fields/TimeField.svelte +30 -0
  92. package/dist/fields/TimeField.svelte.d.ts +19 -0
  93. package/dist/fields/Toggle.svelte +1 -1
  94. package/dist/fields/Toggle.svelte.d.ts +6 -0
  95. package/dist/fields/VideoInput.svelte.d.ts +6 -0
  96. package/dist/fields/calendar-props.d.ts +119 -0
  97. package/dist/fields/calendar-props.js +51 -0
  98. package/dist/fields/dates.d.ts +28 -0
  99. package/dist/fields/dates.js +26 -0
  100. package/dist/fields/field-types.d.ts +29 -0
  101. package/dist/fields/field-types.js +32 -0
  102. package/dist/fields/file-validation.d.ts +30 -0
  103. package/dist/fields/file-validation.js +51 -0
  104. package/dist/fields/index.d.ts +9 -0
  105. package/dist/fields/index.js +8 -0
  106. package/dist/fields/latest-search.svelte.d.ts +38 -0
  107. package/dist/fields/latest-search.svelte.js +85 -0
  108. package/dist/fields/number-format.d.ts +52 -0
  109. package/dist/fields/number-format.js +89 -0
  110. package/dist/fields/segmented-field.d.ts +128 -0
  111. package/dist/fields/segmented-field.js +52 -0
  112. package/dist/fields/slider-keys.d.ts +21 -0
  113. package/dist/fields/slider-keys.js +37 -0
  114. package/dist/formatting/Countdown.svelte +110 -0
  115. package/dist/formatting/Countdown.svelte.d.ts +35 -0
  116. package/dist/formatting/Duration.svelte +18 -0
  117. package/dist/formatting/Duration.svelte.d.ts +20 -0
  118. package/dist/formatting/Highlight.svelte +4 -0
  119. package/dist/formatting/Highlight.svelte.d.ts +4 -0
  120. package/dist/formatting/duration.d.ts +44 -0
  121. package/dist/formatting/duration.js +125 -0
  122. package/dist/formatting/index.d.ts +3 -0
  123. package/dist/formatting/index.js +3 -0
  124. package/dist/forms/EntityForm.svelte +53 -45
  125. package/dist/forms/EntityForm.svelte.d.ts +12 -3
  126. package/dist/hotkeys/index.d.ts +5 -0
  127. package/dist/hotkeys/index.js +5 -0
  128. package/dist/hotkeys/match.js +12 -10
  129. package/dist/hotkeys/types.d.ts +4 -2
  130. package/dist/index.d.ts +2 -1
  131. package/dist/index.js +2 -1
  132. package/dist/infobox/Infobox.svelte +2 -2
  133. package/dist/infobox/Infobox.svelte.d.ts +7 -0
  134. package/dist/infobox/InfoboxHeader.svelte +15 -7
  135. package/dist/infobox/InfoboxHeader.svelte.d.ts +7 -0
  136. package/dist/infobox/types.d.ts +5 -5
  137. package/dist/layout/AppShell.svelte +50 -0
  138. package/dist/layout/AppShell.svelte.d.ts +28 -0
  139. package/dist/layout/Cluster.svelte +28 -0
  140. package/dist/layout/Cluster.svelte.d.ts +27 -0
  141. package/dist/layout/Fits.svelte +27 -0
  142. package/dist/layout/Fits.svelte.d.ts +13 -0
  143. package/dist/layout/Flow.svelte +46 -0
  144. package/dist/layout/Flow.svelte.d.ts +16 -0
  145. package/dist/layout/Grid.svelte +23 -0
  146. package/dist/layout/Grid.svelte.d.ts +26 -0
  147. package/dist/layout/HStack.svelte +12 -0
  148. package/dist/layout/HStack.svelte.d.ts +34 -0
  149. package/dist/layout/Hero.svelte +76 -0
  150. package/dist/layout/Hero.svelte.d.ts +36 -0
  151. package/dist/layout/Navbar.svelte +2 -2
  152. package/dist/layout/Sidebar.svelte +1 -1
  153. package/dist/layout/Spacer.svelte +12 -0
  154. package/dist/layout/Spacer.svelte.d.ts +6 -0
  155. package/dist/layout/VStack.svelte +12 -0
  156. package/dist/layout/VStack.svelte.d.ts +29 -0
  157. package/dist/layout/index.d.ts +9 -0
  158. package/dist/layout/index.js +9 -0
  159. package/dist/layout/space.d.ts +44 -0
  160. package/dist/layout/space.js +76 -0
  161. package/dist/search/types.d.ts +3 -0
  162. package/dist/seo/og-image.d.ts +4 -3
  163. package/dist/seo/og-image.js +4 -3
  164. package/dist/tips/tip-stack.d.ts +1 -0
  165. package/dist/tips/tip-store.svelte.d.ts +2 -1
  166. package/dist/ui/Accordion.svelte +75 -0
  167. package/dist/ui/Accordion.svelte.d.ts +50 -0
  168. package/dist/ui/AccordionItem.svelte +73 -0
  169. package/dist/ui/AccordionItem.svelte.d.ts +18 -0
  170. package/dist/ui/Button.svelte +3 -2
  171. package/dist/ui/ButtonGroup.svelte +24 -0
  172. package/dist/ui/ButtonGroup.svelte.d.ts +17 -0
  173. package/dist/ui/ContextMenu.svelte +75 -0
  174. package/dist/ui/ContextMenu.svelte.d.ts +25 -0
  175. package/dist/ui/Disclosure.svelte +7 -0
  176. package/dist/ui/Disclosure.svelte.d.ts +2 -2
  177. package/dist/ui/Divider.svelte +47 -0
  178. package/dist/ui/Divider.svelte.d.ts +28 -0
  179. package/dist/ui/DropdownMenu.svelte +44 -0
  180. package/dist/ui/DropdownMenu.svelte.d.ts +30 -0
  181. package/dist/ui/Menu.svelte +17 -47
  182. package/dist/ui/Menu.svelte.d.ts +3 -24
  183. package/dist/ui/MenuCheckboxItem.svelte +33 -0
  184. package/dist/ui/MenuCheckboxItem.svelte.d.ts +20 -0
  185. package/dist/ui/MenuItem.svelte +29 -31
  186. package/dist/ui/MenuItem.svelte.d.ts +2 -2
  187. package/dist/ui/MenuRadioGroup.svelte +25 -0
  188. package/dist/ui/MenuRadioGroup.svelte.d.ts +20 -0
  189. package/dist/ui/MenuRadioItem.svelte +20 -0
  190. package/dist/ui/MenuRadioItem.svelte.d.ts +15 -0
  191. package/dist/ui/MenuSeparator.svelte +12 -11
  192. package/dist/ui/MenuSub.svelte +24 -0
  193. package/dist/ui/MenuSub.svelte.d.ts +15 -0
  194. package/dist/ui/Spinner.svelte +41 -0
  195. package/dist/ui/Spinner.svelte.d.ts +35 -0
  196. package/dist/ui/Stack.svelte +36 -0
  197. package/dist/ui/Stack.svelte.d.ts +48 -0
  198. package/dist/ui/ToggleButton.svelte +58 -0
  199. package/dist/ui/ToggleButton.svelte.d.ts +45 -0
  200. package/dist/ui/ToggleGroup.svelte +142 -0
  201. package/dist/ui/ToggleGroup.svelte.d.ts +86 -0
  202. package/dist/ui/Toolbar.svelte +40 -0
  203. package/dist/ui/Toolbar.svelte.d.ts +20 -0
  204. package/dist/ui/ToolbarButton.svelte +29 -0
  205. package/dist/ui/ToolbarButton.svelte.d.ts +21 -0
  206. package/dist/ui/ToolbarLink.svelte +21 -0
  207. package/dist/ui/ToolbarLink.svelte.d.ts +12 -0
  208. package/dist/ui/ToolbarSeparator.svelte +22 -0
  209. package/dist/ui/ToolbarSeparator.svelte.d.ts +12 -0
  210. package/dist/ui/ToolbarToggleGroup.svelte +66 -0
  211. package/dist/ui/ToolbarToggleGroup.svelte.d.ts +52 -0
  212. package/dist/ui/accordion-context.d.ts +26 -0
  213. package/dist/ui/accordion-context.js +23 -0
  214. package/dist/ui/context-menu.d.ts +13 -0
  215. package/dist/ui/context-menu.js +13 -0
  216. package/dist/ui/index.d.ts +19 -0
  217. package/dist/ui/index.js +18 -0
  218. package/dist/ui/menu-context.d.ts +13 -15
  219. package/dist/ui/menu-context.js +14 -18
  220. package/dist/ui/menu-parts/CheckboxItemPart.svelte +26 -0
  221. package/dist/ui/menu-parts/CheckboxItemPart.svelte.d.ts +5 -0
  222. package/dist/ui/menu-parts/ItemPart.svelte +29 -0
  223. package/dist/ui/menu-parts/ItemPart.svelte.d.ts +5 -0
  224. package/dist/ui/menu-parts/RadioGroupPart.svelte +24 -0
  225. package/dist/ui/menu-parts/RadioGroupPart.svelte.d.ts +5 -0
  226. package/dist/ui/menu-parts/RadioItemPart.svelte +22 -0
  227. package/dist/ui/menu-parts/RadioItemPart.svelte.d.ts +5 -0
  228. package/dist/ui/menu-parts/SeparatorPart.svelte +16 -0
  229. package/dist/ui/menu-parts/SeparatorPart.svelte.d.ts +5 -0
  230. package/dist/ui/menu-parts/SubPart.svelte +28 -0
  231. package/dist/ui/menu-parts/SubPart.svelte.d.ts +5 -0
  232. package/dist/ui/menu-parts/menu-parts.d.ts +104 -0
  233. package/dist/ui/menu-parts/menu-parts.js +31 -0
  234. package/dist/ui/menu-parts/parts.d.ts +12 -0
  235. package/dist/ui/menu-parts/parts.js +27 -0
  236. package/dist/ui/toggle-classes.d.ts +39 -0
  237. package/dist/ui/toggle-classes.js +36 -0
  238. package/dist/ui/tones.d.ts +35 -0
  239. package/dist/ui/tones.js +90 -0
  240. package/package.json +34 -18
@@ -18,6 +18,12 @@ interface Props extends Omit<HTMLTextareaAttributes, 'value'> {
18
18
  * @defaultValue `''`
19
19
  */
20
20
  describedBy?: string | undefined;
21
+ /**
22
+ * `FieldControl`: the visible label's id. Not used — this control is
23
+ * labelable, so the `FormField`'s `<label for>` already names it; taken
24
+ * here so a spread `FieldControl` does not leak it as an attribute.
25
+ */
26
+ labelId?: string | undefined;
21
27
  }
22
28
  /** A daisyUI `textarea` with a bindable `value` and `FieldControl` wiring. `rows` defaults to `4`. */
23
29
  declare const Textarea: import("svelte").Component<Props, {}, "value">;
@@ -0,0 +1,30 @@
1
+ <!-- @component
2
+ Segmented hour / minute entry on bits-ui `TimeField`, with the locale's or a forced 12/24-hour clock.
3
+ -->
4
+ <!--
5
+ TimeField — a time of day typed into segments (hour, minute, optionally
6
+ second, and AM/PM on a 12-hour clock), each a `role="spinbutton"`. The
7
+ clock follows the locale unless `hourCycle` forces 12 or 24, so the
8
+ control no longer depends on what the browser's native time input does.
9
+
10
+ ARIA contract: see `SegmentGroup`. With `name`, bits-ui posts the ISO
11
+ time (`14:30:00`) through a hidden input.
12
+ -->
13
+ <script lang="ts">import { TimeField } from "bits-ui";
14
+ import { getComponentsContext } from "../context.svelte.js";
15
+ import { definedProps } from "./calendar-props.js";
16
+ import SegmentGroup from "./SegmentGroup.svelte";
17
+ import { segmentedField } from "./segmented-field.js";
18
+ let { value = $bindable(), minValue, maxValue, granularity = "minute", ...shared } = $props();
19
+ const context = getComponentsContext();
20
+ const field = $derived(segmentedField(shared, context.locale()));
21
+ </script>
22
+
23
+ <TimeField.Root
24
+ bind:value={() => value as Time, (next) => (value = next)}
25
+ {...field.root}
26
+ {...definedProps({ minValue, maxValue })}
27
+ {granularity}
28
+ >
29
+ <SegmentGroup parts={TimeField} group={field.group} />
30
+ </TimeField.Root>
@@ -0,0 +1,19 @@
1
+ import { TimeField } from 'bits-ui';
2
+ import { type SegmentedFieldProps, type TimeValue } from './segmented-field.ts';
3
+ interface Props extends SegmentedFieldProps {
4
+ /** The time; `undefined` while empty or incomplete. Bindable. */
5
+ value?: TimeValue | undefined;
6
+ /** Earliest valid time. */
7
+ minValue?: TimeValue | undefined;
8
+ /** Latest valid time. */
9
+ maxValue?: TimeValue | undefined;
10
+ /**
11
+ * The smallest segment shown.
12
+ * @defaultValue `'minute'`
13
+ */
14
+ granularity?: 'hour' | 'minute' | 'second' | undefined;
15
+ }
16
+ /** Segmented hour / minute entry on bits-ui `TimeField`, with the locale's or a forced 12/24-hour clock. */
17
+ declare const TimeField: import("svelte").Component<Props, {}, "value">;
18
+ type TimeField = ReturnType<typeof TimeField>;
19
+ export default TimeField;
@@ -23,7 +23,7 @@ A switch for an on/off setting that takes effect immediately, rather than a chec
23
23
  for the caller to report — a switch is not the place to render an error.
24
24
  -->
25
25
  <script lang="ts">import { toggleSize } from "./field-types.js";
26
- let { checked = $bindable(false), label, description, onChange, size = "md", invalid = false, describedBy = "", disabled = false, class: className, ...rest } = $props();
26
+ let { checked = $bindable(false), label, description, onChange, size = "md", invalid = false, describedBy = "", labelId: _labelId, disabled = false, class: className, ...rest } = $props();
27
27
  const uid = $props.id();
28
28
  const descriptionId = `${uid}-description`;
29
29
  let pending = $state(false);
@@ -25,6 +25,12 @@ interface Props extends Omit<HTMLInputAttributes, 'type' | 'checked' | 'size' |
25
25
  * @defaultValue `''`
26
26
  */
27
27
  describedBy?: string | undefined;
28
+ /**
29
+ * `FieldControl`: the visible label's id. Not used — this control is
30
+ * labelable, so the `FormField`'s `<label for>` already names it; taken
31
+ * here so a spread `FieldControl` does not leak it as an attribute.
32
+ */
33
+ labelId?: string | undefined;
28
34
  }
29
35
  /** A switch for an on/off setting that takes effect immediately, rather than a checkbox submitted with a form. */
30
36
  declare const Toggle: import("svelte").Component<Props, {}, "checked">;
@@ -33,6 +33,12 @@ interface Props extends Omit<HTMLInputAttributes, 'type' | 'value' | 'files' | '
33
33
  invalid?: boolean | undefined;
34
34
  /** `FieldControl`: space-separated ids for `aria-describedby`. */
35
35
  describedBy?: string | undefined;
36
+ /**
37
+ * `FieldControl`: the visible label's id. Not used — the file input is
38
+ * labelable, so `<label for>` names it; passed through to `FileInput`,
39
+ * which drops it.
40
+ */
41
+ labelId?: string | undefined;
36
42
  }
37
43
  /** A `FileInput` plus the progress readout for an upload the caller performs. */
38
44
  declare const VideoInput: import("svelte").Component<Props, {}, "progress" | "file">;
@@ -0,0 +1,119 @@
1
+ /**
2
+ * Small helpers `Calendar` and `RangeCalendar` share when handing their props
3
+ * to bits-ui.
4
+ *
5
+ * @module
6
+ */
7
+ import type { DateValue } from '@internationalized/date';
8
+ import type { Snippet } from 'svelte';
9
+ import type { HTMLAttributes } from 'svelte/elements';
10
+ /** bits-ui's calendars take ONE locale string; ours takes `Intl`'s list. The first entry wins. */
11
+ export declare function firstLocale(locales: string | readonly string[] | undefined): string | undefined;
12
+ /**
13
+ * Drop the `undefined` entries of an options object. The workspace compiles
14
+ * with `exactOptionalPropertyTypes`, and bits-ui declares its optional props
15
+ * as bare `?: T`, so an option this library leaves unset must be absent, not
16
+ * `undefined`, when it is spread onto a bits-ui root.
17
+ */
18
+ export declare function definedProps<T extends Record<string, unknown>>(options: T): {
19
+ [K in keyof T]?: Exclude<T[K], undefined>;
20
+ };
21
+ /**
22
+ * The props `Calendar` and `RangeCalendar` share — everything but the value.
23
+ * Each maps onto the bits-ui root prop of the same name, except `locales`
24
+ * (bits-ui takes one locale; the first entry is used) and the `FieldControl`
25
+ * members.
26
+ */
27
+ export interface CalendarBaseProps extends Omit<HTMLAttributes<HTMLDivElement>, 'placeholder' | 'children' | 'id'> {
28
+ /**
29
+ * A date in the month to show (bits-ui's `placeholder`); omitted, the
30
+ * selected date's month, else today's. Bindable: it follows paging.
31
+ */
32
+ month?: DateValue | undefined;
33
+ /** Earliest selectable date. */
34
+ minValue?: DateValue | undefined;
35
+ /** Latest selectable date. */
36
+ maxValue?: DateValue | undefined;
37
+ /** Days that cannot be chosen at all; rendered disabled. */
38
+ isDateDisabled?: ((date: DateValue) => boolean) | undefined;
39
+ /** Days shown but marked unavailable — booked, frost, closed. */
40
+ isDateUnavailable?: ((date: DateValue) => boolean) | undefined;
41
+ /**
42
+ * Months shown side by side.
43
+ * @defaultValue `1`
44
+ */
45
+ numberOfMonths?: number | undefined;
46
+ /** 0 = Sunday … 6 = Saturday; omitted, the locale's own week start. */
47
+ weekStartsOn?: 0 | 1 | 2 | 3 | 4 | 5 | 6 | undefined;
48
+ /**
49
+ * Always render six weeks, so the grid never changes height.
50
+ * @defaultValue `false`
51
+ */
52
+ fixedWeeks?: boolean | undefined;
53
+ /** BCP 47 locale(s); the first is used. Falls back to the context's `locale()`. */
54
+ locales?: string | readonly string[] | undefined;
55
+ /**
56
+ * Accessible name of the calendar, before the visible month.
57
+ * @defaultValue `'Calendar'`
58
+ */
59
+ calendarLabel?: string | undefined;
60
+ /**
61
+ * Accessible name of the previous-month button.
62
+ * @defaultValue `'Previous month'`
63
+ */
64
+ previousLabel?: string | undefined;
65
+ /**
66
+ * Accessible name of the next-month button.
67
+ * @defaultValue `'Next month'`
68
+ */
69
+ nextLabel?: string | undefined;
70
+ /**
71
+ * No interaction at all.
72
+ * @defaultValue `false`
73
+ */
74
+ disabled?: boolean | undefined;
75
+ /**
76
+ * Navigable, but the value cannot change.
77
+ * @defaultValue `false`
78
+ */
79
+ readonly?: boolean | undefined;
80
+ /** Extra decoration in a day cell (a dot for "something planted"). */
81
+ day?: Snippet<[{
82
+ date: DateValue;
83
+ selected: boolean;
84
+ unavailable: boolean;
85
+ }]> | undefined;
86
+ /** `FieldControl`: the root's `id`. */
87
+ id?: string | undefined;
88
+ /** `FieldControl`: the visible label's id; the calendar is `aria-labelledby` it. */
89
+ labelId?: string | undefined;
90
+ /**
91
+ * `FieldControl`: space-separated ids for `aria-describedby`.
92
+ * @defaultValue `''`
93
+ */
94
+ describedBy?: string | undefined;
95
+ /**
96
+ * `FieldControl`: the field is in error — draws the error border.
97
+ * @defaultValue `false`
98
+ */
99
+ invalid?: boolean | undefined;
100
+ /** `FieldControl`: accepted for the spread; a calendar has no empty-value validation of its own. */
101
+ required?: boolean | undefined;
102
+ }
103
+ /** What `calendarRoot` hands `CalendarMonths`. */
104
+ export interface CalendarMonthsOptions {
105
+ readonly locale: string;
106
+ readonly previousLabel: string;
107
+ readonly nextLabel: string;
108
+ readonly day: CalendarBaseProps['day'];
109
+ }
110
+ /**
111
+ * Turn the shared props into what both bits-ui roots and `CalendarMonths`
112
+ * take: the defaults applied, the unset options dropped, the `FieldControl`
113
+ * members mapped onto ARIA, and every other attribute passed through. One
114
+ * function, so `Calendar` and `RangeCalendar` cannot disagree about a default.
115
+ */
116
+ export declare function calendarRoot(props: Omit<CalendarBaseProps, 'month'>, contextLocale: string | readonly string[] | undefined): {
117
+ readonly root: Record<string, unknown>;
118
+ readonly months: CalendarMonthsOptions;
119
+ };
@@ -0,0 +1,51 @@
1
+ /**
2
+ * Small helpers `Calendar` and `RangeCalendar` share when handing their props
3
+ * to bits-ui.
4
+ *
5
+ * @module
6
+ */
7
+ /** bits-ui's calendars take ONE locale string; ours takes `Intl`'s list. The first entry wins. */
8
+ export function firstLocale(locales) {
9
+ return typeof locales === 'string' ? locales : locales?.[0];
10
+ }
11
+ /**
12
+ * Drop the `undefined` entries of an options object. The workspace compiles
13
+ * with `exactOptionalPropertyTypes`, and bits-ui declares its optional props
14
+ * as bare `?: T`, so an option this library leaves unset must be absent, not
15
+ * `undefined`, when it is spread onto a bits-ui root.
16
+ */
17
+ export function definedProps(options) {
18
+ return Object.fromEntries(Object.entries(options).filter(([, value]) => value !== undefined));
19
+ }
20
+ /**
21
+ * Turn the shared props into what both bits-ui roots and `CalendarMonths`
22
+ * take: the defaults applied, the unset options dropped, the `FieldControl`
23
+ * members mapped onto ARIA, and every other attribute passed through. One
24
+ * function, so `Calendar` and `RangeCalendar` cannot disagree about a default.
25
+ */
26
+ export function calendarRoot(props, contextLocale) {
27
+ const { minValue, maxValue, isDateDisabled, isDateUnavailable, numberOfMonths = 1, weekStartsOn, fixedWeeks = false, locales = contextLocale, calendarLabel = 'Calendar', previousLabel = 'Previous month', nextLabel = 'Next month', disabled = false, readonly = false, day, id, labelId, describedBy = '', invalid = false, required: _required, class: className, ...rest } = props;
28
+ const locale = firstLocale(locales) ?? 'en';
29
+ return {
30
+ root: {
31
+ ...rest,
32
+ ...definedProps({ id, minValue, maxValue, isDateDisabled, isDateUnavailable, weekStartsOn }),
33
+ numberOfMonths,
34
+ weekdayFormat: 'short',
35
+ fixedWeeks,
36
+ locale,
37
+ calendarLabel,
38
+ disabled,
39
+ readonly,
40
+ 'aria-labelledby': labelId,
41
+ 'aria-describedby': describedBy === '' ? undefined : describedBy,
42
+ class: [
43
+ 'card card-border bg-base-100 inline-block p-3',
44
+ invalid && 'border-error',
45
+ disabled && 'opacity-50',
46
+ className,
47
+ ],
48
+ },
49
+ months: { locale, previousLabel, nextLabel, day },
50
+ };
51
+ }
@@ -0,0 +1,28 @@
1
+ /**
2
+ * `@thomasfosterau/components/dates` — the bits-ui date controls, behind
3
+ * their own entry.
4
+ *
5
+ * They bring bits-ui's calendar state machines and `@internationalized/date`'s
6
+ * calendar maths, so they are not re-exported from the root barrel or from
7
+ * `fields`: the separate entry makes that cost visible at the import site,
8
+ * lets an app `import()` them lazily on the one page that needs them, and
9
+ * keeps any always-loaded component from pulling them in by accident (the
10
+ * #129 failure mode). `DateInput`, the native control, stays in the barrel
11
+ * and remains the default for a single date on a form.
12
+ *
13
+ * `parseDate`, `parseTime`, `Time` and the `DateValue` type are re-exported
14
+ * so a caller converts at the edge without a direct dependency:
15
+ * `parseDate('2026-10-23')` / `parseTime('14:30')` in, `value.toString()`
16
+ * (ISO) out.
17
+ *
18
+ * @module
19
+ */
20
+ export { default as Calendar } from './Calendar.svelte';
21
+ export { default as DateField } from './DateField.svelte';
22
+ export { default as DatePicker } from './DatePicker.svelte';
23
+ export { default as DateRangePicker } from './DateRangePicker.svelte';
24
+ export { default as RangeCalendar } from './RangeCalendar.svelte';
25
+ export { default as TimeField } from './TimeField.svelte';
26
+ export type { CalendarBaseProps } from './calendar-props.ts';
27
+ export type { PickerProps, SegmentedFieldProps, TimeValue } from './segmented-field.ts';
28
+ export { parseDate, parseTime, Time, type DateValue } from '@internationalized/date';
@@ -0,0 +1,26 @@
1
+ /**
2
+ * `@thomasfosterau/components/dates` — the bits-ui date controls, behind
3
+ * their own entry.
4
+ *
5
+ * They bring bits-ui's calendar state machines and `@internationalized/date`'s
6
+ * calendar maths, so they are not re-exported from the root barrel or from
7
+ * `fields`: the separate entry makes that cost visible at the import site,
8
+ * lets an app `import()` them lazily on the one page that needs them, and
9
+ * keeps any always-loaded component from pulling them in by accident (the
10
+ * #129 failure mode). `DateInput`, the native control, stays in the barrel
11
+ * and remains the default for a single date on a form.
12
+ *
13
+ * `parseDate`, `parseTime`, `Time` and the `DateValue` type are re-exported
14
+ * so a caller converts at the edge without a direct dependency:
15
+ * `parseDate('2026-10-23')` / `parseTime('14:30')` in, `value.toString()`
16
+ * (ISO) out.
17
+ *
18
+ * @module
19
+ */
20
+ export { default as Calendar } from './Calendar.svelte';
21
+ export { default as DateField } from './DateField.svelte';
22
+ export { default as DatePicker } from './DatePicker.svelte';
23
+ export { default as DateRangePicker } from './DateRangePicker.svelte';
24
+ export { default as RangeCalendar } from './RangeCalendar.svelte';
25
+ export { default as TimeField } from './TimeField.svelte';
26
+ export { parseDate, parseTime, Time } from '@internationalized/date';
@@ -12,16 +12,34 @@
12
12
  *
13
13
  * @module
14
14
  */
15
+ import type { Size } from '../ui/tones.ts';
15
16
  /** Wiring a field primitive receives from its enclosing `FormField`. */
16
17
  export interface FieldControl {
17
18
  /** The control's `id`; the `FormField` label points at it. */
18
19
  readonly id: string;
20
+ /**
21
+ * The id of the visible `<label>`. A labelable control (`input`, `select`,
22
+ * `textarea`, `button`, …) ignores it: `<label for>` already names it. A
23
+ * control `<label for>` cannot reach — a `role="slider"` thumb, a
24
+ * `role="group"` / `radiogroup` root, a segmented date group — points
25
+ * `aria-labelledby` at it instead of taking a labelling prop of its own.
26
+ */
27
+ readonly labelId: string;
19
28
  /** Space-separated ids of the hint / error paragraphs; empty when neither is shown. */
20
29
  readonly describedBy: string;
21
30
  /** The field currently shows an error. */
22
31
  readonly invalid: boolean;
23
32
  readonly required: boolean;
24
33
  }
34
+ /**
35
+ * What `<Fieldset>` hands its `children` snippet: the id of its `<legend>`,
36
+ * for a role-based control inside the group (which a legend names only
37
+ * through the `<fieldset>`, a different node) to point `aria-labelledby` at.
38
+ */
39
+ export interface FieldsetControl {
40
+ /** The id of the `<legend>`. */
41
+ readonly labelId: string;
42
+ }
25
43
  /** Size steps shared by the field primitives (daisyUI has no `xl` field). */
26
44
  export type FieldSize = 'xs' | 'sm' | 'md' | 'lg';
27
45
  /** One entry of a `Select`, `RadioGroup` or `CheckboxGroup`. */
@@ -49,3 +67,14 @@ export declare const checkboxSize: Record<FieldSize, string>;
49
67
  export declare const toggleSize: Record<FieldSize, string>;
50
68
  /** Literal daisyUI class per `FieldSize` for `FileInput`; a lookup table so Tailwind's scanner sees every class. */
51
69
  export declare const fileInputSize: Record<FieldSize, string>;
70
+ /**
71
+ * Literal daisyUI class per `FieldSize` for `Slider`; a lookup table so Tailwind's scanner sees every class.
72
+ * Each sets `--range-thumb-size`, which the slider's thumb and track are sized from.
73
+ */
74
+ export declare const rangeSize: Record<FieldSize, string>;
75
+ /** Literal cell size and text-size classes per `FieldSize` for `PinInput`'s cells; a lookup table so Tailwind's scanner sees every class. */
76
+ export declare const pinCellSize: Record<FieldSize, string>;
77
+ /** Literal daisyUI button size per `FieldSize` for a button inside an `input` composite (`NumberField`'s − / +, `PasswordInput`'s reveal toggle), one step smaller than the field so it fits; a lookup table so Tailwind's scanner sees every class. */
78
+ export declare const inputButtonSize: Record<FieldSize, string>;
79
+ /** Literal daisyUI class per `Size` for `Rating`; each sets `--size`, the star height. A lookup table so Tailwind's scanner sees every class. */
80
+ export declare const ratingSize: Record<Size, string>;
@@ -58,3 +58,35 @@ export const fileInputSize = {
58
58
  md: 'file-input-md',
59
59
  lg: 'file-input-lg',
60
60
  };
61
+ /**
62
+ * Literal daisyUI class per `FieldSize` for `Slider`; a lookup table so Tailwind's scanner sees every class.
63
+ * Each sets `--range-thumb-size`, which the slider's thumb and track are sized from.
64
+ */
65
+ export const rangeSize = {
66
+ xs: 'range-xs',
67
+ sm: 'range-sm',
68
+ md: 'range-md',
69
+ lg: 'range-lg',
70
+ };
71
+ /** Literal cell size and text-size classes per `FieldSize` for `PinInput`'s cells; a lookup table so Tailwind's scanner sees every class. */
72
+ export const pinCellSize = {
73
+ xs: 'size-6 text-xs',
74
+ sm: 'size-8 text-sm',
75
+ md: 'size-10 text-base',
76
+ lg: 'size-12 text-lg',
77
+ };
78
+ /** Literal daisyUI button size per `FieldSize` for a button inside an `input` composite (`NumberField`'s − / +, `PasswordInput`'s reveal toggle), one step smaller than the field so it fits; a lookup table so Tailwind's scanner sees every class. */
79
+ export const inputButtonSize = {
80
+ xs: 'btn-xs',
81
+ sm: 'btn-xs',
82
+ md: 'btn-sm',
83
+ lg: 'btn-md',
84
+ };
85
+ /** Literal daisyUI class per `Size` for `Rating`; each sets `--size`, the star height. A lookup table so Tailwind's scanner sees every class. */
86
+ export const ratingSize = {
87
+ xs: 'rating-xs',
88
+ sm: 'rating-sm',
89
+ md: 'rating-md',
90
+ lg: 'rating-lg',
91
+ xl: 'rating-xl',
92
+ };
@@ -0,0 +1,30 @@
1
+ /**
2
+ * The client-side file checks `FileInput` and `Dropzone` share.
3
+ *
4
+ * Pure functions, so the rules are the same wherever a file comes from —
5
+ * the native picker or a drop, which the browser does NOT filter by
6
+ * `accept` — and testable without a DOM.
7
+ *
8
+ * @module
9
+ */
10
+ /** Why `Dropzone` refused a file. */
11
+ export interface FileRejection {
12
+ /** The refused file. */
13
+ readonly file: File;
14
+ /** Which rule refused it. */
15
+ readonly reason: 'too-large' | 'wrong-type' | 'too-many';
16
+ }
17
+ /**
18
+ * Whether `file` satisfies an `accept` string, with the native attribute's
19
+ * syntax: comma-separated `.ext` suffixes, `type/*` families and exact MIME
20
+ * types, all case-insensitive. An absent or empty `accept` accepts anything.
21
+ */
22
+ export declare function acceptMatches(file: File, accept: string | undefined): boolean;
23
+ /** Whether `file` is over `maxBytes`; no cap means never. */
24
+ export declare function exceedsSize(file: File, maxBytes: number | undefined): boolean;
25
+ /**
26
+ * A byte count for a file row, in the largest decimal unit that keeps the
27
+ * number at or above 1 ("820 kB", "4.2 MB"), through `Intl.NumberFormat`'s
28
+ * unit style.
29
+ */
30
+ export declare function formatFileSize(bytes: number, locales: string | readonly string[] | undefined): string;
@@ -0,0 +1,51 @@
1
+ /**
2
+ * The client-side file checks `FileInput` and `Dropzone` share.
3
+ *
4
+ * Pure functions, so the rules are the same wherever a file comes from —
5
+ * the native picker or a drop, which the browser does NOT filter by
6
+ * `accept` — and testable without a DOM.
7
+ *
8
+ * @module
9
+ */
10
+ /**
11
+ * Whether `file` satisfies an `accept` string, with the native attribute's
12
+ * syntax: comma-separated `.ext` suffixes, `type/*` families and exact MIME
13
+ * types, all case-insensitive. An absent or empty `accept` accepts anything.
14
+ */
15
+ export function acceptMatches(file, accept) {
16
+ const tokens = (accept ?? '')
17
+ .split(',')
18
+ .map((token) => token.trim().toLowerCase())
19
+ .filter((token) => token !== '');
20
+ if (tokens.length === 0)
21
+ return true;
22
+ const name = file.name.toLowerCase();
23
+ const type = file.type.toLowerCase();
24
+ return tokens.some((token) => {
25
+ if (token.startsWith('.'))
26
+ return name.endsWith(token);
27
+ if (token.endsWith('/*'))
28
+ return type.startsWith(token.slice(0, -1));
29
+ return type === token;
30
+ });
31
+ }
32
+ /** Whether `file` is over `maxBytes`; no cap means never. */
33
+ export function exceedsSize(file, maxBytes) {
34
+ return maxBytes !== undefined && file.size > maxBytes;
35
+ }
36
+ const UNITS = ['byte', 'kilobyte', 'megabyte', 'gigabyte'];
37
+ /**
38
+ * A byte count for a file row, in the largest decimal unit that keeps the
39
+ * number at or above 1 ("820 kB", "4.2 MB"), through `Intl.NumberFormat`'s
40
+ * unit style.
41
+ */
42
+ export function formatFileSize(bytes, locales) {
43
+ const exponent = Math.min(UNITS.length - 1, Math.max(0, Math.floor(Math.log10(Math.max(bytes, 1)) / 3)));
44
+ const value = bytes / 1000 ** exponent;
45
+ return new Intl.NumberFormat(typeof locales === 'string' || locales === undefined ? locales : [...locales], {
46
+ style: 'unit',
47
+ unit: UNITS[exponent],
48
+ unitDisplay: 'short',
49
+ maximumFractionDigits: exponent === 0 ? 0 : 1,
50
+ }).format(value);
51
+ }
@@ -18,17 +18,26 @@ export { default as Checkbox } from './Checkbox.svelte';
18
18
  export { default as CheckboxGroup } from './CheckboxGroup.svelte';
19
19
  export { default as Combobox } from './Combobox.svelte';
20
20
  export { default as DateInput } from './DateInput.svelte';
21
+ export { default as Dropzone } from './Dropzone.svelte';
21
22
  export { default as FieldErrors } from './FieldErrors.svelte';
22
23
  export { default as Fieldset } from './Fieldset.svelte';
23
24
  export { default as FileInput } from './FileInput.svelte';
24
25
  export { default as FormField } from './FormField.svelte';
25
26
  export { default as ImageInput } from './ImageInput.svelte';
27
+ export { default as MultiSelect } from './MultiSelect.svelte';
28
+ export { default as NumberField } from './NumberField.svelte';
29
+ export { default as PasswordInput } from './PasswordInput.svelte';
30
+ export { default as PinInput } from './PinInput.svelte';
26
31
  export { default as RadioGroup } from './RadioGroup.svelte';
32
+ export { default as Rating } from './Rating.svelte';
27
33
  export { default as SearchInput } from './SearchInput.svelte';
28
34
  export { default as Select } from './Select.svelte';
35
+ export { default as Slider } from './Slider.svelte';
36
+ export { default as TagsInput } from './TagsInput.svelte';
29
37
  export { default as Textarea } from './Textarea.svelte';
30
38
  export { default as TextInput } from './TextInput.svelte';
31
39
  export { default as Toggle } from './Toggle.svelte';
32
40
  export { default as PushToggle, type PushDeclineReason, type PushEnableResult, } from './PushToggle.svelte';
33
41
  export { default as VideoInput } from './VideoInput.svelte';
34
42
  export * from './field-types.ts';
43
+ export type { FileRejection } from './file-validation.ts';
@@ -18,14 +18,22 @@ export { default as Checkbox } from './Checkbox.svelte';
18
18
  export { default as CheckboxGroup } from './CheckboxGroup.svelte';
19
19
  export { default as Combobox } from './Combobox.svelte';
20
20
  export { default as DateInput } from './DateInput.svelte';
21
+ export { default as Dropzone } from './Dropzone.svelte';
21
22
  export { default as FieldErrors } from './FieldErrors.svelte';
22
23
  export { default as Fieldset } from './Fieldset.svelte';
23
24
  export { default as FileInput } from './FileInput.svelte';
24
25
  export { default as FormField } from './FormField.svelte';
25
26
  export { default as ImageInput } from './ImageInput.svelte';
27
+ export { default as MultiSelect } from './MultiSelect.svelte';
28
+ export { default as NumberField } from './NumberField.svelte';
29
+ export { default as PasswordInput } from './PasswordInput.svelte';
30
+ export { default as PinInput } from './PinInput.svelte';
26
31
  export { default as RadioGroup } from './RadioGroup.svelte';
32
+ export { default as Rating } from './Rating.svelte';
27
33
  export { default as SearchInput } from './SearchInput.svelte';
28
34
  export { default as Select } from './Select.svelte';
35
+ export { default as Slider } from './Slider.svelte';
36
+ export { default as TagsInput } from './TagsInput.svelte';
29
37
  export { default as Textarea } from './Textarea.svelte';
30
38
  export { default as TextInput } from './TextInput.svelte';
31
39
  export { default as Toggle } from './Toggle.svelte';
@@ -0,0 +1,38 @@
1
+ /**
2
+ * The search lifecycle `Combobox` and `TagsInput` share: debounce,
3
+ * latest-wins and stale-while-revalidate.
4
+ *
5
+ * debounce a keystroke does not start a request; `debounceMs` of
6
+ * quiet does.
7
+ * latest-wins each request gets an `AbortController`, and starting a
8
+ * new one aborts the last, so a slow first response cannot
9
+ * overwrite a fast second one.
10
+ * stale-while- `results` keeps the previous answer while the next is in
11
+ * revalidate flight, with `pending` true, so a dropdown does not
12
+ * strobe on every keystroke.
13
+ *
14
+ * Reactive (`$state`) so a component reads `results`, `pending` and `error`
15
+ * straight into its template. Call `dispose()` from an `$effect` teardown.
16
+ *
17
+ * @module
18
+ */
19
+ /** An asynchronous search: `signal` aborts when a newer query supersedes this one. */
20
+ export type SearchFn<T> = (query: string, signal: AbortSignal) => Promise<readonly T[]>;
21
+ /** Debounced, latest-wins search state for one input. */
22
+ export declare class LatestSearch<T> {
23
+ #private;
24
+ /** What the last settled search returned. */
25
+ results: readonly T[];
26
+ /** A search is scheduled or in flight. */
27
+ pending: boolean;
28
+ /** The last search's failure message, if it failed. */
29
+ error: string | undefined;
30
+ /** Getters, so the component's current props are read at call time. */
31
+ constructor(search: () => SearchFn<T> | undefined, debounceMs: () => number);
32
+ /** Search for `query` after the debounce, superseding anything earlier. */
33
+ schedule(query: string): void;
34
+ /** Cancel anything scheduled or in flight and empty the results. */
35
+ clear(): void;
36
+ /** Cancel anything scheduled or in flight; for an `$effect` teardown. */
37
+ dispose(): void;
38
+ }