summary refs log tree commit diff stats
path: root/src/components
diff options
context:
space:
mode:
Diffstat (limited to 'src/components')
-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;