use dioxus::prelude::*;
#[derive(Clone, PartialEq)]
pub struct SelectOption {
pub value: String,
pub label: String,
pub disabled: bool,
}
impl SelectOption {
pub fn new(value: &str, label: &str) -> Self {
Self {
value: value.to_string(),
label: label.to_string(),
disabled: false,
}
}
pub fn disabled(mut self, disabled: bool) -> Self {
self.disabled = disabled;
self
}
}
#[derive(Clone, PartialEq)]
pub enum SelectSize {
Large,
Default,
Small,
}
impl SelectSize {
pub fn as_class(&self) -> &'static str {
match self {
SelectSize::Large => "el-select--large",
SelectSize::Default => "",
SelectSize::Small => "el-select--small",
}
}
}
#[derive(Props, Clone, PartialEq)]
pub struct SelectProps {
#[props(default)]
pub model_value: Option<String>,
#[props(default)]
pub options: Vec<SelectOption>,
#[props(default = false)]
pub disabled: bool,
#[props(default = false)]
pub clearable: bool,
#[props(default = false)]
pub filterable: bool,
#[props(default = false)]
pub multiple: bool,
#[props(default = "Select".to_string())]
pub placeholder: String,
#[props(default = SelectSize::Default)]
pub size: SelectSize,
#[props(default = false)]
pub collapse_tags: bool,
#[props(default)]
pub collapse_tags_tooltip: Option<bool>,
#[props(default)]
pub max_collapse_tags: Option<u32>,
#[props(default)]
pub on_change: Option<EventHandler<String>>,
#[props(default)]
pub class: Option<String>,
#[props(default)]
pub style: Option<String>,
}
#[component]
pub fn Select(props: SelectProps) -> Element {
let mut class_names = vec!["el-select".to_string()];
let size_class = props.size.as_class();
if !size_class.is_empty() {
class_names.push(size_class.to_string());
}
if props.disabled {
class_names.push("is-disabled".to_string());
}
if let Some(ref custom_class) = props.class {
class_names.push(custom_class.clone());
}
let class_string = class_names.join(" ");
let style_string = props.style.clone().unwrap_or_default();
let selected_label = props
.options
.iter()
.find(|opt| Some(&opt.value) == props.model_value.as_ref())
.map(|opt| opt.label.clone())
.unwrap_or_default();
let is_empty = props.options.is_empty();
let model_value_clone = props.model_value.clone();
let option_data: Vec<(String, String, bool, String)> = props.options.iter().map(|opt| {
let is_selected = Some(&opt.value) == model_value_clone.as_ref();
let mut cls = vec!["el-select-dropdown__item".to_string()];
if is_selected { cls.push("selected".to_string()); }
if opt.disabled { cls.push("is-disabled".to_string()); }
(opt.value.clone(), opt.label.clone(), opt.disabled, cls.join(" "))
}).collect();
rsx! {
div {
class: "{class_string}",
style: "{style_string}",
div {
class: "el-select__wrapper",
onclick: move |_| {
},
span {
class: "el-select__selected-item",
if selected_label.is_empty() {
"{props.placeholder}"
} else {
"{selected_label}"
}
}
if props.clearable && props.model_value.is_some() {
span {
class: "el-select__caret el-select__close",
"×"
}
}
span {
class: "el-select__caret",
i { class: "el-icon-arrow-down" }
}
}
div {
class: "el-select__dropdown el-popper",
style: "display: none;",
div {
class: "el-select-dropdown",
if is_empty {
p {
class: "el-select-dropdown__empty",
"No data"
}
}
for (opt_value, opt_label, opt_disabled, opt_class) in option_data.into_iter() {
div {
class: "{opt_class}",
onclick: move |_| {
if !opt_disabled {
if let Some(handler) = props.on_change {
handler.call(opt_value.clone());
}
}
},
"{opt_label}"
}
}
}
}
}
}
}