dioxus_bootstrap_css/
dropdown.rs1use dioxus::prelude::*;
2
3#[derive(Clone, PartialEq, Props)]
20pub struct DropdownProps {
21 pub open: Signal<bool>,
23 pub toggle: Element,
25 pub menu: Element,
27 #[props(default)]
29 pub class: String,
30 #[props(default)]
32 pub toggle_class: String,
33 #[props(default)]
35 pub direction: DropDirection,
36 #[props(default)]
38 pub align_end: bool,
39}
40
41#[derive(Clone, Copy, Debug, Default, PartialEq)]
43pub enum DropDirection {
44 #[default]
45 Down,
46 Up,
47 Start,
48 End,
49}
50
51#[component]
52pub fn Dropdown(props: DropdownProps) -> Element {
53 let is_open = *props.open.read();
54 let mut open_signal = props.open;
55
56 let dir_class = match props.direction {
57 DropDirection::Down => "dropdown",
58 DropDirection::Up => "dropup",
59 DropDirection::Start => "dropstart",
60 DropDirection::End => "dropend",
61 };
62
63 let container_class = if props.class.is_empty() {
64 dir_class.to_string()
65 } else {
66 format!("{dir_class} {}", props.class)
67 };
68
69 let toggle_class = if props.toggle_class.is_empty() {
70 "btn btn-secondary dropdown-toggle".to_string()
71 } else {
72 format!("btn dropdown-toggle {}", props.toggle_class)
73 };
74
75 let menu_class = if is_open {
76 if props.align_end {
77 "dropdown-menu dropdown-menu-end show"
78 } else {
79 "dropdown-menu show"
80 }
81 } else if props.align_end {
82 "dropdown-menu dropdown-menu-end"
83 } else {
84 "dropdown-menu"
85 };
86
87 rsx! {
88 if is_open {
90 div {
91 style: "position: fixed; inset: 0; z-index: 990;",
92 onclick: move |_| open_signal.set(false),
93 }
94 }
95 div { class: "{container_class}",
96 style: if is_open { "position: relative; z-index: 991;" } else { "" },
97 button {
98 class: "{toggle_class}",
99 r#type: "button",
100 "aria-expanded": if is_open { "true" } else { "false" },
101 onclick: move |evt| {
102 evt.stop_propagation();
103 open_signal.set(!is_open);
104 },
105 {props.toggle}
106 }
107 ul { class: "{menu_class}",
108 onclick: move |_| open_signal.set(false),
110 {props.menu}
111 }
112 }
113 }
114}
115
116#[derive(Clone, PartialEq, Props)]
118pub struct DropdownItemProps {
119 #[props(default)]
121 pub active: bool,
122 #[props(default)]
124 pub disabled: bool,
125 #[props(default)]
127 pub onclick: Option<EventHandler<MouseEvent>>,
128 #[props(default)]
130 pub class: String,
131 pub children: Element,
133}
134
135#[component]
136pub fn DropdownItem(props: DropdownItemProps) -> Element {
137 let mut classes = vec!["dropdown-item".to_string()];
138 if props.active {
139 classes.push("active".to_string());
140 }
141 if props.disabled {
142 classes.push("disabled".to_string());
143 }
144 if !props.class.is_empty() {
145 classes.push(props.class.clone());
146 }
147 let full_class = classes.join(" ");
148
149 rsx! {
150 li {
151 button {
152 class: "{full_class}",
153 r#type: "button",
154 disabled: props.disabled,
155 onclick: move |evt| {
156 if let Some(handler) = &props.onclick {
157 handler.call(evt);
158 }
159 },
160 {props.children}
161 }
162 }
163 }
164}
165
166#[component]
168pub fn DropdownDivider() -> Element {
169 rsx! {
170 li { hr { class: "dropdown-divider" } }
171 }
172}
173
174#[derive(Clone, PartialEq, Props)]
176pub struct DropdownHeaderProps {
177 pub children: Element,
178}
179
180#[component]
181pub fn DropdownHeader(props: DropdownHeaderProps) -> Element {
182 rsx! {
183 li { h6 { class: "dropdown-header", {props.children} } }
184 }
185}