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}