@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,48 @@
1
+ import type { Snippet } from 'svelte';
2
+ import type { HTMLAttributes } from 'svelte/elements';
3
+ import { type StackDirection } from './tones.ts';
4
+ declare function $$render<T>(): {
5
+ props: Omit<HTMLAttributes<HTMLDivElement>, "children"> & {
6
+ /** The items, top first. Only the first is exposed; the rest are depth. */
7
+ items: readonly T[];
8
+ /** Draws one item; `top` is true for the first. */
9
+ item: Snippet<[item: T, top: boolean]>;
10
+ /**
11
+ * Which side the items behind peek out from.
12
+ * @defaultValue `'bottom'`
13
+ */
14
+ direction?: StackDirection | undefined;
15
+ /**
16
+ * Total count when more exist than are passed (3 shown of 12).
17
+ * @defaultValue `items.length`
18
+ */
19
+ count?: number | undefined;
20
+ /**
21
+ * Words for the depth, read after the top item.
22
+ * @defaultValue `(n) => \`${n} items\``
23
+ */
24
+ countLabel?: ((count: number) => string) | undefined;
25
+ };
26
+ exports: {};
27
+ bindings: "";
28
+ slots: {};
29
+ events: {};
30
+ };
31
+ declare class __sveltets_Render<T> {
32
+ props(): ReturnType<typeof $$render<T>>['props'];
33
+ events(): ReturnType<typeof $$render<T>>['events'];
34
+ slots(): ReturnType<typeof $$render<T>>['slots'];
35
+ bindings(): "";
36
+ exports(): {};
37
+ }
38
+ interface $$IsomorphicComponent {
39
+ new <T>(options: import('svelte').ComponentConstructorOptions<ReturnType<__sveltets_Render<T>['props']>>): import('svelte').SvelteComponent<ReturnType<__sveltets_Render<T>['props']>, ReturnType<__sveltets_Render<T>['events']>, ReturnType<__sveltets_Render<T>['slots']>> & {
40
+ $$bindings?: ReturnType<__sveltets_Render<T>['bindings']>;
41
+ } & ReturnType<__sveltets_Render<T>['exports']>;
42
+ <T>(internal: unknown, props: ReturnType<__sveltets_Render<T>['props']> & {}): ReturnType<__sveltets_Render<T>['exports']>;
43
+ z_$$bindings?: ReturnType<__sveltets_Render<any>['bindings']>;
44
+ }
45
+ /** Overlapping cards or images on daisyUI `stack`: only the top item is exposed, the rest are visual depth. */
46
+ declare const Stack: $$IsomorphicComponent;
47
+ type Stack<T> = InstanceType<typeof Stack<T>>;
48
+ export default Stack;
@@ -0,0 +1,58 @@
1
+ <!-- @component
2
+ A button that stays pressed (`aria-pressed`), on bits-ui `Toggle`.
3
+ -->
4
+ <!--
5
+ ToggleButton — "Bold", "Show frost dates", "Mute", "Favourite": a button
6
+ whose state persists between presses. Not a `fields/Toggle` (a switch: a
7
+ setting that takes effect, `role="switch"`), not a `Checkbox` (a form
8
+ value).
9
+
10
+ ARIA contract: bits-ui's `aria-pressed`, and Space / Enter both toggle.
11
+ The accessible name never changes with state: "Mute" stays "Mute" when
12
+ pressed, because the state is `aria-pressed`'s job. An icon-only button
13
+ must have a `label` (the types enforce it), read as visually hidden text;
14
+ `tooltip` opts in to showing it on hover and focus. `data-state="on" |
15
+ "off"` is a supported styling hook. Pressed is not colour alone: see
16
+ `toggle-classes.ts`; `pressedIcon` adds a shape change.
17
+ -->
18
+ <script lang="ts">import { Toggle } from "bits-ui";
19
+ import { toggleClasses } from "./toggle-classes.js";
20
+ import { buttonSize } from "./tones.js";
21
+ import Tooltip from "./Tooltip.svelte";
22
+ let { pressed = $bindable(false), onPressedChange, tone = "neutral", variant = "ghost", size = "md", icon, pressedIcon, tooltip = false, iconOnly = false, label, children, disabled, class: className, ...rest } = $props();
23
+ </script>
24
+
25
+ {#snippet button(extra: Record<string, unknown>)}
26
+ <Toggle.Root
27
+ {...extra}
28
+ bind:pressed
29
+ {...onPressedChange === undefined ? {} : { onPressedChange }}
30
+ {...disabled == null ? {} : { disabled }}
31
+ >
32
+ {#snippet child({ props, pressed: on })}
33
+ <button
34
+ {...rest}
35
+ {...props}
36
+ type="button"
37
+ class={[
38
+ 'btn',
39
+ buttonSize[size],
40
+ toggleClasses(on, tone, variant),
41
+ iconOnly && 'btn-square',
42
+ className,
43
+ ]}
44
+ >
45
+ {#if on && pressedIcon}{@render pressedIcon()}{:else if icon}{@render icon()}{/if}
46
+ {#if iconOnly}<span class="sr-only">{label}</span>{:else}{@render children?.()}{/if}
47
+ </button>
48
+ {/snippet}
49
+ </Toggle.Root>
50
+ {/snippet}
51
+
52
+ {#if tooltip && label !== undefined}
53
+ <Tooltip text={label}>
54
+ {#snippet children({ props })}{@render button(props)}{/snippet}
55
+ </Tooltip>
56
+ {:else}
57
+ {@render button({})}
58
+ {/if}
@@ -0,0 +1,45 @@
1
+ import type { Snippet } from 'svelte';
2
+ import type { HTMLButtonAttributes } from 'svelte/elements';
3
+ import { type ButtonVariant, type Size, type Tone } from './tones.ts';
4
+ type Props = Omit<HTMLButtonAttributes, 'type' | 'children'> & {
5
+ /** Whether the button is pressed. Bindable. */
6
+ pressed?: boolean | undefined;
7
+ /** Called with the next state after a press. */
8
+ onPressedChange?: ((pressed: boolean) => void) | undefined;
9
+ /**
10
+ * Colour while pressed.
11
+ * @defaultValue `'neutral'`
12
+ */
13
+ tone?: Tone | undefined;
14
+ /**
15
+ * Look while not pressed.
16
+ * @defaultValue `'ghost'`
17
+ */
18
+ variant?: ButtonVariant | undefined;
19
+ /** @defaultValue `'md'` */
20
+ size?: Size | undefined;
21
+ /** Leading icon. */
22
+ icon?: Snippet<[]> | undefined;
23
+ /** Icon shown in place of `icon` while pressed (an outlined star becomes a filled one). */
24
+ pressedIcon?: Snippet<[]> | undefined;
25
+ /**
26
+ * Show the label in a tooltip on hover and focus (icon-only buttons).
27
+ * @defaultValue `false`
28
+ */
29
+ tooltip?: boolean | undefined;
30
+ } & ({
31
+ iconOnly?: false | undefined;
32
+ label?: undefined;
33
+ /** The button text; its accessible name. */
34
+ children: Snippet<[]>;
35
+ } | {
36
+ /** Hide the text: only the icon shows, and `label` is the accessible name. */
37
+ iconOnly: true;
38
+ /** The accessible name of an icon-only button. Constant across states. */
39
+ label: string;
40
+ children?: undefined;
41
+ });
42
+ /** A button that stays pressed (`aria-pressed`), on bits-ui `Toggle`. */
43
+ declare const ToggleButton: import("svelte").Component<Props, {}, "pressed">;
44
+ type ToggleButton = ReturnType<typeof ToggleButton>;
45
+ export default ToggleButton;
@@ -0,0 +1,142 @@
1
+ <!-- @component
2
+ A set of toggle buttons answering one question, exclusively or independently, on bits-ui `ToggleGroup`.
3
+ -->
4
+ <!--
5
+ ToggleGroup — a segmented control (`type="single"`: Grid | List | Map,
6
+ Day | Week | Month) or a set of independent toggles (`type="multiple"`:
7
+ B / I / U, "Show: Annuals Perennials Bulbs"), as daisyUI `btn`s in a
8
+ `join`. One Tab stop, arrow keys between items (bits-ui's roving focus).
9
+
10
+ ARIA contract:
11
+ - `type="multiple"`: a `role="group"` of buttons with `aria-pressed`;
12
+ arrows move focus, Space / Enter toggles.
13
+ - `type="single"`: bits-ui makes the items `role="radio"` with
14
+ `aria-checked` but keeps its root a `group`; radios belong in a
15
+ `radiogroup`, so the root's role is overridden. As in an APG radio
16
+ group, arrows move focus AND select (bits-ui only moves focus, so the
17
+ root completes the move), and Tab into the group lands on the checked
18
+ item, not the first. With `required`, pressing the checked item does
19
+ nothing, so one item always stays selected.
20
+ - Named by `label` (`aria-label`) or, inside a `FormField`, by the label
21
+ element (`labelId` → `aria-labelledby`); `describedBy` and `invalid` as
22
+ every field control. An item's name never changes with state; an
23
+ icon-only item reads its `label` as visually hidden text, and
24
+ `tooltip` opts in to showing it on hover and focus.
25
+ - With `name`, the selection posts with a form: one hidden input per
26
+ selected value (bits-ui's group has no hidden inputs of its own).
27
+ -->
28
+ <script lang="ts" generics="V extends string">import { Check } from "@lucide/svelte";
29
+ import { ToggleGroup } from "bits-ui";
30
+ import { toggleClasses, toggleSetters } from "./toggle-classes.js";
31
+ import { buttonSize } from "./tones.js";
32
+ import Tooltip from "./Tooltip.svelte";
33
+ let { type, items, value = $bindable(), onValueChange, required = false, label, labelId, describedBy = "", invalid = false, name, orientation = "horizontal", loop = true, tone = "primary", variant = "solid", size = "md", block = false, showCheck = false, tooltip = false, disabled = false, item: itemBody, class: className, ...rest } = $props();
34
+ const selected = $derived(value === undefined || value === "" ? [] : typeof value === "string" ? [value] : value);
35
+ const setters = toggleSetters({
36
+ required: () => required,
37
+ assign: (next) => value = next,
38
+ notify: () => onValueChange
39
+ });
40
+ const MOVE_KEYS = new Set([
41
+ "ArrowLeft",
42
+ "ArrowRight",
43
+ "ArrowUp",
44
+ "ArrowDown",
45
+ "Home",
46
+ "End"
47
+ ]);
48
+ /** Single mode: once bits-ui has moved focus, select what it moved to. */
49
+ function selectFollowsFocus(event) {
50
+ if (type !== "single" || !MOVE_KEYS.has(event.key)) return;
51
+ const focused = document.activeElement;
52
+ if (!(focused instanceof HTMLElement)) return;
53
+ const next = focused.dataset["value"];
54
+ if (next !== undefined && next !== value && !focused.hasAttribute("data-disabled")) setters.single(next);
55
+ }
56
+ /** Single mode: Tab into the group lands on the checked item. */
57
+ function focusChecked(event) {
58
+ const root = event.currentTarget;
59
+ if (type !== "single" || !(root instanceof HTMLElement)) return;
60
+ if (event.relatedTarget instanceof Node && root.contains(event.relatedTarget)) return;
61
+ const checked = root.querySelector("[aria-checked=\"true\"]");
62
+ if (checked !== null && checked !== event.target) checked.focus();
63
+ }
64
+ </script>
65
+
66
+ {#snippet root({ props }: { props: Record<string, unknown> })}
67
+ <div
68
+ {...props}
69
+ {...rest}
70
+ role={type === 'single' ? 'radiogroup' : 'group'}
71
+ aria-label={labelId === undefined ? label : undefined}
72
+ aria-labelledby={labelId}
73
+ aria-describedby={describedBy === '' ? undefined : describedBy}
74
+ aria-invalid={invalid ? 'true' : undefined}
75
+ aria-required={type === 'single' && required ? 'true' : undefined}
76
+ aria-orientation={orientation}
77
+ onkeydown={selectFollowsFocus}
78
+ onfocusin={focusChecked}
79
+ class={['join', orientation === 'vertical' && 'join-vertical', block && 'flex w-full', className]}
80
+ >
81
+ {#each items as entry (entry.value)}
82
+ {#if tooltip && entry.iconOnly}
83
+ <Tooltip text={entry.label}>
84
+ {#snippet children({ props: tip })}{@render itemButton(entry, tip)}{/snippet}
85
+ </Tooltip>
86
+ {:else}
87
+ {@render itemButton(entry, {})}
88
+ {/if}
89
+ {/each}
90
+ </div>
91
+ {/snippet}
92
+
93
+ {#snippet itemButton(entry: ToggleGroupItem<V>, extra: Record<string, unknown>)}
94
+ <ToggleGroup.Item {...extra} value={entry.value} disabled={entry.disabled ?? false}>
95
+ {#snippet child({ props, pressed })}
96
+ <button
97
+ {...props}
98
+ type="button"
99
+ class={[
100
+ 'btn join-item',
101
+ buttonSize[size],
102
+ toggleClasses(pressed, tone, variant),
103
+ block && 'flex-1',
104
+ entry.iconOnly && !block && 'btn-square',
105
+ ]}
106
+ >
107
+ {#if itemBody}
108
+ {@render itemBody(entry, { pressed })}
109
+ {:else}
110
+ {#if showCheck && pressed}<Check class="size-4" aria-hidden="true" />{/if}
111
+ {@render entry.icon?.()}
112
+ {#if entry.iconOnly}<span class="sr-only">{entry.label}</span>{:else}{entry.label}{/if}
113
+ {/if}
114
+ </button>
115
+ {/snippet}
116
+ </ToggleGroup.Item>
117
+ {/snippet}
118
+
119
+ {#if type === 'single'}
120
+ <ToggleGroup.Root
121
+ type="single"
122
+ bind:value={() => (typeof value === 'string' ? value : ''), setters.single}
123
+ {orientation}
124
+ {loop}
125
+ {disabled}
126
+ child={root}
127
+ />
128
+ {:else}
129
+ <ToggleGroup.Root
130
+ type="multiple"
131
+ bind:value={() => [...selected], setters.multiple}
132
+ {orientation}
133
+ {loop}
134
+ {disabled}
135
+ child={root}
136
+ />
137
+ {/if}
138
+ {#if name !== undefined}
139
+ {#each selected as selectedValue (selectedValue)}
140
+ <input type="hidden" {name} value={selectedValue} />
141
+ {/each}
142
+ {/if}
@@ -0,0 +1,86 @@
1
+ import { ToggleGroup } from 'bits-ui';
2
+ import type { Snippet } from 'svelte';
3
+ import type { HTMLAttributes } from 'svelte/elements';
4
+ import { type ToggleGroupItem } from './toggle-classes.ts';
5
+ import { type ButtonVariant, type Size, type Tone } from './tones.ts';
6
+ declare function $$render<V extends string>(): {
7
+ props: Omit<HTMLAttributes<HTMLDivElement>, "onchange" | "children"> & ({
8
+ items: readonly ToggleGroupItem<V>[];
9
+ /** Accessible name of the group ("View", "Formatting"); omit when `labelId` names it. */
10
+ label?: string | undefined;
11
+ /** `FieldControl`: the visible label's id; the group is `aria-labelledby` it. */
12
+ labelId?: string | undefined;
13
+ /** `FieldControl`: space-separated ids for `aria-describedby`. */
14
+ describedBy?: string | undefined;
15
+ /** `FieldControl`: the field is in error. */
16
+ invalid?: boolean | undefined;
17
+ /** Posts the selection with a form: one hidden input per selected value. */
18
+ name?: string | undefined;
19
+ /** @defaultValue `'horizontal'` */
20
+ orientation?: "horizontal" | "vertical" | undefined;
21
+ /** @defaultValue `true` */
22
+ loop?: boolean | undefined;
23
+ /**
24
+ * Colour of a pressed item.
25
+ * @defaultValue `'primary'`
26
+ */
27
+ tone?: Tone | undefined;
28
+ /**
29
+ * Look of an unpressed item.
30
+ * @defaultValue `'solid'`
31
+ */
32
+ variant?: ButtonVariant | undefined;
33
+ /** @defaultValue `'md'` */
34
+ size?: Size | undefined;
35
+ /** Stretch to the container's width, items sharing it equally. */
36
+ block?: boolean | undefined;
37
+ /** A check mark (`aria-hidden`) on pressed items, a second non-colour cue. */
38
+ showCheck?: boolean | undefined;
39
+ /** Show icon-only items' labels in a tooltip on hover and focus. */
40
+ tooltip?: boolean | undefined;
41
+ disabled?: boolean | undefined;
42
+ /** Per-item content in place of icon + label; the label stays the name unless you render it. */
43
+ item?: Snippet<[ToggleGroupItem<V>, {
44
+ pressed: boolean;
45
+ }]> | undefined;
46
+ } & ({
47
+ type: "single";
48
+ /** The selected value, `''` for none. Bindable. */
49
+ value?: V | "" | undefined;
50
+ onValueChange?: ((value: V | "") => void) | undefined;
51
+ /**
52
+ * Keep one item selected: pressing the selected item does nothing.
53
+ * @defaultValue `false`
54
+ */
55
+ required?: boolean | undefined;
56
+ } | {
57
+ type: "multiple";
58
+ /** The selected values. Bindable. */
59
+ value?: readonly V[] | undefined;
60
+ onValueChange?: ((value: V[]) => void) | undefined;
61
+ /** `FieldControl`: accepted for the spread; a multiple group may be empty. */
62
+ required?: boolean | undefined;
63
+ }));
64
+ exports: {};
65
+ bindings: "value";
66
+ slots: {};
67
+ events: {};
68
+ };
69
+ declare class __sveltets_Render<V extends string> {
70
+ props(): ReturnType<typeof $$render<V>>['props'];
71
+ events(): ReturnType<typeof $$render<V>>['events'];
72
+ slots(): ReturnType<typeof $$render<V>>['slots'];
73
+ bindings(): "value";
74
+ exports(): {};
75
+ }
76
+ interface $$IsomorphicComponent {
77
+ new <V extends string>(options: import('svelte').ComponentConstructorOptions<ReturnType<__sveltets_Render<V>['props']>>): import('svelte').SvelteComponent<ReturnType<__sveltets_Render<V>['props']>, ReturnType<__sveltets_Render<V>['events']>, ReturnType<__sveltets_Render<V>['slots']>> & {
78
+ $$bindings?: ReturnType<__sveltets_Render<V>['bindings']>;
79
+ } & ReturnType<__sveltets_Render<V>['exports']>;
80
+ <V extends string>(internal: unknown, props: ReturnType<__sveltets_Render<V>['props']> & {}): ReturnType<__sveltets_Render<V>['exports']>;
81
+ z_$$bindings?: ReturnType<__sveltets_Render<any>['bindings']>;
82
+ }
83
+ /** A set of toggle buttons answering one question, exclusively or independently, on bits-ui `ToggleGroup`. */
84
+ declare const ToggleGroup: $$IsomorphicComponent;
85
+ type ToggleGroup<V extends string> = InstanceType<typeof ToggleGroup<V>>;
86
+ export default ToggleGroup;
@@ -0,0 +1,40 @@
1
+ <!-- @component
2
+ A `role="toolbar"` on bits-ui `Toolbar`: one Tab stop, arrow keys between its controls.
3
+ -->
4
+ <!--
5
+ Toolbar — an editor's formatting row, a table's bulk-action bar, a canvas
6
+ tool palette (APG Toolbar). Its controls are `ToolbarButton`,
7
+ `ToolbarLink`, `ToolbarToggleGroup` and `ToolbarSeparator`, all bits-ui
8
+ toolbar parts, so the whole row is ONE Tab stop with arrow keys within it.
9
+ A plain `Button` or a nested `ToggleGroup` inside it would each add Tab
10
+ stops of their own and break that.
11
+
12
+ A toolbar is a composite widget. For a cluster of independent actions
13
+ ("Save | Save as… | Discard") where each should be its own Tab stop, use
14
+ `ButtonGroup`.
15
+
16
+ ARIA and keyboard contract: bits-ui's `role="toolbar"` and roving
17
+ tabindex; ← / → (↑ / ↓ when vertical), Home / End, wrapping with `loop`.
18
+ We add the name (`label` → `aria-label`) and `aria-orientation`.
19
+ -->
20
+ <script lang="ts">import { Toolbar } from "bits-ui";
21
+ let { label, orientation = "horizontal", loop = true, children, class: className, ...rest } = $props();
22
+ </script>
23
+
24
+ <Toolbar.Root {orientation} {loop}>
25
+ {#snippet child({ props })}
26
+ <div
27
+ {...rest}
28
+ {...props}
29
+ aria-label={label}
30
+ aria-orientation={orientation}
31
+ class={[
32
+ 'rounded-box bg-base-200 inline-flex items-center gap-1 p-1',
33
+ orientation === 'vertical' && 'flex-col',
34
+ className,
35
+ ]}
36
+ >
37
+ {@render children()}
38
+ </div>
39
+ {/snippet}
40
+ </Toolbar.Root>
@@ -0,0 +1,20 @@
1
+ import { Toolbar } from 'bits-ui';
2
+ import type { Snippet } from 'svelte';
3
+ import type { HTMLAttributes } from 'svelte/elements';
4
+ interface Props extends Omit<HTMLAttributes<HTMLDivElement>, 'children'> {
5
+ /** Accessible name ("Formatting", "Bulk actions"). */
6
+ label: string;
7
+ /** @defaultValue `'horizontal'` */
8
+ orientation?: 'horizontal' | 'vertical' | undefined;
9
+ /**
10
+ * Arrow keys wrap from the last control to the first.
11
+ * @defaultValue `true`
12
+ */
13
+ loop?: boolean | undefined;
14
+ /** The controls: `ToolbarButton`, `ToolbarLink`, `ToolbarToggleGroup`, `ToolbarSeparator`. */
15
+ children: Snippet<[]>;
16
+ }
17
+ /** A `role="toolbar"` on bits-ui `Toolbar`: one Tab stop, arrow keys between its controls. */
18
+ declare const Toolbar: import("svelte").Component<Props, {}, "">;
19
+ type Toolbar = ReturnType<typeof Toolbar>;
20
+ export default Toolbar;
@@ -0,0 +1,29 @@
1
+ <!-- @component
2
+ A button inside a `Toolbar`: one of its roving-focus controls, wearing the library's `Button`.
3
+ -->
4
+ <!--
5
+ ToolbarButton — rendered through bits-ui `Toolbar.Button`'s `child`
6
+ snippet onto `Button`, so the button look and the `loading` contract stay
7
+ one implementation. `iconOnly` hides the text visually; it stays the
8
+ accessible name (never `title`).
9
+ -->
10
+ <script lang="ts">import { Toolbar } from "bits-ui";
11
+ import Button from "./Button.svelte";
12
+ let { tone, variant = "ghost", size = "sm", icon, iconOnly = false, children, disabled, class: className, ...rest } = $props();
13
+ </script>
14
+
15
+ <Toolbar.Button {...disabled == null ? {} : { disabled }}>
16
+ {#snippet child({ props })}
17
+ <Button
18
+ {...rest}
19
+ {...props}
20
+ {tone}
21
+ {variant}
22
+ {size}
23
+ {icon}
24
+ class={[iconOnly && 'btn-square', className]}
25
+ >
26
+ {#if iconOnly}<span class="sr-only">{@render children()}</span>{:else}{@render children()}{/if}
27
+ </Button>
28
+ {/snippet}
29
+ </Toolbar.Button>
@@ -0,0 +1,21 @@
1
+ import type { Snippet } from 'svelte';
2
+ import type { HTMLButtonAttributes } from 'svelte/elements';
3
+ import type { ButtonVariant, Size, Tone } from './tones.ts';
4
+ interface Props extends Omit<HTMLButtonAttributes, 'type' | 'children'> {
5
+ /** Semantic colour. */
6
+ tone?: Tone | undefined;
7
+ /** @defaultValue `'ghost'` */
8
+ variant?: ButtonVariant | undefined;
9
+ /** @defaultValue `'sm'` */
10
+ size?: Size | undefined;
11
+ /** Leading icon. */
12
+ icon?: Snippet<[]> | undefined;
13
+ /** Hide the text visually; it remains the accessible name. */
14
+ iconOnly?: boolean | undefined;
15
+ /** The button text; its accessible name. */
16
+ children: Snippet<[]>;
17
+ }
18
+ /** A button inside a `Toolbar`: one of its roving-focus controls, wearing the library's `Button`. */
19
+ declare const ToolbarButton: import("svelte").Component<Props, {}, "">;
20
+ type ToolbarButton = ReturnType<typeof ToolbarButton>;
21
+ export default ToolbarButton;
@@ -0,0 +1,21 @@
1
+ <!-- @component
2
+ A link inside a `Toolbar`: one of its roving-focus controls, resolved through the components context.
3
+ -->
4
+ <!--
5
+ ToolbarLink — bits-ui `Toolbar.Link`, styled as a small ghost button, with
6
+ `href` resolved through `getComponentsContext().href()` like every other
7
+ link in the library.
8
+ -->
9
+ <script lang="ts">import { Toolbar } from "bits-ui";
10
+ import { getComponentsContext } from "../context.svelte.js";
11
+ let { href, children, class: className, ...rest } = $props();
12
+ const context = getComponentsContext();
13
+ </script>
14
+
15
+ <Toolbar.Link>
16
+ {#snippet child({ props })}
17
+ <a {...rest} {...props} href={context.href(href)} class={['btn btn-ghost btn-sm', className]}>
18
+ {@render children()}
19
+ </a>
20
+ {/snippet}
21
+ </Toolbar.Link>
@@ -0,0 +1,12 @@
1
+ import type { Snippet } from 'svelte';
2
+ import type { HTMLAnchorAttributes } from 'svelte/elements';
3
+ interface Props extends Omit<HTMLAnchorAttributes, 'children'> {
4
+ /** App-relative path, resolved through the context's `href()`. */
5
+ href: string;
6
+ /** The link text; its accessible name. */
7
+ children: Snippet<[]>;
8
+ }
9
+ /** A link inside a `Toolbar`: one of its roving-focus controls, resolved through the components context. */
10
+ declare const ToolbarLink: import("svelte").Component<Props, {}, "">;
11
+ type ToolbarLink = ReturnType<typeof ToolbarLink>;
12
+ export default ToolbarLink;
@@ -0,0 +1,22 @@
1
+ <!-- @component
2
+ A rule between groups of controls in a `Toolbar`, perpendicular to it.
3
+ -->
4
+ <!--
5
+ ToolbarSeparator — `role="separator"` with the perpendicular
6
+ `aria-orientation` (a vertical rule in a horizontal toolbar). Not
7
+ focusable, so the arrow keys skip it.
8
+ -->
9
+ <script lang="ts">let { toolbarOrientation = "horizontal", class: className, ...rest } = $props();
10
+ const vertical = $derived(toolbarOrientation === "horizontal");
11
+ export {};
12
+ </script>
13
+
14
+ <div
15
+ {...rest}
16
+ role="separator"
17
+ aria-orientation={vertical ? 'vertical' : 'horizontal'}
18
+ class={[
19
+ vertical ? 'bg-base-300 mx-1 h-6 w-px self-center' : 'bg-base-300 my-1 h-px w-6 self-center',
20
+ className,
21
+ ]}
22
+ ></div>
@@ -0,0 +1,12 @@
1
+ import type { HTMLAttributes } from 'svelte/elements';
2
+ interface Props extends Omit<HTMLAttributes<HTMLDivElement>, 'children'> {
3
+ /**
4
+ * The orientation of the enclosing toolbar; the rule runs across it.
5
+ * @defaultValue `'horizontal'`
6
+ */
7
+ toolbarOrientation?: 'horizontal' | 'vertical' | undefined;
8
+ }
9
+ /** A rule between groups of controls in a `Toolbar`, perpendicular to it. */
10
+ declare const ToolbarSeparator: import("svelte").Component<Props, {}, "">;
11
+ type ToolbarSeparator = ReturnType<typeof ToolbarSeparator>;
12
+ export default ToolbarSeparator;
@@ -0,0 +1,66 @@
1
+ <!-- @component
2
+ A set of toggles inside a `Toolbar` (Bold / Italic, alignment), sharing the toolbar's single Tab stop.
3
+ -->
4
+ <!--
5
+ ToolbarToggleGroup — `ToggleGroup`'s props and look, built on bits-ui
6
+ `Toolbar.Group` / `Toolbar.GroupItem` rather than a nested
7
+ `ToggleGroup`: a nested group keeps its own roving focus, which the
8
+ toolbar knows nothing about, and the toolbar's one Tab stop breaks. Here
9
+ the items are toolbar controls, so ← / → run across the whole toolbar.
10
+
11
+ `type="multiple"`: a `role="group"` of `aria-pressed` buttons.
12
+ `type="single"`: the items are `radio`s, so the group is overridden to a
13
+ `radiogroup`; as in the APG toolbar example, the arrows only move focus
14
+ across the toolbar and Space / Enter selects. With `required`, pressing
15
+ the selected item does nothing.
16
+ -->
17
+ <script lang="ts" generics="V extends string">import { Toolbar } from "bits-ui";
18
+ import { toggleClasses, toggleSetters } from "./toggle-classes.js";
19
+ import { buttonSize } from "./tones.js";
20
+ let { type, items, label, value = $bindable(), onValueChange, required = false, tone = "primary", variant = "ghost", size = "sm", disabled = false, class: className } = $props();
21
+ const setters = toggleSetters({
22
+ required: () => required,
23
+ assign: (next) => value = next,
24
+ notify: () => onValueChange
25
+ });
26
+ </script>
27
+
28
+ {#snippet group({ props }: { props: Record<string, unknown> })}
29
+ <div
30
+ {...props}
31
+ role={type === 'single' ? 'radiogroup' : 'group'}
32
+ aria-label={label}
33
+ class={['inline-flex items-center gap-0.5', className]}
34
+ >
35
+ {#each items as entry (entry.value)}
36
+ <Toolbar.GroupItem value={entry.value} disabled={entry.disabled ?? false}>
37
+ {#snippet child({ props: itemProps, pressed })}
38
+ <button
39
+ {...itemProps}
40
+ type="button"
41
+ class={['btn', buttonSize[size], toggleClasses(pressed, tone, variant), entry.iconOnly && 'btn-square']}
42
+ >
43
+ {@render entry.icon?.()}
44
+ {#if entry.iconOnly}<span class="sr-only">{entry.label}</span>{:else}{entry.label}{/if}
45
+ </button>
46
+ {/snippet}
47
+ </Toolbar.GroupItem>
48
+ {/each}
49
+ </div>
50
+ {/snippet}
51
+
52
+ {#if type === 'single'}
53
+ <Toolbar.Group
54
+ type="single"
55
+ bind:value={() => (typeof value === 'string' ? value : ''), setters.single}
56
+ {disabled}
57
+ child={group}
58
+ />
59
+ {:else}
60
+ <Toolbar.Group
61
+ type="multiple"
62
+ bind:value={() => [...(Array.isArray(value) ? value : [])], setters.multiple}
63
+ {disabled}
64
+ child={group}
65
+ />
66
+ {/if}