@fiscozen/composables 0.1.37 → 0.1.38-beta.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.
- package/package.json +4 -4
- package/src/FzFloating.vue +2 -0
- package/src/__tests__/FzFloating.spec.ts +15 -0
- package/src/__tests__/__snapshots__/FzFloating.spec.ts.snap +24 -24
- package/src/__tests__/useFloating.spec.ts +339 -0
- package/src/composables/useFloating.ts +320 -243
- package/src/index.ts +1 -1
- package/src/types.ts +1 -1
- package/dist/composables.js +0 -465
- package/dist/composables.umd.cjs +0 -1
|
@@ -7,7 +7,7 @@ export const useFloating = (
|
|
|
7
7
|
): {
|
|
8
8
|
float: FzRect
|
|
9
9
|
rect: Ref<DOMRect | undefined>
|
|
10
|
-
setPosition: () => Promise<void>
|
|
10
|
+
setPosition: (ignoreCallback?: boolean) => Promise<void>
|
|
11
11
|
position: Ref<FzFloatingPosition>
|
|
12
12
|
actualPosition?: Ref<FzFloatingPosition | undefined>
|
|
13
13
|
openerRect: Ref<DOMRect | undefined>
|
|
@@ -38,272 +38,349 @@ export const useFloating = (
|
|
|
38
38
|
const floatObserver = ref(new IntersectionObserver(handleIntersect, options))
|
|
39
39
|
const actualPosition = ref<FzFloatingPosition>()
|
|
40
40
|
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
41
|
+
// Helper: Resolve DOM references
|
|
42
|
+
const resolveDomReferences = () => {
|
|
43
|
+
safeElementDomRef.value = (
|
|
44
|
+
typeof args.element.value.domRef.value === 'string'
|
|
45
|
+
? document.querySelector(args.element.value.domRef.value)
|
|
46
|
+
: args.element.value.domRef.value
|
|
47
|
+
) as HTMLElement
|
|
48
|
+
|
|
49
|
+
if (!args.container?.value) {
|
|
50
|
+
safeContainerDomRef.value = document.body
|
|
51
|
+
} else {
|
|
52
|
+
safeContainerDomRef.value = (
|
|
53
|
+
typeof args.container.value?.domRef.value === 'string'
|
|
54
|
+
? document.querySelector(args.container.value.domRef.value)
|
|
55
|
+
: args.container.value?.domRef.value
|
|
48
56
|
) as HTMLElement
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
57
|
+
safeContainerDomRef.value ??= document.body
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
if (!safeElementDomRef.value) {
|
|
61
|
+
throw new Error('missing reference element for floating behavior')
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
if (args.opener?.value) {
|
|
65
|
+
safeOpenerDomRef.value = (
|
|
66
|
+
typeof args.opener.value?.domRef.value === 'string'
|
|
67
|
+
? document.querySelector(args.opener.value.domRef.value)
|
|
68
|
+
: args.opener.value?.domRef.value
|
|
69
|
+
) as HTMLElement
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
rect.value = safeElementDomRef.value.getBoundingClientRect()
|
|
73
|
+
openerRect.value = safeOpenerDomRef.value?.getBoundingClientRect()
|
|
74
|
+
containerRect.value = safeContainerDomRef.value.getBoundingClientRect()
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
// Helper: Resolve auto position to actual position
|
|
78
|
+
const resolveAutoPositionValue = () => {
|
|
79
|
+
if (!args.opener?.value || !safeOpenerDomRef.value) return
|
|
80
|
+
|
|
81
|
+
switch (actualPosition.value) {
|
|
82
|
+
case 'auto':
|
|
83
|
+
actualPosition.value = getHighestAvailableSpacePos(
|
|
84
|
+
args.useViewport?.value ? null : (safeContainerDomRef.value as HTMLElement),
|
|
85
|
+
safeElementDomRef.value as HTMLElement,
|
|
86
|
+
safeOpenerDomRef.value as HTMLElement
|
|
87
|
+
)
|
|
88
|
+
break
|
|
89
|
+
case 'auto-vertical':
|
|
90
|
+
actualPosition.value = getHighestAvailableSpacePos(
|
|
91
|
+
args.useViewport?.value ? null : (safeContainerDomRef.value as HTMLElement),
|
|
92
|
+
safeElementDomRef.value as HTMLElement,
|
|
93
|
+
safeOpenerDomRef.value as HTMLElement,
|
|
94
|
+
undefined,
|
|
95
|
+
true
|
|
96
|
+
)
|
|
97
|
+
break
|
|
98
|
+
case 'auto-start':
|
|
99
|
+
actualPosition.value = getHighestAvailableSpacePos(
|
|
100
|
+
args.useViewport?.value ? null : (safeContainerDomRef.value as HTMLElement),
|
|
101
|
+
safeElementDomRef.value as HTMLElement,
|
|
102
|
+
safeOpenerDomRef.value as HTMLElement,
|
|
103
|
+
'start'
|
|
104
|
+
)
|
|
105
|
+
break
|
|
106
|
+
case 'auto-vertical-start':
|
|
107
|
+
actualPosition.value = getHighestAvailableSpacePos(
|
|
108
|
+
args.useViewport?.value ? null : (safeContainerDomRef.value as HTMLElement),
|
|
109
|
+
safeElementDomRef.value as HTMLElement,
|
|
110
|
+
safeOpenerDomRef.value as HTMLElement,
|
|
111
|
+
'start',
|
|
112
|
+
true
|
|
113
|
+
)
|
|
114
|
+
break
|
|
115
|
+
case 'auto-end':
|
|
116
|
+
actualPosition.value = getHighestAvailableSpacePos(
|
|
117
|
+
args.useViewport?.value ? null : (safeContainerDomRef.value as HTMLElement),
|
|
118
|
+
safeElementDomRef.value as HTMLElement,
|
|
119
|
+
safeOpenerDomRef.value as HTMLElement,
|
|
120
|
+
'end'
|
|
121
|
+
)
|
|
122
|
+
break
|
|
123
|
+
case 'auto-vertical-end':
|
|
124
|
+
actualPosition.value = getHighestAvailableSpacePos(
|
|
125
|
+
args.useViewport?.value ? null : (safeContainerDomRef.value as HTMLElement),
|
|
126
|
+
safeElementDomRef.value as HTMLElement,
|
|
127
|
+
safeOpenerDomRef.value as HTMLElement,
|
|
128
|
+
'end',
|
|
129
|
+
true
|
|
130
|
+
)
|
|
131
|
+
break
|
|
132
|
+
default:
|
|
133
|
+
break
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
// Helper: Calculate position with opener
|
|
138
|
+
const calculatePositionWithOpener = (
|
|
139
|
+
elStyle: CSSStyleDeclaration,
|
|
140
|
+
translateX: { value: number },
|
|
141
|
+
translateY: { value: number }
|
|
142
|
+
) => {
|
|
143
|
+
if (!openerRect.value) return
|
|
144
|
+
|
|
145
|
+
const leftWithoutXMargin =
|
|
146
|
+
openerRect.value.left - parseFloat(elStyle.marginLeft) - parseFloat(elStyle.marginRight)
|
|
147
|
+
const leftWithoutLeftMargin = openerRect.value.left - parseFloat(elStyle.marginLeft)
|
|
148
|
+
const topWithoutYMargin =
|
|
149
|
+
openerRect.value.top - parseFloat(elStyle.marginTop) - parseFloat(elStyle.marginBottom)
|
|
150
|
+
const topWithoutTopMargin = openerRect.value.top - parseFloat(elStyle.marginTop)
|
|
151
|
+
|
|
152
|
+
switch (actualPosition.value) {
|
|
153
|
+
case 'bottom':
|
|
154
|
+
float.position.y = openerRect.value.bottom
|
|
155
|
+
float.position.x = leftWithoutLeftMargin + openerRect.value.width / 2
|
|
156
|
+
translateX.value = -50
|
|
157
|
+
translateY.value = 0
|
|
158
|
+
break
|
|
159
|
+
case 'bottom-start':
|
|
160
|
+
float.position.y = openerRect.value.bottom
|
|
161
|
+
float.position.x = leftWithoutXMargin
|
|
162
|
+
translateX.value = 0
|
|
163
|
+
translateY.value = 0
|
|
164
|
+
break
|
|
165
|
+
case 'bottom-end':
|
|
166
|
+
float.position.y = openerRect.value.bottom
|
|
167
|
+
float.position.x = openerRect.value.right
|
|
168
|
+
translateX.value = -100
|
|
169
|
+
translateY.value = 0
|
|
170
|
+
break
|
|
171
|
+
case 'left-start':
|
|
172
|
+
float.position.y = topWithoutYMargin
|
|
173
|
+
float.position.x = leftWithoutXMargin
|
|
174
|
+
translateX.value = -100
|
|
175
|
+
translateY.value = 0
|
|
176
|
+
break
|
|
177
|
+
case 'left':
|
|
178
|
+
float.position.y = topWithoutTopMargin + openerRect.value.height / 2
|
|
179
|
+
float.position.x = leftWithoutXMargin
|
|
180
|
+
translateY.value = -50
|
|
181
|
+
translateX.value = -100
|
|
182
|
+
break
|
|
183
|
+
case 'left-end':
|
|
184
|
+
float.position.y = openerRect.value.bottom
|
|
185
|
+
float.position.x = leftWithoutXMargin
|
|
186
|
+
translateY.value = -100
|
|
187
|
+
translateX.value = -100
|
|
188
|
+
break
|
|
189
|
+
case 'top-start':
|
|
190
|
+
float.position.y = topWithoutYMargin
|
|
191
|
+
float.position.x = leftWithoutXMargin
|
|
192
|
+
translateY.value = -100
|
|
193
|
+
translateX.value = 0
|
|
194
|
+
break
|
|
195
|
+
case 'top':
|
|
196
|
+
float.position.y = topWithoutYMargin
|
|
197
|
+
float.position.x = leftWithoutLeftMargin + openerRect.value.width / 2
|
|
198
|
+
translateX.value = -50
|
|
199
|
+
translateY.value = -100
|
|
200
|
+
break
|
|
201
|
+
case 'top-end':
|
|
202
|
+
float.position.y = topWithoutYMargin
|
|
203
|
+
float.position.x = openerRect.value.right
|
|
204
|
+
translateX.value = -100
|
|
205
|
+
translateY.value = -100
|
|
206
|
+
break
|
|
207
|
+
case 'right-start':
|
|
208
|
+
float.position.y = topWithoutYMargin
|
|
209
|
+
float.position.x = openerRect.value.right
|
|
210
|
+
translateX.value = 0
|
|
211
|
+
translateY.value = 0
|
|
212
|
+
break
|
|
213
|
+
case 'right':
|
|
214
|
+
float.position.y = topWithoutTopMargin + openerRect.value.height / 2
|
|
215
|
+
float.position.x = openerRect.value.right
|
|
216
|
+
translateY.value = -50
|
|
217
|
+
translateX.value = 0
|
|
218
|
+
break
|
|
219
|
+
case 'right-end':
|
|
220
|
+
float.position.y = openerRect.value.bottom
|
|
221
|
+
float.position.x = openerRect.value.right
|
|
222
|
+
translateY.value = -100
|
|
223
|
+
translateX.value = 0
|
|
224
|
+
break
|
|
225
|
+
default:
|
|
226
|
+
break
|
|
227
|
+
}
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
// Helper: Calculate position without opener
|
|
231
|
+
const calculatePositionWithoutOpener = (translateX: { value: number }) => {
|
|
232
|
+
if (!containerRect.value || !rect.value) return
|
|
233
|
+
|
|
234
|
+
switch (actualPosition.value) {
|
|
235
|
+
case 'bottom':
|
|
236
|
+
float.position.y = containerRect.value.bottom - rect.value.height
|
|
237
|
+
float.position.x = containerRect.value.left + containerRect.value.width / 2
|
|
238
|
+
translateX.value = -50
|
|
239
|
+
break
|
|
240
|
+
case 'left-end':
|
|
241
|
+
case 'bottom-start':
|
|
242
|
+
float.position.y = containerRect.value.bottom - rect.value.height
|
|
243
|
+
float.position.x = containerRect.value.left
|
|
244
|
+
break
|
|
245
|
+
case 'right-end':
|
|
246
|
+
case 'bottom-end':
|
|
247
|
+
float.position.y = containerRect.value.bottom - rect.value.height
|
|
248
|
+
float.position.x = containerRect.value.right - rect.value.width
|
|
249
|
+
break
|
|
250
|
+
case 'left':
|
|
251
|
+
float.position.y =
|
|
252
|
+
containerRect.value.top + (containerRect.value.height - rect.value.height) / 2
|
|
253
|
+
float.position.x = containerRect.value.left
|
|
254
|
+
break
|
|
255
|
+
case 'top-start':
|
|
256
|
+
case 'left-start':
|
|
257
|
+
float.position.y = containerRect.value.top
|
|
258
|
+
float.position.x = containerRect.value.left
|
|
259
|
+
break
|
|
260
|
+
case 'top':
|
|
261
|
+
float.position.y = containerRect.value.top
|
|
262
|
+
float.position.x =
|
|
263
|
+
containerRect.value.left + (containerRect.value.width - rect.value.width) / 2
|
|
264
|
+
break
|
|
265
|
+
case 'top-end':
|
|
266
|
+
case 'right-start':
|
|
267
|
+
float.position.y = containerRect.value.top
|
|
268
|
+
float.position.x = containerRect.value.right - rect.value.width
|
|
269
|
+
break
|
|
270
|
+
case 'right':
|
|
271
|
+
float.position.y =
|
|
272
|
+
containerRect.value.top + (containerRect.value.height - rect.value.height) / 2
|
|
273
|
+
float.position.x = containerRect.value.right - rect.value.width
|
|
274
|
+
break
|
|
275
|
+
default:
|
|
276
|
+
break
|
|
277
|
+
}
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
// Helper: Apply boundary corrections
|
|
281
|
+
const applyBoundaryCorrections = (
|
|
282
|
+
realPos: { x: number; y: number },
|
|
283
|
+
translateX: { value: number },
|
|
284
|
+
translateY: { value: number }
|
|
285
|
+
) => {
|
|
286
|
+
if (!containerRect.value || !rect.value) return
|
|
287
|
+
|
|
288
|
+
if (realPos.x < containerRect.value.left) {
|
|
289
|
+
float.position.x = containerRect.value.left
|
|
290
|
+
translateX.value = 0
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
if (realPos.x + rect.value.width > containerRect.value.right) {
|
|
294
|
+
const fixVal = containerRect.value.right - rect.value.width
|
|
295
|
+
if (fixVal > 0) {
|
|
296
|
+
float.position.x = fixVal
|
|
58
297
|
}
|
|
298
|
+
translateX.value = 0
|
|
299
|
+
}
|
|
300
|
+
|
|
301
|
+
if (realPos.y < containerRect.value.top) {
|
|
302
|
+
float.position.y = containerRect.value.top
|
|
303
|
+
translateY.value = 0
|
|
304
|
+
}
|
|
59
305
|
|
|
60
|
-
|
|
61
|
-
|
|
306
|
+
if (realPos.y + rect.value.height > containerRect.value.bottom) {
|
|
307
|
+
const fixVal = containerRect.value.bottom - rect.value.height
|
|
308
|
+
if (fixVal > 0) {
|
|
309
|
+
float.position.y = fixVal
|
|
62
310
|
}
|
|
311
|
+
translateY.value = 0
|
|
312
|
+
}
|
|
313
|
+
}
|
|
314
|
+
|
|
315
|
+
// Helper: Apply element styles
|
|
316
|
+
const applyElementStyles = () => {
|
|
317
|
+
if (!safeElementDomRef.value) return
|
|
318
|
+
|
|
319
|
+
safeElementDomRef.value.style.top = `${float.position.y}px`
|
|
320
|
+
safeElementDomRef.value.style.left = `${float.position.x}px`
|
|
321
|
+
safeElementDomRef.value.style.position = 'fixed'
|
|
322
|
+
safeElementDomRef.value.style.display = 'flex'
|
|
323
|
+
}
|
|
324
|
+
|
|
325
|
+
const setPosition = (ignoreCallback: boolean = false) =>
|
|
326
|
+
nextTick(() => {
|
|
327
|
+
actualPosition.value = args.position ? args.position.value : 'auto'
|
|
328
|
+
|
|
329
|
+
// Step 1: Resolve DOM references
|
|
330
|
+
resolveDomReferences()
|
|
63
331
|
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
332
|
+
// Step 1.5: CRITICAL - Remove element from document flow immediately
|
|
333
|
+
// This prevents the floating element from pushing the opener and causing layout shift
|
|
334
|
+
if (safeElementDomRef.value) {
|
|
335
|
+
safeElementDomRef.value.style.position = 'fixed'
|
|
336
|
+
safeElementDomRef.value.style.top = '0px'
|
|
337
|
+
safeElementDomRef.value.style.left = '0px'
|
|
70
338
|
}
|
|
71
339
|
|
|
72
|
-
|
|
340
|
+
// Recalculate all rects after content is rendered
|
|
341
|
+
rect.value = safeElementDomRef.value!.getBoundingClientRect()
|
|
73
342
|
openerRect.value = safeOpenerDomRef.value?.getBoundingClientRect()
|
|
74
|
-
containerRect.value = safeContainerDomRef.value
|
|
343
|
+
containerRect.value = safeContainerDomRef.value!.getBoundingClientRect()
|
|
75
344
|
|
|
76
345
|
const elStyle = window.getComputedStyle(safeElementDomRef.value as HTMLElement)
|
|
77
346
|
|
|
78
|
-
//
|
|
347
|
+
// Step 2: Setup intersection observer
|
|
79
348
|
floatObserver.value.observe(safeElementDomRef.value as HTMLElement)
|
|
80
349
|
floatObserver.value.observe(safeContainerDomRef.value as HTMLElement)
|
|
81
350
|
|
|
82
|
-
|
|
83
|
-
|
|
351
|
+
// Step 3: Resolve auto positions
|
|
352
|
+
resolveAutoPositionValue()
|
|
353
|
+
|
|
354
|
+
// Step 4: Calculate position based on opener presence
|
|
355
|
+
const translate = { x: { value: 0 }, y: { value: 0 } }
|
|
84
356
|
|
|
85
357
|
if (args.opener?.value && safeOpenerDomRef.value && openerRect?.value) {
|
|
86
|
-
|
|
87
|
-
openerRect.value.left - parseFloat(elStyle.marginLeft) - parseFloat(elStyle.marginRight)
|
|
88
|
-
const leftWithoutLeftMargin = openerRect.value.left - parseFloat(elStyle.marginLeft)
|
|
89
|
-
const topWithoutYMargin =
|
|
90
|
-
openerRect.value.top - parseFloat(elStyle.marginTop) - parseFloat(elStyle.marginBottom)
|
|
91
|
-
const topWithoutTopMargin = openerRect.value.top - parseFloat(elStyle.marginTop)
|
|
92
|
-
|
|
93
|
-
switch (actualPosition.value) {
|
|
94
|
-
case 'auto':
|
|
95
|
-
actualPosition.value = getHighestAvailableSpacePos(
|
|
96
|
-
args.useViewport?.value ? null : (safeContainerDomRef.value as HTMLElement),
|
|
97
|
-
safeElementDomRef.value as HTMLElement,
|
|
98
|
-
safeOpenerDomRef.value as HTMLElement
|
|
99
|
-
)
|
|
100
|
-
break
|
|
101
|
-
case 'auto-vertical':
|
|
102
|
-
actualPosition.value = getHighestAvailableSpacePos(
|
|
103
|
-
args.useViewport?.value ? null : (safeContainerDomRef.value as HTMLElement),
|
|
104
|
-
safeElementDomRef.value as HTMLElement,
|
|
105
|
-
safeOpenerDomRef.value as HTMLElement,
|
|
106
|
-
undefined,
|
|
107
|
-
true
|
|
108
|
-
)
|
|
109
|
-
break
|
|
110
|
-
case 'auto-start':
|
|
111
|
-
actualPosition.value = getHighestAvailableSpacePos(
|
|
112
|
-
args.useViewport?.value ? null : (safeContainerDomRef.value as HTMLElement),
|
|
113
|
-
safeElementDomRef.value as HTMLElement,
|
|
114
|
-
safeOpenerDomRef.value as HTMLElement,
|
|
115
|
-
'start'
|
|
116
|
-
)
|
|
117
|
-
break
|
|
118
|
-
case 'auto-vertical-start':
|
|
119
|
-
actualPosition.value = getHighestAvailableSpacePos(
|
|
120
|
-
args.useViewport?.value ? null : (safeContainerDomRef.value as HTMLElement),
|
|
121
|
-
safeElementDomRef.value as HTMLElement,
|
|
122
|
-
safeOpenerDomRef.value as HTMLElement,
|
|
123
|
-
'start',
|
|
124
|
-
true
|
|
125
|
-
)
|
|
126
|
-
break
|
|
127
|
-
case 'auto-end':
|
|
128
|
-
actualPosition.value = getHighestAvailableSpacePos(
|
|
129
|
-
args.useViewport?.value ? null : (safeContainerDomRef.value as HTMLElement),
|
|
130
|
-
safeElementDomRef.value as HTMLElement,
|
|
131
|
-
safeOpenerDomRef.value as HTMLElement,
|
|
132
|
-
'end'
|
|
133
|
-
)
|
|
134
|
-
break
|
|
135
|
-
case 'auto-vertical-end':
|
|
136
|
-
actualPosition.value = getHighestAvailableSpacePos(
|
|
137
|
-
args.useViewport?.value ? null : (safeContainerDomRef.value as HTMLElement),
|
|
138
|
-
safeElementDomRef.value as HTMLElement,
|
|
139
|
-
safeOpenerDomRef.value as HTMLElement,
|
|
140
|
-
'end',
|
|
141
|
-
true
|
|
142
|
-
)
|
|
143
|
-
break
|
|
144
|
-
default:
|
|
145
|
-
break
|
|
146
|
-
}
|
|
147
|
-
switch (actualPosition.value) {
|
|
148
|
-
case 'bottom':
|
|
149
|
-
float.position.y = openerRect.value.bottom
|
|
150
|
-
float.position.x = leftWithoutLeftMargin + openerRect.value.width / 2
|
|
151
|
-
translateX = -50
|
|
152
|
-
translateY = 0
|
|
153
|
-
break
|
|
154
|
-
case 'bottom-start':
|
|
155
|
-
float.position.y = openerRect.value.bottom
|
|
156
|
-
float.position.x = leftWithoutXMargin
|
|
157
|
-
translateX = 0
|
|
158
|
-
translateY = 0
|
|
159
|
-
break
|
|
160
|
-
case 'bottom-end':
|
|
161
|
-
float.position.y = openerRect.value.bottom
|
|
162
|
-
float.position.x = openerRect.value.right
|
|
163
|
-
translateX = -100
|
|
164
|
-
translateY = 0
|
|
165
|
-
break
|
|
166
|
-
case 'left-start':
|
|
167
|
-
float.position.y = topWithoutYMargin
|
|
168
|
-
float.position.x = leftWithoutXMargin
|
|
169
|
-
translateX = -100
|
|
170
|
-
translateY = 0
|
|
171
|
-
break
|
|
172
|
-
case 'left':
|
|
173
|
-
float.position.y = topWithoutTopMargin + openerRect.value.height / 2
|
|
174
|
-
float.position.x = leftWithoutXMargin
|
|
175
|
-
translateY = -50
|
|
176
|
-
translateX = -100
|
|
177
|
-
break
|
|
178
|
-
case 'left-end':
|
|
179
|
-
float.position.y = openerRect.value.bottom
|
|
180
|
-
float.position.x = leftWithoutXMargin
|
|
181
|
-
translateY = -100
|
|
182
|
-
translateX = -100
|
|
183
|
-
break
|
|
184
|
-
case 'top-start':
|
|
185
|
-
float.position.y = topWithoutYMargin
|
|
186
|
-
float.position.x = leftWithoutXMargin
|
|
187
|
-
translateY = -100
|
|
188
|
-
translateX = 0
|
|
189
|
-
break
|
|
190
|
-
case 'top':
|
|
191
|
-
float.position.y = topWithoutYMargin
|
|
192
|
-
float.position.x = leftWithoutLeftMargin + openerRect.value.width / 2
|
|
193
|
-
translateX = -50
|
|
194
|
-
translateY = -100
|
|
195
|
-
break
|
|
196
|
-
case 'top-end':
|
|
197
|
-
float.position.y = topWithoutYMargin
|
|
198
|
-
float.position.x = openerRect.value.right
|
|
199
|
-
translateX = -100
|
|
200
|
-
translateY = -100
|
|
201
|
-
break
|
|
202
|
-
case 'right-start':
|
|
203
|
-
float.position.y = topWithoutYMargin
|
|
204
|
-
float.position.x = openerRect.value.right
|
|
205
|
-
translateX = 0
|
|
206
|
-
translateY = 0
|
|
207
|
-
break
|
|
208
|
-
case 'right':
|
|
209
|
-
float.position.y = topWithoutTopMargin + openerRect.value.height / 2
|
|
210
|
-
float.position.x = openerRect.value.right
|
|
211
|
-
translateY = -50
|
|
212
|
-
translateX = 0
|
|
213
|
-
break
|
|
214
|
-
case 'right-end':
|
|
215
|
-
float.position.y = openerRect.value.bottom
|
|
216
|
-
float.position.x = openerRect.value.right
|
|
217
|
-
translateY = -100
|
|
218
|
-
translateX = 0
|
|
219
|
-
break
|
|
220
|
-
default:
|
|
221
|
-
break
|
|
222
|
-
}
|
|
358
|
+
calculatePositionWithOpener(elStyle, translate.x, translate.y)
|
|
223
359
|
} else {
|
|
224
|
-
|
|
225
|
-
case 'bottom':
|
|
226
|
-
float.position.y = containerRect.value.bottom - rect.value.height
|
|
227
|
-
float.position.x = containerRect.value.left + containerRect.value.width / 2
|
|
228
|
-
translateX = -50
|
|
229
|
-
break
|
|
230
|
-
case 'left-end':
|
|
231
|
-
case 'bottom-start':
|
|
232
|
-
float.position.y = containerRect.value.bottom - rect.value.height
|
|
233
|
-
float.position.x = containerRect.value.left
|
|
234
|
-
break
|
|
235
|
-
case 'right-end':
|
|
236
|
-
case 'bottom-end':
|
|
237
|
-
float.position.y = containerRect.value.bottom - rect.value.height
|
|
238
|
-
float.position.x = containerRect.value.right - rect.value.width
|
|
239
|
-
break
|
|
240
|
-
case 'left':
|
|
241
|
-
float.position.y =
|
|
242
|
-
containerRect.value.top + (containerRect.value.height - rect.value.height) / 2
|
|
243
|
-
float.position.x = containerRect.value.left
|
|
244
|
-
break
|
|
245
|
-
case 'top-start':
|
|
246
|
-
case 'left-start':
|
|
247
|
-
float.position.y = containerRect.value.top
|
|
248
|
-
float.position.x = containerRect.value.left
|
|
249
|
-
break
|
|
250
|
-
case 'top':
|
|
251
|
-
float.position.y = containerRect.value.top
|
|
252
|
-
float.position.x =
|
|
253
|
-
containerRect.value.left + (containerRect.value.width - rect.value.width) / 2
|
|
254
|
-
break
|
|
255
|
-
case 'top-end':
|
|
256
|
-
case 'right-start':
|
|
257
|
-
float.position.y = containerRect.value.top
|
|
258
|
-
float.position.x = containerRect.value.right - rect.value.width
|
|
259
|
-
case 'right':
|
|
260
|
-
float.position.y =
|
|
261
|
-
containerRect.value.top + (containerRect.value.height - rect.value.height) / 2
|
|
262
|
-
float.position.x = containerRect.value.right - rect.value.width
|
|
263
|
-
break
|
|
264
|
-
default:
|
|
265
|
-
break
|
|
266
|
-
}
|
|
267
|
-
}
|
|
268
|
-
|
|
269
|
-
const realPos = calcRealPos(rect.value, float.position, translateX, translateY)
|
|
270
|
-
|
|
271
|
-
float.position.x = realPos.x
|
|
272
|
-
float.position.y = realPos.y
|
|
273
|
-
|
|
274
|
-
if (realPos.x < containerRect.value.left) {
|
|
275
|
-
float.position.x = containerRect.value.left
|
|
276
|
-
translateX = 0
|
|
277
|
-
}
|
|
278
|
-
|
|
279
|
-
if (realPos.x + rect.value.width > containerRect.value.right) {
|
|
280
|
-
const fixVal = containerRect.value.right - rect.value.width
|
|
281
|
-
if (fixVal > 0) {
|
|
282
|
-
float.position.x = fixVal
|
|
283
|
-
}
|
|
284
|
-
translateX = 0
|
|
360
|
+
calculatePositionWithoutOpener(translate.x)
|
|
285
361
|
}
|
|
286
362
|
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
363
|
+
// Step 5: Calculate real position and apply boundary corrections
|
|
364
|
+
if (rect.value) {
|
|
365
|
+
const realPos = calcRealPos(
|
|
366
|
+
rect.value,
|
|
367
|
+
float.position,
|
|
368
|
+
translate.x.value,
|
|
369
|
+
translate.y.value
|
|
370
|
+
)
|
|
371
|
+
float.position.x = realPos.x
|
|
372
|
+
float.position.y = realPos.y
|
|
291
373
|
|
|
292
|
-
|
|
293
|
-
const fixVal = containerRect.value.bottom - rect.value.height
|
|
294
|
-
if (fixVal > 0) {
|
|
295
|
-
float.position.y = fixVal
|
|
296
|
-
}
|
|
297
|
-
translateY = 0
|
|
374
|
+
applyBoundaryCorrections(realPos, translate.x, translate.y)
|
|
298
375
|
}
|
|
299
376
|
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
safeElementDomRef.value.style.position = 'fixed'
|
|
303
|
-
safeElementDomRef.value.style.display = 'flex'
|
|
377
|
+
// Step 6: Apply styles to element
|
|
378
|
+
applyElementStyles()
|
|
304
379
|
|
|
305
|
-
rect
|
|
380
|
+
// Step 7: Update rect after positioning
|
|
381
|
+
rect.value = safeElementDomRef.value!.getBoundingClientRect()
|
|
306
382
|
|
|
383
|
+
// Step 8: Trigger callback if provided
|
|
307
384
|
if (args.callback?.value && !ignoreCallback) {
|
|
308
385
|
args.callback.value(rect, openerRect, containerRect, position, actualPosition)
|
|
309
386
|
}
|
package/src/index.ts
CHANGED
package/src/types.ts
CHANGED