Skip to content

Component Popover

MeowLynxSea edited this page Jan 24, 2026 · 6 revisions

Popover

Popover 是一个点击触发的浮层容器,用来承载菜单/小面板,并且锚定在触发器(trigger)下方

它的实现模型是:

  • Popover 本体是一个 .relative() 容器。
  • 菜单/面板是一个 .absolute() 子元素,通过 gpui::deferred(...) 以较高优先级绘制在上层。

适用场景

  • 头像菜单、更多操作菜单
  • 轻量设置面板(如“排序 / 视图 / 过滤”)
  • 承载 DropdownMenu(DropdownMenu 是基于 Popover 的封装)

不适合:

  • 需要全屏遮罩、焦点锁定、Esc 关闭等“对话框行为”的场景(这通常属于 app/widget 层)。

API

  • popover():构造器(默认会用 caller location 生成 id)
  • id(...) / key(...):稳定 identity(建议在可重复渲染/列表中使用 key(...)
  • open(bool):由调用方控制是否展开
  • placement(PopoverPlacement)
    • BottomStart:触发器左下对齐
    • BottomEnd:触发器右下对齐
  • width(px(...)):固定宽度(未设置时由内容自然决定)
  • bg(...) / border(...):覆盖默认 theme 色
  • trigger(...):触发器元素(按钮/图标/任意元素)
  • content(...):浮层内容
  • on_close(|window, cx| ...):点击浮层外部自动触发(用于把 open 置为 false

交互与行为说明

  • Popover 不“自带打开逻辑”:点击触发器如何切换 open 完全由调用方决定。
  • 点击浮层外部会触发 on_close(如果你没有提供 on_close,Popover 不会自动关闭)。
  • Popover 的关闭逻辑通常需要 window.refresh()(因为 use_keyed_state 更新后需要刷新帧)。

Example: 基础用法

use gpui::{ElementId, px};
use yororen_ui::component::{button, label, popover, PopoverPlacement};

// In a view render:
let open = window.use_keyed_state(("demo", "popover-open"), cx, |_, _| false);
let is_open = *open.read(cx);

let view = popover()
    .id(ElementId::from(("demo", "popover")))
    .open(is_open)
    .placement(PopoverPlacement::BottomStart)
    .width(px(260.))
    .on_close({
        let open = open.clone();
        move |window, cx| {
            open.update(cx, |v, _| *v = false);
            window.refresh();
        }
    })
    .trigger(
        button().child("Open").on_click({
            let open = open.clone();
            move |_ev, window, cx| {
                open.update(cx, |v, _| *v = !*v);
                window.refresh();
            }
        }),
    )
    .content(label("Popover content"));

Example: 右对齐菜单

use yororen_ui::component::{button, popover, PopoverPlacement};

let view = popover()
    .open(true)
    .placement(PopoverPlacement::BottomEnd)
    .trigger(button().child("⋯"))
    .content("Right aligned popover");

Clone this wiki locally