219 lines
5.4 KiB
Rust
219 lines
5.4 KiB
Rust
use dioxus::prelude::*;
|
|
use dioxus_free_icons::icons::io_icons::IoEye;
|
|
use dioxus_free_icons::icons::io_icons::IoEyeOff;
|
|
use dioxus_free_icons::Icon;
|
|
|
|
use super::icons::Pyramid;
|
|
|
|
turf::style_sheet!("src/ui/components/text_input.scss");
|
|
|
|
pub trait InputPropsData {}
|
|
|
|
#[derive(Clone, PartialEq, Props)]
|
|
pub struct InputProps<D: InputPropsData + 'static + std::cmp::PartialEq + std::clone::Clone> {
|
|
value: Option<String>,
|
|
placeholder: Option<String>,
|
|
oninput: Option<EventHandler<Event<FormData>>>,
|
|
state: Option<Signal<D>>,
|
|
}
|
|
|
|
#[derive(Clone, PartialEq)]
|
|
pub struct TextInputState {
|
|
pub is_valid: bool,
|
|
pub helper_text: Option<String>,
|
|
}
|
|
|
|
impl TextInputState {
|
|
pub fn new() -> Self {
|
|
TextInputState {
|
|
is_valid: true,
|
|
helper_text: None,
|
|
}
|
|
}
|
|
|
|
pub fn reset(&mut self) {
|
|
self.is_valid = true;
|
|
self.helper_text = None;
|
|
}
|
|
|
|
pub fn invalidate(&mut self, helper_text: String) {
|
|
self.is_valid = false;
|
|
self.helper_text = Some(helper_text);
|
|
}
|
|
}
|
|
|
|
impl Default for TextInputState {
|
|
fn default() -> Self {
|
|
Self::new()
|
|
}
|
|
}
|
|
|
|
impl InputPropsData for TextInputState {}
|
|
|
|
pub fn TextInput(props: InputProps<TextInputState>) -> Element {
|
|
let mut criticity_class = "";
|
|
let mut helper_text = "".to_string();
|
|
|
|
if let Some(state) = props.state {
|
|
let state = state.read();
|
|
if !state.is_valid {
|
|
criticity_class = ClassName::INVALID;
|
|
}
|
|
if let Some(text) = &state.helper_text {
|
|
helper_text = text.to_string();
|
|
}
|
|
}
|
|
|
|
let input_classes_str = [ClassName::TEXT_INPUT_INPUT, criticity_class].join(" ");
|
|
|
|
rsx! {
|
|
style { {STYLE_SHEET} }
|
|
|
|
div {
|
|
class: ClassName::TEXT_INPUT,
|
|
|
|
input {
|
|
class: "{input_classes_str}",
|
|
r#type: "text",
|
|
placeholder: props.placeholder,
|
|
value: props.value,
|
|
|
|
oninput: move |evt| {
|
|
if let Some(cb) = &props.oninput {
|
|
cb.call(evt);
|
|
}
|
|
},
|
|
}
|
|
|
|
div {
|
|
class: ClassName::TEXT_INPUT_HELPER_TEXT,
|
|
|
|
p {
|
|
class: criticity_class,
|
|
{helper_text}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
#[derive(Clone, PartialEq, Props)]
|
|
pub struct PasswordInputState {
|
|
text_input_state: TextInputState,
|
|
#[props(default = 0.0)]
|
|
pub score: f64,
|
|
}
|
|
|
|
impl PasswordInputState {
|
|
pub fn new() -> Self {
|
|
PasswordInputState {
|
|
text_input_state: TextInputState::new(),
|
|
score: 0.0,
|
|
}
|
|
}
|
|
|
|
pub fn reset(&mut self) {
|
|
self.text_input_state.reset()
|
|
}
|
|
|
|
pub fn invalidate(&mut self, helper_text: String) {
|
|
self.text_input_state.invalidate(helper_text)
|
|
}
|
|
}
|
|
|
|
impl Default for PasswordInputState {
|
|
fn default() -> Self {
|
|
Self::new()
|
|
}
|
|
}
|
|
|
|
impl InputPropsData for PasswordInputState {}
|
|
|
|
pub fn PasswordTextInput(props: InputProps<PasswordInputState>) -> Element {
|
|
let mut criticity_class = "";
|
|
let mut helper_text: String = "".to_string();
|
|
let mut score: Option<f64> = None;
|
|
|
|
let mut show_password = use_signal(|| false);
|
|
|
|
if let Some(state) = props.state {
|
|
let state = state.read();
|
|
if !state.text_input_state.is_valid {
|
|
criticity_class = ClassName::INVALID;
|
|
}
|
|
if let Some(text) = &state.text_input_state.helper_text {
|
|
helper_text = text.to_string();
|
|
}
|
|
score = Some(state.score);
|
|
}
|
|
|
|
let text_input_classes = [
|
|
ClassName::PASSWORD_TEXT_INPUT,
|
|
if score.is_none() {
|
|
ClassName::NO_STRENGTH
|
|
} else {
|
|
""
|
|
},
|
|
]
|
|
.join(" ");
|
|
let input_classes = [ClassName::PASSWORD_TEXT_INPUT_INPUT, criticity_class].join(" ");
|
|
|
|
rsx! {
|
|
style { {STYLE_SHEET} }
|
|
|
|
div {
|
|
class: "{text_input_classes}",
|
|
|
|
input {
|
|
class: "{input_classes}",
|
|
r#type: if *show_password.read() { "text" } else { "password" },
|
|
placeholder: props.placeholder,
|
|
value: props.value,
|
|
|
|
oninput: move |evt| {
|
|
if let Some(cb) = &props.oninput {
|
|
cb.call(evt);
|
|
}
|
|
},
|
|
}
|
|
|
|
if let Some(score) = score {
|
|
div {
|
|
class: ClassName::PASSWORD_TEXT_INPUT_STRENGTH_LEVEL,
|
|
Pyramid {
|
|
ratio: score,
|
|
}
|
|
}
|
|
}
|
|
|
|
div {
|
|
class: ClassName::PASSWORD_TEXT_INPUT_SHOW_TOGGLE,
|
|
onclick: move |_| {
|
|
let current_state = *show_password.read();
|
|
show_password.set(!current_state);
|
|
},
|
|
|
|
if *show_password.read() {
|
|
Icon {
|
|
icon: IoEyeOff,
|
|
}
|
|
}
|
|
else {
|
|
Icon {
|
|
icon: IoEye,
|
|
}
|
|
}
|
|
}
|
|
|
|
div {
|
|
class: ClassName::PASSWORD_TEXT_INPUT_HELPER_TEXT,
|
|
|
|
p {
|
|
class: criticity_class,
|
|
{helper_text}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|