Skip to main content

dioxus_tw_components/components/
hovercard.rs

1use crate::dioxus_core::IntoAttributeValue;
2use dioxus::prelude::*;
3use dioxus_core::AttributeValue;
4
5#[derive(Clone, Debug)]
6pub struct HoverState {
7    is_active: bool,
8}
9
10impl HoverState {
11    fn new() -> Self {
12        Self { is_active: false }
13    }
14
15    fn toggle(&mut self) {
16        self.is_active = !self.is_active;
17    }
18
19    fn open(&mut self) {
20        self.is_active = true;
21    }
22
23    fn close(&mut self) {
24        self.is_active = false;
25    }
26}
27
28impl IntoAttributeValue for HoverState {
29    fn into_value(self) -> AttributeValue {
30        match self.is_active {
31            true => AttributeValue::Text("active".to_string()),
32            false => AttributeValue::Text("inactive".to_string()),
33        }
34    }
35}
36
37#[derive(Clone, PartialEq, Props)]
38pub struct HoverCardProps {
39    #[props(extends = div, extends = GlobalAttributes)]
40    attributes: Vec<Attribute>,
41
42    children: Element,
43}
44
45#[component]
46pub fn HoverCard(mut props: HoverCardProps) -> Element {
47    let mut state = use_context_provider(|| Signal::new(HoverState::new()));
48
49    let default_classes = "hovercard";
50    crate::setup_class_attribute(&mut props.attributes, default_classes);
51
52    rsx! {
53        div {
54            "data-state": state.into_value(),
55            onmouseenter: move |_| state.write().open(),
56            onmouseleave: move |_| state.write().close(),
57            ..props.attributes,
58            {props.children}
59        }
60    }
61}
62
63#[derive(Clone, PartialEq, Props)]
64pub struct HoverCardTriggerProps {
65    #[props(extends = div, extends = GlobalAttributes)]
66    attributes: Vec<Attribute>,
67
68    #[props(optional, default)]
69    onclick: EventHandler<MouseEvent>,
70
71    children: Element,
72}
73
74#[component]
75pub fn HoverCardTrigger(mut props: HoverCardTriggerProps) -> Element {
76    let mut state = use_context::<Signal<HoverState>>();
77
78    let default_classes = "hovercard-trigger";
79    crate::setup_class_attribute(&mut props.attributes, default_classes);
80
81    // We need this event here to not close the hover card when clicking its content
82    let onclick = move |event| {
83        state.write().toggle();
84        props.onclick.call(event);
85    };
86
87    rsx! {
88        div {
89            role: "button",
90            "data-state": state.into_value(),
91            onclick,
92            ..props.attributes,
93            {props.children}
94        }
95    }
96}
97
98#[derive(Clone, PartialEq, Props)]
99pub struct HoverCardContentProps {
100    #[props(extends = div, extends = GlobalAttributes)]
101    attributes: Vec<Attribute>,
102
103    children: Element,
104}
105
106#[component]
107pub fn HoverCardContent(mut props: HoverCardContentProps) -> Element {
108    let state = use_context::<Signal<HoverState>>();
109
110    let default_classes = "hovercard-content";
111    crate::setup_class_attribute(&mut props.attributes, default_classes);
112
113    rsx! {
114        div {
115            "data-state": state.into_value(),
116            ..props.attributes,
117            {props.children}
118        }
119    }
120}