@pzerelles/headlessui-svelte 2.1.2-next.31 → 2.1.2-next.32

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 (113) hide show
  1. package/dist/button/Button.svelte +84 -54
  2. package/dist/button/Button.svelte.d.ts +2 -3
  3. package/dist/checkbox/Checkbox.svelte +173 -120
  4. package/dist/checkbox/Checkbox.svelte.d.ts +2 -3
  5. package/dist/close-button/CloseButton.svelte +12 -6
  6. package/dist/close-button/CloseButton.svelte.d.ts +7 -8
  7. package/dist/combobox/Combobox.svelte +50 -3
  8. package/dist/data-interactive/DataInteractive.svelte +55 -29
  9. package/dist/data-interactive/DataInteractive.svelte.d.ts +2 -3
  10. package/dist/description/Description.svelte +31 -21
  11. package/dist/description/Description.svelte.d.ts +1 -2
  12. package/dist/dialog/Dialog.svelte +320 -228
  13. package/dist/dialog/Dialog.svelte.d.ts +2 -3
  14. package/dist/dialog/DialogBackdrop.svelte +30 -13
  15. package/dist/dialog/DialogBackdrop.svelte.d.ts +2 -3
  16. package/dist/dialog/DialogPanel.svelte +49 -26
  17. package/dist/dialog/DialogPanel.svelte.d.ts +2 -3
  18. package/dist/dialog/DialogTitle.svelte +38 -23
  19. package/dist/dialog/DialogTitle.svelte.d.ts +2 -3
  20. package/dist/field/Field.svelte +47 -25
  21. package/dist/field/Field.svelte.d.ts +1 -2
  22. package/dist/fieldset/Fieldset.svelte +50 -29
  23. package/dist/fieldset/Fieldset.svelte.d.ts +1 -2
  24. package/dist/focus-trap/FocusTrap.svelte +419 -283
  25. package/dist/focus-trap/FocusTrap.svelte.d.ts +1 -2
  26. package/dist/input/Input.svelte +84 -53
  27. package/dist/input/Input.svelte.d.ts +2 -3
  28. package/dist/internal/FloatingProvider.svelte +14 -9
  29. package/dist/internal/FocusSentinel.svelte +16 -8
  30. package/dist/internal/ForcePortalRoot.svelte +7 -3
  31. package/dist/internal/FormFields.svelte +47 -34
  32. package/dist/internal/FormFieldsProvider.svelte +9 -5
  33. package/dist/internal/FormResolver.svelte +20 -15
  34. package/dist/internal/Hidden.svelte +50 -29
  35. package/dist/internal/Hidden.svelte.d.ts +1 -2
  36. package/dist/internal/MainTreeProvider.svelte +89 -36
  37. package/dist/internal/Portal.svelte +18 -14
  38. package/dist/label/Label.svelte +93 -58
  39. package/dist/label/Label.svelte.d.ts +1 -2
  40. package/dist/legend/Legend.svelte +12 -3
  41. package/dist/listbox/Listbox.svelte +525 -387
  42. package/dist/listbox/Listbox.svelte.d.ts +2 -3
  43. package/dist/listbox/ListboxButton.svelte +173 -127
  44. package/dist/listbox/ListboxButton.svelte.d.ts +2 -3
  45. package/dist/listbox/ListboxOption.svelte +170 -129
  46. package/dist/listbox/ListboxOption.svelte.d.ts +2 -3
  47. package/dist/listbox/ListboxOptions.svelte +400 -304
  48. package/dist/listbox/ListboxOptions.svelte.d.ts +2 -3
  49. package/dist/listbox/ListboxSelectedOption.svelte +38 -15
  50. package/dist/listbox/ListboxSelectedOption.svelte.d.ts +1 -2
  51. package/dist/menu/Menu.svelte +77 -51
  52. package/dist/menu/Menu.svelte.d.ts +2 -3
  53. package/dist/menu/MenuButton.svelte +157 -117
  54. package/dist/menu/MenuButton.svelte.d.ts +2 -3
  55. package/dist/menu/MenuHeading.svelte +32 -14
  56. package/dist/menu/MenuHeading.svelte.d.ts +1 -2
  57. package/dist/menu/MenuItem.svelte +142 -107
  58. package/dist/menu/MenuItem.svelte.d.ts +2 -3
  59. package/dist/menu/MenuItems.svelte +301 -229
  60. package/dist/menu/MenuItems.svelte.d.ts +2 -3
  61. package/dist/menu/MenuSection.svelte +24 -9
  62. package/dist/menu/MenuSection.svelte.d.ts +1 -2
  63. package/dist/menu/MenuSeparator.svelte +17 -4
  64. package/dist/menu/MenuSeparator.svelte.d.ts +1 -2
  65. package/dist/popover/Popover.svelte +216 -150
  66. package/dist/popover/Popover.svelte.d.ts +2 -3
  67. package/dist/popover/PopoverBackdrop.svelte +67 -41
  68. package/dist/popover/PopoverBackdrop.svelte.d.ts +2 -3
  69. package/dist/popover/PopoverButton.svelte +292 -212
  70. package/dist/popover/PopoverButton.svelte.d.ts +2 -3
  71. package/dist/popover/PopoverGroup.svelte +62 -35
  72. package/dist/popover/PopoverGroup.svelte.d.ts +1 -2
  73. package/dist/popover/PopoverPanel.svelte +311 -229
  74. package/dist/popover/PopoverPanel.svelte.d.ts +2 -3
  75. package/dist/portal/InternalPortal.svelte +141 -85
  76. package/dist/portal/InternalPortal.svelte.d.ts +1 -2
  77. package/dist/portal/Portal.svelte +5 -2
  78. package/dist/portal/PortalGroup.svelte +30 -9
  79. package/dist/portal/PortalGroup.svelte.d.ts +1 -2
  80. package/dist/select/Select.svelte +98 -68
  81. package/dist/select/Select.svelte.d.ts +2 -3
  82. package/dist/switch/Switch.svelte +179 -132
  83. package/dist/switch/Switch.svelte.d.ts +2 -3
  84. package/dist/switch/SwitchGroup.svelte +44 -31
  85. package/dist/switch/SwitchGroup.svelte.d.ts +1 -2
  86. package/dist/tabs/Tab.svelte +194 -142
  87. package/dist/tabs/Tab.svelte.d.ts +2 -3
  88. package/dist/tabs/TabGroup.svelte +86 -56
  89. package/dist/tabs/TabGroup.svelte.d.ts +2 -3
  90. package/dist/tabs/TabList.svelte +31 -11
  91. package/dist/tabs/TabList.svelte.d.ts +2 -3
  92. package/dist/tabs/TabPanel.svelte +67 -42
  93. package/dist/tabs/TabPanel.svelte.d.ts +2 -3
  94. package/dist/tabs/TabPanels.svelte +18 -7
  95. package/dist/tabs/TabPanels.svelte.d.ts +2 -3
  96. package/dist/textarea/Textarea.svelte +84 -53
  97. package/dist/textarea/Textarea.svelte.d.ts +2 -3
  98. package/dist/transition/InternalTransitionChild.svelte +259 -170
  99. package/dist/transition/InternalTransitionChild.svelte.d.ts +2 -3
  100. package/dist/transition/Transition.svelte +96 -66
  101. package/dist/transition/Transition.svelte.d.ts +2 -3
  102. package/dist/transition/TransitionChild.svelte +31 -11
  103. package/dist/transition/TransitionChild.svelte.d.ts +2 -3
  104. package/dist/utils/DisabledProvider.svelte +7 -3
  105. package/dist/utils/ElementOrComponent.svelte +46 -23
  106. package/dist/utils/ElementOrComponent.svelte.d.ts +8 -10
  107. package/dist/utils/Generic.svelte +30 -19
  108. package/dist/utils/Generic.svelte.d.ts +6 -7
  109. package/dist/utils/StableCollection.svelte +54 -36
  110. package/dist/utils/floating-ui/svelte/components/FloatingNode.svelte +27 -12
  111. package/dist/utils/floating-ui/svelte/components/FloatingTree.svelte +88 -44
  112. package/dist/utils/types.d.ts +4 -5
  113. package/package.json +1 -1
@@ -1,237 +1,309 @@
1
- <script lang="ts" module>import { mergeProps, RenderFeatures } from "../utils/render.js";
2
- import {
3
- useFloatingPanel,
4
- useFloatingPanelProps,
5
- useResolvedAnchor
6
- } from "../internal/floating.svelte.js";
7
- const DEFAULT_ITEMS_TAG = "div";
8
- let ItemsRenderFeatures = RenderFeatures.RenderStrategy | RenderFeatures.Static;
9
- </script>
1
+ <script lang="ts" module>
2
+ import type { ElementType, Props } from "../utils/types.js"
3
+ import { mergeProps, RenderFeatures, type PropsForFeatures } from "../utils/render.js"
4
+ import {
5
+ useFloatingPanel,
6
+ useFloatingPanelProps,
7
+ useResolvedAnchor,
8
+ type AnchorProps,
9
+ } from "../internal/floating.svelte.js"
10
10
 
11
- <script lang="ts" generics="TTag extends ElementType = typeof DEFAULT_ITEMS_TAG">import { useId } from "../hooks/use-id.js";
12
- import { getOwnerDocument } from "../utils/owner.js";
13
- import { State, useOpenClosed } from "../internal/open-closed.js";
14
- import { transitionDataAttributes, useTransition } from "../hooks/use-transition.svelte.js";
15
- import { useOnDisappear } from "../hooks/use-on-disappear.svelte.js";
16
- import { useScrollLock } from "../hooks/use-scroll-lock.svelte.js";
17
- import { useInertOthers } from "../hooks/use-inert-others.svelte.js";
18
- import { useDidElementMove } from "../hooks/use-did-element-move.svelte.js";
19
- import { useDisposables } from "../utils/disposables.js";
20
- import { Focus } from "../utils/calculate-active-index.js";
21
- import { focusFrom, Focus as FocusManagementFocus, restoreFocusIfNecessary } from "../utils/focus-management.js";
22
- import { useElementSize } from "../hooks/use-element-size.svelte.js";
23
- import { tick, untrack } from "svelte";
24
- import Portal from "../portal/Portal.svelte";
25
- import { MenuStates, useMenuContext } from "./context.svelte.js";
26
- import { useTreeWalker } from "../hooks/use-tree-walker.svelte.js";
27
- import ElementOrComponent from "../utils/ElementOrComponent.svelte";
28
- const internalId = useId();
29
- let {
30
- as = DEFAULT_ITEMS_TAG,
31
- ref = $bindable(),
32
- id = `headlessui-menu-items-${internalId}`,
33
- anchor: rawAnchor,
34
- portal = false,
35
- modal = true,
36
- transition = false,
37
- ...theirProps
38
- } = $props();
39
- const resolvedAnchor = useResolvedAnchor({
40
- get anchor() {
41
- return rawAnchor;
42
- }
43
- });
44
- const { anchor } = $derived(resolvedAnchor);
45
- const _state = useMenuContext("MenuOptions");
46
- const floatingPanel = useFloatingPanel({
47
- get placement() {
48
- return anchor;
49
- }
50
- });
51
- const { setFloating, styles } = $derived(floatingPanel);
52
- const getFloatingPanelProps = useFloatingPanelProps();
53
- $effect(() => {
54
- untrack(() => _state.setItemsElement(ref ?? null));
55
- if (anchor) setFloating(ref ?? null);
56
- });
57
- const ownerDocument = $derived(getOwnerDocument(_state.itemsElement));
58
- $effect(() => {
59
- if (anchor) {
60
- portal = true;
61
- }
62
- });
63
- const usesOpenClosedState = useOpenClosed();
64
- const show = $derived(
65
- usesOpenClosedState !== null ? (usesOpenClosedState.value & State.Open) === State.Open : _state.menuState === MenuStates.Open
66
- );
67
- const _transition = useTransition({
68
- get enabled() {
69
- return transition;
70
- },
71
- get element() {
72
- return ref;
73
- },
74
- get show() {
75
- return show;
76
- }
77
- });
78
- const { visible, data: transitionData } = $derived(_transition);
79
- useOnDisappear({
80
- get enabled() {
81
- return visible;
82
- },
83
- get ref() {
84
- return _state.buttonElement;
85
- },
86
- get ondisappear() {
87
- return _state.closeMenu;
88
- }
89
- });
90
- const scrollLockEnabled = $derived(_state.__demoMode ? false : modal && _state.menuState === MenuStates.Open);
91
- useScrollLock({
92
- get enabled() {
93
- return scrollLockEnabled;
94
- },
95
- get ownerDocument() {
96
- return ownerDocument;
11
+ const DEFAULT_ITEMS_TAG = "div" as const
12
+ type ItemsRenderPropArg = {
13
+ open: boolean
97
14
  }
98
- });
99
- const inertOthersEnabled = $derived(_state.__demoMode ? false : modal && _state.menuState === MenuStates.Open);
100
- useInertOthers({
101
- get enabled() {
102
- return inertOthersEnabled;
103
- },
104
- elements: {
105
- get allowed() {
106
- return [_state.buttonElement, _state.itemsElement].filter(Boolean);
15
+ type ItemsPropsWeControl = "aria-activedescendant" | "aria-labelledby" | "role" | "tabIndex"
16
+
17
+ let ItemsRenderFeatures = RenderFeatures.RenderStrategy | RenderFeatures.Static
18
+
19
+ export type MenuItemsProps<TTag extends ElementType = typeof DEFAULT_ITEMS_TAG> = Props<
20
+ TTag,
21
+ ItemsRenderPropArg,
22
+ ItemsPropsWeControl,
23
+ {
24
+ id?: string
25
+ anchor?: AnchorProps
26
+ portal?: boolean
27
+ modal?: boolean
28
+ transition?: boolean
29
+ } & PropsForFeatures<typeof ItemsRenderFeatures>
30
+ >
31
+ </script>
32
+
33
+ <script lang="ts" generics="TTag extends ElementType = typeof DEFAULT_ITEMS_TAG">
34
+ import { useId } from "../hooks/use-id.js"
35
+ import { getOwnerDocument } from "../utils/owner.js"
36
+ import { State, useOpenClosed } from "../internal/open-closed.js"
37
+ import { transitionDataAttributes, useTransition } from "../hooks/use-transition.svelte.js"
38
+ import { useOnDisappear } from "../hooks/use-on-disappear.svelte.js"
39
+ import { useScrollLock } from "../hooks/use-scroll-lock.svelte.js"
40
+ import { useInertOthers } from "../hooks/use-inert-others.svelte.js"
41
+ import { useDidElementMove } from "../hooks/use-did-element-move.svelte.js"
42
+ import { useDisposables } from "../utils/disposables.js"
43
+ import { Focus } from "../utils/calculate-active-index.js"
44
+ import { focusFrom, Focus as FocusManagementFocus, restoreFocusIfNecessary } from "../utils/focus-management.js"
45
+ import { useElementSize } from "../hooks/use-element-size.svelte.js"
46
+ import { tick, untrack, type Snippet } from "svelte"
47
+ import Portal from "../portal/Portal.svelte"
48
+ import { MenuStates, useMenuContext } from "./context.svelte.js"
49
+ import { useTreeWalker } from "../hooks/use-tree-walker.svelte.js"
50
+ import ElementOrComponent from "../utils/ElementOrComponent.svelte"
51
+
52
+ const internalId = useId()
53
+ let {
54
+ as = DEFAULT_ITEMS_TAG as TTag,
55
+ ref = $bindable(),
56
+ id = `headlessui-menu-items-${internalId}`,
57
+ anchor: rawAnchor,
58
+ portal = false,
59
+ modal = true,
60
+ transition = false,
61
+ ...theirProps
62
+ }: { as?: TTag } & MenuItemsProps<TTag> = $props()
63
+ const resolvedAnchor = useResolvedAnchor({
64
+ get anchor() {
65
+ return rawAnchor
66
+ },
67
+ })
68
+ const { anchor } = $derived(resolvedAnchor)
69
+ const _state = useMenuContext("MenuOptions")
70
+ const floatingPanel = useFloatingPanel({
71
+ get placement() {
72
+ return anchor
73
+ },
74
+ })
75
+ const { setFloating, styles } = $derived(floatingPanel)
76
+ const getFloatingPanelProps = useFloatingPanelProps()
77
+
78
+ $effect(() => {
79
+ untrack(() => _state.setItemsElement(ref ?? null))
80
+ if (anchor) setFloating(ref ?? null)
81
+ })
82
+ const ownerDocument = $derived(getOwnerDocument(_state.itemsElement))
83
+
84
+ // Always enable `portal` functionality, when `anchor` is enabled
85
+ $effect(() => {
86
+ if (anchor) {
87
+ portal = true
88
+ }
89
+ })
90
+
91
+ const usesOpenClosedState = useOpenClosed()
92
+ const show = $derived(
93
+ usesOpenClosedState !== null
94
+ ? (usesOpenClosedState.value & State.Open) === State.Open
95
+ : _state.menuState === MenuStates.Open
96
+ )
97
+ const _transition = useTransition({
98
+ get enabled() {
99
+ return transition
100
+ },
101
+ get element() {
102
+ return ref
103
+ },
104
+ get show() {
105
+ return show
106
+ },
107
+ })
108
+ const { visible, data: transitionData } = $derived(_transition)
109
+
110
+ // Ensure we close the listbox as soon as the button becomes hidden
111
+ useOnDisappear({
112
+ get enabled() {
113
+ return visible
114
+ },
115
+ get ref() {
116
+ return _state.buttonElement
117
+ },
118
+ get ondisappear() {
119
+ return _state.closeMenu
120
+ },
121
+ })
122
+
123
+ // Enable scroll locking when the listbox is visible, and `modal` is enabled
124
+ const scrollLockEnabled = $derived(_state.__demoMode ? false : modal && _state.menuState === MenuStates.Open)
125
+ useScrollLock({
126
+ get enabled() {
127
+ return scrollLockEnabled
128
+ },
129
+ get ownerDocument() {
130
+ return ownerDocument
131
+ },
132
+ })
133
+
134
+ // Mark other elements as inert when the listbox is visible, and `modal` is enabled
135
+ const inertOthersEnabled = $derived(_state.__demoMode ? false : modal && _state.menuState === MenuStates.Open)
136
+ useInertOthers({
137
+ get enabled() {
138
+ return inertOthersEnabled
139
+ },
140
+ elements: {
141
+ get allowed() {
142
+ return [_state.buttonElement, _state.itemsElement].filter(Boolean)
143
+ },
144
+ },
145
+ })
146
+
147
+ // We keep track whether the button moved or not, we only check this when the menu state becomes
148
+ // closed. If the button moved, then we want to cancel pending transitions to prevent that the
149
+ // attached `MenuItems` is still transitioning while the button moved away.
150
+ //
151
+ // If we don't cancel these transitions then there will be a period where the `MenuItems` is
152
+ // visible and moving around because it is trying to re-position itself based on the new position.
153
+ //
154
+ // This can be solved by only transitioning the `opacity` instead of everything, but if you _do_
155
+ // want to transition the y-axis for example you will run into the same issue again.
156
+ const didElementMoveEnabled = $derived(_state.menuState !== MenuStates.Open)
157
+ const didButtonMove = useDidElementMove({
158
+ get enabled() {
159
+ return didElementMoveEnabled
160
+ },
161
+ get element() {
162
+ return _state.buttonElement
163
+ },
164
+ })
165
+
166
+ // Now that we know that the button did move or not, we can either disable the panel and all of
167
+ // its transitions, or rely on the `visible` state to hide the panel whenever necessary.
168
+ const panelEnabled = $derived(didButtonMove.value ? false : visible)
169
+
170
+ $effect(() => {
171
+ let container = _state.itemsElement
172
+ if (!container) return
173
+ if (_state.menuState !== MenuStates.Open) return
174
+ if (container === ownerDocument?.activeElement) return
175
+
176
+ container.focus({ preventScroll: true })
177
+ })
178
+
179
+ useTreeWalker({
180
+ get enabled() {
181
+ return _state.menuState === MenuStates.Open
182
+ },
183
+ get container() {
184
+ return _state.itemsElement
185
+ },
186
+ accept: (node) => {
187
+ if (node.getAttribute("role") === "menuitem") return NodeFilter.FILTER_REJECT
188
+ if (node.hasAttribute("role")) return NodeFilter.FILTER_SKIP
189
+ return NodeFilter.FILTER_ACCEPT
190
+ },
191
+ walk: (node) => {
192
+ node.setAttribute("role", "none")
193
+ },
194
+ })
195
+
196
+ const searchDisposables = useDisposables()
197
+
198
+ const handleKeyDown = async (event: KeyboardEvent) => {
199
+ searchDisposables.dispose()
200
+
201
+ switch (event.key) {
202
+ // Ref: https://www.w3.org/WAI/ARIA/apg/patterns/menu/#keyboard-interaction-12
203
+
204
+ case " ":
205
+ if (_state.searchQuery !== "") {
206
+ event.preventDefault()
207
+ event.stopPropagation()
208
+ return _state.search(event.key)
209
+ }
210
+ // When in type ahead mode, fallthrough
211
+ case "Enter":
212
+ event.preventDefault()
213
+ event.stopPropagation()
214
+ _state.closeMenu()
215
+ if (_state.activeItemIndex !== null) {
216
+ let { dataRef } = _state.items[_state.activeItemIndex]
217
+ dataRef.current?.domRef.current?.click()
218
+ }
219
+ restoreFocusIfNecessary(_state.buttonElement)
220
+ break
221
+
222
+ case "ArrowDown":
223
+ event.preventDefault()
224
+ event.stopPropagation()
225
+ return _state.goToItem({ focus: Focus.Next })
226
+
227
+ case "ArrowUp":
228
+ event.preventDefault()
229
+ event.stopPropagation()
230
+ return _state.goToItem({ focus: Focus.Previous })
231
+
232
+ case "Home":
233
+ case "PageUp":
234
+ event.preventDefault()
235
+ event.stopPropagation()
236
+ return _state.goToItem({ focus: Focus.First })
237
+
238
+ case "End":
239
+ case "PageDown":
240
+ event.preventDefault()
241
+ event.stopPropagation()
242
+ return _state.goToItem({ focus: Focus.Last })
243
+
244
+ case "Escape":
245
+ event.preventDefault()
246
+ event.stopPropagation()
247
+ _state.closeMenu()
248
+ await tick()
249
+ _state.buttonElement?.focus({ preventScroll: true })
250
+ break
251
+
252
+ case "Tab":
253
+ event.preventDefault()
254
+ event.stopPropagation()
255
+ _state.closeMenu()
256
+ await tick()
257
+ focusFrom(_state.buttonElement!, event.shiftKey ? FocusManagementFocus.Previous : FocusManagementFocus.Next)
258
+ break
259
+
260
+ default:
261
+ if (event.key.length === 1) {
262
+ _state.search(event.key)
263
+ searchDisposables.setTimeout(() => _state.clearSearch(), 350)
264
+ }
265
+ break
107
266
  }
108
267
  }
109
- });
110
- const didElementMoveEnabled = $derived(_state.menuState !== MenuStates.Open);
111
- const didButtonMove = useDidElementMove({
112
- get enabled() {
113
- return didElementMoveEnabled;
114
- },
115
- get element() {
116
- return _state.buttonElement;
117
- }
118
- });
119
- const panelEnabled = $derived(didButtonMove.value ? false : visible);
120
- $effect(() => {
121
- let container = _state.itemsElement;
122
- if (!container) return;
123
- if (_state.menuState !== MenuStates.Open) return;
124
- if (container === ownerDocument?.activeElement) return;
125
- container.focus({ preventScroll: true });
126
- });
127
- useTreeWalker({
128
- get enabled() {
129
- return _state.menuState === MenuStates.Open;
130
- },
131
- get container() {
132
- return _state.itemsElement;
133
- },
134
- accept: (node) => {
135
- if (node.getAttribute("role") === "menuitem") return NodeFilter.FILTER_REJECT;
136
- if (node.hasAttribute("role")) return NodeFilter.FILTER_SKIP;
137
- return NodeFilter.FILTER_ACCEPT;
138
- },
139
- walk: (node) => {
140
- node.setAttribute("role", "none");
141
- }
142
- });
143
- const searchDisposables = useDisposables();
144
- const handleKeyDown = async (event) => {
145
- searchDisposables.dispose();
146
- switch (event.key) {
147
- case " ":
148
- if (_state.searchQuery !== "") {
149
- event.preventDefault();
150
- event.stopPropagation();
151
- return _state.search(event.key);
152
- }
153
- case "Enter":
154
- event.preventDefault();
155
- event.stopPropagation();
156
- _state.closeMenu();
157
- if (_state.activeItemIndex !== null) {
158
- let { dataRef } = _state.items[_state.activeItemIndex];
159
- dataRef.current?.domRef.current?.click();
160
- }
161
- restoreFocusIfNecessary(_state.buttonElement);
162
- break;
163
- case "ArrowDown":
164
- event.preventDefault();
165
- event.stopPropagation();
166
- return _state.goToItem({ focus: Focus.Next });
167
- case "ArrowUp":
168
- event.preventDefault();
169
- event.stopPropagation();
170
- return _state.goToItem({ focus: Focus.Previous });
171
- case "Home":
172
- case "PageUp":
173
- event.preventDefault();
174
- event.stopPropagation();
175
- return _state.goToItem({ focus: Focus.First });
176
- case "End":
177
- case "PageDown":
178
- event.preventDefault();
179
- event.stopPropagation();
180
- return _state.goToItem({ focus: Focus.Last });
181
- case "Escape":
182
- event.preventDefault();
183
- event.stopPropagation();
184
- _state.closeMenu();
185
- await tick();
186
- _state.buttonElement?.focus({ preventScroll: true });
187
- break;
188
- case "Tab":
189
- event.preventDefault();
190
- event.stopPropagation();
191
- _state.closeMenu();
192
- await tick();
193
- focusFrom(_state.buttonElement, event.shiftKey ? FocusManagementFocus.Previous : FocusManagementFocus.Next);
194
- break;
195
- default:
196
- if (event.key.length === 1) {
197
- _state.search(event.key);
198
- searchDisposables.setTimeout(() => _state.clearSearch(), 350);
199
- }
200
- break;
201
- }
202
- };
203
- const handleKeyUp = (event) => {
204
- switch (event.key) {
205
- case " ":
206
- event.preventDefault();
207
- break;
268
+
269
+ const handleKeyUp = (event: KeyboardEvent) => {
270
+ switch (event.key) {
271
+ case " ":
272
+ // Required for firefox, event.preventDefault() in handleKeyDown for
273
+ // the Space key doesn't cancel the handleKeyUp, which in turn
274
+ // triggers a *click*.
275
+ event.preventDefault()
276
+ break
277
+ }
208
278
  }
209
- };
210
- const slot = $derived({
211
- open: _state.menuState === MenuStates.Open
212
- });
213
- const buttonSize = useElementSize({
214
- get element() {
215
- return _state.buttonElement;
216
- },
217
- unit: true
218
- });
219
- const ourProps = $derived({
220
- ...mergeProps(anchor ? getFloatingPanelProps() : {}, {
221
- "aria-activedescendant": _state.activeItemIndex === null ? void 0 : _state.items[_state.activeItemIndex]?.id,
222
- "aria-labelledby": _state.buttonElement?.id,
223
- id,
224
- onkeydown: handleKeyDown,
225
- onkeyup: handleKeyUp,
226
- role: "menu",
227
- // When the `Menu` is closed, it should not be focusable. This allows us
228
- // to skip focusing the `MenuItems` when pressing the tab key on an
229
- // open `Menu`, and go to the next focusable element.
230
- tabindex: _state.menuState === MenuStates.Open ? 0 : void 0,
231
- style: [theirProps.style, styles, `--button-width: ${buttonSize.width}`].filter(Boolean).join("; ")
232
- }),
233
- ...transitionDataAttributes(transitionData)
234
- });
279
+
280
+ const slot = $derived({
281
+ open: _state.menuState === MenuStates.Open,
282
+ } satisfies ItemsRenderPropArg)
283
+
284
+ const buttonSize = useElementSize({
285
+ get element() {
286
+ return _state.buttonElement
287
+ },
288
+ unit: true,
289
+ })
290
+
291
+ const ourProps = $derived({
292
+ ...mergeProps(anchor ? getFloatingPanelProps() : {}, {
293
+ "aria-activedescendant": _state.activeItemIndex === null ? undefined : _state.items[_state.activeItemIndex]?.id,
294
+ "aria-labelledby": _state.buttonElement?.id,
295
+ id,
296
+ onkeydown: handleKeyDown,
297
+ onkeyup: handleKeyUp,
298
+ role: "menu",
299
+ // When the `Menu` is closed, it should not be focusable. This allows us
300
+ // to skip focusing the `MenuItems` when pressing the tab key on an
301
+ // open `Menu`, and go to the next focusable element.
302
+ tabindex: _state.menuState === MenuStates.Open ? 0 : undefined,
303
+ style: [theirProps.style, styles, `--button-width: ${buttonSize.width}`].filter(Boolean).join("; "),
304
+ }),
305
+ ...transitionDataAttributes(transitionData),
306
+ })
235
307
  </script>
236
308
 
237
309
  <Portal enabled={portal ? theirProps.static || visible : false}>
@@ -19,9 +19,8 @@ declare class __sveltets_Render<TTag extends ElementType = typeof DEFAULT_ITEMS_
19
19
  props(): {
20
20
  as?: TTag | undefined;
21
21
  } & (Exclude<keyof import("../utils/types.js").PropsOf<TTag>, ("slot" | "as" | "children" | "class" | "ref") | "anchor" | "unmount" | "static" | "id" | "transition" | "portal" | "modal" | ItemsPropsWeControl> extends infer T extends keyof import("../utils/types.js").PropsOf<TTag> ? { [P in T]: import("../utils/types.js").PropsOf<TTag>[P]; } : never) & {
22
- children?: Snippet<[{
23
- slot: ItemsRenderPropArg;
24
- props: Record<string, any>;
22
+ children?: Snippet<[ItemsRenderPropArg & {
23
+ props?: Record<string, any>;
25
24
  }]> | undefined;
26
25
  class?: string | ((bag: ItemsRenderPropArg) => string) | null | undefined;
27
26
  ref?: HTMLElement;
@@ -1,14 +1,29 @@
1
- <script lang="ts" module>const DEFAULT_SECTION_TAG = "div";
1
+ <script lang="ts" module>
2
+ import type { Snippet } from "svelte"
3
+ import type { ElementType, Props } from "../utils/types.js"
4
+
5
+ const DEFAULT_SECTION_TAG = "div" as const
6
+ type SectionRenderPropArg = {}
7
+ type SectionPropsWeControl = "role" | "aria-labelledby"
8
+
9
+ export type MenuSectionProps<TTag extends ElementType = typeof DEFAULT_SECTION_TAG> = Props<
10
+ TTag,
11
+ SectionRenderPropArg,
12
+ SectionPropsWeControl
13
+ >
2
14
  </script>
3
15
 
4
- <script lang="ts" generics="TTag extends ElementType = typeof DEFAULT_SECTION_TAG">import { useLabels } from "../label/context.svelte.js";
5
- import ElementOrComponent from "../utils/ElementOrComponent.svelte";
6
- const labelledby = useLabels();
7
- let { ref = $bindable(), ...theirProps } = $props();
8
- const ourProps = $derived({
9
- "aria-labelledby": labelledby,
10
- role: "group"
11
- });
16
+ <script lang="ts" generics="TTag extends ElementType = typeof DEFAULT_SECTION_TAG">
17
+ import { useLabels } from "../label/context.svelte.js"
18
+ import ElementOrComponent from "../utils/ElementOrComponent.svelte"
19
+
20
+ const labelledby = useLabels()
21
+
22
+ let { ref = $bindable(), ...theirProps }: { as?: TTag } & MenuSectionProps<TTag> = $props()
23
+ const ourProps = $derived({
24
+ "aria-labelledby": labelledby,
25
+ role: "group",
26
+ })
12
27
  </script>
13
28
 
14
29
  <ElementOrComponent {ourProps} {theirProps} defaultTag={DEFAULT_SECTION_TAG} name="MenuSection" bind:ref />
@@ -9,8 +9,7 @@ declare class __sveltets_Render<TTag extends ElementType = typeof DEFAULT_SECTIO
9
9
  as?: TTag | undefined;
10
10
  } & (Exclude<keyof import("../utils/types.js").PropsOf<TTag>, ("slot" | "as" | "children" | "class" | "ref") | SectionPropsWeControl> extends infer T extends keyof import("../utils/types.js").PropsOf<TTag> ? { [P in T]: import("../utils/types.js").PropsOf<TTag>[P]; } : never) & {
11
11
  children?: Snippet<[{
12
- slot: SectionRenderPropArg;
13
- props: Record<string, any>;
12
+ props?: Record<string, any>;
14
13
  }]> | undefined;
15
14
  class?: string | ((bag: SectionRenderPropArg) => string) | null | undefined;
16
15
  ref?: HTMLElement;
@@ -1,9 +1,22 @@
1
- <script lang="ts" module>const DEFAULT_SEPARATOR_TAG = "div";
1
+ <script lang="ts" module>
2
+ import type { ElementType, Props } from "../utils/types.js"
3
+
4
+ const DEFAULT_SEPARATOR_TAG = "div" as const
5
+ type SeparatorRenderPropArg = {}
6
+ type SeparatorPropsWeControl = "role"
7
+
8
+ export type MenuSeparatorProps<TTag extends ElementType = typeof DEFAULT_SEPARATOR_TAG> = Props<
9
+ TTag,
10
+ SeparatorRenderPropArg,
11
+ SeparatorPropsWeControl
12
+ >
2
13
  </script>
3
14
 
4
- <script lang="ts" generics="TTag extends ElementType = typeof DEFAULT_SEPARATOR_TAG">import ElementOrComponent from "../utils/ElementOrComponent.svelte";
5
- let { ref = $bindable(), ...theirProps } = $props();
6
- const ourProps = { role: "separator" };
15
+ <script lang="ts" generics="TTag extends ElementType = typeof DEFAULT_SEPARATOR_TAG">
16
+ import ElementOrComponent from "../utils/ElementOrComponent.svelte"
17
+
18
+ let { ref = $bindable(), ...theirProps }: { as?: TTag } & MenuSeparatorProps<TTag> = $props()
19
+ const ourProps = { role: "separator" }
7
20
  </script>
8
21
 
9
22
  <ElementOrComponent {ourProps} {theirProps} defaultTag={DEFAULT_SEPARATOR_TAG} name="MenuSeparator" bind:ref />
@@ -8,8 +8,7 @@ declare class __sveltets_Render<TTag extends ElementType = typeof DEFAULT_SEPARA
8
8
  as?: TTag | undefined;
9
9
  } & (Exclude<keyof import("../utils/types.js").PropsOf<TTag>, ("slot" | "as" | "children" | "class" | "ref") | "role"> extends infer T extends keyof import("../utils/types.js").PropsOf<TTag> ? { [P in T]: import("../utils/types.js").PropsOf<TTag>[P]; } : never) & {
10
10
  children?: import("svelte").Snippet<[{
11
- slot: SeparatorRenderPropArg;
12
- props: Record<string, any>;
11
+ props?: Record<string, any>;
13
12
  }]> | undefined;
14
13
  class?: string | ((bag: SeparatorRenderPropArg) => string) | null | undefined;
15
14
  ref?: HTMLElement;