@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
@@ -21,57 +21,27 @@ A modal on bits-ui’s `Dialog`, wearing daisyUI’s `modal-box`.
21
21
  `trigger` receives the props to spread, so an existing `Button` keeps its
22
22
  own markup and its own accessible name; `actions` receives `close`, so the
23
23
  action row's buttons can dismiss without reaching for the bindable `open`.
24
+
25
+ The modal itself is `DialogFrame`, shared with `Drawer`: this file only
26
+ chooses the centred `modal-box`.
24
27
  -->
25
- <script lang="ts">import { X } from "@lucide/svelte";
26
- import { Dialog } from "bits-ui";
27
- import Button from "../ui/Button.svelte";
28
- import VisuallyHidden from "../ui/VisuallyHidden.svelte";
28
+ <script lang="ts">import DialogFrame from "./DialogFrame.svelte";
29
29
  import { dialogSize } from "./sizes.js";
30
30
  let { open = $bindable(false), title, description, closeLabel = "Close", size = "md", class: className, trigger, actions, children } = $props();
31
- const close = () => {
32
- open = false;
33
- };
34
31
  </script>
35
32
 
36
- <Dialog.Root bind:open>
37
- {#if trigger}
38
- <Dialog.Trigger>
39
- {#snippet child({ props })}
40
- {@render trigger({ props })}
41
- {/snippet}
42
- </Dialog.Trigger>
43
- {/if}
44
- <Dialog.Portal>
45
- <!-- daisyUI's `modal-backdrop` is a child of an open `.modal`; this is
46
- the same scrim as an element bits-ui can animate and dismiss from. -->
47
- <Dialog.Overlay class="bg-base-300/60 fixed inset-0 z-40" />
48
- <Dialog.Content
49
- class={[
50
- 'modal-box fixed top-1/2 left-1/2 z-50 max-h-[calc(100svh-2rem)] w-[calc(100vw-2rem)] -translate-x-1/2 -translate-y-1/2',
51
- dialogSize[size],
52
- className,
53
- ]}
54
- >
55
- <div class="flex items-start justify-between gap-4">
56
- <Dialog.Title class="text-lg font-semibold">{title}</Dialog.Title>
57
- <Dialog.Close>
58
- {#snippet child({ props })}
59
- <Button {...props} variant="ghost" size="sm" class="btn-square -mt-1 -mr-1">
60
- <X class="size-4" aria-hidden="true" />
61
- <VisuallyHidden>{closeLabel}</VisuallyHidden>
62
- </Button>
63
- {/snippet}
64
- </Dialog.Close>
65
- </div>
66
- {#if description !== undefined}
67
- <Dialog.Description class="text-base-content/70 mt-1 text-sm leading-6">
68
- {description}
69
- </Dialog.Description>
70
- {/if}
71
- <div class="py-4">{@render children()}</div>
72
- {#if actions}
73
- <div class="modal-action">{@render actions({ close })}</div>
74
- {/if}
75
- </Dialog.Content>
76
- </Dialog.Portal>
77
- </Dialog.Root>
33
+ <DialogFrame
34
+ bind:open
35
+ {title}
36
+ {description}
37
+ {closeLabel}
38
+ {trigger}
39
+ {actions}
40
+ {children}
41
+ layout="modal"
42
+ contentClass={[
43
+ 'modal-box fixed top-1/2 left-1/2 z-50 max-h-[calc(100svh-2rem)] w-[calc(100vw-2rem)] -translate-x-1/2 -translate-y-1/2',
44
+ dialogSize[size],
45
+ className,
46
+ ]}
47
+ />
@@ -1,4 +1,3 @@
1
- import { Dialog } from 'bits-ui';
2
1
  import type { Snippet } from 'svelte';
3
2
  import { type DialogSize } from './sizes.ts';
4
3
  interface Props {
@@ -0,0 +1,64 @@
1
+ <!-- @component
2
+ The modal body `Dialog` and `Drawer` share; internal, not exported.
3
+ -->
4
+ <!--
5
+ DialogFrame — one implementation of everything that makes a modal a
6
+ modal, on bits-ui's `Dialog`: the trigger, overlay, focus trap and
7
+ return, Escape, scroll lock, the title and description wiring, the
8
+ labelled close button and the `actions({ close })` row. `Dialog` (a
9
+ centred `modal-box`) and `Drawer` (a panel at an edge) differ only in
10
+ `layout` and the content classes they pass, so the two cannot drift on
11
+ focus, dismissal or labelling.
12
+
13
+ `hideTitle` keeps the title as the accessible name but hides it
14
+ visually: a mobile navigation drawer needs a name, not a heading.
15
+ -->
16
+ <script lang="ts">import { X } from "@lucide/svelte";
17
+ import { Dialog } from "bits-ui";
18
+ import Button from "../ui/Button.svelte";
19
+ import VisuallyHidden from "../ui/VisuallyHidden.svelte";
20
+ let { open = $bindable(false), title, description, closeLabel, hideTitle = false, layout, contentClass, trigger, actions, children } = $props();
21
+ const close = () => {
22
+ open = false;
23
+ };
24
+ const sheet = $derived(layout === "sheet");
25
+ </script>
26
+
27
+ <Dialog.Root bind:open>
28
+ {#if trigger}
29
+ <Dialog.Trigger>
30
+ {#snippet child({ props })}
31
+ {@render trigger({ props })}
32
+ {/snippet}
33
+ </Dialog.Trigger>
34
+ {/if}
35
+ <Dialog.Portal>
36
+ <!-- daisyUI's `modal-backdrop` is a child of an open `.modal`; this is
37
+ the same scrim as an element bits-ui can animate and dismiss from. -->
38
+ <Dialog.Overlay class="bg-base-300/60 fixed inset-0 z-40" />
39
+ <Dialog.Content class={contentClass}>
40
+ <div class={['flex items-start justify-between gap-4', sheet && 'px-4 pt-4']}>
41
+ <Dialog.Title class={hideTitle ? 'sr-only' : 'text-lg font-semibold'}>{title}</Dialog.Title>
42
+ <Dialog.Close>
43
+ {#snippet child({ props })}
44
+ <Button {...props} variant="ghost" size="sm" class="btn-square -mt-1 -mr-1 ms-auto">
45
+ <X class="size-4" aria-hidden="true" />
46
+ <VisuallyHidden>{closeLabel}</VisuallyHidden>
47
+ </Button>
48
+ {/snippet}
49
+ </Dialog.Close>
50
+ </div>
51
+ {#if description !== undefined}
52
+ <Dialog.Description class={['text-base-content/70 mt-1 text-sm leading-6', sheet && 'px-4']}>
53
+ {description}
54
+ </Dialog.Description>
55
+ {/if}
56
+ <div class={sheet ? 'min-h-0 flex-1 overflow-y-auto p-4' : 'py-4'}>{@render children()}</div>
57
+ {#if actions}
58
+ <div class={sheet ? 'border-base-300 flex justify-end gap-2 border-t p-4' : 'modal-action'}>
59
+ {@render actions({ close })}
60
+ </div>
61
+ {/if}
62
+ </Dialog.Content>
63
+ </Dialog.Portal>
64
+ </Dialog.Root>
@@ -0,0 +1,23 @@
1
+ import type { Snippet } from 'svelte';
2
+ import type { ClassValue } from 'svelte/elements';
3
+ interface Props {
4
+ open?: boolean | undefined;
5
+ title: string;
6
+ description?: string | undefined;
7
+ closeLabel: string;
8
+ hideTitle?: boolean | undefined;
9
+ /** `modal`: daisyUI `modal-box` flow. `sheet`: a column with a scrolling body and a pinned footer. */
10
+ layout: 'modal' | 'sheet';
11
+ contentClass: ClassValue;
12
+ trigger?: Snippet<[{
13
+ props: Record<string, unknown>;
14
+ }]> | undefined;
15
+ actions?: Snippet<[{
16
+ close: () => void;
17
+ }]> | undefined;
18
+ children: Snippet<[]>;
19
+ }
20
+ /** The modal body `Dialog` and `Drawer` share; internal, not exported. */
21
+ declare const DialogFrame: import("svelte").Component<Props, {}, "open">;
22
+ type DialogFrame = ReturnType<typeof DialogFrame>;
23
+ export default DialogFrame;
@@ -0,0 +1,43 @@
1
+ <!-- @component
2
+ A modal panel that slides in from an edge: a filters sheet, a mobile action sheet, mobile navigation.
3
+ -->
4
+ <!--
5
+ Drawer — `Dialog`'s contract at an edge of the screen. It shares
6
+ `DialogFrame` with `Dialog`, so the focus trap and return, Escape and
7
+ overlay dismissal, scroll lock, `aria-modal`, the title as the accessible
8
+ name and the labelled close button are one implementation.
9
+
10
+ Not daisyUI's `drawer`: that is driven by a hidden checkbox and a
11
+ `<label>` overlay, which gives no focus trap, no focus move or return, no
12
+ Escape, no `aria-modal` and no name, and leaves the page behind it in the
13
+ Tab order. Only daisyUI's look is borrowed here.
14
+
15
+ The panel is a column: the title row, a body that scrolls on its own, and
16
+ an `actions({ close })` footer that stays put. `start` / `end` follow
17
+ the writing direction, so a `start` drawer opens from the right in RTL.
18
+ It does not animate, so there is no motion to reduce; swipe-to-dismiss is
19
+ out of v1.
20
+ -->
21
+ <script lang="ts">import DialogFrame from "./DialogFrame.svelte";
22
+ import { drawerHeight, drawerSide, drawerWidth } from "./sizes.js";
23
+ let { open = $bindable(false), title, description, side = "end", size = "md", closeLabel = "Close", hideTitle = false, class: className, trigger, actions, children } = $props();
24
+ const horizontal = $derived(side === "start" || side === "end");
25
+ </script>
26
+
27
+ <DialogFrame
28
+ bind:open
29
+ {title}
30
+ {description}
31
+ {closeLabel}
32
+ {hideTitle}
33
+ {trigger}
34
+ {actions}
35
+ {children}
36
+ layout="sheet"
37
+ contentClass={[
38
+ 'bg-base-100 fixed z-50 flex flex-col shadow-xl outline-none',
39
+ drawerSide[side],
40
+ horizontal ? drawerWidth[size] : drawerHeight[size],
41
+ className,
42
+ ]}
43
+ />
@@ -0,0 +1,43 @@
1
+ import type { Snippet } from 'svelte';
2
+ import { type DrawerSide, type DrawerSize } from './sizes.ts';
3
+ interface Props {
4
+ /** Whether the drawer is shown. Bindable. */
5
+ open?: boolean | undefined;
6
+ /** Heading — the panel's accessible name. */
7
+ title: string;
8
+ /** Supporting line under the heading — the accessible description. */
9
+ description?: string | undefined;
10
+ /**
11
+ * The edge the panel enters from.
12
+ * @defaultValue `'end'`
13
+ */
14
+ side?: DrawerSide | undefined;
15
+ /**
16
+ * Width at `start` / `end`, height at `top` / `bottom`.
17
+ * @defaultValue `'md'`
18
+ */
19
+ size?: DrawerSize | undefined;
20
+ /**
21
+ * Accessible name of the corner close button.
22
+ * @defaultValue `'Close'`
23
+ */
24
+ closeLabel?: string | undefined;
25
+ /** Hide the title visually; it stays the accessible name (mobile navigation). */
26
+ hideTitle?: boolean | undefined;
27
+ /** Extra classes on the panel. */
28
+ class?: string | undefined;
29
+ /** The opening control — the caller's own element, spreading `props`. */
30
+ trigger?: Snippet<[{
31
+ props: Record<string, unknown>;
32
+ }]> | undefined;
33
+ /** A footer row that stays in view; receives `close`. */
34
+ actions?: Snippet<[{
35
+ close: () => void;
36
+ }]> | undefined;
37
+ /** The panel body; it scrolls when long. */
38
+ children: Snippet<[]>;
39
+ }
40
+ /** A modal panel that slides in from an edge: a filters sheet, a mobile action sheet, mobile navigation. */
41
+ declare const Drawer: import("svelte").Component<Props, {}, "open">;
42
+ type Drawer = ReturnType<typeof Drawer>;
43
+ export default Drawer;
@@ -1,6 +1,7 @@
1
1
  import type { HTMLAttributes } from 'svelte/elements';
2
2
  import { type Tone } from '../ui/tones.ts';
3
3
  import { type BarSize } from './sizes.ts';
4
+ /** One part of a `SegmentBar`: its label, its share of the bar, and its colour. */
4
5
  export interface BarSegment {
5
6
  /** Read out for this segment in the bar's accessible label. */
6
7
  readonly label: string;
@@ -1,5 +1,6 @@
1
1
  import type { Snippet } from 'svelte';
2
2
  import type { HTMLAttributes } from 'svelte/elements';
3
+ /** The change line a `StatTile` shows under its value: a signed amount, how to render it, and trailing words. */
3
4
  export interface StatDelta {
4
5
  /**
5
6
  * The change. Positive is up. With `unit: 'percent'` it is read as a
@@ -0,0 +1,72 @@
1
+ <!-- @component
2
+ The stages of a multi-step flow as daisyUI `steps`, with the current one marked.
3
+ -->
4
+ <!--
5
+ Steps — a short, fixed sequence (a sign-up wizard, an order's fulfilment,
6
+ "Plan → Sow → Transplant → Harvest") where position and state are the
7
+ content. Unlike `Timeline`, which is dated, open-ended history.
8
+
9
+ Each step's state comes from its own `state`, or, when that is omitted,
10
+ from the list's `current` index: before it is complete, at it is current,
11
+ after it is upcoming. An explicit `state` always wins, so an `error` step
12
+ can sit in a `current`-driven wizard.
13
+
14
+ ARIA contract: an `<ol>` named by `label`, because order is the content.
15
+ Colour carries nothing on its own (WCAG 1.4.1): each step reads its label
16
+ followed by a visually hidden state ("Sow, Completed"). The marker, the
17
+ step number or a ✓ / ✕ glyph, is always a real `step-icon` span with
18
+ `aria-hidden`. Rendering one suppresses daisyUI's `::after` number, and
19
+ CSS generated content would otherwise join the accessible name. The
20
+ current step carries `aria-current="step"`: on its link in link mode, on
21
+ the `<li>` otherwise. Only steps with an `href` are links; whether an
22
+ upcoming step is navigable is the caller's call, not this component's.
23
+ -->
24
+ <script lang="ts">import { getComponentsContext } from "../context.svelte.js";
25
+ import { stepTone } from "../ui/tones.js";
26
+ import { stepsOrientation } from "./sizes.js";
27
+ let { items, label, current, orientation = "horizontal", tone = "primary", completeLabel = "Completed", currentLabel = "Current step", upcomingLabel = "Not started", errorLabel = "Error", marker, class: className, ...rest } = $props();
28
+ const context = getComponentsContext();
29
+ function stateOf(step, index) {
30
+ if (step.state !== undefined) return step.state;
31
+ if (current === undefined || index > current) return "upcoming";
32
+ return index < current ? "complete" : "current";
33
+ }
34
+ const stateLabels = $derived({
35
+ complete: completeLabel,
36
+ current: currentLabel,
37
+ upcoming: upcomingLabel,
38
+ error: errorLabel
39
+ });
40
+ const stateClasses = $derived({
41
+ complete: stepTone[tone],
42
+ current: stepTone[tone],
43
+ upcoming: "",
44
+ error: stepTone.error
45
+ });
46
+ const glyphs = {
47
+ complete: "✓",
48
+ error: "✕"
49
+ };
50
+ </script>
51
+
52
+ <ol {...rest} aria-label={label} class={['steps', stepsOrientation[orientation], className]}>
53
+ {#each items as step, index (step.id)}
54
+ {@const state = stateOf(step, index)}
55
+ {@const ariaCurrent = state === 'current' ? 'step' : undefined}
56
+ <li
57
+ class={['step', stateClasses[state]]}
58
+ aria-current={step.href === undefined ? ariaCurrent : undefined}
59
+ >
60
+ <span class="step-icon" aria-hidden="true">
61
+ {#if marker}{@render marker(step, index)}{:else}{glyphs[state] ?? index + 1}{/if}
62
+ </span>
63
+ {#if step.href !== undefined}
64
+ <a href={context.href(step.href)} class="link link-hover" aria-current={ariaCurrent}>
65
+ {step.label}<span class="sr-only">, {stateLabels[state]}</span>
66
+ </a>
67
+ {:else}
68
+ <span>{step.label}<span class="sr-only">, {stateLabels[state]}</span></span>
69
+ {/if}
70
+ </li>
71
+ {/each}
72
+ </ol>
@@ -0,0 +1,61 @@
1
+ import type { Snippet } from 'svelte';
2
+ import type { HTMLOlAttributes } from 'svelte/elements';
3
+ import { type Tone } from '../ui/tones.ts';
4
+ import { type StepsOrientation } from './sizes.ts';
5
+ /** Where a step stands in the flow. */
6
+ export type StepState = 'complete' | 'current' | 'upcoming' | 'error';
7
+ /** One stage of a `Steps` list. */
8
+ export interface StepItem {
9
+ /** Stable key for the step. */
10
+ readonly id: string;
11
+ /** The step's name, read first. */
12
+ readonly label: string;
13
+ /** Overrides the state derived from the list's `current` index. */
14
+ readonly state?: StepState | undefined;
15
+ /** Makes the step a link (to revisit it), resolved through the components context's `href`. */
16
+ readonly href?: string | undefined;
17
+ }
18
+ interface Props extends Omit<HTMLOlAttributes, 'children'> {
19
+ /** The steps, in order. */
20
+ items: readonly StepItem[];
21
+ /** Accessible name of the list, e.g. `'Checkout progress'`. */
22
+ label: string;
23
+ /**
24
+ * Index of the current step. Steps before it are complete and steps after
25
+ * it upcoming, unless a step sets its own `state`.
26
+ */
27
+ current?: number | undefined;
28
+ /** @defaultValue `'horizontal'` */
29
+ orientation?: StepsOrientation | undefined;
30
+ /**
31
+ * Tone of the complete and current steps.
32
+ * @defaultValue `'primary'`
33
+ */
34
+ tone?: Tone | undefined;
35
+ /**
36
+ * Read after a complete step's label.
37
+ * @defaultValue `'Completed'`
38
+ */
39
+ completeLabel?: string | undefined;
40
+ /**
41
+ * Read after the current step's label.
42
+ * @defaultValue `'Current step'`
43
+ */
44
+ currentLabel?: string | undefined;
45
+ /**
46
+ * Read after an upcoming step's label.
47
+ * @defaultValue `'Not started'`
48
+ */
49
+ upcomingLabel?: string | undefined;
50
+ /**
51
+ * Read after a failed step's label.
52
+ * @defaultValue `'Error'`
53
+ */
54
+ errorLabel?: string | undefined;
55
+ /** Custom marker content per step (an icon), rendered in the `aria-hidden` `step-icon`. */
56
+ marker?: Snippet<[StepItem, number]> | undefined;
57
+ }
58
+ /** The stages of a multi-step flow as daisyUI `steps`, with the current one marked. */
59
+ declare const Steps: import("svelte").Component<Props, {}, "">;
60
+ type Steps = ReturnType<typeof Steps>;
61
+ export default Steps;
@@ -1,12 +1,15 @@
1
1
  import type { Snippet } from 'svelte';
2
2
  import type { HTMLOlAttributes } from 'svelte/elements';
3
+ /** One end of a `Timeline` entry's span: display text plus an optional machine-readable `datetime`. */
3
4
  export interface TimelineDate {
4
5
  /** Display text, e.g. `March 1996`. */
5
6
  readonly label: string;
6
7
  /** Machine-readable `<time datetime>` value, e.g. `1996-03-02`. */
7
8
  readonly dateTime?: string | undefined;
8
9
  }
10
+ /** One dated entry of a `Timeline`: a label, a start, an optional end and link, and an optional theme colour. */
9
11
  export interface TimelineEntry {
12
+ /** Stable key for the entry. */
10
13
  readonly id: string;
11
14
  /** The entry itself — the line in the box. */
12
15
  readonly label: string;
@@ -16,16 +16,19 @@
16
16
  */
17
17
  export { default as ConfirmDialog } from './ConfirmDialog.svelte';
18
18
  export { default as Dialog } from './Dialog.svelte';
19
+ export { default as Drawer } from './Drawer.svelte';
19
20
  export { default as LoadingBoundary } from './LoadingBoundary.svelte';
20
21
  export { default as ProgressBar } from './ProgressBar.svelte';
21
22
  export { default as SegmentBar } from './SegmentBar.svelte';
22
23
  export { default as StatTile } from './StatTile.svelte';
23
24
  export { default as Stats } from './Stats.svelte';
25
+ export { default as Steps } from './Steps.svelte';
24
26
  export { default as Timeline } from './Timeline.svelte';
25
27
  export { default as Toaster } from './Toaster.svelte';
26
28
  export type { ConfirmForm } from './ConfirmDialog.svelte';
27
29
  export type { BarSegment } from './SegmentBar.svelte';
28
30
  export type { StatDelta } from './StatTile.svelte';
31
+ export type { StepItem, StepState } from './Steps.svelte';
29
32
  export type { TimelineDate, TimelineEntry } from './Timeline.svelte';
30
33
  export * from './sizes.ts';
31
34
  export * from './toaster.ts';
@@ -16,11 +16,13 @@
16
16
  */
17
17
  export { default as ConfirmDialog } from './ConfirmDialog.svelte';
18
18
  export { default as Dialog } from './Dialog.svelte';
19
+ export { default as Drawer } from './Drawer.svelte';
19
20
  export { default as LoadingBoundary } from './LoadingBoundary.svelte';
20
21
  export { default as ProgressBar } from './ProgressBar.svelte';
21
22
  export { default as SegmentBar } from './SegmentBar.svelte';
22
23
  export { default as StatTile } from './StatTile.svelte';
23
24
  export { default as Stats } from './Stats.svelte';
25
+ export { default as Steps } from './Steps.svelte';
24
26
  export { default as Timeline } from './Timeline.svelte';
25
27
  export { default as Toaster } from './Toaster.svelte';
26
28
  export * from "./sizes.js";
@@ -25,3 +25,17 @@ export declare const barSize: Record<BarSize, string>;
25
25
  export type StatsOrientation = 'horizontal' | 'vertical' | 'responsive';
26
26
  /** Literal daisyUI class per `StatsOrientation` for `Stats`; a lookup table so Tailwind's scanner sees every class. */
27
27
  export declare const statsOrientation: Record<StatsOrientation, string>;
28
+ /** How a `Steps` list lays its steps out. */
29
+ export type StepsOrientation = 'horizontal' | 'vertical' | 'responsive';
30
+ /** Literal daisyUI class per `StepsOrientation` for `Steps`; a lookup table so Tailwind's scanner sees every class. */
31
+ export declare const stepsOrientation: Record<StepsOrientation, string>;
32
+ /** The edge a `Drawer` enters from; `start` / `end` follow the writing direction. */
33
+ export type DrawerSide = 'start' | 'end' | 'top' | 'bottom';
34
+ /** Literal placement classes per `DrawerSide`; a lookup table so Tailwind's scanner sees every class. */
35
+ export declare const drawerSide: Record<DrawerSide, string>;
36
+ /** Size steps of a `Drawer`: its width at `start` / `end`, its height at `top` / `bottom`. */
37
+ export type DrawerSize = 'sm' | 'md' | 'lg' | 'full';
38
+ /** Literal width class per `DrawerSize` for a `start` / `end` drawer. */
39
+ export declare const drawerWidth: Record<DrawerSize, string>;
40
+ /** Literal height class per `DrawerSize` for a `top` / `bottom` drawer. */
41
+ export declare const drawerHeight: Record<DrawerSize, string>;
@@ -39,3 +39,31 @@ export const statsOrientation = {
39
39
  // dashboard reached for by hand.
40
40
  responsive: 'stats-vertical sm:stats-horizontal',
41
41
  };
42
+ /** Literal daisyUI class per `StepsOrientation` for `Steps`; a lookup table so Tailwind's scanner sees every class. */
43
+ export const stepsOrientation = {
44
+ horizontal: 'steps-horizontal',
45
+ vertical: 'steps-vertical',
46
+ // A column on a phone, a row from `sm` up, as `statsOrientation` does.
47
+ responsive: 'steps-vertical sm:steps-horizontal',
48
+ };
49
+ /** Literal placement classes per `DrawerSide`; a lookup table so Tailwind's scanner sees every class. */
50
+ export const drawerSide = {
51
+ start: 'inset-y-0 start-0 h-full max-w-[calc(100vw-2rem)]',
52
+ end: 'inset-y-0 end-0 h-full max-w-[calc(100vw-2rem)]',
53
+ top: 'inset-x-0 top-0 max-h-[85dvh] rounded-b-box',
54
+ bottom: 'inset-x-0 bottom-0 max-h-[85dvh] rounded-t-box',
55
+ };
56
+ /** Literal width class per `DrawerSize` for a `start` / `end` drawer. */
57
+ export const drawerWidth = {
58
+ sm: 'w-72',
59
+ md: 'w-96',
60
+ lg: 'w-[32rem]',
61
+ full: 'w-screen max-w-none',
62
+ };
63
+ /** Literal height class per `DrawerSize` for a `top` / `bottom` drawer. */
64
+ export const drawerHeight = {
65
+ sm: 'h-[33dvh]',
66
+ md: 'h-[50dvh]',
67
+ lg: 'h-[75dvh]',
68
+ full: 'h-dvh max-h-none rounded-none',
69
+ };
@@ -0,0 +1,60 @@
1
+ <!-- @component
2
+ An inline, always-visible month grid for picking a date, on bits-ui's `Calendar`.
3
+ -->
4
+ <!--
5
+ Calendar — a month (or several side by side) to choose one date from,
6
+ inline rather than in a popover: a booking sidebar, a "sown on" planner, an
7
+ availability view. The popover pickers compose it. `DateInput` (the native
8
+ control) stays the default for a single date on a form; this is for when
9
+ the reader benefits from seeing the month.
10
+
11
+ The value is a `DateValue` from `@internationalized/date`, bits-ui's own
12
+ type: lossless, and it carries its calendar system. Convert at the edge with
13
+ `parseDate('2026-10-23')` and `value.toString()`, both re-exported from the
14
+ `./dates` entry.
15
+
16
+ ARIA contract (APG date-picker grid), from bits-ui: a `role="application"`
17
+ wrapper named by `calendarLabel` and the visible month, one `role="grid"`
18
+ per month, `gridcell`s with `aria-selected` / `aria-disabled`, a roving
19
+ tabindex, and the keys — arrows by day and week, Home / End to the week's
20
+ ends, Page Up / Page Down by month, Enter / Space to select. This file
21
+ adds the labelled previous / next buttons (`previousLabel` / `nextLabel`),
22
+ each grid's name ("October 2026"), a struck-through
23
+ unavailable day, and the `FieldControl` wiring: inside a `FormField` the
24
+ root is `aria-labelledby` the label (`labelId`) and `aria-describedby` the
25
+ hint and errors. `invalid` draws the error border; the `application` role
26
+ does not take `aria-invalid`, so the error text reaches it through
27
+ `describedBy`.
28
+ -->
29
+ <script lang="ts">import { Calendar } from "bits-ui";
30
+ import { getComponentsContext } from "../context.svelte.js";
31
+ import CalendarMonths from "./CalendarMonths.svelte";
32
+ import { calendarRoot } from "./calendar-props.js";
33
+ let { value = $bindable(), month = $bindable(), ...props } = $props();
34
+ const context = getComponentsContext();
35
+ const shared = $derived(calendarRoot(props, context.locale()));
36
+ </script>
37
+
38
+ <Calendar.Root
39
+ {...shared.root}
40
+ type="single"
41
+ bind:placeholder={
42
+ // Unset, bits-ui picks the value's month or today's.
43
+ () => month as DateValue,
44
+ (next: DateValue) => {
45
+ month = next;
46
+ }
47
+ }
48
+ bind:value={
49
+ // bits-ui declares `value?: DateValue` (no `| undefined`) but reads an
50
+ // unset value as `undefined` at runtime; the binding passes it through.
51
+ () => value as DateValue,
52
+ (next: DateValue | undefined) => {
53
+ value = next;
54
+ }
55
+ }
56
+ >
57
+ {#snippet children({ months, weekdays })}
58
+ <CalendarMonths parts={Calendar} {...shared.months} {months} {weekdays} />
59
+ {/snippet}
60
+ </Calendar.Root>
@@ -0,0 +1,11 @@
1
+ import type { DateValue } from '@internationalized/date';
2
+ import { Calendar } from 'bits-ui';
3
+ import { type CalendarBaseProps } from './calendar-props.ts';
4
+ interface Props extends CalendarBaseProps {
5
+ /** The selected date; `undefined` when none. Bindable. */
6
+ value?: DateValue | undefined;
7
+ }
8
+ /** An inline, always-visible month grid for picking a date, on bits-ui's `Calendar`. */
9
+ declare const Calendar: import("svelte").Component<Props, {}, "value" | "month">;
10
+ type Calendar = ReturnType<typeof Calendar>;
11
+ export default Calendar;