55 lines
1.8 KiB
TypeScript
55 lines
1.8 KiB
TypeScript
import { CSSProperties } from 'react'
|
|
import { PopoverAlignment, PopoverSide } from './Types'
|
|
import { OppositeSide, checkCollisions, getNonCollidingSide, getNonCollidingAlignment } from './Utils/Collisions'
|
|
import { getPositionedPopoverRect } from './Utils/Rect'
|
|
|
|
const getStylesFromRect = (rect: DOMRect): CSSProperties => {
|
|
return {
|
|
willChange: 'transform',
|
|
transform: `translate(${rect.x}px, ${rect.y}px)`,
|
|
}
|
|
}
|
|
|
|
type Options = {
|
|
align: PopoverAlignment
|
|
anchorRect?: DOMRect
|
|
documentRect: DOMRect
|
|
popoverRect?: DOMRect
|
|
side: PopoverSide
|
|
}
|
|
|
|
export const getPositionedPopoverStyles = ({
|
|
align,
|
|
anchorRect,
|
|
documentRect,
|
|
popoverRect,
|
|
side,
|
|
}: Options): [CSSProperties | null, PopoverSide, PopoverAlignment] => {
|
|
if (!popoverRect || !anchorRect) {
|
|
return [null, side, align]
|
|
}
|
|
|
|
const matchesMediumBreakpoint = matchMedia('(min-width: 768px)').matches
|
|
|
|
if (!matchesMediumBreakpoint) {
|
|
return [null, side, align]
|
|
}
|
|
|
|
const rectForPreferredSide = getPositionedPopoverRect(popoverRect, anchorRect, side, align)
|
|
const preferredSideRectCollisions = checkCollisions(rectForPreferredSide, documentRect)
|
|
|
|
const oppositeSide = OppositeSide[side]
|
|
const rectForOppositeSide = getPositionedPopoverRect(popoverRect, anchorRect, oppositeSide, align)
|
|
const oppositeSideRectCollisions = checkCollisions(rectForOppositeSide, documentRect)
|
|
|
|
const finalSide = getNonCollidingSide(side, preferredSideRectCollisions, oppositeSideRectCollisions)
|
|
const finalAlignment = getNonCollidingAlignment(finalSide, align, preferredSideRectCollisions, {
|
|
popoverRect,
|
|
buttonRect: anchorRect,
|
|
documentRect,
|
|
})
|
|
const finalPositionedRect = getPositionedPopoverRect(popoverRect, anchorRect, finalSide, finalAlignment)
|
|
|
|
return [getStylesFromRect(finalPositionedRect), finalSide, finalAlignment]
|
|
}
|