Skip to main content

yew_datatable/components/
table_body.rs

1//! Table body component.
2
3use crate::hooks::use_table::UseTableHandle;
4use yew::prelude::*;
5use yew_datatable_core::prelude::DataTableRowId;
6
7/// Props for the TableBody component.
8#[derive(Properties, Clone)]
9pub struct TableBodyProps<T: Clone + PartialEq + 'static> {
10    /// The table handle from use_table hook.
11    pub table: UseTableHandle<T>,
12
13    /// Custom class for the tbody element.
14    #[prop_or_default]
15    pub class: Classes,
16
17    /// Custom class for tr elements.
18    #[prop_or_default]
19    pub tr_class: Classes,
20
21    /// Custom class for td elements.
22    #[prop_or_default]
23    pub td_class: Classes,
24
25    /// Custom class for selected rows.
26    #[prop_or_default]
27    pub selected_class: Classes,
28
29    /// Whether rows are selectable by clicking.
30    #[prop_or(true)]
31    pub selectable: bool,
32
33    /// Custom cell renderer.
34    #[prop_or_default]
35    pub render_cell: Option<Callback<CellRenderContext<T>, Html>>,
36}
37
38/// Compares `TableBodyProps` by their configuration fields, excluding the table handle.
39impl<T: Clone + PartialEq + 'static> PartialEq for TableBodyProps<T> {
40    fn eq(&self, other: &Self) -> bool {
41        // Compare all configuration fields except the table handle.
42        self.class == other.class
43            && self.tr_class == other.tr_class
44            && self.td_class == other.td_class
45            && self.selected_class == other.selected_class
46            && self.selectable == other.selectable
47    }
48}
49
50/// Context passed to custom cell renderers.
51#[derive(Clone)]
52pub struct CellRenderContext<T> {
53    /// The row data.
54    pub row: T,
55
56    /// The row ID.
57    pub row_id: DataTableRowId,
58
59    /// The row index.
60    pub row_index: usize,
61
62    /// The column ID.
63    pub column_id: String,
64
65    /// The column index.
66    pub column_index: usize,
67
68    /// The cell value as a string.
69    pub value: String,
70}
71
72/// Table body component that renders rows and cells.
73#[function_component(TableBody)]
74pub fn table_body<T: Clone + PartialEq + 'static>(props: &TableBodyProps<T>) -> Html {
75    // Retrieve the ordered list of visible column identifiers.
76    let column_ids = props.table.visible_column_ids();
77
78    // Collect the visible rows after processing.
79    let rows = props.table.visible_rows();
80
81    html! {
82        <tbody class={props.class.clone()}>
83            {rows.into_iter().enumerate().map(|(row_idx, row)| {
84                // Extract the row identifier and selection state.
85                let row_id = row.id.clone();
86                let is_selected = props.table.is_row_selected(&row_id);
87
88                // Apply the selected class if the row is selected.
89                let row_class = if is_selected {
90                    classes!(props.tr_class.clone(), props.selected_class.clone())
91                } else {
92                    props.tr_class.clone()
93                };
94
95                // Create the click handler for row selection toggling.
96                let onclick = if props.selectable {
97                    let table = props.table.clone();
98                    let row_id = row_id.clone();
99                    Some(Callback::from(move |_: MouseEvent| {
100                        table.toggle_row_selection(row_id.clone());
101                    }))
102                } else {
103                    None
104                };
105
106                html! {
107                    <tr
108                        key={row_id.as_str().to_string()}
109                        class={row_class}
110                        onclick={onclick}
111                    >
112                        {column_ids.iter().enumerate().map(|(col_idx, column_id)| {
113                            // Retrieve the cell value for the current column.
114                            let value = props.table.get_cell_value(&row.original, column_id)
115                                .unwrap_or_default();
116
117                            // Use the custom renderer if provided, otherwise render plain text.
118                            let cell_html = if let Some(render_cell) = &props.render_cell {
119                                let ctx = CellRenderContext {
120                                    row: row.original.clone(),
121                                    row_id: row_id.clone(),
122                                    row_index: row_idx,
123                                    column_id: column_id.as_str().to_string(),
124                                    column_index: col_idx,
125                                    value: value.clone(),
126                                };
127                                render_cell.emit(ctx)
128                            } else {
129                                html! { {value} }
130                            };
131
132                            html! {
133                                <td
134                                    key={column_id.as_str().to_string()}
135                                    class={props.td_class.clone()}
136                                >
137                                    {cell_html}
138                                </td>
139                            }
140                        }).collect::<Html>()}
141                    </tr>
142                }
143            }).collect::<Html>()}
144        </tbody>
145    }
146}