Skip to content

Commit df5e9e9

Browse files
committed
feat: handle diagonal submenu (aim)
1 parent 75694ae commit df5e9e9

File tree

1 file changed

+64
-0
lines changed
  • packages/floating-vue/src/components

1 file changed

+64
-0
lines changed

packages/floating-vue/src/components/Popper.ts

+64
Original file line numberDiff line numberDiff line change
@@ -388,6 +388,8 @@ export default () => ({
388388

389389
methods: {
390390
show ({ event = null, skipDelay = false, force = false } = {}) {
391+
if (this.parentPopper?.lockedChild && this.parentPopper.lockedChild !== this) return
392+
391393
this.$_pendingHide = false
392394
if (force || !this.disabled) {
393395
this.$_scheduleShow(event, skipDelay)
@@ -404,10 +406,24 @@ export default () => ({
404406

405407
hide ({ event = null, skipDelay = false } = {}) {
406408
if (this.$_hideInProgress) return
409+
410+
// Abort if child is shown
407411
if (this.shownChildren.size > 0) {
408412
this.$_pendingHide = true
409413
return
410414
}
415+
416+
// Abort if aiming for the popper
417+
if (this.$_isAimingPopper()) {
418+
if (this.parentPopper) {
419+
this.parentPopper.lockedChild = this
420+
}
421+
return
422+
}
423+
if (this.parentPopper?.lockedChild === this) {
424+
this.parentPopper.lockedChild = null
425+
}
426+
411427
this.$_pendingHide = false
412428
this.$_scheduleHide(event, skipDelay)
413429

@@ -935,6 +951,28 @@ export default () => ({
935951
parent = parent.parentPopper
936952
}
937953
},
954+
955+
$_isAimingPopper () {
956+
const referenceBounds: DOMRect = this.$el.getBoundingClientRect()
957+
if (mouseX >= referenceBounds.left && mouseX <= referenceBounds.right && mouseY >= referenceBounds.top && mouseY <= referenceBounds.bottom) {
958+
const popperBounds: DOMRect = this.$_popperNode.getBoundingClientRect()
959+
const vectorX = mouseX - mousePreviousX
960+
const vectorY = mouseY - mousePreviousY
961+
const distance = (popperBounds.left + popperBounds.width / 2) - mousePreviousX + (popperBounds.top + popperBounds.height / 2) - mousePreviousY
962+
// Make the vector long enough to be sure that it can intersect with the popper
963+
const newVectorLength = distance + popperBounds.width + popperBounds.height
964+
const edgeX = mousePreviousX + vectorX * newVectorLength
965+
const edgeY = mousePreviousY + vectorY * newVectorLength
966+
// Check for collision between the vector and the popper bounds
967+
return (
968+
lineIntersectsLine(mousePreviousX, mousePreviousY, edgeX, edgeY, popperBounds.left, popperBounds.top, popperBounds.left, popperBounds.bottom) || // Left edge
969+
lineIntersectsLine(mousePreviousX, mousePreviousY, edgeX, edgeY, popperBounds.left, popperBounds.top, popperBounds.right, popperBounds.top) || // Top edge
970+
lineIntersectsLine(mousePreviousX, mousePreviousY, edgeX, edgeY, popperBounds.right, popperBounds.top, popperBounds.right, popperBounds.bottom) || // Right edge
971+
lineIntersectsLine(mousePreviousX, mousePreviousY, edgeX, edgeY, popperBounds.left, popperBounds.bottom, popperBounds.right, popperBounds.bottom) // Bottom edge
972+
)
973+
}
974+
return false
975+
},
938976
},
939977

940978
render () {
@@ -1014,3 +1052,29 @@ export function hideAllPoppers () {
10141052
popper.hide()
10151053
}
10161054
}
1055+
1056+
// Track mouse movement to detect aiming at the popper
1057+
1058+
let mousePreviousX = 0
1059+
let mousePreviousY = 0
1060+
let mouseX = 0
1061+
let mouseY = 0
1062+
1063+
if (typeof window !== 'undefined') {
1064+
window.addEventListener('mousemove', event => {
1065+
mousePreviousX = mouseX
1066+
mousePreviousY = mouseY
1067+
mouseX = event.clientX
1068+
mouseY = event.clientY
1069+
}, supportsPassive
1070+
? {
1071+
passive: true,
1072+
}
1073+
: undefined)
1074+
}
1075+
1076+
function lineIntersectsLine (x1: number, y1: number, x2: number, y2: number, x3: number, y3: number, x4: number, y4: number) {
1077+
const uA = ((x4 - x3) * (y1 - y3) - (y4 - y3) * (x1 - x3)) / ((y4 - y3) * (x2 - x1) - (x4 - x3) * (y2 - y1))
1078+
const uB = ((x2 - x1) * (y1 - y3) - (y2 - y1) * (x1 - x3)) / ((y4 - y3) * (x2 - x1) - (x4 - x3) * (y2 - y1))
1079+
return (uA >= 0 && uA <= 1 && uB >= 0 && uB <= 1)
1080+
}

0 commit comments

Comments
 (0)