Skip to main content

ui_grid_core/
models.rs

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, Default)]
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 sort: Option<GridSortDescriptor>,
84    #[serde(default)]
85    pub filter: Option<GridFilterDescriptor>,
86}
87
88fn default_true() -> bool {
89    true
90}
91
92#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, Default)]
93#[serde(rename_all = "camelCase")]
94pub struct GridGroupingOptions {
95    #[serde(default)]
96    pub group_by: Vec<String>,
97    #[serde(default)]
98    pub start_collapsed: bool,
99}
100
101#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
102#[serde(rename_all = "camelCase")]
103pub struct GridOptions {
104    pub id: String,
105    #[serde(default)]
106    pub title: Option<String>,
107    #[serde(default)]
108    pub data: Vec<GridRecord>,
109    #[serde(default)]
110    pub column_defs: Vec<GridColumnDef>,
111    #[serde(default = "default_true")]
112    pub enable_sorting: bool,
113    #[serde(default = "default_true")]
114    pub enable_filtering: bool,
115    #[serde(default)]
116    pub enable_grouping: bool,
117    #[serde(default)]
118    pub enable_cell_edit: bool,
119    #[serde(default)]
120    pub enable_cell_edit_on_focus: bool,
121    #[serde(default = "default_true")]
122    pub enable_virtualization: bool,
123    #[serde(default)]
124    pub enable_pagination: bool,
125    #[serde(default)]
126    pub use_external_pagination: bool,
127    #[serde(default)]
128    pub pagination_page_sizes: Vec<usize>,
129    #[serde(default)]
130    pub pagination_page_size: Option<usize>,
131    #[serde(default)]
132    pub pagination_current_page: Option<usize>,
133    #[serde(default)]
134    pub total_items: Option<usize>,
135    #[serde(default)]
136    pub enable_expandable: bool,
137    #[serde(default)]
138    pub expandable_row_height: Option<usize>,
139    #[serde(default)]
140    pub enable_tree_view: bool,
141    #[serde(default)]
142    pub tree_children_field: Option<String>,
143    #[serde(default)]
144    pub virtualization_threshold: Option<usize>,
145    #[serde(default)]
146    pub grouping: Option<GridGroupingOptions>,
147    #[serde(default)]
148    pub row_id_field: Option<String>,
149}
150
151impl Default for GridOptions {
152    fn default() -> Self {
153        Self {
154            id: String::new(),
155            title: None,
156            data: Vec::new(),
157            column_defs: Vec::new(),
158            enable_sorting: true,
159            enable_filtering: true,
160            enable_grouping: false,
161            enable_cell_edit: false,
162            enable_cell_edit_on_focus: false,
163            enable_virtualization: true,
164            enable_pagination: false,
165            use_external_pagination: false,
166            pagination_page_sizes: Vec::new(),
167            pagination_page_size: None,
168            pagination_current_page: None,
169            total_items: None,
170            enable_expandable: false,
171            expandable_row_height: None,
172            enable_tree_view: false,
173            tree_children_field: None,
174            virtualization_threshold: Some(40),
175            grouping: None,
176            row_id_field: Some("id".to_string()),
177        }
178    }
179}
180
181#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, Default)]
182#[serde(rename_all = "camelCase")]
183pub struct GridCellPosition {
184    pub row_id: String,
185    pub column_name: String,
186}
187
188#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, Default)]
189#[serde(rename_all = "camelCase")]
190pub struct SortState {
191    #[serde(default)]
192    pub column_name: Option<String>,
193    #[serde(default)]
194    pub direction: SortDirection,
195}
196
197#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, Default)]
198#[serde(rename_all = "camelCase")]
199pub struct GridSavedPaginationState {
200    pub pagination_current_page: usize,
201    pub pagination_page_size: usize,
202}
203
204#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, Default)]
205#[serde(rename_all = "camelCase")]
206pub struct GridSavedState {
207    #[serde(default)]
208    pub column_order: Vec<String>,
209    #[serde(default)]
210    pub filters: BTreeMap<String, String>,
211    #[serde(default)]
212    pub sort: Option<SortState>,
213    #[serde(default)]
214    pub grouping: Vec<String>,
215    #[serde(default)]
216    pub pagination: Option<GridSavedPaginationState>,
217    #[serde(default)]
218    pub expandable: BTreeMap<String, bool>,
219    #[serde(default)]
220    pub tree_view: BTreeMap<String, bool>,
221}
222
223#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
224#[serde(rename_all = "camelCase")]
225pub struct GridRow {
226    pub id: String,
227    pub entity: GridRecord,
228    pub index: usize,
229    pub height: usize,
230    #[serde(default)]
231    pub invisible_reasons: Vec<String>,
232    #[serde(default = "default_true")]
233    pub visible: bool,
234    #[serde(default)]
235    pub is_selected: bool,
236    #[serde(default)]
237    pub tree_level: usize,
238    #[serde(default)]
239    pub parent_id: Option<String>,
240    #[serde(default)]
241    pub has_children: bool,
242    #[serde(default)]
243    pub child_count: usize,
244    #[serde(default)]
245    pub expanded: bool,
246    #[serde(default)]
247    pub expanded_row_height: usize,
248}
249
250impl GridRow {
251    pub fn new(id: String, entity: GridRecord, index: usize, height: usize) -> Self {
252        Self {
253            id,
254            entity,
255            index,
256            height,
257            invisible_reasons: Vec::new(),
258            visible: true,
259            is_selected: false,
260            tree_level: 0,
261            parent_id: None,
262            has_children: false,
263            child_count: 0,
264            expanded: false,
265            expanded_row_height: 0,
266        }
267    }
268
269    pub fn set_this_row_invisible(&mut self, reason: impl Into<String>) {
270        let reason = reason.into();
271        if !self
272            .invisible_reasons
273            .iter()
274            .any(|existing| existing == &reason)
275        {
276            self.invisible_reasons.push(reason);
277        }
278        self.visible = false;
279    }
280
281    pub fn clear_this_row_invisible(&mut self, reason: &str) {
282        self.invisible_reasons.retain(|existing| existing != reason);
283        self.visible = self.invisible_reasons.is_empty();
284    }
285}
286
287#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
288#[serde(rename_all = "camelCase")]
289pub struct GroupItem {
290    pub id: String,
291    pub depth: usize,
292    pub field: String,
293    pub label: String,
294    pub count: usize,
295    pub collapsed: bool,
296}
297
298#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
299#[serde(rename_all = "camelCase")]
300pub struct RowItem {
301    pub id: String,
302    pub row: GridRow,
303    pub visible_index: usize,
304}
305
306#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
307#[serde(rename_all = "camelCase")]
308pub struct ExpandableItem {
309    pub id: String,
310    pub row: GridRow,
311}
312
313#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
314#[serde(tag = "kind", rename_all = "camelCase")]
315pub enum DisplayItem {
316    Group(GroupItem),
317    Row(RowItem),
318    Expandable(ExpandableItem),
319}
320
321#[derive(Debug, Clone, PartialEq, Serialize, Deserialize, Default)]
322#[serde(rename_all = "camelCase")]
323pub struct BuildGridPipelineContext {
324    pub options: GridOptions,
325    #[serde(default)]
326    pub columns: Vec<GridColumnDef>,
327    #[serde(default)]
328    pub active_filters: BTreeMap<String, String>,
329    #[serde(default)]
330    pub sort_state: SortState,
331    #[serde(default)]
332    pub group_by_columns: Vec<String>,
333    #[serde(default)]
334    pub collapsed_groups: BTreeMap<String, bool>,
335    #[serde(default)]
336    pub hidden_row_reasons: BTreeMap<String, Vec<String>>,
337    #[serde(default)]
338    pub expanded_rows: BTreeMap<String, bool>,
339    #[serde(default)]
340    pub expanded_tree_rows: BTreeMap<String, bool>,
341    #[serde(default = "default_current_page")]
342    pub current_page: usize,
343    #[serde(default)]
344    pub page_size: usize,
345    #[serde(default = "default_row_size")]
346    pub row_size: usize,
347}
348
349fn default_current_page() -> usize {
350    1
351}
352
353fn default_row_size() -> usize {
354    44
355}
356
357#[derive(Debug, Clone, PartialEq, Serialize, Deserialize, Default)]
358#[serde(rename_all = "camelCase")]
359pub struct PipelineResult {
360    #[serde(default)]
361    pub visible_rows: Vec<GridRow>,
362    #[serde(default)]
363    pub display_items: Vec<DisplayItem>,
364    #[serde(default)]
365    pub virtualization_enabled: bool,
366    #[serde(default)]
367    pub pipeline_ms: f64,
368    #[serde(default)]
369    pub total_items: usize,
370}