@zag-js/popper
Version:
Dynamic positioning logic for ui machines
81 lines (80 loc) • 2.86 kB
JavaScript
// src/middleware.ts
var toVar = (value) => ({ variable: value, reference: `var(${value})` });
var cssVars = {
arrowSize: toVar("--arrow-size"),
arrowSizeHalf: toVar("--arrow-size-half"),
arrowBg: toVar("--arrow-background"),
transformOrigin: toVar("--transform-origin"),
arrowOffset: toVar("--arrow-offset")
};
var getSideAxis = (side) => side === "top" || side === "bottom" ? "y" : "x";
function createTransformOriginMiddleware(opts, arrowEl) {
return {
name: "transformOrigin",
fn(state) {
const { elements, middlewareData, placement, rects, y } = state;
const side = placement.split("-")[0];
const axis = getSideAxis(side);
const arrowX = middlewareData.arrow?.x || 0;
const arrowY = middlewareData.arrow?.y || 0;
const arrowWidth = arrowEl?.clientWidth || 0;
const arrowHeight = arrowEl?.clientHeight || 0;
const transformX = arrowX + arrowWidth / 2;
const transformY = arrowY + arrowHeight / 2;
const shiftY = Math.abs(middlewareData.shift?.y || 0);
const halfAnchorHeight = rects.reference.height / 2;
const arrowOffset = arrowHeight / 2;
const gutter = opts.offset?.mainAxis ?? opts.gutter;
const sideOffsetValue = typeof gutter === "number" ? gutter + arrowOffset : gutter ?? arrowOffset;
const isOverlappingAnchor = shiftY > sideOffsetValue;
const adjacentTransformOrigin = {
top: `${transformX}px calc(100% + ${sideOffsetValue}px)`,
bottom: `${transformX}px ${-sideOffsetValue}px`,
left: `calc(100% + ${sideOffsetValue}px) ${transformY}px`,
right: `${-sideOffsetValue}px ${transformY}px`
}[side];
const overlapTransformOrigin = `${transformX}px ${rects.reference.y + halfAnchorHeight - y}px`;
const useOverlap = Boolean(opts.overlap) && axis === "y" && isOverlappingAnchor;
elements.floating.style.setProperty(
cssVars.transformOrigin.variable,
useOverlap ? overlapTransformOrigin : adjacentTransformOrigin
);
return {
data: {
transformOrigin: useOverlap ? overlapTransformOrigin : adjacentTransformOrigin
}
};
}
};
}
var rectMiddleware = {
name: "rects",
fn({ rects }) {
return {
data: rects
};
}
};
var shiftArrowMiddleware = (arrowEl) => {
if (!arrowEl) return;
return {
name: "shiftArrow",
fn({ placement, middlewareData }) {
if (!middlewareData.arrow) return {};
const { x, y } = middlewareData.arrow;
const dir = placement.split("-")[0];
Object.assign(arrowEl.style, {
left: x != null ? `${x}px` : "",
top: y != null ? `${y}px` : "",
[dir]: `calc(100% + ${cssVars.arrowOffset.reference})`
});
return {};
}
};
};
export {
createTransformOriginMiddleware,
cssVars,
rectMiddleware,
shiftArrowMiddleware
};