Skip to main content

dioxus_element_plug/components/
select.rs

1use dioxus::prelude::*;
2
3/// Select option data
4#[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/// Select size variants
27#[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/// Select props
45#[derive(Props, Clone, PartialEq)]
46pub struct SelectProps {
47    /// Selected value
48    #[props(default)]
49    pub model_value: Option<String>,
50
51    /// Options for the select
52    #[props(default)]
53    pub options: Vec<SelectOption>,
54
55    /// Whether the select is disabled
56    #[props(default = false)]
57    pub disabled: bool,
58
59    /// Whether the select is clearable
60    #[props(default = false)]
61    pub clearable: bool,
62
63    /// Whether the select is filterable
64    #[props(default = false)]
65    pub filterable: bool,
66
67    /// Whether multiple selection is allowed
68    #[props(default = false)]
69    pub multiple: bool,
70
71    /// Placeholder text
72    #[props(default = "Select".to_string())]
73    pub placeholder: String,
74
75    /// Select size
76    #[props(default = SelectSize::Default)]
77    pub size: SelectSize,
78
79    /// Whether to collapse tags in multiple mode
80    #[props(default = false)]
81    pub collapse_tags: bool,
82
83    /// Whether to collapse tags with tooltip
84    #[props(default)]
85    pub collapse_tags_tooltip: Option<bool>,
86
87    /// Maximum number of tags shown in multiple mode
88    #[props(default)]
89    pub max_collapse_tags: Option<u32>,
90
91    /// Change event handler
92    #[props(default)]
93    pub on_change: Option<EventHandler<String>>,
94
95    /// Additional CSS classes
96    #[props(default)]
97    pub class: Option<String>,
98
99    /// Inline styles
100    #[props(default)]
101    pub style: Option<String>,
102}
103
104/// Select component for choosing from a dropdown list
105///
106/// ## Example
107///
108/// ```rust,ignore
109/// rsx! {
110///     Select {
111///         model_value: Some("1".to_string()),
112///         options: vec![
113///             SelectOption::new("1", "Option 1"),
114///             SelectOption::new("2", "Option 2"),
115///         ],
116///         on_change: move |v| println!("Selected: {}", v),
117///     }
118/// }
119/// ```
120#[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    // Find selected label
141    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                    // Toggle dropdown - in a real implementation this would use signals
166                },
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}