use crate::{
core::{
assets,
attrs::{component_attributes, dismissed_attribute},
},
prelude::*,
theme::use_stylesheets,
};
const POPOVER: Bem = Bem::new("fc-popover");
const TOOLTIP: Bem = Bem::new("fc-tooltip");
const BUBBLE: Bem = Bem::new("fc-tooltip__bubble");
fc_modifier! { enum BubbleSide for BUBBLE { Above => None, Below => "below" } }
#[component]
pub fn Popover(
id: Id,
#[props(default)] title: Option<Element>,
#[props(default)] footer: Option<Element>,
#[props(default)] class: Option<String>,
#[props(extends = GlobalAttributes)] attributes: Vec<Attribute>,
children: Element,
) -> Element {
let mut attributes = component_attributes(
attributes,
ClassList::new(POPOVER.block()).extend(class.as_deref()),
&["id", "popover"],
);
attributes.push(Attribute::new("popover", "auto", None, false));
rsx! {
if use_stylesheets() { document::Stylesheet { href: assets::COMPONENTS_POPOVER_CSS } }
div { id: "{id}", ..attributes,
if let Some(title) = title { p { class: POPOVER.element("title"), {title} } }
{children}
if let Some(footer) = footer { div { class: POPOVER.element("footer"), {footer} } }
}
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub(crate) struct DescribedBy(pub(crate) Id);
#[component]
pub fn Tooltip(
tip: Element,
#[props(default)]
below: bool,
#[props(default)]
dismissed: bool,
#[props(default)] class: Option<String>,
#[props(extends = GlobalAttributes)] attributes: Vec<Attribute>,
children: Element,
) -> Element {
let id = use_id();
use_context_provider(|| DescribedBy(id.clone()));
let mut dismissed = use_signal(|| dismissed);
let mut attributes = component_attributes(
attributes,
ClassList::new(TOOLTIP.block()).extend(class.as_deref()),
&[],
);
attributes.extend(dismissed_attribute(dismissed()));
let bubble = ClassList::new(BUBBLE.block())
.with_modifier(
BUBBLE,
if below {
BubbleSide::Below
} else {
BubbleSide::Above
},
)
.to_string();
rsx! {
if use_stylesheets() { document::Stylesheet { href: assets::COMPONENTS_POPOVER_CSS } }
span {
onkeydown: move |event| {
if event.key() == Key::Escape && !dismissed() {
dismissed.set(true);
event.stop_propagation();
}
},
onpointerleave: move |_| dismissed.set(false),
onfocusout: move |_| dismissed.set(false),
..attributes,
{children}
span { class: bubble, role: "tooltip", id: "{id}", {tip} }
}
}
}