context-menu-demo/src/context.js

88 lines
2.6 KiB
JavaScript
Raw Normal View History

2020-04-17 14:49:19 +02:00
export function calculatePosition(parentRect, childRect, alignments) {
const { parentH, parentV, childH, childV } = alignments
const baseLeft = parentRect[parentH]
const baseTop = parentRect[parentV]
const left = childH === 'left' ? baseLeft : baseLeft - childRect.width
const top = childV === 'top' ? baseTop : baseTop - childRect.height
return updateRect(childRect, { left, top })
}
export function alignChild(parent, child, container, alignments, containment) {
const parentRect = parent.getBoundingClientRect()
let childRect = child.getBoundingClientRect()
const containerRect = container.getBoundingClientRect()
childRect = calculatePosition(parentRect, childRect, alignments)
const overflows = getOverflows(containerRect, childRect)
if (containment.invertH && overflows.h) {
alignments = invertH(alignments)
}
2020-04-20 08:54:26 +02:00
if (containment.invertV && overflows.v) {
2020-04-17 14:49:19 +02:00
alignments = invertV(alignments)
}
childRect = calculatePosition(parentRect, childRect, alignments)
if (containment.containInto) {
childRect = containInto(containerRect, childRect)
}
child.style.top = childRect.top
child.style.left = childRect.left
}
const alignMapping = {
top: 'bottom',
bottom: 'top',
left: 'right',
right: 'left',
}
function invertH(alignments) {
const parentH = alignMapping[alignments.parentH]
const childH = alignMapping[alignments.childH]
return { ...alignments, parentH, childH }
}
function invertV(alignments) {
const parentV = alignMapping[alignments.parentV]
const childV = alignMapping[alignments.childV]
return { ...alignments, parentV, childV }
}
/**
* Sets new top and left property of a rectangle
* @param {Object} rect
* @param {Object} updates
*/
export function updateRect(rect, { left, top }) {
const { width, height } = rect
top = top || rect.top
left = left || rect.left
const right = left + width
const bottom = top + height
return { top, bottom, left, right, width, height }
}
export function getOverflows(parentRect, childRect) {
const top = childRect.top < parentRect.top
const bottom = childRect.bottom > parentRect.bottom
const left = childRect.left < parentRect.left
const right = childRect.right > parentRect.right
const h = left || right
const v = top || bottom
const any = h || v
return { top, bottom, left, right, h, v, any }
}
export function containInto(parent, child) {
let top = child.top
top = Math.min(top, parent.bottom - child.height)
top = Math.max(top, parent.top)
let left = child.left
left = Math.min(left, parent.right - child.width)
left = Math.max(left, parent.left)
return updateRect(child, { top, left })
}