use crate::countries::COUNTRY_CODES;
use dioxus::prelude::*;
#[derive(Props, Clone)]
pub struct InputProps {
#[props(default = "text")]
pub r#type: &'static str,
#[props(default = "")]
pub label: &'static str,
#[props(default = "")]
pub name: &'static str,
#[props(default = false)]
pub required: bool,
#[props(default = "")]
pub error_message: &'static str,
#[props(default = "")]
pub input_class: &'static str,
#[props(default = "")]
pub field_class: &'static str,
#[props(default = "")]
pub label_class: &'static str,
#[props(default = "")]
pub class: &'static str,
#[props(default = "")]
pub error_class: &'static str,
#[props(default = "")]
pub icon_class: &'static str,
pub handle: Signal<String>,
pub valid_handle: Signal<bool>,
pub validate_function: fn(String) -> bool,
#[props(
default = "cursor-pointer right-4 top-1 text-2xl text-gray-600 toggle-button fa fa-eye"
)]
pub eye_active: &'static str,
#[props(
default = "cursor-pointer right-4 top-1 text-2xl text-gray-600 toggle-button fa fa-eye-slash"
)]
pub eye_disabled: &'static str,
#[props(default = "")]
pub id: &'static str,
#[props(default = "")]
pub placeholder: &'static str,
#[props(default = "")]
pub aria_label: &'static str,
#[props(default = "true")]
pub aria_required: &'static str,
#[props(default = "true")]
pub aria_invalid: &'static str,
#[props(default = "")]
pub aria_describedby: &'static str,
#[props(default = "")]
pub accept: &'static str,
#[props(default = "")]
pub alt: &'static str,
#[props(default = "")]
pub autocapitalize: &'static str,
#[props(default = "")]
pub autocomplete: &'static str,
#[props(default = "")]
pub capture: &'static str,
#[props(default = false)]
pub checked: bool,
#[props(default = "")]
pub dirname: &'static str,
#[props(default = false)]
pub disabled: bool,
#[props(default = "")]
pub form: &'static str,
#[props(default = "")]
pub formaction: &'static str,
#[props(default = "")]
pub formenctype: &'static str,
#[props(default = "")]
pub formmethod: &'static str,
#[props(default = false)]
pub formnovalidate: bool,
#[props(default = "")]
pub formtarget: &'static str,
#[props(default = None)]
pub height: Option<u32>,
#[props(default = "")]
pub list: &'static str,
#[props(default = "")]
pub max: &'static str,
#[props(default = None)]
pub maxlength: Option<usize>,
#[props(default = "")]
pub min: &'static str,
#[props(default = None)]
pub minlength: Option<usize>,
#[props(default = false)]
pub multiple: bool,
#[props(default = ".*")]
pub pattern: &'static str,
#[props(default = false)]
pub readonly: bool,
#[props(default = None)]
pub size: Option<u32>,
#[props(default = "")]
pub src: &'static str,
#[props(default = "")]
pub step: &'static str,
#[props(default = "")]
pub value: &'static str,
#[props(default = None)]
pub width: Option<u32>,
}
impl PartialEq for InputProps {
fn eq(&self, other: &Self) -> bool {
self.r#type == other.r#type
&& self.label == other.label
&& self.name == other.name
&& self.required == other.required
&& self.error_message == other.error_message
&& self.input_class == other.input_class
&& self.field_class == other.field_class
&& self.label_class == other.label_class
&& self.class == other.class
&& self.error_class == other.error_class
&& self.icon_class == other.icon_class
&& self.handle == other.handle
&& self.valid_handle == other.valid_handle
&& std::ptr::fn_addr_eq(self.validate_function, other.validate_function)
&& self.eye_active == other.eye_active
&& self.eye_disabled == other.eye_disabled
&& self.id == other.id
&& self.placeholder == other.placeholder
&& self.aria_label == other.aria_label
&& self.aria_required == other.aria_required
&& self.aria_invalid == other.aria_invalid
&& self.aria_describedby == other.aria_describedby
&& self.accept == other.accept
&& self.alt == other.alt
&& self.autocapitalize == other.autocapitalize
&& self.autocomplete == other.autocomplete
&& self.capture == other.capture
&& self.checked == other.checked
&& self.dirname == other.dirname
&& self.disabled == other.disabled
&& self.form == other.form
&& self.formaction == other.formaction
&& self.formenctype == other.formenctype
&& self.formmethod == other.formmethod
&& self.formnovalidate == other.formnovalidate
&& self.formtarget == other.formtarget
&& self.height == other.height
&& self.list == other.list
&& self.max == other.max
&& self.maxlength == other.maxlength
&& self.min == other.min
&& self.minlength == other.minlength
&& self.multiple == other.multiple
&& self.pattern == other.pattern
&& self.readonly == other.readonly
&& self.size == other.size
&& self.src == other.src
&& self.step == other.step
&& self.value == other.value
&& self.width == other.width
}
}
#[component]
pub fn Input(mut props: InputProps) -> Element {
let mut is_eye_active = use_signal(|| false);
let password_type = if is_eye_active() { "text" } else { "password" };
let mut country = use_signal(String::default);
let onchange = {
move |e: Event<FormData>| {
let value = e.value();
props.handle.set(value.clone());
props.valid_handle.set((props.validate_function)(value));
}
};
let on_select_change = {
move |e: Event<FormData>| {
let value = e.value();
country.set(value.clone());
props.handle.set(value);
}
};
let on_phone_number_input = {
move |e: Event<FormData>| {
let input_value = e.value();
for (code, _, _, _, _, _) in &COUNTRY_CODES {
if code.starts_with(&input_value) {
country.set(input_value);
break;
}
}
let numeric_value: String = e.value().chars().filter(|c| c.is_numeric()).collect();
props.handle.set('+'.to_string() + &numeric_value);
}
};
let toggle_eye_icon = {
move |_| {
is_eye_active.set(!is_eye_active());
}
};
let input_field = match props.r#type {
"password" => rsx! {
input {
r#type: "{password_type}",
class: "{props.input_class}",
id: "{props.id}",
name: "{props.name}",
value: "{props.handle}",
placeholder: "{props.placeholder}",
aria_label: "{props.aria_label}",
aria_required: "{props.aria_required}",
aria_invalid: "{props.aria_invalid}",
aria_describedby: "{props.aria_describedby}",
oninput: onchange,
required: props.required,
autocomplete: props.autocomplete,
autocapitalize: props.autocapitalize,
readonly: "{props.readonly}",
disabled: "{props.disabled}",
minlength: props.minlength.map(|v| v.to_string()).unwrap_or_default(),
maxlength: props.maxlength.map(|v| v.to_string()).unwrap_or_default(),
pattern: "{props.pattern}",
size: props.size.map(|v| v.to_string()).unwrap_or_default(),
}
span {
class: if is_eye_active() { props.eye_active } else { props.eye_disabled },
onclick: toggle_eye_icon
}
},
"tel" => rsx! {
select {
style: "max-width: 55px; font-size: 14px; padding: 10px;",
onchange: on_select_change,
{ COUNTRY_CODES.iter().map(|(code, emoji, _, name, _, _)| rsx! {
option { value: "{code}", selected: *code == country(), "{emoji} {name} ({code})" }
})}
}
input {
r#type: "tel",
class: "{props.input_class}",
id: "{props.id}",
name: "{props.name}",
value: "{props.handle}",
placeholder: "{props.placeholder}",
aria_label: "{props.aria_label}",
aria_required: "{props.aria_required}",
aria_invalid: "{props.aria_invalid}",
aria_describedby: "{props.aria_describedby}",
oninput: on_phone_number_input,
required: props.required,
autocomplete: props.autocomplete,
autocapitalize: props.autocapitalize,
readonly: "{props.readonly}",
disabled: "{props.disabled}",
minlength: props.minlength.map(|v| v.to_string()).unwrap_or_default(),
maxlength: props.maxlength.map(|v| v.to_string()).unwrap_or_default(),
pattern: "{props.pattern}",
size: props.size.map(|v| v.to_string()).unwrap_or_default(),
}
},
"textarea" => rsx! {
textarea {
class: "{props.input_class}",
id: "{props.id}",
name: "{props.name}",
value: "{props.handle}",
placeholder: "{props.placeholder}",
aria_label: "{props.aria_label}",
aria_required: "{props.aria_required}",
aria_invalid: "{props.aria_invalid}",
aria_describedby: "{props.aria_describedby}",
oninput: onchange,
required: props.required,
readonly: "{props.readonly}",
disabled: "{props.disabled}",
minlength: props.minlength.map(|v| v.to_string()).unwrap_or_default(),
maxlength: props.maxlength.map(|v| v.to_string()).unwrap_or_default(),
}
},
_ => rsx! {
input {
r#type: "{props.r#type}",
class: "{props.input_class}",
id: "{props.id}",
name: "{props.name}",
value: "{props.handle}",
placeholder: "{props.placeholder}",
aria_label: "{props.aria_label}",
aria_required: "{props.aria_required}",
aria_invalid: "{props.aria_invalid}",
aria_describedby: "{props.aria_describedby}",
oninput: onchange,
required: props.required,
autocomplete: props.autocomplete,
autocapitalize: props.autocapitalize,
readonly: "{props.readonly}",
disabled: "{props.disabled}",
minlength: props.minlength.map(|v| v.to_string()).unwrap_or_default(),
maxlength: props.maxlength.map(|v| v.to_string()).unwrap_or_default(),
pattern: "{props.pattern}",
size: props.size.map(|v| v.to_string()).unwrap_or_default(),
}
},
};
rsx! {
div {
class: "{props.class}",
label {
class: "{props.label_class}",
r#for: "{props.id}",
"{props.label}"
}
div {
class: "{props.field_class}",
{input_field}
span {class: "{props.icon_class}" }
}
if !(props.valid_handle)() {
div {
class: "{props.error_class}",
id: "{props.aria_describedby}",
"{props.error_message}"
}
}
}
}
}