Skip to content

Component Popover

MeowLynxSea edited this page Jan 24, 2026 · 6 revisions

Popover

Popover is a click-triggered floating container for menus or small panels, anchored under the trigger element.

Implementation model:

  • The Popover itself is a .relative() container.
  • The menu/panel is an .absolute() child, drawn on top with higher priority via gpui::deferred(...).

When to use

  • Avatar menus, "more actions" menus
  • Lightweight settings panels (for example: sort / view / filter)
  • Hosting DropdownMenu (DropdownMenu is built on top of Popover)

Not a good fit:

  • Cases that require full-screen overlay, focus trapping, Esc-to-dismiss, and other "dialog" behaviors (these typically belong in the app/widget layer).

API

  • popover(): constructor (by default uses caller location to generate an id)
  • id(...) / key(...): stable identity (use key(...) in lists/repeated rendering)
  • open(bool): controlled by the caller
  • placement(PopoverPlacement):
    • BottomStart: align to trigger's bottom-left
    • BottomEnd: align to trigger's bottom-right
  • width(px(...)): fixed width (otherwise sizes to content)
  • bg(...) / border(...): override default theme colors
  • trigger(...): trigger element (button/icon/any element)
  • content(...): popover content
  • on_close(|window, cx| ...): triggered when clicking outside (typically used to set open = false)

Interaction and behavior

  • Popover does not implement open/toggle logic: how clicking the trigger changes open is entirely up to the caller.
  • Clicking outside triggers on_close (if you don't provide on_close, Popover will not auto-close).
  • Closing typically requires window.refresh() (because use_keyed_state updates need a frame refresh).

Example: Basic usage

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: Right-aligned menu

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