use crate::countries::COUNTRY_CODES;
use web_sys::HtmlInputElement;
use yew::prelude::*;
#[derive(Properties, PartialEq, Clone)]
pub struct Props {
#[prop_or("text")]
pub r#type: &'static str,
#[prop_or_default]
pub label: &'static str,
#[prop_or_default]
pub name: &'static str,
#[prop_or_default]
pub required: bool,
pub r#ref: NodeRef,
#[prop_or_default]
pub error_message: &'static str,
#[prop_or_default]
pub input_class: &'static str,
#[prop_or_default]
pub field_class: &'static str,
#[prop_or_default]
pub label_class: &'static str,
#[prop_or_default]
pub class: &'static str,
#[prop_or_default]
pub error_class: &'static str,
#[prop_or_default]
pub icon_class: &'static str,
pub handle: UseStateHandle<String>,
pub valid_handle: UseStateHandle<bool>,
pub validate_function: Callback<String, bool>,
#[prop_or("cursor-pointer right-4 top-1 text-2xl text-gray-600 toggle-button fa fa-eye")]
pub eye_active: &'static str,
#[prop_or("cursor-pointer right-4 top-1 text-2xl text-gray-600 toggle-button fa fa-eye-slash")]
pub eye_disabled: &'static str,
#[prop_or_default]
pub id: &'static str,
#[prop_or_default]
pub placeholder: &'static str,
#[prop_or_default]
pub aria_label: &'static str,
#[prop_or("true")]
pub aria_required: &'static str,
#[prop_or("true")]
pub aria_invalid: &'static str,
#[prop_or_default]
pub aria_describedby: &'static str,
#[prop_or_default]
pub accept: &'static str,
#[prop_or_default]
pub alt: &'static str,
#[prop_or_default]
pub autocapitalize: &'static str,
#[prop_or_default]
pub autocomplete: &'static str,
#[prop_or_default]
pub capture: &'static str,
#[prop_or_default]
pub checked: bool,
#[prop_or_default]
pub dirname: &'static str,
#[prop_or_default]
pub disabled: bool,
#[prop_or_default]
pub form: &'static str,
#[prop_or_default]
pub formaction: &'static str,
#[prop_or_default]
pub formenctype: &'static str,
#[prop_or_default]
pub formmethod: &'static str,
#[prop_or_default]
pub formnovalidate: bool,
#[prop_or_default]
pub formtarget: &'static str,
#[prop_or_default]
pub height: Option<u32>,
#[prop_or_default]
pub list: &'static str,
#[prop_or_default]
pub max: &'static str,
#[prop_or_default]
pub maxlength: Option<usize>,
#[prop_or_default]
pub min: &'static str,
#[prop_or_default]
pub minlength: Option<usize>,
#[prop_or_default]
pub multiple: bool,
#[prop_or(".*")]
pub pattern: &'static str,
#[prop_or_default]
pub readonly: bool,
#[prop_or_default]
pub size: Option<u32>,
#[prop_or_default]
pub src: &'static str,
#[prop_or_default]
pub step: &'static str,
#[prop_or_default]
pub value: &'static str,
#[prop_or_default]
pub width: Option<u32>,
}
#[function_component(Input)]
pub fn input(props: &Props) -> Html {
let eye_active_handle = use_state(|| false);
let eye_active = *eye_active_handle;
let country_ref = use_node_ref();
let country_handle = use_state(String::default);
let country = (*country_handle).clone();
let password_type_handle = use_state(|| "password");
let password_type = *password_type_handle;
let valid = *props.valid_handle;
let eye_icon_active = props.eye_active;
let eye_icon_disabled = props.eye_disabled;
let r#type = props.r#type;
let onchange = {
let r#ref = props.r#ref.clone();
let handle = props.handle.clone();
let valid_handle = props.valid_handle.clone();
let validate_function = props.validate_function.clone();
Callback::from(move |_| {
if let Some(input) = r#ref.cast::<HtmlInputElement>() {
let value = input.value();
handle.set(value);
valid_handle.set(validate_function.emit(input.value()));
}
})
};
let on_select_change = {
let country_ref = country_ref.clone();
let handle = props.handle.clone();
let country_handle = country_handle.clone();
Callback::from(move |_| {
if let Some(input) = country_ref.cast::<HtmlInputElement>() {
let value = input.value();
country_handle.set(value);
handle.set(input.value());
}
})
};
let on_phone_number_input = {
let r#ref = props.r#ref.clone();
let handle = props.handle.clone();
let country_handle = country_handle;
Callback::from(move |_| {
if let Some(input) = r#ref.cast::<HtmlInputElement>() {
for (code, _, _, _, _, _) in &COUNTRY_CODES {
if code.starts_with(&input.value()) {
country_handle.set(input.value());
break;
}
}
let numeric_value: String =
input.value().chars().filter(|c| c.is_numeric()).collect();
handle.set('+'.to_string() + &numeric_value);
}
})
};
let on_toggle_password = {
Callback::from(move |_| {
if eye_active {
password_type_handle.set("password")
} else {
password_type_handle.set("text")
}
eye_active_handle.set(!eye_active);
})
};
let tag = match (*r#type).into() {
"password" => html! {
<>
<input
type={password_type}
class={props.input_class}
id={props.id}
name={props.name}
value={(*props.handle).clone()}
ref={props.r#ref.clone()}
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}
minlength={props.minlength.map(|v| v.to_string())}
maxlength={props.maxlength.map(|v| v.to_string())}
pattern={props.pattern}
size={props.size.map(|v| v.to_string())}
disabled={props.disabled}
list={props.list}
step={props.step}
min={props.min}
max={props.max}
/>
<span
class={format!("toggle-button {}", if eye_active { eye_icon_active } else { eye_icon_disabled })}
onclick={on_toggle_password}
/>
</>
},
"textarea" => html! {
<textarea
class={props.input_class}
id={props.id}
name={props.name}
value={(*props.handle).clone()}
ref={props.r#ref.clone()}
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}
minlength={props.minlength.map(|v| v.to_string())}
maxlength={props.maxlength.map(|v| v.to_string())}
disabled={props.disabled}
/>
},
"tel" => html! {
<>
<select ref={country_ref} onchange={on_select_change}>
{ for COUNTRY_CODES.iter().map(|(code, emoji, _, name, _, _)| {
let selected = *code == country;
html! {
<option value={*code} selected={selected}>{ format!("{} {} {}", emoji, name, code) }</option>
}
}) }
</select>
<input
type="tel"
id="telNo"
name="telNo"
size="20"
minlength="9"
value={(*props.handle).clone()}
maxlength={props.maxlength.map(|v| v.to_string())}
class={props.input_class}
placeholder={props.placeholder}
aria-label={props.aria_label}
aria-required={props.aria_required}
aria-invalid={props.aria_invalid}
oninput={on_phone_number_input}
ref={props.r#ref.clone()}
autocomplete={props.autocomplete}
autocapitalize={props.autocapitalize}
readonly={props.readonly}
pattern={props.pattern}
list={props.list}
disabled={props.disabled}
/>
</>
},
_ => html! {
<input
type={r#type}
class={props.input_class}
id={props.id}
value={(*props.handle).clone()}
name={props.name}
ref={props.r#ref.clone()}
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}
minlength={props.minlength.map(|v| v.to_string())}
maxlength={props.maxlength.map(|v| v.to_string())}
pattern={props.pattern}
size={props.size.map(|v| v.to_string())}
disabled={props.disabled}
list={props.list}
step={props.step}
min={props.min}
max={props.max}
/>
},
};
html! {
<div class={props.class}>
<label class={props.label_class} for={props.id}>{ props.label }</label>
<div class={props.field_class}>
{ tag }
<span class={props.icon_class} />
</div>
if !valid {
<div class={props.error_class} id={props.aria_describedby}>
{ props.error_message }
</div>
}
</div>
}
}