app

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

commit 10d2e77800a620cb4c456acd033cd2cd3bfd5535
parent 7a4c7c40d6409d0b7e3b69ecbb07b58699da7788
Author: triesap <triesap@radroots.dev>
Date:   Wed, 21 Jan 2026 20:25:06 +0000

ui: add base button label separator components

- add headless button component with disabled state data
- add label component with optional for and styling props
- add separator component with orientation helpers
- add unit test for separator orientation values

Diffstat:
Mcrates/ui-components/Cargo.toml | 1+
Acrates/ui-components/src/button.rs | 25+++++++++++++++++++++++++
Acrates/ui-components/src/label.rs | 22++++++++++++++++++++++
Mcrates/ui-components/src/lib.rs | 12++++++++++++
Acrates/ui-components/src/separator.rs | 72++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
5 files changed, 132 insertions(+), 0 deletions(-)

diff --git a/crates/ui-components/Cargo.toml b/crates/ui-components/Cargo.toml @@ -12,3 +12,4 @@ crate-type = ["rlib"] [dependencies] radroots-app-ui-core = { path = "../ui-core" } radroots-app-ui-primitives = { path = "../ui-primitives" } +leptos = { workspace = true, features = ["csr"] } diff --git a/crates/ui-components/src/button.rs b/crates/ui-components/src/button.rs @@ -0,0 +1,25 @@ +use leptos::prelude::*; + +#[component] +pub fn RadrootsAppUiButton( + #[prop(optional)] disabled: bool, + #[prop(optional)] class: Option<String>, + #[prop(optional)] id: Option<String>, + #[prop(optional)] style: Option<String>, + children: Children, +) -> impl IntoView { + let data_disabled = if disabled { Some("".to_string()) } else { None }; + view! { + <button + type="button" + id=id + class=class + style=style + disabled=disabled + data-ui="button" + data-disabled=data_disabled + > + {children()} + </button> + } +} diff --git a/crates/ui-components/src/label.rs b/crates/ui-components/src/label.rs @@ -0,0 +1,22 @@ +use leptos::prelude::*; + +#[component] +pub fn RadrootsAppUiLabel( + #[prop(optional)] for_id: Option<String>, + #[prop(optional)] class: Option<String>, + #[prop(optional)] id: Option<String>, + #[prop(optional)] style: Option<String>, + children: Children, +) -> impl IntoView { + view! { + <label + id=id + class=class + style=style + for=for_id + data-ui="label" + > + {children()} + </label> + } +} diff --git a/crates/ui-components/src/lib.rs b/crates/ui-components/src/lib.rs @@ -1 +1,13 @@ #![forbid(unsafe_code)] + +mod button; +mod label; +mod separator; + +pub use button::RadrootsAppUiButton; +pub use label::RadrootsAppUiLabel; +pub use separator::{ + radroots_app_ui_separator_orientation_value, + RadrootsAppUiSeparator, + RadrootsAppUiSeparatorOrientation, +}; diff --git a/crates/ui-components/src/separator.rs b/crates/ui-components/src/separator.rs @@ -0,0 +1,72 @@ +use leptos::prelude::*; + +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub enum RadrootsAppUiSeparatorOrientation { + Horizontal, + Vertical, +} + +impl Default for RadrootsAppUiSeparatorOrientation { + fn default() -> Self { + RadrootsAppUiSeparatorOrientation::Horizontal + } +} + +pub fn radroots_app_ui_separator_orientation_value( + orientation: RadrootsAppUiSeparatorOrientation, +) -> &'static str { + match orientation { + RadrootsAppUiSeparatorOrientation::Horizontal => "horizontal", + RadrootsAppUiSeparatorOrientation::Vertical => "vertical", + } +} + +#[component] +pub fn RadrootsAppUiSeparator( + #[prop(optional)] orientation: RadrootsAppUiSeparatorOrientation, + #[prop(optional)] decorative: bool, + #[prop(optional)] class: Option<String>, + #[prop(optional)] id: Option<String>, + #[prop(optional)] style: Option<String>, +) -> impl IntoView { + let data_orientation = radroots_app_ui_separator_orientation_value(orientation); + let data_decorative = if decorative { Some("".to_string()) } else { None }; + let role = if decorative { "presentation" } else { "separator" }; + let aria_orientation = if decorative { + None + } else { + Some(data_orientation.to_string()) + }; + view! { + <div + id=id + class=class + style=style + role=role + data-ui="separator" + data-orientation=data_orientation + data-decorative=data_decorative + aria-orientation=aria_orientation + ></div> + } +} + +#[cfg(test)] +mod tests { + use super::{ + radroots_app_ui_separator_orientation_value, + RadrootsAppUiSeparatorOrientation, + }; + + #[test] + fn separator_orientation_values() { + assert_eq!( + radroots_app_ui_separator_orientation_value(RadrootsAppUiSeparatorOrientation::Horizontal), + "horizontal" + ); + assert_eq!( + radroots_app_ui_separator_orientation_value(RadrootsAppUiSeparatorOrientation::Vertical), + "vertical" + ); + } +}