@kobalte/core
Version:
Unstyled components and primitives for building accessible web apps and design systems with SolidJS.
102 lines (81 loc) • 2.13 kB
text/typescript
import type { Direction } from "../i18n";
export type BasePlacement = "top" | "bottom" | "left" | "right";
export type Placement =
| BasePlacement
| `${BasePlacement}-start`
| `${BasePlacement}-end`;
export type AnchorRect = {
x?: number;
y?: number;
width?: number;
height?: number;
};
function createDOMRect(anchorRect?: AnchorRect) {
const { x = 0, y = 0, width = 0, height = 0 } = anchorRect ?? {};
if (typeof DOMRect === "function") {
return new DOMRect(x, y, width, height);
}
// JSDOM doesn't support DOMRect constructor.
const rect = {
x,
y,
width,
height,
top: y,
right: x + width,
bottom: y + height,
left: x,
};
return { ...rect, toJSON: () => rect };
}
export function getAnchorElement(
anchor: HTMLElement | undefined,
getAnchorRect: (anchor?: HTMLElement) => AnchorRect | undefined,
) {
// https://floating-ui.com/docs/virtual-elements
const contextElement = anchor;
return {
contextElement,
getBoundingClientRect: () => {
const anchorRect = getAnchorRect(anchor);
if (anchorRect) {
return createDOMRect(anchorRect);
}
if (anchor) {
return anchor.getBoundingClientRect();
}
return createDOMRect();
},
};
}
export function isValidPlacement(flip: string): flip is Placement {
return /^(?:top|bottom|left|right)(?:-(?:start|end))?$/.test(flip);
}
const REVERSE_BASE_PLACEMENT = {
top: "bottom",
right: "left",
bottom: "top",
left: "right",
};
export function getTransformOrigin(
placement: Placement,
readingDirection: Direction,
) {
const [basePlacement, alignment] = placement.split("-") as [
BasePlacement,
"start" | "end" | undefined,
];
const reversePlacement = REVERSE_BASE_PLACEMENT[basePlacement];
if (!alignment) {
return `${reversePlacement} center`;
}
if (basePlacement === "left" || basePlacement === "right") {
return `${reversePlacement} ${alignment === "start" ? "top" : "bottom"}`;
}
if (alignment === "start") {
return `${reversePlacement} ${
readingDirection === "rtl" ? "right" : "left"
}`;
}
return `${reversePlacement} ${readingDirection === "rtl" ? "left" : "right"}`;
}