Skip to main content

dioxus_bootstrap_css/
list_group.rs

1use dioxus::prelude::*;
2
3use crate::types::Color;
4
5/// Bootstrap ListGroup component.
6///
7/// # Bootstrap HTML → Dioxus
8///
9/// ```html
10/// <!-- Bootstrap HTML -->
11/// <ul class="list-group list-group-flush">
12///   <li class="list-group-item active">Active</li>
13///   <li class="list-group-item">Normal</li>
14///   <li class="list-group-item list-group-item-danger">Danger</li>
15///   <li class="list-group-item disabled">Disabled</li>
16/// </ul>
17/// ```
18///
19/// ```rust,no_run
20/// rsx! {
21///     ListGroup { flush: true,
22///         ListGroupItem { active: true, "Active" }
23///         ListGroupItem { "Normal" }
24///         ListGroupItem { color: Color::Danger, "Danger" }
25///         ListGroupItem { disabled: true, "Disabled" }
26///     }
27///     // Clickable list group
28///     ListGroup {
29///         ListGroupItem { onclick: handler, "Click me" }
30///     }
31///     // Numbered list
32///     ListGroup { numbered: true,
33///         ListGroupItem { "First" }
34///         ListGroupItem { "Second" }
35///     }
36/// }
37/// ```
38#[derive(Clone, PartialEq, Props)]
39pub struct ListGroupProps {
40    /// Remove borders and rounded corners for use inside cards.
41    #[props(default)]
42    pub flush: bool,
43    /// Use numbered list style.
44    #[props(default)]
45    pub numbered: bool,
46    /// Additional CSS classes.
47    #[props(default)]
48    pub class: String,
49    /// Child elements (ListGroupItem components).
50    pub children: Element,
51}
52
53#[component]
54pub fn ListGroup(props: ListGroupProps) -> Element {
55    let mut classes = vec!["list-group".to_string()];
56    if props.flush {
57        classes.push("list-group-flush".to_string());
58    }
59    if props.numbered {
60        classes.push("list-group-numbered".to_string());
61    }
62    if !props.class.is_empty() {
63        classes.push(props.class.clone());
64    }
65    let full_class = classes.join(" ");
66
67    if props.numbered {
68        rsx! {
69            ol { class: "{full_class}", {props.children} }
70        }
71    } else {
72        rsx! {
73            ul { class: "{full_class}", {props.children} }
74        }
75    }
76}
77
78/// Bootstrap ListGroupItem component.
79#[derive(Clone, PartialEq, Props)]
80pub struct ListGroupItemProps {
81    /// Active state.
82    #[props(default)]
83    pub active: bool,
84    /// Disabled state.
85    #[props(default)]
86    pub disabled: bool,
87    /// Item color variant.
88    #[props(default)]
89    pub color: Option<Color>,
90    /// Click event handler. When set, renders as a button for interactivity.
91    #[props(default)]
92    pub onclick: Option<EventHandler<MouseEvent>>,
93    /// Additional CSS classes.
94    #[props(default)]
95    pub class: String,
96    /// Child elements.
97    pub children: Element,
98}
99
100#[component]
101pub fn ListGroupItem(props: ListGroupItemProps) -> Element {
102    let mut classes = vec!["list-group-item".to_string()];
103    if props.active {
104        classes.push("active".to_string());
105    }
106    if props.disabled {
107        classes.push("disabled".to_string());
108    }
109    if let Some(ref c) = props.color {
110        classes.push(format!("list-group-item-{c}"));
111    }
112    if props.onclick.is_some() {
113        classes.push("list-group-item-action".to_string());
114    }
115    if !props.class.is_empty() {
116        classes.push(props.class.clone());
117    }
118    let full_class = classes.join(" ");
119
120    if let Some(handler) = &props.onclick {
121        let handler = *handler;
122        rsx! {
123            button {
124                class: "{full_class}",
125                r#type: "button",
126                disabled: props.disabled,
127                onclick: move |evt| handler.call(evt),
128                {props.children}
129            }
130        }
131    } else {
132        rsx! {
133            li { class: "{full_class}", {props.children} }
134        }
135    }
136}