Skip to main content

dioxus_bootstrap_css/
table.rs

1use dioxus::prelude::*;
2
3use crate::types::{Color, Size};
4
5/// Bootstrap Table component.
6///
7/// # Bootstrap HTML → Dioxus
8///
9/// ```html
10/// <!-- Bootstrap HTML -->
11/// <div class="table-responsive">
12///   <table class="table table-striped table-hover">
13///     <caption>List of users</caption>
14///     <thead><tr><th>Name</th><th>Role</th></tr></thead>
15///     <tbody><tr><td>Alice</td><td>Admin</td></tr></tbody>
16///   </table>
17/// </div>
18/// ```
19///
20/// ```rust,no_run
21/// # use dioxus::prelude::*;
22/// # use dioxus_bootstrap_css::prelude::*;
23/// # fn _doctest() -> Element {
24/// // Dioxus equivalent
25/// rsx! {
26///     Table { striped: true, hover: true, responsive: true,
27///         caption: "List of users", caption_top: true,
28///         thead {
29///             tr { th { "Name" } th { "Role" } }
30///         }
31///         tbody {
32///             tr { td { "Alice" } td { "Admin" } }
33///         }
34///     }
35/// }
36/// # }
37/// ```
38///
39/// # Props
40///
41/// - `striped` — striped rows
42/// - `striped_columns` — striped columns instead of rows
43/// - `hover` — highlight rows on hover
44/// - `bordered` — borders on all cells
45/// - `responsive` — horizontal scroll wrapper
46/// - `caption` / `caption_top` — table caption text and position
47/// - `size` — `Size::Sm` for compact table
48/// - `color` — table color variant
49#[derive(Clone, PartialEq, Props)]
50pub struct TableProps {
51    /// Striped rows.
52    #[props(default)]
53    pub striped: bool,
54    /// Striped columns instead of rows.
55    #[props(default)]
56    pub striped_columns: bool,
57    /// Highlight rows on hover.
58    #[props(default)]
59    pub hover: bool,
60    /// Add borders to all cells.
61    #[props(default)]
62    pub bordered: bool,
63    /// Remove all borders.
64    #[props(default)]
65    pub borderless: bool,
66    /// Compact table with smaller padding.
67    #[props(default)]
68    pub size: Size,
69    /// Table color variant.
70    #[props(default)]
71    pub color: Option<Color>,
72    /// Wrap in a responsive container for horizontal scrolling.
73    #[props(default)]
74    pub responsive: bool,
75    /// Caption text (displayed above or below the table).
76    #[props(default)]
77    pub caption: String,
78    /// Place caption at the top (default is bottom per Bootstrap).
79    #[props(default)]
80    pub caption_top: bool,
81    /// Additional CSS classes.
82    #[props(default)]
83    pub class: String,
84    /// Any additional HTML attributes.
85    #[props(extends = GlobalAttributes)]
86    attributes: Vec<Attribute>,
87    /// Child elements (thead, tbody, etc.).
88    pub children: Element,
89}
90
91#[component]
92pub fn Table(props: TableProps) -> Element {
93    let mut classes = vec!["table".to_string()];
94
95    if props.striped {
96        classes.push("table-striped".to_string());
97    }
98    if props.striped_columns {
99        classes.push("table-striped-columns".to_string());
100    }
101    if props.caption_top {
102        classes.push("caption-top".to_string());
103    }
104    if props.hover {
105        classes.push("table-hover".to_string());
106    }
107    if props.bordered {
108        classes.push("table-bordered".to_string());
109    }
110    if props.borderless {
111        classes.push("table-borderless".to_string());
112    }
113    if let Size::Sm = props.size {
114        classes.push("table-sm".to_string());
115    }
116    if let Some(ref c) = props.color {
117        classes.push(format!("table-{c}"));
118    }
119    if !props.class.is_empty() {
120        classes.push(props.class.clone());
121    }
122
123    let full_class = classes.join(" ");
124
125    let caption = props.caption.clone();
126
127    if props.responsive {
128        rsx! {
129            div { class: "table-responsive",
130                table { class: "{full_class}",
131                    ..props.attributes,
132                    if !caption.is_empty() {
133                        caption { "{caption}" }
134                    }
135                    {props.children}
136                }
137            }
138        }
139    } else {
140        rsx! {
141            table { class: "{full_class}",
142                ..props.attributes,
143                if !caption.is_empty() {
144                    caption { "{caption}" }
145                }
146                {props.children}
147            }
148        }
149    }
150}