@pzerelles/headlessui-svelte 2.1.2-next.22 → 2.1.2-next.24
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 -54
- package/dist/button/Button.svelte.d.ts +7 -4
- package/dist/checkbox/Checkbox.svelte +173 -120
- package/dist/checkbox/Checkbox.svelte.d.ts +7 -4
- package/dist/close-button/CloseButton.svelte +12 -6
- package/dist/close-button/CloseButton.svelte.d.ts +13 -10
- package/dist/combobox/Combobox.svelte +50 -3
- package/dist/data-interactive/DataInteractive.svelte +55 -29
- package/dist/data-interactive/DataInteractive.svelte.d.ts +7 -5
- package/dist/description/Description.svelte +39 -24
- package/dist/description/Description.svelte.d.ts +8 -5
- package/dist/description/context.svelte.js +13 -15
- package/dist/dialog/Dialog.svelte +358 -38
- package/dist/dialog/Dialog.svelte.d.ts +10 -7
- package/dist/dialog/DialogBackdrop.svelte +30 -13
- package/dist/dialog/DialogBackdrop.svelte.d.ts +7 -4
- package/dist/dialog/DialogPanel.svelte +49 -26
- package/dist/dialog/DialogPanel.svelte.d.ts +7 -4
- package/dist/dialog/DialogTitle.svelte +38 -23
- package/dist/dialog/DialogTitle.svelte.d.ts +7 -4
- package/dist/field/Field.svelte +50 -34
- package/dist/field/Field.svelte.d.ts +7 -4
- package/dist/fieldset/Fieldset.svelte +50 -29
- package/dist/fieldset/Fieldset.svelte.d.ts +7 -4
- package/dist/focus-trap/FocusTrap.svelte +419 -283
- package/dist/focus-trap/FocusTrap.svelte.d.ts +7 -4
- package/dist/hooks/use-disabled.d.ts +4 -1
- package/dist/hooks/use-disabled.js +10 -5
- package/dist/input/Input.svelte +84 -53
- package/dist/input/Input.svelte.d.ts +7 -4
- package/dist/internal/FloatingProvider.svelte +14 -9
- package/dist/internal/FocusSentinel.svelte +16 -8
- 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 +20 -15
- package/dist/internal/Hidden.svelte +50 -29
- package/dist/internal/Hidden.svelte.d.ts +7 -4
- package/dist/internal/MainTreeProvider.svelte +89 -36
- package/dist/internal/Portal.svelte +18 -14
- package/dist/internal/floating-provider.svelte.js +1 -1
- package/dist/internal/floating.svelte.d.ts +5 -5
- package/dist/internal/floating.svelte.js +17 -17
- package/dist/label/Label.svelte +93 -58
- package/dist/label/Label.svelte.d.ts +7 -4
- package/dist/legend/Legend.svelte +12 -3
- package/dist/listbox/Listbox.svelte +525 -387
- package/dist/listbox/Listbox.svelte.d.ts +7 -5
- package/dist/listbox/ListboxButton.svelte +173 -127
- package/dist/listbox/ListboxButton.svelte.d.ts +7 -5
- package/dist/listbox/ListboxOption.svelte +170 -129
- package/dist/listbox/ListboxOption.svelte.d.ts +7 -5
- package/dist/listbox/ListboxOptions.svelte +400 -304
- package/dist/listbox/ListboxOptions.svelte.d.ts +7 -5
- package/dist/listbox/ListboxSelectedOption.svelte +38 -15
- package/dist/listbox/ListboxSelectedOption.svelte.d.ts +7 -4
- package/dist/listbox/index.d.ts +4 -4
- package/dist/listbox/index.js +1 -1
- package/dist/menu/Menu.svelte +78 -57
- package/dist/menu/Menu.svelte.d.ts +7 -5
- package/dist/menu/MenuButton.svelte +157 -117
- package/dist/menu/MenuButton.svelte.d.ts +7 -5
- package/dist/menu/MenuHeading.svelte +32 -14
- package/dist/menu/MenuHeading.svelte.d.ts +7 -5
- package/dist/menu/MenuItem.svelte +142 -107
- package/dist/menu/MenuItem.svelte.d.ts +8 -8
- package/dist/menu/MenuItems.svelte +301 -229
- package/dist/menu/MenuItems.svelte.d.ts +7 -5
- package/dist/menu/MenuSection.svelte +24 -9
- package/dist/menu/MenuSection.svelte.d.ts +7 -5
- package/dist/menu/MenuSeparator.svelte +17 -4
- package/dist/menu/MenuSeparator.svelte.d.ts +7 -6
- package/dist/menu/context.svelte.d.ts +1 -29
- package/dist/menu/context.svelte.js +29 -27
- package/dist/menu/index.d.ts +7 -7
- package/dist/popover/Popover.svelte +216 -150
- package/dist/popover/Popover.svelte.d.ts +7 -4
- package/dist/popover/PopoverBackdrop.svelte +67 -41
- package/dist/popover/PopoverBackdrop.svelte.d.ts +7 -4
- package/dist/popover/PopoverButton.svelte +292 -212
- package/dist/popover/PopoverButton.svelte.d.ts +7 -4
- package/dist/popover/PopoverGroup.svelte +62 -35
- package/dist/popover/PopoverGroup.svelte.d.ts +7 -4
- package/dist/popover/PopoverPanel.svelte +311 -229
- package/dist/popover/PopoverPanel.svelte.d.ts +7 -4
- package/dist/portal/InternalPortal.svelte +141 -85
- package/dist/portal/InternalPortal.svelte.d.ts +7 -4
- package/dist/portal/Portal.svelte +5 -2
- package/dist/portal/PortalGroup.svelte +30 -9
- package/dist/portal/PortalGroup.svelte.d.ts +7 -4
- package/dist/select/Select.svelte +98 -68
- package/dist/select/Select.svelte.d.ts +7 -4
- package/dist/switch/Switch.svelte +179 -132
- package/dist/switch/Switch.svelte.d.ts +7 -4
- package/dist/switch/SwitchGroup.svelte +44 -31
- package/dist/switch/SwitchGroup.svelte.d.ts +7 -4
- package/dist/tabs/Tab.svelte +194 -143
- package/dist/tabs/Tab.svelte.d.ts +7 -4
- package/dist/tabs/TabGroup.svelte +81 -214
- package/dist/tabs/TabGroup.svelte.d.ts +7 -24
- package/dist/tabs/TabList.svelte +31 -11
- package/dist/tabs/TabList.svelte.d.ts +7 -4
- package/dist/tabs/TabPanel.svelte +67 -43
- package/dist/tabs/TabPanel.svelte.d.ts +7 -4
- package/dist/tabs/TabPanels.svelte +18 -7
- package/dist/tabs/TabPanels.svelte.d.ts +7 -4
- package/dist/tabs/context.svelte.d.ts +31 -0
- package/dist/tabs/context.svelte.js +134 -0
- package/dist/textarea/Textarea.svelte +84 -53
- package/dist/textarea/Textarea.svelte.d.ts +7 -4
- package/dist/transition/InternalTransitionChild.svelte +259 -170
- package/dist/transition/InternalTransitionChild.svelte.d.ts +7 -4
- package/dist/transition/Transition.svelte +96 -66
- package/dist/transition/Transition.svelte.d.ts +7 -4
- package/dist/transition/TransitionChild.svelte +31 -11
- package/dist/transition/TransitionChild.svelte.d.ts +7 -4
- package/dist/utils/ElementOrComponent.svelte +43 -23
- package/dist/utils/ElementOrComponent.svelte.d.ts +10 -4
- package/dist/utils/Generic.svelte +36 -22
- package/dist/utils/Generic.svelte.d.ts +7 -4
- 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/floating-ui/svelte/hooks/useFloating.svelte.js +7 -7
- package/dist/utils/floating-ui/svelte/hooks/useFloatingRootContext.svelte.js +1 -1
- package/dist/utils/floating-ui/svelte/types.d.ts +4 -4
- package/dist/utils/floating-ui/svelte-dom/types.d.ts +2 -2
- package/dist/utils/floating-ui/svelte-dom/useFloating.svelte.js +6 -6
- package/dist/utils/types.d.ts +11 -4
- package/package.json +2 -2
- package/dist/dialog/InternalDialog.svelte +0 -233
- package/dist/dialog/InternalDialog.svelte.d.ts +0 -42
|
@@ -1,18 +1,19 @@
|
|
|
1
|
-
import type { Snippet } from "svelte";
|
|
2
1
|
import type { ElementType, Props } from "../utils/types.js";
|
|
3
2
|
declare const DEFAULT_SEPARATOR_TAG: "div";
|
|
4
3
|
type SeparatorRenderPropArg = {};
|
|
5
4
|
type SeparatorPropsWeControl = "role";
|
|
6
5
|
export type MenuSeparatorProps<TTag extends ElementType = typeof DEFAULT_SEPARATOR_TAG> = Props<TTag, SeparatorRenderPropArg, SeparatorPropsWeControl>;
|
|
7
|
-
export type MenuSeparatorChildren = Snippet<[SeparatorRenderPropArg]>;
|
|
8
6
|
declare class __sveltets_Render<TTag extends ElementType = typeof DEFAULT_SEPARATOR_TAG> {
|
|
9
7
|
props(): {
|
|
10
8
|
as?: TTag | undefined;
|
|
11
|
-
} & (Exclude<keyof import("../utils/types.js").PropsOf<TTag>, ("as" | "children" | "
|
|
12
|
-
children?: Snippet<[
|
|
9
|
+
} & (Exclude<keyof import("../utils/types.js").PropsOf<TTag>, ("as" | "children" | "class") | "role"> extends infer T extends keyof import("../utils/types.js").PropsOf<TTag> ? { [P in T]: import("../utils/types.js").PropsOf<TTag>[P]; } : never) & {
|
|
10
|
+
children?: import("svelte").Snippet<[{
|
|
11
|
+
slot: SeparatorRenderPropArg;
|
|
12
|
+
props: Record<string, any>;
|
|
13
|
+
}]> | undefined;
|
|
13
14
|
ref?: HTMLElement;
|
|
14
|
-
} & (true extends (import("
|
|
15
|
-
class?:
|
|
15
|
+
} & (true extends (import("svelte/elements").SvelteHTMLElements[TTag] extends infer T_1 ? T_1 extends import("svelte/elements").SvelteHTMLElements[TTag] ? T_1 extends never ? never : "class" extends infer T_2 ? T_2 extends "class" ? T_2 extends keyof T_1 ? true : never : never : never : never : never) ? {
|
|
16
|
+
class?: string | ((bag: SeparatorRenderPropArg) => string) | null | undefined;
|
|
16
17
|
} : {});
|
|
17
18
|
events(): {} & {
|
|
18
19
|
[evt: string]: CustomEvent<any>;
|
|
@@ -45,32 +45,4 @@ export type MenuContext = StateDefinition & {
|
|
|
45
45
|
setItemsElement(element: HTMLElement | null): void;
|
|
46
46
|
};
|
|
47
47
|
export declare function useMenuContext(component: string): MenuContext;
|
|
48
|
-
export declare const
|
|
49
|
-
readonly menuState: MenuStates;
|
|
50
|
-
readonly buttonElement: HTMLButtonElement | null;
|
|
51
|
-
readonly itemsElement: HTMLElement | null;
|
|
52
|
-
readonly items: {
|
|
53
|
-
id: string;
|
|
54
|
-
dataRef: MenuItemDataRef;
|
|
55
|
-
}[];
|
|
56
|
-
readonly searchQuery: string;
|
|
57
|
-
readonly activeItemIndex: number | null;
|
|
58
|
-
readonly activationTrigger: ActivationTrigger;
|
|
59
|
-
readonly __demoMode: boolean;
|
|
60
|
-
closeMenu(): StateDefinition;
|
|
61
|
-
openMenu(): StateDefinition;
|
|
62
|
-
goToItem(action: {
|
|
63
|
-
focus: Focus.Specific;
|
|
64
|
-
id: string;
|
|
65
|
-
trigger?: ActivationTrigger;
|
|
66
|
-
} | {
|
|
67
|
-
focus: Exclude<Focus, Focus.Specific>;
|
|
68
|
-
trigger?: ActivationTrigger;
|
|
69
|
-
}): StateDefinition;
|
|
70
|
-
search(value: string): StateDefinition;
|
|
71
|
-
clearSearch(): StateDefinition;
|
|
72
|
-
registerItem(id: string, dataRef: MenuItemDataRef): StateDefinition;
|
|
73
|
-
unregisterItem(id: string): StateDefinition;
|
|
74
|
-
setButtonElement(element: HTMLButtonElement | null): StateDefinition;
|
|
75
|
-
setItemsElement(element: HTMLElement | null): StateDefinition;
|
|
76
|
-
};
|
|
48
|
+
export declare const createMenuContext: (initialState: StateDefinition) => MenuContext;
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { calculateActiveIndex, Focus } from "../utils/calculate-active-index.js";
|
|
2
2
|
import { sortByDomNode } from "../utils/focus-management.js";
|
|
3
|
-
import { getContext } from "svelte";
|
|
3
|
+
import { getContext, setContext } from "svelte";
|
|
4
4
|
export var MenuStates;
|
|
5
5
|
(function (MenuStates) {
|
|
6
6
|
MenuStates[MenuStates["Open"] = 0] = "Open";
|
|
@@ -14,7 +14,7 @@ export var ActivationTrigger;
|
|
|
14
14
|
export function useMenuContext(component) {
|
|
15
15
|
const context = getContext("MenuContext");
|
|
16
16
|
if (!context) {
|
|
17
|
-
|
|
17
|
+
const err = new Error(`<${component} /> is missing a parent <Menu /> component.`);
|
|
18
18
|
if (Error.captureStackTrace)
|
|
19
19
|
Error.captureStackTrace(err, useMenuContext);
|
|
20
20
|
throw err;
|
|
@@ -22,8 +22,8 @@ export function useMenuContext(component) {
|
|
|
22
22
|
return context;
|
|
23
23
|
}
|
|
24
24
|
function adjustOrderedState(state, adjustment = (i) => i) {
|
|
25
|
-
|
|
26
|
-
|
|
25
|
+
const currentActiveItem = state.activeItemIndex !== null ? state.items[state.activeItemIndex] : null;
|
|
26
|
+
const sortedItems = sortByDomNode(adjustment(state.items.slice()), (item) => item.dataRef.current.domRef.current);
|
|
27
27
|
// If we inserted an item before the current active item then the active item index
|
|
28
28
|
// would be wrong. To fix this, we will re-lookup the correct index.
|
|
29
29
|
let adjustedActiveItemIndex = currentActiveItem ? sortedItems.indexOf(currentActiveItem) : null;
|
|
@@ -36,9 +36,9 @@ function adjustOrderedState(state, adjustment = (i) => i) {
|
|
|
36
36
|
activeItemIndex: adjustedActiveItemIndex,
|
|
37
37
|
};
|
|
38
38
|
}
|
|
39
|
-
export const
|
|
40
|
-
|
|
41
|
-
|
|
39
|
+
export const createMenuContext = (initialState) => {
|
|
40
|
+
const _state = $state(initialState);
|
|
41
|
+
const context = {
|
|
42
42
|
get menuState() {
|
|
43
43
|
return _state.menuState;
|
|
44
44
|
},
|
|
@@ -104,17 +104,17 @@ export const stateReducer = (initialState) => {
|
|
|
104
104
|
// or if the previous DOM node is already the first DOM node, then we don't
|
|
105
105
|
// have to sort all the DOM nodes.
|
|
106
106
|
else if (action.focus === Focus.Previous) {
|
|
107
|
-
|
|
107
|
+
const activeItemIdx = _state.activeItemIndex;
|
|
108
108
|
if (activeItemIdx !== null) {
|
|
109
|
-
|
|
110
|
-
|
|
109
|
+
const currentDom = _state.items[activeItemIdx].dataRef.current.domRef;
|
|
110
|
+
const previousItemIndex = calculateActiveIndex(action, {
|
|
111
111
|
resolveItems: () => _state.items,
|
|
112
112
|
resolveActiveIndex: () => _state.activeItemIndex,
|
|
113
113
|
resolveId: (item) => item.id,
|
|
114
114
|
resolveDisabled: (item) => item.dataRef.current.disabled,
|
|
115
115
|
});
|
|
116
116
|
if (previousItemIndex !== null) {
|
|
117
|
-
|
|
117
|
+
const previousDom = _state.items[previousItemIndex].dataRef.current.domRef;
|
|
118
118
|
if (
|
|
119
119
|
// Next to each other
|
|
120
120
|
currentDom.current?.previousElementSibling === previousDom.current ||
|
|
@@ -132,17 +132,17 @@ export const stateReducer = (initialState) => {
|
|
|
132
132
|
// if the next DOM node is already the last DOM node, then we don't have to
|
|
133
133
|
// sort all the DOM nodes.
|
|
134
134
|
else if (action.focus === Focus.Next) {
|
|
135
|
-
|
|
135
|
+
const activeItemIdx = _state.activeItemIndex;
|
|
136
136
|
if (activeItemIdx !== null) {
|
|
137
|
-
|
|
138
|
-
|
|
137
|
+
const currentDom = _state.items[activeItemIdx].dataRef.current.domRef;
|
|
138
|
+
const nextItemIndex = calculateActiveIndex(action, {
|
|
139
139
|
resolveItems: () => _state.items,
|
|
140
140
|
resolveActiveIndex: () => _state.activeItemIndex,
|
|
141
141
|
resolveId: (item) => item.id,
|
|
142
142
|
resolveDisabled: (item) => item.dataRef.current.disabled,
|
|
143
143
|
});
|
|
144
144
|
if (nextItemIndex !== null) {
|
|
145
|
-
|
|
145
|
+
const nextDom = _state.items[nextItemIndex].dataRef.current.domRef;
|
|
146
146
|
if (
|
|
147
147
|
// Next to each other
|
|
148
148
|
currentDom.current?.nextElementSibling === nextDom.current ||
|
|
@@ -157,8 +157,8 @@ export const stateReducer = (initialState) => {
|
|
|
157
157
|
// Slow path:
|
|
158
158
|
//
|
|
159
159
|
// Ensure all the items are correctly sorted according to DOM position
|
|
160
|
-
|
|
161
|
-
|
|
160
|
+
const adjustedState = adjustOrderedState(_state);
|
|
161
|
+
const activeItemIndex = calculateActiveIndex(action, {
|
|
162
162
|
resolveItems: () => adjustedState.items,
|
|
163
163
|
resolveActiveIndex: () => adjustedState.activeItemIndex,
|
|
164
164
|
resolveId: (item) => item.id,
|
|
@@ -169,16 +169,16 @@ export const stateReducer = (initialState) => {
|
|
|
169
169
|
return _state;
|
|
170
170
|
},
|
|
171
171
|
search(value) {
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
172
|
+
const wasAlreadySearching = _state.searchQuery !== "";
|
|
173
|
+
const offset = wasAlreadySearching ? 0 : 1;
|
|
174
|
+
const searchQuery = _state.searchQuery + value.toLowerCase();
|
|
175
|
+
const reOrderedItems = _state.activeItemIndex !== null
|
|
176
176
|
? _state.items
|
|
177
177
|
.slice(_state.activeItemIndex + offset)
|
|
178
178
|
.concat(_state.items.slice(0, _state.activeItemIndex + offset))
|
|
179
179
|
: _state.items;
|
|
180
|
-
|
|
181
|
-
|
|
180
|
+
const matchingItem = reOrderedItems.find((item) => item.dataRef.current.textValue?.startsWith(searchQuery) && !item.dataRef.current.disabled);
|
|
181
|
+
const matchIdx = matchingItem ? _state.items.indexOf(matchingItem) : -1;
|
|
182
182
|
if (matchIdx === -1 || matchIdx === _state.activeItemIndex) {
|
|
183
183
|
_state.searchQuery = searchQuery;
|
|
184
184
|
return _state;
|
|
@@ -195,15 +195,15 @@ export const stateReducer = (initialState) => {
|
|
|
195
195
|
return _state;
|
|
196
196
|
},
|
|
197
197
|
registerItem(id, dataRef) {
|
|
198
|
-
|
|
199
|
-
|
|
198
|
+
const item = { id, dataRef };
|
|
199
|
+
const adjustedState = adjustOrderedState(_state, (items) => [...items, item]);
|
|
200
200
|
_state.items = adjustedState.items;
|
|
201
201
|
_state.activeItemIndex = adjustedState.activeItemIndex;
|
|
202
202
|
return _state;
|
|
203
203
|
},
|
|
204
204
|
unregisterItem(id) {
|
|
205
|
-
|
|
206
|
-
|
|
205
|
+
const adjustedState = adjustOrderedState(_state, (items) => {
|
|
206
|
+
const idx = items.findIndex((a) => a.id === id);
|
|
207
207
|
if (idx !== -1)
|
|
208
208
|
items.splice(idx, 1);
|
|
209
209
|
return items;
|
|
@@ -226,4 +226,6 @@ export const stateReducer = (initialState) => {
|
|
|
226
226
|
return _state;
|
|
227
227
|
},
|
|
228
228
|
};
|
|
229
|
+
setContext("MenuContext", context);
|
|
230
|
+
return context;
|
|
229
231
|
};
|
package/dist/menu/index.d.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
export { default as Menu, type MenuProps
|
|
2
|
-
export { default as MenuButton, type MenuButtonProps
|
|
3
|
-
export { default as MenuHeading, type MenuHeadingProps
|
|
4
|
-
export { default as MenuItem, type MenuItemProps
|
|
5
|
-
export { default as MenuItems, type MenuItemsProps
|
|
6
|
-
export { default as MenuSection, type MenuSectionProps
|
|
7
|
-
export { default as MenuSeparator, type MenuSeparatorProps
|
|
1
|
+
export { default as Menu, type MenuProps } from "./Menu.svelte";
|
|
2
|
+
export { default as MenuButton, type MenuButtonProps } from "./MenuButton.svelte";
|
|
3
|
+
export { default as MenuHeading, type MenuHeadingProps } from "./MenuHeading.svelte";
|
|
4
|
+
export { default as MenuItem, type MenuItemProps } from "./MenuItem.svelte";
|
|
5
|
+
export { default as MenuItems, type MenuItemsProps } from "./MenuItems.svelte";
|
|
6
|
+
export { default as MenuSection, type MenuSectionProps } from "./MenuSection.svelte";
|
|
7
|
+
export { default as MenuSeparator, type MenuSeparatorProps } from "./MenuSeparator.svelte";
|
|
@@ -1,159 +1,225 @@
|
|
|
1
|
-
<script lang="ts" module>
|
|
2
|
-
|
|
1
|
+
<script lang="ts" module>
|
|
2
|
+
import type { ElementType, Props } from "../utils/types.js"
|
|
3
3
|
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
PopoverStates,
|
|
9
|
-
usePopoverGroupContext
|
|
10
|
-
} from "./context.svelte.js";
|
|
11
|
-
import { FocusableMode, getFocusableElements, isFocusableElement } from "../utils/focus-management.js";
|
|
12
|
-
import { useNestedPortals } from "../portal/InternalPortal.svelte";
|
|
13
|
-
import MainTreeProvider, { useMainTreeNode } from "../internal/MainTreeProvider.svelte";
|
|
14
|
-
import { useRootContainers } from "../hooks/use-root-containers.svelte.js";
|
|
15
|
-
import { useEventListener } from "../hooks/use-event-listener.svelte.js";
|
|
16
|
-
import { useOutsideClick } from "../hooks/use-outside-click.svelte.js";
|
|
17
|
-
import { useFloatingProvider } from "../internal/floating-provider.svelte.js";
|
|
18
|
-
import { createCloseContext } from "../internal/close-provider.js";
|
|
19
|
-
import { createOpenClosedContext, State } from "../internal/open-closed.js";
|
|
20
|
-
import ElementOrComponent from "../utils/ElementOrComponent.svelte";
|
|
21
|
-
let { ref = $bindable(), __demoMode = false, ...theirProps } = $props();
|
|
22
|
-
let buttons = $state([]);
|
|
23
|
-
const context = createPopoverContext({
|
|
24
|
-
__demoMode,
|
|
25
|
-
popoverState: __demoMode ? PopoverStates.Open : PopoverStates.Closed,
|
|
26
|
-
buttons
|
|
27
|
-
});
|
|
28
|
-
const {
|
|
29
|
-
popoverState,
|
|
30
|
-
button,
|
|
31
|
-
buttonId,
|
|
32
|
-
panel,
|
|
33
|
-
panelId,
|
|
34
|
-
beforePanelSentinel,
|
|
35
|
-
afterPanelSentinel,
|
|
36
|
-
afterButtonSentinel
|
|
37
|
-
} = $derived(context);
|
|
38
|
-
const ownerDocument = $derived(getOwnerDocument(ref ?? button));
|
|
39
|
-
const isPortalled = $derived.by(() => {
|
|
40
|
-
if (!button) return false;
|
|
41
|
-
if (!panel) return false;
|
|
42
|
-
return untrack(() => {
|
|
43
|
-
for (let root2 of document.querySelectorAll("body > *")) {
|
|
44
|
-
if (Number(root2?.contains(button)) ^ Number(root2?.contains(panel))) {
|
|
45
|
-
return true;
|
|
46
|
-
}
|
|
47
|
-
}
|
|
48
|
-
let elements = getFocusableElements();
|
|
49
|
-
let buttonIdx = elements.indexOf(button);
|
|
50
|
-
let beforeIdx = (buttonIdx + elements.length - 1) % elements.length;
|
|
51
|
-
let afterIdx = (buttonIdx + 1) % elements.length;
|
|
52
|
-
let beforeElement = elements[beforeIdx];
|
|
53
|
-
let afterElement = elements[afterIdx];
|
|
54
|
-
if (!panel.contains(beforeElement) && !panel.contains(afterElement)) {
|
|
55
|
-
return true;
|
|
56
|
-
}
|
|
57
|
-
return false;
|
|
58
|
-
});
|
|
59
|
-
});
|
|
60
|
-
const registerBag = $derived({
|
|
61
|
-
buttonId,
|
|
62
|
-
panelId,
|
|
63
|
-
close: () => context.closePopover()
|
|
64
|
-
});
|
|
65
|
-
const groupContext = usePopoverGroupContext();
|
|
66
|
-
const registerPopover = $derived(groupContext?.registerPopover);
|
|
67
|
-
const isFocusWithinPopoverGroup = () => {
|
|
68
|
-
return groupContext?.isFocusWithinPopoverGroup() ?? (ownerDocument?.activeElement && (button?.contains(ownerDocument.activeElement) || panel?.contains(ownerDocument.activeElement)));
|
|
69
|
-
};
|
|
70
|
-
$effect(() => registerPopover?.(registerBag));
|
|
71
|
-
const nestedPortals = useNestedPortals();
|
|
72
|
-
const { portals } = $derived(nestedPortals);
|
|
73
|
-
const mainTreeNode = useMainTreeNode({
|
|
74
|
-
get fallbackMainTreeNode() {
|
|
75
|
-
return button;
|
|
76
|
-
}
|
|
77
|
-
});
|
|
78
|
-
const root = useRootContainers({
|
|
79
|
-
get mainTreeNode() {
|
|
80
|
-
return mainTreeNode.node;
|
|
81
|
-
},
|
|
82
|
-
get portals() {
|
|
83
|
-
return portals;
|
|
84
|
-
},
|
|
85
|
-
get defaultContainers() {
|
|
86
|
-
return [button, panel];
|
|
4
|
+
export const DEFAULT_POPOVER_TAG = "div" as const
|
|
5
|
+
type PopoverRenderPropArg = {
|
|
6
|
+
open: boolean
|
|
7
|
+
close(focusableElement?: HTMLElement | MouseEvent<HTMLElement>): void
|
|
87
8
|
}
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
if (!(event.target instanceof HTMLElement)) return;
|
|
97
|
-
if (popoverState !== PopoverStates.Open) return;
|
|
98
|
-
if (isFocusWithinPopoverGroup()) return;
|
|
99
|
-
if (!button) return;
|
|
100
|
-
if (!panel) return;
|
|
101
|
-
if (root.contains(event.target)) return;
|
|
102
|
-
if (beforePanelSentinel?.contains?.(event.target)) return;
|
|
103
|
-
if (afterPanelSentinel?.contains?.(event.target)) return;
|
|
104
|
-
if (afterButtonSentinel?.contains?.(event.target)) return;
|
|
105
|
-
context.closePopover();
|
|
106
|
-
},
|
|
107
|
-
options: true
|
|
108
|
-
});
|
|
109
|
-
const outsideClickEnabled = $derived(popoverState === PopoverStates.Open);
|
|
110
|
-
useOutsideClick({
|
|
111
|
-
get enabled() {
|
|
112
|
-
return outsideClickEnabled;
|
|
113
|
-
},
|
|
114
|
-
get containers() {
|
|
115
|
-
return root.resolvedContainers;
|
|
116
|
-
},
|
|
117
|
-
cb: (event, target) => {
|
|
118
|
-
context.closePopover();
|
|
119
|
-
if (!isFocusableElement(target, FocusableMode.Loose)) {
|
|
120
|
-
event.preventDefault();
|
|
121
|
-
button?.focus();
|
|
9
|
+
type PopoverPropsWeControl = never
|
|
10
|
+
|
|
11
|
+
export type PopoverProps<TTag extends ElementType = typeof DEFAULT_POPOVER_TAG> = Props<
|
|
12
|
+
TTag,
|
|
13
|
+
PopoverRenderPropArg,
|
|
14
|
+
PopoverPropsWeControl,
|
|
15
|
+
{
|
|
16
|
+
__demoMode?: boolean
|
|
122
17
|
}
|
|
18
|
+
>
|
|
19
|
+
</script>
|
|
20
|
+
|
|
21
|
+
<script lang="ts" generics="TTag extends ElementType = typeof DEFAULT_POPOVER_TAG">
|
|
22
|
+
import { getOwnerDocument } from "../utils/owner.js"
|
|
23
|
+
|
|
24
|
+
import { setContext, untrack } from "svelte"
|
|
25
|
+
import {
|
|
26
|
+
createPopoverContext,
|
|
27
|
+
PopoverStates,
|
|
28
|
+
usePopoverGroupContext,
|
|
29
|
+
type MouseEvent,
|
|
30
|
+
type PopoverAPIContext,
|
|
31
|
+
type PopoverPanelContext,
|
|
32
|
+
} from "./context.svelte.js"
|
|
33
|
+
import { FocusableMode, getFocusableElements, isFocusableElement } from "../utils/focus-management.js"
|
|
34
|
+
import { useNestedPortals } from "../portal/InternalPortal.svelte"
|
|
35
|
+
import MainTreeProvider, { useMainTreeNode } from "../internal/MainTreeProvider.svelte"
|
|
36
|
+
import { useRootContainers } from "../hooks/use-root-containers.svelte.js"
|
|
37
|
+
import { useEventListener } from "../hooks/use-event-listener.svelte.js"
|
|
38
|
+
import { useOutsideClick } from "../hooks/use-outside-click.svelte.js"
|
|
39
|
+
import { useFloatingProvider } from "../internal/floating-provider.svelte.js"
|
|
40
|
+
import { createCloseContext } from "../internal/close-provider.js"
|
|
41
|
+
import { createOpenClosedContext, State } from "../internal/open-closed.js"
|
|
42
|
+
import ElementOrComponent from "../utils/ElementOrComponent.svelte"
|
|
43
|
+
|
|
44
|
+
let { ref = $bindable(), __demoMode = false, ...theirProps }: { as?: TTag } & PopoverProps<TTag> = $props()
|
|
45
|
+
|
|
46
|
+
let buttons = $state([])
|
|
47
|
+
const context = createPopoverContext({
|
|
48
|
+
__demoMode,
|
|
49
|
+
popoverState: __demoMode ? PopoverStates.Open : PopoverStates.Closed,
|
|
50
|
+
buttons,
|
|
51
|
+
})
|
|
52
|
+
const {
|
|
53
|
+
popoverState,
|
|
54
|
+
button,
|
|
55
|
+
buttonId,
|
|
56
|
+
panel,
|
|
57
|
+
panelId,
|
|
58
|
+
beforePanelSentinel,
|
|
59
|
+
afterPanelSentinel,
|
|
60
|
+
afterButtonSentinel,
|
|
61
|
+
} = $derived(context)
|
|
62
|
+
|
|
63
|
+
const ownerDocument = $derived(getOwnerDocument(ref ?? button))
|
|
64
|
+
|
|
65
|
+
const isPortalled = $derived.by(() => {
|
|
66
|
+
if (!button) return false
|
|
67
|
+
if (!panel) return false
|
|
68
|
+
|
|
69
|
+
return untrack(() => {
|
|
70
|
+
// We are part of a different "root" tree, so therefore we can consider it portalled. This is a
|
|
71
|
+
// heuristic because 3rd party tools could use some form of portal, typically rendered at the
|
|
72
|
+
// end of the body but we don't have an actual reference to that.
|
|
73
|
+
for (let root of document.querySelectorAll("body > *")) {
|
|
74
|
+
if (Number(root?.contains(button)) ^ Number(root?.contains(panel))) {
|
|
75
|
+
return true
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
// Use another heuristic to try and calculate whether or not the focusable
|
|
80
|
+
// elements are near each other (aka, following the default focus/tab order
|
|
81
|
+
// from the browser). If they are then it doesn't really matter if they are
|
|
82
|
+
// portalled or not because we can follow the default tab order. But if they
|
|
83
|
+
// are not, then we can consider it being portalled so that we can ensure
|
|
84
|
+
// that tab and shift+tab (hopefully) go to the correct spot.
|
|
85
|
+
let elements = getFocusableElements()
|
|
86
|
+
let buttonIdx = elements.indexOf(button)
|
|
87
|
+
|
|
88
|
+
let beforeIdx = (buttonIdx + elements.length - 1) % elements.length
|
|
89
|
+
let afterIdx = (buttonIdx + 1) % elements.length
|
|
90
|
+
|
|
91
|
+
let beforeElement = elements[beforeIdx]
|
|
92
|
+
let afterElement = elements[afterIdx]
|
|
93
|
+
|
|
94
|
+
if (!panel.contains(beforeElement) && !panel.contains(afterElement)) {
|
|
95
|
+
return true
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
// It may or may not be portalled, but we don't really know.
|
|
99
|
+
return false
|
|
100
|
+
})
|
|
101
|
+
})
|
|
102
|
+
|
|
103
|
+
const registerBag = $derived({
|
|
104
|
+
buttonId,
|
|
105
|
+
panelId,
|
|
106
|
+
close: () => context.closePopover(),
|
|
107
|
+
})
|
|
108
|
+
|
|
109
|
+
const groupContext = usePopoverGroupContext()
|
|
110
|
+
const registerPopover = $derived(groupContext?.registerPopover)
|
|
111
|
+
const isFocusWithinPopoverGroup = () => {
|
|
112
|
+
return (
|
|
113
|
+
groupContext?.isFocusWithinPopoverGroup() ??
|
|
114
|
+
(ownerDocument?.activeElement &&
|
|
115
|
+
(button?.contains(ownerDocument.activeElement) || panel?.contains(ownerDocument.activeElement)))
|
|
116
|
+
)
|
|
123
117
|
}
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
const
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
}
|
|
134
|
-
const
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
})
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
118
|
+
|
|
119
|
+
$effect(() => registerPopover?.(registerBag))
|
|
120
|
+
|
|
121
|
+
const nestedPortals = useNestedPortals()
|
|
122
|
+
const { portals } = $derived(nestedPortals)
|
|
123
|
+
const mainTreeNode = useMainTreeNode({
|
|
124
|
+
get fallbackMainTreeNode() {
|
|
125
|
+
return button
|
|
126
|
+
},
|
|
127
|
+
})
|
|
128
|
+
const root = useRootContainers({
|
|
129
|
+
get mainTreeNode() {
|
|
130
|
+
return mainTreeNode.node
|
|
131
|
+
},
|
|
132
|
+
get portals() {
|
|
133
|
+
return portals
|
|
134
|
+
},
|
|
135
|
+
get defaultContainers() {
|
|
136
|
+
return [button, panel]
|
|
137
|
+
},
|
|
138
|
+
})
|
|
139
|
+
|
|
140
|
+
// Handle focus out
|
|
141
|
+
useEventListener({
|
|
142
|
+
get element() {
|
|
143
|
+
return ownerDocument?.defaultView
|
|
144
|
+
},
|
|
145
|
+
type: "focus",
|
|
146
|
+
listener: (event) => {
|
|
147
|
+
if (event.target === window) return
|
|
148
|
+
if (!(event.target instanceof HTMLElement)) return
|
|
149
|
+
if (popoverState !== PopoverStates.Open) return
|
|
150
|
+
if (isFocusWithinPopoverGroup()) return
|
|
151
|
+
if (!button) return
|
|
152
|
+
if (!panel) return
|
|
153
|
+
if (root.contains(event.target)) return
|
|
154
|
+
if (beforePanelSentinel?.contains?.(event.target)) return
|
|
155
|
+
if (afterPanelSentinel?.contains?.(event.target)) return
|
|
156
|
+
if (afterButtonSentinel?.contains?.(event.target)) return
|
|
157
|
+
|
|
158
|
+
context.closePopover()
|
|
159
|
+
},
|
|
160
|
+
options: true,
|
|
161
|
+
})
|
|
162
|
+
|
|
163
|
+
// Handle outside click
|
|
164
|
+
const outsideClickEnabled = $derived(popoverState === PopoverStates.Open)
|
|
165
|
+
useOutsideClick({
|
|
166
|
+
get enabled() {
|
|
167
|
+
return outsideClickEnabled
|
|
168
|
+
},
|
|
169
|
+
get containers() {
|
|
170
|
+
return root.resolvedContainers
|
|
171
|
+
},
|
|
172
|
+
cb: (event, target) => {
|
|
173
|
+
context.closePopover()
|
|
174
|
+
|
|
175
|
+
if (!isFocusableElement(target, FocusableMode.Loose)) {
|
|
176
|
+
event.preventDefault()
|
|
177
|
+
button?.focus()
|
|
178
|
+
}
|
|
179
|
+
},
|
|
180
|
+
})
|
|
181
|
+
|
|
182
|
+
const close = (focusableElement?: HTMLElement | MouseEvent<HTMLElement>) => {
|
|
183
|
+
context.closePopover()
|
|
184
|
+
|
|
185
|
+
const restoreElement = (() => {
|
|
186
|
+
if (!focusableElement) return button
|
|
187
|
+
if (focusableElement instanceof HTMLElement) return focusableElement
|
|
188
|
+
|
|
189
|
+
return button
|
|
190
|
+
})()
|
|
191
|
+
|
|
192
|
+
restoreElement?.focus()
|
|
150
193
|
}
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
194
|
+
|
|
195
|
+
const api: PopoverAPIContext = {
|
|
196
|
+
close,
|
|
197
|
+
get isPortalled() {
|
|
198
|
+
return isPortalled
|
|
199
|
+
},
|
|
155
200
|
}
|
|
156
|
-
|
|
201
|
+
setContext("PopoverAPIContext", api)
|
|
202
|
+
|
|
203
|
+
const slot = $derived({
|
|
204
|
+
open: popoverState === PopoverStates.Open,
|
|
205
|
+
close,
|
|
206
|
+
} satisfies PopoverRenderPropArg)
|
|
207
|
+
|
|
208
|
+
useFloatingProvider()
|
|
209
|
+
|
|
210
|
+
setContext<PopoverPanelContext | undefined>("PopoverPanelContext", undefined)
|
|
211
|
+
|
|
212
|
+
createCloseContext({
|
|
213
|
+
get close() {
|
|
214
|
+
return close
|
|
215
|
+
},
|
|
216
|
+
})
|
|
217
|
+
|
|
218
|
+
createOpenClosedContext({
|
|
219
|
+
get value() {
|
|
220
|
+
return context.popoverState === PopoverStates.Open ? State.Open : State.Closed
|
|
221
|
+
},
|
|
222
|
+
})
|
|
157
223
|
</script>
|
|
158
224
|
|
|
159
225
|
<MainTreeProvider node={mainTreeNode.node}>
|
|
@@ -12,11 +12,14 @@ import { type MouseEvent } from "./context.svelte.js";
|
|
|
12
12
|
declare class __sveltets_Render<TTag extends ElementType = typeof DEFAULT_POPOVER_TAG> {
|
|
13
13
|
props(): {
|
|
14
14
|
as?: TTag | undefined;
|
|
15
|
-
} & (Exclude<keyof import("../utils/types.js").PropsOf<TTag>, ("as" | "children" | "
|
|
16
|
-
children?: import("svelte").Snippet<[
|
|
15
|
+
} & (Exclude<keyof import("../utils/types.js").PropsOf<TTag>, ("as" | "children" | "class") | "__demoMode"> extends infer T extends keyof import("../utils/types.js").PropsOf<TTag> ? { [P in T]: import("../utils/types.js").PropsOf<TTag>[P]; } : never) & {
|
|
16
|
+
children?: import("svelte").Snippet<[{
|
|
17
|
+
slot: PopoverRenderPropArg;
|
|
18
|
+
props: Record<string, any>;
|
|
19
|
+
}]> | undefined;
|
|
17
20
|
ref?: HTMLElement;
|
|
18
|
-
} & (true extends (import("
|
|
19
|
-
class?:
|
|
21
|
+
} & (true extends (import("svelte/elements.js").SvelteHTMLElements[TTag] extends infer T_1 ? T_1 extends import("svelte/elements.js").SvelteHTMLElements[TTag] ? T_1 extends never ? never : "class" extends infer T_2 ? T_2 extends "class" ? T_2 extends keyof T_1 ? true : never : never : never : never : never) ? {
|
|
22
|
+
class?: string | ((bag: PopoverRenderPropArg) => string) | null | undefined;
|
|
20
23
|
} : {}) & {
|
|
21
24
|
__demoMode?: boolean;
|
|
22
25
|
};
|