@pzerelles/headlessui-svelte 2.1.2-next.6 → 2.1.2-next.61

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 (270) hide show
  1. package/dist/button/Button.svelte +16 -19
  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 +31 -27
  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 +29 -24
  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 +25 -30
  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 +30 -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/InternalDialog.svelte +38 -34
  30. package/dist/dialog/InternalDialog.svelte.d.ts +3 -41
  31. package/dist/dialog/context.svelte.js +2 -2
  32. package/dist/dialog/index.d.ts +4 -4
  33. package/dist/dialog/index.js +4 -4
  34. package/dist/disclosure/Disclosure.svelte +61 -0
  35. package/dist/disclosure/Disclosure.svelte.d.ts +14 -0
  36. package/dist/disclosure/DisclosureButton.svelte +191 -0
  37. package/dist/disclosure/DisclosureButton.svelte.d.ts +19 -0
  38. package/dist/disclosure/DisclosurePanel.svelte +99 -0
  39. package/dist/disclosure/DisclosurePanel.svelte.d.ts +16 -0
  40. package/dist/disclosure/context.svelte.d.ts +32 -0
  41. package/dist/disclosure/context.svelte.js +94 -0
  42. package/dist/disclosure/index.d.ts +3 -0
  43. package/dist/disclosure/index.js +3 -0
  44. package/dist/field/Field.svelte +27 -26
  45. package/dist/field/Field.svelte.d.ts +7 -34
  46. package/dist/field/index.d.ts +1 -1
  47. package/dist/fieldset/Fieldset.svelte +21 -20
  48. package/dist/fieldset/Fieldset.svelte.d.ts +8 -35
  49. package/dist/fieldset/index.d.ts +1 -1
  50. package/dist/focus-trap/FocusTrap.svelte +29 -36
  51. package/dist/focus-trap/FocusTrap.svelte.d.ts +8 -38
  52. package/dist/hooks/use-controllable.svelte.js +3 -2
  53. package/dist/hooks/use-did-element-move.svelte.js +5 -10
  54. package/dist/hooks/use-disabled.d.ts +6 -1
  55. package/dist/hooks/use-disabled.js +10 -5
  56. package/dist/hooks/use-element-size.svelte.js +1 -1
  57. package/dist/hooks/use-event-listener.svelte.d.ts +1 -1
  58. package/dist/hooks/use-event-listener.svelte.js +3 -1
  59. package/dist/hooks/use-focus-ring.svelte.js +1 -1
  60. package/dist/hooks/use-inert-others.svelte.js +10 -10
  61. package/dist/hooks/use-is-top-layer.svelte.js +2 -2
  62. package/dist/hooks/use-resolve-button-type.svelte.js +0 -1
  63. package/dist/hooks/use-root-containers.svelte.d.ts +2 -2
  64. package/dist/hooks/use-root-containers.svelte.js +7 -6
  65. package/dist/hooks/use-tab-direction.svelte.js +1 -1
  66. package/dist/hooks/use-transition.svelte.d.ts +1 -0
  67. package/dist/hooks/use-transition.svelte.js +32 -7
  68. package/dist/index.d.ts +11 -2
  69. package/dist/index.js +11 -2
  70. package/dist/input/Input.svelte +28 -21
  71. package/dist/input/Input.svelte.d.ts +16 -33
  72. package/dist/input/index.d.ts +1 -1
  73. package/dist/input/index.js +1 -1
  74. package/dist/internal/FloatingProvider.svelte +17 -0
  75. package/dist/internal/FloatingProvider.svelte.d.ts +8 -0
  76. package/dist/internal/FocusSentinel.svelte +33 -32
  77. package/dist/internal/FocusSentinel.svelte.d.ts +4 -18
  78. package/dist/internal/ForcePortalRoot.svelte.d.ts +4 -18
  79. package/dist/internal/FormFields.svelte +18 -13
  80. package/dist/internal/FormFields.svelte.d.ts +4 -18
  81. package/dist/internal/FormFieldsProvider.svelte +17 -0
  82. package/dist/internal/FormFieldsProvider.svelte.d.ts +7 -0
  83. package/dist/internal/FormResolver.svelte +6 -2
  84. package/dist/internal/FormResolver.svelte.d.ts +4 -18
  85. package/dist/internal/Hidden.svelte +10 -10
  86. package/dist/internal/Hidden.svelte.d.ts +6 -33
  87. package/dist/internal/MainTreeProvider.svelte +2 -2
  88. package/dist/internal/MainTreeProvider.svelte.d.ts +4 -18
  89. package/dist/internal/Portal.svelte.d.ts +4 -18
  90. package/dist/internal/floating-provider.svelte.d.ts +3 -0
  91. package/dist/internal/floating-provider.svelte.js +206 -0
  92. package/dist/internal/floating.svelte.d.ts +47 -23
  93. package/dist/internal/floating.svelte.js +90 -272
  94. package/dist/internal/form-fields.svelte.d.ts +10 -0
  95. package/dist/internal/form-fields.svelte.js +23 -0
  96. package/dist/internal/frozen.svelte.js +1 -1
  97. package/dist/label/Label.svelte +15 -11
  98. package/dist/label/Label.svelte.d.ts +8 -33
  99. package/dist/label/context.svelte.js +1 -1
  100. package/dist/label/index.d.ts +1 -1
  101. package/dist/legend/Legend.svelte +22 -15
  102. package/dist/legend/Legend.svelte.d.ts +10 -34
  103. package/dist/listbox/Listbox.svelte +79 -151
  104. package/dist/listbox/Listbox.svelte.d.ts +16 -91
  105. package/dist/listbox/ListboxButton.svelte +31 -29
  106. package/dist/listbox/ListboxButton.svelte.d.ts +8 -38
  107. package/dist/listbox/ListboxOption.svelte +40 -27
  108. package/dist/listbox/ListboxOption.svelte.d.ts +16 -32
  109. package/dist/listbox/ListboxOptions.svelte +126 -72
  110. package/dist/listbox/ListboxOptions.svelte.d.ts +8 -43
  111. package/dist/listbox/ListboxSelectedOption.svelte +24 -26
  112. package/dist/listbox/ListboxSelectedOption.svelte.d.ts +14 -39
  113. package/dist/listbox/context.svelte.d.ts +76 -0
  114. package/dist/listbox/context.svelte.js +36 -0
  115. package/dist/listbox/index.d.ts +5 -5
  116. package/dist/listbox/index.js +4 -4
  117. package/dist/menu/Menu.svelte +22 -266
  118. package/dist/menu/Menu.svelte.d.ts +7 -37
  119. package/dist/menu/MenuButton.svelte +29 -24
  120. package/dist/menu/MenuButton.svelte.d.ts +8 -39
  121. package/dist/menu/MenuHeading.svelte +12 -16
  122. package/dist/menu/MenuHeading.svelte.d.ts +7 -36
  123. package/dist/menu/MenuItem.svelte +18 -23
  124. package/dist/menu/MenuItem.svelte.d.ts +9 -39
  125. package/dist/menu/MenuItems.svelte +33 -34
  126. package/dist/menu/MenuItems.svelte.d.ts +8 -43
  127. package/dist/menu/MenuSection.svelte +9 -12
  128. package/dist/menu/MenuSection.svelte.d.ts +7 -33
  129. package/dist/menu/MenuSeparator.svelte +9 -12
  130. package/dist/menu/MenuSeparator.svelte.d.ts +7 -33
  131. package/dist/menu/context.svelte.d.ts +2 -1
  132. package/dist/menu/context.svelte.js +212 -2
  133. package/dist/menu/index.d.ts +7 -7
  134. package/dist/menu/index.js +3 -3
  135. package/dist/popover/Popover.svelte +231 -0
  136. package/dist/popover/Popover.svelte.d.ts +15 -0
  137. package/dist/popover/PopoverBackdrop.svelte +83 -0
  138. package/dist/popover/PopoverBackdrop.svelte.d.ts +17 -0
  139. package/dist/popover/PopoverButton.svelte +324 -0
  140. package/dist/popover/PopoverButton.svelte.d.ts +21 -0
  141. package/dist/popover/PopoverGroup.svelte +66 -0
  142. package/dist/popover/PopoverGroup.svelte.d.ts +9 -0
  143. package/dist/popover/PopoverPanel.svelte +359 -0
  144. package/dist/popover/PopoverPanel.svelte.d.ts +22 -0
  145. package/dist/popover/context.svelte.d.ts +51 -0
  146. package/dist/popover/context.svelte.js +108 -0
  147. package/dist/popover/index.d.ts +5 -0
  148. package/dist/popover/index.js +5 -0
  149. package/dist/portal/InternalPortal.svelte +18 -19
  150. package/dist/portal/InternalPortal.svelte.d.ts +7 -34
  151. package/dist/portal/Portal.svelte +7 -6
  152. package/dist/portal/Portal.svelte.d.ts +3 -22
  153. package/dist/portal/PortalGroup.svelte +6 -14
  154. package/dist/portal/PortalGroup.svelte.d.ts +5 -34
  155. package/dist/portal/PortalWrapper.svelte +10 -0
  156. package/dist/portal/PortalWrapper.svelte.d.ts +9 -0
  157. package/dist/radio-group/Radio.svelte +142 -0
  158. package/dist/radio-group/Radio.svelte.d.ts +35 -0
  159. package/dist/radio-group/RadioGroup.svelte +222 -0
  160. package/dist/radio-group/RadioGroup.svelte.d.ts +34 -0
  161. package/dist/radio-group/RadioOption.svelte +145 -0
  162. package/dist/radio-group/RadioOption.svelte.d.ts +37 -0
  163. package/dist/radio-group/contest.svelte.d.ts +30 -0
  164. package/dist/radio-group/contest.svelte.js +40 -0
  165. package/dist/radio-group/index.d.ts +3 -0
  166. package/dist/radio-group/index.js +3 -0
  167. package/dist/select/Select.svelte +112 -0
  168. package/dist/select/Select.svelte.d.ts +21 -0
  169. package/dist/select/index.d.ts +1 -0
  170. package/dist/select/index.js +1 -0
  171. package/dist/switch/Switch.svelte +27 -28
  172. package/dist/switch/Switch.svelte.d.ts +9 -42
  173. package/dist/switch/SwitchGroup.svelte +5 -5
  174. package/dist/switch/SwitchGroup.svelte.d.ts +8 -30
  175. package/dist/switch/index.d.ts +1 -1
  176. package/dist/switch/index.js +1 -1
  177. package/dist/tabs/Tab.svelte +28 -31
  178. package/dist/tabs/Tab.svelte.d.ts +8 -36
  179. package/dist/tabs/TabGroup.svelte +42 -264
  180. package/dist/tabs/TabGroup.svelte.d.ts +7 -57
  181. package/dist/tabs/TabList.svelte +13 -16
  182. package/dist/tabs/TabList.svelte.d.ts +8 -31
  183. package/dist/tabs/TabPanel.svelte +20 -20
  184. package/dist/tabs/TabPanel.svelte.d.ts +8 -38
  185. package/dist/tabs/TabPanels.svelte +11 -9
  186. package/dist/tabs/TabPanels.svelte.d.ts +8 -30
  187. package/dist/tabs/context.svelte.d.ts +31 -0
  188. package/dist/tabs/context.svelte.js +134 -0
  189. package/dist/tabs/index.d.ts +5 -5
  190. package/dist/tabs/index.js +4 -4
  191. package/dist/textarea/Textarea.svelte +24 -20
  192. package/dist/textarea/Textarea.svelte.d.ts +17 -29
  193. package/dist/textarea/index.d.ts +1 -1
  194. package/dist/textarea/index.js +1 -1
  195. package/dist/transition/InternalTransitionChild.svelte +36 -22
  196. package/dist/transition/InternalTransitionChild.svelte.d.ts +6 -37
  197. package/dist/transition/Transition.svelte +16 -17
  198. package/dist/transition/Transition.svelte.d.ts +8 -38
  199. package/dist/transition/TransitionChild.svelte +13 -12
  200. package/dist/transition/TransitionChild.svelte.d.ts +11 -38
  201. package/dist/transition/context.svelte.js +11 -11
  202. package/dist/transition/index.d.ts +2 -2
  203. package/dist/transition/index.js +2 -2
  204. package/dist/utils/DisabledProvider.svelte +10 -0
  205. package/dist/utils/DisabledProvider.svelte.d.ts +8 -0
  206. package/dist/utils/ElementOrComponent.svelte +58 -17
  207. package/dist/utils/ElementOrComponent.svelte.d.ts +19 -30
  208. package/dist/utils/StableCollection.svelte.d.ts +4 -18
  209. package/dist/utils/floating-ui/svelte/components/FloatingNode.svelte +32 -0
  210. package/dist/utils/floating-ui/svelte/components/FloatingNode.svelte.d.ts +8 -0
  211. package/dist/utils/floating-ui/svelte/components/FloatingTree.svelte +94 -0
  212. package/dist/utils/floating-ui/svelte/components/FloatingTree.svelte.d.ts +26 -0
  213. package/dist/utils/floating-ui/svelte/hooks/useFloating.svelte.d.ts +6 -0
  214. package/dist/utils/floating-ui/svelte/hooks/useFloating.svelte.js +158 -0
  215. package/dist/utils/floating-ui/svelte/hooks/useFloatingRootContext.svelte.d.ts +11 -0
  216. package/dist/utils/floating-ui/svelte/hooks/useFloatingRootContext.svelte.js +53 -0
  217. package/dist/utils/floating-ui/svelte/hooks/useId.svelte.d.ts +9 -0
  218. package/dist/utils/floating-ui/svelte/hooks/useId.svelte.js +28 -0
  219. package/dist/utils/floating-ui/svelte/hooks/useInteractions.svelte.d.ts +23 -0
  220. package/dist/utils/floating-ui/svelte/hooks/useInteractions.svelte.js +72 -0
  221. package/dist/utils/floating-ui/svelte/index.d.ts +5 -0
  222. package/dist/utils/floating-ui/svelte/index.js +5 -0
  223. package/dist/utils/floating-ui/svelte/inner.svelte.d.ts +83 -0
  224. package/dist/utils/floating-ui/svelte/inner.svelte.js +178 -0
  225. package/dist/utils/floating-ui/svelte/types.d.ts +114 -0
  226. package/dist/utils/floating-ui/svelte/utils/createPubSub.d.ts +5 -0
  227. package/dist/utils/floating-ui/svelte/utils/createPubSub.js +14 -0
  228. package/dist/utils/floating-ui/svelte/utils/getFloatingFocusElement.d.ts +2 -0
  229. package/dist/utils/floating-ui/svelte/utils/getFloatingFocusElement.js +13 -0
  230. package/dist/utils/floating-ui/svelte/utils/log.d.ts +2 -0
  231. package/dist/utils/floating-ui/svelte/utils/log.js +19 -0
  232. package/dist/utils/floating-ui/svelte/utils.d.ts +19 -0
  233. package/dist/utils/floating-ui/svelte/utils.js +136 -0
  234. package/dist/utils/floating-ui/svelte-dom/arrow.d.ts +22 -0
  235. package/dist/utils/floating-ui/svelte-dom/arrow.js +29 -0
  236. package/dist/utils/floating-ui/svelte-dom/index.d.ts +2 -0
  237. package/dist/utils/floating-ui/svelte-dom/index.js +2 -0
  238. package/dist/utils/floating-ui/svelte-dom/types.d.ts +80 -0
  239. package/dist/utils/floating-ui/svelte-dom/types.js +3 -0
  240. package/dist/utils/floating-ui/svelte-dom/useFloating.svelte.d.ts +6 -0
  241. package/dist/utils/floating-ui/svelte-dom/useFloating.svelte.js +183 -0
  242. package/dist/utils/floating-ui/svelte-dom/utils/deepEqual.d.ts +1 -0
  243. package/dist/utils/floating-ui/svelte-dom/utils/deepEqual.js +50 -0
  244. package/dist/utils/floating-ui/svelte-dom/utils/getDPR.d.ts +1 -0
  245. package/dist/utils/floating-ui/svelte-dom/utils/getDPR.js +7 -0
  246. package/dist/utils/floating-ui/svelte-dom/utils/roundByDPR.d.ts +1 -0
  247. package/dist/utils/floating-ui/svelte-dom/utils/roundByDPR.js +5 -0
  248. package/dist/utils/floating-ui/svelte-dom/utils/useLatestRef.d.ts +4 -0
  249. package/dist/utils/floating-ui/svelte-dom/utils/useLatestRef.js +7 -0
  250. package/dist/utils/id.d.ts +1 -1
  251. package/dist/utils/id.js +1 -1
  252. package/dist/utils/index.d.ts +3 -0
  253. package/dist/utils/index.js +3 -0
  254. package/dist/utils/state.js +4 -4
  255. package/dist/utils/style.d.ts +2 -0
  256. package/dist/utils/style.js +6 -0
  257. package/dist/utils/types.d.ts +13 -19
  258. package/package.json +55 -54
  259. package/dist/combobox/Combobox.svelte +0 -53
  260. package/dist/combobox/Combobox.svelte.d.ts +0 -50
  261. package/dist/internal/HoistFormFields.svelte +0 -14
  262. package/dist/internal/HoistFormFields.svelte.d.ts +0 -21
  263. package/dist/internal/id.d.ts +0 -8
  264. package/dist/internal/id.js +0 -11
  265. package/dist/listbox/ListboxStates.d.ts +0 -12
  266. package/dist/listbox/ListboxStates.js +0 -15
  267. package/dist/utils/Generic.svelte +0 -56
  268. package/dist/utils/Generic.svelte.d.ts +0 -35
  269. package/dist/utils/alternative-types.d.ts +0 -21
  270. /package/dist/utils/{alternative-types.js → floating-ui/svelte/types.js} +0 -0
@@ -0,0 +1,359 @@
1
+ <script lang="ts" module>
2
+ import type { Props } from "../utils/types.js"
3
+ import { RenderFeatures } from "../utils/render.js"
4
+
5
+ const DEFAULT_PANEL_TAG = "div" as const
6
+ export type PanelRenderPropArg = {
7
+ open: boolean
8
+ close: (focusableElement?: HTMLElement) => void
9
+ }
10
+
11
+ const PanelRenderFeatures = RenderFeatures.RenderStrategy | RenderFeatures.Static
12
+
13
+ type PanelPropsWeControl = "tabIndex"
14
+
15
+ export type PopoverPanelOwnProps = {
16
+ element?: HTMLElement
17
+ id?: string
18
+ focus?: boolean
19
+ anchor?: AnchorProps
20
+ portal?: boolean
21
+ modal?: boolean
22
+ transition?: boolean
23
+
24
+ // ItemsRenderFeatures
25
+ static?: boolean
26
+ unmount?: boolean
27
+ }
28
+
29
+ export type PopoverPanelProps = Props<typeof DEFAULT_PANEL_TAG, PanelRenderPropArg, PopoverPanelOwnProps>
30
+ </script>
31
+
32
+ <script lang="ts">
33
+ import { useId } from "../hooks/use-id.js"
34
+ import ElementOrComponent from "../utils/ElementOrComponent.svelte"
35
+ import { mergeProps } from "../utils/render.js"
36
+ import {
37
+ useFloatingPanel,
38
+ useFloatingPanelProps,
39
+ useResolvedAnchor,
40
+ type AnchorProps,
41
+ } from "../internal/floating.svelte.js"
42
+ import {
43
+ type PopoverAPIContext,
44
+ type PopoverPanelContext,
45
+ PopoverStates,
46
+ usePopoverAPIContext,
47
+ usePopoverContext,
48
+ } from "./context.svelte.js"
49
+ import { getOwnerDocument } from "../utils/owner.js"
50
+ import { clearOpenClosedContext, State, useOpenClosed } from "../internal/open-closed.js"
51
+ import { transitionDataAttributes, useTransition } from "../hooks/use-transition.svelte.js"
52
+ import { useOnDisappear } from "../hooks/use-on-disappear.svelte.js"
53
+ import { useScrollLock } from "../hooks/use-scroll-lock.svelte.js"
54
+ import { Focus, focusIn, FocusResult, getFocusableElements } from "../utils/focus-management.js"
55
+ import { useElementSize } from "../hooks/use-element-size.svelte.js"
56
+ import { useTabDirection, Direction as TabDirection } from "../hooks/use-tab-direction.svelte.js"
57
+ import { match } from "../utils/match.js"
58
+ import { microTask } from "../utils/microTask.js"
59
+ import { setContext, untrack } from "svelte"
60
+ import Portal from "../portal/Portal.svelte"
61
+ import Hidden, { HiddenFeatures } from "../internal/Hidden.svelte"
62
+
63
+ let internalId = useId()
64
+ let {
65
+ element = $bindable(),
66
+ id = `headlessui-popover-panel-${internalId}`,
67
+ focus = false,
68
+ anchor: rawAnchor,
69
+ portal: theirPortal = false,
70
+ modal = false,
71
+ transition = false,
72
+ ...theirProps
73
+ }: PopoverPanelProps = $props()
74
+
75
+ const context = usePopoverContext("PopoverPanel")
76
+ const api = usePopoverAPIContext("PopoverPanel")
77
+ const { close, isPortalled } = $derived(api)
78
+
79
+ const beforePanelSentinelId = `headlessui-focus-sentinel-before-${internalId}`
80
+ const afterPanelSentinelId = `headlessui-focus-sentinel-after-${internalId}`
81
+
82
+ const resolvedAnchor = useResolvedAnchor({
83
+ get anchor() {
84
+ return rawAnchor
85
+ },
86
+ })
87
+ const { anchor } = $derived(resolvedAnchor)
88
+ const floatingPanel = useFloatingPanel({
89
+ get placement() {
90
+ return anchor
91
+ },
92
+ })
93
+ const { setFloating, styles } = $derived(floatingPanel)
94
+ const getFloatingPanelProps = useFloatingPanelProps()
95
+
96
+ // Always enable `portal` functionality, when `anchor` is enabled
97
+ const portal = $derived(!!anchor || theirPortal)
98
+
99
+ $effect(() => {
100
+ if (anchor) setFloating(element ?? null)
101
+ untrack(() => context.setPanel(element))
102
+ })
103
+ const ownerDocument = $derived(getOwnerDocument(element))
104
+
105
+ $effect(() => {
106
+ id
107
+ return untrack(() => {
108
+ context.setPanelId(id)
109
+ return () => {
110
+ context.setPanelId(undefined)
111
+ }
112
+ })
113
+ })
114
+
115
+ const usesOpenClosedState = useOpenClosed()
116
+ const _transition = useTransition({
117
+ get enabled() {
118
+ return transition
119
+ },
120
+ get element() {
121
+ return element
122
+ },
123
+ get show() {
124
+ return usesOpenClosedState !== null
125
+ ? (usesOpenClosedState.value & State.Open) === State.Open
126
+ : context.popoverState === PopoverStates.Open
127
+ },
128
+ })
129
+ const { visible, data: transitionData } = $derived(_transition)
130
+
131
+ // Ensure we close the popover as soon as the button becomes hidden
132
+ useOnDisappear({
133
+ get enabled() {
134
+ return visible
135
+ },
136
+ get ref() {
137
+ return context.button
138
+ },
139
+ ondisappear: () => {
140
+ context.closePopover()
141
+ },
142
+ })
143
+
144
+ // Enable scroll locking when the popover is visible, and `modal` is enabled
145
+ const scrollLockEnabled = $derived(context.__demoMode ? false : modal && visible)
146
+ useScrollLock({
147
+ get enabled() {
148
+ return scrollLockEnabled
149
+ },
150
+ get ownerDocument() {
151
+ return ownerDocument
152
+ },
153
+ })
154
+
155
+ const handleKeyDown = (event: KeyboardEvent) => {
156
+ switch (event.key) {
157
+ case "Escape":
158
+ if (context.popoverState !== PopoverStates.Open) return
159
+ if (!element) return
160
+ if (ownerDocument?.activeElement && !element.contains(ownerDocument.activeElement)) {
161
+ return
162
+ }
163
+ event.preventDefault()
164
+ event.stopPropagation()
165
+ context.closePopover()
166
+ context.button?.focus()
167
+ break
168
+ }
169
+ }
170
+
171
+ // Unlink on "unmount" children
172
+ $effect(() => {
173
+ if (theirProps.static) return
174
+
175
+ if (context.popoverState === PopoverStates.Closed && (theirProps.unmount ?? true)) {
176
+ context.setPanel(undefined)
177
+ }
178
+ }) //, [state.popoverState, props.unmount, props.static, dispatch])
179
+
180
+ // Move focus within panel
181
+ $effect(() => {
182
+ if (context.__demoMode) return
183
+ if (!focus) return
184
+ if (context.popoverState !== PopoverStates.Open) return
185
+ if (!element) return
186
+
187
+ const activeElement = ownerDocument?.activeElement as HTMLElement
188
+ if (element.contains(activeElement)) return // Already focused within Dialog
189
+
190
+ focusIn(element, Focus.First)
191
+ }) //, [state.__demoMode, focus, internalPanelRef.current, state.popoverState])
192
+
193
+ const slot = $derived({
194
+ open: context.popoverState === PopoverStates.Open,
195
+ close,
196
+ } satisfies PanelRenderPropArg)
197
+
198
+ const buttonSize = useElementSize({
199
+ get element() {
200
+ return context.button ?? null
201
+ },
202
+ unit: true,
203
+ })
204
+ const ourProps: Record<string, any> = $derived(
205
+ mergeProps(anchor ? getFloatingPanelProps() : {}, {
206
+ id,
207
+ onkeydown: handleKeyDown,
208
+ onblur:
209
+ focus && context.popoverState === PopoverStates.Open
210
+ ? (event: FocusEvent) => {
211
+ let el = event.relatedTarget as HTMLElement
212
+ if (!el) return
213
+ if (!element) return
214
+ if (element.contains(el)) return
215
+
216
+ context.closePopover()
217
+
218
+ if (context.beforePanelSentinel?.contains?.(el) || context.afterPanelSentinel?.contains?.(el)) {
219
+ el.focus({ preventScroll: true })
220
+ }
221
+ }
222
+ : undefined,
223
+ tabIndex: -1,
224
+ style: [theirProps.style, styles, `--button-width: ${buttonSize.width}`].filter(Boolean).join("; "),
225
+ ...transitionDataAttributes(transitionData),
226
+ })
227
+ )
228
+
229
+ const direction = useTabDirection()
230
+ const handleBeforeFocus = () => {
231
+ let el = element as HTMLElement
232
+ if (!el) return
233
+
234
+ function run() {
235
+ match(direction.current, {
236
+ [TabDirection.Forwards]: () => {
237
+ // Try to focus the first thing in the panel. But if that fails (e.g.: there are no
238
+ // focusable elements, then we can move outside of the panel)
239
+ let result = focusIn(el, Focus.First)
240
+ if (result === FocusResult.Error) {
241
+ context.afterPanelSentinel?.focus()
242
+ }
243
+ },
244
+ [TabDirection.Backwards]: () => {
245
+ // Coming from the PopoverPanel (which is portalled to somewhere else). Let's redirect
246
+ // the focus to the PopoverButton again.
247
+ context.button?.focus({ preventScroll: true })
248
+ },
249
+ })
250
+ }
251
+
252
+ // TODO: Cleanup once we are using real browser tests
253
+ if (process.env.NODE_ENV === "test") {
254
+ microTask(run)
255
+ } else {
256
+ run()
257
+ }
258
+ }
259
+
260
+ const handleAfterFocus = () => {
261
+ let el = element as HTMLElement
262
+ if (!el) return
263
+
264
+ function run() {
265
+ match(direction.current, {
266
+ [TabDirection.Forwards]: () => {
267
+ if (!context.button) return
268
+
269
+ const elements = getFocusableElements()
270
+
271
+ const idx = elements.indexOf(context.button)
272
+ const before = elements.slice(0, idx + 1)
273
+ const after = elements.slice(idx + 1)
274
+
275
+ const combined = [...after, ...before]
276
+
277
+ // Ignore sentinel buttons and items inside the panel
278
+ for (const element of combined.slice()) {
279
+ if (element.dataset.headlessuiFocusGuard === "true" || element?.contains(element)) {
280
+ let idx = combined.indexOf(element)
281
+ if (idx !== -1) combined.splice(idx, 1)
282
+ }
283
+ }
284
+
285
+ focusIn(combined, Focus.First, { sorted: false })
286
+ },
287
+ [TabDirection.Backwards]: () => {
288
+ // Try to focus the first thing in the panel. But if that fails (e.g.: there are no
289
+ // focusable elements, then we can move outside of the panel)
290
+ let result = focusIn(el, Focus.Previous)
291
+ if (result === FocusResult.Error) {
292
+ context.button?.focus()
293
+ }
294
+ },
295
+ })
296
+ }
297
+
298
+ // TODO: Cleanup once we are using real browser tests
299
+ if (process.env.NODE_ENV === "test") {
300
+ microTask(run)
301
+ } else {
302
+ run()
303
+ }
304
+ }
305
+
306
+ clearOpenClosedContext()
307
+ setContext<PopoverPanelContext>("PopoverPanelContext", {
308
+ get value() {
309
+ return id
310
+ },
311
+ })
312
+ setContext<PopoverAPIContext>("PopoverAPIContext", {
313
+ get close() {
314
+ return close
315
+ },
316
+ get isPortalled() {
317
+ return isPortalled
318
+ },
319
+ })
320
+ </script>
321
+
322
+ <Portal enabled={portal ? theirProps.static || visible : false}>
323
+ {#if visible && isPortalled}
324
+ <Hidden asChild id={beforePanelSentinelId} features={HiddenFeatures.Focusable}>
325
+ {#snippet children({ props })}
326
+ <button
327
+ {...props}
328
+ type="button"
329
+ data-headlessui-focus-guard
330
+ onfocus={handleBeforeFocus}
331
+ bind:this={context.beforePanelSentinel}>&zwnj;</button
332
+ >
333
+ {/snippet}
334
+ </Hidden>
335
+ {/if}
336
+ <ElementOrComponent
337
+ {ourProps}
338
+ {theirProps}
339
+ slots={slot}
340
+ defaultTag={DEFAULT_PANEL_TAG}
341
+ features={PanelRenderFeatures}
342
+ {visible}
343
+ name="PopoverPanel"
344
+ bind:element
345
+ />
346
+ {#if visible && isPortalled}
347
+ <Hidden asChild id={afterPanelSentinelId} features={HiddenFeatures.Focusable}>
348
+ {#snippet children({ props })}
349
+ <button
350
+ {...props}
351
+ type="button"
352
+ data-headlessui-focus-guard
353
+ onfocus={handleAfterFocus}
354
+ bind:this={context.afterPanelSentinel}>&zwnj;</button
355
+ >
356
+ {/snippet}
357
+ </Hidden>
358
+ {/if}
359
+ </Portal>
@@ -0,0 +1,22 @@
1
+ import type { Props } from "../utils/types.js";
2
+ declare const DEFAULT_PANEL_TAG: "div";
3
+ export type PanelRenderPropArg = {
4
+ open: boolean;
5
+ close: (focusableElement?: HTMLElement) => void;
6
+ };
7
+ export type PopoverPanelOwnProps = {
8
+ element?: HTMLElement;
9
+ id?: string;
10
+ focus?: boolean;
11
+ anchor?: AnchorProps;
12
+ portal?: boolean;
13
+ modal?: boolean;
14
+ transition?: boolean;
15
+ static?: boolean;
16
+ unmount?: boolean;
17
+ };
18
+ export type PopoverPanelProps = Props<typeof DEFAULT_PANEL_TAG, PanelRenderPropArg, PopoverPanelOwnProps>;
19
+ import { type AnchorProps } from "../internal/floating.svelte.js";
20
+ declare const PopoverPanel: import("svelte").Component<PopoverPanelProps, {}, "element">;
21
+ type PopoverPanel = ReturnType<typeof PopoverPanel>;
22
+ export default PopoverPanel;
@@ -0,0 +1,51 @@
1
+ import type { MouseEventHandler } from "svelte/elements";
2
+ export type MouseEvent<T extends EventTarget> = Parameters<MouseEventHandler<T>>[0];
3
+ export declare enum PopoverStates {
4
+ Open = 0,
5
+ Closed = 1
6
+ }
7
+ export interface StateDefinition {
8
+ popoverState: PopoverStates;
9
+ buttons: symbol[];
10
+ button?: HTMLElement;
11
+ buttonId?: string;
12
+ panel?: HTMLElement;
13
+ panelId?: string;
14
+ beforePanelSentinel?: HTMLButtonElement;
15
+ afterPanelSentinel?: HTMLButtonElement;
16
+ afterButtonSentinel?: HTMLButtonElement;
17
+ __demoMode: boolean;
18
+ }
19
+ interface ActionDefinition {
20
+ togglePopover(): void;
21
+ closePopover(): void;
22
+ setButton(button: HTMLElement): void;
23
+ setButtonId(buttonId: string | undefined): void;
24
+ setPanel(panel?: HTMLElement): void;
25
+ setPanelId(panelId?: string): void;
26
+ }
27
+ export type PopoverContext = StateDefinition & ActionDefinition;
28
+ export declare const createPopoverContext: (initialState: StateDefinition) => PopoverContext;
29
+ export declare function usePopoverContext(component: string): PopoverContext;
30
+ export type PopoverAPIContext = {
31
+ close(focusableElement?: HTMLElement | MouseEvent<HTMLElement>): void;
32
+ isPortalled: boolean;
33
+ };
34
+ export declare function usePopoverAPIContext(component: string): PopoverAPIContext;
35
+ export type PopoverGroupContext = {
36
+ registerPopover(registerBag: PopoverRegisterBag): void;
37
+ unregisterPopover(registerBag: PopoverRegisterBag): void;
38
+ isFocusWithinPopoverGroup(): boolean;
39
+ closeOthers(buttonId: string): void;
40
+ };
41
+ export declare function usePopoverGroupContext(): PopoverGroupContext | undefined;
42
+ export type PopoverPanelContext = {
43
+ value: string;
44
+ };
45
+ export declare function usePopoverPanelContext(): PopoverPanelContext | undefined;
46
+ export interface PopoverRegisterBag {
47
+ buttonId?: string;
48
+ panelId?: string;
49
+ close(): void;
50
+ }
51
+ export {};
@@ -0,0 +1,108 @@
1
+ import { getContext, setContext } from "svelte";
2
+ export var PopoverStates;
3
+ (function (PopoverStates) {
4
+ PopoverStates[PopoverStates["Open"] = 0] = "Open";
5
+ PopoverStates[PopoverStates["Closed"] = 1] = "Closed";
6
+ })(PopoverStates || (PopoverStates = {}));
7
+ export const createPopoverContext = (initialState) => {
8
+ const _state = $state(initialState);
9
+ const context = {
10
+ get popoverState() {
11
+ return _state.popoverState;
12
+ },
13
+ get buttons() {
14
+ return _state.buttons;
15
+ },
16
+ get button() {
17
+ return _state.button;
18
+ },
19
+ get buttonId() {
20
+ return _state.buttonId;
21
+ },
22
+ get panel() {
23
+ return _state.panel;
24
+ },
25
+ get panelId() {
26
+ return _state.panelId;
27
+ },
28
+ get beforePanelSentinel() {
29
+ return _state.beforePanelSentinel;
30
+ },
31
+ set beforePanelSentinel(value) {
32
+ _state.beforePanelSentinel = value;
33
+ },
34
+ get afterPanelSentinel() {
35
+ return _state.afterPanelSentinel;
36
+ },
37
+ set afterPanelSentinel(value) {
38
+ _state.afterPanelSentinel = value;
39
+ },
40
+ get afterButtonSentinel() {
41
+ return _state.afterButtonSentinel;
42
+ },
43
+ set afterButtonSentinel(value) {
44
+ _state.afterButtonSentinel = value;
45
+ },
46
+ get __demoMode() {
47
+ return _state.__demoMode;
48
+ },
49
+ togglePopover() {
50
+ _state.__demoMode = false;
51
+ _state.popoverState = _state.popoverState === PopoverStates.Closed ? PopoverStates.Open : PopoverStates.Closed;
52
+ },
53
+ closePopover() {
54
+ if (_state.popoverState === PopoverStates.Closed)
55
+ return;
56
+ _state.__demoMode = false;
57
+ _state.popoverState = PopoverStates.Closed;
58
+ },
59
+ setButton(button) {
60
+ if (_state.button === button)
61
+ return;
62
+ _state.button = button;
63
+ },
64
+ setButtonId(buttonId) {
65
+ if (_state.buttonId === buttonId)
66
+ return;
67
+ _state.buttonId = buttonId;
68
+ },
69
+ setPanel(panel) {
70
+ if (_state.panel === panel)
71
+ return;
72
+ _state.panel = panel;
73
+ },
74
+ setPanelId(panelId) {
75
+ if (_state.panelId === panelId)
76
+ return;
77
+ _state.panelId = panelId;
78
+ },
79
+ };
80
+ setContext("PopoverContext", context);
81
+ return context;
82
+ };
83
+ export function usePopoverContext(component) {
84
+ const context = getContext("PopoverContext");
85
+ if (!context) {
86
+ const err = new Error(`<${component} /> is missing a parent <Popover /> component.`);
87
+ if (Error.captureStackTrace)
88
+ Error.captureStackTrace(err, usePopoverContext);
89
+ throw err;
90
+ }
91
+ return context;
92
+ }
93
+ export function usePopoverAPIContext(component) {
94
+ const context = getContext("PopoverAPIContext");
95
+ if (!context) {
96
+ const err = new Error(`<${component} /> is missing a parent <Popover /> component.`);
97
+ if (Error.captureStackTrace)
98
+ Error.captureStackTrace(err, usePopoverAPIContext);
99
+ throw err;
100
+ }
101
+ return context;
102
+ }
103
+ export function usePopoverGroupContext() {
104
+ return getContext("PopoverGroupContext");
105
+ }
106
+ export function usePopoverPanelContext() {
107
+ return getContext("PopoverPanelContext");
108
+ }
@@ -0,0 +1,5 @@
1
+ export { default as Popover, type PopoverProps, type PopoverRenderPropArg as PopoverSlot, type PopoverOwnProps, } from "./Popover.svelte";
2
+ export { default as PopoverBackdrop, type PopoverBackdropProps, type BackdropRenderPropArg as PopoverBackdropSlot, type PopoverBackdropOwnProps, } from "./PopoverBackdrop.svelte";
3
+ export { default as PopoverButton, type PopoverButtonProps, type PopoverButtonSlot, type PopoverButtonOwnProps, } from "./PopoverButton.svelte";
4
+ export { default as PopoverGroup, type PopoverGroupProps, type PopoverGroupOwnProps } from "./PopoverGroup.svelte";
5
+ export { default as PopoverPanel, type PopoverPanelProps, type PanelRenderPropArg as PopoverPanelSlot, type PopoverPanelOwnProps, } from "./PopoverPanel.svelte";
@@ -0,0 +1,5 @@
1
+ export { default as Popover, } from "./Popover.svelte";
2
+ export { default as PopoverBackdrop, } from "./PopoverBackdrop.svelte";
3
+ export { default as PopoverButton, } from "./PopoverButton.svelte";
4
+ export { default as PopoverGroup } from "./PopoverGroup.svelte";
5
+ export { default as PopoverPanel, } from "./PopoverPanel.svelte";
@@ -3,7 +3,7 @@
3
3
  import { getOwnerDocument } from "../utils/owner.js"
4
4
  import { getContext, onMount, setContext } from "svelte"
5
5
  import { env } from "../utils/env.js"
6
- import type { ElementType, Props } from "../utils/types.js"
6
+ import type { Props } from "../utils/types.js"
7
7
  import type { PortalGroupContext } from "./PortalGroup.svelte"
8
8
 
9
9
  function usePortalTarget(options: { element: HTMLElement | null }): { readonly target: HTMLElement | null } {
@@ -56,7 +56,7 @@
56
56
 
57
57
  // ---
58
58
 
59
- type PortalParentContext = {
59
+ export type PortalParentContext = {
60
60
  register: (portal: HTMLElement) => () => void
61
61
  unregister: (portal: HTMLElement) => void
62
62
  readonly portals: HTMLElement[]
@@ -85,7 +85,6 @@
85
85
  return portals
86
86
  },
87
87
  }
88
- setContext("PortalParentContext", context)
89
88
 
90
89
  return context
91
90
  }
@@ -93,27 +92,27 @@
93
92
  // ---
94
93
 
95
94
  export const DEFAULT_PORTAL_TAG = "div"
96
- type PortalRenderPropArg = {}
95
+ export type PortalRenderPropArg = {}
97
96
  type PortalPropsWeControl = never
98
97
 
99
- export type PortalProps<TTag extends ElementType = typeof DEFAULT_PORTAL_TAG> = Props<
100
- TTag,
98
+ export type PortalProps = Props<
99
+ typeof DEFAULT_PORTAL_TAG,
101
100
  PortalRenderPropArg,
102
- PortalPropsWeControl,
103
101
  {
102
+ element?: HTMLElement
104
103
  enabled?: boolean
105
104
  }
106
105
  >
107
106
  </script>
108
107
 
109
- <script lang="ts" generics="TTag extends ElementType = typeof DEFAULT_PORTAL_TAG">
108
+ <script lang="ts">
110
109
  import ElementOrComponent from "../utils/ElementOrComponent.svelte"
111
110
 
112
- let { ref = $bindable(), ...theirProps }: { as?: TTag } & PortalProps<TTag> = $props()
111
+ let { element = $bindable(), ...theirProps }: PortalProps = $props()
113
112
 
114
113
  const portalTarget = usePortalTarget({
115
114
  get element() {
116
- return ref ?? null
115
+ return element ?? null
117
116
  },
118
117
  })
119
118
  const { target } = $derived(portalTarget)
@@ -121,24 +120,24 @@
121
120
  //const ready = useServerHandoffComplete()
122
121
 
123
122
  $effect(() => {
124
- if (!target || !ref) return
123
+ if (!target || !element) return
125
124
 
126
125
  // Element already exists in target, always calling target.appendChild(element) will cause a
127
126
  // brief unmount/remount.
128
- if (ref.parentNode !== target) {
129
- ref.setAttribute("data-headlessui-portal", "")
130
- target.appendChild(ref)
127
+ if (element.parentNode !== target) {
128
+ element.setAttribute("data-headlessui-portal", "")
129
+ target.appendChild(element)
131
130
  }
132
131
  })
133
132
 
134
133
  onMount(() => {
135
- if (parent) parent.register(ref!)
134
+ if (parent) parent.register(element!)
136
135
 
137
136
  return () => {
138
- if (!target || !ref) return
137
+ if (!target || !element) return
139
138
 
140
- if (ref instanceof Node && target.contains(ref)) {
141
- target.removeChild(ref)
139
+ if (element instanceof Node && target.contains(element)) {
140
+ target.removeChild(element)
142
141
  }
143
142
 
144
143
  if (target.childNodes.length <= 0) {
@@ -149,5 +148,5 @@
149
148
  </script>
150
149
 
151
150
  {#if target}
152
- <ElementOrComponent {theirProps} defaultTag={DEFAULT_PORTAL_TAG} name="InternalPortal" bind:ref />
151
+ <ElementOrComponent {theirProps} defaultTag={DEFAULT_PORTAL_TAG} name="InternalPortal" bind:element />
153
152
  {/if}