dioxus_bulma/components/
dropdown.rs1use crate::utils::build_class;
2use dioxus::prelude::*;
3
4#[derive(Debug, Clone, Copy, PartialEq)]
5pub enum DropdownTrigger {
6 Hover,
7 Click,
8}
9
10#[derive(Props, Clone, PartialEq)]
11pub struct DropdownProps {
12 #[props(default)]
13 pub active: Option<bool>,
14 #[props(default)]
15 pub hoverable: Option<bool>,
16 #[props(default)]
17 pub right: Option<bool>,
18 #[props(default)]
19 pub up: Option<bool>,
20 #[props(default)]
21 pub id: Option<String>,
22 #[props(default)]
23 pub class: Option<String>,
24 #[props(default)]
25 pub style: Option<String>,
26 pub children: Element,
27}
28
29#[component]
30pub fn Dropdown(props: DropdownProps) -> Element {
31 let active = props.active.unwrap_or(false);
32 let hoverable = props.hoverable.unwrap_or(false);
33 let right = props.right.unwrap_or(false);
34 let up = props.up.unwrap_or(false);
35
36 let base_classes = vec!["dropdown"];
37 let optional_classes = vec![
38 if active { Some("is-active".to_string()) } else { None },
39 if hoverable { Some("is-hoverable".to_string()) } else { None },
40 if right { Some("is-right".to_string()) } else { None },
41 if up { Some("is-up".to_string()) } else { None },
42 props.class.clone(),
43 ];
44
45 let final_class = build_class(&base_classes, &optional_classes);
46 let dropdown_style = props.style.as_deref().unwrap_or("");
47
48 rsx! {
49 div {
50 class: "{final_class}",
51 style: "{dropdown_style}",
52 id: props.id.clone(),
53 {props.children}
54 }
55 }
56}
57
58#[derive(Props, Clone, PartialEq)]
59pub struct DropdownTriggerProps {
60 #[props(default)]
61 pub onclick: Option<EventHandler<MouseEvent>>,
62 #[props(default)]
63 pub id: Option<String>,
64 #[props(default)]
65 pub class: Option<String>,
66 #[props(default)]
67 pub style: Option<String>,
68 pub children: Element,
69}
70
71#[component]
72pub fn DropdownTrigger(props: DropdownTriggerProps) -> Element {
73 let base_classes = vec!["dropdown-trigger"];
74 let optional_classes = vec![props.class.clone()];
75 let final_class = build_class(&base_classes, &optional_classes);
76 let trigger_style = props.style.as_deref().unwrap_or("");
77
78 rsx! {
79 div {
80 class: "{final_class}",
81 style: "{trigger_style}",
82 id: props.id.clone(),
83 onclick: move |evt| {
84 if let Some(handler) = &props.onclick {
85 handler.call(evt);
86 }
87 },
88 {props.children}
89 }
90 }
91}
92
93#[derive(Props, Clone, PartialEq)]
94pub struct DropdownMenuProps {
95 #[props(default)]
96 pub id: Option<String>,
97 #[props(default)]
98 pub class: Option<String>,
99 #[props(default)]
100 pub style: Option<String>,
101 pub children: Element,
102}
103
104#[component]
105pub fn DropdownMenu(props: DropdownMenuProps) -> Element {
106 let base_classes = vec!["dropdown-menu"];
107 let optional_classes = vec![props.class.clone()];
108 let final_class = build_class(&base_classes, &optional_classes);
109 let menu_style = props.style.as_deref().unwrap_or("");
110
111 rsx! {
112 div {
113 class: "{final_class}",
114 style: "{menu_style}",
115 id: props.id.clone(),
116 role: "menu",
117 div {
118 class: "dropdown-content",
119 {props.children}
120 }
121 }
122 }
123}
124
125#[derive(Props, Clone, PartialEq)]
126pub struct DropdownItemProps {
127 #[props(default)]
128 pub active: Option<bool>,
129 #[props(default)]
130 pub href: Option<String>,
131 #[props(default)]
132 pub onclick: Option<EventHandler<MouseEvent>>,
133 #[cfg(feature = "router")]
135 #[props(default, into)]
136 pub to: crate::router_helpers::MaybeNav,
137 #[props(default)]
138 pub id: Option<String>,
139 #[props(default)]
140 pub class: Option<String>,
141 #[props(default)]
142 pub style: Option<String>,
143 pub children: Element,
144}
145
146#[component]
147pub fn DropdownItem(props: DropdownItemProps) -> Element {
148 let active = props.active.unwrap_or(false);
149
150 let base_classes = vec!["dropdown-item"];
151 let optional_classes = vec![
152 if active { Some("is-active".to_string()) } else { None },
153 props.class.clone(),
154 ];
155
156 let final_class = build_class(&base_classes, &optional_classes);
157 let item_style = props.style.as_deref().unwrap_or("");
158
159 #[cfg(feature = "router")]
160 if let Some(nav_target) = props.to.0 {
161 return rsx! {
162 Link {
163 to: nav_target,
164 class: "{final_class}",
165 style: "{item_style}",
166 id: props.id.clone(),
167 onclick: move |evt| {
168 if let Some(handler) = &props.onclick {
169 handler.call(evt);
170 }
171 },
172 {props.children}
173 }
174 };
175 }
176
177 rsx! {
178 if let Some(href) = props.href {
179 a {
180 class: "{final_class}",
181 style: "{item_style}",
182 id: props.id.clone(),
183 href: "{href}",
184 onclick: move |evt| {
185 if let Some(handler) = &props.onclick {
186 handler.call(evt);
187 }
188 },
189 {props.children}
190 }
191 } else {
192 div {
193 class: "{final_class}",
194 style: "{item_style}",
195 id: props.id.clone(),
196 onclick: move |evt| {
197 if let Some(handler) = &props.onclick {
198 handler.call(evt);
199 }
200 },
201 {props.children}
202 }
203 }
204 }
205}
206
207#[derive(Props, Clone, PartialEq)]
208pub struct DropdownDividerProps {
209 #[props(default)]
210 pub id: Option<String>,
211 #[props(default)]
212 pub class: Option<String>,
213 #[props(default)]
214 pub style: Option<String>,
215}
216
217#[component]
218pub fn DropdownDivider(props: DropdownDividerProps) -> Element {
219 let base_classes = vec!["dropdown-divider"];
220 let optional_classes = vec![props.class.clone()];
221 let final_class = build_class(&base_classes, &optional_classes);
222 let divider_style = props.style.as_deref().unwrap_or("");
223
224 rsx! {
225 hr {
226 class: "{final_class}",
227 style: "{divider_style}"
228 }
229 }
230}