Skip to main content

basecoat_core/props/
popover.rs

1use crate::{AttrMap, BasecoatProps, Children, Markup};
2use std::borrow::Cow;
3
4/// Popover placement — passed to the floating-ui controller as the
5/// `placement` argument.
6///
7/// The string form matches [floating-ui's placement vocabulary](https://floating-ui.com/docs/computePosition#placement)
8/// so the WASM controller can forward it verbatim.
9#[derive(Clone, Copy, Debug, PartialEq, Eq, Default)]
10pub enum PopoverPlacement {
11    Top,
12    TopStart,
13    TopEnd,
14    #[default]
15    Bottom,
16    BottomStart,
17    BottomEnd,
18    Left,
19    LeftStart,
20    LeftEnd,
21    Right,
22    RightStart,
23    RightEnd,
24}
25
26impl PopoverPlacement {
27    /// Returns the floating-ui placement token (e.g. `"bottom-start"`).
28    pub fn as_str(&self) -> &'static str {
29        match self {
30            PopoverPlacement::Top => "top",
31            PopoverPlacement::TopStart => "top-start",
32            PopoverPlacement::TopEnd => "top-end",
33            PopoverPlacement::Bottom => "bottom",
34            PopoverPlacement::BottomStart => "bottom-start",
35            PopoverPlacement::BottomEnd => "bottom-end",
36            PopoverPlacement::Left => "left",
37            PopoverPlacement::LeftStart => "left-start",
38            PopoverPlacement::LeftEnd => "left-end",
39            PopoverPlacement::Right => "right",
40            PopoverPlacement::RightStart => "right-start",
41            PopoverPlacement::RightEnd => "right-end",
42        }
43    }
44}
45
46impl std::fmt::Display for PopoverPlacement {
47    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
48        f.write_str(self.as_str())
49    }
50}
51
52/// Popover — maps to CSS class `.popover` on a `<details>` element.
53///
54/// Upstream basecoat uses a `<details>` root containing a `<summary>` trigger
55/// and a `<div role="dialog">` content panel. The WASM controller wires
56/// floating-ui positioning, click-outside dismissal, and aria-expanded sync
57/// onto that markup.
58///
59/// The `id` is required by the WASM controller so trigger/content pairs can be
60/// looked up unambiguously and so accessible-name attributes (`aria-controls`,
61/// `aria-labelledby`) can target the right elements.
62#[derive(BasecoatProps, Default, Clone, Debug)]
63pub struct PopoverProps {
64    /// Unique DOM id — required for the WASM controller.
65    #[prop(optional, into)]
66    pub id: Option<Cow<'static, str>>,
67    /// Trigger content placed inside the `<summary>` element.
68    #[prop(optional)]
69    pub trigger: Option<Markup>,
70    /// Floating-ui placement (default `bottom`).
71    #[prop(default)]
72    pub placement: PopoverPlacement,
73    /// Distance in pixels between the trigger edge and the content panel.
74    #[prop(default = 8.0)]
75    pub offset_px: f64,
76    /// Whether to render a `<div data-popover-arrow>` element inside the
77    /// content. The visual arrow is CSS-positioned in v0.2 — see crate docs.
78    #[prop(default = false)]
79    pub arrow: bool,
80    /// Extra CSS classes appended after the `popover` class.
81    #[prop(optional, into)]
82    pub class: Option<Cow<'static, str>>,
83    #[prop(extend)]
84    pub attrs: AttrMap,
85    /// Popover content (rendered inside the `<div role="dialog">`).
86    pub children: Children,
87}