dioxus_element_plug/components/
select.rs1use dioxus::prelude::*;
2
3#[derive(Clone, PartialEq)]
5pub struct SelectOption {
6 pub value: String,
7 pub label: String,
8 pub disabled: bool,
9}
10
11impl SelectOption {
12 pub fn new(value: &str, label: &str) -> Self {
13 Self {
14 value: value.to_string(),
15 label: label.to_string(),
16 disabled: false,
17 }
18 }
19
20 pub fn disabled(mut self, disabled: bool) -> Self {
21 self.disabled = disabled;
22 self
23 }
24}
25
26#[derive(Clone, PartialEq)]
28pub enum SelectSize {
29 Large,
30 Default,
31 Small,
32}
33
34impl SelectSize {
35 pub fn as_class(&self) -> &'static str {
36 match self {
37 SelectSize::Large => "el-select--large",
38 SelectSize::Default => "",
39 SelectSize::Small => "el-select--small",
40 }
41 }
42}
43
44#[derive(Props, Clone, PartialEq)]
46pub struct SelectProps {
47 #[props(default)]
49 pub model_value: Option<String>,
50
51 #[props(default)]
53 pub options: Vec<SelectOption>,
54
55 #[props(default = false)]
57 pub disabled: bool,
58
59 #[props(default = false)]
61 pub clearable: bool,
62
63 #[props(default = false)]
65 pub filterable: bool,
66
67 #[props(default = false)]
69 pub multiple: bool,
70
71 #[props(default = "Select".to_string())]
73 pub placeholder: String,
74
75 #[props(default = SelectSize::Default)]
77 pub size: SelectSize,
78
79 #[props(default = false)]
81 pub collapse_tags: bool,
82
83 #[props(default)]
85 pub collapse_tags_tooltip: Option<bool>,
86
87 #[props(default)]
89 pub max_collapse_tags: Option<u32>,
90
91 #[props(default)]
93 pub on_change: Option<EventHandler<String>>,
94
95 #[props(default)]
97 pub class: Option<String>,
98
99 #[props(default)]
101 pub style: Option<String>,
102}
103
104#[component]
121pub fn Select(props: SelectProps) -> Element {
122 let mut class_names = vec!["el-select".to_string()];
123
124 let size_class = props.size.as_class();
125 if !size_class.is_empty() {
126 class_names.push(size_class.to_string());
127 }
128
129 if props.disabled {
130 class_names.push("is-disabled".to_string());
131 }
132
133 if let Some(ref custom_class) = props.class {
134 class_names.push(custom_class.clone());
135 }
136
137 let class_string = class_names.join(" ");
138 let style_string = props.style.clone().unwrap_or_default();
139
140 let selected_label = props
142 .options
143 .iter()
144 .find(|opt| Some(&opt.value) == props.model_value.as_ref())
145 .map(|opt| opt.label.clone())
146 .unwrap_or_default();
147
148 let is_empty = props.options.is_empty();
149 let model_value_clone = props.model_value.clone();
150 let option_data: Vec<(String, String, bool, String)> = props.options.iter().map(|opt| {
151 let is_selected = Some(&opt.value) == model_value_clone.as_ref();
152 let mut cls = vec!["el-select-dropdown__item".to_string()];
153 if is_selected { cls.push("selected".to_string()); }
154 if opt.disabled { cls.push("is-disabled".to_string()); }
155 (opt.value.clone(), opt.label.clone(), opt.disabled, cls.join(" "))
156 }).collect();
157
158 rsx! {
159 div {
160 class: "{class_string}",
161 style: "{style_string}",
162 div {
163 class: "el-select__wrapper",
164 onclick: move |_| {
165 },
167 span {
168 class: "el-select__selected-item",
169 if selected_label.is_empty() {
170 "{props.placeholder}"
171 } else {
172 "{selected_label}"
173 }
174 }
175 if props.clearable && props.model_value.is_some() {
176 span {
177 class: "el-select__caret el-select__close",
178 "×"
179 }
180 }
181 span {
182 class: "el-select__caret",
183 i { class: "el-icon-arrow-down" }
184 }
185 }
186 div {
187 class: "el-select__dropdown el-popper",
188 style: "display: none;",
189 div {
190 class: "el-select-dropdown",
191 if is_empty {
192 p {
193 class: "el-select-dropdown__empty",
194 "No data"
195 }
196 }
197 for (opt_value, opt_label, opt_disabled, opt_class) in option_data.into_iter() {
198 div {
199 class: "{opt_class}",
200 onclick: move |_| {
201 if !opt_disabled {
202 if let Some(handler) = props.on_change {
203 handler.call(opt_value.clone());
204 }
205 }
206 },
207 "{opt_label}"
208 }
209 }
210 }
211 }
212 }
213 }
214}