@pzerelles/headlessui-svelte 2.1.2-next.3 → 2.1.2-next.4

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 (59) hide show
  1. package/dist/button/Button.svelte +84 -54
  2. package/dist/checkbox/Checkbox.svelte +174 -120
  3. package/dist/close-button/CloseButton.svelte +12 -6
  4. package/dist/combobox/Combobox.svelte +50 -3
  5. package/dist/data-interactive/DataInteractive.svelte +57 -29
  6. package/dist/description/Description.svelte +32 -21
  7. package/dist/dialog/Dialog.svelte +69 -34
  8. package/dist/dialog/DialogBackdrop.svelte +29 -12
  9. package/dist/dialog/DialogPanel.svelte +49 -26
  10. package/dist/dialog/DialogTitle.svelte +38 -23
  11. package/dist/dialog/InternalDialog.svelte +263 -202
  12. package/dist/field/Field.svelte +49 -26
  13. package/dist/fieldset/Fieldset.svelte +50 -29
  14. package/dist/focus-trap/FocusTrap.svelte +436 -290
  15. package/dist/input/Input.svelte +85 -53
  16. package/dist/internal/FocusSentinel.svelte +16 -8
  17. package/dist/internal/ForcePortalRoot.svelte +7 -3
  18. package/dist/internal/FormFields.svelte +31 -20
  19. package/dist/internal/FormResolver.svelte +20 -15
  20. package/dist/internal/Hidden.svelte +44 -27
  21. package/dist/internal/Hidden.svelte.d.ts +2 -5
  22. package/dist/internal/HiddenFeatures.d.ts +5 -0
  23. package/dist/internal/HiddenFeatures.js +9 -0
  24. package/dist/internal/HoistFormFields.svelte +7 -4
  25. package/dist/internal/MainTreeProvider.svelte +89 -36
  26. package/dist/internal/Portal.svelte +18 -14
  27. package/dist/label/Label.svelte +91 -57
  28. package/dist/legend/Legend.svelte +18 -3
  29. package/dist/listbox/Listbox.svelte +600 -409
  30. package/dist/listbox/ListboxButton.svelte +176 -127
  31. package/dist/listbox/ListboxOption.svelte +166 -125
  32. package/dist/listbox/ListboxOptions.svelte +340 -244
  33. package/dist/listbox/ListboxSelectedOption.svelte +38 -15
  34. package/dist/menu/Menu.svelte +307 -218
  35. package/dist/menu/MenuButton.svelte +157 -115
  36. package/dist/menu/MenuHeading.svelte +34 -14
  37. package/dist/menu/MenuItem.svelte +145 -107
  38. package/dist/menu/MenuItems.svelte +298 -224
  39. package/dist/menu/MenuSection.svelte +26 -9
  40. package/dist/menu/MenuSeparator.svelte +20 -4
  41. package/dist/portal/InternalPortal.svelte +141 -85
  42. package/dist/portal/Portal.svelte +5 -2
  43. package/dist/portal/PortalGroup.svelte +30 -9
  44. package/dist/switch/Switch.svelte +179 -122
  45. package/dist/switch/Switch.svelte.d.ts +4 -4
  46. package/dist/switch/SwitchGroup.svelte +44 -31
  47. package/dist/tabs/Tab.svelte +195 -143
  48. package/dist/tabs/TabGroup.svelte +292 -205
  49. package/dist/tabs/TabList.svelte +31 -11
  50. package/dist/tabs/TabPanel.svelte +68 -43
  51. package/dist/tabs/TabPanels.svelte +18 -7
  52. package/dist/textarea/Textarea.svelte +83 -53
  53. package/dist/transition/InternalTransitionChild.svelte +259 -170
  54. package/dist/transition/Transition.svelte +96 -66
  55. package/dist/transition/TransitionChild.svelte +31 -11
  56. package/dist/utils/ElementOrComponent.svelte +44 -23
  57. package/dist/utils/Generic.svelte +29 -17
  58. package/dist/utils/StableCollection.svelte +54 -36
  59. package/package.json +10 -10
@@ -1,235 +1,324 @@
1
- <script lang="ts" module>import { useOutsideClick } from "../hooks/use-outside-click.svelte.js";
2
- import { createFloatingContext } from "../internal/floating.svelte.js";
3
- import { createOpenClosedContext, State } from "../internal/open-closed.js";
4
- import { calculateActiveIndex, Focus } from "../utils/calculate-active-index.js";
5
- import ElementOrComponent from "../utils/ElementOrComponent.svelte";
6
- import { FocusableMode, isFocusableElement, sortByDomNode } from "../utils/focus-management.js";
7
- import { match } from "../utils/match.js";
8
- let DEFAULT_MENU_TAG = "svelte:fragment";
1
+ <script lang="ts" module>
2
+ import { useOutsideClick } from "../hooks/use-outside-click.svelte.js"
3
+ import { createFloatingContext } from "../internal/floating.svelte.js"
4
+ import { createOpenClosedContext, State } from "../internal/open-closed.js"
5
+ import { calculateActiveIndex, Focus } from "../utils/calculate-active-index.js"
6
+ import ElementOrComponent from "../utils/ElementOrComponent.svelte"
7
+ import { FocusableMode, isFocusableElement, sortByDomNode } from "../utils/focus-management.js"
8
+ import { match } from "../utils/match.js"
9
+ import type { ElementType, Props } from "../utils/types.js"
10
+ import type { Snippet } from "svelte"
11
+
12
+ let DEFAULT_MENU_TAG = "svelte:fragment"
13
+ type MenuRenderPropArg = {
14
+ open: boolean
15
+ close: () => void
16
+ }
17
+ type MenuPropsWeControl = never
18
+
19
+ export type MenuProps<TTag extends ElementType = typeof DEFAULT_MENU_TAG> = Props<
20
+ TTag,
21
+ MenuRenderPropArg,
22
+ MenuPropsWeControl,
23
+ {
24
+ __demoMode?: boolean
25
+ }
26
+ >
27
+
28
+ export type MenuChildren<T> = Snippet<[MenuRenderPropArg]>
9
29
  </script>
10
30
 
11
- <script lang="ts" generics="TTag extends ElementType = typeof DEFAULT_MENU_TAG">import { setContext } from "svelte";
12
- import {
13
- ActivationTrigger,
14
- MenuStates
15
- } from "./context.svelte.js";
16
- function adjustOrderedState(state, adjustment = (i) => i) {
17
- let currentActiveItem = state.activeItemIndex !== null ? state.items[state.activeItemIndex] : null;
18
- let sortedItems = sortByDomNode(adjustment(state.items.slice()), (item) => item.dataRef.current.domRef.current);
19
- let adjustedActiveItemIndex = currentActiveItem ? sortedItems.indexOf(currentActiveItem) : null;
20
- if (adjustedActiveItemIndex === -1) {
21
- adjustedActiveItemIndex = null;
31
+ <script lang="ts" generics="TTag extends ElementType = typeof DEFAULT_MENU_TAG">
32
+ import { setContext } from "svelte"
33
+ import {
34
+ ActivationTrigger,
35
+ MenuStates,
36
+ type MenuContext,
37
+ type MenuItemDataRef,
38
+ type StateDefinition,
39
+ } from "./context.svelte.js"
40
+
41
+ function adjustOrderedState(
42
+ state: StateDefinition,
43
+ adjustment: (items: StateDefinition["items"]) => StateDefinition["items"] = (i) => i
44
+ ) {
45
+ let currentActiveItem = state.activeItemIndex !== null ? state.items[state.activeItemIndex] : null
46
+
47
+ let sortedItems = sortByDomNode(adjustment(state.items.slice()), (item) => item.dataRef.current.domRef.current)
48
+
49
+ // If we inserted an item before the current active item then the active item index
50
+ // would be wrong. To fix this, we will re-lookup the correct index.
51
+ let adjustedActiveItemIndex = currentActiveItem ? sortedItems.indexOf(currentActiveItem) : null
52
+
53
+ // Reset to `null` in case the currentActiveItem was removed.
54
+ if (adjustedActiveItemIndex === -1) {
55
+ adjustedActiveItemIndex = null
56
+ }
57
+
58
+ return {
59
+ items: sortedItems,
60
+ activeItemIndex: adjustedActiveItemIndex,
61
+ }
22
62
  }
23
- return {
24
- items: sortedItems,
25
- activeItemIndex: adjustedActiveItemIndex
26
- };
27
- }
28
- const stateReducer = (initialState) => {
29
- let _state2 = $state(initialState);
30
- return {
31
- get menuState() {
32
- return _state2.menuState;
33
- },
34
- get buttonElement() {
35
- return _state2.buttonElement;
36
- },
37
- get itemsElement() {
38
- return _state2.itemsElement;
39
- },
40
- get items() {
41
- return _state2.items;
42
- },
43
- get searchQuery() {
44
- return _state2.searchQuery;
45
- },
46
- get activeItemIndex() {
47
- return _state2.activeItemIndex;
48
- },
49
- get activationTrigger() {
50
- return _state2.activationTrigger;
51
- },
52
- get __demoMode() {
53
- return _state2.__demoMode;
54
- },
55
- closeMenu() {
56
- if (_state2.menuState === MenuStates.Closed) return _state2;
57
- _state2.activeItemIndex = null;
58
- _state2.menuState = MenuStates.Closed;
59
- return _state2;
60
- },
61
- openMenu() {
62
- if (_state2.menuState === MenuStates.Open) return _state2;
63
- _state2.__demoMode = false;
64
- _state2.menuState = MenuStates.Open;
65
- return _state2;
66
- },
67
- goToItem(action) {
68
- if (_state2.menuState === MenuStates.Closed) return _state2;
69
- _state2.searchQuery = "";
70
- _state2.activationTrigger = action.trigger ?? ActivationTrigger.Other;
71
- _state2.__demoMode = false;
72
- if (action.focus === Focus.Nothing) {
73
- _state2.activeItemIndex = null;
74
- return _state2;
75
- }
76
- if (action.focus === Focus.Specific) {
77
- _state2.activeItemIndex = _state2.items.findIndex((o) => o.id === action.id);
78
- return _state2;
79
- } else if (action.focus === Focus.Previous) {
80
- let activeItemIdx = _state2.activeItemIndex;
81
- if (activeItemIdx !== null) {
82
- let currentDom = _state2.items[activeItemIdx].dataRef.current.domRef;
83
- let previousItemIndex = calculateActiveIndex(action, {
84
- resolveItems: () => _state2.items,
85
- resolveActiveIndex: () => _state2.activeItemIndex,
86
- resolveId: (item) => item.id,
87
- resolveDisabled: (item) => item.dataRef.current.disabled
88
- });
89
- if (previousItemIndex !== null) {
90
- let previousDom = _state2.items[previousItemIndex].dataRef.current.domRef;
91
- if (
92
- // Next to each other
93
- currentDom.current?.previousElementSibling === previousDom.current || // Or already the first element
94
- previousDom.current?.previousElementSibling === null
95
- ) {
96
- _state2.activeItemIndex = previousItemIndex;
97
- return _state2;
63
+
64
+ const stateReducer = (initialState: StateDefinition) => {
65
+ let _state = $state(initialState)
66
+ return {
67
+ get menuState() {
68
+ return _state.menuState
69
+ },
70
+ get buttonElement() {
71
+ return _state.buttonElement
72
+ },
73
+ get itemsElement() {
74
+ return _state.itemsElement
75
+ },
76
+ get items() {
77
+ return _state.items
78
+ },
79
+ get searchQuery() {
80
+ return _state.searchQuery
81
+ },
82
+ get activeItemIndex() {
83
+ return _state.activeItemIndex
84
+ },
85
+ get activationTrigger() {
86
+ return _state.activationTrigger
87
+ },
88
+ get __demoMode() {
89
+ return _state.__demoMode
90
+ },
91
+ closeMenu() {
92
+ if (_state.menuState === MenuStates.Closed) return _state
93
+ _state.activeItemIndex = null
94
+ _state.menuState = MenuStates.Closed
95
+ return _state
96
+ },
97
+ openMenu() {
98
+ if (_state.menuState === MenuStates.Open) return _state
99
+ /* We can turn off demo mode once we re-open the `Menu` */
100
+ _state.__demoMode = false
101
+ _state.menuState = MenuStates.Open
102
+ return _state
103
+ },
104
+ goToItem(
105
+ action:
106
+ | { focus: Focus.Specific; id: string; trigger?: ActivationTrigger }
107
+ | { focus: Exclude<Focus, Focus.Specific>; trigger?: ActivationTrigger }
108
+ ) {
109
+ if (_state.menuState === MenuStates.Closed) return _state
110
+
111
+ _state.searchQuery = ""
112
+ _state.activationTrigger = action.trigger ?? ActivationTrigger.Other
113
+ _state.__demoMode = false
114
+
115
+ // Optimization:
116
+ //
117
+ // There is no need to sort the DOM nodes if we know that we don't want to focus anything
118
+ if (action.focus === Focus.Nothing) {
119
+ _state.activeItemIndex = null
120
+ return _state
121
+ }
122
+
123
+ // Optimization:
124
+ //
125
+ // There is no need to sort the DOM nodes if we know exactly where to go
126
+ if (action.focus === Focus.Specific) {
127
+ _state.activeItemIndex = _state.items.findIndex((o) => o.id === action.id)
128
+ return _state
129
+ }
130
+
131
+ // Optimization:
132
+ //
133
+ // If the current DOM node and the previous DOM node are next to each other,
134
+ // or if the previous DOM node is already the first DOM node, then we don't
135
+ // have to sort all the DOM nodes.
136
+ else if (action.focus === Focus.Previous) {
137
+ let activeItemIdx = _state.activeItemIndex
138
+ if (activeItemIdx !== null) {
139
+ let currentDom = _state.items[activeItemIdx].dataRef.current.domRef
140
+ let previousItemIndex = calculateActiveIndex(action, {
141
+ resolveItems: () => _state.items,
142
+ resolveActiveIndex: () => _state.activeItemIndex,
143
+ resolveId: (item) => item.id,
144
+ resolveDisabled: (item) => item.dataRef.current.disabled,
145
+ })
146
+ if (previousItemIndex !== null) {
147
+ let previousDom = _state.items[previousItemIndex].dataRef.current.domRef
148
+ if (
149
+ // Next to each other
150
+ currentDom.current?.previousElementSibling === previousDom.current ||
151
+ // Or already the first element
152
+ previousDom.current?.previousElementSibling === null
153
+ ) {
154
+ _state.activeItemIndex = previousItemIndex
155
+ return _state
156
+ }
98
157
  }
99
158
  }
100
159
  }
101
- } else if (action.focus === Focus.Next) {
102
- let activeItemIdx = _state2.activeItemIndex;
103
- if (activeItemIdx !== null) {
104
- let currentDom = _state2.items[activeItemIdx].dataRef.current.domRef;
105
- let nextItemIndex = calculateActiveIndex(action, {
106
- resolveItems: () => _state2.items,
107
- resolveActiveIndex: () => _state2.activeItemIndex,
108
- resolveId: (item) => item.id,
109
- resolveDisabled: (item) => item.dataRef.current.disabled
110
- });
111
- if (nextItemIndex !== null) {
112
- let nextDom = _state2.items[nextItemIndex].dataRef.current.domRef;
113
- if (
114
- // Next to each other
115
- currentDom.current?.nextElementSibling === nextDom.current || // Or already the last element
116
- nextDom.current?.nextElementSibling === null
117
- ) {
118
- _state2.activeItemIndex = nextItemIndex;
119
- return _state2;
160
+
161
+ // Optimization:
162
+ //
163
+ // If the current DOM node and the next DOM node are next to each other, or
164
+ // if the next DOM node is already the last DOM node, then we don't have to
165
+ // sort all the DOM nodes.
166
+ else if (action.focus === Focus.Next) {
167
+ let activeItemIdx = _state.activeItemIndex
168
+ if (activeItemIdx !== null) {
169
+ let currentDom = _state.items[activeItemIdx].dataRef.current.domRef
170
+ let nextItemIndex = calculateActiveIndex(action, {
171
+ resolveItems: () => _state.items,
172
+ resolveActiveIndex: () => _state.activeItemIndex,
173
+ resolveId: (item) => item.id,
174
+ resolveDisabled: (item) => item.dataRef.current.disabled,
175
+ })
176
+ if (nextItemIndex !== null) {
177
+ let nextDom = _state.items[nextItemIndex].dataRef.current.domRef
178
+ if (
179
+ // Next to each other
180
+ currentDom.current?.nextElementSibling === nextDom.current ||
181
+ // Or already the last element
182
+ nextDom.current?.nextElementSibling === null
183
+ ) {
184
+ _state.activeItemIndex = nextItemIndex
185
+ return _state
186
+ }
120
187
  }
121
188
  }
122
189
  }
123
- }
124
- let adjustedState = adjustOrderedState(_state2);
125
- let activeItemIndex = calculateActiveIndex(action, {
126
- resolveItems: () => adjustedState.items,
127
- resolveActiveIndex: () => adjustedState.activeItemIndex,
128
- resolveId: (item) => item.id,
129
- resolveDisabled: (item) => item.dataRef.current.disabled
130
- });
131
- _state2.items = adjustedState.items;
132
- _state2.activeItemIndex = activeItemIndex;
133
- return _state2;
134
- },
135
- search(value) {
136
- let wasAlreadySearching = _state2.searchQuery !== "";
137
- let offset = wasAlreadySearching ? 0 : 1;
138
- let searchQuery = _state2.searchQuery + value.toLowerCase();
139
- let reOrderedItems = _state2.activeItemIndex !== null ? _state2.items.slice(_state2.activeItemIndex + offset).concat(_state2.items.slice(0, _state2.activeItemIndex + offset)) : _state2.items;
140
- let matchingItem = reOrderedItems.find(
141
- (item) => item.dataRef.current.textValue?.startsWith(searchQuery) && !item.dataRef.current.disabled
142
- );
143
- let matchIdx = matchingItem ? _state2.items.indexOf(matchingItem) : -1;
144
- if (matchIdx === -1 || matchIdx === _state2.activeItemIndex) {
145
- _state2.searchQuery = searchQuery;
146
- return _state2;
147
- }
148
- _state2.searchQuery = searchQuery;
149
- _state2.activeItemIndex = matchIdx;
150
- _state2.activationTrigger = ActivationTrigger.Other;
151
- return _state2;
152
- },
153
- clearSearch() {
154
- if (_state2.searchQuery === "") return _state2;
155
- _state2.searchQuery = "";
156
- return _state2;
190
+
191
+ // Slow path:
192
+ //
193
+ // Ensure all the items are correctly sorted according to DOM position
194
+ let adjustedState = adjustOrderedState(_state)
195
+ let activeItemIndex = calculateActiveIndex(action, {
196
+ resolveItems: () => adjustedState.items,
197
+ resolveActiveIndex: () => adjustedState.activeItemIndex,
198
+ resolveId: (item) => item.id,
199
+ resolveDisabled: (item) => item.dataRef.current.disabled,
200
+ })
201
+
202
+ _state.items = adjustedState.items
203
+ _state.activeItemIndex = activeItemIndex
204
+ return _state
205
+ },
206
+ search(value: string) {
207
+ let wasAlreadySearching = _state.searchQuery !== ""
208
+ let offset = wasAlreadySearching ? 0 : 1
209
+ let searchQuery = _state.searchQuery + value.toLowerCase()
210
+
211
+ let reOrderedItems =
212
+ _state.activeItemIndex !== null
213
+ ? _state.items
214
+ .slice(_state.activeItemIndex + offset)
215
+ .concat(_state.items.slice(0, _state.activeItemIndex + offset))
216
+ : _state.items
217
+
218
+ let matchingItem = reOrderedItems.find(
219
+ (item) => item.dataRef.current.textValue?.startsWith(searchQuery) && !item.dataRef.current.disabled
220
+ )
221
+
222
+ let matchIdx = matchingItem ? _state.items.indexOf(matchingItem) : -1
223
+ if (matchIdx === -1 || matchIdx === _state.activeItemIndex) {
224
+ _state.searchQuery = searchQuery
225
+ return _state
226
+ }
227
+ _state.searchQuery = searchQuery
228
+ _state.activeItemIndex = matchIdx
229
+ _state.activationTrigger = ActivationTrigger.Other
230
+ return _state
231
+ },
232
+ clearSearch() {
233
+ if (_state.searchQuery === "") return _state
234
+ _state.searchQuery = ""
235
+ return _state
236
+ },
237
+ registerItem(id: string, dataRef: MenuItemDataRef) {
238
+ let item = { id, dataRef }
239
+ let adjustedState = adjustOrderedState(_state, (items) => [...items, item])
240
+
241
+ _state.items = adjustedState.items
242
+ _state.activeItemIndex = adjustedState.activeItemIndex
243
+ return _state
244
+ },
245
+ unregisterItem(id: string) {
246
+ let adjustedState = adjustOrderedState(_state, (items) => {
247
+ let idx = items.findIndex((a) => a.id === id)
248
+ if (idx !== -1) items.splice(idx, 1)
249
+ return items
250
+ })
251
+
252
+ _state.items = adjustedState.items
253
+ _state.activeItemIndex = adjustedState.activeItemIndex
254
+ _state.activationTrigger = ActivationTrigger.Other
255
+ return _state
256
+ },
257
+ setButtonElement(element: HTMLButtonElement | null) {
258
+ if (_state.buttonElement === element) return _state
259
+ _state.buttonElement = element
260
+ return _state
261
+ },
262
+ setItemsElement(element: HTMLElement | null) {
263
+ if (_state.itemsElement === element) return _state
264
+ _state.itemsElement = element
265
+ return _state
266
+ },
267
+ }
268
+ }
269
+
270
+ let { ref = $bindable(), __demoMode = false, ...theirProps }: { as?: TTag } & MenuProps<TTag> = $props()
271
+
272
+ const _state = stateReducer({
273
+ __demoMode,
274
+ menuState: __demoMode ? MenuStates.Open : MenuStates.Closed,
275
+ buttonElement: null,
276
+ itemsElement: null,
277
+ items: [],
278
+ searchQuery: "",
279
+ activeItemIndex: null,
280
+ activationTrigger: ActivationTrigger.Other,
281
+ } as StateDefinition)
282
+ const { menuState, itemsElement, buttonElement } = $derived(_state)
283
+ setContext<MenuContext>("MenuContext", _state)
284
+
285
+ // Handle outside click
286
+ const outsideClickEnabled = $derived(menuState === MenuStates.Open)
287
+ useOutsideClick({
288
+ get enabled() {
289
+ return outsideClickEnabled
157
290
  },
158
- registerItem(id, dataRef) {
159
- let item = { id, dataRef };
160
- let adjustedState = adjustOrderedState(_state2, (items) => [...items, item]);
161
- _state2.items = adjustedState.items;
162
- _state2.activeItemIndex = adjustedState.activeItemIndex;
163
- return _state2;
291
+ get containers() {
292
+ return [buttonElement, itemsElement]
164
293
  },
165
- unregisterItem(id) {
166
- let adjustedState = adjustOrderedState(_state2, (items) => {
167
- let idx = items.findIndex((a) => a.id === id);
168
- if (idx !== -1) items.splice(idx, 1);
169
- return items;
170
- });
171
- _state2.items = adjustedState.items;
172
- _state2.activeItemIndex = adjustedState.activeItemIndex;
173
- _state2.activationTrigger = ActivationTrigger.Other;
174
- return _state2;
294
+ cb: (event, target) => {
295
+ _state.closeMenu()
296
+
297
+ if (!isFocusableElement(target, FocusableMode.Loose)) {
298
+ event.preventDefault()
299
+ buttonElement?.focus()
300
+ }
175
301
  },
176
- setButtonElement(element) {
177
- if (_state2.buttonElement === element) return _state2;
178
- _state2.buttonElement = element;
179
- return _state2;
302
+ })
303
+
304
+ const slot = $derived({
305
+ open: _state.menuState === MenuStates.Open,
306
+ close: _state.closeMenu,
307
+ } satisfies MenuRenderPropArg)
308
+
309
+ createFloatingContext()
310
+
311
+ const openClosed = $derived(
312
+ match(menuState, {
313
+ [MenuStates.Open]: State.Open,
314
+ [MenuStates.Closed]: State.Closed,
315
+ })
316
+ )
317
+ createOpenClosedContext({
318
+ get value() {
319
+ return openClosed
180
320
  },
181
- setItemsElement(element) {
182
- if (_state2.itemsElement === element) return _state2;
183
- _state2.itemsElement = element;
184
- return _state2;
185
- }
186
- };
187
- };
188
- let { ref = $bindable(), __demoMode = false, ...theirProps } = $props();
189
- const _state = stateReducer({
190
- __demoMode,
191
- menuState: __demoMode ? MenuStates.Open : MenuStates.Closed,
192
- buttonElement: null,
193
- itemsElement: null,
194
- items: [],
195
- searchQuery: "",
196
- activeItemIndex: null,
197
- activationTrigger: ActivationTrigger.Other
198
- });
199
- const { menuState, itemsElement, buttonElement } = $derived(_state);
200
- setContext("MenuContext", _state);
201
- const outsideClickEnabled = $derived(menuState === MenuStates.Open);
202
- useOutsideClick({
203
- get enabled() {
204
- return outsideClickEnabled;
205
- },
206
- get containers() {
207
- return [buttonElement, itemsElement];
208
- },
209
- cb: (event, target) => {
210
- _state.closeMenu();
211
- if (!isFocusableElement(target, FocusableMode.Loose)) {
212
- event.preventDefault();
213
- buttonElement?.focus();
214
- }
215
- }
216
- });
217
- const slot = $derived({
218
- open: _state.menuState === MenuStates.Open,
219
- close: _state.closeMenu
220
- });
221
- createFloatingContext();
222
- const openClosed = $derived(
223
- match(menuState, {
224
- [MenuStates.Open]: State.Open,
225
- [MenuStates.Closed]: State.Closed
226
321
  })
227
- );
228
- createOpenClosedContext({
229
- get value() {
230
- return openClosed;
231
- }
232
- });
233
322
  </script>
234
323
 
235
324
  <ElementOrComponent {theirProps} {slot} defaultTag={DEFAULT_MENU_TAG} name="Menu" bind:ref />