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