mirror of
https://github.com/adoyle0/thaw.git
synced 2025-02-02 08:34:15 -05:00
feat: Input adds parser and format props
This commit is contained in:
parent
b2ff8d8dd0
commit
6cb1335299
3 changed files with 84 additions and 37 deletions
|
@ -1,21 +1,7 @@
|
||||||
use leptos::{ev, html, prelude::*};
|
use leptos::{ev, html, prelude::*};
|
||||||
use thaw_utils::{class_list, mount_style, BoxOneCallback, ComponentRef, Model, OptionalProp};
|
use thaw_utils::{
|
||||||
|
class_list, mount_style, ArcOneCallback, BoxOneCallback, ComponentRef, Model, OptionalProp,
|
||||||
#[derive(Default, Clone)]
|
};
|
||||||
pub enum InputVariant {
|
|
||||||
#[default]
|
|
||||||
Text,
|
|
||||||
Password,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl InputVariant {
|
|
||||||
pub fn as_str(&self) -> &'static str {
|
|
||||||
match self {
|
|
||||||
InputVariant::Text => "text",
|
|
||||||
InputVariant::Password => "password",
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[slot]
|
#[slot]
|
||||||
pub struct InputPrefix {
|
pub struct InputPrefix {
|
||||||
|
@ -34,34 +20,58 @@ pub struct InputSuffix {
|
||||||
#[component]
|
#[component]
|
||||||
pub fn Input(
|
pub fn Input(
|
||||||
#[prop(optional, into)] value: Model<String>,
|
#[prop(optional, into)] value: Model<String>,
|
||||||
#[prop(optional, into)] allow_value: Option<BoxOneCallback<String, bool>>,
|
#[prop(optional, into)] allow_value: Option<ArcOneCallback<String, bool>>,
|
||||||
#[prop(optional, into)] variant: MaybeSignal<InputVariant>,
|
#[prop(optional, into)] input_type: MaybeSignal<InputType>,
|
||||||
/// Placeholder text for the input.
|
/// Placeholder text for the input.
|
||||||
#[prop(optional, into)] placeholder: OptionalProp<MaybeSignal<String>>,
|
#[prop(optional, into)]
|
||||||
|
placeholder: OptionalProp<MaybeSignal<String>>,
|
||||||
#[prop(optional, into)] on_focus: Option<BoxOneCallback<ev::FocusEvent>>,
|
#[prop(optional, into)] on_focus: Option<BoxOneCallback<ev::FocusEvent>>,
|
||||||
#[prop(optional, into)] on_blur: Option<BoxOneCallback<ev::FocusEvent>>,
|
#[prop(optional, into)] on_blur: Option<BoxOneCallback<ev::FocusEvent>>,
|
||||||
/// Whether the input is disabled
|
/// Whether the input is disabled
|
||||||
#[prop(optional, into)] disabled: MaybeSignal<bool>,
|
#[prop(optional, into)]
|
||||||
|
disabled: MaybeSignal<bool>,
|
||||||
#[prop(optional)] input_prefix: Option<InputPrefix>,
|
#[prop(optional)] input_prefix: Option<InputPrefix>,
|
||||||
#[prop(optional)] input_suffix: Option<InputSuffix>,
|
#[prop(optional)] input_suffix: Option<InputSuffix>,
|
||||||
#[prop(optional)] comp_ref: ComponentRef<InputRef>,
|
#[prop(optional)] comp_ref: ComponentRef<InputRef>,
|
||||||
#[prop(optional, into)] class: MaybeProp<String>,
|
#[prop(optional, into)] class: MaybeProp<String>,
|
||||||
|
#[prop(optional, into)] parser: OptionalProp<BoxOneCallback<String, String>>,
|
||||||
|
#[prop(optional, into)] format: OptionalProp<BoxOneCallback<String, String>>,
|
||||||
// #[prop(attrs)] attrs: Vec<(&'static str, Attribute)>,
|
// #[prop(attrs)] attrs: Vec<(&'static str, Attribute)>,
|
||||||
) -> impl IntoView {
|
) -> impl IntoView {
|
||||||
mount_style("input", include_str!("./input.css"));
|
mount_style("input", include_str!("./input.css"));
|
||||||
|
|
||||||
let value_trigger = ArcTrigger::new();
|
let value_trigger = ArcTrigger::new();
|
||||||
|
let parser_none = parser.is_none();
|
||||||
let on_input = {
|
let on_input = {
|
||||||
let value_trigger = value_trigger.clone();
|
let value_trigger = value_trigger.clone();
|
||||||
move |ev| {
|
let allow_value = allow_value.clone();
|
||||||
let input_value = event_target_value(&ev);
|
|
||||||
if let Some(allow_value) = allow_value.as_ref() {
|
move |e| {
|
||||||
if !allow_value(input_value.clone()) {
|
if parser_none {
|
||||||
value_trigger.trigger();
|
let input_value = event_target_value(&e);
|
||||||
return;
|
if let Some(allow_value) = allow_value.as_ref() {
|
||||||
|
if !allow_value(input_value.clone()) {
|
||||||
|
value_trigger.trigger();
|
||||||
|
return;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
value.set(input_value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
let on_change = {
|
||||||
|
let value_trigger = value_trigger.clone();
|
||||||
|
move |e| {
|
||||||
|
if let Some(parser) = parser.as_ref() {
|
||||||
|
let parsed_input_value = parser(event_target_value(&e));
|
||||||
|
if let Some(allow_value) = allow_value.as_ref() {
|
||||||
|
if !allow_value(parsed_input_value.clone()) {
|
||||||
|
value_trigger.trigger();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
value.set(parsed_input_value);
|
||||||
}
|
}
|
||||||
value.set(input_value);
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
let is_focus = RwSignal::new(false);
|
let is_focus = RwSignal::new(false);
|
||||||
|
@ -139,14 +149,15 @@ pub fn Input(
|
||||||
}}
|
}}
|
||||||
|
|
||||||
<input
|
<input
|
||||||
type=move || variant.get().as_str()
|
type=move || input_type.get().as_str()
|
||||||
value=input_value
|
value=input_value
|
||||||
prop:value=move || {
|
prop:value=move || {
|
||||||
value_trigger.track();
|
value_trigger.track();
|
||||||
value.get()
|
format.as_ref().map_or_else(|| value.get(), |f| f(value.get()))
|
||||||
}
|
}
|
||||||
|
|
||||||
on:input=on_input
|
on:input=on_input
|
||||||
|
on:change=on_change
|
||||||
on:focus=on_internal_focus
|
on:focus=on_internal_focus
|
||||||
on:blur=on_internal_blur
|
on:blur=on_internal_blur
|
||||||
class="thaw-input__input"
|
class="thaw-input__input"
|
||||||
|
@ -165,6 +176,40 @@ pub fn Input(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Default, Clone)]
|
||||||
|
pub enum InputType {
|
||||||
|
#[default]
|
||||||
|
Text,
|
||||||
|
Password,
|
||||||
|
Search,
|
||||||
|
Tel,
|
||||||
|
Url,
|
||||||
|
Email,
|
||||||
|
Time,
|
||||||
|
Date,
|
||||||
|
DatetimeLocal,
|
||||||
|
Month,
|
||||||
|
Week,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl InputType {
|
||||||
|
pub fn as_str(&self) -> &'static str {
|
||||||
|
match self {
|
||||||
|
Self::Text => "text",
|
||||||
|
Self::Password => "password",
|
||||||
|
Self::Search => "search",
|
||||||
|
Self::Tel => "tel",
|
||||||
|
Self::Url => "url",
|
||||||
|
Self::Email => "email",
|
||||||
|
Self::Time => "time",
|
||||||
|
Self::Date => "date",
|
||||||
|
Self::DatetimeLocal => "datetime-local",
|
||||||
|
Self::Month => "month",
|
||||||
|
Self::Week => "week",
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
pub struct InputRef {
|
pub struct InputRef {
|
||||||
input_ref: NodeRef<html::Input>,
|
input_ref: NodeRef<html::Input>,
|
||||||
|
|
|
@ -86,28 +86,28 @@ where
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
pub struct ArcOneCallback<A>(Arc<dyn Fn(A) + Send + Sync + 'static>);
|
pub struct ArcOneCallback<A, Return = ()>(Arc<dyn Fn(A) -> Return + Send + Sync + 'static>);
|
||||||
|
|
||||||
impl<A> ArcOneCallback<A> {
|
impl<A, Return> ArcOneCallback<A, Return> {
|
||||||
pub fn new<F>(f: F) -> Self
|
pub fn new<F>(f: F) -> Self
|
||||||
where
|
where
|
||||||
F: Fn(A) + Send + Sync + 'static,
|
F: Fn(A) -> Return + Send + Sync + 'static,
|
||||||
{
|
{
|
||||||
Self(Arc::new(f))
|
Self(Arc::new(f))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<A> Deref for ArcOneCallback<A> {
|
impl<A, Return> Deref for ArcOneCallback<A, Return> {
|
||||||
type Target = Arc<dyn Fn(A) + Send + Sync + 'static>;
|
type Target = Arc<dyn Fn(A) -> Return + Send + Sync + 'static>;
|
||||||
|
|
||||||
fn deref(&self) -> &Self::Target {
|
fn deref(&self) -> &Self::Target {
|
||||||
&self.0
|
&self.0
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<F, A> From<F> for ArcOneCallback<A>
|
impl<F, A, Return> From<F> for ArcOneCallback<A, Return>
|
||||||
where
|
where
|
||||||
F: Fn(A) + Send + Sync + 'static,
|
F: Fn(A) -> Return + Send + Sync + 'static,
|
||||||
{
|
{
|
||||||
fn from(value: F) -> Self {
|
fn from(value: F) -> Self {
|
||||||
Self::new(value)
|
Self::new(value)
|
||||||
|
|
|
@ -15,6 +15,8 @@ impl<T: Clone> Clone for OptionalProp<T> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl<T: Copy> Copy for OptionalProp<T> {}
|
||||||
|
|
||||||
impl<T> OptionalProp<T> {
|
impl<T> OptionalProp<T> {
|
||||||
pub fn map<U, F>(self, f: F) -> Option<U>
|
pub fn map<U, F>(self, f: F) -> Option<U>
|
||||||
where
|
where
|
||||||
|
|
Loading…
Add table
Reference in a new issue