table_rs/dioxus/
body.rs

1use crate::dioxus::types::Column;
2use crate::dioxus::types::TableClasses;
3use crate::dioxus::types::TableTexts;
4use dioxus::prelude::*;
5use std::collections::HashMap;
6
7/// A table body component that renders rows of data, along with loading and empty states.
8///
9/// This component is responsible for rendering the `<tbody>` section of a table in a Dioxus application.
10/// It dynamically displays row data, a loading message, or an empty state message based on the provided props.
11///
12/// # Props
13/// - `columns`: A `Vec<Column>` defining which fields to render in each table row. Each column corresponds to a key in the row data.
14/// - `rows`: A `Vec<HashMap<&'static str, String>>` representing the data for each row, where keys match column IDs.
15/// - `loading`: A `bool` flag that, when true, displays a loading message instead of data rows.
16/// - `classes`: A `TableClasses` struct for customizing the CSS class names of the body, rows, and cells.
17/// - `texts`: A `TableTexts` struct that provides custom text for the loading and empty states.
18///
19/// # Behavior
20/// - If `loading` is `true`, a single row with a loading message is shown spanning all columns.
21/// - If `rows` is empty and not loading, an empty message row is displayed.
22/// - Otherwise, each data row is rendered in a `<tr>`, with one `<td>` per column.
23///
24/// # Returns
25/// A Dioxus `Element` representing the `<tbody>` of a table, with dynamic row content.
26///
27/// # Example
28/// ```rust
29/// use dioxus::prelude::*;
30/// use maplit::hashmap;
31/// use table_rs::dioxus::table::Table;
32/// use table_rs::dioxus::types::{Column, TableClasses, TableTexts};
33/// use table_rs::dioxus::body::TableBody;
34///
35///
36/// fn App() -> Element {
37///     let rows = vec![
38///         hashmap! { "name" => "Ferris".to_string(), "email" => "ferris@opensass.org".to_string() },
39///         hashmap! { "name" => "Rustacean".to_string(), "email" => "rust@opensass.org".to_string() },
40///     ];
41///
42///     let columns = vec![
43///         Column { id: "name", header: "Name", ..Default::default() },
44///         Column { id: "email", header: "Email", ..Default::default() },
45///     ];
46///
47///     rsx! {
48///         TableBody {
49///             columns: columns,
50///             rows: rows,
51///             loading: false,
52///             classes: TableClasses::default(),
53///             texts: TableTexts::default(),
54///         }
55///     }
56/// }
57/// ```
58///
59/// # See Also
60/// - [MDN `<tbody>` Element](https://developer.mozilla.org/en-US/docs/Web/HTML/Reference/Elements/tbody)
61#[component]
62pub fn TableBody(
63    columns: Vec<Column>,
64    rows: Vec<HashMap<&'static str, String>>,
65    loading: bool,
66    classes: TableClasses,
67    texts: TableTexts,
68) -> Element {
69    let content = if loading {
70        rsx! {
71            tr { class: "{classes.loading_row}",
72                td {
73                    colspan: "{columns.len()}",
74                    "{texts.loading}"
75                }
76            }
77        }
78    } else if rows.is_empty() {
79        rsx! {
80            tr { class: "{classes.empty_row}",
81                td {
82                    colspan: "{columns.len()}",
83                    "{texts.empty}"
84                }
85            }
86        }
87    } else {
88        rsx! {
89            for row in rows.iter() {
90                tr { class: "{classes.row}", role: "row",
91                    for col in columns.iter() {
92                        td { class: "{classes.body_cell}", role: "cell",
93                            BodyCell {
94                                column: col.clone(),
95                                content: row.get(col.id).unwrap_or(&String::new()),
96                            }
97                        }
98                    }
99                }
100            }
101        }
102    };
103
104    rsx! {
105        tbody { class: "{classes.tbody}",
106            {content}
107        }
108    }
109}
110
111#[component]
112fn BodyCell(column: Column, content: String) -> Element {
113    if let Some(cb) = column.cell {
114        cb(content)
115    } else {
116        rsx! {
117            "{content}"
118        }
119    }
120}