summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--Cargo.lock402
-rw-r--r--flake.lock18
-rw-r--r--rocie-macros/Cargo.toml2
-rw-r--r--rocie-macros/src/form/generate.rs255
-rw-r--r--rocie-macros/src/form/mod.rs11
-rw-r--r--rocie-macros/src/form/parse.rs93
-rw-r--r--src/api/mod.rs56
-rw-r--r--src/components/async_fetch.rs67
-rw-r--r--src/components/banner.rs17
-rw-r--r--src/components/buy.rs210
-rw-r--r--src/components/input_placeholder.rs154
-rw-r--r--src/components/mod.rs1
-rw-r--r--src/components/product_overview.rs8
-rw-r--r--src/components/select_placeholder.rs40
14 files changed, 901 insertions, 433 deletions
diff --git a/Cargo.lock b/Cargo.lock
index 2d545c0..645f23d 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -3,21 +3,6 @@
version = 4
[[package]]
-name = "addr2line"
-version = "0.25.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "1b5d307320b3181d6d7954e663bd7c774a838b8220fe0593c86d9fb09f498b4b"
-dependencies = [
- "gimli",
-]
-
-[[package]]
-name = "adler2"
-version = "2.0.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "320119579fcad9c21884f5c4861d16174d0e06250625266f50fe6898340abefa"
-
-[[package]]
name = "aho-corasick"
version = "1.1.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -42,7 +27,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1384d3fe1eecb464229fcf6eebb72306591c56bf27b373561489458a7c73027d"
dependencies = [
"futures",
- "thiserror 2.0.16",
+ "thiserror 2.0.17",
"wasm-bindgen-futures",
]
@@ -88,9 +73,9 @@ checksum = "1505bd5d3d116872e7271a6d4e16d81d0c8570876c8de68093a09ac269d8aac0"
[[package]]
name = "attribute-derive"
-version = "0.10.3"
+version = "0.10.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "0053e96dd3bec5b4879c23a138d6ef26f2cb936c9cdc96274ac2b9ed44b5bb54"
+checksum = "05832cdddc8f2650cc2cc187cc2e952b8c133a48eb055f35211f61ee81502d77"
dependencies = [
"attribute-derive-macro",
"derive-where",
@@ -102,9 +87,9 @@ dependencies = [
[[package]]
name = "attribute-derive-macro"
-version = "0.10.3"
+version = "0.10.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "463b53ad0fd5b460af4b1915fe045ff4d946d025fb6c4dc3337752eaa980f71b"
+checksum = "0a7cdbbd4bd005c5d3e2e9c885e6fa575db4f4a3572335b974d8db853b6beb61"
dependencies = [
"collection_literals",
"interpolator",
@@ -123,21 +108,6 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c08606f8c3cbf4ce6ec8e28fb0014a2c086708fe954eaa885384a6165172e7e8"
[[package]]
-name = "backtrace"
-version = "0.3.76"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "bb531853791a215d7c62a30daf0dde835f381ab5de4589cfe7c649d2cbe92bd6"
-dependencies = [
- "addr2line",
- "cfg-if",
- "libc",
- "miniz_oxide",
- "object",
- "rustc-demangle",
- "windows-link",
-]
-
-[[package]]
name = "base16"
version = "0.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -151,9 +121,9 @@ checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6"
[[package]]
name = "bitflags"
-version = "2.9.4"
+version = "2.10.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "2261d10cca569e4643e526d8dc2e62e433cc8aba21ab764233731f8d369bf394"
+checksum = "812e12b5285cc515a9c72a5c1d3b6d46a19dac5acfef5265968c166106e31dd3"
[[package]]
name = "block-buffer"
@@ -178,15 +148,15 @@ checksum = "d71b6127be86fdcfddb610f7182ac57211d4b18a3e9c82eb2d17662f2227ad6a"
[[package]]
name = "camino"
-version = "1.2.0"
+version = "1.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "e1de8bc0aa9e9385ceb3bf0c152e3a9b9544f6c4a912c8ae504e80c1f0368603"
+checksum = "276a59bf2b2c967788139340c9f0c5b12d7fd6630315c15c217e559de85d2609"
[[package]]
name = "cc"
-version = "1.2.39"
+version = "1.2.41"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "e1354349954c6fc9cb0deab020f27f783cf0b604e8bb754dc4658ecf0d29c35f"
+checksum = "ac9fe6cdbb24b6ade63616c0a0688e45bb56732262c158df3c0c4bea4ca47cb7"
dependencies = [
"find-msvc-tools",
"shlex",
@@ -194,9 +164,9 @@ dependencies = [
[[package]]
name = "cfg-if"
-version = "1.0.3"
+version = "1.0.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "2fd1289c04a9ea8cb22300a459a72a385d7c73d3259e2ed7dcb2af674838cfa9"
+checksum = "9330f8b2ff13f34540b44e946ef35111825727b38d33286ef986142615121801"
[[package]]
name = "chrono"
@@ -212,20 +182,20 @@ dependencies = [
[[package]]
name = "codee"
-version = "0.3.2"
+version = "0.3.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "fd8bbfdadf2f8999c6e404697bc08016dce4a3d77dec465b36c9a0652fdb3327"
+checksum = "774365d8238a8dbd57c3047f865187fe6417e765d9955ba8e99e794678a41a0e"
dependencies = [
"serde",
"serde_json",
- "thiserror 2.0.16",
+ "thiserror 2.0.17",
]
[[package]]
name = "collection_literals"
-version = "1.0.2"
+version = "1.0.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "26b3f65b8fb8e88ba339f7d23a390fe1b0896217da05e2a66c584c9b29a91df8"
+checksum = "2550f75b8cfac212855f6b1885455df8eaee8fe8e246b647d69146142e016084"
[[package]]
name = "concurrent-queue"
@@ -238,9 +208,9 @@ dependencies = [
[[package]]
name = "config"
-version = "0.15.17"
+version = "0.15.18"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "680d3ac2fe066c43300ec831c978871e50113a708d58ab13d231bd92deca5adb"
+checksum = "180e549344080374f9b32ed41bf3b6b57885ff6a289367b3dbc10eea8acc1918"
dependencies = [
"convert_case 0.6.0",
"pathdiff",
@@ -277,9 +247,9 @@ checksum = "451d0640545a0553814b4c646eb549343561618838e9b42495f466131fe3ad49"
[[package]]
name = "const_format"
-version = "0.2.34"
+version = "0.2.35"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "126f97965c8ad46d6d9163268ff28432e8f6a1196a55578867832e3049df63dd"
+checksum = "7faa7469a93a566e9ccc1c73fe783b4a65c274c5ace346038dca9c39fe0030ad"
dependencies = [
"const_format_proc_macros",
]
@@ -504,9 +474,9 @@ dependencies = [
[[package]]
name = "find-msvc-tools"
-version = "0.1.2"
+version = "0.1.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "1ced73b1dacfc750a6db6c0a0c3a3853c8b41997e2e2c563dc90804ae6867959"
+checksum = "52051878f80a721bb68ebfbc930e07b65ba72f2da88968ea5c06fd6ca3d3a127"
[[package]]
name = "fnv"
@@ -615,9 +585,9 @@ dependencies = [
[[package]]
name = "generic-array"
-version = "0.14.7"
+version = "0.14.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a"
+checksum = "4bb6743198531e02858aeaea5398fcc883e71851fcbcb5a2f773e2fb6cb1edf2"
dependencies = [
"typenum",
"version_check",
@@ -625,25 +595,19 @@ dependencies = [
[[package]]
name = "getrandom"
-version = "0.3.3"
+version = "0.3.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "26145e563e54f2cadc477553f1ec5ee650b00862f0a58bcd12cbdc5f0ea2d2f4"
+checksum = "899def5c37c4fd7b2664648c28120ecec138e4d395b459e5ca34f9cce2dd77fd"
dependencies = [
"cfg-if",
"js-sys",
"libc",
"r-efi",
- "wasi 0.14.7+wasi-0.2.4",
+ "wasip2",
"wasm-bindgen",
]
[[package]]
-name = "gimli"
-version = "0.32.3"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "e629b9b98ef3dd8afe6ca2bd0f89306cec16d43d907889945bc5d6687f2f13c7"
-
-[[package]]
name = "gloo-net"
version = "0.6.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -986,9 +950,9 @@ dependencies = [
[[package]]
name = "indexmap"
-version = "2.11.4"
+version = "2.12.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "4b0f83760fb341a774ed326568e19f5a863af4a952def8c39f9ab92fd95b88e5"
+checksum = "6717a8d2a5a929a1a2eb43a12812498ed141a0bcfb7e8f7844fbdbe4303bba9f"
dependencies = [
"equivalent",
"hashbrown 0.16.0",
@@ -1003,17 +967,6 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "71dd52191aae121e8611f1e8dc3e324dd0dd1dee1e6dd91d10ee07a3cfb4d9d8"
[[package]]
-name = "io-uring"
-version = "0.7.10"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "046fa2d4d00aea763528b4950358d0ead425372445dc8ff86312b3c69ff7727b"
-dependencies = [
- "bitflags",
- "cfg-if",
- "libc",
-]
-
-[[package]]
name = "ipnet"
version = "2.11.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -1056,9 +1009,9 @@ dependencies = [
[[package]]
name = "leptos"
-version = "0.8.9"
+version = "0.8.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "52efe8eff3278b12f7897a15bdf067bbbb02212773e379d6fc121592752eb718"
+checksum = "de48e23f7d411ab2f048e2b8f34839c81f6db88489377b045c1099e7e828149e"
dependencies = [
"any_spawner",
"cfg-if",
@@ -1084,7 +1037,7 @@ dependencies = [
"server_fn",
"slotmap",
"tachys",
- "thiserror 2.0.16",
+ "thiserror 2.0.17",
"throw_error",
"typed-builder",
"typed-builder-macro",
@@ -1103,15 +1056,15 @@ dependencies = [
"config",
"regex",
"serde",
- "thiserror 2.0.16",
+ "thiserror 2.0.17",
"typed-builder",
]
[[package]]
name = "leptos_dom"
-version = "0.8.6"
+version = "0.8.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "4e920c8b2fd202b25786b0c72a00c745a6962fa923e600df6f3ec352d844be91"
+checksum = "78f4330c88694c5575e0bfe4eecf81b045d14e76a4f8b00d5fd2a63f8779f895"
dependencies = [
"js-sys",
"or_poisoned",
@@ -1130,7 +1083,7 @@ checksum = "0d61ec3e1ff8aaee8c5151688550c0363f85bc37845450764c31ff7584a33f38"
dependencies = [
"anyhow",
"camino",
- "indexmap 2.11.4",
+ "indexmap 2.12.0",
"parking_lot",
"proc-macro2",
"quote",
@@ -1152,9 +1105,9 @@ dependencies = [
[[package]]
name = "leptos_macro"
-version = "0.8.8"
+version = "0.8.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "a84c7e53895c786f1128e91c36a708435e301f487338d19f2f6b5b67bb39ece2"
+checksum = "bb2ef164285bdd94acb91a57a47f26c4ee3f37fc351e67a67642efb74bc3e198"
dependencies = [
"attribute-derive",
"cfg-if",
@@ -1180,7 +1133,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2d489e38d3f541e9e43ecc2e3a815527840345a2afca629b3e23fcc1dd254578"
dependencies = [
"futures",
- "indexmap 2.11.4",
+ "indexmap 2.12.0",
"leptos",
"or_poisoned",
"send_wrapper",
@@ -1190,9 +1143,9 @@ dependencies = [
[[package]]
name = "leptos_router"
-version = "0.8.7"
+version = "0.8.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "6a35447cd0ffcd6fcee0c4e424186c3e967507f36d9d4aaff4ab9976e5faab68"
+checksum = "cfd675546710e67855983437202e09b137ea60588dbb86e7fc0671f618577b73"
dependencies = [
"any_spawner",
"either_of",
@@ -1206,7 +1159,7 @@ dependencies = [
"rustc_version",
"send_wrapper",
"tachys",
- "thiserror 2.0.16",
+ "thiserror 2.0.17",
"url",
"wasm-bindgen",
"web-sys",
@@ -1246,9 +1199,9 @@ dependencies = [
[[package]]
name = "libc"
-version = "0.2.176"
+version = "0.2.177"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "58f929b4d672ea937a23a1ab494143d968337a5f47e56d0815df1e0890ddf174"
+checksum = "2874a2af47a2325c2001a6e6fad9b16a53b802102b528163885171cf92b15976"
[[package]]
name = "linear-map"
@@ -1264,11 +1217,10 @@ checksum = "241eaef5fd12c88705a01fc1066c48c4b36e0dd4377dcdc7ec3942cea7a69956"
[[package]]
name = "lock_api"
-version = "0.4.13"
+version = "0.4.14"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "96936507f153605bddfcda068dd804796c84324ed2510809e5b2a624c81da765"
+checksum = "224399e74b87b5f3557511d98dff8b14089b3dadafcab6bb93eab67d3aace965"
dependencies = [
- "autocfg",
"scopeguard",
]
@@ -1334,23 +1286,14 @@ dependencies = [
]
[[package]]
-name = "miniz_oxide"
-version = "0.8.9"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "1fa76a2c86f704bdb222d66965fb3d63269ce38518b83cb0575fca855ebb6316"
-dependencies = [
- "adler2",
-]
-
-[[package]]
name = "mio"
-version = "1.0.4"
+version = "1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "78bed444cc8a2160f01cbcf811ef18cac863ad68ae8ca62092e8db51d51c761c"
+checksum = "69d83b0086dc8ecf3ce9ae2874b2d1290252e2a30720bea58a5c6639b0092873"
dependencies = [
"libc",
- "wasi 0.11.1+wasi-snapshot-preview1",
- "windows-sys 0.59.0",
+ "wasi",
+ "windows-sys 0.61.2",
]
[[package]]
@@ -1385,22 +1328,13 @@ dependencies = [
]
[[package]]
-name = "object"
-version = "0.37.3"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "ff76201f031d8863c38aa7f905eca4f53abbfa15f609db4277d44cd8938f33fe"
-dependencies = [
- "memchr",
-]
-
-[[package]]
name = "oco_ref"
version = "0.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ed0423ff9973dea4d6bd075934fdda86ebb8c05bdf9d6b0507067d4a1226371d"
dependencies = [
"serde",
- "thiserror 2.0.16",
+ "thiserror 2.0.17",
]
[[package]]
@@ -1423,9 +1357,9 @@ checksum = "f38d5652c16fde515bb1ecef450ab0f6a219d619a7274976324d5e377f7dceba"
[[package]]
name = "parking_lot"
-version = "0.12.4"
+version = "0.12.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "70d58bf43669b5795d1576d0641cfb6fbb2057bf629506267a92807158584a13"
+checksum = "93857453250e3077bd71ff98b6a65ea6621a19bb0f559a85248955ac12c45a1a"
dependencies = [
"lock_api",
"parking_lot_core",
@@ -1433,15 +1367,15 @@ dependencies = [
[[package]]
name = "parking_lot_core"
-version = "0.9.11"
+version = "0.9.12"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "bc838d2a56b5b1a6c25f55575dfc605fabb63bb2365f6c2353ef9159aa69e4a5"
+checksum = "2621685985a2ebf1c516881c026032ac7deafcda1a2c9b7850dc81e3dfcb64c1"
dependencies = [
"cfg-if",
"libc",
"redox_syscall",
"smallvec",
- "windows-targets",
+ "windows-link",
]
[[package]]
@@ -1554,9 +1488,9 @@ dependencies = [
[[package]]
name = "proc-macro2"
-version = "1.0.101"
+version = "1.0.102"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "89ae43fd86e4158d6db51ad8e2b80f313af9cc74f5c0e03ccb87de09998732de"
+checksum = "8e0f6df8eaa422d97d72edcd152e1451618fed47fabbdbd5a8864167b1d4aff7"
dependencies = [
"unicode-ident",
]
@@ -1613,16 +1547,16 @@ checksum = "69cdb34c158ceb288df11e18b4bd39de994f6657d83847bdffdbd7f346754b0f"
[[package]]
name = "reactive_graph"
-version = "0.2.7"
+version = "0.2.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "37b9e227617c8e257900ea3c9aa536319b138bf961e950a258214edea3c2d591"
+checksum = "2e907f9b4c1ba69f3cd47c85f0d8cacafe681ff2b7cda91ff7d3c1a26432d249"
dependencies = [
"any_spawner",
"async-lock",
"futures",
"guardian",
"hydration_context",
- "indexmap 2.11.4",
+ "indexmap 2.12.0",
"or_poisoned",
"pin-project-lite",
"rustc-hash",
@@ -1630,7 +1564,7 @@ dependencies = [
"send_wrapper",
"serde",
"slotmap",
- "thiserror 2.0.16",
+ "thiserror 2.0.17",
"web-sys",
]
@@ -1666,27 +1600,27 @@ dependencies = [
[[package]]
name = "redox_syscall"
-version = "0.5.17"
+version = "0.5.18"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "5407465600fb0548f1442edf71dd20683c6ed326200ace4b1ef0763521bb3b77"
+checksum = "ed2bf2547551a7053d6fdfafda3f938979645c44812fbfcda098faae3f1a362d"
dependencies = [
"bitflags",
]
[[package]]
name = "ref-cast"
-version = "1.0.24"
+version = "1.0.25"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "4a0ae411dbe946a674d89546582cea4ba2bb8defac896622d6496f14c23ba5cf"
+checksum = "f354300ae66f76f1c85c5f84693f0ce81d747e2c3f21a45fef496d89c960bf7d"
dependencies = [
"ref-cast-impl",
]
[[package]]
name = "ref-cast-impl"
-version = "1.0.24"
+version = "1.0.25"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "1165225c21bff1f3bbce98f5a1f889949bc902d3575308cc7b0de30b4f6d27c7"
+checksum = "b7186006dcb21920990093f30e3dea63b7d6e977bf1256be20c3563a5db070da"
dependencies = [
"proc-macro2",
"quote",
@@ -1695,9 +1629,9 @@ dependencies = [
[[package]]
name = "regex"
-version = "1.11.3"
+version = "1.12.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "8b5288124840bee7b386bc413c487869b360b2b4ec421ea56425128692f2a82c"
+checksum = "843bc0191f75f3e22651ae5f1e72939ab2f72a4bc30fa80a066bd66edefc24d4"
dependencies = [
"aho-corasick",
"memchr",
@@ -1707,9 +1641,9 @@ dependencies = [
[[package]]
name = "regex-automata"
-version = "0.4.11"
+version = "0.4.13"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "833eb9ce86d40ef33cb1306d8accf7bc8ec2bfea4355cbdebb3df68b40925cad"
+checksum = "5276caf25ac86c8d810222b3dbb938e512c55c6831a10f3e6ed1c93b84041f1c"
dependencies = [
"aho-corasick",
"memchr",
@@ -1718,15 +1652,15 @@ dependencies = [
[[package]]
name = "regex-syntax"
-version = "0.8.6"
+version = "0.8.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "caf4aa5b0f434c91fe5c7f1ecb6a5ece2130b02ad2a590589dda5146df959001"
+checksum = "7a2d987857b319362043e95f5353c0535c1f58eec5336fdfcf626430af7def58"
[[package]]
name = "reqwest"
-version = "0.12.23"
+version = "0.12.24"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "d429f34c8092b2d42c7c93cec323bb4adeb7c67698f70839adec842ec10c7ceb"
+checksum = "9d0946410b9f7b082a427e4ef5c8ff541a88b357bc6c637c40db3a68ac70a36f"
dependencies = [
"base64",
"bytes",
@@ -1813,16 +1747,10 @@ dependencies = [
"quote",
"syn",
"syn_derive",
- "thiserror 2.0.16",
+ "thiserror 2.0.17",
]
[[package]]
-name = "rustc-demangle"
-version = "0.1.26"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "56f7d92ca342cea22a06f2121d944b4fd82af56988c270852495420f961d4ace"
-
-[[package]]
name = "rustc-hash"
version = "2.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -1905,9 +1833,9 @@ dependencies = [
[[package]]
name = "serde"
-version = "1.0.227"
+version = "1.0.228"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "80ece43fc6fbed4eb5392ab50c07334d3e577cbf40997ee896fe7af40bba4245"
+checksum = "9a8e94ea7f378bd32cbbd37198a4a91436180c5bb472411e48b5ec2e2124ae9e"
dependencies = [
"serde_core",
"serde_derive",
@@ -1915,18 +1843,18 @@ dependencies = [
[[package]]
name = "serde_core"
-version = "1.0.227"
+version = "1.0.228"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "7a576275b607a2c86ea29e410193df32bc680303c82f31e275bbfcafe8b33be5"
+checksum = "41d385c7d4ca58e59fc732af25c3983b67ac852c1a25000afe1175de458b67ad"
dependencies = [
"serde_derive",
]
[[package]]
name = "serde_derive"
-version = "1.0.227"
+version = "1.0.228"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "51e694923b8824cf0e9b382adf0f60d4e05f348f357b38833a3fa5ed7c2ede04"
+checksum = "d540f220d3187173da220f885ab66608367b6574e925011a9353e4badda91d79"
dependencies = [
"proc-macro2",
"quote",
@@ -1954,7 +1882,7 @@ checksum = "f3faaf9e727533a19351a43cc5a8de957372163c7d35cc48c90b75cdda13c352"
dependencies = [
"percent-encoding",
"serde",
- "thiserror 2.0.16",
+ "thiserror 2.0.17",
]
[[package]]
@@ -1970,9 +1898,9 @@ dependencies = [
[[package]]
name = "serde_spanned"
-version = "1.0.2"
+version = "1.0.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "5417783452c2be558477e104686f7de5dae53dba813c28435e0e70f82d9b04ee"
+checksum = "e24345aa0fe688594e73770a5f6d1b216508b4f93484c0026d521acd30134392"
dependencies = [
"serde_core",
]
@@ -1991,19 +1919,18 @@ dependencies = [
[[package]]
name = "serde_with"
-version = "3.14.1"
+version = "3.15.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "c522100790450cf78eeac1507263d0a350d4d5b30df0c8e1fe051a10c22b376e"
+checksum = "aa66c845eee442168b2c8134fec70ac50dc20e760769c8ba0ad1319ca1959b04"
dependencies = [
"base64",
"chrono",
"hex",
"indexmap 1.9.3",
- "indexmap 2.11.4",
+ "indexmap 2.12.0",
"schemars 0.9.0",
"schemars 1.0.4",
- "serde",
- "serde_derive",
+ "serde_core",
"serde_json",
"serde_with_macros",
"time",
@@ -2011,9 +1938,9 @@ dependencies = [
[[package]]
name = "serde_with_macros"
-version = "3.14.1"
+version = "3.15.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "327ada00f7d64abaac1e55a6911e90cf665aa051b9a561c7006c157f4633135e"
+checksum = "b91a903660542fced4e99881aa481bdbaec1634568ee02e0b8bd57c64cb38955"
dependencies = [
"darling",
"proc-macro2",
@@ -2044,7 +1971,7 @@ dependencies = [
"serde_json",
"serde_qs",
"server_fn_macro_default",
- "thiserror 2.0.16",
+ "thiserror 2.0.17",
"throw_error",
"url",
"wasm-bindgen",
@@ -2119,19 +2046,19 @@ checksum = "67b1b7a3b5fe4f1376887184045fcf45c69e92af734b7aaddc05fb777b6fbd03"
[[package]]
name = "socket2"
-version = "0.6.0"
+version = "0.6.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "233504af464074f9d066d7b5416c5f9b894a5862a6506e306f7b816cdd6f1807"
+checksum = "17129e116933cf371d018bb80ae557e889637989d8638274fb25622827b03881"
dependencies = [
"libc",
- "windows-sys 0.59.0",
+ "windows-sys 0.60.2",
]
[[package]]
name = "stable_deref_trait"
-version = "1.2.0"
+version = "1.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "a8f112729512f8e442d81f95a8a7ddf2b7c6b8a1a6f509a95864142b30cab2d3"
+checksum = "6ce2be8dc25455e1f91df71bfa12ad37d7af1092ae736f3a6cd0e37bc7810596"
[[package]]
name = "strsim"
@@ -2141,9 +2068,9 @@ checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f"
[[package]]
name = "syn"
-version = "2.0.106"
+version = "2.0.108"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "ede7c438028d4436d71104916910f5bb611972c5cfd7f89b8300a8186e6fada6"
+checksum = "da58917d35242480a05c2897064da0a80589a2a0476c9a3f2fdc83b53502e917"
dependencies = [
"proc-macro2",
"quote",
@@ -2184,9 +2111,9 @@ dependencies = [
[[package]]
name = "tachys"
-version = "0.2.8"
+version = "0.2.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "5db6367a7dfbdb427d421ada82425d804bee78ed5297a7c467c10cc993037923"
+checksum = "8b7c22c0a3e8ab7afc9ac34448031316f2e9416f4d489431c5d997a47075fa95"
dependencies = [
"any_spawner",
"async-trait",
@@ -2196,7 +2123,7 @@ dependencies = [
"erased",
"futures",
"html-escape",
- "indexmap 2.11.4",
+ "indexmap 2.12.0",
"itertools",
"js-sys",
"linear-map",
@@ -2227,11 +2154,11 @@ dependencies = [
[[package]]
name = "thiserror"
-version = "2.0.16"
+version = "2.0.17"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "3467d614147380f2e4e374161426ff399c91084acd2363eaf549172b3d5e60c0"
+checksum = "f63587ca0f12b72a0600bcba1d40081f830876000bb46dd2337a3051618f4fc8"
dependencies = [
- "thiserror-impl 2.0.16",
+ "thiserror-impl 2.0.17",
]
[[package]]
@@ -2247,9 +2174,9 @@ dependencies = [
[[package]]
name = "thiserror-impl"
-version = "2.0.16"
+version = "2.0.17"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "6c5e1be1c48b9172ee610da68fd9cd2770e7a4056cb3fc98710ee6906f0c7960"
+checksum = "3ff15c8ecd7de3849db632e14d18d2571fa09dfc5ed93479bc4485c7a517c913"
dependencies = [
"proc-macro2",
"quote",
@@ -2308,25 +2235,22 @@ dependencies = [
[[package]]
name = "tokio"
-version = "1.47.1"
+version = "1.48.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "89e49afdadebb872d3145a5638b59eb0691ea23e46ca484037cfab3b76b95038"
+checksum = "ff360e02eab121e0bc37a2d3b4d4dc622e6eda3a8e5253d5435ecf5bd4c68408"
dependencies = [
- "backtrace",
- "io-uring",
"libc",
"mio",
"pin-project-lite",
- "slab",
"socket2",
- "windows-sys 0.59.0",
+ "windows-sys 0.61.2",
]
[[package]]
name = "toml"
-version = "0.9.7"
+version = "0.9.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "00e5e5d9bf2475ac9d4f0d9edab68cc573dc2fd644b0dba36b0c30a92dd9eaa0"
+checksum = "f0dc8b1fb61449e27716ec0e1bdf0f6b8f3e8f6b05391e8497b8b6d7804ea6d8"
dependencies = [
"serde_core",
"serde_spanned",
@@ -2337,18 +2261,18 @@ dependencies = [
[[package]]
name = "toml_datetime"
-version = "0.7.2"
+version = "0.7.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "32f1085dec27c2b6632b04c80b3bb1b4300d6495d1e129693bdda7d91e72eec1"
+checksum = "f2cdb639ebbc97961c51720f858597f7f24c4fc295327923af55b74c3c724533"
dependencies = [
"serde_core",
]
[[package]]
name = "toml_parser"
-version = "1.0.3"
+version = "1.0.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "4cf893c33be71572e0e9aa6dd15e6677937abd686b066eac3f8cd3531688a627"
+checksum = "c0cbe268d35bdb4bb5a56a2de88d0ad0eb70af5384a99d648cd4b3d04039800e"
dependencies = [
"winnow",
]
@@ -2445,9 +2369,9 @@ dependencies = [
[[package]]
name = "typenum"
-version = "1.18.0"
+version = "1.19.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "1dccffe3ce07af9386bfd29e80c0ab1a8205a2fc34e4bcd40364df902cfa8f3f"
+checksum = "562d481066bde0658276a35467c4af00bdc6ee726305698a55b86e61d7ad82bb"
[[package]]
name = "unicase"
@@ -2457,9 +2381,9 @@ checksum = "75b844d17643ee918803943289730bec8aac480150456169e647ed0b576ba539"
[[package]]
name = "unicode-ident"
-version = "1.0.19"
+version = "1.0.20"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "f63a545481291138910575129486daeaf8ac54aee4387fe7906919f7830c7d9d"
+checksum = "462eeb75aeb73aea900253ce739c8e18a67423fadf006037cd3ff27e82748a06"
[[package]]
name = "unicode-segmentation"
@@ -2541,15 +2465,6 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ccf3ec651a847eb01de73ccad15eb7d99f80485de043efb2f370cd654f4ea44b"
[[package]]
-name = "wasi"
-version = "0.14.7+wasi-0.2.4"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "883478de20367e224c0090af9cf5f9fa85bed63a95c1abf3afc5c083ebc06e8c"
-dependencies = [
- "wasip2",
-]
-
-[[package]]
name = "wasip2"
version = "1.0.1+wasi-0.2.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -2708,14 +2623,14 @@ version = "0.1.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c2a7b1c03c876122aa43f3020e6c3c3ee5c05081c9a00739faf7503aeba10d22"
dependencies = [
- "windows-sys 0.61.1",
+ "windows-sys 0.61.2",
]
[[package]]
name = "windows-core"
-version = "0.62.1"
+version = "0.62.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "6844ee5416b285084d3d3fffd743b925a6c9385455f64f6d4fa3031c4c2749a9"
+checksum = "b8e83a14d34d0623b51dce9581199302a221863196a1dde71a7663a4c2be9deb"
dependencies = [
"windows-implement",
"windows-interface",
@@ -2726,9 +2641,9 @@ dependencies = [
[[package]]
name = "windows-implement"
-version = "0.60.1"
+version = "0.60.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "edb307e42a74fb6de9bf3a02d9712678b22399c87e6fa869d6dfcd8c1b7754e0"
+checksum = "053e2e040ab57b9dc951b72c264860db7eb3b0200ba345b4e4c3b14f67855ddf"
dependencies = [
"proc-macro2",
"quote",
@@ -2737,9 +2652,9 @@ dependencies = [
[[package]]
name = "windows-interface"
-version = "0.59.2"
+version = "0.59.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "c0abd1ddbc6964ac14db11c7213d6532ef34bd9aa042c2e5935f59d7908b46a5"
+checksum = "3f316c4a2570ba26bbec722032c4099d8c8bc095efccdc15688708623367e358"
dependencies = [
"proc-macro2",
"quote",
@@ -2748,52 +2663,53 @@ dependencies = [
[[package]]
name = "windows-link"
-version = "0.2.0"
+version = "0.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "45e46c0661abb7180e7b9c281db115305d49ca1709ab8242adf09666d2173c65"
+checksum = "f0805222e57f7521d6a62e36fa9163bc891acd422f971defe97d64e70d0a4fe5"
[[package]]
name = "windows-result"
-version = "0.4.0"
+version = "0.4.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "7084dcc306f89883455a206237404d3eaf961e5bd7e0f312f7c91f57eb44167f"
+checksum = "7781fa89eaf60850ac3d2da7af8e5242a5ea78d1a11c49bf2910bb5a73853eb5"
dependencies = [
"windows-link",
]
[[package]]
name = "windows-strings"
-version = "0.5.0"
+version = "0.5.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "7218c655a553b0bed4426cf54b20d7ba363ef543b52d515b3e48d7fd55318dda"
+checksum = "7837d08f69c77cf6b07689544538e017c1bfcf57e34b4c0ff58e6c2cd3b37091"
dependencies = [
"windows-link",
]
[[package]]
name = "windows-sys"
-version = "0.59.0"
+version = "0.60.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "1e38bc4d79ed67fd075bcc251a1c39b32a1776bbe92e5bef1f0bf1f8c531853b"
+checksum = "f2f500e4d28234f72040990ec9d39e3a6b950f9f22d3dba18416c35882612bcb"
dependencies = [
"windows-targets",
]
[[package]]
name = "windows-sys"
-version = "0.61.1"
+version = "0.61.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "6f109e41dd4a3c848907eb83d5a42ea98b3769495597450cf6d153507b166f0f"
+checksum = "ae137229bcbd6cdf0f7b80a31df61766145077ddf49416a728b02cb3921ff3fc"
dependencies = [
"windows-link",
]
[[package]]
name = "windows-targets"
-version = "0.52.6"
+version = "0.53.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973"
+checksum = "4945f9f551b88e0d65f3db0bc25c33b8acea4d9e41163edf90dcd0b19f9069f3"
dependencies = [
+ "windows-link",
"windows_aarch64_gnullvm",
"windows_aarch64_msvc",
"windows_i686_gnu",
@@ -2806,51 +2722,51 @@ dependencies = [
[[package]]
name = "windows_aarch64_gnullvm"
-version = "0.52.6"
+version = "0.53.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3"
+checksum = "a9d8416fa8b42f5c947f8482c43e7d89e73a173cead56d044f6a56104a6d1b53"
[[package]]
name = "windows_aarch64_msvc"
-version = "0.52.6"
+version = "0.53.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469"
+checksum = "b9d782e804c2f632e395708e99a94275910eb9100b2114651e04744e9b125006"
[[package]]
name = "windows_i686_gnu"
-version = "0.52.6"
+version = "0.53.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b"
+checksum = "960e6da069d81e09becb0ca57a65220ddff016ff2d6af6a223cf372a506593a3"
[[package]]
name = "windows_i686_gnullvm"
-version = "0.52.6"
+version = "0.53.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66"
+checksum = "fa7359d10048f68ab8b09fa71c3daccfb0e9b559aed648a8f95469c27057180c"
[[package]]
name = "windows_i686_msvc"
-version = "0.52.6"
+version = "0.53.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66"
+checksum = "1e7ac75179f18232fe9c285163565a57ef8d3c89254a30685b57d83a38d326c2"
[[package]]
name = "windows_x86_64_gnu"
-version = "0.52.6"
+version = "0.53.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78"
+checksum = "9c3842cdd74a865a8066ab39c8a7a473c0778a3f29370b5fd6b4b9aa7df4a499"
[[package]]
name = "windows_x86_64_gnullvm"
-version = "0.52.6"
+version = "0.53.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d"
+checksum = "0ffa179e2d07eee8ad8f57493436566c7cc30ac536a3379fdf008f47f6bb7ae1"
[[package]]
name = "windows_x86_64_msvc"
-version = "0.52.6"
+version = "0.53.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec"
+checksum = "d6bbff5f0aada427a1e5a6da5f1f98158182f26556f345ac9e04d36d0ebed650"
[[package]]
name = "winnow"
diff --git a/flake.lock b/flake.lock
index fb9e6c6..6f9f237 100644
--- a/flake.lock
+++ b/flake.lock
@@ -2,11 +2,11 @@
"nodes": {
"nixpkgs": {
"locked": {
- "lastModified": 1758736440,
- "narHash": "sha256-ssTkeaADdxhQl8y1ByejG5TlYakYElAIRhxnfYoQTRk=",
+ "lastModified": 1761164809,
+ "narHash": "sha256-3uM91Lx9WZomE6MMEBorJyEyBNiHWRIxza/GganDxew=",
"owner": "NixOS",
"repo": "nixpkgs",
- "rev": "2cd3cac16691a933e94276f0a810453f17775c28",
+ "rev": "3d2db9755e7815937fb7b8f089fad9b44bc416d8",
"type": "github"
},
"original": {
@@ -30,11 +30,11 @@
]
},
"locked": {
- "lastModified": 1758767687,
- "narHash": "sha256-znUulOqcL/Kkdr7CkyIi8Z1pTGXpi54Xg2FmlyJmv4A=",
+ "lastModified": 1761100675,
+ "narHash": "sha256-LX3TCDBeNpCWTDXtGyRASVcLmRPChSli34bgHnZ1DCw=",
"owner": "oxalica",
"repo": "rust-overlay",
- "rev": "b8bcc09d4f627f4e325408f6e7a85c3ac31f0eeb",
+ "rev": "72161c6c53f6e3f8dadaf54b2204a5094c6a16ae",
"type": "github"
},
"original": {
@@ -50,11 +50,11 @@
]
},
"locked": {
- "lastModified": 1758728421,
- "narHash": "sha256-ySNJ008muQAds2JemiyrWYbwbG+V7S5wg3ZVKGHSFu8=",
+ "lastModified": 1760945191,
+ "narHash": "sha256-ZRVs8UqikBa4Ki3X4KCnMBtBW0ux1DaT35tgsnB1jM4=",
"owner": "numtide",
"repo": "treefmt-nix",
- "rev": "5eda4ee8121f97b218f7cc73f5172098d458f1d1",
+ "rev": "f56b1934f5f8fcab8deb5d38d42fd692632b47c2",
"type": "github"
},
"original": {
diff --git a/rocie-macros/Cargo.toml b/rocie-macros/Cargo.toml
index 01e9e5c..afbb942 100644
--- a/rocie-macros/Cargo.toml
+++ b/rocie-macros/Cargo.toml
@@ -6,7 +6,7 @@ edition = "2024"
[dependencies]
proc-macro2 = "1.0.101"
quote = "1.0.41"
-syn = {version = "2.0.106", features = []}
+syn = { version = "2.0.106", features = ["full"] }
prettyplease = "0.2"
[lib]
diff --git a/rocie-macros/src/form/generate.rs b/rocie-macros/src/form/generate.rs
index 6673ba5..5642e6a 100644
--- a/rocie-macros/src/form/generate.rs
+++ b/rocie-macros/src/form/generate.rs
@@ -1,7 +1,7 @@
use proc_macro::TokenStream;
use proc_macro2::TokenStream as TokenStream2;
use quote::{format_ident, quote};
-use syn::{Ident, parse_macro_input};
+use syn::{Ident, Type, parse_macro_input};
use crate::form::{ParsedChild, ParsedInput};
@@ -10,6 +10,8 @@ pub fn form_impl(item: TokenStream) -> TokenStream {
let node_refs = input.children.iter().map(node_ref);
let signals = input.children.iter().map(signal);
+ let reactive_signals = input.children.iter().map(reactive_signal);
+ let auto_complete_signals = input.children.iter().map(auto_complete_signal);
let type_verifications = input.children.iter().map(type_verification);
@@ -20,6 +22,11 @@ pub fn form_impl(item: TokenStream) -> TokenStream {
let bounds = input.on_submit.inputs;
let on_submit_block = input.on_submit.block;
+ let on_submit_move = if input.on_submit.should_use_move {
+ quote! {move}
+ } else {
+ quote! {}
+ };
let input_htmls = input.children.iter().map(input_html);
@@ -50,11 +57,11 @@ pub fn form_impl(item: TokenStream) -> TokenStream {
#(
#node_refs
- )*
- #(
+
#signals
- )*
- #(
+ #reactive_signals
+ #auto_complete_signals
+
#type_verifications
)*
@@ -68,7 +75,7 @@ pub fn form_impl(item: TokenStream) -> TokenStream {
#fetch_values
)*
- let real_on_submit = |Inputs {#(#bounds),*}| #on_submit_block;
+ let real_on_submit = #on_submit_move |Inputs {#(#bounds),*}| #on_submit_block;
real_on_submit(
#output_struct_construction
)
@@ -76,16 +83,17 @@ pub fn form_impl(item: TokenStream) -> TokenStream {
view! {
- <form class="flex flex-col contents-start m-2 g-2" on:submit=on_submit>
+ <form class="relative flex flex-col contents-start m-2 g-2" on:submit=on_submit>
#(
#input_htmls
)*
- <div class="static">
+ <div class="sticky bottom-2 left-0 flex flex-row contents-start">
+ <div class="w-5/6" />
<input
type="submit"
value="Submit"
- class="absolute bottom-0 right-0 h-20 w-20 rounded-lg bg-green-300 m-2"
+ class="pr-2 pl-2 rounded-lg bg-green-300 m-2"
/>
</div>
</form>
@@ -112,15 +120,20 @@ fn node_ref_name(name: &Ident) -> Ident {
}
fn node_ref(child: &ParsedChild) -> TokenStream2 {
- let (name, ty) = match child {
- ParsedChild::Input { name, .. } => (name, quote! {Input}),
- ParsedChild::Select { name, .. } => (name, quote! {Select}),
- };
+ if let ParsedChild::Show { children, .. } = child {
+ children.iter().map(node_ref).collect()
+ } else {
+ let (name, ty) = match child {
+ ParsedChild::Input { name, .. } => (name, quote! {Input}),
+ ParsedChild::Select { name, .. } => (name, quote! {Select}),
+ ParsedChild::Show { .. } => unreachable!("Filtered out before"),
+ };
- let node_ref_name = node_ref_name(name);
+ let node_ref_name = node_ref_name(name);
- quote! {
- let #node_ref_name: NodeRef<#ty> = NodeRef::new();
+ quote! {
+ let #node_ref_name: NodeRef<#ty> = NodeRef::new();
+ }
}
}
@@ -137,42 +150,152 @@ fn signal(child: &ParsedChild) -> TokenStream2 {
}
}
ParsedChild::Select { .. } => quote! {},
+ ParsedChild::Show { children, .. } => children.iter().map(signal).collect(),
}
}
-fn type_verification(child: &ParsedChild) -> TokenStream2 {
+
+fn signal_auto_complete_names(name: &Ident) -> (Ident, Ident) {
+ (
+ format_ident!("{name}_auto_complete_get"),
+ format_ident!("{name}_auto_complete_set"),
+ )
+}
+fn auto_complete_signal(child: &ParsedChild) -> TokenStream2 {
match child {
- ParsedChild::Input { .. } => quote! {},
- ParsedChild::Select {
- rust_type, options, ..
+ ParsedChild::Input {
+ name,
+ reactive,
+ auto_complete,
+ ..
} => {
- let names: Vec<_> = options
- .0
- .iter()
- .enumerate()
- .map(|(index, _)| format_ident!("name_{index}"))
- .collect();
- let values: Vec<_> = options.0.iter().map(|option| option.1.clone()).collect();
+ if let Some(auto_complete) = auto_complete {
+ let (signal_auto_complete_get, _) = signal_auto_complete_names(name);
+
+ let reactive = reactive
+ .as_ref()
+ .expect("Must be some, if auto_complete is some");
+ quote! {
+ let #signal_auto_complete_get =
+ leptos::prelude::LocalResource::new(
+ move || #auto_complete(#reactive())
+ );
+ }
+ } else {
+ quote! {}
+ }
+ }
+ ParsedChild::Select { .. } => quote! {},
+ ParsedChild::Show { children, .. } => children.iter().map(auto_complete_signal).collect(),
+ }
+}
+
+fn signal_reactive_base_names(name: &Ident) -> (Ident, Ident) {
+ (
+ format_ident!("{name}_reactive_base_get"),
+ format_ident!("{name}_reactive_base_set"),
+ )
+}
+fn reactive_signal(child: &ParsedChild) -> TokenStream2 {
+ match child {
+ ParsedChild::Input {
+ name,
+ reactive,
+ rust_type,
+ ..
+ } => {
+ if let Some(reactive) = reactive {
+ let (signal_reactive_base_get, signal_reactive_base_set) =
+ signal_reactive_base_names(name);
+
+ quote! {
+ let (#signal_reactive_base_get, #signal_reactive_base_set) = signal(None);
+ #reactive = move || {
+ {
+ let output: Option<String> = #signal_reactive_base_get.get();
+
+ if let Some(output) = output {
+ let fin: Result<#rust_type, leptos::error::Error> = output
+ .parse()
+ .map_err(Into::<leptos::error::Error>::into);
+
+ match fin {
+ Ok(ok) => {
+ Some(ok)
+ },
+ Err(err) => {
+ // Reset the signal
+ None
+ }
+ }
+ } else {
+ None
+ }
+ }
+ };
+ }
+ } else {
+ quote! {}
+ }
+ }
+ ParsedChild::Select { .. } => quote! {},
+ ParsedChild::Show { children, .. } => children.iter().map(reactive_signal).collect(),
+ }
+}
+
+fn type_verification(child: &ParsedChild) -> TokenStream2 {
+ match child {
+ ParsedChild::Input { .. } => {
+ quote! {}
+ }
+ ParsedChild::Select { name, options, .. } => {
+ let name = format_ident!("_select_val_{name}");
quote! {
{
- #(
- let #names: #rust_type = #values;
- )*
+ let #name:
+ leptos::prelude::LocalResource<
+ Result<
+ // TODO: Use #rust_type instead of the second String <2025-10-21>
+ Vec<(String, String)>,
+ leptos::error::Error
+ >
+ > = #options;
}
}
}
+ ParsedChild::Show { when: _, children } => {
+ let base = children
+ .iter()
+ .map(type_verification)
+ .collect::<TokenStream2>();
+
+ quote! {
+ #base
+ }
+ }
+ }
+}
+
+fn get_names(input: &ParsedChild) -> Vec<&Ident> {
+ match input {
+ ParsedChild::Input { name, .. } => vec![name],
+ ParsedChild::Select { name, .. } => vec![name],
+ ParsedChild::Show { children, .. } => children.iter().flat_map(get_names).collect(),
}
}
fn output_struct_definition(children: &[ParsedChild]) -> TokenStream2 {
- let names = children.iter().map(|child| match child {
- ParsedChild::Input { name, .. } => name,
- ParsedChild::Select { name, .. } => name,
- });
- let rust_types = children.iter().map(|child| match child {
- ParsedChild::Input { rust_type, .. } => rust_type,
- ParsedChild::Select { rust_type, .. } => rust_type,
- });
+ fn get_rust_types(input: &ParsedChild) -> Vec<&Type> {
+ match input {
+ ParsedChild::Input { rust_type, .. } => vec![rust_type],
+ ParsedChild::Select { rust_type, .. } => vec![rust_type],
+ ParsedChild::Show { children, .. } => {
+ children.iter().flat_map(get_rust_types).collect()
+ }
+ }
+ }
+ let names = children.iter().flat_map(get_names);
+ let rust_types = children.iter().flat_map(get_rust_types);
quote! {
struct Inputs {
@@ -183,10 +306,7 @@ fn output_struct_definition(children: &[ParsedChild]) -> TokenStream2 {
}
}
fn output_struct_construction(children: &[ParsedChild]) -> TokenStream2 {
- let names = children.iter().map(|child| match child {
- ParsedChild::Input { name, .. } => name,
- ParsedChild::Select { name, .. } => name,
- });
+ let names = children.iter().flat_map(get_names);
quote! {
Inputs {
@@ -270,6 +390,9 @@ fn fetch_value(child: &ParsedChild) -> TokenStream2 {
};
}
}
+ ParsedChild::Show { children, .. } => {
+ children.iter().map(fetch_value).collect::<TokenStream2>()
+ }
}
}
@@ -280,13 +403,39 @@ fn input_html(child: &ParsedChild) -> TokenStream2 {
label,
rust_type,
html_type,
+ reactive,
+ auto_complete,
..
} => {
let node_ref_name = node_ref_name(name);
let (signal_name_get, _) = signal_names(name);
+ let reactive = {
+ if reactive.is_some() {
+ let (_, signal_reactive_set) = signal_reactive_base_names(name);
+
+ quote! {
+ reactive=Some(#signal_reactive_set)
+ }
+ } else {
+ quote! {}
+ }
+ };
+
+ let auto_complete = {
+ if auto_complete.is_some() {
+ let (signal_auto_complete_get, _) = signal_auto_complete_names(name);
+
+ quote! {
+ auto_complete=Some(#signal_auto_complete_get)
+ }
+ } else {
+ quote! {}
+ }
+ };
quote! {
- <InputPlaceholder input_type=#html_type label=#label node_ref=#node_ref_name />
+ <InputPlaceholder #reactive #auto_complete input_type=#html_type label=#label node_ref=#node_ref_name />
+
<Show
when=move || #signal_name_get.get().is_some()
fallback=|| ()
@@ -308,14 +457,22 @@ fn input_html(child: &ParsedChild) -> TokenStream2 {
..
} => {
let node_ref_name = node_ref_name(name);
- let options = options.0.iter().map(|(lit, expr)| {
- quote! {
- (#lit, #expr.to_string())
- }
- });
quote! {
- <SelectPlaceholder label=#label node_ref=#node_ref_name options=vec![#(#options),*] />
+ <SelectPlaceholder label=#label node_ref=#node_ref_name options=#options />
+ }
+ }
+ ParsedChild::Show { when, children } => {
+ let rendered_children = children.iter().map(input_html).collect::<Vec<_>>();
+
+ quote! {
+ <Show
+ when=#when
+ >
+ #(
+ #rendered_children
+ )*
+ </Show>
}
}
}
diff --git a/rocie-macros/src/form/mod.rs b/rocie-macros/src/form/mod.rs
index 2719826..7abd8f6 100644
--- a/rocie-macros/src/form/mod.rs
+++ b/rocie-macros/src/form/mod.rs
@@ -1,7 +1,7 @@
use syn::{Expr, Ident, LitStr, Type};
-mod parse;
mod generate;
+mod parse;
pub use generate::form_impl;
@@ -9,6 +9,7 @@ pub use generate::form_impl;
pub struct ParsedOnSubmit {
inputs: Vec<Ident>,
block: Expr,
+ pub(crate) should_use_move: bool,
}
#[derive(Debug)]
@@ -18,19 +19,19 @@ pub enum ParsedChild {
rust_type: Type,
html_type: LitStr,
label: LitStr,
+ reactive: Option<Ident>,
+ auto_complete: Option<Ident>,
},
Select {
name: Ident,
label: LitStr,
rust_type: Type,
- options: SelectOptions,
+ options: Expr, // Vec<(String, Uuid)>
},
+ Show { when: Expr, children: Vec<ParsedChild> },
}
#[derive(Debug)]
-pub struct SelectOptions(Vec<(LitStr, Expr)>);
-
-#[derive(Debug)]
pub struct ParsedInput {
on_submit: ParsedOnSubmit,
children: Vec<ParsedChild>,
diff --git a/rocie-macros/src/form/parse.rs b/rocie-macros/src/form/parse.rs
index ef2087b..b0ca58c 100644
--- a/rocie-macros/src/form/parse.rs
+++ b/rocie-macros/src/form/parse.rs
@@ -1,7 +1,10 @@
use quote::format_ident;
-use syn::{bracketed, parenthesized, parse::{Parse, ParseStream}, Expr, Ident, LitStr, Token, Type};
+use syn::{
+ Expr, Ident, LitStr, Token, Type,
+ parse::{Parse, ParseStream},
+};
-use crate::form::{ParsedChild, ParsedInput, ParsedOnSubmit, SelectOptions};
+use crate::form::{ParsedChild, ParsedInput, ParsedOnSubmit};
macro_rules! parse_key_value {
($input:expr, $name:ident as $ty:ty) => {{
@@ -19,27 +22,13 @@ macro_rules! parse_key_value {
value
}};
-}
-
-impl Parse for SelectOptions {
- fn parse(input: ParseStream) -> syn::Result<Self> {
- let content;
- bracketed!(content in input);
- let inner = content.parse_terminated(
- |arg| {
- let paren;
- parenthesized!(paren in arg);
- let lit = paren.parse::<LitStr>()?;
- paren.parse::<Token![,]>()?;
- let expr = paren.parse::<Expr>()?;
-
- Ok((lit, expr))
- },
- Token![,],
- )?;
-
- Ok(Self(inner.into_iter().collect()))
- }
+ (@option $input:expr, $name:ident as $ty:ty) => {{
+ if $input.peek(Ident) {
+ Some(parse_key_value!($input, $name as $ty))
+ } else {
+ None
+ }
+ }};
}
impl Parse for ParsedInput {
@@ -78,19 +67,33 @@ impl Parse for ParsedChild {
let rust_type = parse_key_value!(input, rust_type as Type);
let html_type = parse_key_value!(input, html_type as LitStr);
let label = parse_key_value!(input, label as LitStr);
+ let reactive = parse_key_value!(@option input, reactive as Ident);
+ let auto_complete = parse_key_value!(@option input, auto_complete as Ident);
+
+ if auto_complete.is_some() && reactive.is_none() {
+ panic!("Cannot provide an autocomplet, without registering an reactive signal")
+ }
+
+ input.parse::<Token![/]>()?;
+ input.parse::<Token![>]>()?;
ParsedChild::Input {
name,
rust_type,
html_type,
label,
+ reactive,
+ auto_complete,
}
}
variant if variant == format_ident!("Select") => {
let name = parse_key_value!(input, name as Ident);
let rust_type = parse_key_value!(input, rust_type as Type);
let label = parse_key_value!(input, label as LitStr);
- let options = parse_key_value!(input, options as SelectOptions);
+ let options = parse_key_value!(input, options as Expr);
+
+ input.parse::<Token![/]>()?;
+ input.parse::<Token![>]>()?;
ParsedChild::Select {
name,
@@ -99,11 +102,36 @@ impl Parse for ParsedChild {
rust_type,
}
}
- _ => panic!("Unkown form child variant: {variant}"),
- };
+ variant if variant == format_ident!("Show") => {
+ let when = parse_key_value!(input, when as Expr);
+
+ input.parse::<Token![>]>()?;
+
+ let children = {
+ let mut children = vec![];
+
+ while !(input.peek(Token![<]) && input.peek2(Token![/])) {
+ children.push(input.parse::<ParsedChild>()?);
+ }
+
+ {
+ input.parse::<Token![<]>()?;
+ input.parse::<Token![/]>()?;
+ let show_ident = input.parse::<Ident>()?;
+
+ if show_ident != format_ident!("Show") {
+ panic!("Expected key name to be 'Show', but found: '{show_ident}'",);
+ }
+ input.parse::<Token![>]>()?;
- input.parse::<Token![/]>()?;
- input.parse::<Token![>]>()?;
+ children
+ }
+ };
+
+ ParsedChild::Show { when, children }
+ }
+ _ => panic!("Unknown form child variant: {variant}"),
+ };
Ok(output)
}
@@ -113,6 +141,13 @@ impl Parse for ParsedOnSubmit {
fn parse(input: ParseStream) -> syn::Result<Self> {
let mut inputs = Vec::new();
+ let should_use_move = if input.peek(Token![move]) {
+ input.parse::<Token![move]>()?;
+ true
+ } else {
+ false
+ };
+
input.parse::<Token![|]>()?;
while !input.peek(Token![|]) {
inputs.push(input.parse::<Ident>()?);
@@ -127,6 +162,6 @@ impl Parse for ParsedOnSubmit {
input.parse::<Token![;]>()?;
- Ok(Self { inputs, block })
+ Ok(Self { inputs, block, should_use_move })
}
}
diff --git a/src/api/mod.rs b/src/api/mod.rs
index 8b9e77d..3879223 100644
--- a/src/api/mod.rs
+++ b/src/api/mod.rs
@@ -6,10 +6,16 @@ use reactive_stores::Store;
use rocie_client::{
apis::{
api_get_inventory_api::amount_by_id,
- api_get_product_api::{product_by_id, products},
+ api_get_product_api::{
+ product_by_id, product_by_name, product_suggestion_by_name, products,
+ },
api_get_unit_api::unit_by_id,
+ api_get_unit_property_api::unit_property_by_id,
+ api_set_barcode_api::buy_barcode, configuration::Configuration,
+ },
+ models::{
+ BarcodeId, Product, ProductAmount, ProductId, Unit, UnitId, UnitProperty, UnitPropertyId,
},
- models::{Product, ProductAmount, ProductId, Unit, UnitId},
};
use crate::{ConfigState, ConfigStateStoreFields};
@@ -26,12 +32,36 @@ pub(crate) async fn get_product_by_id(product_id: ProductId) -> Result<Product,
.await
.map_err(Into::<Error>::into)
}
+pub(crate) async fn get_product_by_name(
+ name: String,
+) -> Result<
+ Product,
+ rocie_client::apis::Error<rocie_client::apis::api_get_product_api::ProductByNameError>,
+> {
+ let config = expect_context::<Store<ConfigState>>();
+ product_by_name(&config.config().read(), &name).await
+}
+pub(crate) async fn get_products_by_part_name(part_name: String) -> Result<Vec<Product>, Error> {
+ let config = expect_context::<Store<ConfigState>>();
+ product_suggestion_by_name(&config.config().read(), &part_name)
+ .await
+ .map_err(Into::<Error>::into)
+}
pub(crate) async fn get_unit_by_id(unit_id: UnitId) -> Result<Unit, Error> {
let config = expect_context::<Store<ConfigState>>();
unit_by_id(&config.config().read(), unit_id)
.await
.map_err(Into::<Error>::into)
}
+pub(crate) async fn get_unit_property_by_id(
+ unit_id: UnitPropertyId,
+) -> Result<UnitProperty, Error> {
+ let config = expect_context::<Store<ConfigState>>();
+ unit_property_by_id(&config.config().read(), unit_id)
+ .await
+ .map_err(Into::<Error>::into)
+}
+
pub(crate) async fn get_full_product_by_id(
id: ProductId,
) -> Result<(Product, ProductAmount, Unit), Error> {
@@ -41,6 +71,14 @@ pub(crate) async fn get_full_product_by_id(
Ok::<_, Error>((product, amount, unit))
}
+pub(crate) async fn get_product_unit_by_id(
+ id: ProductId,
+) -> Result<(Product, UnitProperty), Error> {
+ let product = get_product_by_id(id).await?;
+ let unit = get_unit_property_by_id(product.unit_property).await?;
+
+ Ok::<_, Error>((product, unit))
+}
pub(crate) async fn get_products() -> Result<Vec<Product>, Error> {
let config = expect_context::<Store<ConfigState>>();
@@ -48,3 +86,17 @@ pub(crate) async fn get_products() -> Result<Vec<Product>, Error> {
.await
.map_err(Into::<Error>::into)
}
+
+pub(crate) async fn buy_barcode_wrapper(
+ config: &Configuration,
+ barcode_number: u32,
+) -> Result<(), Error> {
+ buy_barcode(
+ config,
+ BarcodeId {
+ value: barcode_number,
+ },
+ )
+ .await
+ .map_err(Into::<Error>::into)
+}
diff --git a/src/components/async_fetch.rs b/src/components/async_fetch.rs
index 7105c6f..f24e3a5 100644
--- a/src/components/async_fetch.rs
+++ b/src/components/async_fetch.rs
@@ -1,5 +1,23 @@
+macro_rules! AsyncResource {
+ (
+ (
+ $(
+ $input_name:ident : $input_type:ty = $input:expr
+ ),*
+ ) -> $output:ty $fetcher:block
+ ) => {{
+ async fn fetcher($($input_name : $input_type),*) -> $output $fetcher
+
+ leptos::prelude::LocalResource::new(move || fetcher($($input),*))
+ }}
+}
+pub(crate) use AsyncResource;
+
macro_rules! AsyncFetch {
- (fetcher = $fetcher:block producer = |$bound_variable:pat_param| $producer:block) => {{
+ (
+ fetcher = $fetcher:block
+ producer = |$bound_variable:pat_param| $producer:block
+ ) => {{
use leptos::{
prelude::{ElementChild, LocalResource, Suspend, Transition},
view,
@@ -18,33 +36,24 @@ macro_rules! AsyncFetch {
</Transition>
}
}};
+ (
+ @map_error_in_producer
+ from_resource = $resource:ident
+ producer = |$bound_variable:pat_param| $producer:block
+ ) => {{
+ use leptos::prelude::{ElementChild, Suspend, Transition};
+
+ leptos::view! {
+ <Transition fallback=|| {
+ view! { <p>"Loading..."</p> }
+ }>
+ {move || Suspend::new(async move {
+ $resource
+ .await
+ .map(|$bound_variable| $producer)
+ })}
+ </Transition>
+ }
+ }};
}
pub(crate) use AsyncFetch;
-
-// #[component]
-// pub fn AsyncFetch<P, V, T, Fut>(
-// fetcher: impl Fn() -> Fut + 'static + Send + Sync,
-// producer: P,
-// ) -> impl IntoView
-// where
-// V: IntoView + 'static,
-// P: Fn(T) -> V + 'static + Send + Sync,
-// Fut: Future<Output = Result<T, Error>> + 'static,
-// T: 'static,
-// LocalResource<Result<T, Error>>: IntoFuture<Output = Result<T, Error>> + Send,
-// {
-// view! {
-// <Transition fallback=|| {
-// view! { <p>"Loading..."</p> }
-// }>
-// { || Suspend::new(async {
-// let value_resource = LocalResource::new( || fetcher());
-// value_resource
-// .await
-// .map(|value| {
-// producer(value)
-// })
-// })}
-// </Transition>
-// }
-// }
diff --git a/src/components/banner.rs b/src/components/banner.rs
new file mode 100644
index 0000000..acaaf62
--- /dev/null
+++ b/src/components/banner.rs
@@ -0,0 +1,17 @@
+use leptos::{
+ IntoView, component,
+ prelude::{ClassAttribute, ElementChild},
+ view,
+};
+
+#[component]
+pub fn Banner<T>(mut text: T) -> impl IntoView
+where
+ T: FnMut() -> String + Send + 'static,
+{
+ view! {
+ <p class="text-white rounded-lg m-2 p-2 bg-red-600">
+ {move || text()}
+ </p>
+ }
+}
diff --git a/src/components/buy.rs b/src/components/buy.rs
index 6d9402e..cb4cff4 100644
--- a/src/components/buy.rs
+++ b/src/components/buy.rs
@@ -1,41 +1,193 @@
-use leptos::{IntoView, component, view};
+use leptos::{
+ IntoView, component,
+ prelude::{Get, Read, Show, WriteSignal, expect_context, signal},
+ task::spawn_local,
+ view,
+};
+use leptos_router::{NavigateOptions, hooks::use_navigate};
use log::info;
-use rocie_client::models::UnitId;
+use reactive_stores::Store;
+use rocie_client::{
+ apis::Error,
+ models::{Product, Unit},
+};
use uuid::Uuid;
-use crate::components::{form::Form, site_header::SiteHeader};
+use crate::{
+ ConfigState, ConfigStateStoreFields,
+ api::{
+ buy_barcode_wrapper, get_product_by_name, get_product_unit_by_id,
+ get_products_by_part_name, get_unit_by_id,
+ },
+ components::{async_fetch::AsyncResource, banner::Banner, form::Form, site_header::SiteHeader},
+};
#[component]
pub fn Buy() -> impl IntoView {
+ let (on_submit_errored, on_submit_errored_set) = signal(None);
+
+ view! {
+ <SiteHeader logo=icondata_io::IoPricetag back_location="/" name="Buy" />
+
+ <Show when=move || on_submit_errored.get().is_some()>
+ <Banner text=move || on_submit_errored.get().expect("Should be some") />
+ </Show>
+
+ {
+ Form! {
+ on_submit = |barcode_number, amount| {
+ let config = expect_context::<Store<ConfigState>>();
+ let config = config.config().read();
+
+ spawn_local(async move {
+ if let Err(err) = buy_barcode_wrapper(&config, barcode_number).await {
+ let error = format!("Error in form on-submit for barcode `{barcode_number}`: {err}");
+ on_submit_errored_set.set(Some(error));
+ } else {
+ on_submit_errored_set.set(None);
+
+ info!("Bought barcode {barcode_number} {amount} times");
+ }
+
+ });
+ };
+
+ <Input
+ name=barcode_number,
+ rust_type=u32,
+ html_type="number",
+ label="Barcode Number",
+ />
+
+ <Input
+ name=amount,
+ rust_type=u16,
+ html_type="number",
+ label="Amount"
+ />
+ }
+ }
+ }
+}
+
+#[component]
+pub fn AssociateBarcode() -> impl IntoView {
+ let product_name_signal;
+
+ let (show_units, show_units_set) = signal(false);
+
view! {
<SiteHeader logo=icondata_io::IoPricetag back_location="/" name="Buy" />
- {Form! {
- on_submit = |product_barcode, amount, unit_id| {
- info!("Got product barcode: {product_barcode} with amount: {amount}, {unit_id}");
- };
+ {
+ Form! {
+ on_submit = |product_name, amount, unit_id| {
+ spawn_local(async move {
+ let navigate = use_navigate();
+
+ info!("Got product barcode: {product_name} with amount: {amount}, and {unit_id}");
+
+ navigate("/", NavigateOptions::default());
+ });
+ };
+
+ <Input
+ name=product_name,
+ rust_type=String,
+ html_type="text",
+ label="Product Name",
+ reactive=product_name_signal
+ auto_complete=generate_suggest_products
+ />
+
+ <Show
+ when=move || show_units.get(),
+ >
+ <Select
+ name=unit_id,
+ rust_type=Uuid,
+ label="Unit",
+ options=AsyncResource! {
+ (
+ product_name: Option<String> = product_name_signal(),
+ show_units_set: WriteSignal<bool> = show_units_set
+ ) -> Result<Vec<(String, String)>, leptos::error::Error> {
+ let units = product_unit_fetcher(product_name).await?;
+
+ show_units_set.set(units.is_some());
+ if let Some(units) = units {
+ Ok(
+ units
+ .into_iter()
+ .map(|unit| (unit.full_name_singular, unit.id.to_string()))
+ .collect()
+ )
+ } else {
+ Ok(vec![])
+ }
+ }
+ },
+ />
+ </Show>
+
+ <Input
+ name=amount,
+ rust_type=u16,
+ html_type="number",
+ label="Amount"
+ />
+ }
+ }
+ }
+}
+
+async fn generate_suggest_products(
+ optional_product_name: Option<String>,
+) -> Result<Option<Vec<String>>, leptos::error::Error> {
+ if let Some(product_name) = optional_product_name
+ && !product_name.is_empty()
+ {
+ let products = get_products_by_part_name(product_name).await?;
+ Ok(Some(products.into_iter().map(|prod| prod.name).collect()))
+ } else {
+ Ok(None)
+ }
+}
+
+async fn product_unit_fetcher(
+ optinal_product_name: Option<String>,
+) -> Result<Option<Vec<Unit>>, leptos::error::Error> {
+ if let Some(product_name) = optinal_product_name
+ && !product_name.is_empty()
+ {
+ let value: Option<Product> = {
+ match get_product_by_name(product_name).await {
+ Ok(ok) => Ok::<_, leptos::error::Error>(Some(ok)),
+ Err(err) => match err {
+ Error::ResponseError(ref response_content) => {
+ match response_content.status.as_u16() {
+ 404 => Ok(None),
+ _ => Err(err.into()),
+ }
+ }
+ err => Err(err.into()),
+ },
+ }?
+ };
+
+ if let Some(value) = value {
+ let (_, unit_property) = get_product_unit_by_id(value.id).await?;
+
+ let mut units = Vec::with_capacity(unit_property.units.len());
+ for unit_id in unit_property.units {
+ units.push(get_unit_by_id(unit_id).await?);
+ }
- <Input
- name=product_barcode,
- 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,
- rust_type=u16,
- html_type="number",
- label="Amount"
- />
- }}
+ Ok(Some(units))
+ } else {
+ Ok(None)
+ }
+ } else {
+ Ok(None)
}
}
diff --git a/src/components/input_placeholder.rs b/src/components/input_placeholder.rs
index aeef838..99b3196 100644
--- a/src/components/input_placeholder.rs
+++ b/src/components/input_placeholder.rs
@@ -1,27 +1,40 @@
use leptos::{
IntoView, component,
+ error::Error,
html::Input,
- prelude::{ClassAttribute, ElementChild, GlobalAttributes, NodeRef, NodeRefAttribute},
+ prelude::{
+ ClassAttribute, CollectView, ElementChild, Get, GlobalAttributes, LocalResource, NodeRef,
+ NodeRefAttribute, OnAttribute, OnTargetAttribute, PropAttribute, Set, Show, WriteSignal,
+ signal,
+ },
view,
};
+use log::{error, info};
use crate::components::get_id;
-
#[component]
+#[expect(clippy::too_many_lines)]
pub fn InputPlaceholder(
input_type: &'static str,
label: &'static str,
node_ref: NodeRef<Input>,
#[prop(default = None)] initial_value: Option<String>,
+ #[prop(default = None)] reactive: Option<WriteSignal<Option<String>>>,
+ #[prop(default = None)] auto_complete: Option<
+ LocalResource<Result<Option<Vec<String>>, Error>>,
+ >,
) -> impl IntoView {
let id = get_id();
+ let (autocomplete_signal, autocomplete_set) = signal(String::new());
+
view! {
<div class="relative h-14">
<input
id=id.to_string()
type=input_type
+ autocomplete="off"
class="\
absolute \
bottom-0 \
@@ -32,7 +45,8 @@ pub fn InputPlaceholder(
border-gray-200 \
focus:outline-none \
h-10 \
- peer \
+ peer/input \
+ group/input \
placeholder-transparent \
rounded-t-lg \
text-gray-900 \
@@ -41,16 +55,23 @@ pub fn InputPlaceholder(
placeholder="sentinel value"
node_ref=node_ref
value=initial_value
+ on:input:target=move |ev| {
+ if let Some(signal) = reactive {
+ signal.set(Some(ev.target().value()));
+ autocomplete_set.set(ev.target().value());
+ }
+ }
+ prop:value=autocomplete_signal
/>
// TODO: Reference `var(--tw-border-2)` instead of the `2 px` <2025-10-01>
- <div class="
+ <div class="\
absolute \
bottom-0 \
h-[2px] \
w-full \
bg-gray-300 \
- peer-focus:bg-indigo-600 \
+ peer-focus/input:bg-indigo-600 \
" />
<label
@@ -62,18 +83,125 @@ pub fn InputPlaceholder(
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 \
+ peer-focus/input:bottom-10 \
+ peer-focus/input:left-0 \
+ peer-focus/input:text-gray-700 \
+ peer-focus/input:text-sm \
+ peer-placeholder-shown/input:text-base \
+ peer-placeholder-shown/input:text-gray-400 \
+ peer-placeholder-shown/input:bottom-2 \
+ peer-placeholder-shown/input:left-2 \
"
>
{label}
</label>
+
+ <Show
+ when=move || {
+ !autocomplete_signal.get().is_empty()
+ }
+ fallback=move || ()
+ >
+ <div class="\
+ absolute \
+ top-0 \
+ left-0 \
+ invisible \
+ peer-focus/input:visible \
+ in-focus:visible \
+ ">
+ <div class="\
+ flex \
+ flex-row \
+ g-0 \
+ ">
+ // TODO: Reference `var(--tw-border-8)` instead of the `8 px` <2025-10-11>
+ <div class="w-[8px] h-full" />
+ <div class="\
+ flex \
+ flex-col \
+ g-0 \
+ ">
+ <div class="h-14 w-full peer/div" />
+ <div class="\
+ bg-white \
+ shadow \
+ outline \
+ outline-black/5 \
+ rounded-lg \
+ z-50 \
+ p-2 \
+ visible \
+ ">
+ {move || {
+ auto_complete
+ .map(|auto_complete| {
+ provide_auto_completion(
+ auto_complete,
+ autocomplete_set,
+ reactive
+ )
+ })
+ }}
+ </div>
+ </div>
+ </div>
+ </div>
+ </Show>
</div>
}
}
+
+fn provide_auto_completion(
+ auto_complete: LocalResource<Result<Option<Vec<String>>, Error>>,
+ autocomplete_set: WriteSignal<String>,
+ reactive: Option<WriteSignal<Option<String>>>,
+) -> impl IntoView {
+ match auto_complete.get() {
+ Some(resource_result) => match resource_result {
+ Ok(resource_fetch) => resource_fetch.map(|_| {
+ view! {
+ <div class="flex flex-col g-1">
+ {move || {
+ auto_complete
+ .get()
+ .expect("Worked before")
+ .unwrap()
+ .unwrap()
+ .into_iter()
+ .map(|item| {
+ let item2 = item.clone();
+ view! {
+ <button
+ type="button"
+ on:click=move |_| {
+ autocomplete_set.set(item2.clone());
+ reactive
+ .expect(
+ "Should be set, \
+ when autocomplete is used")
+ .set(Some(item2.clone()));
+
+ info!("Set autocomplete to {item2}.");
+ }
+ >
+ {item}
+ </button>
+ }
+ })
+ .collect_view()
+ }}
+ </div>
+ }
+ }),
+ Err(err) => {
+ error!(
+ "Error while loading \
+ autocompletion: {err}"
+ );
+ None
+ }
+ },
+ None => None,
+ }
+}
diff --git a/src/components/mod.rs b/src/components/mod.rs
index 1ee37d5..ca2ac10 100644
--- a/src/components/mod.rs
+++ b/src/components/mod.rs
@@ -7,6 +7,7 @@ pub mod form;
pub mod icon_p;
pub mod input_placeholder;
pub mod select_placeholder;
+pub mod banner;
// Specific
pub mod buy;
diff --git a/src/components/product_overview.rs b/src/components/product_overview.rs
index d86c04d..777baef 100644
--- a/src/components/product_overview.rs
+++ b/src/components/product_overview.rs
@@ -19,8 +19,9 @@ pub fn ProductOverview() -> impl IntoView {
(view! { <IconP icon=icondata_io::IoStorefront text="Buy" /> }, "buy"),
]
>
- {AsyncFetch!(
- fetcher = {get_products()}
+ {
+ AsyncFetch! {
+ fetcher = {get_products()}
producer = |products| {
let products_num = products.len();
let plural_s = if products_num == 1 { "" } else { "s" };
@@ -37,7 +38,8 @@ pub fn ProductOverview() -> impl IntoView {
</p>
}
}
- )}
+ }
+ }
</Container>
}
}
diff --git a/src/components/select_placeholder.rs b/src/components/select_placeholder.rs
index 947931c..2e0f783 100644
--- a/src/components/select_placeholder.rs
+++ b/src/components/select_placeholder.rs
@@ -1,37 +1,24 @@
use leptos::{
- IntoView,
- attr::{AttributeValue, IntoAttributeValue},
- component,
+ IntoView, component,
+ error::Error,
html::Select,
prelude::{
ClassAttribute, CollectView, ElementChild, GlobalAttributes, NodeRef, NodeRefAttribute,
},
+ server::LocalResource,
view,
};
-use crate::components::get_id;
+use crate::components::{async_fetch::AsyncFetch, get_id};
#[component]
-pub fn SelectPlaceholder<T>(
+pub fn SelectPlaceholder(
label: &'static str,
node_ref: NodeRef<Select>,
- options: Vec<(&'static str, T)>,
-) -> impl IntoView
-where
- T: IntoAttributeValue,
- <T as IntoAttributeValue>::Output: Send + AttributeValue,
-{
+ options: LocalResource<Result<Vec<(String, String)>, Error>>,
+) -> impl IntoView {
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
@@ -54,7 +41,18 @@ where
"
node_ref=node_ref
>
- {options}
+ {move || AsyncFetch! {
+ @map_error_in_producer
+ from_resource = options
+ producer = |options| {
+ options
+ .into_iter()
+ .map(|(label, value)| {
+ view! { <option value=value>{label}</option> }
+ })
+ .collect_view()
+ }
+ }}
</select>
// TODO: Reference `var(--tw-border-2)` instead of the `2 px` <2025-10-01>