Skip to main content

yew_datatable_core/features/grouping/
grouping_state.rs

1//! Complete grouping state for the table.
2//!
3//! Manages row grouping by column values with support for
4//! multi-level grouping, aggregation display, and default expansion.
5
6use crate::column::column_id::ColumnId;
7
8/// Complete grouping state for the table.
9///
10/// Tracks which columns are used for grouping and provides
11/// configuration for aggregation and expansion behavior.
12#[derive(Debug, Clone, Default, PartialEq, Eq)]
13#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
14pub struct GroupingState {
15    /// Columns to group by, in order.
16    group_by: Vec<ColumnId>,
17
18    /// Whether grouping is enabled.
19    enabled: bool,
20
21    /// Whether to show aggregated rows.
22    show_aggregation: bool,
23
24    /// Whether to expand grouped rows by default.
25    expand_by_default: bool,
26}
27
28impl GroupingState {
29    /// Creates a new empty grouping state.
30    ///
31    /// # Returns
32    ///
33    /// - `GroupingState`: A new grouping state with no groups configured.
34    pub fn new() -> Self {
35        Self {
36            group_by: Vec::new(),
37            enabled: true,
38            show_aggregation: true,
39            expand_by_default: false,
40        }
41    }
42
43    /// Creates a grouping state with the given columns.
44    ///
45    /// # Parameters
46    ///
47    /// - `columns`: The column identifiers to group by.
48    ///
49    /// # Returns
50    ///
51    /// - `GroupingState`: A new grouping state with the specified columns.
52    pub fn with_columns(columns: Vec<ColumnId>) -> Self {
53        Self {
54            group_by: columns,
55            ..Self::new()
56        }
57    }
58
59    /// Sets whether grouping is enabled.
60    ///
61    /// # Parameters
62    ///
63    /// - `enabled`: Whether grouping is active.
64    ///
65    /// # Returns
66    ///
67    /// - `Self`: The modified grouping state.
68    pub fn with_enabled(mut self, enabled: bool) -> Self {
69        // Update the enabled flag.
70        self.enabled = enabled;
71        self
72    }
73
74    /// Sets whether to show aggregation.
75    ///
76    /// # Parameters
77    ///
78    /// - `show`: Whether aggregated values should be displayed.
79    ///
80    /// # Returns
81    ///
82    /// - `Self`: The modified grouping state.
83    pub fn with_aggregation(mut self, show: bool) -> Self {
84        // Update the aggregation display flag.
85        self.show_aggregation = show;
86        self
87    }
88
89    /// Sets whether to expand groups by default.
90    ///
91    /// # Parameters
92    ///
93    /// - `expand`: Whether groups start expanded.
94    ///
95    /// # Returns
96    ///
97    /// - `Self`: The modified grouping state.
98    pub fn with_expand_by_default(mut self, expand: bool) -> Self {
99        // Update the default expansion flag.
100        self.expand_by_default = expand;
101        self
102    }
103
104    /// Returns whether grouping is enabled.
105    ///
106    /// # Returns
107    ///
108    /// - `bool`: Whether grouping is enabled.
109    pub fn is_enabled(&self) -> bool {
110        self.enabled
111    }
112
113    /// Returns whether any grouping is active.
114    ///
115    /// # Returns
116    ///
117    /// - `bool`: Whether grouping is enabled and columns are configured.
118    pub fn is_grouped(&self) -> bool {
119        self.enabled && !self.group_by.is_empty()
120    }
121
122    /// Returns the group-by columns.
123    ///
124    /// # Returns
125    ///
126    /// - `&[ColumnId]`: A slice of column identifiers used for grouping.
127    pub fn group_by(&self) -> &[ColumnId] {
128        &self.group_by
129    }
130
131    /// Returns whether aggregation should be shown.
132    ///
133    /// # Returns
134    ///
135    /// - `bool`: Whether aggregated values are displayed.
136    pub fn show_aggregation(&self) -> bool {
137        self.show_aggregation
138    }
139
140    /// Returns whether groups should expand by default.
141    ///
142    /// # Returns
143    ///
144    /// - `bool`: Whether groups start in expanded state.
145    pub fn expand_by_default(&self) -> bool {
146        self.expand_by_default
147    }
148
149    /// Returns the grouping depth (number of group-by columns).
150    ///
151    /// # Returns
152    ///
153    /// - `usize`: The number of grouping levels.
154    pub fn depth(&self) -> usize {
155        self.group_by.len()
156    }
157
158    /// Checks if a column is being grouped by.
159    ///
160    /// # Parameters
161    ///
162    /// - `column_id`: The column identifier to check.
163    ///
164    /// # Returns
165    ///
166    /// - `bool`: Whether the column is used for grouping.
167    pub fn is_grouped_by(&self, column_id: &ColumnId) -> bool {
168        self.group_by.contains(column_id)
169    }
170
171    /// Gets the group index for a column.
172    ///
173    /// # Parameters
174    ///
175    /// - `column_id`: The column identifier to look up.
176    ///
177    /// # Returns
178    ///
179    /// - `Option<usize>`: The zero-based group index if the column is grouped.
180    pub fn get_group_index(&self, column_id: &ColumnId) -> Option<usize> {
181        // Find the position of the column in the group-by list.
182        self.group_by.iter().position(|id| id == column_id)
183    }
184
185    /// Adds a column to group by.
186    ///
187    /// # Parameters
188    ///
189    /// - `column_id`: The column identifier to add.
190    pub fn add_group(&mut self, column_id: ColumnId) {
191        // Only add if not already in the group-by list.
192        if !self.group_by.contains(&column_id) {
193            self.group_by.push(column_id);
194        }
195    }
196
197    /// Removes a column from grouping.
198    ///
199    /// # Parameters
200    ///
201    /// - `column_id`: The column identifier to remove.
202    pub fn remove_group(&mut self, column_id: &ColumnId) {
203        // Remove the column from the group-by list.
204        self.group_by.retain(|id| id != column_id);
205    }
206
207    /// Toggles grouping for a column.
208    ///
209    /// # Parameters
210    ///
211    /// - `column_id`: The column identifier to toggle.
212    pub fn toggle_group(&mut self, column_id: ColumnId) {
213        // Toggle between grouped and ungrouped.
214        if self.group_by.contains(&column_id) {
215            self.remove_group(&column_id);
216        } else {
217            self.add_group(column_id);
218        }
219    }
220
221    /// Sets the group-by columns.
222    ///
223    /// # Parameters
224    ///
225    /// - `columns`: The column identifiers to group by.
226    pub fn set_group_by(&mut self, columns: Vec<ColumnId>) {
227        // Replace the group-by list.
228        self.group_by = columns;
229    }
230
231    /// Moves a group column to a new index.
232    ///
233    /// # Parameters
234    ///
235    /// - `column_id`: The column identifier to move.
236    /// - `to_index`: The target index.
237    pub fn move_group(&mut self, column_id: &ColumnId, to_index: usize) {
238        // Find the current position and move to the target.
239        if let Some(from_index) = self.get_group_index(column_id) {
240            let id = self.group_by.remove(from_index);
241            let to_index = to_index.min(self.group_by.len());
242            self.group_by.insert(to_index, id);
243        }
244    }
245
246    /// Clears all grouping.
247    pub fn clear(&mut self) {
248        // Remove all group-by columns.
249        self.group_by.clear();
250    }
251
252    /// Resets to initial state.
253    pub fn reset(&mut self) {
254        // Clear all grouping.
255        self.group_by.clear();
256    }
257}