@pzerelles/headlessui-svelte 2.1.2-next.31 → 2.1.2-next.32

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 (113) hide show
  1. package/dist/button/Button.svelte +84 -54
  2. package/dist/button/Button.svelte.d.ts +2 -3
  3. package/dist/checkbox/Checkbox.svelte +173 -120
  4. package/dist/checkbox/Checkbox.svelte.d.ts +2 -3
  5. package/dist/close-button/CloseButton.svelte +12 -6
  6. package/dist/close-button/CloseButton.svelte.d.ts +7 -8
  7. package/dist/combobox/Combobox.svelte +50 -3
  8. package/dist/data-interactive/DataInteractive.svelte +55 -29
  9. package/dist/data-interactive/DataInteractive.svelte.d.ts +2 -3
  10. package/dist/description/Description.svelte +31 -21
  11. package/dist/description/Description.svelte.d.ts +1 -2
  12. package/dist/dialog/Dialog.svelte +320 -228
  13. package/dist/dialog/Dialog.svelte.d.ts +2 -3
  14. package/dist/dialog/DialogBackdrop.svelte +30 -13
  15. package/dist/dialog/DialogBackdrop.svelte.d.ts +2 -3
  16. package/dist/dialog/DialogPanel.svelte +49 -26
  17. package/dist/dialog/DialogPanel.svelte.d.ts +2 -3
  18. package/dist/dialog/DialogTitle.svelte +38 -23
  19. package/dist/dialog/DialogTitle.svelte.d.ts +2 -3
  20. package/dist/field/Field.svelte +47 -25
  21. package/dist/field/Field.svelte.d.ts +1 -2
  22. package/dist/fieldset/Fieldset.svelte +50 -29
  23. package/dist/fieldset/Fieldset.svelte.d.ts +1 -2
  24. package/dist/focus-trap/FocusTrap.svelte +419 -283
  25. package/dist/focus-trap/FocusTrap.svelte.d.ts +1 -2
  26. package/dist/input/Input.svelte +84 -53
  27. package/dist/input/Input.svelte.d.ts +2 -3
  28. package/dist/internal/FloatingProvider.svelte +14 -9
  29. package/dist/internal/FocusSentinel.svelte +16 -8
  30. package/dist/internal/ForcePortalRoot.svelte +7 -3
  31. package/dist/internal/FormFields.svelte +47 -34
  32. package/dist/internal/FormFieldsProvider.svelte +9 -5
  33. package/dist/internal/FormResolver.svelte +20 -15
  34. package/dist/internal/Hidden.svelte +50 -29
  35. package/dist/internal/Hidden.svelte.d.ts +1 -2
  36. package/dist/internal/MainTreeProvider.svelte +89 -36
  37. package/dist/internal/Portal.svelte +18 -14
  38. package/dist/label/Label.svelte +93 -58
  39. package/dist/label/Label.svelte.d.ts +1 -2
  40. package/dist/legend/Legend.svelte +12 -3
  41. package/dist/listbox/Listbox.svelte +525 -387
  42. package/dist/listbox/Listbox.svelte.d.ts +2 -3
  43. package/dist/listbox/ListboxButton.svelte +173 -127
  44. package/dist/listbox/ListboxButton.svelte.d.ts +2 -3
  45. package/dist/listbox/ListboxOption.svelte +170 -129
  46. package/dist/listbox/ListboxOption.svelte.d.ts +2 -3
  47. package/dist/listbox/ListboxOptions.svelte +400 -304
  48. package/dist/listbox/ListboxOptions.svelte.d.ts +2 -3
  49. package/dist/listbox/ListboxSelectedOption.svelte +38 -15
  50. package/dist/listbox/ListboxSelectedOption.svelte.d.ts +1 -2
  51. package/dist/menu/Menu.svelte +77 -51
  52. package/dist/menu/Menu.svelte.d.ts +2 -3
  53. package/dist/menu/MenuButton.svelte +157 -117
  54. package/dist/menu/MenuButton.svelte.d.ts +2 -3
  55. package/dist/menu/MenuHeading.svelte +32 -14
  56. package/dist/menu/MenuHeading.svelte.d.ts +1 -2
  57. package/dist/menu/MenuItem.svelte +142 -107
  58. package/dist/menu/MenuItem.svelte.d.ts +2 -3
  59. package/dist/menu/MenuItems.svelte +301 -229
  60. package/dist/menu/MenuItems.svelte.d.ts +2 -3
  61. package/dist/menu/MenuSection.svelte +24 -9
  62. package/dist/menu/MenuSection.svelte.d.ts +1 -2
  63. package/dist/menu/MenuSeparator.svelte +17 -4
  64. package/dist/menu/MenuSeparator.svelte.d.ts +1 -2
  65. package/dist/popover/Popover.svelte +216 -150
  66. package/dist/popover/Popover.svelte.d.ts +2 -3
  67. package/dist/popover/PopoverBackdrop.svelte +67 -41
  68. package/dist/popover/PopoverBackdrop.svelte.d.ts +2 -3
  69. package/dist/popover/PopoverButton.svelte +292 -212
  70. package/dist/popover/PopoverButton.svelte.d.ts +2 -3
  71. package/dist/popover/PopoverGroup.svelte +62 -35
  72. package/dist/popover/PopoverGroup.svelte.d.ts +1 -2
  73. package/dist/popover/PopoverPanel.svelte +311 -229
  74. package/dist/popover/PopoverPanel.svelte.d.ts +2 -3
  75. package/dist/portal/InternalPortal.svelte +141 -85
  76. package/dist/portal/InternalPortal.svelte.d.ts +1 -2
  77. package/dist/portal/Portal.svelte +5 -2
  78. package/dist/portal/PortalGroup.svelte +30 -9
  79. package/dist/portal/PortalGroup.svelte.d.ts +1 -2
  80. package/dist/select/Select.svelte +98 -68
  81. package/dist/select/Select.svelte.d.ts +2 -3
  82. package/dist/switch/Switch.svelte +179 -132
  83. package/dist/switch/Switch.svelte.d.ts +2 -3
  84. package/dist/switch/SwitchGroup.svelte +44 -31
  85. package/dist/switch/SwitchGroup.svelte.d.ts +1 -2
  86. package/dist/tabs/Tab.svelte +194 -142
  87. package/dist/tabs/Tab.svelte.d.ts +2 -3
  88. package/dist/tabs/TabGroup.svelte +86 -56
  89. package/dist/tabs/TabGroup.svelte.d.ts +2 -3
  90. package/dist/tabs/TabList.svelte +31 -11
  91. package/dist/tabs/TabList.svelte.d.ts +2 -3
  92. package/dist/tabs/TabPanel.svelte +67 -42
  93. package/dist/tabs/TabPanel.svelte.d.ts +2 -3
  94. package/dist/tabs/TabPanels.svelte +18 -7
  95. package/dist/tabs/TabPanels.svelte.d.ts +2 -3
  96. package/dist/textarea/Textarea.svelte +84 -53
  97. package/dist/textarea/Textarea.svelte.d.ts +2 -3
  98. package/dist/transition/InternalTransitionChild.svelte +259 -170
  99. package/dist/transition/InternalTransitionChild.svelte.d.ts +2 -3
  100. package/dist/transition/Transition.svelte +96 -66
  101. package/dist/transition/Transition.svelte.d.ts +2 -3
  102. package/dist/transition/TransitionChild.svelte +31 -11
  103. package/dist/transition/TransitionChild.svelte.d.ts +2 -3
  104. package/dist/utils/DisabledProvider.svelte +7 -3
  105. package/dist/utils/ElementOrComponent.svelte +46 -23
  106. package/dist/utils/ElementOrComponent.svelte.d.ts +8 -10
  107. package/dist/utils/Generic.svelte +30 -19
  108. package/dist/utils/Generic.svelte.d.ts +6 -7
  109. package/dist/utils/StableCollection.svelte +54 -36
  110. package/dist/utils/floating-ui/svelte/components/FloatingNode.svelte +27 -12
  111. package/dist/utils/floating-ui/svelte/components/FloatingTree.svelte +88 -44
  112. package/dist/utils/types.d.ts +4 -5
  113. package/package.json +1 -1
@@ -1,228 +1,308 @@
1
- <script lang="ts" module>const DEFAULT_BUTTON_TAG = "button";
1
+ <script lang="ts" module>
2
+ import type { ElementType, Props, PropsOf } from "../utils/types.js"
3
+
4
+ const DEFAULT_BUTTON_TAG = "button" as const
5
+ export type PopoverButtonSlot = {
6
+ open: boolean
7
+ active: boolean
8
+ hover: boolean
9
+ focus: boolean
10
+ disabled: boolean
11
+ autofocus: boolean
12
+ }
13
+ export type PopoverButtonPropsWeControl = "aria-controls" | "aria-expanded"
14
+
15
+ export type PopoverButtonComponentProps = {
16
+ disabled?: boolean
17
+ autofocus?: boolean
18
+ }
19
+
20
+ export type PopoverButtonProps<TTag extends ElementType = typeof DEFAULT_BUTTON_TAG> = Props<
21
+ TTag,
22
+ PopoverButtonSlot,
23
+ PopoverButtonPropsWeControl,
24
+ PopoverButtonComponentProps
25
+ >
2
26
  </script>
3
27
 
4
- <script lang="ts" generics="TTag extends ElementType = typeof DEFAULT_BUTTON_TAG">import { useId } from "../hooks/use-id.js";
5
- import {
6
- PopoverStates,
7
- usePopoverAPIContext,
8
- usePopoverContext,
9
- usePopoverGroupContext,
10
- usePopoverPanelContext
11
- } from "./context.svelte.js";
12
- import { useFloatingReference } from "../internal/floating.svelte.js";
13
- import { untrack } from "svelte";
14
- import { getOwnerDocument } from "../utils/owner.js";
15
- import { useFocusRing } from "../hooks/use-focus-ring.svelte.js";
16
- import { useHover } from "../hooks/use-hover.svelte.js";
17
- import { useActivePress } from "../hooks/use-active-press.svelte.js";
18
- import { useResolveButtonType } from "../hooks/use-resolve-button-type.svelte.js";
19
- import { mergeProps } from "../utils/render.js";
20
- import { useTabDirection, Direction as TabDirection } from "../hooks/use-tab-direction.svelte.js";
21
- import { match } from "../utils/match.js";
22
- import { Focus, focusIn, FocusResult, getFocusableElements } from "../utils/focus-management.js";
23
- import { microTask } from "../utils/microTask.js";
24
- import Hidden, { HiddenFeatures } from "../internal/Hidden.svelte";
25
- import ElementOrComponent from "../utils/ElementOrComponent.svelte";
26
- const internalId = useId();
27
- let {
28
- ref = $bindable(),
29
- id = `headlessui-popover-button-${internalId}`,
30
- disabled = false,
31
- autofocus = false,
32
- ...theirProps
33
- } = $props();
34
- const context = usePopoverContext("PopoverButton");
35
- const api = usePopoverAPIContext("PopoverButton");
36
- const { isPortalled } = $derived(api);
37
- const sentinelId = `headlessui-focus-sentinel-${useId()}`;
38
- const groupContext = usePopoverGroupContext();
39
- const closeOthers = $derived(groupContext?.closeOthers);
40
- const panelContext = usePopoverPanelContext();
41
- const isWithinPanel = panelContext !== void 0;
42
- $effect(() => {
43
- if (isWithinPanel) return;
44
- id;
45
- return untrack(() => {
46
- context.setButtonId(id);
47
- return () => {
48
- context.setButtonId(void 0);
49
- };
50
- });
51
- });
52
- const uniqueIdentifier = Symbol();
53
- const floatingReference = useFloatingReference();
54
- const { setReference } = $derived(floatingReference);
55
- $effect(() => {
56
- setReference(ref);
57
- });
58
- $effect(() => {
59
- if (isWithinPanel) return;
60
- ref;
61
- untrack(() => {
62
- if (ref) {
63
- context.buttons.push(uniqueIdentifier);
28
+ <script lang="ts" generics="TTag extends ElementType = typeof DEFAULT_BUTTON_TAG">
29
+ import { useId } from "../hooks/use-id.js"
30
+ import {
31
+ PopoverStates,
32
+ usePopoverAPIContext,
33
+ usePopoverContext,
34
+ usePopoverGroupContext,
35
+ usePopoverPanelContext,
36
+ } from "./context.svelte.js"
37
+ import { useFloatingReference } from "../internal/floating.svelte.js"
38
+ import { untrack } from "svelte"
39
+ import { getOwnerDocument } from "../utils/owner.js"
40
+ import { useFocusRing } from "../hooks/use-focus-ring.svelte.js"
41
+ import { useHover } from "../hooks/use-hover.svelte.js"
42
+ import { useActivePress } from "../hooks/use-active-press.svelte.js"
43
+ import { useResolveButtonType } from "../hooks/use-resolve-button-type.svelte.js"
44
+ import { mergeProps } from "../utils/render.js"
45
+ import { useTabDirection, Direction as TabDirection } from "../hooks/use-tab-direction.svelte.js"
46
+ import { match } from "../utils/match.js"
47
+ import { Focus, focusIn, FocusResult, getFocusableElements } from "../utils/focus-management.js"
48
+ import { microTask } from "../utils/microTask.js"
49
+ import Hidden, { HiddenFeatures } from "../internal/Hidden.svelte"
50
+ import ElementOrComponent from "../utils/ElementOrComponent.svelte"
51
+ import type { FocusEventHandler } from "svelte/elements"
52
+
53
+ const internalId = useId()
54
+ let {
55
+ ref = $bindable(),
56
+ id = `headlessui-popover-button-${internalId}` as PropsOf<TTag>["id"],
57
+ disabled = false,
58
+ autofocus = false as PropsOf<TTag>["autofocus"],
59
+ ...theirProps
60
+ }: { as?: TTag } & PopoverButtonProps<TTag> = $props()
61
+ const context = usePopoverContext("PopoverButton")
62
+ const api = usePopoverAPIContext("PopoverButton")
63
+ const { isPortalled } = $derived(api)
64
+
65
+ const sentinelId = `headlessui-focus-sentinel-${useId()}`
66
+
67
+ const groupContext = usePopoverGroupContext()
68
+ const closeOthers = $derived(groupContext?.closeOthers)
69
+
70
+ const panelContext = usePopoverPanelContext()
71
+
72
+ // A button inside a panel will just have "close" functionality, no "open" functionality. However,
73
+ // if a `PopoverButton` is rendered inside a `Popover` which in turn is rendered inside a
74
+ // `PopoverPanel` (aka nested popovers), then we need to make sure that the button is able to
75
+ // open the nested popover.
76
+ //
77
+ // The `Popover` itself will also render a `PopoverPanelContext` but with a value of `null`. That
78
+ // way we don't need to keep track of _which_ `PopoverPanel` (if at all) we are in, we can just
79
+ // check if we are in a `PopoverPanel` or not since this will always point to the nearest one and
80
+ // won't pierce through `Popover` components themselves.
81
+ const isWithinPanel = panelContext !== undefined
82
+
83
+ $effect(() => {
84
+ // [isWithinPanel, id, dispatch]
85
+ if (isWithinPanel) return
86
+ id
87
+ return untrack(() => {
88
+ context.setButtonId(id)
89
+ return () => {
90
+ context.setButtonId(undefined)
91
+ }
92
+ })
93
+ })
94
+
95
+ // This is a little bit different compared to the `id` we already have. The goal is to have a very
96
+ // unique identifier for this specific component. This can be achieved with the `id` from above.
97
+ //
98
+ // However, the difference is for React 17 and lower where the `useId` hook doesn't exist yet.
99
+ // There we will generate a unique ID based on a simple counter, but for SSR this will result in
100
+ // `undefined` first, later it is patched to be a unique ID. The problem is that this patching
101
+ // happens after the component is rendered and therefore there is a moment in time where multiple
102
+ // buttons have the exact same ID and the `state.buttons` would result in something like:
103
+ //
104
+ // ```js
105
+ // ['headlessui-popover-button-undefined', 'headlessui-popover-button-1']
106
+ // ```
107
+ //
108
+ // With this approach we guarantee that there is a unique value for each button.
109
+ const uniqueIdentifier = Symbol()
110
+
111
+ const floatingReference = useFloatingReference()
112
+ const { setReference } = $derived(floatingReference)
113
+ $effect(() => {
114
+ setReference(ref)
115
+ })
116
+ $effect(() => {
117
+ if (isWithinPanel) return
118
+ ref
119
+ untrack(() => {
120
+ if (ref) {
121
+ context.buttons.push(uniqueIdentifier)
122
+ } else {
123
+ let idx = context.buttons.indexOf(uniqueIdentifier)
124
+ if (idx !== -1) context.buttons.splice(idx, 1)
125
+ }
126
+
127
+ if (context.buttons.length > 1) {
128
+ console.warn("You are already using a <PopoverButton /> but only 1 <PopoverButton /> is supported.")
129
+ }
130
+
131
+ if (ref) context.setButton(ref)
132
+ })
133
+ })
134
+ const ownerDocument = $derived(getOwnerDocument(ref))
135
+
136
+ const handleKeyDown = (event: KeyboardEvent) => {
137
+ if (isWithinPanel) {
138
+ if (context.popoverState === PopoverStates.Closed) return
139
+ switch (event.key) {
140
+ case "Space":
141
+ case "Enter":
142
+ event.preventDefault() // Prevent triggering a *click* event
143
+ // @ts-expect-error
144
+ event.target.click?.()
145
+ context.closePopover()
146
+ context.button?.focus() // Re-focus the original opening Button
147
+ break
148
+ }
64
149
  } else {
65
- let idx = context.buttons.indexOf(uniqueIdentifier);
66
- if (idx !== -1) context.buttons.splice(idx, 1);
67
- }
68
- if (context.buttons.length > 1) {
69
- console.warn("You are already using a <PopoverButton /> but only 1 <PopoverButton /> is supported.");
70
- }
71
- if (ref) context.setButton(ref);
72
- });
73
- });
74
- const ownerDocument = $derived(getOwnerDocument(ref));
75
- const handleKeyDown = (event) => {
76
- if (isWithinPanel) {
77
- if (context.popoverState === PopoverStates.Closed) return;
78
- switch (event.key) {
79
- case "Space":
80
- case "Enter":
81
- event.preventDefault();
82
- event.target.click?.();
83
- context.closePopover();
84
- context.button?.focus();
85
- break;
86
- }
87
- } else {
88
- switch (event.key) {
89
- case "Space":
90
- case "Enter":
91
- event.preventDefault();
92
- event.stopPropagation();
93
- if (context.popoverState === PopoverStates.Closed) closeOthers?.(context.buttonId);
94
- context.togglePopover();
95
- break;
96
- case "Escape":
97
- if (context.popoverState !== PopoverStates.Open) return closeOthers?.(context.buttonId);
98
- if (!ref) return;
99
- if (ownerDocument?.activeElement && !ref.contains(ownerDocument.activeElement)) {
100
- return;
101
- }
102
- event.preventDefault();
103
- event.stopPropagation();
104
- context.closePopover();
105
- break;
150
+ switch (event.key) {
151
+ case "Space":
152
+ case "Enter":
153
+ event.preventDefault() // Prevent triggering a *click* event
154
+ event.stopPropagation()
155
+ if (context.popoverState === PopoverStates.Closed) closeOthers?.(context.buttonId!)
156
+ context.togglePopover()
157
+ break
158
+
159
+ case "Escape":
160
+ if (context.popoverState !== PopoverStates.Open) return closeOthers?.(context.buttonId!)
161
+ if (!ref) return
162
+ if (ownerDocument?.activeElement && !ref.contains(ownerDocument.activeElement)) {
163
+ return
164
+ }
165
+ event.preventDefault()
166
+ event.stopPropagation()
167
+ context.closePopover()
168
+ break
169
+ }
106
170
  }
107
171
  }
108
- };
109
- const handleKeyUp = (event) => {
110
- if (isWithinPanel) return;
111
- if (event.key === "Space") {
112
- event.preventDefault();
113
- }
114
- };
115
- const handleClick = (event) => {
116
- if (disabled) return;
117
- if (isWithinPanel) {
118
- context.closePopover();
119
- context.button?.focus();
120
- } else {
121
- event.preventDefault();
122
- event.stopPropagation();
123
- if (context.popoverState === PopoverStates.Closed) closeOthers?.(context.buttonId);
124
- context.togglePopover();
125
- context.button?.focus();
126
- }
127
- };
128
- const handleMouseDown = (event) => {
129
- event.preventDefault();
130
- event.stopPropagation();
131
- };
132
- const { isFocusVisible: focus, focusProps } = $derived(
133
- useFocusRing({
134
- get autofocus() {
135
- return autofocus;
136
- }
137
- })
138
- );
139
- const { isHovered: hover, hoverProps } = $derived(
140
- useHover({
141
- get disabled() {
142
- return disabled;
172
+
173
+ const handleKeyUp = (event: KeyboardEvent) => {
174
+ if (isWithinPanel) return
175
+ if (event.key === "Space") {
176
+ // Required for firefox, event.preventDefault() in handleKeyDown for
177
+ // the Space key doesn't cancel the handleKeyUp, which in turn
178
+ // triggers a *click*.
179
+ event.preventDefault()
143
180
  }
144
- })
145
- );
146
- const { pressed: active, pressProps } = $derived(
147
- useActivePress({
148
- get disabled() {
149
- return disabled;
181
+ }
182
+
183
+ const handleClick = (event: MouseEvent) => {
184
+ //if (isDisabledReactIssue7711(event.currentTarget)) return
185
+ if (disabled) return
186
+ if (isWithinPanel) {
187
+ context.closePopover()
188
+ context.button?.focus() // Re-focus the original opening Button
189
+ } else {
190
+ event.preventDefault()
191
+ event.stopPropagation()
192
+ if (context.popoverState === PopoverStates.Closed) closeOthers?.(context.buttonId!)
193
+ context.togglePopover()
194
+ context.button?.focus()
150
195
  }
151
- })
152
- );
153
- const visible = $derived(context.popoverState === PopoverStates.Open);
154
- const slot = $derived({
155
- open: visible,
156
- active: active || visible,
157
- disabled,
158
- hover,
159
- focus,
160
- autofocus
161
- });
162
- const type = useResolveButtonType({
163
- get props() {
164
- return { type: theirProps.type, as: theirProps.as };
165
- },
166
- get ref() {
167
- return { current: context.button };
168
196
  }
169
- });
170
- const ourProps = $derived(
171
- isWithinPanel ? mergeProps(
172
- {
173
- type,
174
- onkeydown: handleKeyDown,
175
- onclick: handleClick,
176
- disabled: disabled || void 0,
177
- autofocus
197
+
198
+ const handleMouseDown = (event: MouseEvent) => {
199
+ event.preventDefault()
200
+ event.stopPropagation()
201
+ }
202
+
203
+ const { isFocusVisible: focus, focusProps } = $derived(
204
+ useFocusRing({
205
+ get autofocus() {
206
+ return autofocus
207
+ },
208
+ })
209
+ )
210
+ const { isHovered: hover, hoverProps } = $derived(
211
+ useHover({
212
+ get disabled() {
213
+ return disabled
214
+ },
215
+ })
216
+ )
217
+ const { pressed: active, pressProps } = $derived(
218
+ useActivePress({
219
+ get disabled() {
220
+ return disabled
221
+ },
222
+ })
223
+ )
224
+
225
+ const visible = $derived(context.popoverState === PopoverStates.Open)
226
+ const slot = $derived({
227
+ open: visible,
228
+ active: active || visible,
229
+ disabled,
230
+ hover,
231
+ focus,
232
+ autofocus,
233
+ } satisfies PopoverButtonSlot)
234
+
235
+ const type = useResolveButtonType({
236
+ get props() {
237
+ return { type: theirProps.type, as: theirProps.as }
178
238
  },
179
- focusProps,
180
- hoverProps,
181
- pressProps
182
- ) : mergeProps(
183
- {
184
- id: context.buttonId,
185
- type,
186
- "aria-expanded": context.popoverState === PopoverStates.Open,
187
- "aria-controls": context.panel ? context.panelId : void 0,
188
- disabled: disabled || void 0,
189
- autofocus,
190
- onkeydown: handleKeyDown,
191
- onkeyup: handleKeyUp,
192
- onclick: handleClick,
193
- onmousedown: handleMouseDown
239
+ get ref() {
240
+ return { current: context.button }
194
241
  },
195
- focusProps,
196
- hoverProps,
197
- pressProps
242
+ })
243
+ const ourProps = $derived(
244
+ isWithinPanel
245
+ ? mergeProps(
246
+ {
247
+ type,
248
+ onkeydown: handleKeyDown,
249
+ onclick: handleClick,
250
+ disabled: disabled || undefined,
251
+ autofocus,
252
+ },
253
+ focusProps,
254
+ hoverProps,
255
+ pressProps
256
+ )
257
+ : mergeProps(
258
+ {
259
+ id: context.buttonId,
260
+ type,
261
+ "aria-expanded": context.popoverState === PopoverStates.Open,
262
+ "aria-controls": context.panel ? context.panelId : undefined,
263
+ disabled: disabled || undefined,
264
+ autofocus,
265
+ onkeydown: handleKeyDown,
266
+ onkeyup: handleKeyUp,
267
+ onclick: handleClick,
268
+ onmousedown: handleMouseDown,
269
+ },
270
+ focusProps,
271
+ hoverProps,
272
+ pressProps
273
+ )
198
274
  )
199
- );
200
- const direction = useTabDirection();
201
- const handleFocus = () => {
202
- const el = context.panel;
203
- if (!el) return;
204
- function run() {
205
- let result = match(direction.current, {
206
- [TabDirection.Forwards]: () => focusIn(el, Focus.First),
207
- [TabDirection.Backwards]: () => focusIn(el, Focus.Last)
208
- });
209
- if (result === FocusResult.Error) {
210
- focusIn(
211
- getFocusableElements().filter((el2) => el2.dataset.headlessuiFocusGuard !== "true"),
212
- match(direction.current, {
213
- [TabDirection.Forwards]: Focus.Next,
214
- [TabDirection.Backwards]: Focus.Previous
215
- }),
216
- { relativeTo: context.button }
217
- );
275
+
276
+ const direction = useTabDirection()
277
+ const handleFocus = () => {
278
+ const el = context.panel as HTMLElement
279
+ if (!el) return
280
+
281
+ function run() {
282
+ let result = match(direction.current, {
283
+ [TabDirection.Forwards]: () => focusIn(el, Focus.First),
284
+ [TabDirection.Backwards]: () => focusIn(el, Focus.Last),
285
+ })
286
+
287
+ if (result === FocusResult.Error) {
288
+ focusIn(
289
+ getFocusableElements().filter((el) => el.dataset.headlessuiFocusGuard !== "true"),
290
+ match(direction.current, {
291
+ [TabDirection.Forwards]: Focus.Next,
292
+ [TabDirection.Backwards]: Focus.Previous,
293
+ }),
294
+ { relativeTo: context.button }
295
+ )
296
+ }
297
+ }
298
+
299
+ // TODO: Cleanup once we are using real browser tests
300
+ if (process.env.NODE_ENV === "test") {
301
+ microTask(run)
302
+ } else {
303
+ run()
218
304
  }
219
305
  }
220
- if (process.env.NODE_ENV === "test") {
221
- microTask(run);
222
- } else {
223
- run();
224
- }
225
- };
226
306
  </script>
227
307
 
228
308
  <ElementOrComponent
@@ -18,9 +18,8 @@ declare class __sveltets_Render<TTag extends ElementType = typeof DEFAULT_BUTTON
18
18
  props(): {
19
19
  as?: TTag | undefined;
20
20
  } & (Exclude<keyof PropsOf<TTag>, ("slot" | "as" | "children" | "class" | "ref") | PopoverButtonPropsWeControl | keyof PopoverButtonComponentProps> extends infer T extends keyof PropsOf<TTag> ? { [P in T]: PropsOf<TTag>[P]; } : never) & {
21
- children?: import("svelte").Snippet<[{
22
- slot: PopoverButtonSlot;
23
- props: Record<string, any>;
21
+ children?: import("svelte").Snippet<[PopoverButtonSlot & {
22
+ props?: Record<string, any>;
24
23
  }]> | undefined;
25
24
  class?: string | ((bag: PopoverButtonSlot) => string) | null | undefined;
26
25
  ref?: HTMLElement;
@@ -1,41 +1,68 @@
1
- <script lang="ts" module>import { setContext } from "svelte";
2
- const DEFAULT_GROUP_TAG = "div";
1
+ <script lang="ts" module>
2
+ import type { ElementType, Props } from "../utils/types.js"
3
+ import { setContext } from "svelte"
4
+
5
+ const DEFAULT_GROUP_TAG = "div" as const
6
+ type GroupRenderPropArg = {}
7
+ type GroupPropsWeControl = never
8
+
9
+ export type PopoverGroupProps<TTag extends ElementType = typeof DEFAULT_GROUP_TAG> = Props<
10
+ TTag,
11
+ GroupRenderPropArg,
12
+ GroupPropsWeControl
13
+ >
3
14
  </script>
4
15
 
5
- <script lang="ts" generics="TTag extends ElementType = typeof DEFAULT_GROUP_TAG">import MainTreeProvider from "../internal/MainTreeProvider.svelte";
6
- import ElementOrComponent from "../utils/ElementOrComponent.svelte";
7
- import { getOwnerDocument } from "../utils/owner.js";
8
- let { ref = $bindable(), ...theirProps } = $props();
9
- const popovers = $state([]);
10
- const unregisterPopover = (registerBag) => {
11
- const idx = popovers.indexOf(registerBag);
12
- if (idx !== -1) popovers.splice(idx, 1);
13
- };
14
- const registerPopover = (registerBag) => {
15
- popovers.push(registerBag);
16
- return () => unregisterPopover(registerBag);
17
- };
18
- const isFocusWithinPopoverGroup = () => {
19
- const ownerDocument = getOwnerDocument(ref);
20
- if (!ownerDocument) return false;
21
- let element = ownerDocument.activeElement;
22
- if (ref?.contains(element)) return true;
23
- return popovers.some((bag) => {
24
- return ownerDocument.getElementById(bag.buttonId)?.contains(element) || ownerDocument.getElementById(bag.panelId)?.contains(element);
25
- });
26
- };
27
- const closeOthers = (buttonId) => {
28
- for (const popover of popovers) {
29
- if (popover.buttonId !== buttonId) popover.close();
16
+ <script lang="ts" generics="TTag extends ElementType = typeof DEFAULT_GROUP_TAG">
17
+ import type { PopoverGroupContext, PopoverRegisterBag } from "./context.svelte"
18
+ import MainTreeProvider from "../internal/MainTreeProvider.svelte"
19
+ import ElementOrComponent from "../utils/ElementOrComponent.svelte"
20
+ import { getOwnerDocument } from "../utils/owner.js"
21
+
22
+ let { ref = $bindable(), ...theirProps }: { as?: TTag } & PopoverGroupProps<TTag> = $props()
23
+
24
+ const popovers = $state<PopoverRegisterBag[]>([])
25
+
26
+ const unregisterPopover = (registerBag: PopoverRegisterBag) => {
27
+ const idx = popovers.indexOf(registerBag)
28
+ if (idx !== -1) popovers.splice(idx, 1)
29
+ }
30
+
31
+ const registerPopover = (registerBag: PopoverRegisterBag) => {
32
+ popovers.push(registerBag)
33
+ return () => unregisterPopover(registerBag)
30
34
  }
31
- };
32
- setContext("PopoverGroupContext", {
33
- registerPopover,
34
- unregisterPopover,
35
- isFocusWithinPopoverGroup,
36
- closeOthers
37
- });
38
- const slot = {};
35
+
36
+ const isFocusWithinPopoverGroup = () => {
37
+ const ownerDocument = getOwnerDocument(ref)
38
+ if (!ownerDocument) return false
39
+ let element = ownerDocument.activeElement
40
+
41
+ if (ref?.contains(element)) return true
42
+
43
+ // Check if the focus is in one of the button or panel elements. This is important in case you are rendering inside a Portal.
44
+ return popovers.some((bag) => {
45
+ return (
46
+ ownerDocument!.getElementById(bag.buttonId!)?.contains(element) ||
47
+ ownerDocument!.getElementById(bag.panelId!)?.contains(element)
48
+ )
49
+ })
50
+ }
51
+
52
+ const closeOthers = (buttonId: string) => {
53
+ for (const popover of popovers) {
54
+ if (popover.buttonId !== buttonId) popover.close()
55
+ }
56
+ }
57
+
58
+ setContext<PopoverGroupContext>("PopoverGroupContext", {
59
+ registerPopover,
60
+ unregisterPopover,
61
+ isFocusWithinPopoverGroup,
62
+ closeOthers,
63
+ })
64
+
65
+ const slot = {} satisfies GroupRenderPropArg
39
66
  </script>
40
67
 
41
68
  <MainTreeProvider>
@@ -8,8 +8,7 @@ declare class __sveltets_Render<TTag extends ElementType = typeof DEFAULT_GROUP_
8
8
  as?: TTag | undefined;
9
9
  } & (Exclude<keyof import("../utils/types.js").PropsOf<TTag>, "slot" | "as" | "children" | "class" | "ref"> extends infer T extends keyof import("../utils/types.js").PropsOf<TTag> ? { [P in T]: import("../utils/types.js").PropsOf<TTag>[P]; } : never) & {
10
10
  children?: import("svelte").Snippet<[{
11
- slot: GroupRenderPropArg;
12
- props: Record<string, any>;
11
+ props?: Record<string, any>;
13
12
  }]> | undefined;
14
13
  class?: string | ((bag: GroupRenderPropArg) => string) | null | undefined;
15
14
  ref?: HTMLElement;