@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.
Files changed (132) hide show
  1. package/dist/button/Button.svelte +84 -54
  2. package/dist/button/Button.svelte.d.ts +7 -4
  3. package/dist/checkbox/Checkbox.svelte +173 -120
  4. package/dist/checkbox/Checkbox.svelte.d.ts +7 -4
  5. package/dist/close-button/CloseButton.svelte +12 -6
  6. package/dist/close-button/CloseButton.svelte.d.ts +13 -10
  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 +7 -5
  10. package/dist/description/Description.svelte +39 -24
  11. package/dist/description/Description.svelte.d.ts +8 -5
  12. package/dist/description/context.svelte.js +13 -15
  13. package/dist/dialog/Dialog.svelte +358 -38
  14. package/dist/dialog/Dialog.svelte.d.ts +10 -7
  15. package/dist/dialog/DialogBackdrop.svelte +30 -13
  16. package/dist/dialog/DialogBackdrop.svelte.d.ts +7 -4
  17. package/dist/dialog/DialogPanel.svelte +49 -26
  18. package/dist/dialog/DialogPanel.svelte.d.ts +7 -4
  19. package/dist/dialog/DialogTitle.svelte +38 -23
  20. package/dist/dialog/DialogTitle.svelte.d.ts +7 -4
  21. package/dist/field/Field.svelte +50 -34
  22. package/dist/field/Field.svelte.d.ts +7 -4
  23. package/dist/fieldset/Fieldset.svelte +50 -29
  24. package/dist/fieldset/Fieldset.svelte.d.ts +7 -4
  25. package/dist/focus-trap/FocusTrap.svelte +419 -283
  26. package/dist/focus-trap/FocusTrap.svelte.d.ts +7 -4
  27. package/dist/hooks/use-disabled.d.ts +4 -1
  28. package/dist/hooks/use-disabled.js +10 -5
  29. package/dist/input/Input.svelte +84 -53
  30. package/dist/input/Input.svelte.d.ts +7 -4
  31. package/dist/internal/FloatingProvider.svelte +14 -9
  32. package/dist/internal/FocusSentinel.svelte +16 -8
  33. package/dist/internal/ForcePortalRoot.svelte +7 -3
  34. package/dist/internal/FormFields.svelte +47 -34
  35. package/dist/internal/FormFieldsProvider.svelte +9 -5
  36. package/dist/internal/FormResolver.svelte +20 -15
  37. package/dist/internal/Hidden.svelte +50 -29
  38. package/dist/internal/Hidden.svelte.d.ts +7 -4
  39. package/dist/internal/MainTreeProvider.svelte +89 -36
  40. package/dist/internal/Portal.svelte +18 -14
  41. package/dist/internal/floating-provider.svelte.js +1 -1
  42. package/dist/internal/floating.svelte.d.ts +5 -5
  43. package/dist/internal/floating.svelte.js +17 -17
  44. package/dist/label/Label.svelte +93 -58
  45. package/dist/label/Label.svelte.d.ts +7 -4
  46. package/dist/legend/Legend.svelte +12 -3
  47. package/dist/listbox/Listbox.svelte +525 -387
  48. package/dist/listbox/Listbox.svelte.d.ts +7 -5
  49. package/dist/listbox/ListboxButton.svelte +173 -127
  50. package/dist/listbox/ListboxButton.svelte.d.ts +7 -5
  51. package/dist/listbox/ListboxOption.svelte +170 -129
  52. package/dist/listbox/ListboxOption.svelte.d.ts +7 -5
  53. package/dist/listbox/ListboxOptions.svelte +400 -304
  54. package/dist/listbox/ListboxOptions.svelte.d.ts +7 -5
  55. package/dist/listbox/ListboxSelectedOption.svelte +38 -15
  56. package/dist/listbox/ListboxSelectedOption.svelte.d.ts +7 -4
  57. package/dist/listbox/index.d.ts +4 -4
  58. package/dist/listbox/index.js +1 -1
  59. package/dist/menu/Menu.svelte +78 -57
  60. package/dist/menu/Menu.svelte.d.ts +7 -5
  61. package/dist/menu/MenuButton.svelte +157 -117
  62. package/dist/menu/MenuButton.svelte.d.ts +7 -5
  63. package/dist/menu/MenuHeading.svelte +32 -14
  64. package/dist/menu/MenuHeading.svelte.d.ts +7 -5
  65. package/dist/menu/MenuItem.svelte +142 -107
  66. package/dist/menu/MenuItem.svelte.d.ts +8 -8
  67. package/dist/menu/MenuItems.svelte +301 -229
  68. package/dist/menu/MenuItems.svelte.d.ts +7 -5
  69. package/dist/menu/MenuSection.svelte +24 -9
  70. package/dist/menu/MenuSection.svelte.d.ts +7 -5
  71. package/dist/menu/MenuSeparator.svelte +17 -4
  72. package/dist/menu/MenuSeparator.svelte.d.ts +7 -6
  73. package/dist/menu/context.svelte.d.ts +1 -29
  74. package/dist/menu/context.svelte.js +29 -27
  75. package/dist/menu/index.d.ts +7 -7
  76. package/dist/popover/Popover.svelte +216 -150
  77. package/dist/popover/Popover.svelte.d.ts +7 -4
  78. package/dist/popover/PopoverBackdrop.svelte +67 -41
  79. package/dist/popover/PopoverBackdrop.svelte.d.ts +7 -4
  80. package/dist/popover/PopoverButton.svelte +292 -212
  81. package/dist/popover/PopoverButton.svelte.d.ts +7 -4
  82. package/dist/popover/PopoverGroup.svelte +62 -35
  83. package/dist/popover/PopoverGroup.svelte.d.ts +7 -4
  84. package/dist/popover/PopoverPanel.svelte +311 -229
  85. package/dist/popover/PopoverPanel.svelte.d.ts +7 -4
  86. package/dist/portal/InternalPortal.svelte +141 -85
  87. package/dist/portal/InternalPortal.svelte.d.ts +7 -4
  88. package/dist/portal/Portal.svelte +5 -2
  89. package/dist/portal/PortalGroup.svelte +30 -9
  90. package/dist/portal/PortalGroup.svelte.d.ts +7 -4
  91. package/dist/select/Select.svelte +98 -68
  92. package/dist/select/Select.svelte.d.ts +7 -4
  93. package/dist/switch/Switch.svelte +179 -132
  94. package/dist/switch/Switch.svelte.d.ts +7 -4
  95. package/dist/switch/SwitchGroup.svelte +44 -31
  96. package/dist/switch/SwitchGroup.svelte.d.ts +7 -4
  97. package/dist/tabs/Tab.svelte +194 -143
  98. package/dist/tabs/Tab.svelte.d.ts +7 -4
  99. package/dist/tabs/TabGroup.svelte +81 -214
  100. package/dist/tabs/TabGroup.svelte.d.ts +7 -24
  101. package/dist/tabs/TabList.svelte +31 -11
  102. package/dist/tabs/TabList.svelte.d.ts +7 -4
  103. package/dist/tabs/TabPanel.svelte +67 -43
  104. package/dist/tabs/TabPanel.svelte.d.ts +7 -4
  105. package/dist/tabs/TabPanels.svelte +18 -7
  106. package/dist/tabs/TabPanels.svelte.d.ts +7 -4
  107. package/dist/tabs/context.svelte.d.ts +31 -0
  108. package/dist/tabs/context.svelte.js +134 -0
  109. package/dist/textarea/Textarea.svelte +84 -53
  110. package/dist/textarea/Textarea.svelte.d.ts +7 -4
  111. package/dist/transition/InternalTransitionChild.svelte +259 -170
  112. package/dist/transition/InternalTransitionChild.svelte.d.ts +7 -4
  113. package/dist/transition/Transition.svelte +96 -66
  114. package/dist/transition/Transition.svelte.d.ts +7 -4
  115. package/dist/transition/TransitionChild.svelte +31 -11
  116. package/dist/transition/TransitionChild.svelte.d.ts +7 -4
  117. package/dist/utils/ElementOrComponent.svelte +43 -23
  118. package/dist/utils/ElementOrComponent.svelte.d.ts +10 -4
  119. package/dist/utils/Generic.svelte +36 -22
  120. package/dist/utils/Generic.svelte.d.ts +7 -4
  121. package/dist/utils/StableCollection.svelte +54 -36
  122. package/dist/utils/floating-ui/svelte/components/FloatingNode.svelte +27 -12
  123. package/dist/utils/floating-ui/svelte/components/FloatingTree.svelte +88 -44
  124. package/dist/utils/floating-ui/svelte/hooks/useFloating.svelte.js +7 -7
  125. package/dist/utils/floating-ui/svelte/hooks/useFloatingRootContext.svelte.js +1 -1
  126. package/dist/utils/floating-ui/svelte/types.d.ts +4 -4
  127. package/dist/utils/floating-ui/svelte-dom/types.d.ts +2 -2
  128. package/dist/utils/floating-ui/svelte-dom/useFloating.svelte.js +6 -6
  129. package/dist/utils/types.d.ts +11 -4
  130. package/package.json +2 -2
  131. package/dist/dialog/InternalDialog.svelte +0 -233
  132. package/dist/dialog/InternalDialog.svelte.d.ts +0 -42
@@ -1,47 +1,73 @@
1
- <script lang="ts" module>import { RenderFeatures } from "../utils/render.js";
2
- let DEFAULT_BACKDROP_TAG = "div";
3
- const BackdropRenderFeatures = RenderFeatures.RenderStrategy | RenderFeatures.Static;
1
+ <script lang="ts" module>
2
+ import type { ElementType, Props, PropsOf } from "../utils/types.js"
3
+ import { RenderFeatures, type PropsForFeatures } from "../utils/render.js"
4
+
5
+ let DEFAULT_BACKDROP_TAG = "div" as const
6
+ type BackdropRenderPropArg = {
7
+ open: boolean
8
+ }
9
+ type BackdropPropsWeControl = "aria-hidden"
10
+
11
+ const BackdropRenderFeatures = RenderFeatures.RenderStrategy | RenderFeatures.Static
12
+
13
+ export type PopoverBackdropProps<TTag extends ElementType = typeof DEFAULT_BACKDROP_TAG> = Props<
14
+ TTag,
15
+ BackdropRenderPropArg,
16
+ BackdropPropsWeControl,
17
+ { transition?: boolean } & PropsForFeatures<typeof BackdropRenderFeatures>
18
+ >
19
+
20
+ export type PopoverOverlayProps<TTag extends ElementType = typeof DEFAULT_BACKDROP_TAG> = PopoverBackdropProps<TTag>
4
21
  </script>
5
22
 
6
- <script lang="ts" generics="TTag extends ElementType = typeof DEFAULT_BACKDROP_TAG">import ElementOrComponent from "../utils/ElementOrComponent.svelte";
7
- import { useId } from "../hooks/use-id.js";
8
- import { PopoverStates, usePopoverContext } from "./context.svelte.js";
9
- import { State, useOpenClosed } from "../internal/open-closed.js";
10
- import { transitionDataAttributes, useTransition } from "../hooks/use-transition.svelte.js";
11
- const internalId = useId();
12
- let {
13
- ref = $bindable(),
14
- id = `headlessui-popover-backdrop-${internalId}`,
15
- transition = false,
16
- ...theirProps
17
- } = $props();
18
- const context = usePopoverContext("PopoverBackdrop");
19
- const { popoverState } = $derived(context);
20
- const usesOpenClosedState = useOpenClosed();
21
- const _transition = useTransition({
22
- get enabled() {
23
- return transition;
24
- },
25
- get element() {
26
- return ref;
27
- },
28
- get show() {
29
- return usesOpenClosedState !== null ? (usesOpenClosedState.value & State.Open) === State.Open : popoverState === PopoverStates.Open;
23
+ <script lang="ts" generics="TTag extends ElementType = typeof DEFAULT_BACKDROP_TAG">
24
+ import ElementOrComponent from "../utils/ElementOrComponent.svelte"
25
+ import { useId } from "../hooks/use-id.js"
26
+ import { PopoverStates, usePopoverContext } from "./context.svelte.js"
27
+ import { State, useOpenClosed } from "../internal/open-closed.js"
28
+ import { transitionDataAttributes, useTransition } from "../hooks/use-transition.svelte.js"
29
+
30
+ const internalId = useId()
31
+ let {
32
+ ref = $bindable(),
33
+ id = `headlessui-popover-backdrop-${internalId}` as PropsOf<TTag>["id"],
34
+ transition = false,
35
+ ...theirProps
36
+ }: { as?: TTag } & PopoverBackdropProps<TTag> = $props()
37
+ const context = usePopoverContext("PopoverBackdrop")
38
+ const { popoverState } = $derived(context)
39
+
40
+ const usesOpenClosedState = useOpenClosed()
41
+ const _transition = useTransition({
42
+ get enabled() {
43
+ return transition
44
+ },
45
+ get element() {
46
+ return ref
47
+ },
48
+ get show() {
49
+ return usesOpenClosedState !== null
50
+ ? (usesOpenClosedState.value & State.Open) === State.Open
51
+ : popoverState === PopoverStates.Open
52
+ },
53
+ })
54
+ const { visible, data: transitionData } = $derived(_transition)
55
+
56
+ const handleClick = (event: MouseEvent) => {
57
+ //if (isDisabledReactIssue7711(event.currentTarget)) return event.preventDefault()
58
+ context.closePopover()
30
59
  }
31
- });
32
- const { visible, data: transitionData } = $derived(_transition);
33
- const handleClick = (event) => {
34
- context.closePopover();
35
- };
36
- const slot = $derived({
37
- open: popoverState === PopoverStates.Open
38
- });
39
- const ourProps = $derived({
40
- id,
41
- "aria-hidden": true,
42
- onclick: handleClick,
43
- ...transitionDataAttributes(transitionData)
44
- });
60
+
61
+ const slot = $derived({
62
+ open: popoverState === PopoverStates.Open,
63
+ } satisfies BackdropRenderPropArg)
64
+
65
+ const ourProps = $derived({
66
+ id,
67
+ "aria-hidden": true,
68
+ onclick: handleClick,
69
+ ...transitionDataAttributes(transitionData),
70
+ })
45
71
  </script>
46
72
 
47
73
  <ElementOrComponent
@@ -13,11 +13,14 @@ export type PopoverOverlayProps<TTag extends ElementType = typeof DEFAULT_BACKDR
13
13
  declare class __sveltets_Render<TTag extends ElementType = typeof DEFAULT_BACKDROP_TAG> {
14
14
  props(): {
15
15
  as?: TTag | undefined;
16
- } & (Exclude<keyof PropsOf<TTag>, ("as" | "children" | "refName" | "class") | "unmount" | "static" | "aria-hidden" | "transition"> extends infer T extends keyof PropsOf<TTag> ? { [P in T]: PropsOf<TTag>[P]; } : never) & {
17
- children?: import("svelte").Snippet<[BackdropRenderPropArg, Record<string, any>]> | undefined;
16
+ } & (Exclude<keyof PropsOf<TTag>, ("as" | "children" | "class") | "unmount" | "static" | "aria-hidden" | "transition"> extends infer T extends keyof PropsOf<TTag> ? { [P in T]: PropsOf<TTag>[P]; } : never) & {
17
+ children?: import("svelte").Snippet<[{
18
+ slot: BackdropRenderPropArg;
19
+ props: Record<string, any>;
20
+ }]> | undefined;
18
21
  ref?: HTMLElement;
19
- } & (true extends (PropsOf<TTag> extends infer T_1 ? T_1 extends PropsOf<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) ? {
20
- class?: PropsOf<TTag>["class"] | ((bag: BackdropRenderPropArg) => string) | undefined;
22
+ } & (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) ? {
23
+ class?: string | ((bag: BackdropRenderPropArg) => string) | null | undefined;
21
24
  } : {}) & {
22
25
  transition?: boolean;
23
26
  } & {
@@ -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