import type { JSX } from "@solidjs/web"; /** * Solid port of Base UI React's `InternalBackdrop` * (`packages/react/src/utils/InternalBackdrop.tsx`). * * Full-viewport backdrop for modal popups, with optional `cutout` — a * DOM element whose rectangle is cut out of the backdrop via `clip-path` * so it remains interactive (useful for letting a trigger stay clickable * while the rest of the page is covered). * * Port notes: * - React's `forwardRef` is replaced with a plain `ref` prop the * consumer hands a callback to (Solid ref syntax). * - `React.HTMLAttributes` → `JSX.HTMLAttributes`. * - Style is a Solid `JSX.CSSProperties` literal. React's camelCase * `WebkitUserSelect` becomes the Solid-style `-webkit-user-select` * key because Solid forwards CSS-property keys verbatim. */ import { createMemo, omit, untrack } from "solid-js"; export type InternalBackdropState = Record; export interface InternalBackdropProps extends JSX.HTMLAttributes { /** * The element to cut out of the backdrop. * This is useful for allowing certain elements to be interactive while * the backdrop is present. */ cutout?: Element | null | undefined; ref?: ((el: HTMLDivElement) => void) | HTMLDivElement | undefined; } /** * @internal */ export function InternalBackdrop(props: InternalBackdropProps): JSX.Element { // Memoize so the proxy read of `props.cutout` happens inside a tracked // scope, not synchronously in the component body (which is wrapped in // Solid 2's `untrack(label)` strict-read shell). const style = createMemo(() => { const cutout = props.cutout; let clipPath: string | undefined; if (cutout) { const rect = cutout.getBoundingClientRect(); clipPath = `polygon(0% 0%,100% 0%,100% 100%,0% 100%,0% 0%,${rect.left}px ${rect.top}px,${rect.left}px ${rect.bottom}px,${rect.right}px ${rect.bottom}px,${rect.right}px ${rect.top}px,${rect.left}px ${rect.top}px)`; } return { position: "fixed", inset: 0, "user-select": "none", "-webkit-user-select": "none", "clip-path": clipPath, }; }); // Explicitly strip `cutout` and `ref` before spreading so they don't land // on the DOM as unknown attributes. `omit` returns a reactive proxy that // defers reads until Solid's spread-effect runs in a tracked scope, // avoiding `[STRICT_READ_UNTRACKED]` warnings from synchronous body reads. const rest = omit(props, "cutout", "ref"); // Ref forwarder — Solid web's `applyRef` invokes this callback inside // `runWithOwner` of the InternalBackdrop body, which inherits the // `strictRead` label. The `props.ref` read is a one-shot snapshot at // mount; `untrack` keeps it out of the strict-read shell. const handleRef = (el: HTMLDivElement): void => { const r = untrack(() => props.ref); if (typeof r === "function") { (r as (el: HTMLDivElement) => void)(el); } // Object refs (`{ current }`) aren't part of this hook's contract — // callers always pass a function. }; return (
); } export namespace InternalBackdrop { export type State = InternalBackdropState; export type Props = InternalBackdropProps; }