@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,254 +1,339 @@
|
|
|
1
|
-
<script lang="ts" module>
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
</script>
|
|
1
|
+
<script lang="ts" module>
|
|
2
|
+
import type { Props } from "../utils/types.js"
|
|
3
|
+
import { RenderFeatures } from "../utils/render.js"
|
|
5
4
|
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
useFloatingPanel,
|
|
11
|
-
useFloatingPanelProps,
|
|
12
|
-
useResolvedAnchor
|
|
13
|
-
} from "../internal/floating.svelte.js";
|
|
14
|
-
import {
|
|
15
|
-
PopoverStates,
|
|
16
|
-
usePopoverAPIContext,
|
|
17
|
-
usePopoverContext
|
|
18
|
-
} from "./context.svelte.js";
|
|
19
|
-
import { getOwnerDocument } from "../utils/owner.js";
|
|
20
|
-
import { clearOpenClosedContext, State, useOpenClosed } from "../internal/open-closed.js";
|
|
21
|
-
import { transitionDataAttributes, useTransition } from "../hooks/use-transition.svelte.js";
|
|
22
|
-
import { useOnDisappear } from "../hooks/use-on-disappear.svelte.js";
|
|
23
|
-
import { useScrollLock } from "../hooks/use-scroll-lock.svelte.js";
|
|
24
|
-
import { Focus, focusIn, FocusResult, getFocusableElements } from "../utils/focus-management.js";
|
|
25
|
-
import { useElementSize } from "../hooks/use-element-size.svelte.js";
|
|
26
|
-
import { useTabDirection, Direction as TabDirection } from "../hooks/use-tab-direction.svelte.js";
|
|
27
|
-
import { match } from "../utils/match.js";
|
|
28
|
-
import { microTask } from "../utils/microTask.js";
|
|
29
|
-
import { setContext, untrack } from "svelte";
|
|
30
|
-
import Portal from "../portal/Portal.svelte";
|
|
31
|
-
import Hidden, { HiddenFeatures } from "../internal/Hidden.svelte";
|
|
32
|
-
let internalId = useId();
|
|
33
|
-
let {
|
|
34
|
-
ref = $bindable(),
|
|
35
|
-
id = `headlessui-popover-panel-${internalId}`,
|
|
36
|
-
focus = false,
|
|
37
|
-
anchor: rawAnchor,
|
|
38
|
-
portal: theirPortal = false,
|
|
39
|
-
modal = false,
|
|
40
|
-
transition = false,
|
|
41
|
-
...theirProps
|
|
42
|
-
} = $props();
|
|
43
|
-
const context = usePopoverContext("PopoverPanel");
|
|
44
|
-
const api = usePopoverAPIContext("PopoverPanel");
|
|
45
|
-
const { close, isPortalled } = $derived(api);
|
|
46
|
-
const beforePanelSentinelId = `headlessui-focus-sentinel-before-${internalId}`;
|
|
47
|
-
const afterPanelSentinelId = `headlessui-focus-sentinel-after-${internalId}`;
|
|
48
|
-
const resolvedAnchor = useResolvedAnchor({
|
|
49
|
-
get anchor() {
|
|
50
|
-
return rawAnchor;
|
|
51
|
-
}
|
|
52
|
-
});
|
|
53
|
-
const { anchor } = $derived(resolvedAnchor);
|
|
54
|
-
const floatingPanel = useFloatingPanel({
|
|
55
|
-
get placement() {
|
|
56
|
-
return anchor;
|
|
57
|
-
}
|
|
58
|
-
});
|
|
59
|
-
const { setFloating, styles } = $derived(floatingPanel);
|
|
60
|
-
const getFloatingPanelProps = useFloatingPanelProps();
|
|
61
|
-
const portal = $derived(!!anchor || theirPortal);
|
|
62
|
-
$effect(() => {
|
|
63
|
-
if (anchor) setFloating(ref ?? null);
|
|
64
|
-
untrack(() => context.setPanel(ref));
|
|
65
|
-
});
|
|
66
|
-
const ownerDocument = $derived(getOwnerDocument(ref));
|
|
67
|
-
$effect(() => {
|
|
68
|
-
id;
|
|
69
|
-
return untrack(() => {
|
|
70
|
-
context.setPanelId(id);
|
|
71
|
-
return () => {
|
|
72
|
-
context.setPanelId(void 0);
|
|
73
|
-
};
|
|
74
|
-
});
|
|
75
|
-
});
|
|
76
|
-
const usesOpenClosedState = useOpenClosed();
|
|
77
|
-
const _transition = useTransition({
|
|
78
|
-
get enabled() {
|
|
79
|
-
return transition;
|
|
80
|
-
},
|
|
81
|
-
get element() {
|
|
82
|
-
return ref;
|
|
83
|
-
},
|
|
84
|
-
get show() {
|
|
85
|
-
return usesOpenClosedState !== null ? (usesOpenClosedState.value & State.Open) === State.Open : context.popoverState === PopoverStates.Open;
|
|
86
|
-
}
|
|
87
|
-
});
|
|
88
|
-
const { visible, data: transitionData } = $derived(_transition);
|
|
89
|
-
useOnDisappear({
|
|
90
|
-
get enabled() {
|
|
91
|
-
return visible;
|
|
92
|
-
},
|
|
93
|
-
get ref() {
|
|
94
|
-
return context.button;
|
|
95
|
-
},
|
|
96
|
-
ondisappear: () => {
|
|
97
|
-
context.closePopover();
|
|
98
|
-
}
|
|
99
|
-
});
|
|
100
|
-
const scrollLockEnabled = $derived(context.__demoMode ? false : modal && visible);
|
|
101
|
-
useScrollLock({
|
|
102
|
-
get enabled() {
|
|
103
|
-
return scrollLockEnabled;
|
|
104
|
-
},
|
|
105
|
-
get ownerDocument() {
|
|
106
|
-
return ownerDocument;
|
|
107
|
-
}
|
|
108
|
-
});
|
|
109
|
-
const handleKeyDown = (event) => {
|
|
110
|
-
switch (event.key) {
|
|
111
|
-
case "Escape":
|
|
112
|
-
if (context.popoverState !== PopoverStates.Open) return;
|
|
113
|
-
if (!ref) return;
|
|
114
|
-
if (ownerDocument?.activeElement && !ref.contains(ownerDocument.activeElement)) {
|
|
115
|
-
return;
|
|
116
|
-
}
|
|
117
|
-
event.preventDefault();
|
|
118
|
-
event.stopPropagation();
|
|
119
|
-
context.closePopover();
|
|
120
|
-
context.button?.focus();
|
|
121
|
-
break;
|
|
122
|
-
}
|
|
123
|
-
};
|
|
124
|
-
$effect(() => {
|
|
125
|
-
if (theirProps.static) return;
|
|
126
|
-
if (context.popoverState === PopoverStates.Closed && (theirProps.unmount ?? true)) {
|
|
127
|
-
context.setPanel(void 0);
|
|
5
|
+
const DEFAULT_PANEL_TAG = "div" as const
|
|
6
|
+
type PanelRenderPropArg = {
|
|
7
|
+
open: boolean
|
|
8
|
+
close: (focusableElement?: HTMLElement) => void
|
|
128
9
|
}
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
10
|
+
|
|
11
|
+
const PanelRenderFeatures = RenderFeatures.RenderStrategy | RenderFeatures.Static
|
|
12
|
+
|
|
13
|
+
type PanelPropsWeControl = "tabIndex"
|
|
14
|
+
|
|
15
|
+
export type PopoverPanelProps = Props<
|
|
16
|
+
typeof DEFAULT_PANEL_TAG,
|
|
17
|
+
PanelRenderPropArg,
|
|
18
|
+
{
|
|
19
|
+
element?: HTMLElement
|
|
20
|
+
id?: string
|
|
21
|
+
focus?: boolean
|
|
22
|
+
anchor?: AnchorProps
|
|
23
|
+
portal?: boolean
|
|
24
|
+
modal?: boolean
|
|
25
|
+
transition?: boolean
|
|
26
|
+
|
|
27
|
+
// ItemsRenderFeatures
|
|
28
|
+
static?: boolean
|
|
29
|
+
unmount?: boolean
|
|
30
|
+
}
|
|
31
|
+
>
|
|
32
|
+
</script>
|
|
33
|
+
|
|
34
|
+
<script lang="ts">
|
|
35
|
+
import { useId } from "../hooks/use-id.js"
|
|
36
|
+
import ElementOrComponent from "../utils/ElementOrComponent.svelte"
|
|
37
|
+
import { mergeProps } from "../utils/render.js"
|
|
38
|
+
import {
|
|
39
|
+
useFloatingPanel,
|
|
40
|
+
useFloatingPanelProps,
|
|
41
|
+
useResolvedAnchor,
|
|
42
|
+
type AnchorProps,
|
|
43
|
+
} from "../internal/floating.svelte.js"
|
|
44
|
+
import {
|
|
45
|
+
type PopoverAPIContext,
|
|
46
|
+
type PopoverPanelContext,
|
|
47
|
+
PopoverStates,
|
|
48
|
+
usePopoverAPIContext,
|
|
49
|
+
usePopoverContext,
|
|
50
|
+
} from "./context.svelte.js"
|
|
51
|
+
import { getOwnerDocument } from "../utils/owner.js"
|
|
52
|
+
import { clearOpenClosedContext, State, useOpenClosed } from "../internal/open-closed.js"
|
|
53
|
+
import { transitionDataAttributes, useTransition } from "../hooks/use-transition.svelte.js"
|
|
54
|
+
import { useOnDisappear } from "../hooks/use-on-disappear.svelte.js"
|
|
55
|
+
import { useScrollLock } from "../hooks/use-scroll-lock.svelte.js"
|
|
56
|
+
import { Focus, focusIn, FocusResult, getFocusableElements } from "../utils/focus-management.js"
|
|
57
|
+
import { useElementSize } from "../hooks/use-element-size.svelte.js"
|
|
58
|
+
import { useTabDirection, Direction as TabDirection } from "../hooks/use-tab-direction.svelte.js"
|
|
59
|
+
import { match } from "../utils/match.js"
|
|
60
|
+
import { microTask } from "../utils/microTask.js"
|
|
61
|
+
import { setContext, untrack } from "svelte"
|
|
62
|
+
import Portal from "../portal/Portal.svelte"
|
|
63
|
+
import Hidden, { HiddenFeatures } from "../internal/Hidden.svelte"
|
|
64
|
+
|
|
65
|
+
let internalId = useId()
|
|
66
|
+
let {
|
|
67
|
+
element = $bindable(),
|
|
68
|
+
id = `headlessui-popover-panel-${internalId}`,
|
|
69
|
+
focus = false,
|
|
70
|
+
anchor: rawAnchor,
|
|
71
|
+
portal: theirPortal = false,
|
|
72
|
+
modal = false,
|
|
73
|
+
transition = false,
|
|
74
|
+
...theirProps
|
|
75
|
+
}: PopoverPanelProps = $props()
|
|
76
|
+
|
|
77
|
+
const context = usePopoverContext("PopoverPanel")
|
|
78
|
+
const api = usePopoverAPIContext("PopoverPanel")
|
|
79
|
+
const { close, isPortalled } = $derived(api)
|
|
80
|
+
|
|
81
|
+
const beforePanelSentinelId = `headlessui-focus-sentinel-before-${internalId}`
|
|
82
|
+
const afterPanelSentinelId = `headlessui-focus-sentinel-after-${internalId}`
|
|
83
|
+
|
|
84
|
+
const resolvedAnchor = useResolvedAnchor({
|
|
85
|
+
get anchor() {
|
|
86
|
+
return rawAnchor
|
|
87
|
+
},
|
|
88
|
+
})
|
|
89
|
+
const { anchor } = $derived(resolvedAnchor)
|
|
90
|
+
const floatingPanel = useFloatingPanel({
|
|
91
|
+
get placement() {
|
|
92
|
+
return anchor
|
|
93
|
+
},
|
|
94
|
+
})
|
|
95
|
+
const { setFloating, styles } = $derived(floatingPanel)
|
|
96
|
+
const getFloatingPanelProps = useFloatingPanelProps()
|
|
97
|
+
|
|
98
|
+
// Always enable `portal` functionality, when `anchor` is enabled
|
|
99
|
+
const portal = $derived(!!anchor || theirPortal)
|
|
100
|
+
|
|
101
|
+
$effect(() => {
|
|
102
|
+
if (anchor) setFloating(element ?? null)
|
|
103
|
+
untrack(() => context.setPanel(element))
|
|
104
|
+
})
|
|
105
|
+
const ownerDocument = $derived(getOwnerDocument(element))
|
|
106
|
+
|
|
107
|
+
$effect(() => {
|
|
108
|
+
id
|
|
109
|
+
return untrack(() => {
|
|
110
|
+
context.setPanelId(id)
|
|
111
|
+
return () => {
|
|
112
|
+
context.setPanelId(undefined)
|
|
161
113
|
}
|
|
162
|
-
}
|
|
163
|
-
tabIndex: -1,
|
|
164
|
-
style: [theirProps.style, styles, `--button-width: ${buttonSize.width}`].filter(Boolean).join("; "),
|
|
165
|
-
...transitionDataAttributes(transitionData)
|
|
114
|
+
})
|
|
166
115
|
})
|
|
167
|
-
|
|
168
|
-
const
|
|
169
|
-
const
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
116
|
+
|
|
117
|
+
const usesOpenClosedState = useOpenClosed()
|
|
118
|
+
const _transition = useTransition({
|
|
119
|
+
get enabled() {
|
|
120
|
+
return transition
|
|
121
|
+
},
|
|
122
|
+
get element() {
|
|
123
|
+
return element
|
|
124
|
+
},
|
|
125
|
+
get show() {
|
|
126
|
+
return usesOpenClosedState !== null
|
|
127
|
+
? (usesOpenClosedState.value & State.Open) === State.Open
|
|
128
|
+
: context.popoverState === PopoverStates.Open
|
|
129
|
+
},
|
|
130
|
+
})
|
|
131
|
+
const { visible, data: transitionData } = $derived(_transition)
|
|
132
|
+
|
|
133
|
+
// Ensure we close the popover as soon as the button becomes hidden
|
|
134
|
+
useOnDisappear({
|
|
135
|
+
get enabled() {
|
|
136
|
+
return visible
|
|
137
|
+
},
|
|
138
|
+
get ref() {
|
|
139
|
+
return context.button
|
|
140
|
+
},
|
|
141
|
+
ondisappear: () => {
|
|
142
|
+
context.closePopover()
|
|
143
|
+
},
|
|
144
|
+
})
|
|
145
|
+
|
|
146
|
+
// Enable scroll locking when the popover is visible, and `modal` is enabled
|
|
147
|
+
const scrollLockEnabled = $derived(context.__demoMode ? false : modal && visible)
|
|
148
|
+
useScrollLock({
|
|
149
|
+
get enabled() {
|
|
150
|
+
return scrollLockEnabled
|
|
151
|
+
},
|
|
152
|
+
get ownerDocument() {
|
|
153
|
+
return ownerDocument
|
|
154
|
+
},
|
|
155
|
+
})
|
|
156
|
+
|
|
157
|
+
const handleKeyDown = (event: KeyboardEvent) => {
|
|
158
|
+
switch (event.key) {
|
|
159
|
+
case "Escape":
|
|
160
|
+
if (context.popoverState !== PopoverStates.Open) return
|
|
161
|
+
if (!element) return
|
|
162
|
+
if (ownerDocument?.activeElement && !element.contains(ownerDocument.activeElement)) {
|
|
163
|
+
return
|
|
178
164
|
}
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
context.
|
|
182
|
-
|
|
183
|
-
|
|
165
|
+
event.preventDefault()
|
|
166
|
+
event.stopPropagation()
|
|
167
|
+
context.closePopover()
|
|
168
|
+
context.button?.focus()
|
|
169
|
+
break
|
|
170
|
+
}
|
|
184
171
|
}
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
172
|
+
|
|
173
|
+
// Unlink on "unmount" children
|
|
174
|
+
$effect(() => {
|
|
175
|
+
if (theirProps.static) return
|
|
176
|
+
|
|
177
|
+
if (context.popoverState === PopoverStates.Closed && (theirProps.unmount ?? true)) {
|
|
178
|
+
context.setPanel(undefined)
|
|
179
|
+
}
|
|
180
|
+
}) //, [state.popoverState, props.unmount, props.static, dispatch])
|
|
181
|
+
|
|
182
|
+
// Move focus within panel
|
|
183
|
+
$effect(() => {
|
|
184
|
+
if (context.__demoMode) return
|
|
185
|
+
if (!focus) return
|
|
186
|
+
if (context.popoverState !== PopoverStates.Open) return
|
|
187
|
+
if (!element) return
|
|
188
|
+
|
|
189
|
+
const activeElement = ownerDocument?.activeElement as HTMLElement
|
|
190
|
+
if (element.contains(activeElement)) return // Already focused within Dialog
|
|
191
|
+
|
|
192
|
+
focusIn(element, Focus.First)
|
|
193
|
+
}) //, [state.__demoMode, focus, internalPanelRef.current, state.popoverState])
|
|
194
|
+
|
|
195
|
+
const slot = $derived({
|
|
196
|
+
open: context.popoverState === PopoverStates.Open,
|
|
197
|
+
close,
|
|
198
|
+
} satisfies PanelRenderPropArg)
|
|
199
|
+
|
|
200
|
+
const buttonSize = useElementSize({
|
|
201
|
+
get element() {
|
|
202
|
+
return context.button ?? null
|
|
203
|
+
},
|
|
204
|
+
unit: true,
|
|
205
|
+
})
|
|
206
|
+
const ourProps: Record<string, any> = $derived(
|
|
207
|
+
mergeProps(anchor ? getFloatingPanelProps() : {}, {
|
|
208
|
+
id,
|
|
209
|
+
onkeydown: handleKeyDown,
|
|
210
|
+
onblur:
|
|
211
|
+
focus && context.popoverState === PopoverStates.Open
|
|
212
|
+
? (event: FocusEvent) => {
|
|
213
|
+
let el = event.relatedTarget as HTMLElement
|
|
214
|
+
if (!el) return
|
|
215
|
+
if (!element) return
|
|
216
|
+
if (element.contains(el)) return
|
|
217
|
+
|
|
218
|
+
context.closePopover()
|
|
219
|
+
|
|
220
|
+
if (context.beforePanelSentinel?.contains?.(el) || context.afterPanelSentinel?.contains?.(el)) {
|
|
221
|
+
el.focus({ preventScroll: true })
|
|
222
|
+
}
|
|
223
|
+
}
|
|
224
|
+
: undefined,
|
|
225
|
+
tabIndex: -1,
|
|
226
|
+
style: [theirProps.style, styles, `--button-width: ${buttonSize.width}`].filter(Boolean).join("; "),
|
|
227
|
+
...transitionDataAttributes(transitionData),
|
|
228
|
+
})
|
|
229
|
+
)
|
|
230
|
+
|
|
231
|
+
const direction = useTabDirection()
|
|
232
|
+
const handleBeforeFocus = () => {
|
|
233
|
+
let el = element as HTMLElement
|
|
234
|
+
if (!el) return
|
|
235
|
+
|
|
236
|
+
function run() {
|
|
237
|
+
match(direction.current, {
|
|
238
|
+
[TabDirection.Forwards]: () => {
|
|
239
|
+
// Try to focus the first thing in the panel. But if that fails (e.g.: there are no
|
|
240
|
+
// focusable elements, then we can move outside of the panel)
|
|
241
|
+
let result = focusIn(el, Focus.First)
|
|
242
|
+
if (result === FocusResult.Error) {
|
|
243
|
+
context.afterPanelSentinel?.focus()
|
|
207
244
|
}
|
|
208
|
-
}
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
}
|
|
224
|
-
};
|
|
225
|
-
clearOpenClosedContext();
|
|
226
|
-
setContext("PopoverPanelContext", {
|
|
227
|
-
get value() {
|
|
228
|
-
return id;
|
|
245
|
+
},
|
|
246
|
+
[TabDirection.Backwards]: () => {
|
|
247
|
+
// Coming from the PopoverPanel (which is portalled to somewhere else). Let's redirect
|
|
248
|
+
// the focus to the PopoverButton again.
|
|
249
|
+
context.button?.focus({ preventScroll: true })
|
|
250
|
+
},
|
|
251
|
+
})
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
// TODO: Cleanup once we are using real browser tests
|
|
255
|
+
if (process.env.NODE_ENV === "test") {
|
|
256
|
+
microTask(run)
|
|
257
|
+
} else {
|
|
258
|
+
run()
|
|
259
|
+
}
|
|
229
260
|
}
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
return
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
261
|
+
|
|
262
|
+
const handleAfterFocus = () => {
|
|
263
|
+
let el = element as HTMLElement
|
|
264
|
+
if (!el) return
|
|
265
|
+
|
|
266
|
+
function run() {
|
|
267
|
+
match(direction.current, {
|
|
268
|
+
[TabDirection.Forwards]: () => {
|
|
269
|
+
if (!context.button) return
|
|
270
|
+
|
|
271
|
+
const elements = getFocusableElements()
|
|
272
|
+
|
|
273
|
+
const idx = elements.indexOf(context.button)
|
|
274
|
+
const before = elements.slice(0, idx + 1)
|
|
275
|
+
const after = elements.slice(idx + 1)
|
|
276
|
+
|
|
277
|
+
const combined = [...after, ...before]
|
|
278
|
+
|
|
279
|
+
// Ignore sentinel buttons and items inside the panel
|
|
280
|
+
for (const element of combined.slice()) {
|
|
281
|
+
if (element.dataset.headlessuiFocusGuard === "true" || element?.contains(element)) {
|
|
282
|
+
let idx = combined.indexOf(element)
|
|
283
|
+
if (idx !== -1) combined.splice(idx, 1)
|
|
284
|
+
}
|
|
285
|
+
}
|
|
286
|
+
|
|
287
|
+
focusIn(combined, Focus.First, { sorted: false })
|
|
288
|
+
},
|
|
289
|
+
[TabDirection.Backwards]: () => {
|
|
290
|
+
// Try to focus the first thing in the panel. But if that fails (e.g.: there are no
|
|
291
|
+
// focusable elements, then we can move outside of the panel)
|
|
292
|
+
let result = focusIn(el, Focus.Previous)
|
|
293
|
+
if (result === FocusResult.Error) {
|
|
294
|
+
context.button?.focus()
|
|
295
|
+
}
|
|
296
|
+
},
|
|
297
|
+
})
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
// TODO: Cleanup once we are using real browser tests
|
|
301
|
+
if (process.env.NODE_ENV === "test") {
|
|
302
|
+
microTask(run)
|
|
303
|
+
} else {
|
|
304
|
+
run()
|
|
305
|
+
}
|
|
237
306
|
}
|
|
238
|
-
|
|
307
|
+
|
|
308
|
+
clearOpenClosedContext()
|
|
309
|
+
setContext<PopoverPanelContext>("PopoverPanelContext", {
|
|
310
|
+
get value() {
|
|
311
|
+
return id
|
|
312
|
+
},
|
|
313
|
+
})
|
|
314
|
+
setContext<PopoverAPIContext>("PopoverAPIContext", {
|
|
315
|
+
get close() {
|
|
316
|
+
return close
|
|
317
|
+
},
|
|
318
|
+
get isPortalled() {
|
|
319
|
+
return isPortalled
|
|
320
|
+
},
|
|
321
|
+
})
|
|
239
322
|
</script>
|
|
240
323
|
|
|
241
324
|
<Portal enabled={portal ? theirProps.static || visible : false}>
|
|
242
325
|
{#if visible && isPortalled}
|
|
243
|
-
<Hidden
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
326
|
+
<Hidden asChild id={beforePanelSentinelId} features={HiddenFeatures.Focusable}>
|
|
327
|
+
{#snippet children({ props })}
|
|
328
|
+
<button
|
|
329
|
+
{...props}
|
|
330
|
+
type="button"
|
|
331
|
+
data-headlessui-focus-guard
|
|
332
|
+
onfocus={handleBeforeFocus}
|
|
333
|
+
bind:this={context.beforePanelSentinel}>‌</button
|
|
334
|
+
>
|
|
335
|
+
{/snippet}
|
|
336
|
+
</Hidden>
|
|
252
337
|
{/if}
|
|
253
338
|
<ElementOrComponent
|
|
254
339
|
{ourProps}
|
|
@@ -258,17 +343,19 @@ setContext("PopoverAPIContext", {
|
|
|
258
343
|
features={PanelRenderFeatures}
|
|
259
344
|
{visible}
|
|
260
345
|
name="PopoverPanel"
|
|
261
|
-
bind:
|
|
346
|
+
bind:element
|
|
262
347
|
/>
|
|
263
348
|
{#if visible && isPortalled}
|
|
264
|
-
<Hidden
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
349
|
+
<Hidden asChild id={afterPanelSentinelId} features={HiddenFeatures.Focusable}>
|
|
350
|
+
{#snippet children({ props })}
|
|
351
|
+
<button
|
|
352
|
+
{...props}
|
|
353
|
+
type="button"
|
|
354
|
+
data-headlessui-focus-guard
|
|
355
|
+
onfocus={handleAfterFocus}
|
|
356
|
+
bind:this={context.afterPanelSentinel}>‌</button
|
|
357
|
+
>
|
|
358
|
+
{/snippet}
|
|
359
|
+
</Hidden>
|
|
273
360
|
{/if}
|
|
274
361
|
</Portal>
|
|
@@ -1,11 +1,12 @@
|
|
|
1
|
-
import type {
|
|
1
|
+
import type { Props } from "../utils/types.js";
|
|
2
2
|
declare const DEFAULT_PANEL_TAG: "div";
|
|
3
3
|
type PanelRenderPropArg = {
|
|
4
4
|
open: boolean;
|
|
5
5
|
close: (focusableElement?: HTMLElement) => void;
|
|
6
6
|
};
|
|
7
|
-
type
|
|
8
|
-
|
|
7
|
+
export type PopoverPanelProps = Props<typeof DEFAULT_PANEL_TAG, PanelRenderPropArg, {
|
|
8
|
+
element?: HTMLElement;
|
|
9
|
+
id?: string;
|
|
9
10
|
focus?: boolean;
|
|
10
11
|
anchor?: AnchorProps;
|
|
11
12
|
portal?: boolean;
|
|
@@ -15,37 +16,5 @@ export type PopoverPanelProps<TTag extends ElementType = typeof DEFAULT_PANEL_TA
|
|
|
15
16
|
unmount?: boolean;
|
|
16
17
|
}>;
|
|
17
18
|
import { type AnchorProps } from "../internal/floating.svelte.js";
|
|
18
|
-
declare
|
|
19
|
-
props(): {
|
|
20
|
-
as?: TTag | undefined;
|
|
21
|
-
} & (Exclude<keyof PropsOf<TTag>, ("slot" | "as" | "children" | "class" | "ref") | "focus" | "anchor" | "unmount" | "static" | "tabIndex" | "transition" | "portal" | "modal"> extends infer T extends keyof PropsOf<TTag> ? { [P in T]: PropsOf<TTag>[P]; } : never) & {
|
|
22
|
-
children?: import("svelte").Snippet<[{
|
|
23
|
-
slot: PanelRenderPropArg;
|
|
24
|
-
props: Record<string, any>;
|
|
25
|
-
}]> | undefined;
|
|
26
|
-
class?: string | ((bag: PanelRenderPropArg) => string) | null | undefined;
|
|
27
|
-
ref?: HTMLElement;
|
|
28
|
-
} & {
|
|
29
|
-
focus?: boolean;
|
|
30
|
-
anchor?: AnchorProps;
|
|
31
|
-
portal?: boolean;
|
|
32
|
-
modal?: boolean;
|
|
33
|
-
transition?: boolean;
|
|
34
|
-
static?: boolean;
|
|
35
|
-
unmount?: boolean;
|
|
36
|
-
};
|
|
37
|
-
events(): {};
|
|
38
|
-
slots(): {};
|
|
39
|
-
bindings(): "ref";
|
|
40
|
-
exports(): {};
|
|
41
|
-
}
|
|
42
|
-
interface $$IsomorphicComponent {
|
|
43
|
-
new <TTag extends ElementType = typeof DEFAULT_PANEL_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']>> & {
|
|
44
|
-
$$bindings?: ReturnType<__sveltets_Render<TTag>['bindings']>;
|
|
45
|
-
} & ReturnType<__sveltets_Render<TTag>['exports']>;
|
|
46
|
-
<TTag extends ElementType = typeof DEFAULT_PANEL_TAG>(internal: unknown, props: ReturnType<__sveltets_Render<TTag>['props']> & {}): ReturnType<__sveltets_Render<TTag>['exports']>;
|
|
47
|
-
z_$$bindings?: ReturnType<__sveltets_Render<any>['bindings']>;
|
|
48
|
-
}
|
|
49
|
-
declare const PopoverPanel: $$IsomorphicComponent;
|
|
50
|
-
type PopoverPanel<TTag extends ElementType = typeof DEFAULT_PANEL_TAG> = InstanceType<typeof PopoverPanel<TTag>>;
|
|
19
|
+
declare const PopoverPanel: import("svelte").Component<PopoverPanelProps, {}, "element">;
|
|
51
20
|
export default PopoverPanel;
|
package/dist/popover/index.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
export { default as Popover, type PopoverProps } from "./Popover.svelte";
|
|
2
2
|
export { default as PopoverBackdrop, type PopoverBackdropProps } from "./PopoverBackdrop.svelte";
|
|
3
|
-
export { default as PopoverButton, type PopoverButtonProps, type
|
|
3
|
+
export { default as PopoverButton, type PopoverButtonProps, type PopoverButtonSlot, type PopoverButtonPropsWeControl, } from "./PopoverButton.svelte";
|
|
4
4
|
export { default as PopoverGroup, type PopoverGroupProps } from "./PopoverGroup.svelte";
|
|
5
5
|
export { default as PopoverPanel, type PopoverPanelProps } from "./PopoverPanel.svelte";
|