app

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

commit 3a8ced323884c51477637b4a256ea26e1d0e7c0d
parent a5d74574ba56d110512f0b3dd72a64c29e53f966
Author: triesap <triesap@radroots.dev>
Date:   Thu, 22 Jan 2026 04:38:08 +0000

app: add lucide icon foundation

- add icondata lucide dependency to ui-components
- introduce RadrootsAppUiIconKey and key parsing helper
- render lucide svg via RadrootsAppUiIcon
- cover icon mapping with unit tests

Diffstat:
MCargo.lock | 26++++++++++++++++++++++++++
MCargo.toml | 1+
Mcrates/ui-components/Cargo.toml | 1+
Acrates/ui-components/src/icon.rs | 115+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Mcrates/ui-components/src/lib.rs | 7+++++++
5 files changed, 150 insertions(+), 0 deletions(-)

diff --git a/Cargo.lock b/Cargo.lock @@ -885,6 +885,31 @@ dependencies = [ ] [[package]] +name = "icondata" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "18db9eec46b6c870bf84281dd8065fb207b23a9c1c0cb367c49a919a61a38dec" +dependencies = [ + "icondata_core", + "icondata_lu", +] + +[[package]] +name = "icondata_core" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6c97be924215abd5e630d84e95a47c710138a6559b4c55039f4f33aa897fa859" + +[[package]] +name = "icondata_lu" +version = "0.0.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d552c45cc3ab1d1bf88cc0201004eb92418141e5454e9e0e46c4b4a4faf66248" +dependencies = [ + "icondata_core", +] + +[[package]] name = "icu_collections" version = "2.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -1714,6 +1739,7 @@ dependencies = [ name = "radroots-app-ui-components" version = "0.1.0" dependencies = [ + "icondata", "leptos", "radroots-app-ui-core", "radroots-app-ui-primitives", diff --git a/Cargo.toml b/Cargo.toml @@ -24,6 +24,7 @@ license = "AGPL-3.0" [workspace.dependencies] leptos = { version = "0.8.5", default-features = false } leptos_router = { version = "0.8.5", default-features = false } +icondata = { version = "0.4", default-features = false, features = ["lucide"] } wasm-bindgen = "=0.2.100" serde = { version = "1", features = ["derive"] } serde_json = "1" diff --git a/crates/ui-components/Cargo.toml b/crates/ui-components/Cargo.toml @@ -13,3 +13,4 @@ crate-type = ["rlib"] radroots-app-ui-core = { path = "../ui-core" } radroots-app-ui-primitives = { path = "../ui-primitives" } leptos = { workspace = true, features = ["csr"] } +icondata.workspace = true diff --git a/crates/ui-components/src/icon.rs b/crates/ui-components/src/icon.rs @@ -0,0 +1,115 @@ +#![forbid(unsafe_code)] + +use icondata::{Icon, LuChevronRight, LuChevronsUpDown, LuPlus}; +use leptos::prelude::*; + +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] +pub enum RadrootsAppUiIconKey { + CaretRight, + CaretUpDown, + Plus, +} + +impl RadrootsAppUiIconKey { + pub const fn as_str(self) -> &'static str { + match self { + RadrootsAppUiIconKey::CaretRight => "caret-right", + RadrootsAppUiIconKey::CaretUpDown => "caret-up-down", + RadrootsAppUiIconKey::Plus => "plus", + } + } +} + +pub fn radroots_app_ui_icon_key_from_name(name: &str) -> Option<RadrootsAppUiIconKey> { + match name { + "caret-right" | "chevron-right" => Some(RadrootsAppUiIconKey::CaretRight), + "caret-up-down" | "chevrons-up-down" => Some(RadrootsAppUiIconKey::CaretUpDown), + "plus" => Some(RadrootsAppUiIconKey::Plus), + _ => None, + } +} + +pub fn radroots_app_ui_icon_data(key: RadrootsAppUiIconKey) -> Icon { + match key { + RadrootsAppUiIconKey::CaretRight => LuChevronRight, + RadrootsAppUiIconKey::CaretUpDown => LuChevronsUpDown, + RadrootsAppUiIconKey::Plus => LuPlus, + } +} + +#[component] +pub fn RadrootsAppUiIcon( + key: RadrootsAppUiIconKey, + #[prop(optional)] class: Option<String>, + #[prop(optional)] size: Option<u32>, +) -> impl IntoView { + let icon = radroots_app_ui_icon_data(key); + let class_value = class.unwrap_or_default(); + let size_value = size.unwrap_or(20).to_string(); + let view_box = icon.view_box.unwrap_or("0 0 24 24"); + let stroke = icon.stroke.unwrap_or("currentColor"); + let fill = icon.fill.unwrap_or("none"); + let stroke_width = icon.stroke_width.unwrap_or("2"); + let stroke_linecap = icon.stroke_linecap.unwrap_or("round"); + let stroke_linejoin = icon.stroke_linejoin.unwrap_or("round"); + view! { + <svg + class=class_value + width=size_value.clone() + height=size_value + viewBox=view_box + fill=fill + stroke=stroke + stroke-width=stroke_width + stroke-linecap=stroke_linecap + stroke-linejoin=stroke_linejoin + xmlns="http://www.w3.org/2000/svg" + focusable="false" + aria-hidden="true" + attr:style=icon.style + attr:x=icon.x + attr:y=icon.y + inner_html=icon.data + /> + } +} + +#[cfg(test)] +mod tests { + use super::{ + radroots_app_ui_icon_key_from_name, + radroots_app_ui_icon_data, + RadrootsAppUiIconKey, + }; + + #[test] + fn icon_key_parses_names() { + assert_eq!( + radroots_app_ui_icon_key_from_name("caret-right"), + Some(RadrootsAppUiIconKey::CaretRight) + ); + assert_eq!( + radroots_app_ui_icon_key_from_name("chevron-right"), + Some(RadrootsAppUiIconKey::CaretRight) + ); + assert_eq!( + radroots_app_ui_icon_key_from_name("caret-up-down"), + Some(RadrootsAppUiIconKey::CaretUpDown) + ); + assert_eq!( + radroots_app_ui_icon_key_from_name("chevrons-up-down"), + Some(RadrootsAppUiIconKey::CaretUpDown) + ); + assert_eq!( + radroots_app_ui_icon_key_from_name("plus"), + Some(RadrootsAppUiIconKey::Plus) + ); + assert_eq!(radroots_app_ui_icon_key_from_name("unknown"), None); + } + + #[test] + fn icon_data_resolves() { + let icon = radroots_app_ui_icon_data(RadrootsAppUiIconKey::Plus); + assert!(!icon.data.is_empty()); + } +} diff --git a/crates/ui-components/src/lib.rs b/crates/ui-components/src/lib.rs @@ -1,12 +1,19 @@ #![forbid(unsafe_code)] mod button; +mod icon; mod label; mod separator; mod dialog; mod sheet; pub use button::RadrootsAppUiButton; +pub use icon::{ + radroots_app_ui_icon_data, + radroots_app_ui_icon_key_from_name, + RadrootsAppUiIcon, + RadrootsAppUiIconKey, +}; pub use label::RadrootsAppUiLabel; pub use separator::{ radroots_app_ui_separator_orientation_value,