You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
79 lines
2.9 KiB
79 lines
2.9 KiB
// size — computes the width/height available to the floating element within the
|
|
// clipping boundary and hands it to `apply` (the consumer writes CSS vars).
|
|
// PURE. Our engine never re-reads DOM mid-loop: `apply` doesn't resize the
|
|
// floating element synchronously in the soma layer, so the floating-ui
|
|
// `reset: { rects: true }` round-trip is unnecessary (a real resize re-triggers
|
|
// via the autoUpdate ResizeObserver). Reimplemented from floating-ui's `size`.
|
|
|
|
import { getAlignment, getSide, getSideAxis } from '../geometry'
|
|
import { detectOverflow, type DetectOverflowOptions } from '../overflow'
|
|
import type { Middleware, MiddlewareState } from '../types'
|
|
|
|
export type SizeApplyArgs = MiddlewareState & {
|
|
availableWidth: number
|
|
availableHeight: number
|
|
}
|
|
|
|
export type SizeOptions = DetectOverflowOptions & {
|
|
apply?: (args: SizeApplyArgs) => void
|
|
}
|
|
|
|
const { min, max } = Math
|
|
|
|
export function size(options: SizeOptions = {}): Middleware {
|
|
const { apply = () => {}, ...detectOptions } = options
|
|
return {
|
|
name: 'size',
|
|
options,
|
|
fn(state) {
|
|
const { placement, rects, rtl, middlewareData } = state
|
|
const overflow = detectOverflow(state, detectOptions)
|
|
const side = getSide(placement)
|
|
const alignment = getAlignment(placement)
|
|
const isYAxis = getSideAxis(placement) === 'y'
|
|
const { width, height } = rects.floating
|
|
|
|
let heightSide: 'top' | 'bottom'
|
|
let widthSide: 'left' | 'right'
|
|
if (side === 'top' || side === 'bottom') {
|
|
heightSide = side
|
|
widthSide = alignment === (rtl ? 'start' : 'end') ? 'left' : 'right'
|
|
} else {
|
|
widthSide = side as 'left' | 'right'
|
|
heightSide = alignment === 'end' ? 'top' : 'bottom'
|
|
}
|
|
|
|
const maximumClippingHeight = height - overflow.top - overflow.bottom
|
|
const maximumClippingWidth = width - overflow.left - overflow.right
|
|
const overflowAvailableHeight = min(height - overflow[heightSide], maximumClippingHeight)
|
|
const overflowAvailableWidth = min(width - overflow[widthSide], maximumClippingWidth)
|
|
|
|
const shiftData = middlewareData.shift as
|
|
| { enabled?: { x?: boolean; y?: boolean } }
|
|
| undefined
|
|
const noShift = !shiftData
|
|
let availableHeight = overflowAvailableHeight
|
|
let availableWidth = overflowAvailableWidth
|
|
if (shiftData?.enabled?.x) availableWidth = maximumClippingWidth
|
|
if (shiftData?.enabled?.y) availableHeight = maximumClippingHeight
|
|
|
|
if (noShift && !alignment) {
|
|
const xMin = max(overflow.left, 0)
|
|
const xMax = max(overflow.right, 0)
|
|
const yMin = max(overflow.top, 0)
|
|
const yMax = max(overflow.bottom, 0)
|
|
if (isYAxis) {
|
|
availableWidth =
|
|
width - 2 * (xMin !== 0 || xMax !== 0 ? xMin + xMax : max(overflow.left, overflow.right))
|
|
} else {
|
|
availableHeight =
|
|
height - 2 * (yMin !== 0 || yMax !== 0 ? yMin + yMax : max(overflow.top, overflow.bottom))
|
|
}
|
|
}
|
|
|
|
apply({ ...state, availableWidth, availableHeight })
|
|
return {}
|
|
}
|
|
}
|
|
}
|