🎨 Isolate infra and ui components
The src/base.rs is still to be reworked.
This commit is contained in:
218
src/ui/components/text_input.rs
Normal file
218
src/ui/components/text_input.rs
Normal file
@@ -0,0 +1,218 @@
|
||||
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(Props, Clone, PartialEq)]
|
||||
pub struct InputProps<D: InputPropsData + 'static + std::cmp::PartialEq> {
|
||||
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(Props, Clone, PartialEq)]
|
||||
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}
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user