stratum_components/overlay/
tooltip.rs1use crate::common::merge_classes;
4use stratum_core::aria::{AriaAttributes, AriaRole};
5use stratum_core::render::{AttrValue, RenderOutput};
6
7#[derive(Debug, Clone, Copy, Default, PartialEq, Eq)]
9pub enum TooltipSide {
10 #[default]
11 Top,
12 Right,
13 Bottom,
14 Left,
15}
16
17#[derive(Debug, Clone, PartialEq, Default)]
19pub struct TooltipProps {
20 pub side: TooltipSide,
21 pub open: bool,
22 pub class: Option<String>,
23 pub content: String,
24 pub id: Option<String>,
25}
26
27pub struct Tooltip;
28
29impl Tooltip {
30 const BASE: &'static str = "z-50 overflow-hidden rounded-md bg-primary px-3 py-1.5 text-xs text-primary-foreground animate-in fade-in-0 zoom-in-95";
31
32 pub fn classes(props: &TooltipProps) -> String {
33 let side_cls = match props.side {
34 TooltipSide::Top => "slide-in-from-bottom-2",
35 TooltipSide::Right => "slide-in-from-left-2",
36 TooltipSide::Bottom => "slide-in-from-top-2",
37 TooltipSide::Left => "slide-in-from-right-2",
38 };
39
40 let computed = format!("{} {}", Self::BASE, side_cls);
41 merge_classes(&computed, &props.class)
42 }
43
44 pub fn render(props: &TooltipProps) -> RenderOutput {
45 let classes = Self::classes(props);
46 let aria = AriaAttributes::new().with_role(AriaRole::ToolTip);
47
48 let mut output = RenderOutput::new()
49 .with_tag("div")
50 .with_class(classes)
51 .with_aria(aria)
52 .with_data("state", if props.open { "open" } else { "closed" })
53 .with_data(
54 "side",
55 match props.side {
56 TooltipSide::Top => "top",
57 TooltipSide::Right => "right",
58 TooltipSide::Bottom => "bottom",
59 TooltipSide::Left => "left",
60 },
61 );
62
63 if let Some(ref id) = props.id {
64 output = output.with_attr("id", AttrValue::String(id.clone()));
65 }
66
67 output
68 }
69}
70
71#[cfg(test)]
72mod tests {
73 use super::*;
74
75 #[test]
76 fn tooltip_default_classes() {
77 let props = TooltipProps::default();
78 let classes = Tooltip::classes(&props);
79 assert!(classes.contains("rounded-md"));
80 assert!(classes.contains("slide-in-from-bottom-2"));
81 }
82
83 #[test]
84 fn tooltip_side_classes() {
85 let props = TooltipProps {
86 side: TooltipSide::Left,
87 ..Default::default()
88 };
89 let classes = Tooltip::classes(&props);
90 assert!(classes.contains("slide-in-from-right-2"));
91 }
92
93 #[test]
94 fn tooltip_render_role() {
95 let props = TooltipProps::default();
96 let output = Tooltip::render(&props);
97 assert_eq!(output.aria.role, Some(AriaRole::ToolTip));
98 }
99}