app

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

commit d979476a5d2b580bb2a9bc038da862c57bc01f72
parent 14c96e984d64c6ecde4f3c1de4a8f945bd07797c
Author: triesap <triesap@radroots.dev>
Date:   Thu, 22 Jan 2026 04:39:09 +0000

app: add list configuration types

- introduce list config structs mirroring trellis options
- model touch/input/select list item kinds
- add list style resolver for border/rounding defaults
- cover style defaults and overrides with unit tests

Diffstat:
Mcrates/ui-components/src/lib.rs | 33+++++++++++++++++++++++++++++++++
Acrates/ui-components/src/list_types.rs | 306+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
2 files changed, 339 insertions(+), 0 deletions(-)

diff --git a/crates/ui-components/src/lib.rs b/crates/ui-components/src/lib.rs @@ -4,6 +4,7 @@ mod button; mod icon; mod label; mod list; +mod list_types; mod separator; mod dialog; mod sheet; @@ -27,6 +28,38 @@ pub use list::{ RadrootsAppUiListRowTrailing, RadrootsAppUiListSection, }; +pub use list_types::{ + radroots_app_ui_list_icon_key, + radroots_app_ui_list_styles_resolve, + RadrootsAppUiList, + RadrootsAppUiListDefault, + RadrootsAppUiListDefaultLabel, + RadrootsAppUiListDisplay, + RadrootsAppUiListDisplayValue, + RadrootsAppUiListIcon, + RadrootsAppUiListInput, + RadrootsAppUiListInputAction, + RadrootsAppUiListInputField, + RadrootsAppUiListInputLineLabel, + RadrootsAppUiListItem, + RadrootsAppUiListItemKind, + RadrootsAppUiListLabel, + RadrootsAppUiListLabelText, + RadrootsAppUiListLabelValue, + RadrootsAppUiListLabelValueKind, + RadrootsAppUiListOffset, + RadrootsAppUiListOffsetMod, + RadrootsAppUiListSelect, + RadrootsAppUiListSelectField, + RadrootsAppUiListSelectOption, + RadrootsAppUiListStyles, + RadrootsAppUiListStylesResolved, + RadrootsAppUiListTitle, + RadrootsAppUiListTitleLink, + RadrootsAppUiListTitleValue, + RadrootsAppUiListTouch, + RadrootsAppUiListTouchEnd, +}; pub use label::RadrootsAppUiLabel; pub use separator::{ radroots_app_ui_separator_orientation_value, diff --git a/crates/ui-components/src/list_types.rs b/crates/ui-components/src/list_types.rs @@ -0,0 +1,306 @@ +#![forbid(unsafe_code)] + +use leptos::ev::MouseEvent; +use leptos::prelude::Callback; + +use crate::{radroots_app_ui_icon_key_from_name, RadrootsAppUiIconKey}; + +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub struct RadrootsAppUiListStylesResolved { + pub hide_border_top: bool, + pub hide_border_bottom: bool, + pub hide_rounded: bool, + pub set_title_background: bool, + pub set_default_background: bool, +} + +impl Default for RadrootsAppUiListStylesResolved { + fn default() -> Self { + Self { + hide_border_top: true, + hide_border_bottom: true, + hide_rounded: false, + set_title_background: false, + set_default_background: false, + } + } +} + +#[derive(Debug, Clone)] +pub struct RadrootsAppUiListStyles { + pub hide_border_top: Option<bool>, + pub hide_border_bottom: Option<bool>, + pub hide_rounded: Option<bool>, + pub set_title_background: Option<bool>, + pub set_default_background: Option<bool>, +} + +pub fn radroots_app_ui_list_styles_resolve( + styles: Option<&RadrootsAppUiListStyles>, +) -> RadrootsAppUiListStylesResolved { + let defaults = RadrootsAppUiListStylesResolved::default(); + match styles { + Some(styles) => RadrootsAppUiListStylesResolved { + hide_border_top: styles.hide_border_top.unwrap_or(defaults.hide_border_top), + hide_border_bottom: styles + .hide_border_bottom + .unwrap_or(defaults.hide_border_bottom), + hide_rounded: styles.hide_rounded.unwrap_or(defaults.hide_rounded), + set_title_background: styles + .set_title_background + .unwrap_or(defaults.set_title_background), + set_default_background: styles + .set_default_background + .unwrap_or(defaults.set_default_background), + }, + None => defaults, + } +} + +#[derive(Debug, Clone)] +pub struct RadrootsAppUiListIcon { + pub key: String, + pub class: Option<String>, +} + +pub fn radroots_app_ui_list_icon_key( + icon: &RadrootsAppUiListIcon, +) -> Option<RadrootsAppUiIconKey> { + radroots_app_ui_icon_key_from_name(icon.key.as_str()) +} + +#[derive(Debug, Clone)] +pub enum RadrootsAppUiListTitleValue { + Text(String), + Spacer, +} + +#[derive(Debug, Clone)] +pub struct RadrootsAppUiListTitleLink { + pub label: Option<RadrootsAppUiListLabelValue>, + pub icon: Option<RadrootsAppUiListIcon>, + pub classes: Option<String>, + pub on_click: Option<Callback<()>>, +} + +#[derive(Debug, Clone)] +pub struct RadrootsAppUiListTitle { + pub value: RadrootsAppUiListTitleValue, + pub classes: Option<String>, + pub mod_value: Option<RadrootsAppUiListOffsetMod>, + pub link: Option<RadrootsAppUiListTitleLink>, + pub on_click: Option<Callback<()>>, +} + +#[derive(Debug, Clone)] +pub struct RadrootsAppUiListDefaultLabel { + pub label: String, + pub classes: Option<String>, + pub on_click: Option<Callback<()>>, +} + +#[derive(Debug, Clone)] +pub struct RadrootsAppUiListDefault { + pub labels: Option<Vec<RadrootsAppUiListDefaultLabel>>, + pub show_title: bool, + pub classes: Option<String>, +} + +#[derive(Debug, Clone)] +pub struct RadrootsAppUiListLabelText { + pub value: String, + pub classes: Option<String>, +} + +#[derive(Debug, Clone)] +pub enum RadrootsAppUiListLabelValueKind { + Text(RadrootsAppUiListLabelText), + Icon(RadrootsAppUiListIcon), +} + +#[derive(Debug, Clone)] +pub struct RadrootsAppUiListLabelValue { + pub classes_wrap: Option<String>, + pub hide_truncate: bool, + pub value: RadrootsAppUiListLabelValueKind, +} + +#[derive(Debug, Clone)] +pub struct RadrootsAppUiListLabel { + pub left: Vec<RadrootsAppUiListLabelValue>, + pub right: Vec<RadrootsAppUiListLabelValue>, +} + +#[derive(Debug, Clone)] +pub enum RadrootsAppUiListDisplayValue { + Icon(RadrootsAppUiListIcon), + Label(RadrootsAppUiListLabelText), +} + +#[derive(Debug, Clone)] +pub struct RadrootsAppUiListDisplay { + pub value: RadrootsAppUiListDisplayValue, + pub loading: bool, + pub on_click: Option<Callback<MouseEvent>>, +} + +#[derive(Debug, Clone)] +pub struct RadrootsAppUiListTouchEnd { + pub icon: RadrootsAppUiListIcon, + pub on_click: Option<Callback<MouseEvent>>, +} + +#[derive(Debug, Clone)] +pub struct RadrootsAppUiListTouch { + pub label: RadrootsAppUiListLabel, + pub display: Option<RadrootsAppUiListDisplay>, + pub end: Option<RadrootsAppUiListTouchEnd>, + pub on_click: Option<Callback<MouseEvent>>, +} + +#[derive(Debug, Clone)] +pub struct RadrootsAppUiListInputAction { + pub visible: bool, + pub loading: bool, + pub icon: Option<RadrootsAppUiListIcon>, + pub on_click: Option<Callback<MouseEvent>>, +} + +#[derive(Debug, Clone)] +pub struct RadrootsAppUiListInputLineLabel { + pub value: String, + pub classes: Option<String>, +} + +#[derive(Debug, Clone)] +pub struct RadrootsAppUiListInputField { + pub value: String, + pub placeholder: Option<String>, + pub disabled: bool, + pub classes: Option<String>, + pub id: Option<String>, + pub on_input: Option<Callback<String>>, +} + +#[derive(Debug, Clone)] +pub struct RadrootsAppUiListInput { + pub field: RadrootsAppUiListInputField, + pub line_label: Option<RadrootsAppUiListInputLineLabel>, + pub action: Option<RadrootsAppUiListInputAction>, +} + +#[derive(Debug, Clone)] +pub struct RadrootsAppUiListSelectOption { + pub label: String, + pub value: String, + pub classes: Option<String>, +} + +#[derive(Debug, Clone)] +pub struct RadrootsAppUiListSelectField { + pub value: String, + pub options: Vec<RadrootsAppUiListSelectOption>, + pub disabled: bool, + pub classes: Option<String>, + pub id: Option<String>, + pub on_change: Option<Callback<String>>, +} + +#[derive(Debug, Clone)] +pub struct RadrootsAppUiListSelect { + pub field: RadrootsAppUiListSelectField, + pub label: RadrootsAppUiListLabel, + pub display: Option<RadrootsAppUiListDisplay>, + pub end: Option<RadrootsAppUiListTouchEnd>, + pub loading: bool, + pub on_click: Option<Callback<MouseEvent>>, +} + +#[derive(Debug, Clone)] +pub enum RadrootsAppUiListOffsetMod { + Small, + Glyph, + Icon { + icon: RadrootsAppUiListIcon, + loading: bool, + }, + IconCircle { + icon: RadrootsAppUiListIcon, + loading: bool, + }, +} + +#[derive(Debug, Clone)] +pub struct RadrootsAppUiListOffset { + pub mod_value: Option<RadrootsAppUiListOffsetMod>, + pub classes: Option<String>, + pub hide_space: bool, + pub hide_offset: bool, + pub on_click: Option<Callback<MouseEvent>>, +} + +#[derive(Debug, Clone)] +pub enum RadrootsAppUiListItemKind { + Touch(RadrootsAppUiListTouch), + Input(RadrootsAppUiListInput), + Select(RadrootsAppUiListSelect), +} + +#[derive(Debug, Clone)] +pub struct RadrootsAppUiListItem { + pub kind: RadrootsAppUiListItemKind, + pub loading: bool, + pub hide_active: bool, + pub hide_field: bool, + pub full_rounded: bool, + pub offset: Option<RadrootsAppUiListOffset>, +} + +#[derive(Debug, Clone)] +pub struct RadrootsAppUiList { + pub id: Option<String>, + pub view: Option<String>, + pub classes: Option<String>, + pub title: Option<RadrootsAppUiListTitle>, + pub default_state: Option<RadrootsAppUiListDefault>, + pub list: Option<Vec<Option<RadrootsAppUiListItem>>>, + pub hide_offset: bool, + pub styles: Option<RadrootsAppUiListStyles>, +} + +#[cfg(test)] +mod tests { + use super::{ + radroots_app_ui_list_styles_resolve, + RadrootsAppUiListStyles, + RadrootsAppUiListStylesResolved, + }; + + #[test] + fn list_style_defaults_match_spec() { + let resolved = radroots_app_ui_list_styles_resolve(None); + assert_eq!(resolved, RadrootsAppUiListStylesResolved::default()); + } + + #[test] + fn list_style_overrides_apply() { + let styles = RadrootsAppUiListStyles { + hide_border_top: Some(false), + hide_border_bottom: Some(true), + hide_rounded: Some(true), + set_title_background: Some(true), + set_default_background: Some(true), + }; + let resolved = radroots_app_ui_list_styles_resolve(Some(&styles)); + assert_eq!( + resolved, + RadrootsAppUiListStylesResolved { + hide_border_top: false, + hide_border_bottom: true, + hide_rounded: true, + set_title_background: true, + set_default_background: true, + } + ); + } +}