Files
standardnotes-app-web/packages/web/src/javascripts/Components/Popover/Utils/Collisions.ts
2022-11-22 19:16:59 +05:30

98 lines
2.8 KiB
TypeScript

import { PopoverSide, PopoverAlignment, RectCollisions } from '../Types'
import { getAppRect, getPositionedPopoverRect } from './Rect'
export const OppositeSide: Record<PopoverSide, PopoverSide> = {
top: 'bottom',
bottom: 'top',
left: 'right',
right: 'left',
}
export const getOverflows = (popoverRect: DOMRect, documentRect: DOMRect): Record<PopoverSide, number> => {
const overflows = {
top: documentRect.top - popoverRect.top,
bottom: popoverRect.height - (documentRect.bottom - popoverRect.top),
left: documentRect.left - popoverRect.left,
right: popoverRect.right - documentRect.right,
}
return overflows
}
export const checkCollisions = (popoverRect: DOMRect, containerRect: DOMRect): RectCollisions => {
const appRect = getAppRect(containerRect)
return {
top: popoverRect.top < appRect.top,
left: popoverRect.left < appRect.left,
bottom: popoverRect.bottom > appRect.bottom,
right: popoverRect.right > appRect.right,
}
}
export const getNonCollidingSide = (
preferredSide: PopoverSide,
preferredSideCollisions: RectCollisions,
oppositeSideCollisions: RectCollisions,
): PopoverSide => {
const oppositeSide = OppositeSide[preferredSide]
return preferredSideCollisions[preferredSide] && !oppositeSideCollisions[oppositeSide] ? oppositeSide : preferredSide
}
const OppositeAlignment: Record<Exclude<PopoverAlignment, 'center'>, PopoverAlignment> = {
start: 'end',
end: 'start',
}
export const getNonCollidingAlignment = (
finalSide: PopoverSide,
preferredAlignment: PopoverAlignment,
collisions: RectCollisions,
{
popoverRect,
buttonRect,
documentRect,
}: {
popoverRect: DOMRect
buttonRect: DOMRect
documentRect: DOMRect
},
): PopoverAlignment => {
const isHorizontalSide = finalSide === 'top' || finalSide === 'bottom'
const boundToCheckForStart = isHorizontalSide ? 'right' : 'bottom'
const boundToCheckForEnd = isHorizontalSide ? 'left' : 'top'
const prefersAligningAtStart = preferredAlignment === 'start'
const prefersAligningAtCenter = preferredAlignment === 'center'
const prefersAligningAtEnd = preferredAlignment === 'end'
if (prefersAligningAtCenter) {
if (collisions[boundToCheckForStart]) {
return 'end'
}
if (collisions[boundToCheckForEnd]) {
return 'start'
}
} else {
const oppositeAlignmentCollisions = checkCollisions(
getPositionedPopoverRect(popoverRect, buttonRect, finalSide, OppositeAlignment[preferredAlignment]),
documentRect,
)
if (
prefersAligningAtStart &&
collisions[boundToCheckForStart] &&
!oppositeAlignmentCollisions[boundToCheckForEnd]
) {
return 'end'
}
if (prefersAligningAtEnd && collisions[boundToCheckForEnd] && !oppositeAlignmentCollisions[boundToCheckForStart]) {
return 'start'
}
}
return preferredAlignment
}