coupdoeil
Version:
Javascript for Ruby on Rails Coupdoeil gem
75 lines (62 loc) • 2.25 kB
JavaScript
import {
autoPlacement,
computePosition,
detectOverflow,
offset,
arrow
} from "@floating-ui/dom"
export async function positionPopover(target, card, options) {
let { placement: placements, offset: offsetValue } = options
const placement = placements[0]
const arrowElement = card.querySelector('[data-popover-arrow]')
const middleware = [AutoPositioningWithFallbacks(placements)]
if (arrowElement) {
const arrowSize = arrowElement.clientWidth
offsetValue += arrowSize
middleware.push(arrow({ element: arrowElement }))
}
middleware.push(offset(offsetValue))
const computedPosition = await computePosition(
target, card, { placement, middleware }
)
const { x, y, placement: actualPlacement} = computedPosition
if (arrowElement) {
positionArrow(arrowElement, computedPosition)
}
card.dataset.placement = actualPlacement
Object.assign(card.style, { left: `${x}px`, top: `${y}px` })
}
const AutoPositioningWithFallbacks = (placements) => {
let placementIndex = 0
return {
name: 'autoPlacement',
async fn(state) {
if (state.placement === 'auto') {
return autoPlacement().fn(state)
}
const { top, bottom, left, right } = await detectOverflow(state)
const isOverflowing = (top > 0 || bottom > 0 || left > 0 || right > 0)
if (placements[placementIndex] !== 'auto' && isOverflowing) {
placementIndex++
return { reset: { placement: placements[placementIndex] || 'auto' } }
} else if (isOverflowing) {
return autoPlacement().fn(state)
}
return {}
}
}
}
const OPPOSITE_SIDES = { right: 'left', left: 'right', top: 'bottom', bottom: 'top' }
function positionArrow(arrowElement, computedData) {
const arrowSize = arrowElement.clientWidth
const { placement, middlewareData: { arrow: arrowData } } = computedData
const side = placement.split("-")[0]
const oppositeSide = OPPOSITE_SIDES[side]
const { x, y } = arrowData
const { borderWidth: parentBorderWidht } = getComputedStyle(arrowElement.parentElement)
Object.assign(arrowElement.style, {
left: x != null ? `${x}px` : '',
top: y != null ? `${y}px` : '',
[oppositeSide]: `calc(${-arrowSize}px + ${parentBorderWidht})`
})
}