@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
@@ -0,0 +1,44 @@
1
+ <!-- @component
2
+ An application menu behind a trigger — a bits-ui dropdown of `MenuItem`s styled as a daisyUI `menu`.
3
+ -->
4
+ <!--
5
+ DropdownMenu — an application menu: bits-ui's `DropdownMenu`, which owns
6
+ `aria-haspopup`/`aria-expanded` on the trigger, arrow-key roving focus,
7
+ typeahead, Escape to close and focus return. The daisyUI `menu` styling is
8
+ applied to a `<ul>` INSIDE the content; that list and its items' `<li>`
9
+ wrappers carry `role="none"` so the presentation layer does not come
10
+ between `role="menu"` and its `role="menuitem"` children in the
11
+ accessibility tree.
12
+
13
+ It is a separate component from `Menu` (site navigation) so that bits-ui's
14
+ dropdown runtime is reached only by an app that actually renders a
15
+ dropdown: `Menu`, `MenuItem`, `MenuSeparator` and `Sidebar` import nothing
16
+ from bits-ui (#129). The children are the same `MenuItem` /
17
+ `MenuSeparator` a `Menu` takes; this component lends them its bits-ui item
18
+ and separator parts through context (`menu-parts/menu-parts.ts`), and
19
+ the same for `MenuCheckboxItem`, `MenuRadioGroup`, `MenuRadioItem` and
20
+ `MenuSub`.
21
+ -->
22
+ <script lang="ts">import { DropdownMenu } from "bits-ui";
23
+ import { menuParts } from "./menu-parts/parts.js";
24
+ import { setMenuParts } from "./menu-parts/menu-parts.js";
25
+ let { label, open = $bindable(false), side = "bottom", align = "end", class: className, trigger, children } = $props();
26
+ setMenuParts(menuParts("dropdown"));
27
+ </script>
28
+
29
+ <DropdownMenu.Root bind:open>
30
+ {#if trigger}
31
+ <DropdownMenu.Trigger>
32
+ {#snippet child({ props })}
33
+ {@render trigger({ props })}
34
+ {/snippet}
35
+ </DropdownMenu.Trigger>
36
+ {/if}
37
+ <DropdownMenu.Portal>
38
+ <DropdownMenu.Content {side} {align} sideOffset={8} aria-label={label} class="z-50">
39
+ <ul role="none" class={['menu bg-base-100 rounded-box w-56 p-2 shadow-md', className]}>
40
+ {@render children()}
41
+ </ul>
42
+ </DropdownMenu.Content>
43
+ </DropdownMenu.Portal>
44
+ </DropdownMenu.Root>
@@ -0,0 +1,30 @@
1
+ import { DropdownMenu } from 'bits-ui';
2
+ import type { Snippet } from 'svelte';
3
+ interface Props {
4
+ /** Accessible name of the open menu. */
5
+ label: string;
6
+ /** Whether the menu is open. Bindable. */
7
+ open?: boolean | undefined;
8
+ /**
9
+ * Which side of the trigger the menu opens on.
10
+ * @defaultValue `'bottom'`
11
+ */
12
+ side?: 'top' | 'right' | 'bottom' | 'left' | undefined;
13
+ /**
14
+ * Alignment against the trigger.
15
+ * @defaultValue `'end'`
16
+ */
17
+ align?: 'start' | 'center' | 'end' | undefined;
18
+ /** Extra classes on the `<ul class="menu">`. */
19
+ class?: string | undefined;
20
+ /** The trigger — the caller's own element, spreading `props`. */
21
+ trigger?: Snippet<[{
22
+ props: Record<string, unknown>;
23
+ }]> | undefined;
24
+ /** The `MenuItem`s. */
25
+ children: Snippet<[]>;
26
+ }
27
+ /** An application menu behind a trigger — a bits-ui dropdown of `MenuItem`s styled as a daisyUI `menu`. */
28
+ declare const DropdownMenu: import("svelte").Component<Props, {}, "open">;
29
+ type DropdownMenu = ReturnType<typeof DropdownMenu>;
30
+ export default DropdownMenu;
@@ -1,54 +1,24 @@
1
1
  <!-- @component
2
- A daisyUI `menu` as either a labelled navigation list (`mode="nav"`) or a bits-ui dropdown (`mode="dropdown"`).
2
+ A labelled navigation list: a `<nav>` landmark around a daisyUI `menu` of `MenuItem` links.
3
3
  -->
4
4
  <!--
5
- Menu — one component over the two things daisyUI calls a `menu`, because a
6
- call site writes the same `<MenuItem>` list for both.
5
+ Menu — site navigation: a `<nav>` landmark around a `<ul class="menu">` of
6
+ links, where the current page marks itself with `aria-current="page"`.
7
+ Links, list, landmark — no widget, no roving focus, no Escape. Everything
8
+ stays reachable with nothing but Tab, which is the correct behaviour for
9
+ navigation and the wrong behaviour for a menu.
7
10
 
8
- `mode="nav"` is site navigation: a `<nav>` landmark around a `<ul
9
- class="menu">` of links, where the current page marks itself with
10
- `aria-current="page"`. Links, list, landmark — no widget, no roving focus,
11
- no Escape. Everything stays reachable with nothing but Tab, which is the
12
- correct behaviour for navigation and the wrong behaviour for a menu.
13
-
14
- `mode="dropdown"` is an application menu: bits-ui's `DropdownMenu`, which
15
- owns `aria-haspopup`/`aria-expanded` on the trigger, arrow-key roving
16
- focus, typeahead, Escape to close and focus return. The daisyUI `menu`
17
- styling is applied to a `<ul>` INSIDE the content; that list and its items'
18
- `<li>` wrappers carry `role="none"` so the presentation layer does not come
19
- between `role="menu"` and its `role="menuitem"` children in the
20
- accessibility tree.
21
-
22
- The mode reaches `MenuItem` through context rather than a prop, so the two
23
- can never be set inconsistently. See `menu-context.ts`.
11
+ An application menu behind a trigger is `DropdownMenu`, which takes the
12
+ same `MenuItem`s. The two are separate components so this one imports
13
+ nothing from bits-ui: a page that only navigates (`Sidebar` included) never
14
+ loads the dropdown runtime (#129).
24
15
  -->
25
- <script lang="ts">import { DropdownMenu } from "bits-ui";
26
- import { setMenuMode } from "./menu-context.js";
27
- let { mode = "nav", label, open = $bindable(false), side = "bottom", align = "end", class: className, trigger, children } = $props();
28
- setMenuMode(() => mode);
16
+ <script lang="ts">let { label, class: className, children } = $props();
17
+ export {};
29
18
  </script>
30
19
 
31
- {#if mode === 'dropdown'}
32
- <DropdownMenu.Root bind:open>
33
- {#if trigger}
34
- <DropdownMenu.Trigger>
35
- {#snippet child({ props })}
36
- {@render trigger({ props })}
37
- {/snippet}
38
- </DropdownMenu.Trigger>
39
- {/if}
40
- <DropdownMenu.Portal>
41
- <DropdownMenu.Content {side} {align} sideOffset={8} aria-label={label} class="z-50">
42
- <ul role="none" class={['menu bg-base-100 rounded-box w-56 p-2 shadow-md', className]}>
43
- {@render children()}
44
- </ul>
45
- </DropdownMenu.Content>
46
- </DropdownMenu.Portal>
47
- </DropdownMenu.Root>
48
- {:else}
49
- <nav aria-label={label}>
50
- <ul class={['menu w-full gap-1 p-0', className]}>
51
- {@render children()}
52
- </ul>
53
- </nav>
54
- {/if}
20
+ <nav aria-label={label}>
21
+ <ul class={['menu w-full gap-1 p-0', className]}>
22
+ {@render children()}
23
+ </ul>
24
+ </nav>
@@ -1,34 +1,13 @@
1
1
  import type { Snippet } from 'svelte';
2
2
  interface Props {
3
- /**
4
- * `nav` renders a navigation list; `dropdown` a menu behind `trigger`.
5
- * @defaultValue `'nav'`
6
- */
7
- mode?: 'dropdown' | 'nav' | undefined;
8
- /** Accessible name of the navigation landmark / the open menu. */
3
+ /** Accessible name of the navigation landmark. */
9
4
  label: string;
10
- /** Whether the dropdown is open. Bindable; ignored in `nav` mode. */
11
- open?: boolean | undefined;
12
- /**
13
- * Which side of the trigger the dropdown opens on.
14
- * @defaultValue `'bottom'`
15
- */
16
- side?: 'top' | 'right' | 'bottom' | 'left' | undefined;
17
- /**
18
- * Alignment against the trigger.
19
- * @defaultValue `'end'`
20
- */
21
- align?: 'start' | 'center' | 'end' | undefined;
22
5
  /** Extra classes on the `<ul class="menu">`. */
23
6
  class?: string | undefined;
24
- /** The dropdown's trigger — the caller's own element, spreading `props`. */
25
- trigger?: Snippet<[{
26
- props: Record<string, unknown>;
27
- }]> | undefined;
28
7
  /** The `MenuItem`s. */
29
8
  children: Snippet<[]>;
30
9
  }
31
- /** A daisyUI `menu` as either a labelled navigation list (`mode="nav"`) or a bits-ui dropdown (`mode="dropdown"`). */
32
- declare const Menu: import("svelte").Component<Props, {}, "open">;
10
+ /** A labelled navigation list: a `<nav>` landmark around a daisyUI `menu` of `MenuItem` links. */
11
+ declare const Menu: import("svelte").Component<Props, {}, "">;
33
12
  type Menu = ReturnType<typeof Menu>;
34
13
  export default Menu;
@@ -0,0 +1,33 @@
1
+ <!-- @component
2
+ An on/off entry of a `DropdownMenu` or `ContextMenu` (`role="menuitemcheckbox"`).
3
+ -->
4
+ <!--
5
+ MenuCheckboxItem — a setting toggled from a menu ("Show archived"). It
6
+ renders the checkbox item part its enclosing `DropdownMenu` or
7
+ `ContextMenu` lends it through context (`menu-parts/CheckboxItemPart.svelte`),
8
+ so this file imports nothing from bits-ui and costs a navigation-only page
9
+ nothing (#129).
10
+
11
+ ARIA contract: `role="menuitemcheckbox"` with `aria-checked` from bits-ui;
12
+ the check mark is `aria-hidden`. A checkbox item belongs to an application
13
+ menu, not a navigation list, so outside a `DropdownMenu` / `ContextMenu` it
14
+ renders nothing.
15
+ -->
16
+ <script lang="ts">import { getMenuParts } from "./menu-parts/menu-parts.js";
17
+ let { checked = $bindable(false), onCheckedChange, disabled = false, class: className, children } = $props();
18
+ const parts = getMenuParts();
19
+ const change = (next) => {
20
+ checked = next;
21
+ onCheckedChange?.(next);
22
+ };
23
+ </script>
24
+
25
+ {#if parts}
26
+ <parts.checkboxItem
27
+ {checked}
28
+ onCheckedChange={change}
29
+ {disabled}
30
+ class={className}
31
+ label={children}
32
+ />
33
+ {/if}
@@ -0,0 +1,20 @@
1
+ import type { Snippet } from 'svelte';
2
+ interface Props {
3
+ /**
4
+ * Whether the item is checked. Bindable.
5
+ * @defaultValue `false`
6
+ */
7
+ checked?: boolean | undefined;
8
+ /** Called with the next state when the reader toggles it. */
9
+ onCheckedChange?: ((checked: boolean) => void) | undefined;
10
+ /** Shown but not activatable. */
11
+ disabled?: boolean | undefined;
12
+ /** Extra classes on the `<li>`. */
13
+ class?: string | undefined;
14
+ /** The item's label. */
15
+ children: Snippet<[]>;
16
+ }
17
+ /** An on/off entry of a `DropdownMenu` or `ContextMenu` (`role="menuitemcheckbox"`). */
18
+ declare const MenuCheckboxItem: import("svelte").Component<Props, {}, "checked">;
19
+ type MenuCheckboxItem = ReturnType<typeof MenuCheckboxItem>;
20
+ export default MenuCheckboxItem;
@@ -1,9 +1,9 @@
1
1
  <!-- @component
2
- One entry of a `Menu`, in whichever mode the enclosing `Menu` declared.
2
+ One entry of a `Menu` (a navigation link), or of a `DropdownMenu` or `ContextMenu` (a bits-ui menu item).
3
3
  -->
4
4
  <!--
5
- MenuItem — one entry of a `Menu`, in whichever of the two modes the
6
- enclosing `Menu` declared.
5
+ MenuItem — one entry of a `Menu`, `DropdownMenu` or `ContextMenu`; which
6
+ one is decided by the enclosing menu, never by a prop.
7
7
 
8
8
  In `nav` mode it is a plain `<li><a>`: the current page takes daisyUI's
9
9
  `menu-active` AND `aria-current="page"`, because the highlight is a
@@ -11,25 +11,28 @@ One entry of a `Menu`, in whichever mode the enclosing `Menu` declared.
11
11
  context's `isCurrent(href)`, which is what an app without a router-aware
12
12
  context uses.
13
13
 
14
- In `dropdown` mode it renders through bits-ui's `DropdownMenu.Item` `child`
15
- snippet, so the element is still the caller's own `<a>` or `<button>` —
16
- carrying bits-ui's `role="menuitem"`, its focus management and its
17
- selection handling — rather than a `<div>` pretending to be one. An item
18
- with an `href` is a link and must stay a link (middle-click, "open in new
19
- tab"); an item without one is a button.
14
+ Inside a `DropdownMenu` or `ContextMenu` it renders the item part that menu lends it
15
+ (`menu-parts/ItemPart.svelte`, through context), which goes through
16
+ bits-ui's `DropdownMenu.Item` `child` snippet, so the element is still the
17
+ caller's own `<a>` or `<button>` — carrying bits-ui's `role="menuitem"`, its
18
+ focus management and its selection handling — rather than a `<div>`
19
+ pretending to be one. This file imports nothing from bits-ui, so a
20
+ navigation-only page never loads the dropdown runtime (#129). An item with
21
+ an `href` is a link and must stay a link (middle-click, "open in new tab");
22
+ an item without one is a button.
20
23
 
21
- ARIA contract: the `<li>` wrapper is `role="none"` in dropdown mode so it
22
- does not sit between `role="menu"` and `role="menuitem"`. `disabled` is
23
- bits-ui's in dropdown mode (skipped by arrow keys, not activatable) and
24
- `aria-disabled` + no `href` in nav mode, where there is no widget to skip.
24
+ ARIA contract: inside a `DropdownMenu` the `<li>` wrapper is `role="none"`
25
+ so it does not sit between `role="menu"` and `role="menuitem"`, and
26
+ `disabled` is bits-ui's (skipped by arrow keys, not activatable). In a
27
+ navigation list `disabled` is `aria-disabled` + no `href`, where there is
28
+ no widget to skip.
25
29
  The `icon` snippet is decorative — the label carries the meaning.
26
30
  -->
27
- <script lang="ts">import { DropdownMenu } from "bits-ui";
28
- import { getComponentsContext } from "../context.svelte.js";
29
- import { getMenuMode } from "./menu-context.js";
31
+ <script lang="ts">import { getComponentsContext } from "../context.svelte.js";
32
+ import { getMenuParts } from "./menu-parts/menu-parts.js";
30
33
  let { href, onSelect, current, disabled = false, icon, class: className, children } = $props();
31
34
  const context = getComponentsContext();
32
- const mode = getMenuMode();
35
+ const parts = getMenuParts();
33
36
  const resolved = $derived(href === undefined ? undefined : context.href(href));
34
37
  // bits-ui's `onSelect` is not optional-friendly under
35
38
  // `exactOptionalPropertyTypes`; a stable wrapper is simpler than a spread.
@@ -42,20 +45,15 @@ const isCurrent = $derived(current ?? (resolved === undefined ? false : context.
42
45
  {@render children()}
43
46
  {/snippet}
44
47
 
45
- {#if mode() === 'dropdown'}
46
- <li role="none" class={className}>
47
- <DropdownMenu.Item {disabled} onSelect={select}>
48
- {#snippet child({ props })}
49
- {#if resolved === undefined}
50
- <button {...props} type="button">{@render label()}</button>
51
- {:else}
52
- <a {...props} href={resolved} aria-current={isCurrent ? 'page' : undefined}>
53
- {@render label()}
54
- </a>
55
- {/if}
56
- {/snippet}
57
- </DropdownMenu.Item>
58
- </li>
48
+ {#if parts}
49
+ <parts.item
50
+ href={resolved}
51
+ current={isCurrent}
52
+ {disabled}
53
+ onSelect={select}
54
+ class={className}
55
+ {label}
56
+ />
59
57
  {:else}
60
58
  <li class={className}>
61
59
  {#if resolved === undefined}
@@ -12,10 +12,10 @@ interface Props {
12
12
  icon?: Snippet<[]> | undefined;
13
13
  /** Extra classes on the `<li>`. */
14
14
  class?: string | undefined;
15
- /** The item's content: a link or button in `nav` mode, the label in `dropdown` mode. */
15
+ /** The item's label. */
16
16
  children: Snippet<[]>;
17
17
  }
18
- /** One entry of a `Menu`, in whichever mode the enclosing `Menu` declared. */
18
+ /** One entry of a `Menu` (a navigation link), or of a `DropdownMenu` or `ContextMenu` (a bits-ui menu item). */
19
19
  declare const MenuItem: import("svelte").Component<Props, {}, "">;
20
20
  type MenuItem = ReturnType<typeof MenuItem>;
21
21
  export default MenuItem;
@@ -0,0 +1,25 @@
1
+ <!-- @component
2
+ A set of mutually exclusive `MenuRadioItem`s in a `DropdownMenu` or `ContextMenu`.
3
+ -->
4
+ <!--
5
+ MenuRadioGroup — one choice from a few, made in a menu ("Sort by: name,
6
+ date, size"). It renders the radio group part its enclosing `DropdownMenu`
7
+ or `ContextMenu` lends it through context (`menu-parts/RadioGroupPart.svelte`),
8
+ so this file imports nothing from bits-ui (#129).
9
+
10
+ ARIA contract: a `role="group"` named by its visible `label` heading, around
11
+ `role="menuitemradio"` items, exactly one `aria-checked`. Outside a
12
+ `DropdownMenu` / `ContextMenu` it renders nothing.
13
+ -->
14
+ <script lang="ts">import { getMenuParts } from "./menu-parts/menu-parts.js";
15
+ let { value = $bindable(""), onValueChange, label, class: className, children } = $props();
16
+ const parts = getMenuParts();
17
+ const change = (next) => {
18
+ value = next;
19
+ onValueChange?.(next);
20
+ };
21
+ </script>
22
+
23
+ {#if parts}
24
+ <parts.radioGroup {value} onValueChange={change} {label} class={className} {children} />
25
+ {/if}
@@ -0,0 +1,20 @@
1
+ import type { Snippet } from 'svelte';
2
+ interface Props {
3
+ /**
4
+ * The chosen item's value; empty string when none is chosen. Bindable.
5
+ * @defaultValue `''`
6
+ */
7
+ value?: string | undefined;
8
+ /** Called with the chosen value. */
9
+ onValueChange?: ((value: string) => void) | undefined;
10
+ /** Visible group heading; also its accessible name. */
11
+ label?: string | undefined;
12
+ /** Extra classes on the `<li>`. */
13
+ class?: string | undefined;
14
+ /** The `MenuRadioItem`s. */
15
+ children: Snippet<[]>;
16
+ }
17
+ /** A set of mutually exclusive `MenuRadioItem`s in a `DropdownMenu` or `ContextMenu`. */
18
+ declare const MenuRadioGroup: import("svelte").Component<Props, {}, "value">;
19
+ type MenuRadioGroup = ReturnType<typeof MenuRadioGroup>;
20
+ export default MenuRadioGroup;
@@ -0,0 +1,20 @@
1
+ <!-- @component
2
+ One option of a `MenuRadioGroup` (`role="menuitemradio"`).
3
+ -->
4
+ <!--
5
+ MenuRadioItem — one option of a `MenuRadioGroup`. It renders the radio
6
+ item part its enclosing `DropdownMenu` or `ContextMenu` lends it through
7
+ context (`menu-parts/RadioItemPart.svelte`), so this file imports nothing
8
+ from bits-ui (#129).
9
+
10
+ ARIA contract: `role="menuitemradio"` with `aria-checked`; the dot is
11
+ `aria-hidden`. Outside a `DropdownMenu` / `ContextMenu` it renders nothing.
12
+ -->
13
+ <script lang="ts">import { getMenuParts } from "./menu-parts/menu-parts.js";
14
+ let { value, disabled = false, class: className, children } = $props();
15
+ const parts = getMenuParts();
16
+ </script>
17
+
18
+ {#if parts}
19
+ <parts.radioItem {value} {disabled} class={className} label={children} />
20
+ {/if}
@@ -0,0 +1,15 @@
1
+ import type { Snippet } from 'svelte';
2
+ interface Props {
3
+ /** The value this item chooses in its `MenuRadioGroup`. */
4
+ value: string;
5
+ /** Shown but not activatable. */
6
+ disabled?: boolean | undefined;
7
+ /** Extra classes on the `<li>`. */
8
+ class?: string | undefined;
9
+ /** The item's label. */
10
+ children: Snippet<[]>;
11
+ }
12
+ /** One option of a `MenuRadioGroup` (`role="menuitemradio"`). */
13
+ declare const MenuRadioItem: import("svelte").Component<Props, {}, "">;
14
+ type MenuRadioItem = ReturnType<typeof MenuRadioItem>;
15
+ export default MenuRadioItem;
@@ -6,21 +6,22 @@ A rule between groups of `MenuItem`s.
6
6
 
7
7
  ARIA contract: a real `role="separator"`, which is what tells a screen
8
8
  reader the grouping exists at all; a bare styled `<li>` is a line sighted
9
- readers can see and nobody else can. In dropdown mode the role comes from
10
- bits-ui's own separator (which also marks it non-focusable for the menu's
11
- arrow-key navigation), wrapped in a `role="none"` `<li>` so the
12
- presentational list item stays out of the accessibility tree.
9
+ readers can see and nobody else can. Inside a `DropdownMenu` or `ContextMenu` it renders the
10
+ separator part that menu lends it through context: bits-ui's own separator
11
+ (which also marks it non-focusable for the menu's arrow-key navigation),
12
+ wrapped in a `role="none"` `<li>` so the presentational list item stays out
13
+ of the accessibility tree. This file imports nothing from bits-ui (#129).
14
+
15
+ Outside a menu, use `Divider`: it is the free-standing rule, and this one
16
+ must stay an `<li>` inside the menu's list.
13
17
  -->
14
- <script lang="ts">import { DropdownMenu } from "bits-ui";
15
- import { getMenuMode } from "./menu-context.js";
18
+ <script lang="ts">import { getMenuParts } from "./menu-parts/menu-parts.js";
16
19
  let { class: className } = $props();
17
- const mode = getMenuMode();
20
+ const parts = getMenuParts();
18
21
  </script>
19
22
 
20
- {#if mode() === 'dropdown'}
21
- <li role="none">
22
- <DropdownMenu.Separator class={['divider my-1 h-px', className]} />
23
- </li>
23
+ {#if parts}
24
+ <parts.separator class={className} />
24
25
  {:else}
25
26
  <li role="separator" class={['divider my-1 h-px', className]}></li>
26
27
  {/if}
@@ -0,0 +1,24 @@
1
+ <!-- @component
2
+ A submenu of a `DropdownMenu` or `ContextMenu`: an item that opens a further menu.
3
+ -->
4
+ <!--
5
+ MenuSub — an item that opens a further menu ("Move to ▸"). It renders the
6
+ submenu part its enclosing `DropdownMenu` or `ContextMenu` lends it through
7
+ context (`menu-parts/SubPart.svelte`), so this file imports nothing from
8
+ bits-ui (#129). The children are the same `MenuItem`s, separators and
9
+ checkbox / radio items the parent menu takes.
10
+
11
+ ARIA contract: the opener is a `role="menuitem"` with
12
+ `aria-haspopup="menu"` and `aria-expanded`; → opens the submenu and focuses
13
+ its first item, ← closes it and returns focus to the opener. Outside a
14
+ `DropdownMenu` / `ContextMenu` it renders nothing: a navigation list has no
15
+ submenus to open.
16
+ -->
17
+ <script lang="ts">import { getMenuParts } from "./menu-parts/menu-parts.js";
18
+ let { label, disabled = false, class: className, children } = $props();
19
+ const parts = getMenuParts();
20
+ </script>
21
+
22
+ {#if parts}
23
+ <parts.sub {label} {disabled} class={className} {children} />
24
+ {/if}
@@ -0,0 +1,15 @@
1
+ import type { Snippet } from 'svelte';
2
+ interface Props {
3
+ /** Text of the item that opens the submenu. */
4
+ label: string;
5
+ /** Shown but not activatable. */
6
+ disabled?: boolean | undefined;
7
+ /** Extra classes on the `<li>`. */
8
+ class?: string | undefined;
9
+ /** The submenu's items. */
10
+ children: Snippet<[]>;
11
+ }
12
+ /** A submenu of a `DropdownMenu` or `ContextMenu`: an item that opens a further menu. */
13
+ declare const MenuSub: import("svelte").Component<Props, {}, "">;
14
+ type MenuSub = ReturnType<typeof MenuSub>;
15
+ export default MenuSub;
@@ -0,0 +1,41 @@
1
+ <!-- @component
2
+ An indeterminate loading indicator in daisyUI's `loading` shapes, named for assistive technology unless it is decorative.
3
+ -->
4
+ <!--
5
+ Spinner — "something is happening, and there is no shape to preview": a
6
+ panel waiting on a fetch, a lazy section, an inline "Checking…". When the
7
+ content's shape is known, `Skeleton` is the better placeholder.
8
+
9
+ ARIA contract, mirroring `Skeleton`'s on purpose: the wrapper is
10
+ `role="status"` (a polite live region, so its appearance is announced)
11
+ named by `aria-label={label}` — `status` does not take its name from its
12
+ content — and the animated shape is `aria-hidden`. With `showLabel` the
13
+ visible text is `aria-hidden` too, so the label is announced once.
14
+
15
+ `decorative` is for a spinner whose meaning something else already
16
+ carries — `Button`'s `aria-busy`, a live region around it — and renders the
17
+ shape alone, `aria-hidden`, with no role. Never nest a non-decorative
18
+ spinner inside another live region: two regions announce twice.
19
+
20
+ Motion: daisyUI's `loading` already slows its animation under
21
+ `prefers-reduced-motion: reduce` rather than stopping it — a frozen spinner
22
+ reads as "hung" (WCAG 2.3.3) — so nothing is added here.
23
+ -->
24
+ <script lang="ts">import { loadingSize, loadingVariant, textTone } from "./tones.js";
25
+ let { variant = "spinner", size = "md", tone, label = "Loading", showLabel = false, decorative = false, class: className, ...rest } = $props();
26
+ const shape = $derived([
27
+ "loading",
28
+ loadingVariant[variant],
29
+ loadingSize[size],
30
+ tone !== undefined && textTone[tone]
31
+ ]);
32
+ </script>
33
+
34
+ {#if decorative}
35
+ <span {...rest} aria-hidden="true" class={[shape, className]}></span>
36
+ {:else}
37
+ <span {...rest} role="status" aria-label={label} class={['inline-flex items-center gap-2', className]}>
38
+ <span aria-hidden="true" class={shape}></span>
39
+ {#if showLabel}<span aria-hidden="true">{label}</span>{/if}
40
+ </span>
41
+ {/if}
@@ -0,0 +1,35 @@
1
+ import type { HTMLAttributes } from 'svelte/elements';
2
+ import { type Size, type SpinnerVariant, type Tone } from './tones.ts';
3
+ interface Props extends HTMLAttributes<HTMLSpanElement> {
4
+ /**
5
+ * The daisyUI `loading` shape.
6
+ * @defaultValue `'spinner'`
7
+ */
8
+ variant?: SpinnerVariant | undefined;
9
+ /**
10
+ * daisyUI size step.
11
+ * @defaultValue `'md'`
12
+ */
13
+ size?: Size | undefined;
14
+ /** Colour, through `textTone`; inherits `currentColor` when omitted. */
15
+ tone?: Tone | undefined;
16
+ /**
17
+ * What assistive technology announces.
18
+ * @defaultValue `'Loading'`
19
+ */
20
+ label?: string | undefined;
21
+ /**
22
+ * Print `label` beside the spinner.
23
+ * @defaultValue `false`
24
+ */
25
+ showLabel?: boolean | undefined;
26
+ /**
27
+ * Something else already says it is loading: render the shape only, `aria-hidden`, with no role.
28
+ * @defaultValue `false`
29
+ */
30
+ decorative?: boolean | undefined;
31
+ }
32
+ /** An indeterminate loading indicator in daisyUI's `loading` shapes, named for assistive technology unless it is decorative. */
33
+ declare const Spinner: import("svelte").Component<Props, {}, "">;
34
+ type Spinner = ReturnType<typeof Spinner>;
35
+ export default Spinner;
@@ -0,0 +1,36 @@
1
+ <!-- @component
2
+ Overlapping cards or images on daisyUI `stack`: only the top item is exposed, the rest are visual depth.
3
+ -->
4
+ <!--
5
+ Stack — a pile with a peek of what is behind: a "3 photos" thumbnail, a
6
+ collapsed pile of notifications. The first item is on top.
7
+
8
+ The rule this component exists for: daisyUI's `stack` only paints the
9
+ items behind; every child stays in the DOM, so a screen reader would read
10
+ all three cards and Tab would land on links hidden behind the top one.
11
+ So every item after the first is `inert` (not focusable, not clickable)
12
+ and `aria-hidden` (not read). The depth is then reported in words: the top
13
+ item ends with a visually hidden `countLabel(count)` ("3 items") whenever
14
+ there is more than one, so it is not visual-only.
15
+
16
+ The items are data, not children: `stack.css` positions them with
17
+ `.stack > *:nth-child(n)`, so each must be a direct child of the root and
18
+ no wrapper (not even `display: contents`) can group the ones behind.
19
+ -->
20
+ <script lang="ts" generics="T">import { stackDirection } from "./tones.js";
21
+ let { items, item, direction = "bottom", count, countLabel = (n) => `${n} items`, class: className, ...rest } = $props();
22
+ const total = $derived(count ?? items.length);
23
+ </script>
24
+
25
+ <div {...rest} class={['stack', stackDirection[direction], className]}>
26
+ {#each items as entry, index (index)}
27
+ {#if index === 0}
28
+ <div>
29
+ {@render item(entry, true)}
30
+ {#if total > 1}<span class="sr-only">{countLabel(total)}</span>{/if}
31
+ </div>
32
+ {:else}
33
+ <div inert aria-hidden="true">{@render item(entry, false)}</div>
34
+ {/if}
35
+ {/each}
36
+ </div>