1use std::collections::BTreeMap;
2
3use serde::{Deserialize, Serialize};
4use serde_json::Value;
5
6use crate::constants::{FilterCondition, SortDirection};
7
8pub type GridRecord = Value;
9
10#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, Default)]
11#[serde(rename_all = "camelCase")]
12pub enum GridColumnType {
13 #[default]
14 String,
15 Number,
16 Boolean,
17 Date,
18 Object,
19}
20
21#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, Default)]
22#[serde(rename_all = "camelCase")]
23pub struct GridFilterFlags {
24 #[serde(default)]
25 pub case_sensitive: bool,
26 #[serde(default)]
27 pub date: bool,
28}
29
30#[derive(Debug, Clone, PartialEq, Serialize, Deserialize, Default)]
31#[serde(rename_all = "camelCase")]
32pub struct GridFilterDescriptor {
33 #[serde(default)]
34 pub term: Option<Value>,
35 #[serde(default)]
36 pub condition: Option<FilterCondition>,
37 #[serde(default)]
38 pub flags: GridFilterFlags,
39 #[serde(default)]
40 pub raw_term: bool,
41 #[serde(default)]
42 pub no_term: bool,
43}
44
45#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, Default)]
46#[serde(rename_all = "camelCase")]
47pub struct GridSortDescriptor {
48 #[serde(default)]
49 pub direction: Option<SortDirection>,
50 #[serde(default)]
51 pub priority: Option<u32>,
52 #[serde(default)]
53 pub ignore_sort: bool,
54}
55
56#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
57#[serde(rename_all = "camelCase")]
58pub struct GridColumnDef {
59 pub name: String,
60 #[serde(default)]
61 pub display_name: Option<String>,
62 #[serde(default)]
63 pub field: Option<String>,
64 #[serde(default)]
65 pub r#type: GridColumnType,
66 #[serde(default = "default_true")]
67 pub visible: bool,
68 #[serde(default = "default_true")]
69 pub sortable: bool,
70 #[serde(default = "default_true")]
71 pub filterable: bool,
72 #[serde(default = "default_true")]
73 pub enable_sorting: bool,
74 #[serde(default = "default_true")]
75 pub enable_filtering: bool,
76 #[serde(default = "default_true")]
77 pub enable_grouping: bool,
78 #[serde(default)]
79 pub enable_cell_edit: bool,
80 #[serde(default)]
81 pub enable_cell_edit_on_focus: bool,
82 #[serde(default)]
83 pub pinned_left: bool,
84 #[serde(default)]
85 pub pinned_right: bool,
86 #[serde(default = "default_true")]
87 pub enable_pinning: bool,
88 #[serde(default)]
89 pub width: Option<String>,
90 #[serde(default)]
91 pub align: Option<String>,
92 #[serde(default)]
93 pub sort: Option<GridSortDescriptor>,
94 #[serde(default)]
95 pub filter: Option<GridFilterDescriptor>,
96}
97
98impl Default for GridColumnDef {
99 fn default() -> Self {
100 Self {
101 name: String::new(),
102 display_name: None,
103 field: None,
104 r#type: GridColumnType::default(),
105 visible: true,
106 sortable: true,
107 filterable: true,
108 enable_sorting: true,
109 enable_filtering: true,
110 enable_grouping: true,
111 enable_cell_edit: false,
112 enable_cell_edit_on_focus: false,
113 pinned_left: false,
114 pinned_right: false,
115 enable_pinning: true,
116 width: None,
117 align: None,
118 sort: None,
119 filter: None,
120 }
121 }
122}
123
124fn default_true() -> bool {
125 true
126}
127
128#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, Default)]
129#[serde(rename_all = "camelCase")]
130pub struct GridGroupingOptions {
131 #[serde(default)]
132 pub group_by: Vec<String>,
133 #[serde(default)]
134 pub start_collapsed: bool,
135}
136
137#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
138#[serde(rename_all = "camelCase")]
139pub enum GridIcon {
140 Grip,
141 Sort,
142 SortAsc,
143 SortDesc,
144 Group,
145 Ungroup,
146 ChevronLeft,
147 ChevronRight,
148 ChevronDown,
149 PinLeft,
150 PinRight,
151 Unpin,
152}
153
154#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
155#[serde(rename_all = "camelCase")]
156pub struct GridIcons {
157 pub sort_default: GridIcon,
158 pub sort_asc: GridIcon,
159 pub sort_desc: GridIcon,
160 pub group_column: GridIcon,
161 pub ungroup_column: GridIcon,
162 pub group_collapse: GridIcon,
163 pub group_expand: GridIcon,
164 pub tree_collapse: GridIcon,
165 pub tree_expand: GridIcon,
166 pub expand_detail: GridIcon,
167 pub collapse_detail: GridIcon,
168 pub drag_handle: GridIcon,
169 pub move_left: GridIcon,
170 pub move_right: GridIcon,
171 pub pin_left: GridIcon,
172 pub pin_right: GridIcon,
173 pub unpin: GridIcon,
174}
175
176impl Default for GridIcons {
177 fn default() -> Self {
178 Self {
179 sort_default: GridIcon::Sort,
180 sort_asc: GridIcon::SortAsc,
181 sort_desc: GridIcon::SortDesc,
182 group_column: GridIcon::Group,
183 ungroup_column: GridIcon::Ungroup,
184 group_collapse: GridIcon::ChevronDown,
185 group_expand: GridIcon::ChevronRight,
186 tree_collapse: GridIcon::ChevronDown,
187 tree_expand: GridIcon::ChevronRight,
188 expand_detail: GridIcon::ChevronDown,
189 collapse_detail: GridIcon::ChevronDown,
190 drag_handle: GridIcon::Grip,
191 move_left: GridIcon::ChevronLeft,
192 move_right: GridIcon::ChevronRight,
193 pin_left: GridIcon::PinLeft,
194 pin_right: GridIcon::PinRight,
195 unpin: GridIcon::Unpin,
196 }
197 }
198}
199
200#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
201#[serde(rename_all = "camelCase")]
202pub struct GridLabels {
203 pub sort_default: String,
204 pub sort_asc: String,
205 pub sort_desc: String,
206 pub group_column: String,
207 pub ungroup_column: String,
208 pub group_collapse: String,
209 pub group_expand: String,
210 pub tree_collapse: String,
211 pub tree_expand: String,
212 pub expand_detail: String,
213 pub collapse_detail: String,
214 pub filter_placeholder: String,
215 pub filter_disabled: String,
216 pub filter_column: String,
217 pub pagination_previous: String,
218 pub pagination_next: String,
219 pub pagination_page: String,
220 pub pagination_of: String,
221 pub pagination_rows: String,
222 pub empty_heading: String,
223 pub empty_description: String,
224 pub toolbar_of: String,
225 pub toolbar_rows: String,
226 pub stats_visible_rows: String,
227 pub group_rows_suffix: String,
228 pub pin_column: String,
229 pub pin_left: String,
230 pub pin_right: String,
231 pub unpin: String,
232}
233
234impl Default for GridLabels {
235 fn default() -> Self {
236 Self {
237 sort_default: "Sort".to_string(),
238 sort_asc: "Sort ascending".to_string(),
239 sort_desc: "Sort descending".to_string(),
240 group_column: "Group by this column".to_string(),
241 ungroup_column: "Remove grouping".to_string(),
242 group_collapse: "Collapse group".to_string(),
243 group_expand: "Expand group".to_string(),
244 tree_collapse: "Collapse row".to_string(),
245 tree_expand: "Expand row".to_string(),
246 expand_detail: "Expand details".to_string(),
247 collapse_detail: "Collapse details".to_string(),
248 filter_placeholder: "Filter…".to_string(),
249 filter_disabled: "Filter disabled".to_string(),
250 filter_column: "Filter".to_string(),
251 pagination_previous: "Previous page".to_string(),
252 pagination_next: "Next page".to_string(),
253 pagination_page: "Page".to_string(),
254 pagination_of: "of".to_string(),
255 pagination_rows: "Rows per page".to_string(),
256 empty_heading: "No matching rows".to_string(),
257 empty_description: "Adjust the active filters, grouping, or sort order.".to_string(),
258 toolbar_of: "of".to_string(),
259 toolbar_rows: "rows".to_string(),
260 stats_visible_rows: "visible rows".to_string(),
261 group_rows_suffix: "rows".to_string(),
262 pin_column: "Pin column".to_string(),
263 pin_left: "Pin left".to_string(),
264 pin_right: "Pin right".to_string(),
265 unpin: "Unpin".to_string(),
266 }
267 }
268}
269
270#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, Default)]
271#[serde(rename_all = "camelCase")]
272pub struct GridInfiniteScrollState {
273 #[serde(default)]
274 pub scroll_up: bool,
275 #[serde(default)]
276 pub scroll_down: bool,
277 #[serde(default)]
278 pub data_loading: bool,
279 #[serde(default)]
280 pub previous_visible_rows: usize,
281}
282
283#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
284#[serde(rename_all = "camelCase")]
285pub struct GridOptions {
286 pub id: String,
287 #[serde(default)]
288 pub title: Option<String>,
289 #[serde(default)]
290 pub data: Vec<GridRecord>,
291 #[serde(default)]
292 pub column_defs: Vec<GridColumnDef>,
293 #[serde(default)]
294 pub labels: GridLabels,
295 #[serde(default)]
296 pub icons: GridIcons,
297 #[serde(default = "default_true")]
298 pub enable_sorting: bool,
299 #[serde(default = "default_true")]
300 pub enable_filtering: bool,
301 #[serde(default)]
302 pub enable_grouping: bool,
303 #[serde(default)]
304 pub enable_column_moving: bool,
305 #[serde(default = "default_true")]
306 pub enable_column_resizing: bool,
307 #[serde(default)]
308 pub enable_cell_edit: bool,
309 #[serde(default)]
310 pub enable_cell_edit_on_focus: bool,
311 #[serde(default = "default_true")]
312 pub enable_virtualization: bool,
313 #[serde(default)]
314 pub enable_pagination: bool,
315 #[serde(default = "default_true")]
316 pub enable_pagination_controls: bool,
317 #[serde(default)]
318 pub use_external_pagination: bool,
319 #[serde(default)]
320 pub pagination_page_sizes: Vec<usize>,
321 #[serde(default)]
322 pub pagination_page_size: Option<usize>,
323 #[serde(default)]
324 pub pagination_current_page: Option<usize>,
325 #[serde(default)]
326 pub total_items: Option<usize>,
327 #[serde(default)]
328 pub enable_expandable: bool,
329 #[serde(default)]
330 pub expandable_row_height: Option<usize>,
331 #[serde(default)]
332 pub expandable_row_header_width: Option<usize>,
333 #[serde(default)]
334 pub enable_tree_view: bool,
335 #[serde(default)]
336 pub tree_children_field: Option<String>,
337 #[serde(default)]
338 pub tree_indent: Option<usize>,
339 #[serde(default = "default_true")]
340 pub show_tree_expand_no_children: bool,
341 #[serde(default)]
342 pub tree_row_header_always_visible: bool,
343 #[serde(default)]
344 pub enable_auto_resize: bool,
345 #[serde(default)]
346 pub infinite_scroll_rows_from_end: Option<usize>,
347 #[serde(default)]
348 pub infinite_scroll_up: bool,
349 #[serde(default)]
350 pub infinite_scroll_down: Option<bool>,
351 #[serde(default)]
352 pub virtualization_threshold: Option<usize>,
353 #[serde(default)]
354 pub viewport_height: Option<usize>,
355 #[serde(default)]
356 pub grouping: Option<GridGroupingOptions>,
357 #[serde(default)]
358 pub enable_pinning: bool,
359 #[serde(default)]
360 pub row_id_field: Option<String>,
361}
362
363impl Default for GridOptions {
364 fn default() -> Self {
365 Self {
366 id: String::new(),
367 title: None,
368 data: Vec::new(),
369 column_defs: Vec::new(),
370 labels: GridLabels::default(),
371 icons: GridIcons::default(),
372 enable_sorting: true,
373 enable_filtering: false,
374 enable_grouping: false,
375 enable_column_moving: false,
376 enable_column_resizing: true,
377 enable_cell_edit: false,
378 enable_cell_edit_on_focus: false,
379 enable_virtualization: true,
380 enable_pagination: false,
381 enable_pagination_controls: false,
382 use_external_pagination: false,
383 pagination_page_sizes: Vec::new(),
384 pagination_page_size: None,
385 pagination_current_page: None,
386 total_items: None,
387 enable_expandable: false,
388 expandable_row_height: None,
389 expandable_row_header_width: None,
390 enable_tree_view: false,
391 tree_children_field: None,
392 tree_indent: None,
393 show_tree_expand_no_children: false,
394 tree_row_header_always_visible: false,
395 enable_auto_resize: false,
396 infinite_scroll_rows_from_end: None,
397 infinite_scroll_up: false,
398 infinite_scroll_down: None,
399 virtualization_threshold: None,
400 viewport_height: None,
401 grouping: None,
402 enable_pinning: false,
403 row_id_field: Some("id".to_string()),
404 }
405 }
406}
407
408#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, Default)]
409#[serde(rename_all = "camelCase")]
410pub struct GridCellPosition {
411 pub row_id: String,
412 pub column_name: String,
413}
414
415#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, Default)]
416#[serde(rename_all = "camelCase")]
417pub struct SortState {
418 #[serde(default)]
419 pub column_name: Option<String>,
420 #[serde(default)]
421 pub direction: SortDirection,
422}
423
424#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, Default)]
425#[serde(rename_all = "camelCase")]
426pub struct GridSavedPaginationState {
427 pub pagination_current_page: usize,
428 pub pagination_page_size: usize,
429}
430
431#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, Default)]
432#[serde(rename_all = "camelCase")]
433pub struct GridSavedState {
434 #[serde(default)]
435 pub column_order: Vec<String>,
436 #[serde(default)]
437 pub filters: BTreeMap<String, String>,
438 #[serde(default)]
439 pub sort: Option<SortState>,
440 #[serde(default)]
441 pub grouping: Vec<String>,
442 #[serde(default)]
443 pub pagination: Option<GridSavedPaginationState>,
444 #[serde(default)]
445 pub expandable: BTreeMap<String, bool>,
446 #[serde(default)]
447 pub tree_view: BTreeMap<String, bool>,
448 #[serde(default)]
449 pub pinning: BTreeMap<String, String>,
450}
451
452#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
453#[serde(rename_all = "camelCase")]
454pub struct GridRow {
455 pub id: String,
456 pub entity: GridRecord,
457 pub index: usize,
458 pub height: usize,
459 #[serde(default)]
460 pub invisible_reasons: Vec<String>,
461 #[serde(default = "default_true")]
462 pub visible: bool,
463 #[serde(default)]
464 pub is_selected: bool,
465 #[serde(default)]
466 pub tree_level: usize,
467 #[serde(default)]
468 pub parent_id: Option<String>,
469 #[serde(default)]
470 pub has_children: bool,
471 #[serde(default)]
472 pub child_count: usize,
473 #[serde(default)]
474 pub expanded: bool,
475 #[serde(default)]
476 pub expanded_row_height: usize,
477}
478
479impl GridRow {
480 pub fn new(id: String, entity: GridRecord, index: usize, height: usize) -> Self {
481 Self {
482 id,
483 entity,
484 index,
485 height,
486 invisible_reasons: Vec::new(),
487 visible: true,
488 is_selected: false,
489 tree_level: 0,
490 parent_id: None,
491 has_children: false,
492 child_count: 0,
493 expanded: false,
494 expanded_row_height: 0,
495 }
496 }
497
498 pub fn set_this_row_invisible(&mut self, reason: impl Into<String>) {
499 let reason = reason.into();
500 if !self
501 .invisible_reasons
502 .iter()
503 .any(|existing| existing == &reason)
504 {
505 self.invisible_reasons.push(reason);
506 }
507 self.visible = false;
508 }
509
510 pub fn clear_this_row_invisible(&mut self, reason: &str) {
511 self.invisible_reasons.retain(|existing| existing != reason);
512 self.visible = self.invisible_reasons.is_empty();
513 }
514}
515
516#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
517#[serde(rename_all = "camelCase")]
518pub struct GroupItem {
519 pub id: String,
520 pub depth: usize,
521 pub field: String,
522 pub label: String,
523 pub count: usize,
524 pub collapsed: bool,
525}
526
527#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
528#[serde(rename_all = "camelCase")]
529pub struct RowItem {
530 pub id: String,
531 pub row: GridRow,
532 pub visible_index: usize,
533}
534
535#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
536#[serde(rename_all = "camelCase")]
537pub struct ExpandableItem {
538 pub id: String,
539 pub row: GridRow,
540}
541
542#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
543#[serde(tag = "kind", rename_all = "camelCase")]
544pub enum DisplayItem {
545 Group(GroupItem),
546 Row(RowItem),
547 Expandable(ExpandableItem),
548}
549
550#[derive(Debug, Clone, PartialEq, Serialize, Deserialize, Default)]
551#[serde(rename_all = "camelCase")]
552pub struct BuildGridPipelineContext {
553 pub options: GridOptions,
554 #[serde(default)]
555 pub columns: Vec<GridColumnDef>,
556 #[serde(default)]
557 pub active_filters: BTreeMap<String, String>,
558 #[serde(default)]
559 pub sort_state: SortState,
560 #[serde(default)]
561 pub group_by_columns: Vec<String>,
562 #[serde(default)]
563 pub collapsed_groups: BTreeMap<String, bool>,
564 #[serde(default)]
565 pub hidden_row_reasons: BTreeMap<String, Vec<String>>,
566 #[serde(default)]
567 pub expanded_rows: BTreeMap<String, bool>,
568 #[serde(default)]
569 pub expanded_tree_rows: BTreeMap<String, bool>,
570 #[serde(default = "default_current_page")]
571 pub current_page: usize,
572 #[serde(default)]
573 pub page_size: usize,
574 #[serde(default = "default_row_size")]
575 pub row_size: usize,
576}
577
578fn default_current_page() -> usize {
579 1
580}
581
582fn default_row_size() -> usize {
583 44
584}
585
586#[derive(Debug, Clone, PartialEq, Serialize, Deserialize, Default)]
587#[serde(rename_all = "camelCase")]
588pub struct PipelineResult {
589 #[serde(default)]
590 pub visible_rows: Vec<GridRow>,
591 #[serde(default)]
592 pub display_items: Vec<DisplayItem>,
593 #[serde(default)]
594 pub virtualization_enabled: bool,
595 #[serde(default)]
596 pub pipeline_ms: f64,
597 #[serde(default)]
598 pub total_items: usize,
599}