UNPKG

coupdoeil

Version:

Javascript for Ruby on Rails Coupdoeil gem

75 lines (62 loc) 2.25 kB
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})` }) }