Skip to main content

yew_datatable/components/
data_table.rs

1//! Main DataTable component.
2
3use crate::components::pagination::Pagination;
4use crate::components::table_body::TableBody;
5use crate::components::table_header::TableHeader;
6use crate::hooks::use_table::UseTableHandle;
7use yew::prelude::*;
8
9/// Props for the DataTable component.
10///
11/// This component accepts a pre-created table handle from the `use_table` hook.
12/// For usage, create the table handle in your component and pass it here.
13#[derive(Properties, Clone)]
14pub struct DataTableProps<T: Clone + PartialEq + 'static> {
15    /// The table handle from use_table hook.
16    pub table: UseTableHandle<T>,
17
18    /// Custom class for the table container.
19    #[prop_or_default]
20    pub class: Classes,
21
22    /// Custom class for the table element.
23    #[prop_or_default]
24    pub table_class: Classes,
25
26    /// Whether to show pagination.
27    #[prop_or(true)]
28    pub show_pagination: bool,
29
30    /// Whether to show global filter.
31    #[prop_or(true)]
32    pub show_global_filter: bool,
33
34    /// Placeholder for global filter input.
35    #[prop_or_else(|| "Search...".to_string())]
36    pub filter_placeholder: String,
37
38    /// Whether rows are selectable.
39    #[prop_or(true)]
40    pub selectable: bool,
41}
42
43/// Compares `DataTableProps` by their configuration fields, excluding the table handle.
44impl<T: Clone + PartialEq + 'static> PartialEq for DataTableProps<T> {
45    fn eq(&self, other: &Self) -> bool {
46        // Compare all configuration fields except the table handle.
47        self.class == other.class
48            && self.table_class == other.table_class
49            && self.show_pagination == other.show_pagination
50            && self.show_global_filter == other.show_global_filter
51            && self.filter_placeholder == other.filter_placeholder
52            && self.selectable == other.selectable
53    }
54}
55
56/// A complete data table component with all features.
57///
58/// Use this component with a table handle created by the `use_table` hook:
59///
60/// ```ignore
61/// let table = use_table(columns, data, None);
62/// html! { <DataTable<MyRow> table={table} /> }
63/// ```
64#[function_component(DataTable)]
65pub fn data_table<T: Clone + PartialEq + 'static>(props: &DataTableProps<T>) -> Html {
66    // Clone the table handle for use in callbacks.
67    let table = props.table.clone();
68
69    // Create the global filter input callback.
70    let on_global_filter = {
71        let table = table.clone();
72        Callback::from(move |e: InputEvent| {
73            // Extract the input element from the event target.
74            let target = e.target_dyn_into::<web_sys::HtmlInputElement>();
75            if let Some(input) = target {
76                // Apply the filter value to the table.
77                table.set_global_filter(input.value());
78            }
79        })
80    };
81
82    html! {
83        <div class={classes!("datatable-container", props.class.clone())}>
84            {if props.show_global_filter {
85                html! {
86                    <div class="datatable-toolbar">
87                        <input
88                            type="text"
89                            class="global-filter"
90                            placeholder={props.filter_placeholder.clone()}
91                            oninput={on_global_filter}
92                        />
93                    </div>
94                }
95            } else {
96                html! {}
97            }}
98
99            <table class={classes!("datatable", props.table_class.clone())}>
100                <TableHeader<T> table={table.clone()} />
101                <TableBody<T> table={table.clone()} selectable={props.selectable} />
102            </table>
103
104            {if props.show_pagination {
105                html! {
106                    <Pagination<T> table={table.clone()} />
107                }
108            } else {
109                html! {}
110            }}
111        </div>
112    }
113}