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