@pzerelles/headlessui-svelte 2.0.0-next.1 → 2.1.1-next.1
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 +65 -0
- package/dist/button/Button.svelte.d.ts +39 -0
- package/dist/button/index.d.ts +1 -0
- package/dist/button/index.js +1 -0
- package/dist/checkbox/Checkbox.svelte +60 -46
- package/dist/checkbox/Checkbox.svelte.d.ts +1 -1
- package/dist/close-button/CloseButton.svelte +10 -0
- package/dist/close-button/CloseButton.svelte.d.ts +25 -0
- package/dist/close-button/index.d.ts +1 -0
- package/dist/close-button/index.js +1 -0
- package/dist/combobox/Combobox.svelte +6 -0
- package/dist/combobox/Combobox.svelte.d.ts +50 -0
- package/dist/description/Description.svelte +50 -32
- package/dist/description/Description.svelte.d.ts +14 -5
- package/dist/field/Field.svelte +9 -9
- package/dist/fieldset/Fieldset.svelte +9 -9
- package/dist/hooks/document-overflow/adjust-scrollbar-padding.d.ts +2 -0
- package/dist/hooks/document-overflow/adjust-scrollbar-padding.js +18 -0
- package/dist/hooks/document-overflow/handle-ios-locking.d.ts +6 -0
- package/dist/hooks/document-overflow/handle-ios-locking.js +134 -0
- package/dist/hooks/document-overflow/overflow-store.d.ts +19 -0
- package/dist/hooks/document-overflow/overflow-store.js +76 -0
- package/dist/hooks/document-overflow/prevent-scroll.d.ts +2 -0
- package/dist/hooks/document-overflow/prevent-scroll.js +7 -0
- package/dist/hooks/document-overflow/use-document-overflow.svelte.d.ts +7 -0
- package/dist/hooks/document-overflow/use-document-overflow.svelte.js +27 -0
- package/dist/hooks/use-active-press.svelte.d.ts +14 -0
- package/dist/{actions/activePress.svelte.js → hooks/use-active-press.svelte.js} +33 -39
- package/dist/hooks/use-by-comparator.d.ts +2 -0
- package/dist/hooks/use-by-comparator.js +15 -0
- package/dist/hooks/use-controllable.svelte.d.ts +6 -0
- package/dist/hooks/use-controllable.svelte.js +34 -0
- package/dist/hooks/use-did-element-move.svelte.d.ts +6 -0
- package/dist/hooks/use-did-element-move.svelte.js +27 -0
- package/dist/hooks/use-disabled.d.ts +3 -0
- package/dist/hooks/use-disabled.js +9 -0
- package/dist/hooks/use-element-size.svelte.d.ts +7 -0
- package/dist/hooks/use-element-size.svelte.js +36 -0
- package/dist/hooks/use-flags.svelte.d.ts +8 -0
- package/dist/hooks/use-flags.svelte.js +18 -0
- package/dist/hooks/use-focus-ring.svelte.d.ts +10 -0
- package/dist/hooks/use-focus-ring.svelte.js +24 -0
- package/dist/hooks/use-hover.svelte.d.ts +26 -0
- package/dist/hooks/use-hover.svelte.js +124 -0
- package/dist/hooks/use-id.d.ts +1 -0
- package/dist/hooks/use-id.js +1 -0
- package/dist/hooks/use-inert-others.svelte.d.ts +32 -0
- package/dist/hooks/use-inert-others.svelte.js +114 -0
- package/dist/hooks/use-is-top-layer.svelte.d.ts +29 -0
- package/dist/hooks/use-is-top-layer.svelte.js +82 -0
- package/dist/hooks/use-on-disappear.svelte.d.ts +12 -0
- package/dist/hooks/use-on-disappear.svelte.js +38 -0
- package/dist/hooks/use-outside-click.svelte.d.ts +10 -0
- package/dist/hooks/use-outside-click.svelte.js +150 -0
- package/dist/hooks/use-reducer.d.ts +4 -0
- package/dist/hooks/use-reducer.js +11 -0
- package/dist/hooks/use-resolve-button-type.svelte.d.ts +10 -0
- package/dist/hooks/use-resolve-button-type.svelte.js +19 -0
- package/dist/hooks/use-scroll-lock.svelte.d.ts +5 -0
- package/dist/hooks/use-scroll-lock.svelte.js +24 -0
- package/dist/hooks/use-sync-refs.d.ts +7 -0
- package/dist/hooks/use-sync-refs.js +22 -0
- package/dist/hooks/use-text-value.svelte.d.ts +3 -0
- package/dist/hooks/use-text-value.svelte.js +20 -0
- package/dist/hooks/use-tracked-pointer.d.ts +4 -0
- package/dist/hooks/use-tracked-pointer.js +26 -0
- package/dist/hooks/use-transition.svelte.d.ts +20 -0
- package/dist/hooks/use-transition.svelte.js +252 -0
- package/dist/index.d.ts +4 -0
- package/dist/index.js +4 -0
- package/dist/internal/FocusSentinel.svelte +45 -0
- package/dist/internal/FocusSentinel.svelte.d.ts +17 -0
- package/dist/internal/FormFields.svelte +2 -4
- package/dist/internal/FormFields.svelte.d.ts +5 -6
- package/dist/internal/FormResolver.svelte +11 -16
- package/dist/internal/FormResolver.svelte.d.ts +2 -3
- package/dist/internal/Hidden.svelte +8 -8
- package/dist/internal/Hidden.svelte.d.ts +28 -19
- package/dist/internal/HoistFormFields.svelte.d.ts +1 -1
- package/dist/internal/Portal.svelte.d.ts +1 -1
- package/dist/internal/floating.svelte.d.ts +57 -0
- package/dist/internal/floating.svelte.js +477 -0
- package/dist/internal/frozen.svelte.d.ts +6 -0
- package/dist/internal/frozen.svelte.js +18 -0
- package/dist/internal/id.d.ts +8 -0
- package/dist/internal/id.js +11 -0
- package/dist/internal/open-closed.d.ts +14 -0
- package/dist/internal/open-closed.js +17 -0
- package/dist/internal/portal-force-root.svelte.d.ts +6 -0
- package/dist/internal/portal-force-root.svelte.js +11 -0
- package/dist/label/Label.svelte +53 -32
- package/dist/label/Label.svelte.d.ts +14 -5
- package/dist/legend/Legend.svelte.d.ts +1 -2
- package/dist/listbox/Listbox.svelte +451 -0
- package/dist/listbox/Listbox.svelte.d.ts +107 -0
- package/dist/listbox/ListboxButton.svelte +141 -0
- package/dist/listbox/ListboxButton.svelte.d.ts +41 -0
- package/dist/listbox/ListboxOption.svelte +138 -0
- package/dist/listbox/ListboxOption.svelte.d.ts +39 -0
- package/dist/listbox/ListboxOptions.svelte +267 -0
- package/dist/listbox/ListboxOptions.svelte.d.ts +39 -0
- package/dist/listbox/ListboxSelectedOption.svelte +25 -0
- package/dist/listbox/ListboxSelectedOption.svelte.d.ts +30 -0
- package/dist/listbox/index.d.ts +5 -0
- package/dist/listbox/index.js +5 -0
- package/dist/portal/InternalPortal.svelte +108 -0
- package/dist/portal/InternalPortal.svelte.d.ts +34 -0
- package/dist/portal/Portal.svelte +11 -0
- package/dist/portal/Portal.svelte.d.ts +23 -0
- package/dist/portal/PortalGroup.svelte +15 -0
- package/dist/portal/PortalGroup.svelte.d.ts +31 -0
- package/dist/switch/Switch.svelte +149 -0
- package/dist/switch/Switch.svelte.d.ts +44 -0
- package/dist/switch/SwitchGroup.svelte +38 -0
- package/dist/switch/SwitchGroup.svelte.d.ts +27 -0
- package/dist/switch/index.d.ts +2 -0
- package/dist/switch/index.js +2 -0
- package/dist/tabs/Button.svelte +65 -0
- package/dist/tabs/Button.svelte.d.ts +39 -0
- package/dist/tabs/Tab.svelte +161 -0
- package/dist/tabs/Tab.svelte.d.ts +36 -0
- package/dist/tabs/TabGroup.svelte +244 -0
- package/dist/tabs/TabGroup.svelte.d.ts +54 -0
- package/dist/tabs/TabList.svelte +18 -0
- package/dist/tabs/TabList.svelte.d.ts +28 -0
- package/dist/tabs/TabPanel.svelte +63 -0
- package/dist/tabs/TabPanel.svelte.d.ts +34 -0
- package/dist/tabs/TabPanels.svelte +13 -0
- package/dist/tabs/TabPanels.svelte.d.ts +27 -0
- package/dist/tabs/index.d.ts +5 -0
- package/dist/tabs/index.js +5 -0
- package/dist/test-utils/accessability-assertions.d.ts +271 -0
- package/dist/test-utils/accessability-assertions.js +1572 -0
- package/dist/test-utils/fake-pointer.d.ts +24 -0
- package/dist/test-utils/fake-pointer.js +48 -0
- package/dist/test-utils/interactions.d.ts +61 -0
- package/dist/test-utils/interactions.js +453 -0
- package/dist/test-utils/suppress-console-logs.d.ts +7 -0
- package/dist/test-utils/suppress-console-logs.js +17 -0
- package/dist/utils/StableCollection.svelte +43 -0
- package/dist/utils/StableCollection.svelte.d.ts +19 -0
- package/dist/utils/calculate-active-index.d.ts +25 -0
- package/dist/utils/calculate-active-index.js +74 -0
- package/dist/utils/close.d.ts +2 -0
- package/dist/utils/close.js +3 -0
- package/dist/utils/default-map.d.ts +5 -0
- package/dist/utils/default-map.js +15 -0
- package/dist/utils/disposables.d.ts +14 -12
- package/dist/utils/disposables.js +13 -10
- package/dist/utils/dom.d.ts +0 -2
- package/dist/utils/dom.js +2 -4
- package/dist/utils/env.d.ts +17 -0
- package/dist/utils/env.js +39 -0
- package/dist/utils/focus-management.d.ts +44 -0
- package/dist/utils/focus-management.js +242 -0
- package/dist/utils/focusVisible.svelte.d.ts +3 -3
- package/dist/utils/focusVisible.svelte.js +52 -41
- package/dist/utils/get-text-value.d.ts +1 -0
- package/dist/utils/get-text-value.js +71 -0
- package/dist/utils/id.d.ts +1 -1
- package/dist/utils/match.d.ts +1 -0
- package/dist/utils/match.js +13 -0
- package/dist/utils/once.d.ts +1 -0
- package/dist/utils/once.js +9 -0
- package/dist/utils/owner.d.ts +1 -0
- package/dist/utils/owner.js +8 -0
- package/dist/utils/platform.d.ts +2 -0
- package/dist/utils/platform.js +17 -0
- package/dist/utils/ref.svelte.d.ts +4 -0
- package/dist/utils/ref.svelte.js +4 -0
- package/dist/utils/render.d.ts +31 -0
- package/dist/utils/render.js +56 -0
- package/dist/utils/store.d.ts +11 -0
- package/dist/utils/store.js +20 -0
- package/dist/utils/types.d.ts +27 -0
- package/dist/utils/types.js +6 -0
- package/package.json +28 -21
- package/dist/actions/activePress.svelte.d.ts +0 -8
- package/dist/actions/focusRing.svelte.d.ts +0 -9
- package/dist/actions/focusRing.svelte.js +0 -34
- package/dist/utils/disabled.d.ts +0 -3
- package/dist/utils/disabled.js +0 -2
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
import { useInteractions, type UseFloatingReturn } from "@skeletonlabs/floating-ui-svelte";
|
|
2
|
+
type Align = "start" | "end";
|
|
3
|
+
type Placement = "top" | "right" | "bottom" | "left";
|
|
4
|
+
type BaseAnchorProps = {
|
|
5
|
+
/**
|
|
6
|
+
* The `gap` is the space between the trigger and the panel.
|
|
7
|
+
*/
|
|
8
|
+
gap: number | string;
|
|
9
|
+
/**
|
|
10
|
+
* The `offset` is the amount the panel should be nudged from its original position.
|
|
11
|
+
*/
|
|
12
|
+
offset: number | string;
|
|
13
|
+
/**
|
|
14
|
+
* The `padding` is the minimum space between the panel and the viewport.
|
|
15
|
+
*/
|
|
16
|
+
padding: number | string;
|
|
17
|
+
};
|
|
18
|
+
export type AnchorProps = false | (`${Placement}` | `${Placement} ${Align}`) | Partial<BaseAnchorProps & {
|
|
19
|
+
/**
|
|
20
|
+
* The `to` value defines which side of the trigger the panel should be placed on and its
|
|
21
|
+
* alignment.
|
|
22
|
+
*/
|
|
23
|
+
to: `${Placement}` | `${Placement} ${Align}`;
|
|
24
|
+
}>;
|
|
25
|
+
export type AnchorPropsWithSelection = false | (`${Placement | "selection"}` | `${Placement | "selection"} ${Align}`) | Partial<BaseAnchorProps & {
|
|
26
|
+
/**
|
|
27
|
+
* The `to` value defines which side of the trigger the panel should be placed on and its
|
|
28
|
+
* alignment.
|
|
29
|
+
*/
|
|
30
|
+
to: `${Placement | "selection"}` | `${Placement | "selection"} ${Align}`;
|
|
31
|
+
}>;
|
|
32
|
+
export type InternalFloatingPanelProps = Partial<{
|
|
33
|
+
inner: {};
|
|
34
|
+
}>;
|
|
35
|
+
type FloatingContext = {
|
|
36
|
+
styles?: UseFloatingReturn["floatingStyles"];
|
|
37
|
+
setReference: (reference: HTMLElement | null | undefined) => void;
|
|
38
|
+
setFloating: (floating: HTMLElement | null | undefined) => void;
|
|
39
|
+
getReferenceProps: ReturnType<typeof useInteractions>["getReferenceProps"];
|
|
40
|
+
getFloatingProps: ReturnType<typeof useInteractions>["getFloatingProps"];
|
|
41
|
+
slot: Partial<{
|
|
42
|
+
anchor: `${Placement | "selection"}` | `${Placement | "selection"} ${Align}`;
|
|
43
|
+
}>;
|
|
44
|
+
};
|
|
45
|
+
export declare function useResolvedAnchor<T extends AnchorProps | AnchorPropsWithSelection>(anchor?: T): Exclude<T, boolean | string> | null;
|
|
46
|
+
export declare function useFloating(): FloatingContext;
|
|
47
|
+
export declare function useFloatingPanelProps(): (userProps?: import("svelte/elements").HTMLAttributes<Element> | undefined) => Record<string, unknown> & {
|
|
48
|
+
"data-anchor": Placement | "top start" | "top end" | "right start" | "right end" | "bottom start" | "bottom end" | "left start" | "left end" | "selection" | "selection start" | "selection end" | undefined;
|
|
49
|
+
};
|
|
50
|
+
export declare function useFloatingPanel(placement?: (AnchorPropsWithSelection & InternalFloatingPanelProps) | null): {
|
|
51
|
+
readonly setFloating: (floating: HTMLElement | null | undefined) => void;
|
|
52
|
+
readonly style: string | undefined;
|
|
53
|
+
};
|
|
54
|
+
export declare const createFloatingContext: ({ enabled }?: {
|
|
55
|
+
enabled?: boolean;
|
|
56
|
+
}) => FloatingContext;
|
|
57
|
+
export {};
|
|
@@ -0,0 +1,477 @@
|
|
|
1
|
+
import { useDisposables } from "../utils/disposables.js";
|
|
2
|
+
import { useRef } from "../utils/ref.svelte.js";
|
|
3
|
+
import { autoUpdate, flip as flipMiddleware,
|
|
4
|
+
//inner as innerMiddleware,
|
|
5
|
+
offset as offsetMiddleware, shift as shiftMiddleware, size as sizeMiddleware, useFloating as _useFloating,
|
|
6
|
+
//useInnerOffset,
|
|
7
|
+
useInteractions, } from "@skeletonlabs/floating-ui-svelte";
|
|
8
|
+
import { getContext, setContext } from "svelte";
|
|
9
|
+
export function useResolvedAnchor(anchor) {
|
|
10
|
+
if (!anchor)
|
|
11
|
+
return null; // Disable entirely
|
|
12
|
+
if (typeof anchor === "string")
|
|
13
|
+
return { to: anchor }; // Simple string based value,
|
|
14
|
+
return anchor; // User-provided value
|
|
15
|
+
}
|
|
16
|
+
export function useFloating() {
|
|
17
|
+
return getContext("FloatingContext");
|
|
18
|
+
}
|
|
19
|
+
export function useFloatingPanelProps() {
|
|
20
|
+
let { getFloatingProps, slot } = getContext("FloatingContext");
|
|
21
|
+
return (...args) => {
|
|
22
|
+
return Object.assign({}, getFloatingProps(...args), {
|
|
23
|
+
"data-anchor": slot.anchor,
|
|
24
|
+
});
|
|
25
|
+
};
|
|
26
|
+
}
|
|
27
|
+
export function useFloatingPanel(placement = null) {
|
|
28
|
+
if (placement === false)
|
|
29
|
+
placement = null; // Disable entirely
|
|
30
|
+
if (typeof placement === "string")
|
|
31
|
+
placement = { to: placement }; // Simple string based value
|
|
32
|
+
const updatePlacementConfig = getContext("PlacementContext");
|
|
33
|
+
/*let stablePlacement = useMemo(
|
|
34
|
+
() => placement,
|
|
35
|
+
[
|
|
36
|
+
JSON.stringify(
|
|
37
|
+
placement,
|
|
38
|
+
typeof HTMLElement !== 'undefined'
|
|
39
|
+
? (_, v) => {
|
|
40
|
+
if (v instanceof HTMLElement) {
|
|
41
|
+
return v.outerHTML
|
|
42
|
+
}
|
|
43
|
+
return v
|
|
44
|
+
}
|
|
45
|
+
: undefined
|
|
46
|
+
),
|
|
47
|
+
]
|
|
48
|
+
)*/
|
|
49
|
+
updatePlacementConfig?.(placement ?? null);
|
|
50
|
+
const context = getContext("FloatingContext");
|
|
51
|
+
return {
|
|
52
|
+
get setFloating() {
|
|
53
|
+
return context.setFloating;
|
|
54
|
+
},
|
|
55
|
+
get style() {
|
|
56
|
+
return placement ? context.styles : undefined;
|
|
57
|
+
},
|
|
58
|
+
};
|
|
59
|
+
}
|
|
60
|
+
// TODO: Make this a config part of the `config`. Just need to decide on a name.
|
|
61
|
+
let MINIMUM_ITEMS_VISIBLE = 4;
|
|
62
|
+
export const createFloatingContext = ({ enabled = true } = {}) => {
|
|
63
|
+
let config = $state(null);
|
|
64
|
+
let innerOffset = $state(0);
|
|
65
|
+
let overflowRef = useRef(null);
|
|
66
|
+
let floatingEl = $state(null);
|
|
67
|
+
$effect(() => useFixScrollingPixel(floatingEl));
|
|
68
|
+
const isEnabled = $derived(enabled && config !== null && floatingEl !== null);
|
|
69
|
+
let { to: placement = "bottom", gap = 0, offset = 0, padding = 0, inner, } = useResolvedConfig({
|
|
70
|
+
get config() {
|
|
71
|
+
return config;
|
|
72
|
+
},
|
|
73
|
+
get element() {
|
|
74
|
+
return floatingEl;
|
|
75
|
+
},
|
|
76
|
+
});
|
|
77
|
+
let [to, align = "center"] = placement.split(" ");
|
|
78
|
+
// Reset
|
|
79
|
+
$effect(() => {
|
|
80
|
+
if (!isEnabled)
|
|
81
|
+
return;
|
|
82
|
+
innerOffset = 0;
|
|
83
|
+
});
|
|
84
|
+
const { elements, floatingStyles, context } = $derived(_useFloating({
|
|
85
|
+
open: isEnabled,
|
|
86
|
+
placement: to === "selection"
|
|
87
|
+
? align === "center"
|
|
88
|
+
? "bottom"
|
|
89
|
+
: `bottom-${align}`
|
|
90
|
+
: align === "center"
|
|
91
|
+
? `${to}`
|
|
92
|
+
: `${to}-${align}`,
|
|
93
|
+
// This component will be used in combination with a `Portal`, which means the floating
|
|
94
|
+
// element will be rendered outside of the current DOM tree.
|
|
95
|
+
strategy: "absolute",
|
|
96
|
+
// We use the panel in a `Dialog` which is making the page inert, therefore no re-positioning is
|
|
97
|
+
// needed when scrolling changes.
|
|
98
|
+
transform: false,
|
|
99
|
+
middleware: [
|
|
100
|
+
// - The `mainAxis` is set to `gap` which defines the gap between the panel and the
|
|
101
|
+
// trigger/reference.
|
|
102
|
+
// - The `crossAxis` is set to `offset` which nudges the panel from its original position.
|
|
103
|
+
//
|
|
104
|
+
// When we are showing the panel on top of the selected item, we don't want a gap between the
|
|
105
|
+
// reference and the panel, therefore setting the `mainAxis` to `0`.
|
|
106
|
+
offsetMiddleware({
|
|
107
|
+
mainAxis: to === "selection" ? 0 : gap,
|
|
108
|
+
crossAxis: offset,
|
|
109
|
+
}),
|
|
110
|
+
// When the panel overflows the viewport, we will try to nudge the panel to the other side to
|
|
111
|
+
// ensure it's not clipped. We use the `padding` to define the minimum space between the
|
|
112
|
+
// panel and the viewport.
|
|
113
|
+
shiftMiddleware({ padding }),
|
|
114
|
+
// The `flip` middleware will swap the `placement` of the panel if there is not enough room.
|
|
115
|
+
// This is not compatible with the `inner` middleware (which is only enabled when `to` is set
|
|
116
|
+
// to "selection").
|
|
117
|
+
to !== "selection" && flipMiddleware({ padding }),
|
|
118
|
+
// The `inner` middleware will ensure the panel is always fully visible on screen and
|
|
119
|
+
// positioned on top of the reference and moved to the currently selected item.
|
|
120
|
+
to === "selection" && inner
|
|
121
|
+
? null /* TODO: use inner when available: innerMiddleware({
|
|
122
|
+
...inner,
|
|
123
|
+
padding, // For overflow detection
|
|
124
|
+
overflowRef,
|
|
125
|
+
offset: innerOffset,
|
|
126
|
+
minItemsVisible: MINIMUM_ITEMS_VISIBLE,
|
|
127
|
+
referenceOverflowThreshold: padding,
|
|
128
|
+
onFallbackChange(fallback) {
|
|
129
|
+
if (!fallback) return
|
|
130
|
+
let parent = context.elements.floating
|
|
131
|
+
if (!parent) return
|
|
132
|
+
let scrollPaddingBottom =
|
|
133
|
+
parseFloat(getComputedStyle(parent!).scrollPaddingBottom) || 0
|
|
134
|
+
|
|
135
|
+
// We want at least X visible items, but if there are less than X items in the list,
|
|
136
|
+
// we want to show as many as possible.
|
|
137
|
+
let missing = Math.min(MINIMUM_ITEMS_VISIBLE, parent.childElementCount)
|
|
138
|
+
|
|
139
|
+
let elementHeight = 0
|
|
140
|
+
let elementAmountVisible = 0
|
|
141
|
+
|
|
142
|
+
for (let child of context.elements.floating?.childNodes ?? []) {
|
|
143
|
+
if (child instanceof HTMLElement) {
|
|
144
|
+
let childTop = child.offsetTop
|
|
145
|
+
// It can be that the child is fully visible, but we also want to keep the scroll
|
|
146
|
+
// padding into account to ensure the UI looks good. Therefore we fake that the
|
|
147
|
+
// bottom of the child is actually `scrollPaddingBottom` amount of pixels lower.
|
|
148
|
+
let childBottom = childTop + child.clientHeight + scrollPaddingBottom
|
|
149
|
+
|
|
150
|
+
let parentTop = parent.scrollTop
|
|
151
|
+
let parentBottom = parentTop + parent.clientHeight
|
|
152
|
+
|
|
153
|
+
// Figure out if the child is fully visible in the scroll parent.
|
|
154
|
+
if (childTop >= parentTop && childBottom <= parentBottom) {
|
|
155
|
+
missing--
|
|
156
|
+
} else {
|
|
157
|
+
// Not fully visible, so we will use this child to calculate the height of
|
|
158
|
+
// each item. We will also use this to calculate how much of the item is
|
|
159
|
+
// already visible.
|
|
160
|
+
elementAmountVisible = Math.max(
|
|
161
|
+
0,
|
|
162
|
+
Math.min(childBottom, parentBottom) - Math.max(childTop, parentTop)
|
|
163
|
+
)
|
|
164
|
+
elementHeight = child.clientHeight
|
|
165
|
+
break
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
// There are fewer visible items than we want, so we will try to nudge the offset
|
|
171
|
+
// to show more items.
|
|
172
|
+
if (missing >= 1) {
|
|
173
|
+
setInnerOffset((existingOffset) => {
|
|
174
|
+
let newInnerOffset =
|
|
175
|
+
elementHeight * missing - // `missing` amount of `elementHeight`
|
|
176
|
+
elementAmountVisible + // The amount of the last item that is visible
|
|
177
|
+
scrollPaddingBottom // The scroll padding to ensure the UI looks good
|
|
178
|
+
|
|
179
|
+
// Nudged enough already, no need to continue
|
|
180
|
+
if (existingOffset >= newInnerOffset) {
|
|
181
|
+
return existingOffset
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
return newInnerOffset
|
|
185
|
+
})
|
|
186
|
+
}
|
|
187
|
+
},
|
|
188
|
+
})*/
|
|
189
|
+
: null,
|
|
190
|
+
// The `size` middleware will ensure the panel is never bigger than the viewport minus the
|
|
191
|
+
// provided `padding` that we want.
|
|
192
|
+
sizeMiddleware({
|
|
193
|
+
padding,
|
|
194
|
+
apply({ availableWidth, availableHeight, elements }) {
|
|
195
|
+
Object.assign(elements.floating.style, {
|
|
196
|
+
overflow: "auto",
|
|
197
|
+
maxWidth: `${availableWidth}px`,
|
|
198
|
+
maxHeight: `min(var(--anchor-max-height, 100vh), ${availableHeight}px)`,
|
|
199
|
+
});
|
|
200
|
+
},
|
|
201
|
+
}),
|
|
202
|
+
].filter(Boolean),
|
|
203
|
+
whileElementsMounted: autoUpdate,
|
|
204
|
+
}));
|
|
205
|
+
// Calculate placement information to expose as data attributes
|
|
206
|
+
let [exposedTo = to, exposedAlign = align] = context.placement.split("-");
|
|
207
|
+
// If user-land code is using custom styles specifically for `bottom`, but
|
|
208
|
+
// they chose `selection`, then we want to make sure to map it to selection
|
|
209
|
+
// again otherwise styles could be wrong.
|
|
210
|
+
if (to === "selection")
|
|
211
|
+
exposedTo = "selection";
|
|
212
|
+
const data = $derived({
|
|
213
|
+
anchor: [exposedTo, exposedAlign].filter(Boolean).join(" "),
|
|
214
|
+
});
|
|
215
|
+
/*let innerOffsetConfig = useInnerOffset(context, {
|
|
216
|
+
overflowRef,
|
|
217
|
+
onChange: setInnerOffset,
|
|
218
|
+
})*/
|
|
219
|
+
let { getReferenceProps, getFloatingProps } = useInteractions([
|
|
220
|
+
/*innerOffsetConfig*/
|
|
221
|
+
]);
|
|
222
|
+
let setFloatingRef = (el) => {
|
|
223
|
+
floatingEl = el || null;
|
|
224
|
+
elements.floating = el;
|
|
225
|
+
};
|
|
226
|
+
/*
|
|
227
|
+
|
|
228
|
+
return (
|
|
229
|
+
<PlacementContext.Provider value={setConfig}>
|
|
230
|
+
<FloatingContext.Provider
|
|
231
|
+
value={{
|
|
232
|
+
setFloating: setFloatingRef,
|
|
233
|
+
setReference: refs.setReference,
|
|
234
|
+
styles: floatingStyles,
|
|
235
|
+
getReferenceProps,
|
|
236
|
+
getFloatingProps,
|
|
237
|
+
slot: data,
|
|
238
|
+
}}
|
|
239
|
+
>
|
|
240
|
+
{children}
|
|
241
|
+
</FloatingContext.Provider>
|
|
242
|
+
</PlacementContext.Provider>
|
|
243
|
+
)*/
|
|
244
|
+
const floatingContext = {
|
|
245
|
+
setFloating: setFloatingRef,
|
|
246
|
+
setReference: (reference) => {
|
|
247
|
+
elements.reference = reference;
|
|
248
|
+
},
|
|
249
|
+
get styles() {
|
|
250
|
+
return floatingStyles;
|
|
251
|
+
},
|
|
252
|
+
getReferenceProps,
|
|
253
|
+
getFloatingProps,
|
|
254
|
+
get slot() {
|
|
255
|
+
return data;
|
|
256
|
+
},
|
|
257
|
+
};
|
|
258
|
+
setContext("FloatingContext", floatingContext);
|
|
259
|
+
setContext("PlacementContext", null);
|
|
260
|
+
return floatingContext;
|
|
261
|
+
};
|
|
262
|
+
function useFixScrollingPixel(element) {
|
|
263
|
+
if (!element)
|
|
264
|
+
return;
|
|
265
|
+
let observer = new MutationObserver(() => {
|
|
266
|
+
let maxHeight = window.getComputedStyle(element).maxHeight;
|
|
267
|
+
let maxHeightFloat = parseFloat(maxHeight);
|
|
268
|
+
if (isNaN(maxHeightFloat))
|
|
269
|
+
return;
|
|
270
|
+
let maxHeightInt = parseInt(maxHeight);
|
|
271
|
+
if (isNaN(maxHeightInt))
|
|
272
|
+
return;
|
|
273
|
+
if (maxHeightFloat !== maxHeightInt) {
|
|
274
|
+
element.style.maxHeight = `${Math.ceil(maxHeightFloat)}px`;
|
|
275
|
+
}
|
|
276
|
+
});
|
|
277
|
+
observer.observe(element, {
|
|
278
|
+
attributes: true,
|
|
279
|
+
attributeFilter: ["style"],
|
|
280
|
+
});
|
|
281
|
+
return () => {
|
|
282
|
+
observer.disconnect();
|
|
283
|
+
};
|
|
284
|
+
}
|
|
285
|
+
function useResolvedConfig({ config, element, }) {
|
|
286
|
+
const gap = useResolvePxValue({
|
|
287
|
+
get input() {
|
|
288
|
+
return config?.gap ?? "var(--anchor-gap, 0)";
|
|
289
|
+
},
|
|
290
|
+
get element() {
|
|
291
|
+
return element;
|
|
292
|
+
},
|
|
293
|
+
});
|
|
294
|
+
const offset = useResolvePxValue({
|
|
295
|
+
get input() {
|
|
296
|
+
return config?.gap ?? "var(--anchor-offset, 0)";
|
|
297
|
+
},
|
|
298
|
+
get element() {
|
|
299
|
+
return element;
|
|
300
|
+
},
|
|
301
|
+
});
|
|
302
|
+
const padding = useResolvePxValue({
|
|
303
|
+
get input() {
|
|
304
|
+
return config?.gap ?? "var(--anchor-padding, 0)";
|
|
305
|
+
},
|
|
306
|
+
get element() {
|
|
307
|
+
return element;
|
|
308
|
+
},
|
|
309
|
+
});
|
|
310
|
+
return {
|
|
311
|
+
...config,
|
|
312
|
+
get gap() {
|
|
313
|
+
return gap.value;
|
|
314
|
+
},
|
|
315
|
+
get offset() {
|
|
316
|
+
return offset.value;
|
|
317
|
+
},
|
|
318
|
+
get padding() {
|
|
319
|
+
return padding.value;
|
|
320
|
+
},
|
|
321
|
+
};
|
|
322
|
+
}
|
|
323
|
+
function useResolvePxValue({ input, element, defaultValue, }) {
|
|
324
|
+
let d = useDisposables();
|
|
325
|
+
const computeValue = (value, element) => {
|
|
326
|
+
// Nullish
|
|
327
|
+
if (value == null)
|
|
328
|
+
return [defaultValue, null];
|
|
329
|
+
// Number as-is
|
|
330
|
+
if (typeof value === "number")
|
|
331
|
+
return [value, null];
|
|
332
|
+
// String values, the interesting part
|
|
333
|
+
if (typeof value === "string") {
|
|
334
|
+
if (!element)
|
|
335
|
+
return [defaultValue, null];
|
|
336
|
+
let result = resolveCSSVariablePxValue(value, element);
|
|
337
|
+
return [
|
|
338
|
+
result,
|
|
339
|
+
(setValue) => {
|
|
340
|
+
let variables = resolveVariables(value);
|
|
341
|
+
// TODO: Improve this part and make it work
|
|
342
|
+
//
|
|
343
|
+
// Observe variables themselves. Currently the browser doesn't support this, but the
|
|
344
|
+
// variables we are interested in resolve to a pixel value. Which means that we can use
|
|
345
|
+
// this variable in the `margin` of an element. Then we can observe the `margin` of the
|
|
346
|
+
// element and we will be notified when the variable changes.
|
|
347
|
+
//
|
|
348
|
+
// if (typeof ResizeObserver !== 'undefined') {
|
|
349
|
+
// let tmpEl = document.createElement('div')
|
|
350
|
+
// element.appendChild(tmpEl)
|
|
351
|
+
//
|
|
352
|
+
// // Didn't use `fontSize` because a `fontSize` can't be negative.
|
|
353
|
+
// tmpEl.style.setProperty('margin-top', '0px', 'important')
|
|
354
|
+
//
|
|
355
|
+
// // Set the new value, if this is invalid the previous value will be used.
|
|
356
|
+
// tmpEl.style.setProperty('margin-top', value, 'important')
|
|
357
|
+
//
|
|
358
|
+
// let observer = new ResizeObserver(() => {
|
|
359
|
+
// let newResult = resolveCSSVariableValue(value, element)
|
|
360
|
+
//
|
|
361
|
+
// if (result !== newResult) {
|
|
362
|
+
// setValue(newResult)
|
|
363
|
+
// result = newResult
|
|
364
|
+
// }
|
|
365
|
+
// })
|
|
366
|
+
// observer.observe(tmpEl)
|
|
367
|
+
// d.add(() => observer.disconnect())
|
|
368
|
+
// return d.dispose
|
|
369
|
+
// }
|
|
370
|
+
// Works as a fallback, but not very performant because we are polling the value.
|
|
371
|
+
{
|
|
372
|
+
let history = variables.map((variable) => window.getComputedStyle(element).getPropertyValue(variable));
|
|
373
|
+
d.requestAnimationFrame(function check() {
|
|
374
|
+
d.nextFrame(check);
|
|
375
|
+
// Fast path, detect if the value of the CSS Variable has changed before completely
|
|
376
|
+
// computing the new value. Once we use `resolveCSSVariablePxValue` we will have to
|
|
377
|
+
// compute the actual px value by injecting a temporary element into the DOM.
|
|
378
|
+
//
|
|
379
|
+
// This is a lot of work, so we want to avoid it if possible.
|
|
380
|
+
let changed = false;
|
|
381
|
+
for (let [idx, variable] of variables.entries()) {
|
|
382
|
+
let value = window.getComputedStyle(element).getPropertyValue(variable);
|
|
383
|
+
if (history[idx] !== value) {
|
|
384
|
+
history[idx] = value;
|
|
385
|
+
changed = true;
|
|
386
|
+
break;
|
|
387
|
+
}
|
|
388
|
+
}
|
|
389
|
+
// Nothing changed, no need to perform the expensive computation.
|
|
390
|
+
if (!changed)
|
|
391
|
+
return;
|
|
392
|
+
let newResult = resolveCSSVariablePxValue(value, element);
|
|
393
|
+
if (result !== newResult) {
|
|
394
|
+
setValue(newResult);
|
|
395
|
+
result = newResult;
|
|
396
|
+
}
|
|
397
|
+
});
|
|
398
|
+
}
|
|
399
|
+
return d.dispose;
|
|
400
|
+
},
|
|
401
|
+
];
|
|
402
|
+
}
|
|
403
|
+
return [defaultValue, null];
|
|
404
|
+
};
|
|
405
|
+
// Calculate the value immediately when the input or element changes. Later we can setup a watcher
|
|
406
|
+
// to track the value changes over time.
|
|
407
|
+
const immediateValue = computeValue(input, element)[0];
|
|
408
|
+
let value = $state(immediateValue);
|
|
409
|
+
$effect(() => {
|
|
410
|
+
let [newValue, watcher] = computeValue(input, element);
|
|
411
|
+
value = newValue;
|
|
412
|
+
if (!watcher)
|
|
413
|
+
return;
|
|
414
|
+
return watcher((_value) => (value = _value));
|
|
415
|
+
});
|
|
416
|
+
return {
|
|
417
|
+
get value() {
|
|
418
|
+
return value;
|
|
419
|
+
},
|
|
420
|
+
};
|
|
421
|
+
}
|
|
422
|
+
function resolveVariables(value) {
|
|
423
|
+
let matches = /var\((.*)\)/.exec(value);
|
|
424
|
+
if (matches) {
|
|
425
|
+
let idx = matches[1].indexOf(",");
|
|
426
|
+
if (idx === -1) {
|
|
427
|
+
return [matches[1]];
|
|
428
|
+
}
|
|
429
|
+
let variable = matches[1].slice(0, idx).trim();
|
|
430
|
+
let fallback = matches[1].slice(idx + 1).trim();
|
|
431
|
+
if (fallback) {
|
|
432
|
+
return [variable, ...resolveVariables(fallback)];
|
|
433
|
+
}
|
|
434
|
+
return [variable];
|
|
435
|
+
}
|
|
436
|
+
return [];
|
|
437
|
+
}
|
|
438
|
+
function resolveCSSVariablePxValue(input, element) {
|
|
439
|
+
// Resolve the value: Instead of trying to compute the value ourselves by converting rem /
|
|
440
|
+
// vwh / ... values to pixels or by parsing out the fallback values and evaluating it
|
|
441
|
+
// (because it can contain calc expressions or other variables).
|
|
442
|
+
//
|
|
443
|
+
// We will let the browser compute all of it by creating a temporary element and setting
|
|
444
|
+
// the value as a CSS variable. Then we can read the computed value from the browser.
|
|
445
|
+
//
|
|
446
|
+
//
|
|
447
|
+
// BUG REPORT ABOUT INCORRECT VALUES, look here:
|
|
448
|
+
// ---------------------------------------------
|
|
449
|
+
//
|
|
450
|
+
// Currently this technically contains a bug because we are rendering a new element inside of the
|
|
451
|
+
// current element. Which means that if the passed in element has CSS that looks like:
|
|
452
|
+
//
|
|
453
|
+
// ```css
|
|
454
|
+
// .the-element {
|
|
455
|
+
// --the-variable: 1rem
|
|
456
|
+
// }
|
|
457
|
+
//
|
|
458
|
+
// .the-element > * {
|
|
459
|
+
// --the-variable: 2rem
|
|
460
|
+
// }
|
|
461
|
+
// ```
|
|
462
|
+
//
|
|
463
|
+
// Then this will result to resolved value of `2rem`, instead of `1rem`
|
|
464
|
+
let tmpEl = document.createElement("div");
|
|
465
|
+
element.appendChild(tmpEl);
|
|
466
|
+
// Set the value to `0px` otherwise if an invalid value is provided later the browser will read
|
|
467
|
+
// out the default value.
|
|
468
|
+
//
|
|
469
|
+
// Didn't use `fontSize` because a `fontSize` can't be negative.
|
|
470
|
+
tmpEl.style.setProperty("margin-top", "0px", "important");
|
|
471
|
+
// Set the new value, if this is invalid the previous value will be used.
|
|
472
|
+
tmpEl.style.setProperty("margin-top", input, "important");
|
|
473
|
+
// Reading the `margin-top` will already be in pixels (e.g.: 123px).
|
|
474
|
+
let pxValue = parseFloat(window.getComputedStyle(tmpEl).marginTop) || 0;
|
|
475
|
+
element.removeChild(tmpEl);
|
|
476
|
+
return pxValue;
|
|
477
|
+
}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { untrack } from "svelte";
|
|
2
|
+
export function useFrozenData(options) {
|
|
3
|
+
const { freeze, data } = $derived(options);
|
|
4
|
+
let frozenValue = $state(data);
|
|
5
|
+
$effect(() => {
|
|
6
|
+
// We should keep updating the frozen value, as long as we shouldn't freeze
|
|
7
|
+
// the value yet. The moment we should freeze the value we stop updating it
|
|
8
|
+
// which allows us to reference the "previous" (thus frozen) value.
|
|
9
|
+
if (!freeze && untrack(() => frozenValue) !== data) {
|
|
10
|
+
frozenValue = data;
|
|
11
|
+
}
|
|
12
|
+
});
|
|
13
|
+
return {
|
|
14
|
+
get data() {
|
|
15
|
+
return freeze ? frozenValue : data;
|
|
16
|
+
},
|
|
17
|
+
};
|
|
18
|
+
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
export declare enum State {
|
|
2
|
+
Open = 1,
|
|
3
|
+
Closed = 2,
|
|
4
|
+
Closing = 4,
|
|
5
|
+
Opening = 8
|
|
6
|
+
}
|
|
7
|
+
export type OpenClosedContext = {
|
|
8
|
+
readonly value: State;
|
|
9
|
+
};
|
|
10
|
+
export declare function useOpenClosed(): OpenClosedContext;
|
|
11
|
+
export declare function createOpenClosedContext(options: {
|
|
12
|
+
readonly value: State;
|
|
13
|
+
}): void;
|
|
14
|
+
export declare function clearOpenClosedContext(): void;
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { getContext, setContext } from "svelte";
|
|
2
|
+
export var State;
|
|
3
|
+
(function (State) {
|
|
4
|
+
State[State["Open"] = 1] = "Open";
|
|
5
|
+
State[State["Closed"] = 2] = "Closed";
|
|
6
|
+
State[State["Closing"] = 4] = "Closing";
|
|
7
|
+
State[State["Opening"] = 8] = "Opening";
|
|
8
|
+
})(State || (State = {}));
|
|
9
|
+
export function useOpenClosed() {
|
|
10
|
+
return getContext("OpenClosedContext");
|
|
11
|
+
}
|
|
12
|
+
export function createOpenClosedContext(options) {
|
|
13
|
+
setContext("OpenClosedContext", options);
|
|
14
|
+
}
|
|
15
|
+
export function clearOpenClosedContext() {
|
|
16
|
+
setContext("OpenClosedContext", undefined);
|
|
17
|
+
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { getContext, setContext } from "svelte";
|
|
2
|
+
export function usePortalRoot() {
|
|
3
|
+
return getContext("ForcePortalRootContext");
|
|
4
|
+
}
|
|
5
|
+
export function createPortalRoot(context) {
|
|
6
|
+
setContext("ForcePortalRootContext", {
|
|
7
|
+
get force() {
|
|
8
|
+
return context.force;
|
|
9
|
+
},
|
|
10
|
+
});
|
|
11
|
+
}
|