fc_dioxus/components/
popover.rs1use crate::{
3 core::{
4 assets,
5 attrs::{component_attributes, dismissed_attribute},
6 },
7 prelude::*,
8 theme::use_stylesheets,
9};
10const POPOVER: Bem = Bem::new("fc-popover");
11const TOOLTIP: Bem = Bem::new("fc-tooltip");
12const BUBBLE: Bem = Bem::new("fc-tooltip__bubble");
13fc_modifier! { enum BubbleSide for BUBBLE { Above => None, Below => "below" } }
14
15#[component]
19pub fn Popover(
20 id: Id,
21 #[props(default)] title: Option<Element>,
22 #[props(default)] footer: Option<Element>,
23 #[props(default)] class: Option<String>,
24 #[props(extends = GlobalAttributes)] attributes: Vec<Attribute>,
25 children: Element,
26) -> Element {
27 let mut attributes = component_attributes(
28 attributes,
29 ClassList::new(POPOVER.block()).extend(class.as_deref()),
30 &["id", "popover"],
31 );
32 attributes.push(Attribute::new("popover", "auto", None, false));
33 rsx! {
34 if use_stylesheets() { document::Stylesheet { href: assets::COMPONENTS_POPOVER_CSS } }
35 div { id: "{id}", ..attributes,
36 if let Some(title) = title { p { class: POPOVER.element("title"), {title} } }
37 {children}
38 if let Some(footer) = footer { div { class: POPOVER.element("footer"), {footer} } }
39 }
40 }
41}
42
43#[derive(Debug, Clone, PartialEq, Eq)]
45pub(crate) struct DescribedBy(pub(crate) Id);
46
47#[component]
53pub fn Tooltip(
54 tip: Element,
55 #[props(default)]
57 below: bool,
58 #[props(default)]
60 dismissed: bool,
61 #[props(default)] class: Option<String>,
62 #[props(extends = GlobalAttributes)] attributes: Vec<Attribute>,
63 children: Element,
64) -> Element {
65 let id = use_id();
66 use_context_provider(|| DescribedBy(id.clone()));
67 let mut dismissed = use_signal(|| dismissed);
68 let mut attributes = component_attributes(
69 attributes,
70 ClassList::new(TOOLTIP.block()).extend(class.as_deref()),
71 &[],
72 );
73 attributes.extend(dismissed_attribute(dismissed()));
74 let bubble = ClassList::new(BUBBLE.block())
75 .with_modifier(
76 BUBBLE,
77 if below {
78 BubbleSide::Below
79 } else {
80 BubbleSide::Above
81 },
82 )
83 .to_string();
84 rsx! {
85 if use_stylesheets() { document::Stylesheet { href: assets::COMPONENTS_POPOVER_CSS } }
86 span {
87 onkeydown: move |event| {
88 if event.key() == Key::Escape && !dismissed() {
91 dismissed.set(true);
92 event.stop_propagation();
93 }
94 },
95 onpointerleave: move |_| dismissed.set(false),
96 onfocusout: move |_| dismissed.set(false),
97 ..attributes,
98 {children}
99 span { class: bubble, role: "tooltip", id: "{id}", {tip} }
100 }
101 }
102}