@pzerelles/headlessui-svelte 2.1.2-next.10 → 2.1.2-next.12

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (69) hide show
  1. package/dist/hooks/use-controllable.svelte.js +2 -1
  2. package/dist/hooks/use-did-element-move.svelte.js +5 -10
  3. package/dist/internal/FloatingProvider.svelte +12 -0
  4. package/dist/internal/FloatingProvider.svelte.d.ts +22 -0
  5. package/dist/internal/floating-provider.svelte.d.ts +3 -0
  6. package/dist/internal/floating-provider.svelte.js +206 -0
  7. package/dist/internal/floating.svelte.d.ts +48 -23
  8. package/dist/internal/floating.svelte.js +78 -260
  9. package/dist/internal/inner.svelte.d.ts +91 -0
  10. package/dist/internal/inner.svelte.js +202 -0
  11. package/dist/listbox/Listbox.svelte +38 -52
  12. package/dist/listbox/Listbox.svelte.d.ts +3 -54
  13. package/dist/listbox/ListboxButton.svelte +7 -6
  14. package/dist/listbox/ListboxOption.svelte +7 -3
  15. package/dist/listbox/ListboxOptions.svelte +50 -50
  16. package/dist/listbox/context.svelte.d.ts +75 -0
  17. package/dist/listbox/context.svelte.js +36 -0
  18. package/dist/menu/Menu.svelte +2 -2
  19. package/dist/menu/MenuButton.svelte +4 -2
  20. package/dist/menu/MenuItems.svelte +15 -11
  21. package/dist/tabs/TabGroup.svelte.d.ts +1 -1
  22. package/dist/utils/ElementOrComponent.svelte +1 -1
  23. package/dist/utils/floating-ui/svelte/components/FloatingNode.svelte +17 -0
  24. package/dist/utils/floating-ui/svelte/components/FloatingNode.svelte.d.ts +23 -0
  25. package/dist/utils/floating-ui/svelte/components/FloatingTree.svelte +50 -0
  26. package/dist/utils/floating-ui/svelte/components/FloatingTree.svelte.d.ts +41 -0
  27. package/dist/utils/floating-ui/svelte/hooks/useFloating.svelte.d.ts +6 -0
  28. package/dist/utils/floating-ui/svelte/hooks/useFloating.svelte.js +158 -0
  29. package/dist/utils/floating-ui/svelte/hooks/useFloatingRootContext.svelte.d.ts +11 -0
  30. package/dist/utils/floating-ui/svelte/hooks/useFloatingRootContext.svelte.js +53 -0
  31. package/dist/utils/floating-ui/svelte/hooks/useId.svelte.d.ts +9 -0
  32. package/dist/utils/floating-ui/svelte/hooks/useId.svelte.js +28 -0
  33. package/dist/utils/floating-ui/svelte/hooks/useInteractions.svelte.d.ts +23 -0
  34. package/dist/utils/floating-ui/svelte/hooks/useInteractions.svelte.js +72 -0
  35. package/dist/utils/floating-ui/svelte/index.d.ts +5 -0
  36. package/dist/utils/floating-ui/svelte/index.js +5 -0
  37. package/dist/utils/floating-ui/svelte/inner.svelte.d.ts +83 -0
  38. package/dist/utils/floating-ui/svelte/inner.svelte.js +178 -0
  39. package/dist/utils/floating-ui/svelte/types.d.ts +114 -0
  40. package/dist/utils/floating-ui/svelte/types.js +1 -0
  41. package/dist/utils/floating-ui/svelte/utils/createPubSub.d.ts +5 -0
  42. package/dist/utils/floating-ui/svelte/utils/createPubSub.js +14 -0
  43. package/dist/utils/floating-ui/svelte/utils/getFloatingFocusElement.d.ts +2 -0
  44. package/dist/utils/floating-ui/svelte/utils/getFloatingFocusElement.js +13 -0
  45. package/dist/utils/floating-ui/svelte/utils/log.d.ts +2 -0
  46. package/dist/utils/floating-ui/svelte/utils/log.js +19 -0
  47. package/dist/utils/floating-ui/svelte/utils.d.ts +19 -0
  48. package/dist/utils/floating-ui/svelte/utils.js +136 -0
  49. package/dist/utils/floating-ui/svelte-dom/arrow.d.ts +22 -0
  50. package/dist/utils/floating-ui/svelte-dom/arrow.js +29 -0
  51. package/dist/utils/floating-ui/svelte-dom/index.d.ts +2 -0
  52. package/dist/utils/floating-ui/svelte-dom/index.js +2 -0
  53. package/dist/utils/floating-ui/svelte-dom/types.d.ts +80 -0
  54. package/dist/utils/floating-ui/svelte-dom/types.js +3 -0
  55. package/dist/utils/floating-ui/svelte-dom/useFloating.svelte.d.ts +6 -0
  56. package/dist/utils/floating-ui/svelte-dom/useFloating.svelte.js +182 -0
  57. package/dist/utils/floating-ui/svelte-dom/utils/deepEqual.d.ts +1 -0
  58. package/dist/utils/floating-ui/svelte-dom/utils/deepEqual.js +50 -0
  59. package/dist/utils/floating-ui/svelte-dom/utils/getDPR.d.ts +1 -0
  60. package/dist/utils/floating-ui/svelte-dom/utils/getDPR.js +7 -0
  61. package/dist/utils/floating-ui/svelte-dom/utils/roundByDPR.d.ts +1 -0
  62. package/dist/utils/floating-ui/svelte-dom/utils/roundByDPR.js +5 -0
  63. package/dist/utils/floating-ui/svelte-dom/utils/useLatestRef.d.ts +4 -0
  64. package/dist/utils/floating-ui/svelte-dom/utils/useLatestRef.js +7 -0
  65. package/dist/utils/style.d.ts +2 -0
  66. package/dist/utils/style.js +6 -0
  67. package/package.json +3 -2
  68. package/dist/listbox/ListboxStates.d.ts +0 -12
  69. package/dist/listbox/ListboxStates.js +0 -15
@@ -27,7 +27,8 @@ export function useControllable(input, onchange, defaultValue) {
27
27
  }
28
28
  else {
29
29
  internalValue = value;
30
- input.controlledValue = value;
30
+ if (isControlled)
31
+ input.controlledValue = value;
31
32
  }
32
33
  },
33
34
  };
@@ -1,23 +1,18 @@
1
1
  export function useDidElementMove(options) {
2
2
  const { enabled, element } = $derived(options);
3
- let elementPosition = $state({ left: 0, top: 0 });
3
+ let elementPosition = $state();
4
4
  $effect(() => {
5
5
  if (!element)
6
6
  return;
7
- let DOMRect = element.getBoundingClientRect();
7
+ const DOMRect = element.getBoundingClientRect();
8
8
  if (DOMRect)
9
9
  elementPosition = DOMRect;
10
10
  });
11
11
  const value = $derived.by(() => {
12
- if (element == null)
12
+ if (element == null || !enabled || element === document.activeElement || elementPosition === undefined)
13
13
  return false;
14
- if (!enabled)
15
- return false;
16
- if (element === document.activeElement)
17
- return false;
18
- let buttonRect = element.getBoundingClientRect();
19
- let didElementMove = buttonRect.top !== elementPosition.top || buttonRect.left !== elementPosition.left;
20
- return didElementMove;
14
+ const buttonRect = element.getBoundingClientRect();
15
+ return buttonRect.top !== elementPosition.top || buttonRect.left !== elementPosition.left;
21
16
  });
22
17
  return {
23
18
  get value() {
@@ -0,0 +1,12 @@
1
+ <script lang="ts" module></script>
2
+
3
+ <script lang="ts">import { useFloatingProvider } from "./floating-provider.svelte.js";
4
+ const { children, enabled = true } = $props();
5
+ useFloatingProvider({
6
+ get enabled() {
7
+ return enabled;
8
+ }
9
+ });
10
+ </script>
11
+
12
+ {#if children}{@render children()}{/if}
@@ -0,0 +1,22 @@
1
+ import type { Snippet } from "svelte";
2
+ interface $$__sveltets_2_IsomorphicComponent<Props extends Record<string, any> = any, Events extends Record<string, any> = any, Slots extends Record<string, any> = any, Exports = {}, Bindings = string> {
3
+ new (options: import('svelte').ComponentConstructorOptions<Props>): import('svelte').SvelteComponent<Props, Events, Slots> & {
4
+ $$bindings?: Bindings;
5
+ } & Exports;
6
+ (internal: unknown, props: Props & {
7
+ $$events?: Events;
8
+ $$slots?: Slots;
9
+ }): Exports & {
10
+ $set?: any;
11
+ $on?: any;
12
+ };
13
+ z_$$bindings?: Bindings;
14
+ }
15
+ declare const FloatingProvider: $$__sveltets_2_IsomorphicComponent<{
16
+ children: Snippet;
17
+ enabled?: boolean;
18
+ }, {
19
+ [evt: string]: CustomEvent<any>;
20
+ }, {}, {}, "">;
21
+ type FloatingProvider = InstanceType<typeof FloatingProvider>;
22
+ export default FloatingProvider;
@@ -0,0 +1,3 @@
1
+ export declare const useFloatingProvider: (options?: {
2
+ enabled: boolean;
3
+ }) => void;
@@ -0,0 +1,206 @@
1
+ import { setContext } from "svelte";
2
+ import { useFixScrollingPixel, useResolvedConfig, } from "./floating.svelte.js";
3
+ import { autoUpdate, flip as flipMiddleware, inner as innerMiddleware, offset as offsetMiddleware, shift as shiftMiddleware, size as sizeMiddleware, useFloating, useInnerOffset, useInteractions, } from "../utils/floating-ui/svelte/index.js";
4
+ export const useFloatingProvider = (options = { enabled: true }) => {
5
+ const { enabled } = $derived(options);
6
+ // TODO: Make this a config part of the `config`. Just need to decide on a name.
7
+ let MINIMUM_ITEMS_VISIBLE = 4;
8
+ let config = $state(null);
9
+ let innerOffset = $state(0);
10
+ const setInnerOffset = (offset) => (innerOffset = typeof offset === "function" ? offset(innerOffset) : offset);
11
+ let overflowRef = $state({ current: null });
12
+ let floatingEl = $state(null);
13
+ const setFloatingElement = (element) => (floatingEl = element);
14
+ useFixScrollingPixel({
15
+ get element() {
16
+ return floatingEl;
17
+ },
18
+ });
19
+ const isEnabled = $derived(enabled && config !== null && floatingEl !== null);
20
+ const resolvedConfig = useResolvedConfig({
21
+ get config() {
22
+ return config;
23
+ },
24
+ get element() {
25
+ return floatingEl;
26
+ },
27
+ });
28
+ const { to: placement = "bottom", gap = 0, offset = 0, padding = 0, inner } = $derived(resolvedConfig);
29
+ let [to, align = "center"] = $derived(placement.split(" "));
30
+ // Reset
31
+ $effect(() => {
32
+ if (!isEnabled)
33
+ return;
34
+ innerOffset = 0;
35
+ });
36
+ const middleware = $derived([
37
+ // - The `mainAxis` is set to `gap` which defines the gap between the panel and the
38
+ // trigger/reference.
39
+ // - The `crossAxis` is set to `offset` which nudges the panel from its original position.
40
+ //
41
+ // When we are showing the panel on top of the selected item, we don't want a gap between the
42
+ // reference and the panel, therefore setting the `mainAxis` to `0`.
43
+ offsetMiddleware({
44
+ mainAxis: to === "selection" ? 0 : gap,
45
+ crossAxis: offset,
46
+ }),
47
+ // When the panel overflows the viewport, we will try to nudge the panel to the other side to
48
+ // ensure it's not clipped. We use the `padding` to define the minimum space between the
49
+ // panel and the viewport.
50
+ shiftMiddleware({ padding }),
51
+ // The `flip` middleware will swap the `placement` of the panel if there is not enough room.
52
+ // This is not compatible with the `inner` middleware (which is only enabled when `to` is set
53
+ // to "selection").
54
+ to !== "selection" && flipMiddleware({ padding }),
55
+ // The `inner` middleware will ensure the panel is always fully visible on screen and
56
+ // positioned on top of the reference and moved to the currently selected item.
57
+ to === "selection" && inner
58
+ ? innerMiddleware({
59
+ ...inner,
60
+ padding, // For overflow detection
61
+ overflowRef,
62
+ offset: innerOffset,
63
+ minItemsVisible: MINIMUM_ITEMS_VISIBLE,
64
+ referenceOverflowThreshold: padding,
65
+ onFallbackChange(fallback) {
66
+ if (!fallback)
67
+ return;
68
+ let parent = context.elements.floating;
69
+ if (!parent)
70
+ return;
71
+ let scrollPaddingBottom = parseFloat(getComputedStyle(parent).scrollPaddingBottom) || 0;
72
+ // We want at least X visible items, but if there are less than X items in the list,
73
+ // we want to show as many as possible.
74
+ let missing = Math.min(MINIMUM_ITEMS_VISIBLE, parent.childElementCount);
75
+ let elementHeight = 0;
76
+ let elementAmountVisible = 0;
77
+ for (let child of context.elements.floating?.childNodes ?? []) {
78
+ if (child instanceof HTMLElement) {
79
+ let childTop = child.offsetTop;
80
+ // It can be that the child is fully visible, but we also want to keep the scroll
81
+ // padding into account to ensure the UI looks good. Therefore we fake that the
82
+ // bottom of the child is actually `scrollPaddingBottom` amount of pixels lower.
83
+ let childBottom = childTop + child.clientHeight + scrollPaddingBottom;
84
+ let parentTop = parent.scrollTop;
85
+ let parentBottom = parentTop + parent.clientHeight;
86
+ // Figure out if the child is fully visible in the scroll parent.
87
+ if (childTop >= parentTop && childBottom <= parentBottom) {
88
+ missing--;
89
+ }
90
+ else {
91
+ // Not fully visible, so we will use this child to calculate the height of
92
+ // each item. We will also use this to calculate how much of the item is
93
+ // already visible.
94
+ elementAmountVisible = Math.max(0, Math.min(childBottom, parentBottom) - Math.max(childTop, parentTop));
95
+ elementHeight = child.clientHeight;
96
+ break;
97
+ }
98
+ }
99
+ }
100
+ // There are fewer visible items than we want, so we will try to nudge the offset
101
+ // to show more items.
102
+ if (missing >= 1) {
103
+ setInnerOffset((existingOffset) => {
104
+ let newInnerOffset = elementHeight * missing - // `missing` amount of `elementHeight`
105
+ elementAmountVisible + // The amount of the last item that is visible
106
+ scrollPaddingBottom; // The scroll padding to ensure the UI looks good
107
+ // Nudged enough already, no need to continue
108
+ if (existingOffset >= newInnerOffset) {
109
+ return existingOffset;
110
+ }
111
+ return newInnerOffset;
112
+ });
113
+ }
114
+ },
115
+ })
116
+ : null,
117
+ // The `size` middleware will ensure the panel is never bigger than the viewport minus the
118
+ // provided `padding` that we want.
119
+ sizeMiddleware({
120
+ padding,
121
+ apply({ availableWidth, availableHeight, elements }) {
122
+ Object.assign(elements.floating.style, {
123
+ overflow: "auto",
124
+ maxWidth: `${availableWidth}px`,
125
+ maxHeight: `min(var(--anchor-max-height, 100vh), ${availableHeight}px)`,
126
+ });
127
+ },
128
+ }),
129
+ ].filter(Boolean));
130
+ const floating = useFloating({
131
+ get open() {
132
+ return isEnabled;
133
+ },
134
+ get placement() {
135
+ return (to === "selection"
136
+ ? align === "center"
137
+ ? "bottom"
138
+ : `bottom-${align}`
139
+ : align === "center"
140
+ ? `${to}`
141
+ : `${to}-${align}`);
142
+ },
143
+ // This component will be used in combination with a `Portal`, which means the floating
144
+ // element will be rendered outside of the current DOM tree.
145
+ strategy: "absolute",
146
+ // We use the panel in a `Dialog` which is making the page inert, therefore no re-positioning is
147
+ // needed when scrolling changes.
148
+ transform: false,
149
+ get middleware() {
150
+ return middleware;
151
+ },
152
+ whileElementsMounted: autoUpdate,
153
+ });
154
+ const { refs, floatingStyles, context } = $derived(floating);
155
+ // Calculate placement information to expose as data attributes
156
+ const { exposedTo, exposedAlign } = $derived.by(() => {
157
+ let [exposedTo = to, exposedAlign = align] = context.placement.split("-");
158
+ return { exposedTo: to === "selection" ? "selection" : exposedTo, exposedAlign };
159
+ });
160
+ // If user-land code is using custom styles specifically for `bottom`, but
161
+ // they chose `selection`, then we want to make sure to map it to selection
162
+ // again otherwise styles could be wrong.
163
+ //if (to === "selection") exposedTo = "selection"
164
+ const data = $derived({
165
+ anchor: [exposedTo, exposedAlign].filter(Boolean).join(" "),
166
+ });
167
+ let innerOffsetConfig = useInnerOffset({
168
+ get context() {
169
+ return context;
170
+ },
171
+ get props() {
172
+ return {
173
+ overflowRef,
174
+ onChange: setInnerOffset,
175
+ };
176
+ },
177
+ });
178
+ let { getReferenceProps, getFloatingProps } = useInteractions({
179
+ get propsList() {
180
+ return [innerOffsetConfig];
181
+ },
182
+ });
183
+ const setFloatingRef = (el) => {
184
+ setFloatingElement(el);
185
+ refs.setFloating(el);
186
+ };
187
+ setContext("PlacementContext", {
188
+ updatePlacementConfig: (value) => {
189
+ config = value;
190
+ },
191
+ });
192
+ setContext("FloatingContext", {
193
+ setFloating: setFloatingRef,
194
+ get setReference() {
195
+ return refs.setReference;
196
+ },
197
+ get styles() {
198
+ return floatingStyles;
199
+ },
200
+ getReferenceProps,
201
+ getFloatingProps,
202
+ get slot() {
203
+ return data;
204
+ },
205
+ });
206
+ };
@@ -1,7 +1,8 @@
1
- import type { MutableRefObject } from "../utils/ref.svelte.js";
2
- type Align = "start" | "end";
3
- type Placement = "top" | "right" | "bottom" | "left";
4
- type BaseAnchorProps = {
1
+ import { useInteractions, type InnerProps, type UseFloatingReturn } from "../utils/floating-ui/svelte/index.js";
2
+ export { useFloatingProvider } from "./floating-provider.svelte.js";
3
+ export type Align = "start" | "end";
4
+ export type Placement = "top" | "right" | "bottom" | "left";
5
+ export type BaseAnchorProps = {
5
6
  /**
6
7
  * The `gap` is the space between the trigger and the panel.
7
8
  */
@@ -31,32 +32,56 @@ export type AnchorPropsWithSelection = false | (`${Placement | "selection"}` | `
31
32
  }>;
32
33
  export type InternalFloatingPanelProps = Partial<{
33
34
  inner: {
34
- listRef: MutableRefObject<(HTMLElement | null)[]>;
35
- index: () => number | null;
35
+ listRef: InnerProps["listRef"];
36
+ index: InnerProps["index"];
36
37
  };
37
38
  }>;
38
- type FloatingContext = {
39
- styles?: string;
40
- setReference: (reference: HTMLElement | null | undefined) => void;
41
- setFloating: (floating: HTMLElement | null | undefined) => void;
42
- getReferenceProps: () => Record<string, any>;
43
- getFloatingProps: () => Record<string, any>;
39
+ export type FloatingContext = {
40
+ styles?: UseFloatingReturn<any>["floatingStyles"];
41
+ setReference: UseFloatingReturn<any>["refs"]["setReference"];
42
+ setFloating: UseFloatingReturn<any>["refs"]["setFloating"];
43
+ getReferenceProps: ReturnType<typeof useInteractions>["getReferenceProps"];
44
+ getFloatingProps: ReturnType<typeof useInteractions>["getFloatingProps"];
44
45
  slot: Partial<{
45
46
  anchor: `${Placement | "selection"}` | `${Placement | "selection"} ${Align}`;
46
47
  }>;
47
48
  };
48
- export declare function useResolvedAnchor<T extends AnchorProps | AnchorPropsWithSelection>(anchor?: T): Exclude<T, boolean | string> | null;
49
- export declare function useFloating(): FloatingContext;
50
- export declare function useFloatingPanelProps(): () => Record<string, any> & {
51
- "data-anchor": Placement | "top end" | "top start" | "right end" | "right start" | "bottom end" | "bottom start" | "left end" | "left start" | "selection" | "selection end" | "selection start" | undefined;
49
+ export type PlacementContext = {
50
+ updatePlacementConfig: ((value: Exclude<AnchorPropsWithSelection, boolean> | null) => void) | null;
52
51
  };
53
- export declare function useFloatingPanel(options: {
52
+ export declare function useResolvedAnchor<T extends AnchorProps | AnchorPropsWithSelection>(options: {
53
+ anchor?: T;
54
+ }): {
55
+ anchor: Exclude<T, boolean | string> | null;
56
+ };
57
+ export declare function useFloatingReference(): {
58
+ readonly setReference: ((node: import("../utils/floating-ui/svelte-dom/types.js").ReferenceType | null) => void) & ((node: any) => void);
59
+ };
60
+ export declare function useFloatingReferenceProps(): {
61
+ readonly getReferenceProps: (userProps?: import("svelte/elements.js").HTMLAttributes<Element>) => Record<string, unknown>;
62
+ };
63
+ export declare function useFloatingPanelProps(): (userProps?: import("svelte/elements.js").HTMLAttributes<HTMLElement> | undefined) => Record<string, unknown> & {
64
+ "data-anchor": Placement | "selection" | "top end" | "top start" | "right end" | "right start" | "bottom end" | "bottom start" | "left end" | "left start" | "selection end" | "selection start" | undefined;
65
+ };
66
+ export declare function useFloatingPanel(options?: {
54
67
  placement: (AnchorPropsWithSelection & InternalFloatingPanelProps) | null;
55
68
  }): {
56
- readonly setFloating: (floating: HTMLElement | null | undefined) => void;
57
- readonly style: string | undefined;
69
+ readonly setFloating: ((node: HTMLElement | null) => void) & ((node: HTMLElement | null) => void);
70
+ readonly styles: string | undefined;
71
+ };
72
+ export declare function useFixScrollingPixel(options: {
73
+ element: HTMLElement | null;
74
+ }): void;
75
+ export declare function useResolvedConfig(options: {
76
+ config: (Exclude<AnchorPropsWithSelection, boolean | string> & InternalFloatingPanelProps) | null;
77
+ element?: HTMLElement | null;
78
+ }): {
79
+ readonly to: Placement | "selection" | "top end" | "top start" | "right end" | "right start" | "bottom end" | "bottom start" | "left end" | "left start" | "selection end" | "selection start" | undefined;
80
+ readonly gap: number | undefined;
81
+ readonly offset: number | undefined;
82
+ readonly padding: number | undefined;
83
+ readonly inner: {
84
+ listRef: InnerProps["listRef"];
85
+ index: InnerProps["index"];
86
+ } | undefined;
58
87
  };
59
- export declare const createFloatingContext: ({ enabled }?: {
60
- enabled?: boolean;
61
- }) => FloatingContext;
62
- export {};