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 })
|
|
|
|
}
|