// Copyright (c) Meta Platforms, Inc. and affiliates. 'use client'; /** * @file Popover.tsx * @input Uses React layout measurement and the usePopover hook * @output Exports Popover with viewport fitting, conditional overflow, and content-first focus * @position Layer component; declarative wrapper around usePopover hook * * For hover-triggered overlays, use HoverCard instead. * * SYNC: When modified, update these files to stay in sync: * - /packages/core/src/Popover/Popover.test.tsx * - /packages/core/src/Popover/index.ts * - /apps/storybook/stories/Popover.stories.tsx * - /packages/cli/assets/templates/blocks/components/Popover/ (showcase blocks) */ import React, { useCallback, useRef, useState, type ReactElement, type ReactNode } from 'react'; import { useIsomorphicLayoutEffect } from "../hooks/useIsomorphicLayoutEffect.js"; import * as stylex from "../style-runtime.js"; import { devWarn } from "../utils/devWarning.js"; import type { BaseProps } from "../BaseProps.js"; import { usePopover } from "./usePopover.js"; import type { LayerAlignment, LayerPlacement } from "../Layer/useLayer.js"; import { layerAnimations } from "../Layer/layerAnimations.stylex.js"; import "../theme/tokens.stylex.js"; import { spacingVars } from "../theme/tokens.stylex.js"; import { InteractiveRoleContext } from "../InteractiveRoleContext/InteractiveRoleContext.js"; // ============================================================================= // Helpers // ============================================================================= const BUTTON_SELECTOR = 'button, [role="button"]'; const POPOVER_VIEWPORT_GUTTER = spacingVars['--spacing-4']; const POPOVER_MAX_INLINE_SIZE = `calc(100vi - max(${POPOVER_VIEWPORT_GUTTER}, env(safe-area-inset-left, 0px)) - max(${POPOVER_VIEWPORT_GUTTER}, env(safe-area-inset-right, 0px)))`; const POPOVER_MAX_INLINE_SIZE_FALLBACK = `calc(100vw - ${POPOVER_VIEWPORT_GUTTER} - ${POPOVER_VIEWPORT_GUTTER})`; const POPOVER_MAX_BLOCK_SIZE = `calc(100dvb - max(${POPOVER_VIEWPORT_GUTTER}, env(safe-area-inset-top, 0px)) - max(${POPOVER_VIEWPORT_GUTTER}, env(safe-area-inset-bottom, 0px)))`; const POPOVER_MAX_BLOCK_SIZE_FALLBACK = `calc(100vh - ${POPOVER_VIEWPORT_GUTTER} - ${POPOVER_VIEWPORT_GUTTER})`; const POPOVER_POSITION_AREA_MAX_INLINE_SIZE = `calc(100% - max(${POPOVER_VIEWPORT_GUTTER}, env(safe-area-inset-left, 0px), env(safe-area-inset-right, 0px)))`; const POPOVER_POSITION_AREA_MAX_INLINE_SIZE_FALLBACK = `calc(100% - ${POPOVER_VIEWPORT_GUTTER})`; const POPOVER_INLINE_EDGE_GUTTER = `max(${POPOVER_VIEWPORT_GUTTER}, env(safe-area-inset-left, 0px), env(safe-area-inset-right, 0px))`; /** * Find the trigger button inside a container element. * Looks for `