JGuang

气泡弹层

Popover · Material UI

交互预览
npm install @mui/material@9.4.0 @emotion/react@11.14.0 @emotion/styled@11.14.1

气泡弹层

以带模态行为的弹层将内容锚定到目标,并处理方向和关闭。

本指南覆盖默认 Emotion 环境的 Material UI Core;扩展包与额外示例按清单单独标注。

使用场景与业务边界

Popover 包含 Modal 行为;非模态定位使用 Popper,并管理真实锚点生命周期。

原生组成与范围

Grow, Popover

保持 Material UI 原生组合与类型,不替换为 JGuang/Base UI/Radix 同名 API。示例条目不是额外组件。

原生安装与初始化

npm install @mui/material@9.4.0 @emotion/react@11.14.0 @emotion/styled@11.14.1

采用原生 npm 包与 Emotion,不提供虚构的 JGuang UI Registry 地址。将示例放在原生 ThemeProvider 内,按项目选择 CssBaseline 或 ScopedCssBaseline。先检查 React、现有 Provider、锁文件与 SSR 配置;不要覆盖用户文件或忽略 peer 冲突。本批实际验收 React 19.2.8;React 18 及以下须匹配 react-is。完整初始化规则见随附的双语 setup 文档。

关键原生参数

仅解释本指南选出的关键参数,不是平铺的通用 API。原生继承来源、slot 结构、可空值与泛型仍按固定源码保留。

所属组件.属性用途说明声明来源
Popover.open受控原生可见性,不获取数据或执行操作。Popover
Popover.anchorEl真实或受支持的虚拟锚点,保留所属类型与生命周期。Popover
Popover.anchorOrigin原生垂直/水平方向的锚点组合。Popover
Popover.transformOrigin原生气泡内容的对齐原点。Popover
Popover.onClose原生关闭请求,reason/event 签名因组件不同,宿主须更新 open。Popover

可运行原生示例

下列机器键只选择示例入口,不会作为通用 variant 属性传入每个组件。原生示例 JSX 与数据保持上游原文;JGuang 编写的验收示例明确标注。

  • 基础模态气泡弹层 · BasicPopover · 上游原生示例
  • 鼠标悬停交互 · MouseHoverPopover · 上游原生示例
  • 虚拟元素锚点 · VirtualElementPopover · 上游原生示例

尚未支持的额外示例

这些条目仍保留在上游清单中,不静默跳过,也不展示为已完成预览。需要补充依赖/框架验收后再纳入可运行列表。

  • 锚点演示 · AnchorPlayground: @mui/internal-core-docs/HighlightedCode
  • 额外弹层状态库 · PopoverPopupState: material-ui-popup-state

真实使用示例

固定版本的未修改原生示例;复制时同时保留所需的局部辅助文件。

import * as React from 'react';
import Popover from '@mui/material/Popover';
import Typography from '@mui/material/Typography';
import Button from '@mui/material/Button';

export default function BasicPopover() {
  const [anchorEl, setAnchorEl] = React.useState<HTMLButtonElement | null>(null);

  const handleClick = (event: React.MouseEvent<HTMLButtonElement>) => {
    setAnchorEl(event.currentTarget);
  };

  const handleClose = () => {
    setAnchorEl(null);
  };

  const open = Boolean(anchorEl);
  const id = open ? 'simple-popover' : undefined;

  return (
    <div>
      <Button aria-describedby={id} variant="contained" onClick={handleClick}>
        Open Popover
      </Button>
      <Popover
        id={id}
        open={open}
        anchorEl={anchorEl}
        onClose={handleClose}
        anchorOrigin={{
          vertical: 'bottom',
          horizontal: 'left',
        }}
      >
        <Typography sx={{ p: 2 }}>The content of the Popover.</Typography>
      </Popover>
    </div>
  );
}

示例依赖边界

react, @mui/material/Popover, @mui/material/Typography, @mui/material/Button, @mui/material/Paper

直接导入 Icons/System/Utils 的示例需要匹配 9.4.0 包。局部数据与辅助文件属于示例闭包,不是额外组件。远程图片和模拟操作不提供后端。

固定上游 API 元数据参考

以下表格保留上游元数据,不是重新推断的通用类型体系。条件泛型、继承属性与回调细节以以下源码声明为准。主题默认配置可能覆盖元数据默认值。

Grow

import Grow from '@mui/material/Grow';
import { Grow } from '@mui/material';

原生继承: Transition. 父级与 DOM 属性未被伪造为本组件新属性。

属性必需上游元数据类型/约束元数据默认值
children是element—
addEndListener否func—
appear否booltrue
disablePrefersReducedMotion否boolfalse
easing否{ enter?: string, exit?: string } | string—
in否bool—
timeout否'auto' | number | { appear?: number, enter?: number, exit?: number }'auto'

Popover

import Popover from '@mui/material/Popover';
import { Popover } from '@mui/material';

原生继承: Modal. 父级与 DOM 属性未被伪造为本组件新属性。

属性必需上游元数据类型/约束元数据默认值
open是bool—
action否ref—
anchorEl否HTML element | func—
anchorOrigin否{ horizontal: 'center' | 'left' | 'right' | number, vertical: 'bottom' | 'center' | 'top' | number }{ vertical: 'top', horizontal: 'left', }
anchorPosition否{ left: number, top: number }—
anchorReference否'anchorEl' | 'anchorPosition' | 'none''anchorEl'
children否node—
classes否object—
container否HTML element | func—
disableAutoFocus否boolfalse
disableScrollLock否boolfalse
elevation否integer8
marginThreshold否number16
onClose否func—
slotProps否{ backdrop?: func | object, paper?: func | object, root?: func | object, transition?: func | object }{}
slots否{ backdrop?: elementType, paper?: elementType, root?: elementType, transition?: elementType }{}
sx否Array<func | object | bool> | func | object—
transformOrigin否{ horizontal: 'center' | 'left' | 'right' | number, vertical: 'bottom' | 'center' | 'top' | number }{ vertical: 'top', horizontal: 'left', }
transitionDuration否'auto' | number | { appear?: number, enter?: number, exit?: number }'auto'

原生源码声明

声明与 import 从固定源码提取,不平铺联合类型或外部继承。英文注释/类型属于原始源码参考,不代表上游正文已全文翻译。共享类型仍位于原生包中。

vendor/src/Grow/Grow.d.ts

import * as React from 'react';

import { TransitionProps } from '../transitions/types';

export interface GrowProps extends Omit<TransitionProps, 'timeout'> {
  /**
   * Add a custom transition end trigger.
   * Use it when you need custom logic to decide when the transition has ended.
   * Note: Timeouts are still used as a fallback if provided.
   *
   * @param {HTMLElement} node The transitioning DOM node.
   * @param {Function} done Call this when the transition has finished.
   */
  addEndListener?: TransitionProps['addEndListener'] | undefined;
  /**
   * Perform the enter transition when it first mounts if `in` is also `true`.
   * Set this to `false` to disable this behavior.
   * @default true
   */
  appear?: boolean | undefined;
  /**
   * A single child content element.
   */
  children: React.ReactElement<unknown, any>;
  /**
   * If `true`, the transition ignores `theme.motion.reducedMotion` and keeps its normal timing.
   * @default false
   */
  disablePrefersReducedMotion?: boolean | undefined;
  /**
   * The transition timing function.
   * You may specify a single easing or a object containing enter and exit values.
   */
  easing?: TransitionProps['easing'] | undefined;
  /**
   * If `true`, the component will transition in.
   */
  in?: boolean | undefined;
  ref?: React.Ref<unknown> | undefined;
  /**
   * The duration for the transition, in milliseconds.
   * You may specify a single timeout for all transitions, or individually with an object.
   *
   * Set to 'auto' to automatically calculate transition time based on height.
   * @default 'auto'
   */
  timeout?: TransitionProps['timeout'] | 'auto' | undefined;
}

export default function Grow(props: GrowProps): React.JSX.Element;

vendor/src/Popover/Popover.d.ts

import * as React from 'react';

import { SxProps } from '@mui/system';

import { SlotComponentProps } from '@mui/utils/types';

import { BackdropProps } from '../Backdrop';

import { Theme } from '../styles';

import { InternalStandardProps as StandardProps } from '../internal';

import { PaperProps } from '../Paper';

import { ModalProps } from '../Modal';

import { TransitionProps } from '../transitions/types';

import { PopoverClasses } from './popoverClasses';

import { CreateSlotsAndSlotProps, SlotProps } from '../utils/types';

export interface PopoverSlots {
  /**
   * The component used for the root slot.
   * @default Modal
   */
  root: React.ElementType;
  /**
   * The component used for the paper slot.
   * @default Paper
   */
  paper: React.ElementType;
  /**
   * The component used for the transition slot.
   * @default Grow
   */
  transition: React.ElementType;
  /**
   * The component used for the backdrop slot.
   * @default Backdrop
   */
  backdrop: React.ElementType;
}

export interface PopoverRootSlotPropsOverrides {}

export interface PopoverPaperSlotPropsOverrides {}

export interface PopoverTransitionSlotPropsOverrides {}

export interface PopoverBackdropSlotPropsOverrides {}

export type PopoverSlotsAndSlotProps = CreateSlotsAndSlotProps<
  PopoverSlots,
  {
    /**
     * Props forwarded to the root slot.
     * By default, the available props are based on the [Modal](https://mui.com/material-ui/api/modal/#props) component.
     */
    root: SlotProps<
      React.ElementType<ModalProps>,
      PopoverRootSlotPropsOverrides,
      PopoverOwnerState
    >;
    /**
     * Props forwarded to the paper slot.
     * By default, the available props are based on the [Paper](https://mui.com/material-ui/api/paper/#props) component.
     */
    paper: SlotProps<
      React.ElementType<PaperProps>,
      PopoverPaperSlotPropsOverrides,
      PopoverOwnerState
    >;
    /**
     * Props forwarded to the transition slot.
     * By default, the available props are based on the [Grow](https://mui.com/material-ui/api/grow/#props) component.
     */
    transition: SlotComponentProps<
      // use SlotComponentProps because transition slot does not support `component` and `sx` prop
      React.ElementType<TransitionProps>,
      TransitionProps & PopoverTransitionSlotPropsOverrides,
      PopoverOwnerState
    >;
    /**
     * Props forwarded to the backdrop slot.
     * By default, the available props are based on the [Backdrop](https://mui.com/material-ui/api/backdrop/#props) component.
     */
    backdrop: SlotProps<
      React.ElementType<BackdropProps>,
      PopoverBackdropSlotPropsOverrides,
      PopoverOwnerState
    >;
  }
>;

export interface PopoverOrigin {
  vertical: 'top' | 'center' | 'bottom' | number;
  horizontal: 'left' | 'center' | 'right' | number;
}

export interface PopoverPosition {
  top: number;
  left: number;
}

export type PopoverReference = 'anchorEl' | 'anchorPosition' | 'none';

interface PopoverVirtualElement {
  getBoundingClientRect: () => DOMRect;
  nodeType: Node['ELEMENT_NODE'];
}

export interface PopoverProps
  extends
    StandardProps<
      Omit<ModalProps, 'slots' | 'slotProps' | 'BackdropProps' | 'BackdropComponent'>,
      'children'
    >,
    PopoverSlotsAndSlotProps {
  /**
   * A ref for imperative actions.
   * It currently only supports updatePosition() action.
   */
  action?: React.Ref<PopoverActions> | undefined;
  /**
   * An HTML element, [PopoverVirtualElement](https://mui.com/material-ui/react-popover/#virtual-element),
   * or a function that returns either.
   * It's used to set the position of the popover.
   */
  anchorEl?:
    | null
    | Element
    | PopoverVirtualElement
    | (() => Element | PopoverVirtualElement | null)
    | undefined;
  /**
   * This is the point on the anchor where the popover's
   * `anchorEl` will attach to. This is not used when the
   * anchorReference is 'anchorPosition'.
   *
   * Options:
   * vertical: [top, center, bottom];
   * horizontal: [left, center, right].
   * @default {
   *   vertical: 'top',
   *   horizontal: 'left',
   * }
   */
  anchorOrigin?: PopoverOrigin | undefined;
  /**
   * This is the position that may be used to set the position of the popover.
   * The coordinates are relative to the application's client area.
   */
  anchorPosition?: PopoverPosition | undefined;
  /**
   * This determines which anchor prop to refer to when setting
   * the position of the popover.
   * @default 'anchorEl'
   */
  anchorReference?: PopoverReference | undefined;
  /**
   * The content of the component.
   */
  children?: React.ReactNode;
  /**
   * Override or extend the styles applied to the component.
   */
  classes?: Partial<PopoverClasses> | undefined;
  /**
   * An HTML element, component instance, or function that returns either.
   * The `container` will passed to the Modal component.
   *
   * By default, it uses the body of the anchorEl's top-level document object,
   * so it's simply `document.body` most of the time.
   */
  container?: ModalProps['container'] | undefined;
  /**
   * The elevation of the popover.
   * @default 8
   */
  elevation?: number | undefined;
  /**
   * Specifies how close to the edge of the window the popover can appear.
   * If null, the popover will not be constrained by the window.
   * @default 16
   */
  marginThreshold?: number | null | undefined;
  onClose?: ModalProps['onClose'] | undefined;
  /**
   * If `true`, the component is shown.
   */
  open: boolean;
  /**
   * The system prop that allows defining system overrides as well as additional CSS styles.
   */
  sx?: SxProps<Theme> | undefined;
  /**
   * This is the point on the popover which
   * will attach to the anchor's origin.
   *
   * Options:
   * vertical: [top, center, bottom, x(px)];
   * horizontal: [left, center, right, x(px)].
   * @default {
   *   vertical: 'top',
   *   horizontal: 'left',
   * }
   */
  transformOrigin?: PopoverOrigin | undefined;
  /**
   * Set to 'auto' to automatically calculate transition time based on height.
   * @default 'auto'
   */
  transitionDuration?: TransitionProps['timeout'] | 'auto' | undefined;
}

export interface PopoverOwnerState extends Omit<PopoverProps, 'slots' | 'slotProps'> {}

export interface PopoverActions {
  updatePosition(): void;
}

export function getOffsetTop(rect: DOMRect, vertical: number | 'center' | 'bottom' | 'top'): number;

export function getOffsetLeft(
  rect: DOMRect,
  horizontal: number | 'center' | 'right' | 'left',
): number;

type PopoverRootProps = NonNullable<PopoverProps['slotProps']>['root'];

type PopoverPaperProps = NonNullable<PopoverProps['slotProps']>['paper'];

export declare const PopoverRoot: React.FC<PopoverRootProps>;

export declare const PopoverPaper: React.FC<PopoverPaperProps>;

export default function Popover(props: PopoverProps): React.JSX.Element;

安装后验收

执行严格类型检查、生产构建和实际交互。检查键盘标签/焦点、原生支持的禁用/错误/只读语义、深浅色、窄屏和减少动画。按真实宿主数据检查回调,不把复制提示词当作安装完成。初始化工具还需检查服务端 HTML 与无告警水合。

来源与许可

https://github.com/mui/material-ui/tree/809a7717b4c050ba3f69b75300689f07c050a16e

MIT · @mui/material@9.4.0. 保留上游原始内容与许可证。上游完整原文保留于 vendor/docs,不标为全文中文化。