Skip to main content

yew_datatable/components/
table_header.rs

1//! Table header component.
2
3use crate::hooks::use_table::UseTableHandle;
4use yew::prelude::*;
5use yew_datatable_core::prelude::SortDirection;
6
7/// Props for the TableHeader component.
8#[derive(Properties, Clone)]
9pub struct TableHeaderProps<T: Clone + PartialEq + 'static> {
10    /// The table handle from use_table hook.
11    pub table: UseTableHandle<T>,
12
13    /// Custom class for the thead element.
14    #[prop_or_default]
15    pub class: Classes,
16
17    /// Custom class for th elements.
18    #[prop_or_default]
19    pub th_class: Classes,
20
21    /// Whether to show sort indicators.
22    #[prop_or(true)]
23    pub show_sort_indicator: bool,
24}
25
26/// Compares `TableHeaderProps` by all fields, including the table handle.
27impl<T: Clone + PartialEq + 'static> PartialEq for TableHeaderProps<T> {
28    fn eq(&self, other: &Self) -> bool {
29        // Compare the table handle for reactivity-aware equality.
30        self.table == other.table
31            // Compare all configuration fields.
32            && self.class == other.class
33            && self.th_class == other.th_class
34            && self.show_sort_indicator == other.show_sort_indicator
35    }
36}
37
38/// Table header component that renders column headers with sorting support.
39#[function_component(TableHeader)]
40pub fn table_header<T: Clone + PartialEq + 'static>(props: &TableHeaderProps<T>) -> Html {
41    // Retrieve the ordered list of visible column identifiers.
42    let column_ids = props.table.visible_column_ids();
43
44    html! {
45        <thead class={props.class.clone()}>
46            <tr>
47                {column_ids.iter().map(|column_id| {
48                    // Resolve column metadata for the current header cell.
49                    let header = props.table.get_column_header(column_id).unwrap_or_default();
50                    let is_sortable = props.table.is_column_sortable(column_id);
51                    let sort_direction = props.table.get_sort_direction(column_id);
52                    let sort_index = props.table.get_sort_index(column_id);
53
54                    // Create the click handler for toggling column sort.
55                    let onclick = {
56                        let table = props.table.clone();
57                        let column_id = column_id.clone();
58                        Callback::from(move |e: MouseEvent| {
59                            if is_sortable {
60                                // Detect shift-click for multi-column sorting.
61                                let multi = e.shift_key();
62                                table.toggle_sort(column_id.clone(), multi);
63                            }
64                        })
65                    };
66
67                    // Render the sort direction indicator if enabled.
68                    let sort_indicator = if props.show_sort_indicator {
69                        render_sort_indicator(sort_direction, sort_index)
70                    } else {
71                        html! {}
72                    };
73
74                    // Apply a pointer cursor class when the column is sortable.
75                    let cursor_class = if is_sortable { "cursor-pointer" } else { "" };
76
77                    html! {
78                        <th
79                            key={column_id.as_str().to_string()}
80                            class={classes!(props.th_class.clone(), cursor_class)}
81                            onclick={onclick}
82                        >
83                            <div class="flex items-center gap-1">
84                                <span>{header}</span>
85                                {sort_indicator}
86                            </div>
87                        </th>
88                    }
89                }).collect::<Html>()}
90            </tr>
91        </thead>
92    }
93}
94
95/// Renders the visual sort direction indicator for a column header.
96///
97/// # Parameters
98///
99/// - `direction`: The current sort direction, if any.
100/// - `index`: The sort priority index for multi-column sorting.
101///
102/// # Returns
103///
104/// - `Html`: The rendered sort indicator markup.
105fn render_sort_indicator(direction: Option<SortDirection>, index: Option<usize>) -> Html {
106    // Render the appropriate indicator based on sort direction.
107    match direction {
108        Some(SortDirection::Asc) => html! {
109            <span class="sort-indicator">
110                {"▲"}
111                {index.map(|i| html! { <sub>{i + 1}</sub> }).unwrap_or_default()}
112            </span>
113        },
114        Some(SortDirection::Desc) => html! {
115            <span class="sort-indicator">
116                {"▼"}
117                {index.map(|i| html! { <sub>{i + 1}</sub> }).unwrap_or_default()}
118            </span>
119        },
120        None => html! {},
121    }
122}