@pzerelles/headlessui-svelte 2.1.2-next.4 → 2.1.2-next.41

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