Skip to main content

orbital_base_components/data/list/
base.rs

1use leptos::prelude::*;
2
3#[derive(Clone, Copy, Debug, PartialEq, Eq)]
4pub enum ListNavigationMode {
5    Selectable,
6    Nav,
7}
8
9#[derive(Clone, Copy, Debug, PartialEq, Eq)]
10pub enum ListSelectionMode {
11    Single,
12    Multiselect,
13}
14
15#[component]
16pub fn BaseList(
17    #[prop(optional, into)] class: MaybeProp<String>,
18    #[prop(optional, into)] navigation_mode: Signal<Option<ListNavigationMode>>,
19    #[prop(optional, into)] selection_mode: Signal<Option<ListSelectionMode>>,
20    children: Children,
21) -> impl IntoView {
22    let role = move || {
23        if selection_mode.get().is_some() {
24            "listbox"
25        } else {
26            "list"
27        }
28    };
29
30    view! {
31        <ul
32            class=move || {
33                let mut parts = vec!["orbital-list".to_string()];
34                if let Some(mode) = navigation_mode.get() {
35                    parts.push(format!("orbital-list--nav-{}", mode_as_str(mode)));
36                }
37                if let Some(mode) = selection_mode.get() {
38                    parts.push(format!("orbital-list--select-{}", select_mode_as_str(mode)));
39                }
40                if let Some(extra) = class.get() {
41                    if !extra.is_empty() {
42                        parts.push(extra);
43                    }
44                }
45                parts.join(" ")
46            }
47            role=role
48        >
49            {children()}
50        </ul>
51    }
52}
53
54fn mode_as_str(mode: ListNavigationMode) -> &'static str {
55    match mode {
56        ListNavigationMode::Selectable => "selectable",
57        ListNavigationMode::Nav => "nav",
58    }
59}
60
61fn select_mode_as_str(mode: ListSelectionMode) -> &'static str {
62    match mode {
63        ListSelectionMode::Single => "single",
64        ListSelectionMode::Multiselect => "multiselect",
65    }
66}
67
68#[component]
69pub fn BaseListItem(
70    #[prop(optional, into)] class: MaybeProp<String>,
71    #[prop(optional, into)] selected: Signal<bool>,
72    #[prop(optional)] on_click: Option<Callback<()>>,
73    children: Children,
74) -> impl IntoView {
75    view! {
76        <li
77            class=move || {
78                let mut parts = vec!["orbital-list__item".to_string()];
79                if selected.get() {
80                    parts.push("orbital-list__item--selected".to_string());
81                }
82                if let Some(extra) = class.get() {
83                    if !extra.is_empty() {
84                        parts.push(extra);
85                    }
86                }
87                parts.join(" ")
88            }
89            role="listitem"
90            aria-selected=move || selected.get().then_some("true")
91            tabindex=move || if selected.get() { "0" } else { "-1" }
92            on:click=move |_| {
93                if let Some(cb) = on_click {
94                    cb.run(());
95                }
96            }
97        >
98            {children()}
99        </li>
100    }
101}