use dioxus::prelude::*;
use crate::theme::classes;
#[derive(Clone, PartialEq)]
pub enum InputSize {
Large,
Medium,
Small,
Mini,
}
impl InputSize {
pub fn as_class(&self) -> &'static str {
match self {
InputSize::Large => classes::INPUT_LARGE,
InputSize::Medium => classes::INPUT_MEDIUM,
InputSize::Small => classes::INPUT_SMALL,
InputSize::Mini => classes::INPUT_MINI,
}
}
}
#[derive(Clone, PartialEq)]
pub enum InputType {
Text,
Password,
Email,
Url,
Number,
Tel,
Search,
Textarea,
}
impl InputType {
pub fn as_str(&self) -> &'static str {
match self {
InputType::Text => "text",
InputType::Password => "password",
InputType::Email => "email",
InputType::Url => "url",
InputType::Number => "number",
InputType::Tel => "tel",
InputType::Search => "search",
InputType::Textarea => "textarea",
}
}
}
#[derive(Props, Clone, PartialEq)]
pub struct InputProps {
#[props(default)]
pub value: Option<String>,
#[props(default)]
pub placeholder: Option<String>,
#[props(default = InputType::Text)]
pub input_type: InputType,
#[props(default)]
pub size: Option<InputSize>,
#[props(default = false)]
pub disabled: bool,
#[props(default = false)]
pub readonly: bool,
#[props(default = false)]
pub clearable: bool,
#[props(default = false)]
pub show_password: bool,
#[props(default = false)]
pub error: bool,
#[props(default)]
pub label: Option<String>,
#[props(default)]
pub prefix_icon: Option<String>,
#[props(default)]
pub suffix_icon: Option<String>,
#[props(default)]
pub maxlength: Option<u32>,
#[props(default)]
pub minlength: Option<u32>,
#[props(default = false)]
pub autofocus: bool,
#[props(default)]
pub name: Option<String>,
#[props(default)]
pub id: Option<String>,
#[props(default)]
pub on_input: Option<EventHandler<FormEvent>>,
#[props(default)]
pub on_change: Option<EventHandler<FormEvent>>,
#[props(default)]
pub on_focus: Option<EventHandler<FocusEvent>>,
#[props(default)]
pub on_blur: Option<EventHandler<FocusEvent>>,
#[props(default)]
pub on_keydown: Option<EventHandler<KeyboardEvent>>,
#[props(default)]
pub class: Option<String>,
#[props(default)]
pub style: Option<String>,
}
#[component]
pub fn Input(props: InputProps) -> Element {
let base_class = "el-input";
let mut wrapper_classes = vec![base_class];
if let Some(size) = &props.size {
wrapper_classes.push(size.as_class());
}
if props.disabled {
wrapper_classes.push(classes::IS_DISABLED);
}
if props.error {
wrapper_classes.push("is-error");
}
let wrapper_class_string = wrapper_classes.join(" ");
let input_class = "el-input__inner";
let input_element = if props.input_type == InputType::Textarea {
rsx! {
textarea {
class: "{input_class}",
value: props.value.unwrap_or_default(),
placeholder: props.placeholder.unwrap_or_default(),
disabled: props.disabled,
readonly: props.readonly,
maxlength: props.maxlength.map(|n| n.to_string()).unwrap_or_default(),
minlength: props.minlength.map(|n| n.to_string()).unwrap_or_default(),
autofocus: props.autofocus,
name: props.name.unwrap_or_default(),
id: props.id.unwrap_or_default(),
style: props.style.unwrap_or_default(),
oninput: move |event| {
if let Some(handler) = props.on_input {
handler.call(event);
}
},
onchange: move |event| {
if let Some(handler) = props.on_change {
handler.call(event);
}
},
onfocus: move |event| {
if let Some(handler) = props.on_focus {
handler.call(event);
}
},
onblur: move |event| {
if let Some(handler) = props.on_blur {
handler.call(event);
}
},
onkeydown: move |event| {
if let Some(handler) = props.on_keydown {
handler.call(event);
}
},
}
}
} else {
rsx! {
input {
class: "{input_class}",
r#type: props.input_type.as_str(),
value: props.value.unwrap_or_default(),
placeholder: props.placeholder.unwrap_or_default(),
disabled: props.disabled,
readonly: props.readonly,
maxlength: props.maxlength.map(|n| n.to_string()).unwrap_or_default(),
minlength: props.minlength.map(|n| n.to_string()).unwrap_or_default(),
autofocus: props.autofocus,
name: props.name.unwrap_or_default(),
id: props.id.unwrap_or_default(),
style: props.style.unwrap_or_default(),
oninput: move |event| {
if let Some(handler) = props.on_input {
handler.call(event);
}
},
onchange: move |event| {
if let Some(handler) = props.on_change {
handler.call(event);
}
},
onfocus: move |event| {
if let Some(handler) = props.on_focus {
handler.call(event);
}
},
onblur: move |event| {
if let Some(handler) = props.on_blur {
handler.call(event);
}
},
onkeydown: move |event| {
if let Some(handler) = props.on_keydown {
handler.call(event);
}
},
}
}
};
rsx! {
div {
class: "{wrapper_class_string}",
if let Some(ref label) = props.label {
label {
class: "el-form-item__label",
"{label}"
}
}
div {
class: "el-input__wrapper",
if let Some(ref prefix_icon) = props.prefix_icon {
span {
class: "el-input__prefix",
i {
class: "{prefix_icon} el-input__icon"
}
}
}
{input_element}
if props.clearable {
span {
class: "el-input__suffix",
i {
class: "el-input__icon el-icon-circle-close"
}
}
}
if props.show_password && props.input_type == InputType::Password {
span {
class: "el-input__suffix",
i {
class: "el-input__icon el-icon-view"
}
}
}
if let Some(ref suffix_icon) = props.suffix_icon {
span {
class: "el-input__suffix",
i {
class: "{suffix_icon} el-input__icon"
}
}
}
}
}
}
}
#[component]
pub fn SearchInput(props: InputProps) -> Element {
let mut props = props;
if props.suffix_icon.is_none() {
props.suffix_icon = Some("el-icon-search".to_string());
}
Input(props)
}
#[component]
pub fn PasswordInput(props: InputProps) -> Element {
let mut props = props;
props.input_type = InputType::Password;
props.show_password = true;
Input(props)
}