@navikt/ds-react
Version:
React components from the Norwegian Labour and Welfare Administration.
61 lines (51 loc) • 2.12 kB
text/typescript
import type { Middleware, Placement } from "@floating-ui/react-dom";
type Side = "top" | "right" | "bottom" | "left";
type Align = "start" | "center" | "end";
type Measurable = { getBoundingClientRect(): DOMRect };
/**
* `transformOrigin` is a custom middleware for floating-ui that calculates the transform origin of the floating-element.
*/
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 } };
},
};
}
function getSideAndAlignFromPlacement(placement: Placement) {
const [side, align = "center"] = placement.split("-");
return [side as Side, align as Align] as const;
}
export { getSideAndAlignFromPlacement, transformOrigin };
export type { Side, Align, Measurable };