// Copyright (c) Meta Platforms, Inc. and affiliates. 'use client'; /** * @file OverlayScrim.tsx * @input Scrim props (mode, position, align, visibility) * @output Renders the scrim div with background, media theme, transitions * @position Internal rendering primitive; used by Overlay and useOverlay * * Not exported directly — consumers use Overlay (component) or * useOverlay (hook), both of which render this internally. */ import type { ReactNode } from 'react'; import * as stylex from "../style-runtime.js"; import "../theme/tokens.stylex.js"; import { colorVars, durationVars, easeVars, spacingVars } from "../theme/tokens.stylex.js"; import { MediaTheme } from "../theme/MediaTheme.js"; import { mergeProps } from "../utils/index.js"; import "./overlay.markers.stylex.js"; import { overlayScope } from "./overlay.markers.stylex.js"; import { themeProps } from "../utils/themeProps.js"; // ============================================================================= // Types (re-exported from index.ts for consumers) // ============================================================================= export type OverlayScrimMode = 'dark' | 'light' | false; export type OverlayPosition = 'fill' | 'bottom' | 'top'; export type OverlayAlign = 'start' | 'center' | 'end'; export type OverlayShowOn = 'hover' | 'always' | 'focus' | 'hover-or-focus'; export interface OverlayScrimProps { scrim: OverlayScrimMode; position: OverlayPosition; align: OverlayAlign; showOn: OverlayShowOn; isOpen: boolean | undefined; children: ReactNode; } // ============================================================================= // Styles // ============================================================================= const styles = { base: { kVAEAm: "x10l6tqk", k1xSpc: "x78zum5", kXwgrk: "xdt5ytf", kOIVth: "x1txdalj", kmVPX3: "x1b2ylru", kfzvcC: "x47corl", k1ekBW: "xxup5nn", kIyJzY: "xuedmi6 x12w9bfk", kAMwcw: "xlr8y92", $$css: true as const }, fill: { kpwlN0: "x10a8y8t", $$css: true as const }, bottom: { kkqhue: "x17y0mx6", krVfgx: "x1ey2m1c", $$css: true as const }, top: { kkqhue: "x17y0mx6", k87sOh: "x13vifvy", $$css: true as const }, alignStart: { kGNEyG: "x1cy8zhl", kjj79g: "x1nhvcw1", $$css: true as const }, alignCenter: { kGNEyG: "x6s0dn4", kjj79g: "xl56j7k", $$css: true as const }, alignEnd: { kGNEyG: "xuk3077", kjj79g: "x1nhvcw1", $$css: true as const }, scrimDark: { kWkggS: "x1u4601x", $$css: true as const }, scrimLight: { kWkggS: "x1rkw3yq", $$css: true as const }, hidden: { kSiTet: "xg01cxk", k33iCy: "xlshs6z", $$css: true as const }, hiddenBottom: { k3aq6I: "x1weeur4", $$css: true as const }, hiddenTop: { k3aq6I: "x105ttfm", $$css: true as const }, visible: { kSiTet: "x1hc1fzr", k33iCy: "xnpuxes", kfzvcC: "x67bb7w", k3aq6I: "xnn1q72", kamtoy: "x4itv7f", $$css: true as const }, visibleFromBottom: { kamtoy: "x4itv7f", k1tdAh: "xhbqy3z", $$css: true as const }, visibleFromTop: { kamtoy: "x4itv7f", k1tdAh: "xub2912", $$css: true as const }, hoverReveal: { kSiTet: "xg01cxk xcwhr9z x1v2claj", k33iCy: "xlshs6z xneequu x7v6acp", kfzvcC: "x47corl x8ydxbz x3730vf", $$css: true as const }, hoverRevealBottom: { k3aq6I: "x1weeur4 x17hxqr7 xuqivmr", $$css: true as const }, hoverRevealTop: { k3aq6I: "x105ttfm x17hxqr7 xuqivmr", $$css: true as const }, focusReveal: { kSiTet: "xg01cxk x1v2claj", k33iCy: "xlshs6z x7v6acp", kfzvcC: "x47corl x3730vf", $$css: true as const }, focusRevealBottom: { k3aq6I: "x1weeur4 xuqivmr", $$css: true as const }, focusRevealTop: { k3aq6I: "x105ttfm xuqivmr", $$css: true as const } }; const alignMap = { start: styles.alignStart, center: styles.alignCenter, end: styles.alignEnd } as const; const positionMap = { fill: styles.fill, bottom: styles.bottom, top: styles.top } as const; // ============================================================================= // Visibility style helpers — extract branching logic from render // ============================================================================= /** * Returns visibility/animation styles for JS-controlled overlays (isOpen defined). * Uses position to determine slide direction for strips. */ function getControlledVisibility(isOpen: boolean, position: OverlayPosition) { if (isOpen) { return { base: styles.visible, bottom: position === 'bottom' && styles.visibleFromBottom, top: position === 'top' && styles.visibleFromTop }; } return { base: styles.hidden, bottom: position === 'bottom' && styles.hiddenBottom, top: position === 'top' && styles.hiddenTop }; } /** * Returns visibility/animation styles for CSS-driven overlays (showOn mode). * Each mode has a base reveal style + optional position-based slide. */ function getShowOnVisibility(showOn: OverlayShowOn, position: OverlayPosition) { switch (showOn) { case 'always': return { base: styles.visible, bottom: position === 'bottom' && styles.visibleFromBottom, top: position === 'top' && styles.visibleFromTop }; case 'hover': case 'hover-or-focus': return { base: styles.hoverReveal, bottom: position === 'bottom' && styles.hoverRevealBottom, top: position === 'top' && styles.hoverRevealTop }; case 'focus': return { base: styles.focusReveal, bottom: position === 'bottom' && styles.focusRevealBottom, top: position === 'top' && styles.focusRevealTop }; } } // ============================================================================= // Component (internal) // ============================================================================= export function OverlayScrim({ scrim, position, align, showOn, isOpen, children }: OverlayScrimProps) { const isControlled = isOpen !== undefined; const themeMode = scrim === 'dark' ? 'dark' : scrim === 'light' ? 'light' : null; const content = themeMode ? {children} : children; const visibility = isControlled ? getControlledVisibility(isOpen, position) : getShowOnVisibility(showOn, position); return
{content}
; }