1use std::collections::{BTreeMap, BTreeSet};
2
3use serde_json::Value;
4
5use crate::{
6 filtering::{clear_grid_filter_reasons, matches_grid_row_filters},
7 models::{GridColumnDef, GridOptions, GridRecord, GridRow, SortState},
8 sorting::sort_grid_rows,
9 utils::get_path_value,
10};
11
12pub fn is_tree_enabled(options: &GridOptions) -> bool {
13 options.enable_tree_view
14}
15
16fn get_tree_children(options: &GridOptions, entity: &GridRecord) -> Vec<GridRecord> {
17 if !is_tree_enabled(options) {
18 return Vec::new();
19 }
20
21 let children_field = options.tree_children_field.as_deref().unwrap_or("children");
22 match get_path_value(entity, children_field) {
23 Some(Value::Array(values)) => values,
24 _ => Vec::new(),
25 }
26}
27
28fn resolve_row_id(options: &GridOptions, entity: &GridRecord, index: usize) -> String {
29 if let Some(field) = &options.row_id_field
30 && let Some(Value::String(id)) = get_path_value(entity, field)
31 {
32 return id;
33 }
34
35 format!("{}-{}", options.id, index)
36}
37
38struct CreateRowContext<'a> {
39 options: &'a GridOptions,
40 row_size: usize,
41 hidden_row_reasons: &'a BTreeMap<String, Vec<String>>,
42 expanded_rows: &'a BTreeMap<String, bool>,
43}
44
45fn create_row(
46 context: &CreateRowContext<'_>,
47 entity: &GridRecord,
48 index: usize,
49 tree_level: usize,
50 parent_id: Option<String>,
51 child_count: usize,
52) -> GridRow {
53 let row_id = resolve_row_id(context.options, entity, index);
54 let mut row = GridRow::new(row_id.clone(), entity.clone(), index, context.row_size);
55 row.tree_level = tree_level;
56 row.parent_id = parent_id;
57 row.child_count = child_count;
58 row.has_children = child_count > 0;
59 row.expanded = context.expanded_rows.get(&row_id).copied().unwrap_or(false);
60 row.expanded_row_height = context.options.expandable_row_height.unwrap_or(150);
61
62 if let Some(reasons) = context.hidden_row_reasons.get(&row_id) {
63 for reason in reasons {
64 row.set_this_row_invisible(reason.clone());
65 }
66 }
67
68 row
69}
70
71pub fn build_grid_rows(
72 options: &GridOptions,
73 row_size: usize,
74 hidden_row_reasons: &BTreeMap<String, Vec<String>>,
75 expanded_rows: &BTreeMap<String, bool>,
76) -> Vec<GridRow> {
77 let mut rows = Vec::new();
78 let mut next_index = 0usize;
79 let context = CreateRowContext {
80 options,
81 row_size,
82 hidden_row_reasons,
83 expanded_rows,
84 };
85
86 struct VisitContext<'a> {
87 create_row: CreateRowContext<'a>,
88 }
89
90 fn visit(
91 context: &VisitContext<'_>,
92 rows: &mut Vec<GridRow>,
93 next_index: &mut usize,
94 entities: &[GridRecord],
95 tree_level: usize,
96 parent_id: Option<String>,
97 ) {
98 for entity in entities {
99 let child_entities = get_tree_children(context.create_row.options, entity);
100 let row = create_row(
101 &context.create_row,
102 entity,
103 *next_index,
104 tree_level,
105 parent_id.clone(),
106 child_entities.len(),
107 );
108
109 *next_index += 1;
110 let parent = row.id.clone();
111 rows.push(row);
112
113 if is_tree_enabled(context.create_row.options) && !child_entities.is_empty() {
114 visit(
115 context,
116 rows,
117 next_index,
118 &child_entities,
119 tree_level + 1,
120 Some(parent),
121 );
122 }
123 }
124 }
125
126 let visit_context = VisitContext {
127 create_row: context,
128 };
129
130 visit(
131 &visit_context,
132 &mut rows,
133 &mut next_index,
134 &options.data,
135 0,
136 None,
137 );
138 rows
139}
140
141pub fn filter_and_flatten_grid_tree_rows(
142 rows: &[GridRow],
143 columns: &[GridColumnDef],
144 options: &GridOptions,
145 active_filters: &BTreeMap<String, String>,
146 expanded_tree_rows: &BTreeMap<String, bool>,
147 sort_state: &SortState,
148) -> Vec<GridRow> {
149 let mut rows_by_parent: BTreeMap<Option<String>, Vec<GridRow>> = BTreeMap::new();
150 for row in rows {
151 rows_by_parent
152 .entry(row.parent_id.clone())
153 .or_default()
154 .push(row.clone());
155 }
156
157 let mut included = BTreeSet::new();
158
159 fn visit(
160 row: &GridRow,
161 rows_by_parent: &BTreeMap<Option<String>, Vec<GridRow>>,
162 included: &mut BTreeSet<String>,
163 columns: &[GridColumnDef],
164 options: &GridOptions,
165 active_filters: &BTreeMap<String, String>,
166 ) -> bool {
167 let manually_hidden = !row.visible
168 && row
169 .invisible_reasons
170 .iter()
171 .any(|reason| !reason.starts_with("filter:"));
172 if manually_hidden {
173 return false;
174 }
175
176 let children = rows_by_parent
177 .get(&Some(row.id.clone()))
178 .cloned()
179 .unwrap_or_default();
180 let mut child_included = false;
181 for child in children {
182 child_included = visit(
183 &child,
184 rows_by_parent,
185 included,
186 columns,
187 options,
188 active_filters,
189 ) || child_included;
190 }
191
192 let mut current = row.clone();
193 let self_included =
194 matches_grid_row_filters(&mut current, columns, options, active_filters);
195 if child_included {
196 clear_grid_filter_reasons(&mut current);
197 }
198
199 let include = current.visible && (self_included || child_included);
200 if include {
201 included.insert(current.id);
202 }
203 include
204 }
205
206 for root_row in rows_by_parent.get(&None).cloned().unwrap_or_default() {
207 visit(
208 &root_row,
209 &rows_by_parent,
210 &mut included,
211 columns,
212 options,
213 active_filters,
214 );
215 }
216
217 let mut flattened = Vec::new();
218
219 struct FlattenContext<'a> {
220 rows_by_parent: &'a BTreeMap<Option<String>, Vec<GridRow>>,
221 included: &'a BTreeSet<String>,
222 columns: &'a [GridColumnDef],
223 options: &'a GridOptions,
224 expanded_tree_rows: &'a BTreeMap<String, bool>,
225 sort_state: &'a SortState,
226 }
227
228 fn flatten(
229 context: &FlattenContext<'_>,
230 parent_id: Option<String>,
231 flattened: &mut Vec<GridRow>,
232 ) {
233 let siblings = sort_grid_rows(
234 &context
235 .rows_by_parent
236 .get(&parent_id)
237 .cloned()
238 .unwrap_or_default()
239 .into_iter()
240 .filter(|row| context.included.contains(&row.id))
241 .collect::<Vec<_>>(),
242 context.columns,
243 context.options,
244 context.sort_state,
245 );
246
247 for row in siblings {
248 flattened.push(row.clone());
249 if row.has_children
250 && context
251 .expanded_tree_rows
252 .get(&row.id)
253 .copied()
254 .unwrap_or(false)
255 {
256 flatten(context, Some(row.id.clone()), flattened);
257 }
258 }
259 }
260
261 let flatten_context = FlattenContext {
262 rows_by_parent: &rows_by_parent,
263 included: &included,
264 columns,
265 options,
266 expanded_tree_rows,
267 sort_state,
268 };
269
270 flatten(&flatten_context, None, &mut flattened);
271 flattened
272}