summaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
authorBenedikt Peetz <benedikt.peetz@b-peetz.de>2025-10-05 18:27:05 +0200
committerBenedikt Peetz <benedikt.peetz@b-peetz.de>2025-10-05 18:27:05 +0200
commit1fc165f2a5a3b6d77da2cfea2aa05e1db1c73577 (patch)
tree2ea10b7aa960ecfa932b1091a43f101c5668cea8 /src
parentfeat(form): Provide basic form framework (diff)
downloadweb-client-1fc165f2a5a3b6d77da2cfea2aa05e1db1c73577.zip
feat(form): Re-write the form macro as a proc macro
This allows more possibilities.
Diffstat (limited to '')
-rw-r--r--src/components/buy.rs23
-rw-r--r--src/components/form.rs120
-rw-r--r--src/components/input_placeholder.rs7
-rw-r--r--src/components/mod.rs11
-rw-r--r--src/components/select_placeholder.rs93
5 files changed, 120 insertions, 134 deletions
diff --git a/src/components/buy.rs b/src/components/buy.rs
index 0c294ee..6d9402e 100644
--- a/src/components/buy.rs
+++ b/src/components/buy.rs
@@ -1,7 +1,9 @@
use leptos::{IntoView, component, view};
use log::info;
+use rocie_client::models::UnitId;
+use uuid::Uuid;
-use crate::components::{form::Form, input_placeholder::InputPlaceholder, site_header::SiteHeader};
+use crate::components::{form::Form, site_header::SiteHeader};
#[component]
pub fn Buy() -> impl IntoView {
@@ -9,22 +11,27 @@ pub fn Buy() -> impl IntoView {
<SiteHeader logo=icondata_io::IoPricetag back_location="/" name="Buy" />
{Form! {
- on_submit = |Inputs {product_barcode, amount}| {
- info!("Got product barcode: {product_barcode} with amount: {amount}");
- }
+ on_submit = |product_barcode, amount, unit_id| {
+ info!("Got product barcode: {product_barcode} with amount: {amount}, {unit_id}");
+ };
<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"
/>
+ <Select
+ name=unit_id,
+ rust_type=Uuid,
+ label="Unit",
+ options=[
+ ("Kilogram", Uuid::new_v4()),
+ ("Gram", Uuid::new_v4())
+ ]
+ />
<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
index fd55897..9c371ad 100644
--- a/src/components/form.rs
+++ b/src/components/form.rs
@@ -1,119 +1 @@
-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;
+pub(crate) use rocie_macros::Form;
diff --git a/src/components/input_placeholder.rs b/src/components/input_placeholder.rs
index 05b9509..aeef838 100644
--- a/src/components/input_placeholder.rs
+++ b/src/components/input_placeholder.rs
@@ -1,5 +1,3 @@
-use std::sync::atomic::{AtomicU32, Ordering};
-
use leptos::{
IntoView, component,
html::Input,
@@ -7,11 +5,8 @@ use leptos::{
view,
};
-fn get_id() -> u32 {
- static ID: AtomicU32 = AtomicU32::new(0);
+use crate::components::get_id;
- ID.fetch_add(1, Ordering::Relaxed)
-}
#[component]
pub fn InputPlaceholder(
diff --git a/src/components/mod.rs b/src/components/mod.rs
index f7b8dba..1ee37d5 100644
--- a/src/components/mod.rs
+++ b/src/components/mod.rs
@@ -1,9 +1,12 @@
+use std::sync::atomic::{AtomicU32, Ordering};
+
// Generic
pub mod async_fetch;
pub mod container;
+pub mod form;
pub mod icon_p;
pub mod input_placeholder;
-pub mod form;
+pub mod select_placeholder;
// Specific
pub mod buy;
@@ -11,3 +14,9 @@ pub mod inventory;
pub mod product_overview;
pub mod recipies;
pub mod site_header;
+
+fn get_id() -> u32 {
+ static ID: AtomicU32 = AtomicU32::new(0);
+
+ ID.fetch_add(1, Ordering::Relaxed)
+}
diff --git a/src/components/select_placeholder.rs b/src/components/select_placeholder.rs
new file mode 100644
index 0000000..947931c
--- /dev/null
+++ b/src/components/select_placeholder.rs
@@ -0,0 +1,93 @@
+use leptos::{
+ IntoView,
+ attr::{AttributeValue, IntoAttributeValue},
+ component,
+ html::Select,
+ prelude::{
+ ClassAttribute, CollectView, ElementChild, GlobalAttributes, NodeRef, NodeRefAttribute,
+ },
+ view,
+};
+
+use crate::components::get_id;
+
+#[component]
+pub fn SelectPlaceholder<T>(
+ label: &'static str,
+ node_ref: NodeRef<Select>,
+ options: Vec<(&'static str, T)>,
+) -> impl IntoView
+where
+ T: IntoAttributeValue,
+ <T as IntoAttributeValue>::Output: Send + AttributeValue,
+{
+ let id = get_id();
+
+ let options = options
+ .into_iter()
+ .map(|(label, value)| {
+ view! {
+ <option value=value>{label}</option>
+ }
+ })
+ .collect_view();
+
+ view! {
+ <div class="relative h-14">
+ <select
+ id=id.to_string()
+ class="\
+ absolute \
+ bottom-0 \
+ bg-gray-200 \
+ border-8 \
+ border-b-2 \
+ border-b-trasparent \
+ border-gray-200 \
+ focus:outline-none \
+ h-10 \
+ peer \
+ placeholder-transparent \
+ rounded-t-lg \
+ text-gray-900 \
+ w-full \
+ "
+ node_ref=node_ref
+ >
+ {options}
+ </select>
+
+ // TODO: Reference `var(--tw-border-2)` instead of the `2 px` <2025-10-01>
+ <div class="
+ absolute \
+ bottom-0 \
+ h-[2px] \
+ w-full \
+ bg-gray-300 \
+ peer-focus:bg-indigo-600 \
+ " />
+
+ <label
+ for=id.to_string()
+ class="\
+ bottom-10 \
+ absolute \
+ left-0 \
+ text-gray-700 \
+ text-sm \
+ transition-all \
+ peer-focus:bottom-10 \
+ peer-focus:left-0 \
+ peer-focus:text-gray-700 \
+ peer-focus:text-sm \
+ peer-placeholder-shown:text-base \
+ peer-placeholder-shown:text-gray-400 \
+ peer-placeholder-shown:bottom-2 \
+ peer-placeholder-shown:left-2 \
+ "
+ >
+ {label}
+ </label>
+ </div>
+ }
+}