mirror of
https://github.com/adoyle0/thaw.git
synced 2025-02-02 08:34:15 -05:00
feat: add InputNumber component
This commit is contained in:
parent
cf96d5e6b2
commit
28956cf732
9 changed files with 133 additions and 6 deletions
|
@ -23,7 +23,10 @@ icondata = { version = "0.0.7", features = [
|
|||
"AiLoadingOutlined",
|
||||
"AiCheckCircleFilled",
|
||||
"AiExclamationCircleFilled",
|
||||
"AiCloseCircleFilled"
|
||||
"AiCloseCircleFilled",
|
||||
"AiPlusOutlined",
|
||||
"AiPlusOutlined",
|
||||
"AiMinusOutlined",
|
||||
] }
|
||||
|
||||
[workspace]
|
||||
|
|
|
@ -31,6 +31,7 @@ pub fn App() -> impl IntoView {
|
|||
<Route path="/badge" view=BadgePage/>
|
||||
<Route path="/card" view=CardPage/>
|
||||
<Route path="/divider" view=DividerPage/>
|
||||
<Route path="/input-number" view=InputNumberPage/>
|
||||
</Route>
|
||||
<Route path="/mobile/tabbar" view=TabbarDemoPage/>
|
||||
<Route path="/mobile/nav-bar" view=NavBarDemoPage/>
|
||||
|
|
|
@ -117,6 +117,10 @@ fn gen_menu_data() -> Vec<MenuGroupOption> {
|
|||
value: "input".into(),
|
||||
label: "Input".into(),
|
||||
},
|
||||
MenuItemOption {
|
||||
value: "input-number".into(),
|
||||
label: "InputNumber".into(),
|
||||
},
|
||||
MenuItemOption {
|
||||
value: "select".into(),
|
||||
label: "Select".into(),
|
||||
|
|
40
demo/src/pages/input_number/mod.rs
Normal file
40
demo/src/pages/input_number/mod.rs
Normal file
|
@ -0,0 +1,40 @@
|
|||
use crate::components::{Demo, DemoCode};
|
||||
use leptos::*;
|
||||
use melt_ui::*;
|
||||
use prisms::highlight_str;
|
||||
|
||||
#[component]
|
||||
pub fn InputNumberPage() -> impl IntoView {
|
||||
let value = create_rw_signal(0);
|
||||
let value_f64 = create_rw_signal(0.0);
|
||||
view! {
|
||||
<div style="width: 896px; margin: 0 auto;">
|
||||
<h1>"InputNumber"</h1>
|
||||
<Demo>
|
||||
<Space vertical=true>
|
||||
<InputNumber value step=1/>
|
||||
<InputNumber value=value_f64 step=1.0/>
|
||||
</Space>
|
||||
<DemoCode
|
||||
slot
|
||||
html=highlight_str!(
|
||||
r#"
|
||||
let value = create_rw_signal(0);
|
||||
view! {
|
||||
<Space vertical=true>
|
||||
<InputNumber value step=1/>
|
||||
<InputNumber value=value_f64 step=1.0/>
|
||||
</Space>
|
||||
}
|
||||
"#,
|
||||
"rust"
|
||||
)
|
||||
>
|
||||
|
||||
""
|
||||
""
|
||||
</DemoCode>
|
||||
</Demo>
|
||||
</div>
|
||||
}
|
||||
}
|
|
@ -12,6 +12,7 @@ mod grid;
|
|||
mod home;
|
||||
mod image;
|
||||
mod input;
|
||||
mod input_number;
|
||||
mod menu;
|
||||
mod mobile;
|
||||
mod modal;
|
||||
|
@ -38,6 +39,7 @@ pub use grid::*;
|
|||
pub use home::*;
|
||||
pub use image::*;
|
||||
pub use input::*;
|
||||
pub use input_number::*;
|
||||
pub use menu::*;
|
||||
pub use mobile::*;
|
||||
pub use modal::*;
|
||||
|
|
|
@ -36,6 +36,11 @@
|
|||
background-color: #f2f2f2;
|
||||
}
|
||||
|
||||
.melt-button--link {
|
||||
height: auto;
|
||||
padding: inherit;
|
||||
}
|
||||
|
||||
.melt-button--link:hover {
|
||||
color: var(--font-color-hover);
|
||||
}
|
||||
|
|
|
@ -41,10 +41,12 @@ pub fn Input(
|
|||
let theme = use_theme(Theme::light);
|
||||
mount_style("input", include_str!("./input.css"));
|
||||
|
||||
let value_trigger = create_trigger();
|
||||
let on_input = move |ev| {
|
||||
let input_value = event_target_value(&ev);
|
||||
if let Some(allow_value) = allow_value.as_ref() {
|
||||
if !allow_value.call(input_value.clone()) {
|
||||
value_trigger.notify();
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
@ -74,7 +76,10 @@ pub fn Input(
|
|||
<div class="melt-input" style=move || css_vars.get()>
|
||||
<input
|
||||
type=move || variant.get().as_str()
|
||||
prop:value=move || value.get()
|
||||
prop:value=move || {
|
||||
value_trigger.track();
|
||||
value.get()
|
||||
}
|
||||
on:input=on_input
|
||||
on:focus=on_internal_focus
|
||||
on:blur=on_internal_blur
|
||||
|
|
|
@ -1,9 +1,58 @@
|
|||
use crate::Input;
|
||||
use crate::utils::StoredMaybeSignal;
|
||||
use crate::{
|
||||
utils::maybe_rw_signal::MaybeRwSignal, AiIcon, Button, ButtonVariant, Icon, Input, InputSuffix,
|
||||
};
|
||||
use leptos::*;
|
||||
use std::ops::{Add, Sub};
|
||||
use std::str::FromStr;
|
||||
|
||||
#[component]
|
||||
pub fn InputNumber() -> impl IntoView {
|
||||
pub fn InputNumber<T>(
|
||||
#[prop(optional, into)] value: MaybeRwSignal<T>,
|
||||
#[prop(optional, into)] placeholder: MaybeSignal<String>,
|
||||
#[prop(into)] step: MaybeSignal<T>,
|
||||
) -> impl IntoView
|
||||
where
|
||||
T: Add<Output = T> + Sub<Output = T>,
|
||||
T: Default + Clone + FromStr + ToString + 'static,
|
||||
{
|
||||
let input_value = create_rw_signal(String::default());
|
||||
create_effect(move |prev| {
|
||||
value.with(|value| {
|
||||
let value = value.to_string();
|
||||
if let Some(prev) = prev {
|
||||
if value == prev {
|
||||
return prev;
|
||||
}
|
||||
}
|
||||
input_value.set(value.clone());
|
||||
value
|
||||
})
|
||||
});
|
||||
|
||||
let allow_value = move |v: String| {
|
||||
let Ok(v) = v.parse::<T>() else { return false };
|
||||
value.set(v);
|
||||
true
|
||||
};
|
||||
let step: StoredMaybeSignal<_> = step.into();
|
||||
|
||||
let add = move |_| {
|
||||
value.set(value.get_untracked() + step.get_untracked());
|
||||
};
|
||||
let sub = move |_| {
|
||||
value.set(value.get_untracked() - step.get_untracked());
|
||||
};
|
||||
view! {
|
||||
<Input />
|
||||
<Input value=input_value allow_value placeholder>
|
||||
<InputSuffix slot>
|
||||
<Button variant=ButtonVariant::Link on_click=sub>
|
||||
<Icon icon=Icon::from(AiIcon::AiMinusOutlined) style="font-size: 18px"/>
|
||||
</Button>
|
||||
<Button variant=ButtonVariant::Link on_click=add>
|
||||
<Icon icon=Icon::from(AiIcon::AiPlusOutlined) style="font-size: 18px"/>
|
||||
</Button>
|
||||
</InputSuffix>
|
||||
</Input>
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
use leptos::{MaybeSignal, Signal, SignalGet, SignalWith, StoredValue};
|
||||
use leptos::{MaybeSignal, Signal, SignalGet, SignalGetUntracked, SignalWith, StoredValue};
|
||||
|
||||
#[derive(Clone)]
|
||||
pub enum StoredMaybeSignal<T>
|
||||
|
@ -29,6 +29,24 @@ impl<T: Clone> SignalGet for StoredMaybeSignal<T> {
|
|||
}
|
||||
}
|
||||
|
||||
impl<T: Clone> SignalGetUntracked for StoredMaybeSignal<T> {
|
||||
type Value = T;
|
||||
|
||||
fn get_untracked(&self) -> Self::Value {
|
||||
match self {
|
||||
StoredMaybeSignal::StoredValue(value) => value.get_value(),
|
||||
StoredMaybeSignal::Signal(signal) => signal.get_untracked(),
|
||||
}
|
||||
}
|
||||
|
||||
fn try_get_untracked(&self) -> Option<Self::Value> {
|
||||
match self {
|
||||
StoredMaybeSignal::StoredValue(value) => value.try_get_value(),
|
||||
StoredMaybeSignal::Signal(signal) => signal.try_get_untracked(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> SignalWith for StoredMaybeSignal<T> {
|
||||
type Value = T;
|
||||
|
||||
|
|
Loading…
Add table
Reference in a new issue