summaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
authorBenedikt Peetz <benedikt.peetz@b-peetz.de>2025-10-05 13:21:31 +0200
committerBenedikt Peetz <benedikt.peetz@b-peetz.de>2025-10-05 13:21:31 +0200
commit0564611c8e77e0f5791a3f4854bb456a8717e86a (patch)
tree5f6f65a3837c2c37f984b7f3cfa5425351ff6752 /src
parentfeat(buy): Add the framework for the /buy page (diff)
downloadweb-client-0564611c8e77e0f5791a3f4854bb456a8717e86a.zip
feat(form): Provide basic form framework
Diffstat (limited to '')
-rw-r--r--src/components/buy.rs56
-rw-r--r--src/components/form.rs119
-rw-r--r--src/components/input_placeholder.rs11
-rw-r--r--src/components/mod.rs1
4 files changed, 155 insertions, 32 deletions
diff --git a/src/components/buy.rs b/src/components/buy.rs
index 86e9952..0c294ee 100644
--- a/src/components/buy.rs
+++ b/src/components/buy.rs
@@ -1,38 +1,34 @@
-use leptos::{
- IntoView, component,
- html::Input,
- prelude::{ClassAttribute, ElementChild, Get, NodeRef, OnAttribute, Set, signal},
- view,
- web_sys::SubmitEvent,
-};
+use leptos::{IntoView, component, view};
+use log::info;
-use crate::components::{input_placeholder::InputPlaceholder, site_header::SiteHeader};
+use crate::components::{form::Form, input_placeholder::InputPlaceholder, site_header::SiteHeader};
#[component]
pub fn Buy() -> impl IntoView {
- let (product_barcode, set_product_barcode) = signal(String::new());
-
- let input_element: NodeRef<Input> = NodeRef::new();
-
- let on_submit = move |ev: SubmitEvent| {
- // stop the page from reloading!
- ev.prevent_default();
-
- let value = input_element
- .get()
- // event handlers can only fire after the view
- // is mounted to the DOM, so the `NodeRef` will be `Some`
- .expect("<input> to exist")
- .value();
- set_product_barcode.set(value);
- };
-
view! {
<SiteHeader logo=icondata_io::IoPricetag back_location="/" name="Buy" />
- <form class="flex flex-col contents-start m-2 g-2" on:submit=on_submit>
- <InputPlaceholder input_type="number" label="Product Barcode" node_ref=input_element />
- <input type="submit" value="Submit" />
- </form>
- <p>"Name is: " {product_barcode}</p>
+
+ {Form! {
+ on_submit = |Inputs {product_barcode, amount}| {
+ info!("Got product barcode: {product_barcode} with amount: {amount}");
+ }
+
+ <Input
+ name=product_barcode,
+ signal_name_get=product_barcode_get,
+ signal_name_set=product_barcode_set,
+ rust_type=u32,
+ html_type="number",
+ label="Product Barcode"
+ />
+ <Input
+ name=amount,
+ signal_name_get=amount_get,
+ signal_name_set=amount_set,
+ rust_type=u16,
+ html_type="number",
+ label="Amount"
+ />
+ }}
}
}
diff --git a/src/components/form.rs b/src/components/form.rs
new file mode 100644
index 0000000..fd55897
--- /dev/null
+++ b/src/components/form.rs
@@ -0,0 +1,119 @@
+macro_rules! Form {
+ (
+ on_submit = |$bound:pat_param| $on_submit:block
+ $(
+ <Input
+ name=$name:ident,
+ signal_name_get=$signal_name_get:ident,
+ signal_name_set=$signal_name_set:ident,
+ rust_type=$rust_type:ty,
+ html_type=$input_type:literal,
+ label=$label:literal $(,)*
+ />
+ )*
+ ) => {{
+ use leptos::{
+ view,
+ prelude::{
+ Get,
+ NodeRef,
+ ElementChild,
+ ClassAttribute,
+ OnAttribute,
+ signal,
+ Set,
+ Show,
+ },
+ html::Input,
+ web_sys::SubmitEvent
+ };
+
+ use log::info;
+
+
+ $(
+ let ($signal_name_get, $signal_name_set) = signal(None);
+ let $name: NodeRef<Input> = NodeRef::new();
+ )*
+
+ let on_submit = move |ev: SubmitEvent| {
+ struct Inputs {
+ $(
+ $name: $rust_type
+ ),*
+ }
+
+ // stop the page from reloading!
+ ev.prevent_default();
+
+ $(
+ let value = {
+ let output = $name
+ .get()
+ // event handlers can only fire after the view
+ // is mounted to the DOM, so the `NodeRef` will be `Some`
+ .expect("<input> to exist")
+ .value();
+
+ let fin: Result<$rust_type, leptos::error::Error> = output
+ .parse()
+ .map_err(Into::<leptos::error::Error>::into);
+ fin
+ };
+
+ let $name = match value {
+ Ok(ok) => {
+ // Reset the signal
+ $signal_name_set.set(None);
+
+ ok
+ } ,
+ Err(err) => {
+ $signal_name_set.set(Some(err));
+
+ // Skip running the real `on_submit`
+ return
+ }
+ };
+ )*
+
+ let real_on_submit = |$bound| $on_submit;
+ real_on_submit(Inputs {
+ $(
+ $name
+ ),*
+ })
+ };
+
+
+ view! {
+ <form class="flex flex-col contents-start m-2 g-2" on:submit=on_submit>
+ $(
+ <InputPlaceholder input_type=$input_type label=$label node_ref=$name />
+ <Show
+ when=move || $signal_name_get.get().is_some()
+ fallback=|| ()
+ >
+ <p class="ps-2 text-red-300">{move ||
+ format!(
+ "Input is invalid for type {}: {}",
+ stringify!($rust_type),
+ $signal_name_get.get().expect("Was `is_some`")
+ )
+ }</p>
+ </Show>
+ )*
+
+ <div class="static">
+ <input
+ type="submit"
+ value="Submit"
+ class="absolute bottom-0 right-0 h-20 w-20 rounded-lg bg-green-300 m-2"
+ />
+ </div>
+ </form>
+ }
+ }};
+}
+
+pub(crate) use Form;
diff --git a/src/components/input_placeholder.rs b/src/components/input_placeholder.rs
index 92f9926..05b9509 100644
--- a/src/components/input_placeholder.rs
+++ b/src/components/input_placeholder.rs
@@ -1,10 +1,17 @@
+use std::sync::atomic::{AtomicU32, Ordering};
+
use leptos::{
IntoView, component,
html::Input,
prelude::{ClassAttribute, ElementChild, GlobalAttributes, NodeRef, NodeRefAttribute},
view,
};
-use uuid::Uuid;
+
+fn get_id() -> u32 {
+ static ID: AtomicU32 = AtomicU32::new(0);
+
+ ID.fetch_add(1, Ordering::Relaxed)
+}
#[component]
pub fn InputPlaceholder(
@@ -13,7 +20,7 @@ pub fn InputPlaceholder(
node_ref: NodeRef<Input>,
#[prop(default = None)] initial_value: Option<String>,
) -> impl IntoView {
- let id = Uuid::new_v4();
+ let id = get_id();
view! {
<div class="relative h-14">
diff --git a/src/components/mod.rs b/src/components/mod.rs
index 55e4397..f7b8dba 100644
--- a/src/components/mod.rs
+++ b/src/components/mod.rs
@@ -3,6 +3,7 @@ pub mod async_fetch;
pub mod container;
pub mod icon_p;
pub mod input_placeholder;
+pub mod form;
// Specific
pub mod buy;