feat: add InputNumber component

This commit is contained in:
luoxiao 2023-10-18 16:12:13 +08:00
parent cf96d5e6b2
commit 28956cf732
9 changed files with 133 additions and 6 deletions

View file

@ -23,7 +23,10 @@ icondata = { version = "0.0.7", features = [
"AiLoadingOutlined",
"AiCheckCircleFilled",
"AiExclamationCircleFilled",
"AiCloseCircleFilled"
"AiCloseCircleFilled",
"AiPlusOutlined",
"AiPlusOutlined",
"AiMinusOutlined",
] }
[workspace]

View file

@ -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/>

View file

@ -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(),

View 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>
}
}

View file

@ -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::*;

View file

@ -36,6 +36,11 @@
background-color: #f2f2f2;
}
.melt-button--link {
height: auto;
padding: inherit;
}
.melt-button--link:hover {
color: var(--font-color-hover);
}

View file

@ -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

View file

@ -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>
}
}

View file

@ -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;