base-ui-vue 0.3.0 → 0.5.0

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 (218) hide show
  1. package/dist/arrow/TooltipArrow.cjs +609 -0
  2. package/dist/arrow/TooltipArrow.cjs.map +1 -0
  3. package/dist/arrow/TooltipArrow.js +478 -0
  4. package/dist/arrow/TooltipArrow.js.map +1 -0
  5. package/dist/button/Button.cjs +5 -317
  6. package/dist/button/Button.cjs.map +1 -1
  7. package/dist/button/Button.js +3 -249
  8. package/dist/button/Button.js.map +1 -1
  9. package/dist/button/ToolbarButton.cjs +19 -6
  10. package/dist/button/ToolbarButton.cjs.map +1 -1
  11. package/dist/button/ToolbarButton.js +3 -2
  12. package/dist/button/ToolbarButton.js.map +1 -1
  13. package/dist/checkbox/index.cjs +20 -9
  14. package/dist/checkbox/index.cjs.map +1 -1
  15. package/dist/checkbox/index.js +7 -2
  16. package/dist/checkbox/index.js.map +1 -1
  17. package/dist/checkbox-group/CheckboxGroup.cjs +14 -13
  18. package/dist/checkbox-group/CheckboxGroup.cjs.map +1 -1
  19. package/dist/checkbox-group/CheckboxGroup.js +2 -1
  20. package/dist/checkbox-group/CheckboxGroup.js.map +1 -1
  21. package/dist/components/FloatingPortal.cjs +66 -0
  22. package/dist/components/FloatingPortal.cjs.map +1 -0
  23. package/dist/components/FloatingPortal.js +55 -0
  24. package/dist/components/FloatingPortal.js.map +1 -0
  25. package/dist/components/FloatingRootStore.cjs +101 -0
  26. package/dist/components/FloatingRootStore.cjs.map +1 -0
  27. package/dist/components/FloatingRootStore.js +84 -0
  28. package/dist/components/FloatingRootStore.js.map +1 -0
  29. package/dist/components/FloatingTree.cjs +33 -0
  30. package/dist/components/FloatingTree.cjs.map +1 -0
  31. package/dist/components/FloatingTree.js +22 -0
  32. package/dist/components/FloatingTree.js.map +1 -0
  33. package/dist/composite/composite.cjs +6 -0
  34. package/dist/composite/composite.js +1 -1
  35. package/dist/content/ScrollAreaContent.cjs +3 -3
  36. package/dist/content/ScrollAreaContent.cjs.map +1 -1
  37. package/dist/content/ScrollAreaContent.js +1 -1
  38. package/dist/control/FieldControl.cjs +3 -3
  39. package/dist/control/FieldControl.cjs.map +1 -1
  40. package/dist/control/FieldControl.js +1 -1
  41. package/dist/control/SliderControl.cjs +3 -3
  42. package/dist/control/SliderControl.cjs.map +1 -1
  43. package/dist/control/SliderControl.js +3 -3
  44. package/dist/corner/ScrollAreaCorner.cjs +3 -3
  45. package/dist/corner/ScrollAreaCorner.cjs.map +1 -1
  46. package/dist/corner/ScrollAreaCorner.js +1 -1
  47. package/dist/decrement/NumberFieldDecrement.cjs +868 -0
  48. package/dist/decrement/NumberFieldDecrement.cjs.map +1 -0
  49. package/dist/decrement/NumberFieldDecrement.js +701 -0
  50. package/dist/decrement/NumberFieldDecrement.js.map +1 -0
  51. package/dist/description/FieldDescription.cjs +2 -2
  52. package/dist/description/FieldDescription.cjs.map +1 -1
  53. package/dist/description/FieldDescription.js +1 -1
  54. package/dist/error/FieldError.cjs +2 -2
  55. package/dist/error/FieldError.cjs.map +1 -1
  56. package/dist/error/FieldError.js +1 -1
  57. package/dist/fallback/AvatarFallback.cjs +4 -48
  58. package/dist/fallback/AvatarFallback.cjs.map +1 -1
  59. package/dist/fallback/AvatarFallback.js +4 -42
  60. package/dist/fallback/AvatarFallback.js.map +1 -1
  61. package/dist/floating-ui-vue/index.js +638 -0
  62. package/dist/floating-ui-vue/index.js.map +1 -0
  63. package/dist/form/Form.cjs +3 -3
  64. package/dist/form/Form.cjs.map +1 -1
  65. package/dist/form/Form.js +1 -1
  66. package/dist/group/NumberFieldGroup.cjs +72 -0
  67. package/dist/group/NumberFieldGroup.cjs.map +1 -0
  68. package/dist/group/NumberFieldGroup.js +67 -0
  69. package/dist/group/NumberFieldGroup.js.map +1 -0
  70. package/dist/group/ToolbarGroup.cjs +2 -2
  71. package/dist/group/ToolbarGroup.cjs.map +1 -1
  72. package/dist/group/ToolbarGroup.js +1 -1
  73. package/dist/header/AccordionHeader.cjs +2 -2
  74. package/dist/header/AccordionHeader.cjs.map +1 -1
  75. package/dist/header/AccordionHeader.js +1 -1
  76. package/dist/hooks/useDismiss.cjs +502 -0
  77. package/dist/hooks/useDismiss.cjs.map +1 -0
  78. package/dist/hooks/useHoverFloatingInteraction.cjs +233 -0
  79. package/dist/hooks/useHoverFloatingInteraction.cjs.map +1 -0
  80. package/dist/image/AvatarImage.cjs +4 -4
  81. package/dist/image/AvatarImage.cjs.map +1 -1
  82. package/dist/image/AvatarImage.js +1 -1
  83. package/dist/increment/NumberFieldIncrement.cjs +113 -0
  84. package/dist/increment/NumberFieldIncrement.cjs.map +1 -0
  85. package/dist/increment/NumberFieldIncrement.js +108 -0
  86. package/dist/increment/NumberFieldIncrement.js.map +1 -0
  87. package/dist/index.cjs +80 -8
  88. package/dist/index.d.cts +4328 -1600
  89. package/dist/index.d.cts.map +1 -1
  90. package/dist/index.d.ts +4328 -1600
  91. package/dist/index.d.ts.map +1 -1
  92. package/dist/index.js +8 -4
  93. package/dist/index2.cjs +9435 -3236
  94. package/dist/index2.cjs.map +1 -1
  95. package/dist/index2.js +8905 -3049
  96. package/dist/index2.js.map +1 -1
  97. package/dist/rolldown-runtime.js +36 -0
  98. package/package.json +1 -1
  99. package/src/floating-ui-vue/components/FloatingPortal.ts +171 -0
  100. package/src/floating-ui-vue/components/FloatingPortal.vue +259 -0
  101. package/src/floating-ui-vue/components/FloatingRootStore.ts +83 -0
  102. package/src/floating-ui-vue/components/FloatingTree.ts +64 -0
  103. package/src/floating-ui-vue/components/FloatingTreeStore.ts +21 -0
  104. package/src/floating-ui-vue/hooks/useDismiss.ts +689 -0
  105. package/src/floating-ui-vue/hooks/useHoverFloatingInteraction.ts +244 -0
  106. package/src/floating-ui-vue/hooks/useHoverInteractionSharedState.ts +125 -0
  107. package/src/floating-ui-vue/hooks/useHoverShared.ts +53 -0
  108. package/src/floating-ui-vue/index.ts +48 -0
  109. package/src/floating-ui-vue/middleware/arrow.ts +114 -0
  110. package/src/floating-ui-vue/types.ts +117 -0
  111. package/src/floating-ui-vue/utils/composite.ts +20 -0
  112. package/src/floating-ui-vue/utils/constants.ts +2 -0
  113. package/src/floating-ui-vue/utils/createAttribute.ts +3 -0
  114. package/src/floating-ui-vue/utils/createEventEmitter.ts +20 -0
  115. package/src/floating-ui-vue/utils/element.ts +59 -0
  116. package/src/floating-ui-vue/utils/event.ts +15 -0
  117. package/src/floating-ui-vue/utils/nodes.ts +14 -0
  118. package/src/floating-ui-vue/utils/tabbable.ts +309 -0
  119. package/src/index.ts +7 -1
  120. package/src/number-field/decrement/NumberFieldDecrement.vue +109 -0
  121. package/src/number-field/group/NumberFieldGroup.vue +47 -0
  122. package/src/number-field/increment/NumberFieldIncrement.vue +109 -0
  123. package/src/number-field/index.ts +42 -0
  124. package/src/number-field/input/NumberFieldInput.vue +455 -0
  125. package/src/number-field/root/NumberFieldRoot.vue +626 -0
  126. package/src/number-field/root/NumberFieldRootContext.ts +94 -0
  127. package/src/number-field/root/useNumberFieldButton.ts +171 -0
  128. package/src/number-field/scrub-area/NumberFieldScrubArea.vue +359 -0
  129. package/src/number-field/scrub-area/NumberFieldScrubAreaContext.ts +26 -0
  130. package/src/number-field/scrub-area-cursor/NumberFieldScrubAreaCursor.vue +75 -0
  131. package/src/number-field/utils/constants.ts +4 -0
  132. package/src/number-field/utils/getViewportRect.ts +34 -0
  133. package/src/number-field/utils/parse.ts +248 -0
  134. package/src/number-field/utils/stateAttributesMapping.ts +9 -0
  135. package/src/number-field/utils/subscribeToVisualViewportResize.ts +27 -0
  136. package/src/number-field/utils/types.ts +24 -0
  137. package/src/number-field/utils/validate.ts +120 -0
  138. package/src/otp-field/index.ts +22 -0
  139. package/src/otp-field/input/OtpFieldInput.vue +336 -0
  140. package/src/otp-field/root/OtpFieldRoot.vue +583 -0
  141. package/src/otp-field/root/OtpFieldRootContext.ts +81 -0
  142. package/src/otp-field/utils/otp.ts +135 -0
  143. package/src/otp-field/utils/stateAttributesMapping.ts +16 -0
  144. package/src/radio/index.ts +28 -0
  145. package/src/radio/indicator/RadioIndicator.vue +90 -0
  146. package/src/radio/indicator/RadioIndicatorDataAttributes.ts +54 -0
  147. package/src/radio/root/RadioRoot.vue +544 -0
  148. package/src/radio/root/RadioRootContext.ts +18 -0
  149. package/src/radio/root/RadioRootDataAttributes.ts +46 -0
  150. package/src/radio/utils/stateAttributesMapping.ts +29 -0
  151. package/src/radio-group/RadioGroup.vue +381 -0
  152. package/src/radio-group/RadioGroupContext.ts +42 -0
  153. package/src/radio-group/RadioGroupDataAttributes.ts +6 -0
  154. package/src/radio-group/index.ts +10 -0
  155. package/src/tabs/index.ts +38 -0
  156. package/src/tabs/indicator/TabsIndicator.vue +201 -0
  157. package/src/tabs/indicator/TabsIndicatorCssVars.ts +26 -0
  158. package/src/tabs/indicator/TabsIndicatorDataAttributes.ts +12 -0
  159. package/src/tabs/indicator/prehydrationScript.min.ts +5 -0
  160. package/src/tabs/indicator/prehydrationScript.template.js +87 -0
  161. package/src/tabs/list/TabsList.vue +201 -0
  162. package/src/tabs/list/TabsListContext.ts +29 -0
  163. package/src/tabs/list/TabsListDataAttributes.ts +12 -0
  164. package/src/tabs/panel/TabsPanel.vue +167 -0
  165. package/src/tabs/panel/TabsPanelDataAttributes.ts +28 -0
  166. package/src/tabs/root/TabsRoot.vue +468 -0
  167. package/src/tabs/root/TabsRootContext.ts +56 -0
  168. package/src/tabs/root/TabsRootDataAttributes.ts +12 -0
  169. package/src/tabs/root/stateAttributesMapping.ts +9 -0
  170. package/src/tabs/tab/TabsTab.vue +274 -0
  171. package/src/tabs/tab/TabsTabDataAttributes.ts +20 -0
  172. package/src/tabs/utils/areTabValuesEqual.ts +6 -0
  173. package/src/tooltip/arrow/TooltipArrow.vue +116 -0
  174. package/src/tooltip/arrow/TooltipArrowDataAttributes.ts +31 -0
  175. package/src/tooltip/index.ts +54 -0
  176. package/src/tooltip/popup/TooltipPopup.vue +142 -0
  177. package/src/tooltip/popup/TooltipPopupDataAttributes.ts +35 -0
  178. package/src/tooltip/portal/TooltipPortal.vue +83 -0
  179. package/src/tooltip/portal/TooltipPortalContext.ts +19 -0
  180. package/src/tooltip/positioner/TooltipPositioner.vue +221 -0
  181. package/src/tooltip/positioner/TooltipPositionerContext.ts +23 -0
  182. package/src/tooltip/positioner/TooltipPositionerCssVars.ts +27 -0
  183. package/src/tooltip/positioner/TooltipPositionerDataAttributes.ts +26 -0
  184. package/src/tooltip/provider/TooltipProvider.vue +78 -0
  185. package/src/tooltip/provider/TooltipProviderContext.ts +19 -0
  186. package/src/tooltip/root/TooltipRoot.vue +401 -0
  187. package/src/tooltip/root/TooltipRootContext.ts +71 -0
  188. package/src/tooltip/store/TooltipHandle.ts +215 -0
  189. package/src/tooltip/trigger/TooltipTrigger.vue +343 -0
  190. package/src/tooltip/trigger/TooltipTriggerDataAttributes.ts +12 -0
  191. package/src/tooltip/utils/constants.ts +2 -0
  192. package/src/tooltip/viewport/TooltipViewport.vue +149 -0
  193. package/src/tooltip/viewport/TooltipViewportCssVars.ts +14 -0
  194. package/src/tooltip/viewport/TooltipViewportDataAttributes.ts +26 -0
  195. package/src/utils/FloatingPortalLite.vue +66 -0
  196. package/src/utils/FocusGuard.vue +36 -0
  197. package/src/utils/adaptiveOriginMiddleware.ts +79 -0
  198. package/src/utils/addEventListener.ts +16 -0
  199. package/src/utils/constants.ts +11 -0
  200. package/src/utils/createBaseUIEventDetails.ts +2 -0
  201. package/src/utils/detectBrowser.ts +15 -0
  202. package/src/utils/formatNumber.ts +35 -2
  203. package/src/utils/getCssDimensions.ts +19 -0
  204. package/src/utils/getDisabledMountTransitionStyles.ts +9 -0
  205. package/src/utils/hideMiddleware.ts +19 -0
  206. package/src/utils/mergeCleanups.ts +7 -0
  207. package/src/utils/platform.ts +18 -0
  208. package/src/utils/popupStateMapping.ts +103 -0
  209. package/src/utils/reasons.ts +4 -0
  210. package/src/utils/serializeValue.ts +17 -0
  211. package/src/utils/useAnchorPositioning.ts +644 -0
  212. package/src/utils/useInterval.ts +45 -0
  213. package/src/utils/usePopupAutoResize.ts +254 -0
  214. package/src/utils/usePopupViewport.ts +398 -0
  215. package/src/utils/usePositioner.ts +61 -0
  216. package/src/utils/usePressAndHold.ts +260 -0
  217. package/src/utils/useRenderElement.ts +3 -0
  218. package/src/utils/useValueChanged.ts +21 -0
@@ -0,0 +1,689 @@
1
+ import type { MaybeRefOrGetter } from 'vue'
2
+ import type { FloatingTreeStore } from '../components/FloatingTreeStore'
3
+ import type { ElementProps, FloatingContext, FloatingRootContext } from '../types'
4
+ import {
5
+ getComputedStyle,
6
+ getParentNode,
7
+ isElement,
8
+ isHTMLElement,
9
+ isLastTraversableNode,
10
+ isShadowRoot,
11
+ } from '@floating-ui/utils/dom'
12
+ import { onScopeDispose, shallowRef, toValue, watchEffect } from 'vue'
13
+ import { addEventListener } from '../../utils/addEventListener'
14
+ import { createChangeEventDetails } from '../../utils/createBaseUIEventDetails'
15
+ import { mergeCleanups } from '../../utils/mergeCleanups'
16
+ import { ownerDocument } from '../../utils/owner'
17
+ import { platform } from '../../utils/platform'
18
+ import { REASONS } from '../../utils/reasons'
19
+ import { Timeout, useTimeout } from '../../utils/useTimeout'
20
+ import { useFloatingTree } from '../components/FloatingTree'
21
+ import { createAttribute } from '../utils/createAttribute'
22
+ import { contains, getTarget, isEventTargetWithin, isRootElement } from '../utils/element'
23
+ import { getNodeChildren } from '../utils/nodes'
24
+
25
+ type PressType = 'intentional' | 'sloppy'
26
+
27
+ function alwaysFalse() {
28
+ return false
29
+ }
30
+
31
+ export function normalizeProp(
32
+ normalizable?: boolean | { escapeKey?: boolean | undefined, outsidePress?: boolean | undefined },
33
+ ) {
34
+ return {
35
+ escapeKey:
36
+ typeof normalizable === 'boolean' ? normalizable : (normalizable?.escapeKey ?? false),
37
+ outsidePress:
38
+ typeof normalizable === 'boolean' ? normalizable : (normalizable?.outsidePress ?? true),
39
+ }
40
+ }
41
+
42
+ export interface UseDismissProps {
43
+ /**
44
+ * Whether the Hook is enabled, including all internal Effects and event
45
+ * handlers.
46
+ * @default true
47
+ */
48
+ enabled?: MaybeRefOrGetter<boolean | undefined>
49
+ /**
50
+ * Whether to dismiss the floating element upon pressing the `esc` key.
51
+ * @default true
52
+ */
53
+ escapeKey?: MaybeRefOrGetter<boolean | undefined>
54
+ /**
55
+ * Whether to dismiss the floating element upon pressing the reference
56
+ * element.
57
+ *
58
+ * A lazy getter invoked when handling reference press events.
59
+ * @default false
60
+ */
61
+ referencePress?: (() => boolean) | undefined
62
+ /**
63
+ * Whether to dismiss the floating element upon pressing outside of the
64
+ * floating element.
65
+ * @default true
66
+ */
67
+ outsidePress?: boolean | ((event: MouseEvent | TouchEvent) => boolean) | undefined
68
+ /**
69
+ * The type of event to use to determine an outside "press".
70
+ */
71
+ outsidePressEvent?:
72
+ | PressType
73
+ | {
74
+ mouse: PressType
75
+ touch: PressType
76
+ }
77
+ | (() =>
78
+ | PressType
79
+ | {
80
+ mouse: PressType
81
+ touch: PressType
82
+ })
83
+ | undefined
84
+ /**
85
+ * Determines whether event listeners bubble upwards through a tree of
86
+ * floating elements.
87
+ */
88
+ bubbles?:
89
+ | MaybeRefOrGetter<boolean
90
+ | { escapeKey?: boolean | undefined, outsidePress?: boolean | undefined }
91
+ | undefined>
92
+ /**
93
+ * External FloatingTree to use when the one provided by context can't be used.
94
+ */
95
+ externalTree?: FloatingTreeStore | undefined
96
+ }
97
+
98
+ /**
99
+ * Closes the floating element when a dismissal is requested — by default, when
100
+ * the user presses the `escape` key or outside of the floating element.
101
+ * @see https://floating-ui.com/docs/useDismiss
102
+ */
103
+ export function useDismiss(
104
+ context: FloatingRootContext | FloatingContext,
105
+ props: UseDismissProps = {},
106
+ ): ElementProps {
107
+ const store = 'rootStore' in context ? context.rootStore : context
108
+
109
+ const open = store.useState('open')
110
+ const floatingElement = store.useState('floatingElement')
111
+ const domReferenceElement = store.useState('domReferenceElement')
112
+ const { dataRef } = store.context
113
+
114
+ const tree = useFloatingTree(props.externalTree)
115
+
116
+ const pressStartedInsideRef = shallowRef(false)
117
+ const pressStartPreventedRef = shallowRef(false)
118
+ const suppressNextOutsideClickRef = shallowRef(false)
119
+ const isComposingRef = shallowRef(false)
120
+ const currentPointerTypeRef = shallowRef<PointerEvent['pointerType']>('')
121
+
122
+ const touchStateRef = shallowRef<{
123
+ startTime: number
124
+ startX: number
125
+ startY: number
126
+ dismissOnTouchEnd: boolean
127
+ dismissOnMouseDown: boolean
128
+ } | null>(null)
129
+
130
+ const cancelDismissOnEndTimeout = useTimeout()
131
+ const clearInsideVueTreeTimeout = useTimeout()
132
+
133
+ function enabled() {
134
+ return toValue(props.enabled ?? true) !== false
135
+ }
136
+
137
+ function escapeKey() {
138
+ return toValue(props.escapeKey ?? true) !== false
139
+ }
140
+
141
+ function referencePress() {
142
+ return (props.referencePress ?? alwaysFalse)()
143
+ }
144
+
145
+ function outsidePress() {
146
+ return props.outsidePress ?? true
147
+ }
148
+
149
+ function outsidePressEnabled() {
150
+ return outsidePress() !== false
151
+ }
152
+
153
+ function getBubbles() {
154
+ return normalizeProp(toValue(props.bubbles))
155
+ }
156
+
157
+ function clearInsideVueTree() {
158
+ clearInsideVueTreeTimeout.clear()
159
+ dataRef.value.insideVueTree = false
160
+ }
161
+
162
+ function hasBlockingChild(bubbleKey: '__escapeKeyBubbles' | '__outsidePressBubbles') {
163
+ const nodeId = dataRef.value.floatingContext?.nodeId
164
+ const children = tree ? getNodeChildren(tree.nodesRef.value, nodeId) : []
165
+
166
+ return children.some((child) => {
167
+ return child.context?.open.value && !child.context.rootStore.context.dataRef.value[bubbleKey]
168
+ })
169
+ }
170
+
171
+ function isEventWithinOwnElements(event: Event) {
172
+ return (
173
+ isEventTargetWithin(event, store.select('floatingElement'))
174
+ || isEventTargetWithin(event, store.select('domReferenceElement'))
175
+ )
176
+ }
177
+
178
+ function closeOnReferencePress(event: Event) {
179
+ if (!enabled() || !referencePress()) {
180
+ return
181
+ }
182
+
183
+ store.setOpen(
184
+ false,
185
+ createChangeEventDetails(
186
+ REASONS.triggerPress,
187
+ event as MouseEvent | PointerEvent | TouchEvent | KeyboardEvent,
188
+ ),
189
+ )
190
+ }
191
+
192
+ function closeOnEscapeKeyDown(event: KeyboardEvent) {
193
+ if (!open.value || !enabled() || !escapeKey() || event.key !== 'Escape') {
194
+ return
195
+ }
196
+
197
+ if (isComposingRef.value) {
198
+ return
199
+ }
200
+
201
+ const { escapeKey: escapeKeyBubbles } = getBubbles()
202
+
203
+ if (!escapeKeyBubbles && hasBlockingChild('__escapeKeyBubbles')) {
204
+ return
205
+ }
206
+
207
+ const eventDetails = createChangeEventDetails(REASONS.escapeKey, event)
208
+
209
+ store.setOpen(false, eventDetails)
210
+
211
+ if (!eventDetails.isCanceled) {
212
+ event.preventDefault()
213
+ }
214
+
215
+ if (!escapeKeyBubbles && !eventDetails.isPropagationAllowed) {
216
+ event.stopPropagation()
217
+ }
218
+ }
219
+
220
+ function markInsideVueTree() {
221
+ dataRef.value.insideVueTree = true
222
+ clearInsideVueTreeTimeout.start(0, clearInsideVueTree)
223
+ }
224
+
225
+ function markPressStartedInsideVueTree(event: PointerEvent | MouseEvent) {
226
+ if (!open.value || !enabled() || event.button !== 0) {
227
+ return
228
+ }
229
+
230
+ const target = getTarget(event) as Element | null
231
+
232
+ if (!contains(store.select('floatingElement'), target)) {
233
+ return
234
+ }
235
+
236
+ if (!pressStartedInsideRef.value) {
237
+ pressStartedInsideRef.value = true
238
+ pressStartPreventedRef.value = false
239
+ }
240
+ }
241
+
242
+ function markInsidePressStartPrevented(event: PointerEvent | MouseEvent) {
243
+ if (!open.value || !enabled()) {
244
+ return
245
+ }
246
+
247
+ if (!event.defaultPrevented) {
248
+ return
249
+ }
250
+
251
+ if (pressStartedInsideRef.value) {
252
+ pressStartPreventedRef.value = true
253
+ }
254
+ }
255
+
256
+ function resetPressStartState() {
257
+ pressStartedInsideRef.value = false
258
+ pressStartPreventedRef.value = false
259
+ }
260
+
261
+ function getOutsidePressEvent(): PressType {
262
+ const type = currentPointerTypeRef.value as 'pen' | 'mouse' | 'touch' | ''
263
+ const computedType = type === 'pen' || !type ? 'mouse' : type
264
+ const outsidePressEvent = props.outsidePressEvent ?? 'sloppy'
265
+ const resolved = typeof outsidePressEvent === 'function'
266
+ ? outsidePressEvent()
267
+ : outsidePressEvent
268
+
269
+ if (resolved == null) {
270
+ return 'sloppy'
271
+ }
272
+
273
+ if (typeof resolved === 'string') {
274
+ return resolved
275
+ }
276
+
277
+ return resolved[computedType] ?? 'sloppy'
278
+ }
279
+
280
+ function shouldIgnoreEvent(event: Event) {
281
+ const computedOutsidePressEvent = getOutsidePressEvent()
282
+ return (
283
+ (computedOutsidePressEvent === 'intentional' && event.type !== 'click')
284
+ || (computedOutsidePressEvent === 'sloppy' && event.type === 'click')
285
+ )
286
+ }
287
+
288
+ function isEventWithinFloatingTree(event: Event) {
289
+ const nodeId = dataRef.value.floatingContext?.nodeId
290
+ const targetIsInsideChildren
291
+ = tree
292
+ && getNodeChildren(tree.nodesRef.value, nodeId).some(node =>
293
+ isEventTargetWithin(event, node.context?.rootStore.select('floatingElement')),
294
+ )
295
+
296
+ return isEventWithinOwnElements(event) || Boolean(targetIsInsideChildren)
297
+ }
298
+
299
+ function addTargetEventListenerOnce<EventType extends Event>(
300
+ event: EventType,
301
+ listener: (event: EventType) => void,
302
+ ) {
303
+ const target = getTarget(event)
304
+
305
+ if (!target) {
306
+ return
307
+ }
308
+
309
+ const eventTarget = target as EventTarget
310
+ const handler = () => {
311
+ listener(event)
312
+ eventTarget.removeEventListener(event.type, handler)
313
+ }
314
+
315
+ eventTarget.addEventListener(event.type, handler)
316
+ }
317
+
318
+ watchEffect((onCleanup) => {
319
+ if (!open.value || !enabled()) {
320
+ return
321
+ }
322
+
323
+ const { escapeKey: escapeKeyBubbles, outsidePress: outsidePressBubbles } = getBubbles()
324
+ dataRef.value.__escapeKeyBubbles = escapeKeyBubbles
325
+ dataRef.value.__outsidePressBubbles = outsidePressBubbles
326
+
327
+ const compositionTimeout = new Timeout()
328
+ const preventedPressSuppressionTimeout = new Timeout()
329
+
330
+ function handleCompositionStart() {
331
+ compositionTimeout.clear()
332
+ isComposingRef.value = true
333
+ }
334
+
335
+ function handleCompositionEnd() {
336
+ compositionTimeout.start(
337
+ platform.engine.webkit ? 5 : 0,
338
+ () => {
339
+ isComposingRef.value = false
340
+ },
341
+ )
342
+ }
343
+
344
+ function suppressImmediateOutsideClickAfterPreventedStart() {
345
+ suppressNextOutsideClickRef.value = true
346
+ preventedPressSuppressionTimeout.start(0, () => {
347
+ suppressNextOutsideClickRef.value = false
348
+ })
349
+ }
350
+
351
+ function closeOnPressOutside(event: MouseEvent | PointerEvent | TouchEvent) {
352
+ if (shouldIgnoreEvent(event)) {
353
+ if (event.type !== 'click' && !isEventWithinOwnElements(event)) {
354
+ preventedPressSuppressionTimeout.clear()
355
+ suppressNextOutsideClickRef.value = false
356
+ }
357
+ clearInsideVueTree()
358
+ return
359
+ }
360
+
361
+ if (dataRef.value.insideVueTree) {
362
+ clearInsideVueTree()
363
+ return
364
+ }
365
+
366
+ const target = getTarget(event)
367
+ const inertSelector = `[${createAttribute('inert')}]`
368
+ const targetRoot = isElement(target) ? target.getRootNode() : null
369
+ const doc = ownerDocument(store.select('floatingElement'))
370
+ const markers = Array.from(
371
+ (isShadowRoot(targetRoot) ? targetRoot : doc)?.querySelectorAll(inertSelector) ?? [],
372
+ )
373
+
374
+ const triggers = store.context.triggerElements
375
+
376
+ if (
377
+ target
378
+ && (triggers.hasElement(target as Element)
379
+ || triggers.hasMatchingElement(trigger => contains(trigger, target as Element)))
380
+ ) {
381
+ return
382
+ }
383
+
384
+ let targetRootAncestor = isElement(target) ? target : null
385
+ while (targetRootAncestor && !isLastTraversableNode(targetRootAncestor)) {
386
+ const nextParent = getParentNode(targetRootAncestor)
387
+ if (isLastTraversableNode(nextParent) || !isElement(nextParent)) {
388
+ break
389
+ }
390
+
391
+ targetRootAncestor = nextParent
392
+ }
393
+
394
+ if (
395
+ markers.length
396
+ && isElement(target)
397
+ && !isRootElement(target)
398
+ && !contains(target, store.select('floatingElement'))
399
+ && markers.every(marker => !contains(targetRootAncestor, marker))
400
+ ) {
401
+ return
402
+ }
403
+
404
+ if (isHTMLElement(target) && !('touches' in event)) {
405
+ const lastTraversableNode = isLastTraversableNode(target)
406
+ const style = getComputedStyle(target)
407
+ const scrollRe = /auto|scroll/
408
+ const isScrollableX = lastTraversableNode || scrollRe.test(style.overflowX)
409
+ const isScrollableY = lastTraversableNode || scrollRe.test(style.overflowY)
410
+
411
+ const canScrollX
412
+ = isScrollableX && target.clientWidth > 0 && target.scrollWidth > target.clientWidth
413
+ const canScrollY
414
+ = isScrollableY && target.clientHeight > 0 && target.scrollHeight > target.clientHeight
415
+
416
+ const isRTL = style.direction === 'rtl'
417
+ const pressedVerticalScrollbar
418
+ = canScrollY
419
+ && (isRTL
420
+ ? event.offsetX <= target.offsetWidth - target.clientWidth
421
+ : event.offsetX > target.clientWidth)
422
+ const pressedHorizontalScrollbar = canScrollX && event.offsetY > target.clientHeight
423
+
424
+ if (pressedVerticalScrollbar || pressedHorizontalScrollbar) {
425
+ return
426
+ }
427
+ }
428
+
429
+ if (isEventWithinFloatingTree(event)) {
430
+ return
431
+ }
432
+
433
+ if (getOutsidePressEvent() === 'intentional' && suppressNextOutsideClickRef.value) {
434
+ preventedPressSuppressionTimeout.clear()
435
+ suppressNextOutsideClickRef.value = false
436
+ return
437
+ }
438
+
439
+ const shouldDismiss = outsidePress()
440
+ if (typeof shouldDismiss === 'function' && !shouldDismiss(event)) {
441
+ return
442
+ }
443
+
444
+ if (hasBlockingChild('__outsidePressBubbles')) {
445
+ return
446
+ }
447
+
448
+ store.setOpen(false, createChangeEventDetails(REASONS.outsidePress, event))
449
+ clearInsideVueTree()
450
+ }
451
+
452
+ function handlePointerDown(event: PointerEvent) {
453
+ if (
454
+ getOutsidePressEvent() !== 'sloppy'
455
+ || event.pointerType === 'touch'
456
+ || !store.select('open')
457
+ || !enabled()
458
+ || isEventWithinOwnElements(event)
459
+ ) {
460
+ return
461
+ }
462
+
463
+ closeOnPressOutside(event)
464
+ }
465
+
466
+ function handleTouchStart(event: TouchEvent) {
467
+ if (
468
+ getOutsidePressEvent() !== 'sloppy'
469
+ || !store.select('open')
470
+ || !enabled()
471
+ || isEventWithinOwnElements(event)
472
+ ) {
473
+ return
474
+ }
475
+
476
+ const touch = event.touches[0]
477
+ if (touch) {
478
+ touchStateRef.value = {
479
+ startTime: Date.now(),
480
+ startX: touch.clientX,
481
+ startY: touch.clientY,
482
+ dismissOnTouchEnd: false,
483
+ dismissOnMouseDown: true,
484
+ }
485
+
486
+ cancelDismissOnEndTimeout.start(1000, () => {
487
+ if (touchStateRef.value) {
488
+ touchStateRef.value.dismissOnTouchEnd = false
489
+ touchStateRef.value.dismissOnMouseDown = false
490
+ }
491
+ })
492
+ }
493
+ }
494
+
495
+ function handleTouchStartCapture(event: TouchEvent) {
496
+ currentPointerTypeRef.value = 'touch'
497
+ addTargetEventListenerOnce(event, handleTouchStart)
498
+ }
499
+
500
+ function closeOnPressOutsideCapture(event: PointerEvent | MouseEvent) {
501
+ cancelDismissOnEndTimeout.clear()
502
+
503
+ if (event.type === 'pointerdown') {
504
+ currentPointerTypeRef.value = (event as PointerEvent).pointerType
505
+ }
506
+
507
+ if (
508
+ event.type === 'mousedown'
509
+ && touchStateRef.value
510
+ && !touchStateRef.value.dismissOnMouseDown
511
+ ) {
512
+ return
513
+ }
514
+
515
+ addTargetEventListenerOnce(event, (targetEvent) => {
516
+ if (targetEvent.type === 'pointerdown') {
517
+ handlePointerDown(targetEvent as PointerEvent)
518
+ }
519
+ else {
520
+ closeOnPressOutside(targetEvent as MouseEvent)
521
+ }
522
+ })
523
+ }
524
+
525
+ function handlePressEndCapture(event: PointerEvent | MouseEvent) {
526
+ if (!pressStartedInsideRef.value) {
527
+ return
528
+ }
529
+
530
+ const pressStartedInsideDefaultPrevented = pressStartPreventedRef.value
531
+ resetPressStartState()
532
+
533
+ if (getOutsidePressEvent() !== 'intentional') {
534
+ return
535
+ }
536
+
537
+ if (event.type === 'pointercancel') {
538
+ if (pressStartedInsideDefaultPrevented) {
539
+ suppressImmediateOutsideClickAfterPreventedStart()
540
+ }
541
+ return
542
+ }
543
+
544
+ if (isEventWithinFloatingTree(event)) {
545
+ return
546
+ }
547
+
548
+ if (pressStartedInsideDefaultPrevented) {
549
+ suppressImmediateOutsideClickAfterPreventedStart()
550
+ return
551
+ }
552
+
553
+ const shouldDismiss = outsidePress()
554
+ if (typeof shouldDismiss === 'function' && !shouldDismiss(event as MouseEvent)) {
555
+ return
556
+ }
557
+
558
+ preventedPressSuppressionTimeout.clear()
559
+ suppressNextOutsideClickRef.value = true
560
+ clearInsideVueTree()
561
+ }
562
+
563
+ function handleTouchMove(event: TouchEvent) {
564
+ if (
565
+ getOutsidePressEvent() !== 'sloppy'
566
+ || !touchStateRef.value
567
+ || isEventWithinOwnElements(event)
568
+ ) {
569
+ return
570
+ }
571
+
572
+ const touch = event.touches[0]
573
+ if (!touch) {
574
+ return
575
+ }
576
+
577
+ const deltaX = Math.abs(touch.clientX - touchStateRef.value.startX)
578
+ const deltaY = Math.abs(touch.clientY - touchStateRef.value.startY)
579
+ const distance = Math.sqrt(deltaX * deltaX + deltaY * deltaY)
580
+
581
+ if (distance > 5) {
582
+ touchStateRef.value.dismissOnTouchEnd = true
583
+ }
584
+
585
+ if (distance > 10) {
586
+ closeOnPressOutside(event)
587
+ cancelDismissOnEndTimeout.clear()
588
+ touchStateRef.value = null
589
+ }
590
+ }
591
+
592
+ function handleTouchMoveCapture(event: TouchEvent) {
593
+ addTargetEventListenerOnce(event, handleTouchMove)
594
+ }
595
+
596
+ function handleTouchEnd(event: TouchEvent) {
597
+ if (
598
+ getOutsidePressEvent() !== 'sloppy'
599
+ || !touchStateRef.value
600
+ || isEventWithinOwnElements(event)
601
+ ) {
602
+ return
603
+ }
604
+
605
+ if (touchStateRef.value.dismissOnTouchEnd) {
606
+ closeOnPressOutside(event)
607
+ }
608
+
609
+ cancelDismissOnEndTimeout.clear()
610
+ touchStateRef.value = null
611
+ }
612
+
613
+ function handleTouchEndCapture(event: TouchEvent) {
614
+ addTargetEventListenerOnce(event, handleTouchEnd)
615
+ }
616
+
617
+ const doc = ownerDocument(floatingElement.value ?? domReferenceElement.value)
618
+ const unsubscribe = mergeCleanups(
619
+ escapeKey()
620
+ ? mergeCleanups(
621
+ addEventListener(doc, 'keydown', closeOnEscapeKeyDown),
622
+ addEventListener(doc, 'compositionstart', handleCompositionStart),
623
+ addEventListener(doc, 'compositionend', handleCompositionEnd),
624
+ )
625
+ : undefined,
626
+ outsidePressEnabled()
627
+ ? mergeCleanups(
628
+ addEventListener(doc, 'click', closeOnPressOutsideCapture, true),
629
+ addEventListener(doc, 'pointerdown', closeOnPressOutsideCapture, true),
630
+ addEventListener(doc, 'pointerup', handlePressEndCapture, true),
631
+ addEventListener(doc, 'pointercancel', handlePressEndCapture, true),
632
+ addEventListener(doc, 'mousedown', closeOnPressOutsideCapture, true),
633
+ addEventListener(doc, 'mouseup', handlePressEndCapture, true),
634
+ addEventListener(doc, 'touchstart', handleTouchStartCapture, true),
635
+ addEventListener(doc, 'touchmove', handleTouchMoveCapture, true),
636
+ addEventListener(doc, 'touchend', handleTouchEndCapture, true),
637
+ )
638
+ : undefined,
639
+ )
640
+
641
+ onCleanup(() => {
642
+ unsubscribe()
643
+ compositionTimeout.clear()
644
+ preventedPressSuppressionTimeout.clear()
645
+ resetPressStartState()
646
+ suppressNextOutsideClickRef.value = false
647
+ })
648
+ })
649
+
650
+ watchEffect(() => {
651
+ outsidePress()
652
+ clearInsideVueTree()
653
+ })
654
+
655
+ onScopeDispose(() => {
656
+ cancelDismissOnEndTimeout.clear()
657
+ clearInsideVueTreeTimeout.clear()
658
+ })
659
+
660
+ const reference = {
661
+ onKeydown: closeOnEscapeKeyDown,
662
+ onPointerdown: closeOnReferencePress,
663
+ onClick: closeOnReferencePress,
664
+ }
665
+
666
+ const floating = {
667
+ onKeydown: closeOnEscapeKeyDown,
668
+ onPointerdown: markInsidePressStartPrevented,
669
+ onMousedown: markInsidePressStartPrevented,
670
+ onClickCapture: markInsideVueTree,
671
+ onMousedownCapture(event: MouseEvent) {
672
+ markInsideVueTree()
673
+ markPressStartedInsideVueTree(event)
674
+ },
675
+ onPointerdownCapture(event: PointerEvent) {
676
+ markInsideVueTree()
677
+ markPressStartedInsideVueTree(event)
678
+ },
679
+ onMouseupCapture: markInsideVueTree,
680
+ onTouchendCapture: markInsideVueTree,
681
+ onTouchmoveCapture: markInsideVueTree,
682
+ }
683
+
684
+ return {
685
+ reference,
686
+ floating,
687
+ trigger: reference,
688
+ }
689
+ }