@human-kit/svelte-components 1.0.0-alpha.1 → 1.0.0-alpha.3

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 (187) hide show
  1. package/dist/FOCUS_STATE_CONTRACT.md +51 -0
  2. package/dist/FOCUS_STATE_REVIEW_TEMPLATE.md +70 -0
  3. package/dist/calendar/README.md +66 -0
  4. package/dist/calendar/TODO.md +23 -0
  5. package/dist/calendar/body-cell/calendar-body-cell.svelte +230 -0
  6. package/dist/calendar/body-cell/calendar-body-cell.svelte.d.ts +9 -0
  7. package/dist/calendar/grid/calendar-grid-month-scope.svelte +16 -0
  8. package/dist/calendar/grid/calendar-grid-month-scope.svelte.d.ts +8 -0
  9. package/dist/calendar/grid/calendar-grid.svelte +45 -0
  10. package/dist/calendar/grid/calendar-grid.svelte.d.ts +8 -0
  11. package/dist/calendar/grid/month-scope.d.ts +2 -0
  12. package/dist/calendar/grid/month-scope.js +8 -0
  13. package/dist/calendar/grid-body/calendar-grid-body-custom-test.svelte +13 -0
  14. package/dist/calendar/grid-body/calendar-grid-body-custom-test.svelte.d.ts +18 -0
  15. package/dist/calendar/grid-body/calendar-grid-body.svelte +36 -0
  16. package/dist/calendar/grid-body/calendar-grid-body.svelte.d.ts +8 -0
  17. package/dist/calendar/grid-header/calendar-grid-header-custom-test.svelte +13 -0
  18. package/dist/calendar/grid-header/calendar-grid-header-custom-test.svelte.d.ts +18 -0
  19. package/dist/calendar/grid-header/calendar-grid-header.svelte +31 -0
  20. package/dist/calendar/grid-header/calendar-grid-header.svelte.d.ts +8 -0
  21. package/dist/calendar/header-cell/calendar-header-cell-test.svelte +11 -0
  22. package/dist/calendar/header-cell/calendar-header-cell-test.svelte.d.ts +18 -0
  23. package/dist/calendar/header-cell/calendar-header-cell.svelte +16 -0
  24. package/dist/calendar/header-cell/calendar-header-cell.svelte.d.ts +8 -0
  25. package/dist/calendar/heading/calendar-heading.svelte +17 -0
  26. package/dist/calendar/heading/calendar-heading.svelte.d.ts +5 -0
  27. package/dist/calendar/index.d.ts +13 -0
  28. package/dist/calendar/index.js +13 -0
  29. package/dist/calendar/index.parts.d.ts +9 -0
  30. package/dist/calendar/index.parts.js +9 -0
  31. package/dist/calendar/root/calendar-root-bind-value-test.svelte +14 -0
  32. package/dist/calendar/root/calendar-root-bind-value-test.svelte.d.ts +3 -0
  33. package/dist/calendar/root/calendar-root-controlled-clear-test.svelte +20 -0
  34. package/dist/calendar/root/calendar-root-controlled-clear-test.svelte.d.ts +3 -0
  35. package/dist/calendar/root/calendar-root-test.svelte +71 -0
  36. package/dist/calendar/root/calendar-root-test.svelte.d.ts +13 -0
  37. package/dist/calendar/root/calendar-root.svelte +143 -0
  38. package/dist/calendar/root/calendar-root.svelte.d.ts +31 -0
  39. package/dist/calendar/root/context.d.ts +66 -0
  40. package/dist/calendar/root/context.js +727 -0
  41. package/dist/calendar/root/date-utils.d.ts +17 -0
  42. package/dist/calendar/root/date-utils.js +94 -0
  43. package/dist/calendar/trigger-next/calendar-trigger-next.svelte +38 -0
  44. package/dist/calendar/trigger-next/calendar-trigger-next.svelte.d.ts +8 -0
  45. package/dist/calendar/trigger-previous/calendar-trigger-previous.svelte +38 -0
  46. package/dist/calendar/trigger-previous/calendar-trigger-previous.svelte.d.ts +8 -0
  47. package/dist/combobox/README.md +40 -0
  48. package/dist/combobox/TODO.md +28 -175
  49. package/dist/combobox/button/README.md +15 -0
  50. package/dist/combobox/button/combobox-button.svelte +2 -0
  51. package/dist/combobox/input/README.md +16 -0
  52. package/dist/combobox/item/README.md +27 -0
  53. package/dist/combobox/item-indicator/README.md +15 -0
  54. package/dist/combobox/list/README.md +27 -0
  55. package/dist/combobox/popover/README.md +13 -0
  56. package/dist/combobox/root/README.md +44 -0
  57. package/dist/combobox/root/combobox.svelte +30 -0
  58. package/dist/combobox/tag/README.md +37 -0
  59. package/dist/combobox/tag-remove/README.md +14 -0
  60. package/dist/combobox/tags/README.md +23 -0
  61. package/dist/datepicker/README.md +100 -0
  62. package/dist/datepicker/TODO.md +28 -0
  63. package/dist/datepicker/calendar/date-picker-calendar-unsafe-props-test.svelte +60 -0
  64. package/dist/datepicker/calendar/date-picker-calendar-unsafe-props-test.svelte.d.ts +3 -0
  65. package/dist/datepicker/calendar/date-picker-calendar.svelte +65 -0
  66. package/dist/datepicker/calendar/date-picker-calendar.svelte.d.ts +10 -0
  67. package/dist/datepicker/index.d.ts +18 -0
  68. package/dist/datepicker/index.js +18 -0
  69. package/dist/datepicker/index.parts.d.ts +14 -0
  70. package/dist/datepicker/index.parts.js +14 -0
  71. package/dist/datepicker/input/date-picker-input.svelte +108 -0
  72. package/dist/datepicker/input/date-picker-input.svelte.d.ts +11 -0
  73. package/dist/datepicker/internal/strict-props.d.ts +2 -0
  74. package/dist/datepicker/internal/strict-props.js +28 -0
  75. package/dist/datepicker/popover/date-picker-popover-handler-test.svelte +57 -0
  76. package/dist/datepicker/popover/date-picker-popover-handler-test.svelte.d.ts +3 -0
  77. package/dist/datepicker/popover/date-picker-popover-unsafe-props-test.svelte +45 -0
  78. package/dist/datepicker/popover/date-picker-popover-unsafe-props-test.svelte.d.ts +18 -0
  79. package/dist/datepicker/popover/date-picker-popover.svelte +87 -0
  80. package/dist/datepicker/popover/date-picker-popover.svelte.d.ts +7 -0
  81. package/dist/datepicker/root/context.d.ts +43 -0
  82. package/dist/datepicker/root/context.js +15 -0
  83. package/dist/datepicker/root/date-picker-bindable-empty-test.svelte +24 -0
  84. package/dist/datepicker/root/date-picker-bindable-empty-test.svelte.d.ts +3 -0
  85. package/dist/datepicker/root/date-picker-bindable-test.svelte +41 -0
  86. package/dist/datepicker/root/date-picker-bindable-test.svelte.d.ts +3 -0
  87. package/dist/datepicker/root/date-picker-empty-test.svelte +47 -0
  88. package/dist/datepicker/root/date-picker-empty-test.svelte.d.ts +3 -0
  89. package/dist/datepicker/root/date-picker-locale-typing-test.svelte +47 -0
  90. package/dist/datepicker/root/date-picker-locale-typing-test.svelte.d.ts +3 -0
  91. package/dist/datepicker/root/date-picker-open-cancel-test.svelte +54 -0
  92. package/dist/datepicker/root/date-picker-open-cancel-test.svelte.d.ts +8 -0
  93. package/dist/datepicker/root/date-picker-root.svelte +495 -0
  94. package/dist/datepicker/root/date-picker-root.svelte.d.ts +24 -0
  95. package/dist/datepicker/root/date-picker-test.svelte +86 -0
  96. package/dist/datepicker/root/date-picker-test.svelte.d.ts +13 -0
  97. package/dist/datepicker/root/date-utils.d.ts +17 -0
  98. package/dist/datepicker/root/date-utils.js +138 -0
  99. package/dist/datepicker/root/draft-evaluation.d.ts +13 -0
  100. package/dist/datepicker/root/draft-evaluation.js +56 -0
  101. package/dist/datepicker/root/focus-controller.d.ts +3 -0
  102. package/dist/datepicker/root/focus-controller.js +15 -0
  103. package/dist/datepicker/root/open-change.d.ts +5 -0
  104. package/dist/datepicker/root/open-change.js +13 -0
  105. package/dist/datepicker/root/open-controller.d.ts +7 -0
  106. package/dist/datepicker/root/open-controller.js +15 -0
  107. package/dist/datepicker/root/segment-controller.d.ts +8 -0
  108. package/dist/datepicker/root/segment-controller.js +53 -0
  109. package/dist/datepicker/root/segment-state.d.ts +18 -0
  110. package/dist/datepicker/root/segment-state.js +134 -0
  111. package/dist/datepicker/root/value-commit.d.ts +4 -0
  112. package/dist/datepicker/root/value-commit.js +8 -0
  113. package/dist/datepicker/segment/date-picker-segment.svelte +319 -0
  114. package/dist/datepicker/segment/date-picker-segment.svelte.d.ts +9 -0
  115. package/dist/datepicker/trigger/date-picker-trigger.svelte +110 -0
  116. package/dist/datepicker/trigger/date-picker-trigger.svelte.d.ts +9 -0
  117. package/dist/dialog/README.md +35 -0
  118. package/dist/dialog/content/README.md +16 -0
  119. package/dist/dialog/content/dialog-content.svelte +6 -6
  120. package/dist/dialog/overlay/README.md +13 -0
  121. package/dist/dialog/portal/README.md +12 -0
  122. package/dist/dialog/root/README.md +53 -0
  123. package/dist/dialog/root/context.d.ts +2 -1
  124. package/dist/dialog/root/dialog-root.svelte +9 -2
  125. package/dist/dialog/trigger/README.md +12 -0
  126. package/dist/dialog/trigger/dialog-trigger-multi-button-test.svelte +19 -0
  127. package/dist/dialog/trigger/dialog-trigger-multi-button-test.svelte.d.ts +18 -0
  128. package/dist/dialog/trigger/dialog-trigger.svelte +18 -6
  129. package/dist/index.d.ts +7 -0
  130. package/dist/index.js +7 -0
  131. package/dist/listbox/README.md +26 -0
  132. package/dist/listbox/item/README.md +24 -0
  133. package/dist/listbox/root/README.md +40 -0
  134. package/dist/listbox/root/listbox.svelte +44 -0
  135. package/dist/locale-provider/context.d.ts +8 -0
  136. package/dist/locale-provider/context.js +18 -0
  137. package/dist/locale-provider/index.d.ts +4 -0
  138. package/dist/locale-provider/index.js +4 -0
  139. package/dist/locale-provider/locale-provider-initial-value-test.svelte +15 -0
  140. package/dist/locale-provider/locale-provider-initial-value-test.svelte.d.ts +7 -0
  141. package/dist/locale-provider/locale-provider-test.svelte +20 -0
  142. package/dist/locale-provider/locale-provider-test.svelte.d.ts +6 -0
  143. package/dist/locale-provider/locale-provider-value-probe.svelte +22 -0
  144. package/dist/locale-provider/locale-provider-value-probe.svelte.d.ts +6 -0
  145. package/dist/locale-provider/locale-provider.svelte +23 -0
  146. package/dist/locale-provider/locale-provider.svelte.d.ts +8 -0
  147. package/dist/popover/README.md +42 -0
  148. package/dist/popover/content/README.md +25 -0
  149. package/dist/popover/content/popover-content-standalone-test.svelte +28 -0
  150. package/dist/popover/content/popover-content-standalone-test.svelte.d.ts +6 -0
  151. package/dist/popover/content/popover-content-test.svelte +2 -1
  152. package/dist/popover/content/popover-content-test.svelte.d.ts +2 -1
  153. package/dist/popover/content/popover-content.svelte +91 -18
  154. package/dist/popover/content/popover-content.svelte.d.ts +5 -1
  155. package/dist/popover/index.d.ts +1 -1
  156. package/dist/popover/index.js +1 -3
  157. package/dist/popover/root/README.md +25 -0
  158. package/dist/popover/root/context.d.ts +16 -7
  159. package/dist/popover/root/context.js +0 -2
  160. package/dist/popover/root/focus-state.d.ts +4 -0
  161. package/dist/popover/root/focus-state.js +33 -0
  162. package/dist/popover/root/popover-root.svelte +90 -17
  163. package/dist/popover/root/popover-root.svelte.d.ts +2 -1
  164. package/dist/popover/root/popover-test.svelte +2 -1
  165. package/dist/popover/root/popover-test.svelte.d.ts +2 -1
  166. package/dist/popover/trigger/README.md +23 -0
  167. package/dist/popover/trigger/popover-trigger-button.svelte +10 -7
  168. package/dist/popover/trigger/popover-trigger-button.svelte.d.ts +2 -3
  169. package/dist/popover/trigger/popover-trigger-multi-button-test.svelte +16 -0
  170. package/dist/popover/trigger/popover-trigger-multi-button-test.svelte.d.ts +18 -0
  171. package/dist/popover/trigger/popover-trigger.svelte +19 -7
  172. package/dist/portal/portal.svelte +3 -1
  173. package/dist/primitives/click-outside.d.ts +1 -1
  174. package/dist/primitives/click-outside.js +1 -1
  175. package/dist/primitives/focus-trap.d.ts +7 -2
  176. package/dist/primitives/focus-trap.js +40 -6
  177. package/dist/primitives/index.d.ts +1 -0
  178. package/dist/primitives/index.js +1 -0
  179. package/dist/primitives/input-modality.d.ts +7 -0
  180. package/dist/primitives/input-modality.js +116 -0
  181. package/dist/test-utils/focus-contract.d.ts +3 -0
  182. package/dist/test-utils/focus-contract.js +26 -0
  183. package/dist/utils/date-only.d.ts +11 -0
  184. package/dist/utils/date-only.js +53 -0
  185. package/dist/utils/index.d.ts +1 -0
  186. package/dist/utils/index.js +1 -0
  187. package/package.json +16 -1
@@ -0,0 +1,19 @@
1
+ <script lang="ts">
2
+ import { Dialog } from '../../dialog';
3
+ </script>
4
+
5
+ <Dialog.Root>
6
+ <Dialog.Trigger>
7
+ <div>
8
+ <button type="button">First Dialog Trigger</button>
9
+ <button type="button">Second Dialog Trigger</button>
10
+ </div>
11
+ </Dialog.Trigger>
12
+
13
+ <Dialog.Portal>
14
+ <Dialog.Overlay />
15
+ <Dialog.Content>
16
+ <p>Dialog body</p>
17
+ </Dialog.Content>
18
+ </Dialog.Portal>
19
+ </Dialog.Root>
@@ -0,0 +1,18 @@
1
+ interface $$__sveltets_2_IsomorphicComponent<Props extends Record<string, any> = any, Events extends Record<string, any> = any, Slots extends Record<string, any> = any, Exports = {}, Bindings = string> {
2
+ new (options: import('svelte').ComponentConstructorOptions<Props>): import('svelte').SvelteComponent<Props, Events, Slots> & {
3
+ $$bindings?: Bindings;
4
+ } & Exports;
5
+ (internal: unknown, props: {
6
+ $$events?: Events;
7
+ $$slots?: Slots;
8
+ }): Exports & {
9
+ $set?: any;
10
+ $on?: any;
11
+ };
12
+ z_$$bindings?: Bindings;
13
+ }
14
+ declare const DialogTriggerMultiButtonTest: $$__sveltets_2_IsomorphicComponent<Record<string, never>, {
15
+ [evt: string]: CustomEvent<any>;
16
+ }, {}, {}, string>;
17
+ type DialogTriggerMultiButtonTest = InstanceType<typeof DialogTriggerMultiButtonTest>;
18
+ export default DialogTriggerMultiButtonTest;
@@ -24,16 +24,25 @@
24
24
  const dialogCtx = ctx;
25
25
 
26
26
  let wrapperRef: HTMLElement | null = $state(null);
27
+ let activeTrigger: HTMLElement | null = null;
28
+
29
+ function setActiveTrigger(button: HTMLElement) {
30
+ if (activeTrigger && activeTrigger !== button) {
31
+ activeTrigger.setAttribute('aria-expanded', 'false');
32
+ }
33
+
34
+ activeTrigger = button;
35
+ dialogCtx.setTriggerRef(button);
36
+ button.setAttribute('aria-haspopup', 'dialog');
37
+ button.setAttribute('aria-expanded', String(dialogCtx.isOpen));
38
+ }
27
39
 
28
40
  function handleClick(event: MouseEvent) {
29
41
  const target = event.target as HTMLElement;
30
42
  const button = target.closest('button, [role="button"]') as HTMLElement | null;
31
43
 
32
44
  if (button && wrapperRef?.contains(button)) {
33
- // Set trigger ref if not already set
34
- if (!dialogCtx.triggerRef) {
35
- dialogCtx.setTriggerRef(button);
36
- }
45
+ setActiveTrigger(button);
37
46
  dialogCtx.toggle();
38
47
  }
39
48
  }
@@ -43,8 +52,7 @@
43
52
  // Find and set up the trigger button
44
53
  const firstButton = wrapperRef.querySelector('button, [role="button"]') as HTMLElement | null;
45
54
  if (firstButton) {
46
- dialogCtx.setTriggerRef(firstButton);
47
- firstButton.setAttribute('aria-haspopup', 'dialog');
55
+ setActiveTrigger(firstButton);
48
56
  }
49
57
  }
50
58
 
@@ -59,6 +67,10 @@
59
67
 
60
68
  $effect(() => {
61
69
  if (dialogCtx.triggerRef) {
70
+ if (activeTrigger !== dialogCtx.triggerRef) {
71
+ activeTrigger = dialogCtx.triggerRef;
72
+ }
73
+ dialogCtx.triggerRef.setAttribute('aria-haspopup', 'dialog');
62
74
  dialogCtx.triggerRef.setAttribute('aria-expanded', String(dialogCtx.isOpen));
63
75
  }
64
76
  });
package/dist/index.d.ts CHANGED
@@ -1,13 +1,20 @@
1
1
  export { ComboBox } from './combobox/index.ts';
2
+ export { Calendar } from './calendar/index.ts';
3
+ export { DatePicker } from './datepicker/index.ts';
2
4
  export { Dialog } from './dialog/index.ts';
3
5
  export { ListBox } from './listbox/index.ts';
4
6
  export { Popover } from './popover/index.ts';
5
7
  export { default as Input } from './input/index.ts';
6
8
  export { default as Label } from './label/index.ts';
9
+ export { default as LocaleProvider } from './locale-provider/index.ts';
7
10
  export { Portal } from './portal/index.ts';
11
+ export * from './locale-provider/index.ts';
8
12
  export * from './combobox/index.ts';
13
+ export * from './calendar/index.ts';
14
+ export * from './datepicker/index.ts';
9
15
  export * from './dialog/index.ts';
10
16
  export * from './listbox/index.ts';
11
17
  export * from './popover/index.ts';
12
18
  export * from './primitives/index.ts';
13
19
  export { cn } from './utils/index.ts';
20
+ export * from './utils/index.ts';
package/dist/index.js CHANGED
@@ -1,15 +1,21 @@
1
1
  // Main library entry point
2
2
  // Components (namespace exports)
3
3
  export { ComboBox } from './combobox/index.ts';
4
+ export { Calendar } from './calendar/index.ts';
5
+ export { DatePicker } from './datepicker/index.ts';
4
6
  export { Dialog } from './dialog/index.ts';
5
7
  export { ListBox } from './listbox/index.ts';
6
8
  export { Popover } from './popover/index.ts';
7
9
  // Simple components
8
10
  export { default as Input } from './input/index.ts';
9
11
  export { default as Label } from './label/index.ts';
12
+ export { default as LocaleProvider } from './locale-provider/index.ts';
10
13
  export { Portal } from './portal/index.ts';
14
+ export * from './locale-provider/index.ts';
11
15
  // Re-export named exports from components
12
16
  export * from './combobox/index.ts';
17
+ export * from './calendar/index.ts';
18
+ export * from './datepicker/index.ts';
13
19
  export * from './dialog/index.ts';
14
20
  export * from './listbox/index.ts';
15
21
  export * from './popover/index.ts';
@@ -17,3 +23,4 @@ export * from './popover/index.ts';
17
23
  export * from './primitives/index.ts';
18
24
  // Utilities
19
25
  export { cn } from './utils/index.ts';
26
+ export * from './utils/index.ts';
@@ -0,0 +1,26 @@
1
+ # ListBox
2
+
3
+ ## Description
4
+
5
+ `ListBox` is a headless selectable list primitive with keyboard navigation, single and multiple selection, and controlled or uncontrolled state.
6
+
7
+ ## Usage guidelines
8
+
9
+ - Use `ListBox.Root` as the container for selection state and keyboard interactions.
10
+ - Render each option with `ListBox.Item`.
11
+ - Use `value` and `onChange` for controlled selection.
12
+ - Use `defaultValue` for uncontrolled initial selection.
13
+ - Provide `aria-label` when there is no visible label.
14
+
15
+ ## Anatomy
16
+
17
+ Import the component and compose its parts:
18
+
19
+ ```svelte
20
+ <ListBox.Root aria-label="Options">
21
+ <ListBox.Item id="1">Option 1</ListBox.Item>
22
+ </ListBox.Root>
23
+ ```
24
+
25
+ - `ListBox.Root`
26
+ - `ListBox.Item`
@@ -0,0 +1,24 @@
1
+ # ListBox Item
2
+
3
+ ## API reference
4
+
5
+ ### ListBox.Item
6
+
7
+ Name: `ListBox.Item`
8
+ Description: Selectable option element with built-in selected, focused, hovered, and disabled states.
9
+
10
+ | Prop | Type | Default | Description |
11
+ | ---------------------- | -------------------------------- | -------------------- | -------------------------------------------------------- |
12
+ | `id` | `string \| number` | `required` | Unique id for option registration and selection. |
13
+ | `textValue` | `string` | `content text` | String used for text resolution and typeahead. |
14
+ | `disabled` | `boolean` | `false` | Disables item interaction. |
15
+ | `class` | `string` | `''` | CSS class names for the item. |
16
+ | `children` | `Snippet` | `undefined` | Rendered item content. |
17
+ | `customId` | `string` | `listbox-item-${id}` | Overrides generated DOM id. |
18
+ | `disableFocusHandling` | `boolean` | `false` | Disables internal DOM focus behavior. |
19
+ | `isFocusedOverride` | `boolean` | `undefined` | Forces focused state from parent composition. |
20
+ | `onItemSelect` | `(id, label) => void` | `undefined` | Custom selection callback override. |
21
+ | `onResolvedTextValue` | `(label: string) => void` | `undefined` | Called when item text value is resolved. |
22
+ | `scrollOnFocus` | `boolean` | `false` | Scrolls item into view when focused. |
23
+ | `isParentDisabled` | `boolean` | `false` | Additional disabled state inherited from parent wrapper. |
24
+ | `...restProps` | `HTMLAttributes<HTMLDivElement>` | `-` | Additional option attributes. |
@@ -0,0 +1,40 @@
1
+ # ListBox Root
2
+
3
+ ## API reference
4
+
5
+ ### ListBox.Root
6
+
7
+ Name: `ListBox.Root`
8
+ Description: Main listbox state container that manages registration, selection, focus, and keyboard navigation.
9
+
10
+ | Prop | Type | Default | Description |
11
+ | ------------------- | ---------------------------------------- | --------------------- | ------------------------------------------------------------ |
12
+ | `selectionBehavior` | `'toggle' \| 'replace'` | `'toggle'` | Selection behavior for already selected options. |
13
+ | `emptyPlaceholder` | `string \| Snippet` | `'No items selected'` | Fallback content when no options are available. |
14
+ | `items` | `Iterable<T>` | `undefined` | Dynamic source collection for rendering. |
15
+ | `disabledIds` | `Iterable<string \| number>` | `undefined` | Option ids that should be disabled. |
16
+ | `selectionMode` | `'single' \| 'multiple'` | `'single'` | Selection mode for the listbox. |
17
+ | `value` | `Iterable<string \| number>` | `undefined` | Controlled selection values. |
18
+ | `defaultValue` | `Iterable<string \| number>` | `undefined` | Initial uncontrolled selection values. |
19
+ | `children` | `Snippet \| Snippet<[T]>` | `undefined` | Static or dynamic option rendering. |
20
+ | `class` | `string` | `''` | CSS class names for the root element. |
21
+ | `id` | `string` | `undefined` | DOM id for the listbox element. |
22
+ | `aria-label` | `string` | `undefined` | Accessible label for the listbox. |
23
+ | `onChange` | `(value: Set<string \| number>) => void` | `undefined` | Called when selection changes. |
24
+ | `context` | `ListBoxContext` | `bindable` | Exposes context via `bind:context` for advanced composition. |
25
+ | `element` | `HTMLElement` | `bindable` | Exposes root element via `bind:element`. |
26
+
27
+ ### Context utilities
28
+
29
+ Name: `context.ts` helpers
30
+ Description: Low-level APIs for creating and consuming listbox state outside visual composition.
31
+
32
+ | Prop | Type | Default | Description |
33
+ | ----------------------------------------------- | ------------------------------------------------- | ----------- | ------------------------------------------------------------ |
34
+ | `createListBoxContext(options)` | `(CreateListBoxContextOptions) => ListBoxContext` | `{}` | Creates a listbox context with selection and focus behavior. |
35
+ | `useListBoxContext()` | `() => ListBoxContext` | `-` | Returns context and throws outside `ListBox.Root`. |
36
+ | `CreateListBoxContextOptions.selectionMode` | `'single' \| 'multiple'` | `'single'` | Initial selection mode. |
37
+ | `CreateListBoxContextOptions.selectionBehavior` | `'toggle' \| 'replace'` | `'toggle'` | Initial selection behavior. |
38
+ | `CreateListBoxContextOptions.disabledIds` | `Iterable<string \| number>` | `undefined` | Initial disabled ids. |
39
+ | `CreateListBoxContextOptions.initialSelection` | `Set<string \| number>` | `new Set()` | Initial uncontrolled selection set. |
40
+ | `CreateListBoxContextOptions.onSelectionChange` | `(selection) => void` | `undefined` | Callback for selection updates. |
@@ -1,6 +1,10 @@
1
1
  <script lang="ts" generics="T extends object = object">
2
2
  import type { Snippet } from 'svelte';
3
3
  import { createListBoxContext, type ListBoxContext } from './context';
4
+ import {
5
+ shouldShowFocusVisible,
6
+ trackInteractionModality
7
+ } from '../../primitives/input-modality';
4
8
 
5
9
  /**
6
10
  * Props for the ListBox component.
@@ -114,6 +118,40 @@
114
118
 
115
119
  const itemsArray = $derived(items ? Array.from(items) : []);
116
120
  const hasItems = $derived(itemsArray.length > 0 || itemCount > 0);
121
+
122
+ let focusWithin = $state(false);
123
+ let focusVisible = $state(false);
124
+
125
+ function syncFocusWithin() {
126
+ focusWithin =
127
+ !!listboxElement &&
128
+ !!document.activeElement &&
129
+ listboxElement.contains(document.activeElement);
130
+ if (!focusWithin) {
131
+ focusVisible = false;
132
+ }
133
+ }
134
+
135
+ function handleFocusIn(event: FocusEvent) {
136
+ focusWithin = true;
137
+ focusVisible = shouldShowFocusVisible(event.target as HTMLElement | null);
138
+ }
139
+
140
+ function handleFocusOut() {
141
+ queueMicrotask(syncFocusWithin);
142
+ }
143
+
144
+ function handleMouseDown(event: MouseEvent) {
145
+ trackInteractionModality(event, event.target as HTMLElement | null);
146
+ focusVisible = false;
147
+ }
148
+
149
+ function handleKeyDown(event: KeyboardEvent) {
150
+ trackInteractionModality(event, event.target as HTMLElement | null);
151
+ if (focusWithin) {
152
+ focusVisible = true;
153
+ }
154
+ }
117
155
  </script>
118
156
 
119
157
  <div
@@ -124,7 +162,13 @@
124
162
  aria-label={ariaLabel}
125
163
  class={className}
126
164
  tabindex="0"
165
+ data-focus-within={focusWithin || undefined}
166
+ data-focus-visible={focusVisible || undefined}
127
167
  use:keyboardAction
168
+ onfocusin={handleFocusIn}
169
+ onfocusout={handleFocusOut}
170
+ onmousedown={handleMouseDown}
171
+ onkeydown={handleKeyDown}
128
172
  >
129
173
  {#if items && children}
130
174
  {#each itemsArray as item (item)}
@@ -0,0 +1,8 @@
1
+ import type { Readable } from 'svelte/store';
2
+ export type LocaleContext = {
3
+ locale: Readable<string | undefined>;
4
+ };
5
+ export declare function setLocaleContext(context: LocaleContext): void;
6
+ export declare function getLocaleContext(): LocaleContext | undefined;
7
+ export declare function useLocaleContext(): LocaleContext;
8
+ export declare function useLocaleContextOptional(): LocaleContext | undefined;
@@ -0,0 +1,18 @@
1
+ import { getContext, setContext } from 'svelte';
2
+ const KEY = Symbol('locale-provider');
3
+ export function setLocaleContext(context) {
4
+ setContext(KEY, context);
5
+ }
6
+ export function getLocaleContext() {
7
+ return getContext(KEY);
8
+ }
9
+ export function useLocaleContext() {
10
+ const context = getLocaleContext();
11
+ if (!context) {
12
+ throw new Error('LocaleProvider must wrap component tree when using useLocaleContext.');
13
+ }
14
+ return context;
15
+ }
16
+ export function useLocaleContextOptional() {
17
+ return getLocaleContext();
18
+ }
@@ -0,0 +1,4 @@
1
+ import LocaleProvider from './locale-provider.svelte';
2
+ export { LocaleProvider };
3
+ export default LocaleProvider;
4
+ export { getLocaleContext, setLocaleContext, useLocaleContext, useLocaleContextOptional, type LocaleContext } from './context';
@@ -0,0 +1,4 @@
1
+ import LocaleProvider from './locale-provider.svelte';
2
+ export { LocaleProvider };
3
+ export default LocaleProvider;
4
+ export { getLocaleContext, setLocaleContext, useLocaleContext, useLocaleContextOptional } from './context';
@@ -0,0 +1,15 @@
1
+ <script lang="ts">
2
+ import { LocaleProvider } from '../index';
3
+ import LocaleProviderValueProbe from './locale-provider-value-probe.svelte';
4
+
5
+ type Props = {
6
+ locale?: string;
7
+ onValue?: (value: string | undefined) => void;
8
+ };
9
+
10
+ let { locale = 'es-ES', onValue }: Props = $props();
11
+ </script>
12
+
13
+ <LocaleProvider {locale}>
14
+ <LocaleProviderValueProbe {onValue} />
15
+ </LocaleProvider>
@@ -0,0 +1,7 @@
1
+ type Props = {
2
+ locale?: string;
3
+ onValue?: (value: string | undefined) => void;
4
+ };
5
+ declare const LocaleProviderInitialValueTest: import("svelte").Component<Props, {}, "">;
6
+ type LocaleProviderInitialValueTest = ReturnType<typeof LocaleProviderInitialValueTest>;
7
+ export default LocaleProviderInitialValueTest;
@@ -0,0 +1,20 @@
1
+ <script lang="ts">
2
+ import { LocaleProvider } from '../index';
3
+ import Calendar from '../calendar';
4
+
5
+ type Props = {
6
+ locale?: string;
7
+ };
8
+
9
+ let { locale = 'es-ES' }: Props = $props();
10
+ </script>
11
+
12
+ <LocaleProvider {locale}>
13
+ <Calendar.Root defaultValue="2026-02-10" aria-label="Locale provider test calendar">
14
+ <Calendar.Heading />
15
+ <Calendar.Grid>
16
+ <Calendar.GridHeader />
17
+ <Calendar.GridBody />
18
+ </Calendar.Grid>
19
+ </Calendar.Root>
20
+ </LocaleProvider>
@@ -0,0 +1,6 @@
1
+ type Props = {
2
+ locale?: string;
3
+ };
4
+ declare const LocaleProviderTest: import("svelte").Component<Props, {}, "">;
5
+ type LocaleProviderTest = ReturnType<typeof LocaleProviderTest>;
6
+ export default LocaleProviderTest;
@@ -0,0 +1,22 @@
1
+ <script lang="ts">
2
+ import { useLocaleContext } from './context';
3
+
4
+ type Props = {
5
+ onValue?: (value: string | undefined) => void;
6
+ };
7
+
8
+ let { onValue }: Props = $props();
9
+
10
+ const { locale } = useLocaleContext();
11
+ const unsubscribe = locale.subscribe((value) => {
12
+ onValue?.(value);
13
+ });
14
+
15
+ $effect(() => {
16
+ return () => {
17
+ unsubscribe();
18
+ };
19
+ });
20
+ </script>
21
+
22
+ <div data-testid="locale-value-probe"></div>
@@ -0,0 +1,6 @@
1
+ type Props = {
2
+ onValue?: (value: string | undefined) => void;
3
+ };
4
+ declare const LocaleProviderValueProbe: import("svelte").Component<Props, {}, "">;
5
+ type LocaleProviderValueProbe = ReturnType<typeof LocaleProviderValueProbe>;
6
+ export default LocaleProviderValueProbe;
@@ -0,0 +1,23 @@
1
+ <script lang="ts">
2
+ import type { Snippet } from 'svelte';
3
+ import { writable } from 'svelte/store';
4
+ import { setLocaleContext } from './context';
5
+
6
+ type LocaleProviderProps = {
7
+ locale?: string;
8
+ children?: Snippet;
9
+ };
10
+
11
+ let props: LocaleProviderProps = $props();
12
+
13
+ const localeStore = writable<string | undefined>(props.locale);
14
+ setLocaleContext({ locale: localeStore });
15
+
16
+ $effect(() => {
17
+ localeStore.set(props.locale);
18
+ });
19
+ </script>
20
+
21
+ {#if props.children}
22
+ {@render props.children()}
23
+ {/if}
@@ -0,0 +1,8 @@
1
+ import type { Snippet } from 'svelte';
2
+ type LocaleProviderProps = {
3
+ locale?: string;
4
+ children?: Snippet;
5
+ };
6
+ declare const LocaleProvider: import("svelte").Component<LocaleProviderProps, {}, "">;
7
+ type LocaleProvider = ReturnType<typeof LocaleProvider>;
8
+ export default LocaleProvider;
@@ -0,0 +1,42 @@
1
+ # Popover
2
+
3
+ ## Description
4
+
5
+ `Popover` renders floating content anchored to a trigger element. It supports modal and non-modal interaction patterns, outside interaction handling, and configurable positioning.
6
+
7
+ ## Usage guidelines
8
+
9
+ - Use `Popover.Root` to share open state and trigger reference.
10
+ - Use `Popover.Trigger` when you want to auto-wire an existing button in children.
11
+ - Use `Popover.TriggerButton` when you want a pre-wired trigger button component.
12
+ - Use `Popover.Content` inside `Popover.Root`, or in standalone mode with `open`, `triggerRef`, and `onOpenChange`.
13
+ - Configure `isNonModal`, `shouldCloseOnInteractOutside`, and `shouldCloseOnBlur` to match your interaction model.
14
+
15
+ ## onOpenChange details
16
+
17
+ `Popover.Root` and standalone `Popover.Content` use:
18
+
19
+ - `onOpenChange(open, details)`
20
+ - `details.reason`: `trigger-press | imperative-action | none | escape-key | outside-press | focus-out | close-press`
21
+ - `details.event?`: native event that triggered the change when available
22
+ - `details.cancel()`: prevents the open-state transition
23
+ - `details.isCanceled`: reflects cancellation state inside the callback
24
+
25
+ ## Anatomy
26
+
27
+ Import the component and compose its parts:
28
+
29
+ ```svelte
30
+ <Popover.Root>
31
+ <Popover.Trigger>
32
+ <button>Open</button>
33
+ </Popover.Trigger>
34
+ <Popover.Content>
35
+ <div>Content</div>
36
+ </Popover.Content>
37
+ </Popover.Root>
38
+ ```
39
+
40
+ - `Popover.Root`
41
+ - `Popover.Trigger` or `Popover.TriggerButton`
42
+ - `Popover.Content`
@@ -0,0 +1,25 @@
1
+ # Popover Content
2
+
3
+ ## API reference
4
+
5
+ ### Popover.Content
6
+
7
+ Name: `Popover.Content`
8
+ Description: Floating panel rendered in a portal. Supports context mode (`Popover.Root`) and standalone controlled mode.
9
+
10
+ | Prop | Type | Default | Description |
11
+ | ------------------------------ | -------------------------------- | ----------- | ------------------------------------------------------------------------------ |
12
+ | `offset` | `number` | `8` | Main-axis offset from the anchor element. |
13
+ | `placement` | `ExtendedPlacement` | `'bottom'` | Preferred floating placement. |
14
+ | `shouldFlip` | `boolean` | `true` | Enables automatic fallback placement when space is limited. |
15
+ | `boundaryElement` | `Element \| null` | `null` | Optional boundary element for positioning constraints. |
16
+ | `children` | `Snippet` | `undefined` | Rendered popover panel content. |
17
+ | `class` | `string` | `''` | CSS class names for the panel element. |
18
+ | `isNonModal` | `boolean` | `false` | Disables modal behaviors (focus trap, scroll lock, outside aria hiding). |
19
+ | `shouldCloseOnInteractOutside` | `boolean` | `true` | Closes when interacting outside the panel. |
20
+ | `shouldCloseOnEscape` | `boolean` | `true` | Closes on Escape key press. |
21
+ | `shouldCloseOnBlur` | `boolean` | `undefined` | Closes on focus leaving trigger/content. Defaults to `true` in non-modal mode. |
22
+ | `open` | `boolean` | `undefined` | Controlled open state in standalone mode. |
23
+ | `triggerRef` | `HTMLElement \| null` | `null` | Trigger reference in standalone mode. |
24
+ | `onOpenChange` | `(open: boolean) => void` | `undefined` | Open-state callback in standalone mode. |
25
+ | `...restProps` | `HTMLAttributes<HTMLDivElement>` | `-` | Additional panel attributes. |
@@ -0,0 +1,28 @@
1
+ <script lang="ts">
2
+ import { Popover } from '../index';
3
+ import type { PopoverOpenChangeDetails } from '../root/context';
4
+
5
+ type Props = {
6
+ preventClose?: boolean;
7
+ };
8
+
9
+ let { preventClose = true }: Props = $props();
10
+
11
+ let open = $state(true);
12
+ let triggerRef = $state<HTMLElement | null>(null);
13
+
14
+ function handleOpenChange(nextOpen: boolean, details: PopoverOpenChangeDetails) {
15
+ if (!nextOpen && preventClose) {
16
+ details.cancel();
17
+ }
18
+ if (!details.isCanceled) {
19
+ open = nextOpen;
20
+ }
21
+ }
22
+ </script>
23
+
24
+ <button bind:this={triggerRef} type="button">Standalone Trigger</button>
25
+
26
+ <Popover.Content {open} {triggerRef} onOpenChange={handleOpenChange}>
27
+ <div>Standalone content</div>
28
+ </Popover.Content>
@@ -0,0 +1,6 @@
1
+ type Props = {
2
+ preventClose?: boolean;
3
+ };
4
+ declare const PopoverContentStandaloneTest: import("svelte").Component<Props, {}, "">;
5
+ type PopoverContentStandaloneTest = ReturnType<typeof PopoverContentStandaloneTest>;
6
+ export default PopoverContentStandaloneTest;
@@ -1,5 +1,6 @@
1
1
  <script lang="ts">
2
2
  import { Popover } from '../index';
3
+ import type { PopoverOpenChangeDetails } from '../root/context';
3
4
 
4
5
  type Props = {
5
6
  open?: boolean;
@@ -8,7 +9,7 @@
8
9
  shouldCloseOnInteractOutside?: boolean;
9
10
  shouldCloseOnEscape?: boolean;
10
11
  shouldCloseOnBlur?: boolean;
11
- onOpenChange?: (open: boolean) => void;
12
+ onOpenChange?: (open: boolean, details: PopoverOpenChangeDetails) => void;
12
13
  };
13
14
 
14
15
  let {
@@ -1,3 +1,4 @@
1
+ import type { PopoverOpenChangeDetails } from '../root/context';
1
2
  type Props = {
2
3
  open?: boolean;
3
4
  defaultOpen?: boolean;
@@ -5,7 +6,7 @@ type Props = {
5
6
  shouldCloseOnInteractOutside?: boolean;
6
7
  shouldCloseOnEscape?: boolean;
7
8
  shouldCloseOnBlur?: boolean;
8
- onOpenChange?: (open: boolean) => void;
9
+ onOpenChange?: (open: boolean, details: PopoverOpenChangeDetails) => void;
9
10
  };
10
11
  declare const PopoverContentTest: import("svelte").Component<Props, {}, "">;
11
12
  type PopoverContentTest = ReturnType<typeof PopoverContentTest>;