reka-ui
Version:
Vue port for Radix UI Primitives.
64 lines (53 loc) • 2.06 kB
text/typescript
import type { Middleware, Placement } from '@floating-ui/vue'
const SIDE_OPTIONS = ['top', 'right', 'bottom', 'left'] as const
const ALIGN_OPTIONS = ['start', 'center', 'end'] as const
export type Side = (typeof SIDE_OPTIONS)[number]
export type Align = (typeof ALIGN_OPTIONS)[number]
export function isNotNull<T>(value: T | null): value is T {
return value !== null
}
export function transformOrigin(options: {
arrowWidth: number
arrowHeight: number
}): Middleware {
return {
name: 'transformOrigin',
options,
fn(data) {
const { placement, rects, middlewareData } = data
const cannotCenterArrow = middlewareData.arrow?.centerOffset !== 0
const isArrowHidden = cannotCenterArrow
const arrowWidth = isArrowHidden ? 0 : options.arrowWidth
const arrowHeight = isArrowHidden ? 0 : options.arrowHeight
const [placedSide, placedAlign] = getSideAndAlignFromPlacement(placement)
const noArrowAlign = { start: '0%', center: '50%', end: '100%' }[
placedAlign
]
const arrowXCenter = (middlewareData.arrow?.x ?? 0) + arrowWidth / 2
const arrowYCenter = (middlewareData.arrow?.y ?? 0) + arrowHeight / 2
let x = ''
let y = ''
if (placedSide === 'bottom') {
x = isArrowHidden ? noArrowAlign : `${arrowXCenter}px`
y = `${-arrowHeight}px`
}
else if (placedSide === 'top') {
x = isArrowHidden ? noArrowAlign : `${arrowXCenter}px`
y = `${rects.floating.height + arrowHeight}px`
}
else if (placedSide === 'right') {
x = `${-arrowHeight}px`
y = isArrowHidden ? noArrowAlign : `${arrowYCenter}px`
}
else if (placedSide === 'left') {
x = `${rects.floating.width + arrowHeight}px`
y = isArrowHidden ? noArrowAlign : `${arrowYCenter}px`
}
return { data: { x, y } }
},
}
}
export function getSideAndAlignFromPlacement(placement: Placement) {
const [side, align = 'center'] = placement.split('-')
return [side as Side, align as Align] as const
}