Skip to main content

dioxus_tw_components/components/
dropdown.rs

1use crate::dioxus_core::IntoAttributeValue;
2use dioxus::prelude::*;
3use dioxus_core::AttributeValue;
4
5#[derive(Clone, Copy)]
6struct DropdownState {
7    is_active: bool,
8}
9
10impl DropdownState {
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 close(&mut self) {
20        self.is_active = false;
21    }
22
23    fn get_is_active(&self) -> bool {
24        self.is_active
25    }
26}
27
28impl IntoAttributeValue for DropdownState {
29    fn into_value(self) -> AttributeValue {
30        match self.is_active {
31            true => AttributeValue::Text("open".to_string()),
32            false => AttributeValue::Text("closed".to_string()),
33        }
34    }
35}
36
37#[derive(Clone, PartialEq, Props)]
38pub struct DropdownProps {
39    #[props(extends = div, extends = GlobalAttributes)]
40    attributes: Vec<Attribute>,
41    children: Element,
42}
43
44/// Usage:
45/// ```ignore
46/// Dropdown {
47///    DropdownToggle {
48///        "Dropdown"
49///     }
50///     DropdownContent {
51///       div { "content" }
52///    }
53/// }
54/// ```
55/// Use 0 closing_delay_ms to disable the auto close feature
56#[component]
57pub fn Dropdown(mut props: DropdownProps) -> Element {
58    let mut state = use_context_provider(|| Signal::new(DropdownState::new()));
59
60    let default_classes = "dropdown";
61    crate::setup_class_attribute(&mut props.attributes, default_classes);
62
63    rsx! {
64        div { "data-state": state.read().into_value(), ..props.attributes, {props.children} }
65        if state.read().get_is_active() {
66            div {
67                class: "dropdown-backdrop",
68                onclick: move |_event| {
69                    state.write().close();
70                },
71            }
72        }
73    }
74}
75
76#[derive(Clone, PartialEq, Props)]
77pub struct DropdownToggleProps {
78    #[props(extends = button, extends = GlobalAttributes)]
79    attributes: Vec<Attribute>,
80
81    children: Element,
82}
83
84#[component]
85pub fn DropdownToggle(mut props: DropdownToggleProps) -> Element {
86    let mut state = use_context::<Signal<DropdownState>>();
87
88    let default_classes = "button";
89    crate::setup_class_attribute(&mut props.attributes, default_classes);
90
91    rsx! {
92        button {
93            onclick: move |e: MouseEvent| {
94                e.stop_propagation();
95                e.prevent_default();
96                state.write().toggle();
97            },
98            ..props.attributes,
99            {props.children}
100        }
101    }
102}
103
104#[derive(Clone, PartialEq, Props)]
105pub struct DropdownContentProps {
106    #[props(extends = div, extends = GlobalAttributes)]
107    attributes: Vec<Attribute>,
108
109    children: Element,
110}
111
112#[component]
113pub fn DropdownContent(mut props: DropdownContentProps) -> Element {
114    let state = use_context::<Signal<DropdownState>>();
115
116    let default_classes = "dropdown-content";
117    crate::setup_class_attribute(&mut props.attributes, default_classes);
118
119    rsx! {
120        div {
121            "data-state": state.read().into_value(),
122            ..props.attributes,
123            {props.children}
124        }
125    }
126}