展示类 · Popover
气泡卡片
点击/鼠标移入元素,弹出气泡式的卡片浮层。

使用场景

Popover 气泡卡片是由用户自主打开的临时性浮层卡片,能够承载一些额外内容和交互行为而不影响原页面。
和 Tooltip 的区别是,它可以承载更复杂的内容,而不仅仅是提示文本。

代码演示

如何引入

注意事项

Popover 需要将 DOM 事件监听器应用到 children 中,如果子元素是自定义的组件,你需要确保它能将属性传递至底层的 DOM 元素
同时为了计算弹出层的定位,需要获取到 children 的真实 DOM 元素,因此 Popover 支持如下类型的 children
  1. Class Component,不强制绑定ref,但需要确保 props 可被透传至真实的 DOM 节点上
  2. 使用 forwardRef 包裹后的函数式组件,将 props 与 ref 透传到 children 内真实的 DOM 节点上
  3. 真实 DOM 节点, 如 span,div,p...

基本使用

将浮层的触发器 Trigger 作为children,使用 Popover 包裹(如下的例子中触发器为 Tag 元素)。浮层内容通过content传入
注意事项同 Tooltip

弹出位置

支持通过position设置浮层弹出方向,共支持十二个方向。

受控显示

设置trigger='custom',此场景下,Popover 的显示与否完全受到参数 visible 的控制。

显示小三角

通过设置showArrow, Popover 同样也支持展示一个小三角。
这种模式下浮层会拥有一个默认的样式,你可以通过传递 style 参数来覆盖掉。

指向元素中心

显示小三角的条件(showArrow=true)下,可以传入 arrowPointAtCenter=true 使得小三角始终指向元素中心位置。

设置浮层背景色

如果你需要定制浮层的背景色或边框颜色,请务必单独声明 style 中的 backgroundColorborderColor 属性,这样能够使得“小三角”也能应用相同的背景色和边框颜色。

初始化弹出层焦点位置

Popover content 支持传入函数,它的入参是一个对象,将 initialFocusRef 绑定在可聚焦 DOM 或组件上,打开面板时会自动聚焦在该位置。

搭配 Tooltip 或 Popconfirm 使用

请参考搭配使用

API 参考

属性说明类型默认值版本
autoAdjustOverflow是否自动调整弹出层展开方向,用于边缘遮挡时自动调整展开方向booleantrue
arrowPointAtCenter“小三角”是否指向元素中心,需要同时传入"showArrow=true"booleantrue0.34.0
closeOnEsc在 trigger 或 弹出层按 Esc 键是否关闭面板,受控时不生效booleantrue2.8.0
content显示的内容(函数类型,2.8.0 版本支持)ReactNode | ({ initialFocusRef }) => ReactNode
clickToHide点击弹出层及内部任一元素时是否自动关闭弹层booleanfalse0.24.0
disableFocusListenertrigger为hover时,不响应键盘聚焦弹出浮层事件,详见issue#977booleantrue2.17.0
getPopupContainer指定父级 DOM,弹层将会渲染至该 DOM 中,自定义需要设置 position: relative 这会改变浮层 DOM 树位置,但不会改变视图渲染位置。function():HTMLElement() => document.body
guardFocus当焦点处于弹出层内时,切换 Tab 是否让焦点在弹出层内循环booleantrue2.8.0
keepDOM关闭时是否保留内部组件不销毁booleanfalse2.31.0
margin弹出层计算溢出时的增加的冗余值,详见issue#549,作用同 Tooltip marginnumber|object2.25.0
mouseEnterDelay鼠标移入后,延迟显示的时间,单位毫秒(仅当 trigger 为 hover/focus 时生效)number50
mouseLeaveDelay鼠标移出后,延迟消失的时间,单位毫秒(仅当 trigger 为 hover/focus 时生效)number50
rePosKey可以更新该项值手动触发弹出层的重新定位string|number
returnFocusOnClose按下 Esc 键后,焦点是否回到 trigger 上,设置 trigger 为 hover, focus, click 时生效booleantrue2.8.0
position方向,可选值:top,topLeft,topRight,left,leftTop,leftBottom,right,rightTop,rightBottom,bottom,bottomLeft,bottomRightstring"bottom"
spacing弹出层与 children 元素的距离,单位 px(object类型自 v2.45后支持)number|
SpacingObject
4(showArrow=false 时) 10(showArrow=true 时)
showArrow是否显示“小三角”boolean
stopPropagation是否阻止弹出层上的点击事件冒泡booleanfalse0.34.0
trigger触发方式,可选值:hover, focus, click, custom, contextMenu(v2.42支持)string'hover'
visible是否显示,配合trigger='custom'可实现完全受控boolean
zIndex弹出层 z-index 值number1030
onClickOutSide当弹出层处于展示状态,点击非Children、非浮层内部区域时的回调(仅trigger为custom、click时有效)function(e:event)2.1.0
onEscKeyDown在 trigger 或 弹出层按 Esc 键时调用function(e:event)2.8.0
onVisibleChange弹出层展示/隐藏时触发的回调function(isVisble:boolean)

Accessibility

ARIA

  • 关于 role
    • 当 Popover 的 trigger 为 click、custom时,Popover的 content 具有 dialog role
    • 当trigger为hover时,Popover的content 具有 tooltip role
  • Popover 的 content
    • content 的 wrapper 会被自动添加 id 属性
  • Popover 的 children
    • 会被自动添加 aria-expanded 属性,当 Popover 可见时,属性值为 true,不可见时为 false
    • 会被自动添加 aria-haspopup 属性,为 dialog
    • 会被自动添加 aria-controls 属性,为 content 的 wrapper 的 id

键盘和焦点

  • Popover 触发方式设置为 hover 时:鼠标悬浮或聚焦时打开 Popover
  • Popover 触发方式设置为 click 时:点击触发器或聚焦时并使用 Enter 键打开 Popover
  • Popover 激活后,按下方向键 ⬇️ 将焦点移动到 Popover 上,此时焦点默认处于 Popover 中第一个可交互元素上,用户也可自定义焦点位置(若 Popover 内无可交互元素则表现为无响应)
  • 焦点处于 Popover 内时使用 Tab 键,焦点会在 Popover 内循环,使用 Shift + Tab 会反方向移动焦点
  • 键盘用户能够通过按 Esc 关闭 Popover,关闭后焦点返回到触发器上(仅当 trigger 为 click 时)

设计变量

FAQ

  • 为什么 Popover 浮层卡片的位置和浮层的触发器的相对位置不符合预期?
    Popover 底层依赖了 Tooltip,Tooltip 为了计算定位,需要获取到 children 的真实 DOM 元素,因此 Popover 类型目前支持如下类型的 children:
    1. Class Component,不强制绑定ref,但需要确保 props 可被透传至真实的 DOM 节点上
    2. 使用 forwardRef 包裹后的函数式组件,将 props 与 ref 透传到 children 内真实的 DOM 节点上
    3. 真实 DOM 节点, 如 span,div,p...
    若通过 ref 或 findDOMNode 获取到的真实 DOM 节点宽高并非是你的 children 元素的全部,则位置可能有出入。例如设置了 prefix、suffix 的 Input,Popover位置仍是相对于不包含前缀部分的 input 框进行定位,此时只要在 Input 外层再套一个 div 就能解决问题。
  • 为什么 Popover 浮层卡片在靠近屏幕边界宽度不够时,丢失宽度意外换行?
    在 chromium 104 后 对于屏幕边界文本宽度不够时的换行渲染策略发生变化,详细原因可查看 issue #1022,semi侧已经在v2.17.0版本修复了这个问题。