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:
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"
+ );
+ }
+}