@@ -46,7 +46,7 @@ import { getFallbackId, type LinkableByIdProps,type TailwindClassProp } from "..
46
46
import { twMerge } from "../../helpers/twMerge.js"
47
47
import { castType } from "@alanscodelog/utils/castType.js"
48
48
import { isArray } from "@alanscodelog/utils/isArray.js"
49
- import type { IPopupReference, PopupPosition, PopupPositioner, PopupPositionModifier, PopupSpaceInfo } from "../../types.js"
49
+ import type { IPopupReference, PopupPosition, PopupPositioner, PopupPositionModifier, PopupSpaceInfo, SimpleDOMRect } from "../../types.js"
50
50
51
51
const fallbackId = getFallbackId()
52
52
// eslint-disable-next-line no-use-before-define
@@ -65,19 +65,19 @@ defineOptions({ name: "lib-popup" })
65
65
const dialogEl = ref<HTMLDialogElement | null>(null)
66
66
const popupEl = ref<IPopupReference | null>(null)
67
67
const buttonEl = ref<IPopupReference | null>(null)
68
+ const backgroundEl = ref<IPopupReference | null>(null)
68
69
69
70
const pos = ref<PopupPosition>({} as any)
70
71
const modelValue = defineModel<boolean>({ default: false })
71
72
let isOpen = false
72
73
73
74
74
75
/**
75
- * We don't have access to the dialog backdrop and without extra styling, it's of 0 width/height,
76
- * positioned in the center of the screen, with margins taking up all the space.
76
+ * We don't have access to the dialog backdrop and without extra styling, it's of 0 width/height, positioned in the center of the screen, with margins taking up all the space.
77
77
*
78
- * This returns a modified rect that makes more logical sense. It's also available when we aren't using the dialog element.
78
+ * This returns a modified rect that makes more logical sense.
79
79
*/
80
- const getVeilBoundingRect = (el: HTMLElement): Omit<DOMRect, "toJSON"> => {
80
+ const getDialogBoundingRect = (el: HTMLElement): SimpleDOMRect => {
81
81
const rect = el.getBoundingClientRect()
82
82
return {
83
83
x: 0,
@@ -90,7 +90,7 @@ const getVeilBoundingRect = (el: HTMLElement): Omit<DOMRect, "toJSON"> => {
90
90
right: 0,
91
91
}
92
92
}
93
- let lastButtonElPos: ReturnType<IPopupReference["getBoundingClientRect"]> | undefined
93
+ let lastButtonElPos: SimpleDOMRect | undefined
94
94
const recompute = (force: boolean = false): void => {
95
95
requestAnimationFrame(() => {
96
96
const horzHasCenterScreen = isArray(props.preferredHorizontal)
@@ -107,7 +107,11 @@ const recompute = (force: boolean = false): void => {
107
107
return
108
108
}
109
109
const el = buttonEl.value?.getBoundingClientRect()
110
- const veil = getVeilBoundingRect(props.useBackdrop ? dialogEl.value : document.body)
110
+ const bg = backgroundEl.value?.getBoundingClientRect() ?? (
111
+ props.useBackdrop
112
+ ? getDialogBoundingRect(dialogEl.value)
113
+ : document.body.getBoundingClientRect()
114
+ )
111
115
const popup = popupEl.value.getBoundingClientRect()
112
116
113
117
let finalPos: { x: number, y: number, maxWidth?: number, maxHeight?: number } = {} as any
@@ -136,30 +140,30 @@ const recompute = (force: boolean = false): void => {
136
140
bottom: 0,
137
141
}
138
142
if (el) {
139
- space.left = (el.x + el.width) - veil .x
140
- space.leftLeft = el.x - veil .x
141
- space.right = (veil .x + veil .width) - (el.x + el.width)
142
- space.rightRight = veil .x + veil .width - el.x
143
- space.leftFromCenter = (el.x + (el.width / 2)) - veil .x
144
- space.rightFromCenter = (veil .x + veil .width) - (el.x + (el.width / 2))
145
- space.topFromCenter = (el.y + (el.height / 2)) - veil .y
146
- space.bottomFromCenter = (veil .y + veil .height) - (el.y + (el.height / 2))
147
- space.top = el.y - veil .y
148
- space.bottom = (veil .y + veil .height) - (el.y + el.height)
143
+ space.left = (el.x + el.width) - bg .x
144
+ space.leftLeft = el.x - bg .x
145
+ space.right = (bg .x + bg .width) - (el.x + el.width)
146
+ space.rightRight = bg .x + bg .width - el.x
147
+ space.leftFromCenter = (el.x + (el.width / 2)) - bg .x
148
+ space.rightFromCenter = (bg .x + bg .width) - (el.x + (el.width / 2))
149
+ space.topFromCenter = (el.y + (el.height / 2)) - bg .y
150
+ space.bottomFromCenter = (bg .y + bg .height) - (el.y + (el.height / 2))
151
+ space.top = el.y - bg .y
152
+ space.bottom = (bg .y + bg .height) - (el.y + el.height)
149
153
}
150
154
const { preferredHorizontal, preferredVertical } = props
151
155
let maxWidth: number | undefined
152
156
let maxHeight: number | undefined
153
157
if (typeof preferredHorizontal === "function") {
154
- finalPos.x = preferredHorizontal(el, popup, veil , space)
158
+ finalPos.x = preferredHorizontal(el, popup, bg , space)
155
159
} else {
156
160
/* eslint-disable no-labels */
157
161
outerloop:
158
162
for (const type of preferredHorizontal) {
159
163
switch (type) {
160
164
case "center-screen":
161
- if (popup.width < veil .width) {
162
- finalPos.x = (veil .width / 2) - (popup.width / 2)
165
+ if (popup.width < bg .width) {
166
+ finalPos.x = (bg .width / 2) - (popup.width / 2)
163
167
} else {
164
168
finalPos.x = 0
165
169
maxWidth = finalPos.x
@@ -198,7 +202,7 @@ const recompute = (force: boolean = false): void => {
198
202
if (space.right >= popup.width) {
199
203
finalPos.x = el.x + el.width; break outerloop
200
204
} else {
201
- finalPos.x = veil .x + veil .width - popup.width; break outerloop
205
+ finalPos.x = bg .x + bg .width - popup.width; break outerloop
202
206
}
203
207
204
208
case "right":
@@ -226,14 +230,14 @@ const recompute = (force: boolean = false): void => {
226
230
}
227
231
}
228
232
if (typeof preferredVertical === "function") {
229
- finalPos.y = preferredVertical(el, popup, veil , space)
233
+ finalPos.y = preferredVertical(el, popup, bg , space)
230
234
} else {
231
235
outerloop:
232
236
for (const type of preferredVertical) {
233
237
switch (type) {
234
238
case "center-screen":
235
- if (popup.height < veil .height) {
236
- finalPos.y = (veil .height / 2) - (popup.height / 2)
239
+ if (popup.height < bg .height) {
240
+ finalPos.y = (bg .height / 2) - (popup.height / 2)
237
241
} else {
238
242
finalPos.y = 0
239
243
maxHeight = finalPos.y
@@ -263,7 +267,7 @@ const recompute = (force: boolean = false): void => {
263
267
if (space.bottom >= popup.height) {
264
268
finalPos.y = el.y + el.height; break outerloop
265
269
} else {
266
- finalPos.y = veil .y + veil .height - popup.height; break outerloop
270
+ finalPos.y = bg .y + bg .height - popup.height; break outerloop
267
271
}
268
272
case "center-most":
269
273
case "center":
@@ -299,7 +303,7 @@ const recompute = (force: boolean = false): void => {
299
303
finalPos.maxHeight = maxHeight ?? undefined
300
304
/* eslint-enable no-labels */
301
305
if (props.modifyPosition) {
302
- finalPos = props.modifyPosition(finalPos, el, popup, veil , space)
306
+ finalPos = props.modifyPosition(finalPos, el, popup, bg , space)
303
307
}
304
308
pos.value = finalPos
305
309
lastButtonElPos = el
@@ -363,7 +367,11 @@ defineExpose({
363
367
recompute,
364
368
setReference: (el: IPopupReference | null) => {
365
369
buttonEl.value = el
366
- }
370
+ },
371
+ setBackground: (el: IPopupReference | null) => {
372
+ backgroundEl.value = el
373
+ },
374
+
367
375
})
368
376
369
377
</script>
0 commit comments