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    /// Any additional HTML attributes.
50    #[props(extends = GlobalAttributes)]
51    attributes: Vec<Attribute>,
52    /// Child elements (ListGroupItem components).
53    pub children: Element,
54}
55
56#[component]
57pub fn ListGroup(props: ListGroupProps) -> Element {
58    let mut classes = vec!["list-group".to_string()];
59    if props.flush {
60        classes.push("list-group-flush".to_string());
61    }
62    if props.numbered {
63        classes.push("list-group-numbered".to_string());
64    }
65    if !props.class.is_empty() {
66        classes.push(props.class.clone());
67    }
68    let full_class = classes.join(" ");
69
70    if props.numbered {
71        rsx! {
72            ol { class: "{full_class}", ..props.attributes, {props.children} }
73        }
74    } else {
75        rsx! {
76            ul { class: "{full_class}", ..props.attributes, {props.children} }
77        }
78    }
79}
80
81/// Bootstrap ListGroupItem component.
82#[derive(Clone, PartialEq, Props)]
83pub struct ListGroupItemProps {
84    /// Active state.
85    #[props(default)]
86    pub active: bool,
87    /// Disabled state.
88    #[props(default)]
89    pub disabled: bool,
90    /// Item color variant.
91    #[props(default)]
92    pub color: Option<Color>,
93    /// Click event handler. When set, renders as a button for interactivity.
94    #[props(default)]
95    pub onclick: Option<EventHandler<MouseEvent>>,
96    /// Additional CSS classes.
97    #[props(default)]
98    pub class: String,
99    /// Any additional HTML attributes.
100    #[props(extends = GlobalAttributes)]
101    attributes: Vec<Attribute>,
102    /// Child elements.
103    pub children: Element,
104}
105
106#[component]
107pub fn ListGroupItem(props: ListGroupItemProps) -> Element {
108    let mut classes = vec!["list-group-item".to_string()];
109    if props.active {
110        classes.push("active".to_string());
111    }
112    if props.disabled {
113        classes.push("disabled".to_string());
114    }
115    if let Some(ref c) = props.color {
116        classes.push(format!("list-group-item-{c}"));
117    }
118    if props.onclick.is_some() {
119        classes.push("list-group-item-action".to_string());
120    }
121    if !props.class.is_empty() {
122        classes.push(props.class.clone());
123    }
124    let full_class = classes.join(" ");
125
126    if let Some(handler) = &props.onclick {
127        let handler = *handler;
128        rsx! {
129            button {
130                class: "{full_class}",
131                r#type: "button",
132                disabled: props.disabled,
133                onclick: move |evt| handler.call(evt),
134                ..props.attributes,
135                {props.children}
136            }
137        }
138    } else {
139        rsx! {
140            li { class: "{full_class}", ..props.attributes, {props.children} }
141        }
142    }
143}