app

Local-first trade for farms and co-ops
git clone https://radroots.dev/git/app.git
Log | Files | Refs | README | LICENSE

commit 95a59b3f228c94121d5bd242f738d32fd75051b3
parent 66bf69b8e7b8635d18e2309d80edb584a3dc74fb
Author: triesap <tyson@radroots.org>
Date:   Fri,  6 Feb 2026 16:18:17 +0000

ui: add form field primitives

- add form field, chip, and chips components
- add form css for labels, hints, and chip states
- export form ui helpers from ui-components
- import form styles into app css

Diffstat:
Mapp/app.css | 1+
Acrates/ui-components/assets/form.css | 29+++++++++++++++++++++++++++++
Acrates/ui-components/src/form.rs | 74++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Mcrates/ui-components/src/lib.rs | 2++
4 files changed, 106 insertions(+), 0 deletions(-)

diff --git a/app/app.css b/app/app.css @@ -10,6 +10,7 @@ @import "./stylesheets/styles-maplibre-gl.css"; @import "./stylesheets/styles-superellipse.css"; @import "../crates/ui-components/assets/list.css"; +@import "../crates/ui-components/assets/form.css"; @import "../crates/ui-components/assets/nav.css"; @import "../crates/ui-components/assets/nav_tabs.css"; diff --git a/crates/ui-components/assets/form.css b/crates/ui-components/assets/form.css @@ -0,0 +1,29 @@ +@layer components { + .form-field { + @apply flex flex-col gap-2; + } + + .form-field__label { + @apply text-[0.72rem] font-semibold uppercase tracking-[0.08em] text-ly1-gl-label/80; + } + + .form-field__control { + @apply flex flex-col gap-2; + } + + .form-field__hint { + @apply text-xs text-ly1-gl-label/70; + } + + .form-chips { + @apply flex flex-wrap gap-2; + } + + .form-chip { + @apply inline-flex items-center justify-center rounded-full border border-ly1-edge/60 bg-ly1 px-3 py-1 text-sm text-ly1-gl transition-colors; + } + + .form-chip[data-active="true"] { + @apply border-ly1-edge/80 bg-ly1-focus text-ly1-gl; + } +} diff --git a/crates/ui-components/src/form.rs b/crates/ui-components/src/form.rs @@ -0,0 +1,74 @@ +#![forbid(unsafe_code)] + +use leptos::ev::MouseEvent; +use leptos::prelude::*; + +use crate::RadrootsAppUiLabel; + +#[component] +pub fn RadrootsAppUiFormField( + label: String, + #[prop(optional)] hint: Option<String>, + #[prop(optional)] id: Option<String>, + #[prop(optional)] class: Option<String>, + children: Children, +) -> impl IntoView { + view! { + <section id=id class=class.unwrap_or_else(|| "form-field".to_string())> + <RadrootsAppUiLabel class="form-field__label".to_string()> + {label} + </RadrootsAppUiLabel> + <div class="form-field__control">{children()}</div> + {move || { + hint.clone() + .map(|value| view! { <p class="form-field__hint">{value}</p> }.into_any()) + .unwrap_or_else(|| view! { <></> }.into_any()) + }} + </section> + } +} + +#[component] +pub fn RadrootsAppUiChips( + #[prop(optional)] id: Option<String>, + #[prop(optional)] class: Option<String>, + children: Children, +) -> impl IntoView { + let class_value = match class { + Some(value) => format!("form-chips {value}"), + None => "form-chips".to_string(), + }; + view! { + <div id=id class=class_value> + {children()} + </div> + } +} + +#[component] +pub fn RadrootsAppUiChip( + label: String, + active: bool, + #[prop(optional)] class: Option<String>, + #[prop(optional)] on_click: Option<Callback<MouseEvent>>, +) -> impl IntoView { + let class_value = match class { + Some(value) => format!("form-chip {value}"), + None => "form-chip".to_string(), + }; + let on_click = move |ev: MouseEvent| { + if let Some(handler) = on_click { + handler.run(ev); + } + }; + view! { + <button + type="button" + class=class_value + attr:data-active=move || if active { "true" } else { "false" } + on:click=on_click + > + {label} + </button> + } +} diff --git a/crates/ui-components/src/lib.rs b/crates/ui-components/src/lib.rs @@ -9,6 +9,7 @@ mod list_types; mod separator; mod spinner; mod dialog; +mod form; mod sheet; mod scroll; mod nav_header; @@ -22,6 +23,7 @@ pub use button_layout::{ RadrootsAppUiButtonLayoutBottom, RadrootsAppUiButtonLayoutPair, }; +pub use form::{RadrootsAppUiChip, RadrootsAppUiChips, RadrootsAppUiFormField}; pub use icon::{ radroots_app_ui_icon_data, radroots_app_ui_icon_key_from_name,