/** * Solid 2 port of Base UI React's `adaptiveOrigin` middleware * (`packages/react/src/utils/adaptiveOriginMiddleware.ts`). * * When the floating element has a CSS transition active, this middleware * rewrites the positioning coordinates so the popup anchors to the side * opposite the transition's starting origin. That lets consumers animate * `transform` / `opacity` without the popup jumping from a stale rect * while the transition is mid-flight. */ import type { Middleware } from "@floating-ui/dom"; import { getSide } from "@floating-ui/utils"; import { ownerDocument } from "../dom/owner-document"; import { ownerWindow } from "../dom/owner-window"; export const DEFAULT_SIDES = { sideX: "left", sideY: "top", } as const; export const adaptiveOriginMiddleware: Middleware = { name: "adaptiveOrigin", async fn(state) { const { x: rawX, y: rawY, rects: { floating: floatRect }, elements: { floating }, platform, strategy, placement, } = state; const win = ownerWindow(floating); const styles = win.getComputedStyle(floating); const hasTransition = styles.transitionDuration !== "0s" && styles.transitionDuration !== ""; if (!hasTransition) { return { x: rawX, y: rawY, data: DEFAULT_SIDES, }; } const offsetParent = await platform.getOffsetParent?.(floating); let offsetDimensions = { width: 0, height: 0 }; if (strategy === "fixed" && win?.visualViewport) { offsetDimensions = { width: win.visualViewport.width, height: win.visualViewport.height, }; } else if (offsetParent === win) { const doc = ownerDocument(floating); offsetDimensions = { width: doc.documentElement.clientWidth, height: doc.documentElement.clientHeight, }; } else if (await platform.isElement?.(offsetParent)) { offsetDimensions = await platform.getDimensions(offsetParent as Element); } const currentSide = getSide(placement); let x = rawX; let y = rawY; if (currentSide === "left") { x = offsetDimensions.width - (rawX + floatRect.width); } if (currentSide === "top") { y = offsetDimensions.height - (rawY + floatRect.height); } const sideX = currentSide === "left" ? "right" : DEFAULT_SIDES.sideX; const sideY = currentSide === "top" ? "bottom" : DEFAULT_SIDES.sideY; return { x, y, data: { sideX, sideY, }, }; }, };