summary refs log tree commit diff stats
path: root/src/components/form.rs
diff options
context:
space:
mode:
Diffstat (limited to 'src/components/form.rs')
-rw-r--r--src/components/form.rs119
1 files changed, 119 insertions, 0 deletions
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;