mirror of
https://github.com/adoyle0/thaw.git
synced 2025-02-02 16:44: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",
|
"AiLoadingOutlined",
|
||||||
"AiCheckCircleFilled",
|
"AiCheckCircleFilled",
|
||||||
"AiExclamationCircleFilled",
|
"AiExclamationCircleFilled",
|
||||||
"AiCloseCircleFilled"
|
"AiCloseCircleFilled",
|
||||||
|
"AiPlusOutlined",
|
||||||
|
"AiPlusOutlined",
|
||||||
|
"AiMinusOutlined",
|
||||||
] }
|
] }
|
||||||
|
|
||||||
[workspace]
|
[workspace]
|
||||||
|
|
|
@ -31,6 +31,7 @@ pub fn App() -> impl IntoView {
|
||||||
<Route path="/badge" view=BadgePage/>
|
<Route path="/badge" view=BadgePage/>
|
||||||
<Route path="/card" view=CardPage/>
|
<Route path="/card" view=CardPage/>
|
||||||
<Route path="/divider" view=DividerPage/>
|
<Route path="/divider" view=DividerPage/>
|
||||||
|
<Route path="/input-number" view=InputNumberPage/>
|
||||||
</Route>
|
</Route>
|
||||||
<Route path="/mobile/tabbar" view=TabbarDemoPage/>
|
<Route path="/mobile/tabbar" view=TabbarDemoPage/>
|
||||||
<Route path="/mobile/nav-bar" view=NavBarDemoPage/>
|
<Route path="/mobile/nav-bar" view=NavBarDemoPage/>
|
||||||
|
|
|
@ -117,6 +117,10 @@ fn gen_menu_data() -> Vec<MenuGroupOption> {
|
||||||
value: "input".into(),
|
value: "input".into(),
|
||||||
label: "Input".into(),
|
label: "Input".into(),
|
||||||
},
|
},
|
||||||
|
MenuItemOption {
|
||||||
|
value: "input-number".into(),
|
||||||
|
label: "InputNumber".into(),
|
||||||
|
},
|
||||||
MenuItemOption {
|
MenuItemOption {
|
||||||
value: "select".into(),
|
value: "select".into(),
|
||||||
label: "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 home;
|
||||||
mod image;
|
mod image;
|
||||||
mod input;
|
mod input;
|
||||||
|
mod input_number;
|
||||||
mod menu;
|
mod menu;
|
||||||
mod mobile;
|
mod mobile;
|
||||||
mod modal;
|
mod modal;
|
||||||
|
@ -38,6 +39,7 @@ pub use grid::*;
|
||||||
pub use home::*;
|
pub use home::*;
|
||||||
pub use image::*;
|
pub use image::*;
|
||||||
pub use input::*;
|
pub use input::*;
|
||||||
|
pub use input_number::*;
|
||||||
pub use menu::*;
|
pub use menu::*;
|
||||||
pub use mobile::*;
|
pub use mobile::*;
|
||||||
pub use modal::*;
|
pub use modal::*;
|
||||||
|
|
|
@ -36,6 +36,11 @@
|
||||||
background-color: #f2f2f2;
|
background-color: #f2f2f2;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.melt-button--link {
|
||||||
|
height: auto;
|
||||||
|
padding: inherit;
|
||||||
|
}
|
||||||
|
|
||||||
.melt-button--link:hover {
|
.melt-button--link:hover {
|
||||||
color: var(--font-color-hover);
|
color: var(--font-color-hover);
|
||||||
}
|
}
|
||||||
|
|
|
@ -41,10 +41,12 @@ pub fn Input(
|
||||||
let theme = use_theme(Theme::light);
|
let theme = use_theme(Theme::light);
|
||||||
mount_style("input", include_str!("./input.css"));
|
mount_style("input", include_str!("./input.css"));
|
||||||
|
|
||||||
|
let value_trigger = create_trigger();
|
||||||
let on_input = move |ev| {
|
let on_input = move |ev| {
|
||||||
let input_value = event_target_value(&ev);
|
let input_value = event_target_value(&ev);
|
||||||
if let Some(allow_value) = allow_value.as_ref() {
|
if let Some(allow_value) = allow_value.as_ref() {
|
||||||
if !allow_value.call(input_value.clone()) {
|
if !allow_value.call(input_value.clone()) {
|
||||||
|
value_trigger.notify();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -74,7 +76,10 @@ pub fn Input(
|
||||||
<div class="melt-input" style=move || css_vars.get()>
|
<div class="melt-input" style=move || css_vars.get()>
|
||||||
<input
|
<input
|
||||||
type=move || variant.get().as_str()
|
type=move || variant.get().as_str()
|
||||||
prop:value=move || value.get()
|
prop:value=move || {
|
||||||
|
value_trigger.track();
|
||||||
|
value.get()
|
||||||
|
}
|
||||||
on:input=on_input
|
on:input=on_input
|
||||||
on:focus=on_internal_focus
|
on:focus=on_internal_focus
|
||||||
on:blur=on_internal_blur
|
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 leptos::*;
|
||||||
|
use std::ops::{Add, Sub};
|
||||||
|
use std::str::FromStr;
|
||||||
|
|
||||||
#[component]
|
#[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! {
|
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)]
|
#[derive(Clone)]
|
||||||
pub enum StoredMaybeSignal<T>
|
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> {
|
impl<T> SignalWith for StoredMaybeSignal<T> {
|
||||||
type Value = T;
|
type Value = T;
|
||||||
|
|
||||||
|
|
Loading…
Add table
Reference in a new issue