Skip to content

Commit

Permalink
feat: handle diagonal submenu (aim)
Browse files Browse the repository at this point in the history
  • Loading branch information
Akryum committed Mar 23, 2022
1 parent 75694ae commit df5e9e9
Showing 1 changed file with 64 additions and 0 deletions.
64 changes: 64 additions & 0 deletions packages/floating-vue/src/components/Popper.ts
Original file line number Diff line number Diff line change
Expand Up @@ -388,6 +388,8 @@ export default () => ({

methods: {
show ({ event = null, skipDelay = false, force = false } = {}) {
if (this.parentPopper?.lockedChild && this.parentPopper.lockedChild !== this) return

this.$_pendingHide = false
if (force || !this.disabled) {
this.$_scheduleShow(event, skipDelay)
Expand All @@ -404,10 +406,24 @@ export default () => ({

hide ({ event = null, skipDelay = false } = {}) {
if (this.$_hideInProgress) return

// Abort if child is shown
if (this.shownChildren.size > 0) {
this.$_pendingHide = true
return
}

// Abort if aiming for the popper
if (this.$_isAimingPopper()) {
if (this.parentPopper) {
this.parentPopper.lockedChild = this
}
return
}
if (this.parentPopper?.lockedChild === this) {
this.parentPopper.lockedChild = null
}

this.$_pendingHide = false
this.$_scheduleHide(event, skipDelay)

Expand Down Expand Up @@ -935,6 +951,28 @@ export default () => ({
parent = parent.parentPopper
}
},

$_isAimingPopper () {
const referenceBounds: DOMRect = this.$el.getBoundingClientRect()
if (mouseX >= referenceBounds.left && mouseX <= referenceBounds.right && mouseY >= referenceBounds.top && mouseY <= referenceBounds.bottom) {
const popperBounds: DOMRect = this.$_popperNode.getBoundingClientRect()
const vectorX = mouseX - mousePreviousX
const vectorY = mouseY - mousePreviousY
const distance = (popperBounds.left + popperBounds.width / 2) - mousePreviousX + (popperBounds.top + popperBounds.height / 2) - mousePreviousY
// Make the vector long enough to be sure that it can intersect with the popper
const newVectorLength = distance + popperBounds.width + popperBounds.height
const edgeX = mousePreviousX + vectorX * newVectorLength
const edgeY = mousePreviousY + vectorY * newVectorLength
// Check for collision between the vector and the popper bounds
return (
lineIntersectsLine(mousePreviousX, mousePreviousY, edgeX, edgeY, popperBounds.left, popperBounds.top, popperBounds.left, popperBounds.bottom) || // Left edge
lineIntersectsLine(mousePreviousX, mousePreviousY, edgeX, edgeY, popperBounds.left, popperBounds.top, popperBounds.right, popperBounds.top) || // Top edge
lineIntersectsLine(mousePreviousX, mousePreviousY, edgeX, edgeY, popperBounds.right, popperBounds.top, popperBounds.right, popperBounds.bottom) || // Right edge
lineIntersectsLine(mousePreviousX, mousePreviousY, edgeX, edgeY, popperBounds.left, popperBounds.bottom, popperBounds.right, popperBounds.bottom) // Bottom edge
)
}
return false
},
},

render () {
Expand Down Expand Up @@ -1014,3 +1052,29 @@ export function hideAllPoppers () {
popper.hide()
}
}

// Track mouse movement to detect aiming at the popper

let mousePreviousX = 0
let mousePreviousY = 0
let mouseX = 0
let mouseY = 0

if (typeof window !== 'undefined') {
window.addEventListener('mousemove', event => {
mousePreviousX = mouseX
mousePreviousY = mouseY
mouseX = event.clientX
mouseY = event.clientY
}, supportsPassive
? {
passive: true,
}
: undefined)
}

function lineIntersectsLine (x1: number, y1: number, x2: number, y2: number, x3: number, y3: number, x4: number, y4: number) {
const uA = ((x4 - x3) * (y1 - y3) - (y4 - y3) * (x1 - x3)) / ((y4 - y3) * (x2 - x1) - (x4 - x3) * (y2 - y1))
const uB = ((x2 - x1) * (y1 - y3) - (y2 - y1) * (x1 - x3)) / ((y4 - y3) * (x2 - x1) - (x4 - x3) * (y2 - y1))
return (uA >= 0 && uA <= 1 && uB >= 0 && uB <= 1)
}

0 comments on commit df5e9e9

Please sign in to comment.