Skip to main content

fret_ui_headless/table/
row_model.rs

1use std::borrow::Borrow;
2use std::cell::OnceCell;
3use std::collections::{HashMap, HashSet};
4use std::sync::Arc;
5
6/// Stable identity for a row in the table.
7///
8/// This is aligned with TanStack Table's `getRowId` guidance, but uses an efficient numeric key so
9/// it can be used in hot paths (selection, row maps, virtualization keys) without heap allocation.
10///
11/// The default key strategy is index-path based, so callers should supply their own stable key
12/// (e.g. a database primary key) when the underlying data can reorder or change over time.
13#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord)]
14pub struct RowKey(pub u64);
15
16impl RowKey {
17    pub fn from_index(index: usize) -> Self {
18        Self(index as u64)
19    }
20}
21
22/// Stable string identity for a row (TanStack `RowId` equivalent).
23#[derive(Debug, Clone, PartialEq, Eq, Hash)]
24pub struct RowId(pub Arc<str>);
25
26impl RowId {
27    pub fn new(id: impl Into<Arc<str>>) -> Self {
28        Self(id.into())
29    }
30
31    pub fn as_str(&self) -> &str {
32        self.0.as_ref()
33    }
34}
35
36impl From<String> for RowId {
37    fn from(value: String) -> Self {
38        Self::new(value)
39    }
40}
41
42impl From<&str> for RowId {
43    fn from(value: &str) -> Self {
44        Self::new(Arc::<str>::from(value))
45    }
46}
47
48impl AsRef<str> for RowId {
49    fn as_ref(&self) -> &str {
50        self.0.as_ref()
51    }
52}
53
54impl Borrow<str> for RowId {
55    fn borrow(&self) -> &str {
56        self.0.as_ref()
57    }
58}
59
60/// Index into a [`RowModel`] arena.
61pub type RowIndex = usize;
62
63#[derive(Debug)]
64pub struct Row<'a, TData> {
65    pub id: RowId,
66    pub key: RowKey,
67    pub original: &'a TData,
68    pub index: usize,
69    pub depth: u16,
70    pub parent: Option<RowIndex>,
71    pub parent_key: Option<RowKey>,
72    pub sub_rows: Vec<RowIndex>,
73}
74
75impl<'a, TData> Clone for Row<'a, TData> {
76    fn clone(&self) -> Self {
77        Self {
78            id: self.id.clone(),
79            key: self.key,
80            original: self.original,
81            index: self.index,
82            depth: self.depth,
83            parent: self.parent,
84            parent_key: self.parent_key,
85            sub_rows: self.sub_rows.clone(),
86        }
87    }
88}
89
90#[derive(Debug)]
91pub struct RowModel<'a, TData> {
92    pub(super) root_rows: Vec<RowIndex>,
93    pub(super) flat_rows: Vec<RowIndex>,
94    pub(super) rows_by_key: HashMap<RowKey, RowIndex>,
95    pub(super) rows_by_id: HashMap<RowId, RowIndex>,
96    pub(super) arena: Vec<Row<'a, TData>>,
97}
98
99impl<'a, TData> Clone for RowModel<'a, TData> {
100    fn clone(&self) -> Self {
101        Self {
102            root_rows: self.root_rows.clone(),
103            flat_rows: self.flat_rows.clone(),
104            rows_by_key: self.rows_by_key.clone(),
105            rows_by_id: self.rows_by_id.clone(),
106            arena: self.arena.clone(),
107        }
108    }
109}
110
111impl<'a, TData> RowModel<'a, TData> {
112    pub fn root_rows(&self) -> &[RowIndex] {
113        &self.root_rows
114    }
115
116    pub fn flat_rows(&self) -> &[RowIndex] {
117        &self.flat_rows
118    }
119
120    /// TanStack-aligned: `row.getParentRows()` (root → ... → parent), excluding the row itself.
121    pub fn parent_rows(&self, row: RowIndex) -> Vec<RowIndex> {
122        let Some(mut current) = self.row(row).and_then(|r| r.parent) else {
123            return Vec::new();
124        };
125
126        let mut out = Vec::new();
127        while let Some(r) = self.row(current) {
128            out.push(current);
129            let Some(parent) = r.parent else {
130                break;
131            };
132            current = parent;
133        }
134
135        out.reverse();
136        out
137    }
138
139    /// TanStack-aligned: `row.getParentRows().map(r => r.id)`.
140    pub fn parent_row_ids(&self, row: RowIndex) -> Vec<RowId> {
141        self.parent_rows(row)
142            .into_iter()
143            .filter_map(|idx| self.row(idx).map(|r| r.id.clone()))
144            .collect()
145    }
146
147    /// TanStack-aligned: `row.getLeafRows()` is implemented upstream as
148    /// `flattenBy(row.subRows, r => r.subRows)` (DFS preorder), excluding the row itself.
149    ///
150    /// Note: despite the name, this includes all descendants (not only leaf nodes).
151    pub fn leaf_rows(&self, row: RowIndex) -> Vec<RowIndex> {
152        fn push_descendants<TData>(
153            arena: &[Row<'_, TData>],
154            out: &mut Vec<RowIndex>,
155            row: RowIndex,
156        ) {
157            let Some(r) = arena.get(row) else {
158                return;
159            };
160            for &child in &r.sub_rows {
161                out.push(child);
162                push_descendants(arena, out, child);
163            }
164        }
165
166        let mut out = Vec::new();
167        push_descendants(&self.arena, &mut out, row);
168        out
169    }
170
171    /// TanStack-aligned: `row.getLeafRows().map(r => r.id)`.
172    pub fn leaf_row_ids(&self, row: RowIndex) -> Vec<RowId> {
173        self.leaf_rows(row)
174            .into_iter()
175            .filter_map(|idx| self.row(idx).map(|r| r.id.clone()))
176            .collect()
177    }
178
179    pub fn row(&self, index: RowIndex) -> Option<&Row<'a, TData>> {
180        self.arena.get(index)
181    }
182
183    pub fn row_by_key(&self, key: RowKey) -> Option<RowIndex> {
184        self.rows_by_key.get(&key).copied()
185    }
186
187    pub fn row_by_id(&self, id: &str) -> Option<RowIndex> {
188        self.rows_by_id.get(id).copied()
189    }
190
191    pub fn rows_by_key(&self) -> &HashMap<RowKey, RowIndex> {
192        &self.rows_by_key
193    }
194
195    pub fn rows_by_id(&self) -> &HashMap<RowId, RowIndex> {
196        &self.rows_by_id
197    }
198
199    pub fn arena(&self) -> &[Row<'a, TData>] {
200        &self.arena
201    }
202}
203
204type GetRowKeyFn<'a, TData> = Box<dyn Fn(&TData, usize, Option<&RowKey>) -> RowKey + 'a>;
205type GetRowIdFn<'a, TData> = Box<dyn Fn(&TData, usize, Option<&RowId>) -> RowId + 'a>;
206type GetSubRowsFn<'a, TData> = Box<dyn for<'r> Fn(&'r TData, usize) -> Option<&'r [TData]> + 'a>;
207type GetGroupedRowModelFn<'a, TData> = Arc<
208    dyn Fn(
209            &RowModel<'a, TData>,
210            &[super::ColumnDef<TData>],
211            &super::GroupingState,
212        ) -> super::GroupedRowModel
213        + 'a,
214>;
215type GetGlobalFacetedRowModelFn<'a, TData> =
216    Arc<dyn Fn(&Table<'a, TData>) -> RowModel<'a, TData> + 'a>;
217type GetGlobalFacetedUniqueValuesFn<'a, TData> =
218    Arc<dyn Fn(&Table<'a, TData>) -> super::FacetCounts + 'a>;
219type GetGlobalFacetedMinMaxU64Fn<'a, TData> =
220    Arc<dyn Fn(&Table<'a, TData>) -> Option<(u64, u64)> + 'a>;
221
222pub struct TableBuilder<'a, TData> {
223    data: &'a [TData],
224    columns: Vec<super::ColumnDef<TData>>,
225    sorting_fns: HashMap<Arc<str>, super::SortingFnDef<TData>>,
226    filter_fns: HashMap<Arc<str>, super::FilterFnDef>,
227    aggregation_fns: HashMap<Arc<str>, super::AggregationFn>,
228    global_filter_fn: super::FilteringFnSpec,
229    get_column_can_global_filter: Option<Arc<dyn Fn(&super::ColumnDef<TData>, &TData) -> bool>>,
230    enable_row_pinning: Option<Arc<dyn Fn(RowKey, &TData) -> bool>>,
231    enable_row_selection: Option<Arc<dyn Fn(RowKey, &TData) -> bool>>,
232    enable_multi_row_selection: Option<Arc<dyn Fn(RowKey, &TData) -> bool>>,
233    enable_sub_row_selection: Option<Arc<dyn Fn(RowKey, &TData) -> bool>>,
234    get_row_can_expand: Option<Arc<dyn Fn(RowKey, &TData) -> bool>>,
235    get_is_row_expanded: Option<Arc<dyn Fn(RowKey, &TData) -> bool>>,
236    get_row_key: Option<GetRowKeyFn<'a, TData>>,
237    get_row_id: Option<GetRowIdFn<'a, TData>>,
238    get_sub_rows: Option<GetSubRowsFn<'a, TData>>,
239    get_grouped_row_model: Option<GetGroupedRowModelFn<'a, TData>>,
240    get_global_faceted_row_model: Option<GetGlobalFacetedRowModelFn<'a, TData>>,
241    get_global_faceted_unique_values: Option<GetGlobalFacetedUniqueValuesFn<'a, TData>>,
242    get_global_faceted_min_max_u64: Option<GetGlobalFacetedMinMaxU64Fn<'a, TData>>,
243    filtered_row_model_override_pre_filtered: bool,
244    sorted_row_model_override_pre_sorted: bool,
245    expanded_row_model_override_pre_expanded: bool,
246    pagination_row_model_override_pre_pagination: bool,
247    initial_state: Option<super::TableState>,
248    state: super::TableState,
249    options: super::TableOptions,
250    render_fallback_value: super::TanStackValue,
251}
252
253impl<'a, TData> TableBuilder<'a, TData> {
254    pub fn new(data: &'a [TData]) -> Self {
255        Self {
256            data,
257            columns: Vec::new(),
258            sorting_fns: HashMap::new(),
259            filter_fns: HashMap::new(),
260            aggregation_fns: HashMap::new(),
261            global_filter_fn: super::FilteringFnSpec::Auto,
262            get_column_can_global_filter: None,
263            enable_row_pinning: None,
264            enable_row_selection: None,
265            enable_multi_row_selection: None,
266            enable_sub_row_selection: None,
267            get_row_can_expand: None,
268            get_is_row_expanded: None,
269            get_row_key: None,
270            get_row_id: None,
271            get_sub_rows: None,
272            get_grouped_row_model: None,
273            get_global_faceted_row_model: None,
274            get_global_faceted_unique_values: None,
275            get_global_faceted_min_max_u64: None,
276            filtered_row_model_override_pre_filtered: false,
277            sorted_row_model_override_pre_sorted: false,
278            expanded_row_model_override_pre_expanded: false,
279            pagination_row_model_override_pre_pagination: false,
280            initial_state: None,
281            state: super::TableState::default(),
282            options: super::TableOptions::default(),
283            render_fallback_value: super::TanStackValue::Null,
284        }
285    }
286
287    pub fn columns(mut self, columns: Vec<super::ColumnDef<TData>>) -> Self {
288        self.columns = columns;
289        self
290    }
291
292    /// Register a named sorting function (TanStack `options.sortingFns` equivalent).
293    pub fn sorting_fn_builtin(
294        mut self,
295        key: impl Into<Arc<str>>,
296        sorting_fn: super::BuiltInSortingFn,
297    ) -> Self {
298        self.sorting_fns
299            .insert(key.into(), super::SortingFnDef::BuiltIn(sorting_fn));
300        self
301    }
302
303    /// Register a named sorting comparator (TanStack `options.sortingFns` equivalent).
304    pub fn sorting_fn_cmp(
305        mut self,
306        key: impl Into<Arc<str>>,
307        cmp: impl Fn(&TData, &TData) -> std::cmp::Ordering + 'static,
308    ) -> Self {
309        self.sorting_fns
310            .insert(key.into(), super::SortingFnDef::Cmp(Arc::new(cmp)));
311        self
312    }
313
314    /// Register a named filter function (TanStack `options.filterFns` equivalent).
315    pub fn filter_fn_builtin(
316        mut self,
317        key: impl Into<Arc<str>>,
318        filter_fn: super::BuiltInFilterFn,
319    ) -> Self {
320        self.filter_fns
321            .insert(key.into(), super::FilterFnDef::BuiltIn(filter_fn));
322        self
323    }
324
325    /// Register a named filter function that operates over the column's `getValue()`.
326    pub fn filter_fn_value(
327        mut self,
328        key: impl Into<Arc<str>>,
329        f: impl Fn(&super::TanStackValue, &serde_json::Value) -> bool + 'static,
330    ) -> Self {
331        self.filter_fns
332            .insert(key.into(), super::FilterFnDef::Value(Arc::new(f)));
333        self
334    }
335
336    /// Register a named filter function with TanStack-like `addMeta` support.
337    pub fn filter_fn_value_with_meta(
338        mut self,
339        key: impl Into<Arc<str>>,
340        f: impl Fn(&super::TanStackValue, &serde_json::Value, &mut dyn FnMut(serde_json::Value)) -> bool
341        + 'static,
342    ) -> Self {
343        self.filter_fns
344            .insert(key.into(), super::FilterFnDef::ValueWithMeta(Arc::new(f)));
345        self
346    }
347
348    /// Configure the global filter function (TanStack `globalFilterFn`).
349    pub fn global_filter_fn(mut self, spec: super::FilteringFnSpec) -> Self {
350        self.global_filter_fn = spec;
351        self
352    }
353
354    /// Configure the table-level global filter hook (TanStack `getColumnCanGlobalFilter`).
355    pub fn get_column_can_global_filter(
356        mut self,
357        f: impl Fn(&super::ColumnDef<TData>, &TData) -> bool + 'static,
358    ) -> Self {
359        self.get_column_can_global_filter = Some(Arc::new(f));
360        self
361    }
362
363    pub fn state(mut self, state: super::TableState) -> Self {
364        self.state = state;
365        self
366    }
367
368    /// TanStack-aligned: override the `initialState` snapshot used by table-level reset APIs.
369    ///
370    /// If not set, the `initialState` defaults to the same value as `state`.
371    pub fn initial_state(mut self, initial_state: super::TableState) -> Self {
372        self.initial_state = Some(initial_state);
373        self
374    }
375
376    pub fn options(mut self, options: super::TableOptions) -> Self {
377        self.options = options;
378        self
379    }
380
381    pub fn manual_filtering(mut self, manual: bool) -> Self {
382        self.options.manual_filtering = manual;
383        self
384    }
385
386    pub fn filter_from_leaf_rows(mut self, enabled: bool) -> Self {
387        self.options.filter_from_leaf_rows = enabled;
388        self
389    }
390
391    pub fn max_leaf_row_filter_depth(mut self, depth: usize) -> Self {
392        self.options.max_leaf_row_filter_depth = depth;
393        self
394    }
395
396    /// TanStack-aligned: override `getFilteredRowModel` to return `getPreFilteredRowModel()`.
397    pub fn override_filtered_row_model_pre_filtered(mut self) -> Self {
398        self.filtered_row_model_override_pre_filtered = true;
399        self
400    }
401
402    pub fn manual_sorting(mut self, manual: bool) -> Self {
403        self.options.manual_sorting = manual;
404        self
405    }
406
407    /// TanStack-aligned: override `getSortedRowModel` to return `getPreSortedRowModel()`.
408    pub fn override_sorted_row_model_pre_sorted(mut self) -> Self {
409        self.sorted_row_model_override_pre_sorted = true;
410        self
411    }
412
413    pub fn manual_pagination(mut self, manual: bool) -> Self {
414        self.options.manual_pagination = manual;
415        self
416    }
417
418    pub fn manual_expanding(mut self, manual: bool) -> Self {
419        self.options.manual_expanding = manual;
420        self
421    }
422
423    /// TanStack-aligned: override `getExpandedRowModel` to return `getPreExpandedRowModel()`.
424    pub fn override_expanded_row_model_pre_expanded(mut self) -> Self {
425        self.expanded_row_model_override_pre_expanded = true;
426        self
427    }
428
429    /// TanStack-aligned: override `getPaginationRowModel` to return `getPrePaginationRowModel()`.
430    pub fn override_pagination_row_model_pre_pagination(mut self) -> Self {
431        self.pagination_row_model_override_pre_pagination = true;
432        self
433    }
434
435    pub fn paginate_expanded_rows(mut self, enabled: bool) -> Self {
436        self.options.paginate_expanded_rows = enabled;
437        self
438    }
439
440    pub fn keep_pinned_rows(mut self, keep: bool) -> Self {
441        self.options.keep_pinned_rows = keep;
442        self
443    }
444
445    pub fn enable_hiding(mut self, enabled: bool) -> Self {
446        self.options.enable_hiding = enabled;
447        self
448    }
449
450    pub fn enable_column_ordering(mut self, enabled: bool) -> Self {
451        self.options.enable_column_ordering = enabled;
452        self
453    }
454
455    pub fn enable_pinning(mut self, enabled: bool) -> Self {
456        self.options.enable_pinning = enabled;
457        self
458    }
459
460    pub fn enable_column_pinning(mut self, enabled: bool) -> Self {
461        self.options.enable_column_pinning = enabled;
462        self
463    }
464
465    pub fn enable_row_pinning(mut self, enabled: bool) -> Self {
466        self.options.enable_row_pinning = enabled;
467        self
468    }
469
470    /// TanStack-aligned: configure `options.enableRowPinning` as a per-row predicate.
471    pub fn enable_row_pinning_by(mut self, f: impl Fn(RowKey, &TData) -> bool + 'static) -> Self {
472        self.enable_row_pinning = Some(Arc::new(f));
473        self
474    }
475
476    /// TanStack-aligned: configure `options.enableRowSelection` as a per-row predicate.
477    pub fn enable_row_selection_by(mut self, f: impl Fn(RowKey, &TData) -> bool + 'static) -> Self {
478        self.enable_row_selection = Some(Arc::new(f));
479        self
480    }
481
482    /// TanStack-aligned: configure `options.enableMultiRowSelection` as a per-row predicate.
483    pub fn enable_multi_row_selection_by(
484        mut self,
485        f: impl Fn(RowKey, &TData) -> bool + 'static,
486    ) -> Self {
487        self.enable_multi_row_selection = Some(Arc::new(f));
488        self
489    }
490
491    /// TanStack-aligned: configure `options.enableSubRowSelection` as a per-row predicate.
492    pub fn enable_sub_row_selection_by(
493        mut self,
494        f: impl Fn(RowKey, &TData) -> bool + 'static,
495    ) -> Self {
496        self.enable_sub_row_selection = Some(Arc::new(f));
497        self
498    }
499
500    /// TanStack-aligned: configure `options.getRowCanExpand` as a per-row predicate.
501    pub fn get_row_can_expand_by(mut self, f: impl Fn(RowKey, &TData) -> bool + 'static) -> Self {
502        self.get_row_can_expand = Some(Arc::new(f));
503        self
504    }
505
506    /// TanStack-aligned: configure `options.getIsRowExpanded` as a per-row predicate.
507    pub fn get_is_row_expanded_by(mut self, f: impl Fn(RowKey, &TData) -> bool + 'static) -> Self {
508        self.get_is_row_expanded = Some(Arc::new(f));
509        self
510    }
511
512    pub fn enable_column_resizing(mut self, enabled: bool) -> Self {
513        self.options.enable_column_resizing = enabled;
514        self
515    }
516
517    pub fn get_row_key(
518        mut self,
519        f: impl Fn(&TData, usize, Option<&RowKey>) -> RowKey + 'a,
520    ) -> Self {
521        self.get_row_key = Some(Box::new(f));
522        self
523    }
524
525    /// TanStack-aligned: set the string row identity function (`options.getRowId` equivalent).
526    pub fn get_row_id(mut self, f: impl Fn(&TData, usize, Option<&RowId>) -> RowId + 'a) -> Self {
527        self.get_row_id = Some(Box::new(f));
528        self
529    }
530
531    pub fn get_sub_rows(
532        mut self,
533        f: impl for<'r> Fn(&'r TData, usize) -> Option<&'r [TData]> + 'a,
534    ) -> Self {
535        self.get_sub_rows = Some(Box::new(f));
536        self
537    }
538
539    /// Override the grouped row model generator (TanStack `getGroupedRowModel` equivalent).
540    pub fn get_grouped_row_model(
541        mut self,
542        f: impl Fn(
543            &RowModel<'a, TData>,
544            &[super::ColumnDef<TData>],
545            &super::GroupingState,
546        ) -> super::GroupedRowModel
547        + 'a,
548    ) -> Self {
549        self.get_grouped_row_model = Some(Arc::new(f));
550        self
551    }
552
553    /// Override the "global faceted row model" surface (TanStack `getGlobalFacetedRowModel`).
554    pub fn get_global_faceted_row_model(
555        mut self,
556        f: impl Fn(&Table<'a, TData>) -> RowModel<'a, TData> + 'a,
557    ) -> Self {
558        self.get_global_faceted_row_model = Some(Arc::new(f));
559        self
560    }
561
562    /// Override the "global faceted unique values" surface (TanStack `getGlobalFacetedUniqueValues`).
563    pub fn get_global_faceted_unique_values(
564        mut self,
565        f: impl Fn(&Table<'a, TData>) -> super::FacetCounts + 'a,
566    ) -> Self {
567        self.get_global_faceted_unique_values = Some(Arc::new(f));
568        self
569    }
570
571    /// Override the "global faceted min/max" surface (TanStack `getGlobalFacetedMinMaxValues`).
572    pub fn get_global_faceted_min_max_u64(
573        mut self,
574        f: impl Fn(&Table<'a, TData>) -> Option<(u64, u64)> + 'a,
575    ) -> Self {
576        self.get_global_faceted_min_max_u64 = Some(Arc::new(f));
577        self
578    }
579
580    /// Register a named aggregation function (TanStack `options.aggregationFns` equivalent).
581    pub fn aggregation_fn(
582        mut self,
583        key: impl Into<Arc<str>>,
584        aggregation_fn: super::AggregationFn,
585    ) -> Self {
586        self.aggregation_fns.insert(key.into(), aggregation_fn);
587        self
588    }
589
590    /// TanStack-aligned: set `renderFallbackValue` used by `cell.renderValue()`.
591    pub fn render_fallback_value(mut self, value: super::TanStackValue) -> Self {
592        self.render_fallback_value = value;
593        self
594    }
595
596    pub fn build(self) -> Table<'a, TData> {
597        Table::new(self)
598    }
599}
600
601pub struct Table<'a, TData> {
602    data: &'a [TData],
603    column_tree: Vec<super::ColumnDef<TData>>,
604    columns: Vec<super::ColumnDef<TData>>,
605    sorting_fns: HashMap<Arc<str>, super::SortingFnDef<TData>>,
606    filter_fns: HashMap<Arc<str>, super::FilterFnDef>,
607    aggregation_fns: HashMap<Arc<str>, super::AggregationFn>,
608    global_filter_fn: super::FilteringFnSpec,
609    get_column_can_global_filter: Option<Arc<dyn Fn(&super::ColumnDef<TData>, &TData) -> bool>>,
610    enable_row_pinning: Option<Arc<dyn Fn(RowKey, &TData) -> bool>>,
611    enable_row_selection: Option<Arc<dyn Fn(RowKey, &TData) -> bool>>,
612    enable_multi_row_selection: Option<Arc<dyn Fn(RowKey, &TData) -> bool>>,
613    enable_sub_row_selection: Option<Arc<dyn Fn(RowKey, &TData) -> bool>>,
614    get_row_can_expand: Option<Arc<dyn Fn(RowKey, &TData) -> bool>>,
615    get_is_row_expanded: Option<Arc<dyn Fn(RowKey, &TData) -> bool>>,
616    get_row_key: GetRowKeyFn<'a, TData>,
617    get_row_id: Option<GetRowIdFn<'a, TData>>,
618    get_sub_rows: Option<GetSubRowsFn<'a, TData>>,
619    get_grouped_row_model: Option<GetGroupedRowModelFn<'a, TData>>,
620    get_global_faceted_row_model: Option<GetGlobalFacetedRowModelFn<'a, TData>>,
621    get_global_faceted_unique_values: Option<GetGlobalFacetedUniqueValuesFn<'a, TData>>,
622    get_global_faceted_min_max_u64: Option<GetGlobalFacetedMinMaxU64Fn<'a, TData>>,
623    filtered_row_model_override_pre_filtered: bool,
624    sorted_row_model_override_pre_sorted: bool,
625    expanded_row_model_override_pre_expanded: bool,
626    pagination_row_model_override_pre_pagination: bool,
627    /// TanStack-aligned `initialState` snapshot (used by table-level reset APIs).
628    initial_state: super::TableState,
629    state: super::TableState,
630    options: super::TableOptions,
631    render_fallback_value: super::TanStackValue,
632    core_row_model: OnceCell<RowModel<'a, TData>>,
633    filtered_row_model: OnceCell<RowModel<'a, TData>>,
634    grouped_row_model: OnceCell<super::GroupedRowModel>,
635    grouped_pre_sorted_row_model: OnceCell<RowModel<'a, TData>>,
636    grouped_sorted_row_model: OnceCell<RowModel<'a, TData>>,
637    grouped_u64_aggregations: OnceCell<HashMap<RowKey, Arc<[(super::ColumnId, u64)]>>>,
638    grouped_any_aggregations:
639        OnceCell<HashMap<RowKey, Arc<[(super::ColumnId, super::TanStackValue)]>>>,
640    sorted_row_model: OnceCell<RowModel<'a, TData>>,
641    expanded_row_model: OnceCell<RowModel<'a, TData>>,
642    paginated_row_model: OnceCell<RowModel<'a, TData>>,
643    expanded_paginated_row_model: OnceCell<RowModel<'a, TData>>,
644    selected_row_model: OnceCell<RowModel<'a, TData>>,
645    filtered_selected_row_model: OnceCell<RowModel<'a, TData>>,
646    grouped_selected_row_model: OnceCell<RowModel<'a, TData>>,
647    page_selected_row_model: OnceCell<RowModel<'a, TData>>,
648    faceted_row_model_by_column: OnceCell<Vec<OnceCell<RowModel<'a, TData>>>>,
649    faceted_unique_values_by_column: OnceCell<Vec<OnceCell<super::FacetCounts>>>,
650    faceted_unique_labels_by_column: OnceCell<Vec<OnceCell<super::FacetLabels<'a>>>>,
651    faceted_min_max_u64_by_column: OnceCell<Vec<OnceCell<Option<(u64, u64)>>>>,
652    global_faceted_row_model: OnceCell<RowModel<'a, TData>>,
653    global_faceted_unique_values: OnceCell<super::FacetCounts>,
654    global_faceted_min_max_u64: OnceCell<Option<(u64, u64)>>,
655}
656
657fn rebuild_flat_rows_from_roots_including_duplicates<TData>(row_model: &mut RowModel<'_, TData>) {
658    // TanStack Table v8 `getPaginationRowModel` rebuilds `flatRows` by recursively visiting each row
659    // from the paginated `rows` list and then traversing `subRows` unconditionally. When
660    // `paginateExpandedRows=false`, those paginated `rows` can already include expanded descendants,
661    // which results in duplicated `flatRows` entries.
662    let roots = row_model.root_rows.clone();
663    let mut flat = Vec::new();
664
665    fn push_flat<TData>(arena: &[Row<'_, TData>], out: &mut Vec<RowIndex>, row: RowIndex) {
666        out.push(row);
667        let Some(r) = arena.get(row) else {
668            return;
669        };
670        for &child in &r.sub_rows {
671            push_flat(arena, out, child);
672        }
673    }
674
675    for root in roots {
676        push_flat(&row_model.arena, &mut flat, root);
677    }
678
679    row_model.flat_rows = flat;
680}
681
682impl<'a, TData> Table<'a, TData> {
683    pub fn builder(data: &'a [TData]) -> TableBuilder<'a, TData> {
684        TableBuilder::new(data)
685    }
686
687    fn new(builder: TableBuilder<'a, TData>) -> Self {
688        fn push_leaf_columns<TData>(
689            cols: &[super::ColumnDef<TData>],
690            out: &mut Vec<super::ColumnDef<TData>>,
691        ) {
692            for col in cols {
693                if col.columns.is_empty() {
694                    out.push(col.clone());
695                } else {
696                    push_leaf_columns(&col.columns, out);
697                }
698            }
699        }
700
701        let get_row_key = builder
702            .get_row_key
703            .unwrap_or_else(|| Box::new(default_row_key_for_index_path));
704
705        let column_tree = builder.columns;
706        let mut columns: Vec<super::ColumnDef<TData>> = Vec::new();
707        push_leaf_columns(&column_tree, &mut columns);
708
709        // TanStack Table v8: `table.initialState` is derived from `options.initialState` and
710        // feature defaults. It does not implicitly mirror `options.state`.
711        let initial_state = builder.initial_state.clone().unwrap_or_default();
712
713        Self {
714            data: builder.data,
715            column_tree,
716            columns,
717            sorting_fns: builder.sorting_fns,
718            filter_fns: builder.filter_fns,
719            aggregation_fns: builder.aggregation_fns,
720            global_filter_fn: builder.global_filter_fn,
721            get_column_can_global_filter: builder.get_column_can_global_filter,
722            enable_row_pinning: builder.enable_row_pinning,
723            enable_row_selection: builder.enable_row_selection,
724            enable_multi_row_selection: builder.enable_multi_row_selection,
725            enable_sub_row_selection: builder.enable_sub_row_selection,
726            get_row_can_expand: builder.get_row_can_expand,
727            get_is_row_expanded: builder.get_is_row_expanded,
728            get_row_key,
729            get_row_id: builder.get_row_id,
730            get_sub_rows: builder.get_sub_rows,
731            get_grouped_row_model: builder.get_grouped_row_model,
732            get_global_faceted_row_model: builder.get_global_faceted_row_model,
733            get_global_faceted_unique_values: builder.get_global_faceted_unique_values,
734            get_global_faceted_min_max_u64: builder.get_global_faceted_min_max_u64,
735            filtered_row_model_override_pre_filtered: builder
736                .filtered_row_model_override_pre_filtered,
737            sorted_row_model_override_pre_sorted: builder.sorted_row_model_override_pre_sorted,
738            expanded_row_model_override_pre_expanded: builder
739                .expanded_row_model_override_pre_expanded,
740            pagination_row_model_override_pre_pagination: builder
741                .pagination_row_model_override_pre_pagination,
742            initial_state,
743            state: builder.state,
744            options: builder.options,
745            render_fallback_value: builder.render_fallback_value,
746            core_row_model: OnceCell::new(),
747            filtered_row_model: OnceCell::new(),
748            grouped_row_model: OnceCell::new(),
749            grouped_pre_sorted_row_model: OnceCell::new(),
750            grouped_sorted_row_model: OnceCell::new(),
751            grouped_u64_aggregations: OnceCell::new(),
752            grouped_any_aggregations: OnceCell::new(),
753            sorted_row_model: OnceCell::new(),
754            expanded_row_model: OnceCell::new(),
755            paginated_row_model: OnceCell::new(),
756            expanded_paginated_row_model: OnceCell::new(),
757            selected_row_model: OnceCell::new(),
758            filtered_selected_row_model: OnceCell::new(),
759            grouped_selected_row_model: OnceCell::new(),
760            page_selected_row_model: OnceCell::new(),
761            faceted_row_model_by_column: OnceCell::new(),
762            faceted_unique_values_by_column: OnceCell::new(),
763            faceted_unique_labels_by_column: OnceCell::new(),
764            faceted_min_max_u64_by_column: OnceCell::new(),
765            global_faceted_row_model: OnceCell::new(),
766            global_faceted_unique_values: OnceCell::new(),
767            global_faceted_min_max_u64: OnceCell::new(),
768        }
769    }
770
771    pub fn data(&self) -> &'a [TData] {
772        self.data
773    }
774
775    pub fn columns(&self) -> &[super::ColumnDef<TData>] {
776        &self.columns
777    }
778
779    /// TanStack-aligned: `options.renderFallbackValue` used by `cell.renderValue()`.
780    pub fn render_fallback_value(&self) -> &super::TanStackValue {
781        &self.render_fallback_value
782    }
783
784    pub fn column_tree(&self) -> &[super::ColumnDef<TData>] {
785        &self.column_tree
786    }
787
788    pub fn column(&self, id: &str) -> Option<&super::ColumnDef<TData>> {
789        self.columns.iter().find(|c| c.id.as_ref() == id)
790    }
791
792    fn tanstack_value_for_item(
793        &self,
794        col: &super::ColumnDef<TData>,
795        item: &TData,
796    ) -> super::TanStackValue {
797        if let Some(get) = col.sort_value.as_ref() {
798            return get(item);
799        }
800        if let Some(get) = col.value_u64_fn.as_ref() {
801            return super::TanStackValue::Number(get(item) as f64);
802        }
803        if let Some(get) = col.facet_key_fn.as_ref() {
804            return super::TanStackValue::Number(get(item) as f64);
805        }
806        if let Some(get) = col.facet_str_fn.as_ref() {
807            return super::TanStackValue::String(Arc::<str>::from(get(item)));
808        }
809        super::TanStackValue::Undefined
810    }
811
812    /// TanStack-aligned: `cell.getValue()` equivalent for the current row model.
813    ///
814    /// This returns a [`TanStackValue`] representation and currently uses the column's
815    /// `sort_value` accessor when available.
816    pub fn cell_value(&self, row_key: RowKey, column_id: &str) -> Option<super::TanStackValue> {
817        let col = self.column(column_id)?;
818        let core = self.core_row_model();
819        let index = core.row_by_key(row_key)?;
820        let row = core.row(index)?;
821        Some(self.tanstack_value_for_item(col, row.original))
822    }
823
824    /// TanStack-aligned: `cell.renderValue()` equivalent: `getValue() ?? renderFallbackValue`.
825    pub fn cell_render_value(
826        &self,
827        row_key: RowKey,
828        column_id: &str,
829    ) -> Option<super::TanStackValue> {
830        let value = self.cell_value(row_key, column_id)?;
831        match value {
832            super::TanStackValue::Undefined | super::TanStackValue::Null => {
833                Some(self.render_fallback_value.clone())
834            }
835            other => Some(other),
836        }
837    }
838
839    /// TanStack-aligned: `row.getUniqueValues(columnId)` for a leaf row in the core row model.
840    ///
841    /// Notes:
842    ///
843    /// - If `column.unique_values_fn` is configured, it is used (matches TanStack
844    ///   `columnDef.getUniqueValues`).
845    /// - Otherwise, returns a single-element array containing `getValue(columnId)`.
846    /// - If the column has no value source in the Rust engine, this returns `None`.
847    pub fn row_unique_values(
848        &self,
849        row_key: RowKey,
850        column_id: &str,
851    ) -> Option<Vec<super::TanStackValue>> {
852        let col = self.column(column_id)?;
853        let core = self.core_row_model();
854        let index = core.row_by_key(row_key)?;
855        let row = core.row(index)?;
856
857        if let Some(get_unique_values) = col.unique_values_fn.as_ref() {
858            return Some(get_unique_values(row.original, row.index));
859        }
860
861        let has_value_source = col.sort_value.is_some()
862            || col.value_u64_fn.is_some()
863            || col.facet_key_fn.is_some()
864            || col.facet_str_fn.is_some();
865        if !has_value_source {
866            return None;
867        }
868
869        Some(vec![self.tanstack_value_for_item(col, row.original)])
870    }
871
872    fn column_index(&self, id: &str) -> Option<usize> {
873        self.columns.iter().position(|c| c.id.as_ref() == id)
874    }
875
876    /// TanStack-aligned: resolve a row by id, optionally searching outside the current paginated
877    /// row model (e.g. pinned rows).
878    pub fn row(&self, row_key: RowKey, search_all: bool) -> Option<&Row<'a, TData>> {
879        let first = if search_all {
880            self.pre_pagination_row_model()
881        } else {
882            self.row_model()
883        };
884
885        first
886            .row_by_key(row_key)
887            .and_then(|i| first.row(i))
888            .or_else(|| {
889                let core = self.core_row_model();
890                core.row_by_key(row_key).and_then(|i| core.row(i))
891            })
892    }
893
894    /// TanStack-aligned: resolve a row by string id, optionally searching outside the current
895    /// paginated row model.
896    pub fn row_by_id(&self, row_id: &str, search_all: bool) -> Option<&Row<'a, TData>> {
897        let first = if search_all {
898            self.pre_pagination_row_model()
899        } else {
900            self.row_model()
901        };
902
903        first
904            .row_by_id(row_id)
905            .and_then(|i| first.row(i))
906            .or_else(|| {
907                let core = self.core_row_model();
908                core.row_by_id(row_id).and_then(|i| core.row(i))
909            })
910    }
911
912    pub fn row_key_for_id(&self, row_id: &str, search_all: bool) -> Option<RowKey> {
913        if let Some(row) = self.row_by_id(row_id, search_all) {
914            return Some(row.key);
915        }
916
917        if !self.state.grouping.is_empty() {
918            let grouped = self.grouped_row_model();
919            if let Some(i) = grouped.row_by_id(row_id) {
920                return grouped.row(i).map(|r| r.key);
921            }
922        }
923
924        None
925    }
926
927    pub fn row_id_for_key(&self, row_key: RowKey) -> Option<RowId> {
928        if !self.state.grouping.is_empty() && !self.options.manual_grouping {
929            let grouped = self.grouped_row_model();
930            if let Some(i) = grouped.row_by_key(row_key)
931                && let Some(row) = grouped.row(i)
932            {
933                return Some(row.id.clone());
934            }
935        }
936
937        let core = self.core_row_model();
938        let index = core.row_by_key(row_key)?;
939        let row = core.row(index)?;
940        Some(row.id.clone())
941    }
942
943    /// Rust-native equivalent of TanStack `cell.getContext()`.
944    pub fn cell_context(
945        &self,
946        row_key: RowKey,
947        column_id: &str,
948    ) -> Option<super::CellContextSnapshot> {
949        let row_id = self.row_id_for_key(row_key)?;
950        let col = self.column(column_id)?;
951        let id = Arc::<str>::from(format!("{}_{}", row_id.as_str(), col.id.as_ref()));
952        Some(super::CellContextSnapshot {
953            id,
954            row_id,
955            row_key,
956            column_id: col.id.clone(),
957        })
958    }
959
960    pub fn state(&self) -> &super::TableState {
961        &self.state
962    }
963
964    pub fn options(&self) -> super::TableOptions {
965        self.options
966    }
967
968    pub fn column_visibility(&self) -> &super::ColumnVisibilityState {
969        &self.state.column_visibility
970    }
971
972    pub fn is_column_visible(&self, column_id: &str) -> Option<bool> {
973        let col = self.column(column_id)?;
974
975        fn is_visible<TData>(
976            col: &super::ColumnDef<TData>,
977            visibility: &super::ColumnVisibilityState,
978        ) -> bool {
979            if !col.columns.is_empty() {
980                return col.columns.iter().any(|c| is_visible(c, visibility));
981            }
982
983            super::is_column_visible(visibility, &col.id)
984        }
985
986        Some(is_visible(col, &self.state.column_visibility))
987    }
988
989    pub fn column_can_hide(&self, column_id: &str) -> Option<bool> {
990        let col = self.column(column_id)?;
991        Some(self.options.enable_hiding && col.enable_hiding)
992    }
993
994    /// TanStack-aligned: `table.getAllFlatColumns()`.
995    ///
996    /// This returns the full column tree in a pre-order DFS flattening (`column`, then its
997    /// descendants). It does **not** apply `columnOrder` reordering, matching upstream semantics.
998    pub fn all_flat_columns(&self) -> Vec<&super::ColumnDef<TData>> {
999        fn push<'a, TData>(
1000            cols: &'a [super::ColumnDef<TData>],
1001            out: &mut Vec<&'a super::ColumnDef<TData>>,
1002        ) {
1003            for col in cols {
1004                out.push(col);
1005                if !col.columns.is_empty() {
1006                    push(col.columns.as_slice(), out);
1007                }
1008            }
1009        }
1010
1011        let mut out = Vec::new();
1012        push(self.column_tree.as_slice(), &mut out);
1013        out
1014    }
1015
1016    /// TanStack-aligned: `table.getVisibleFlatColumns()`.
1017    ///
1018    /// Visibility rules match TanStack:
1019    /// - leaf columns consult `state.column_visibility` (default visible),
1020    /// - group columns are visible if any descendant is visible.
1021    pub fn visible_flat_columns(&self) -> Vec<&super::ColumnDef<TData>> {
1022        fn is_visible<TData>(
1023            col: &super::ColumnDef<TData>,
1024            visibility: &super::ColumnVisibilityState,
1025        ) -> bool {
1026            if !col.columns.is_empty() {
1027                return col.columns.iter().any(|c| is_visible(c, visibility));
1028            }
1029
1030            super::is_column_visible(visibility, &col.id)
1031        }
1032
1033        fn push<'a, TData>(
1034            cols: &'a [super::ColumnDef<TData>],
1035            visibility: &super::ColumnVisibilityState,
1036            out: &mut Vec<&'a super::ColumnDef<TData>>,
1037        ) {
1038            for col in cols {
1039                if is_visible(col, visibility) {
1040                    out.push(col);
1041                }
1042                if !col.columns.is_empty() {
1043                    push(col.columns.as_slice(), visibility, out);
1044                }
1045            }
1046        }
1047
1048        let mut out = Vec::new();
1049        push(
1050            self.column_tree.as_slice(),
1051            &self.state.column_visibility,
1052            &mut out,
1053        );
1054        out
1055    }
1056
1057    pub fn hideable_columns(&self) -> Vec<&super::ColumnDef<TData>> {
1058        self.ordered_columns()
1059            .into_iter()
1060            .filter(|c| self.options.enable_hiding && c.enable_hiding)
1061            .collect()
1062    }
1063
1064    pub fn is_all_columns_visible(&self) -> bool {
1065        self.columns
1066            .iter()
1067            .all(|c| super::is_column_visible(&self.state.column_visibility, &c.id))
1068    }
1069
1070    pub fn is_some_columns_visible(&self) -> bool {
1071        self.columns
1072            .iter()
1073            .any(|c| super::is_column_visible(&self.state.column_visibility, &c.id))
1074    }
1075
1076    pub fn toggled_column_visibility(
1077        &self,
1078        column_id: &str,
1079        visible: Option<bool>,
1080    ) -> Option<super::ColumnVisibilityState> {
1081        let col = self.column(column_id)?;
1082        if !(self.options.enable_hiding && col.enable_hiding) {
1083            return Some(self.state.column_visibility.clone());
1084        }
1085        Some(super::toggled_column_visible(
1086            &self.state.column_visibility,
1087            &col.id,
1088            visible,
1089        ))
1090    }
1091
1092    pub fn toggled_all_columns_visible(
1093        &self,
1094        visible: Option<bool>,
1095    ) -> super::ColumnVisibilityState {
1096        let visible = visible.unwrap_or_else(|| !self.is_all_columns_visible());
1097
1098        let mut next = self.state.column_visibility.clone();
1099        for col in &self.columns {
1100            let can_hide = self.options.enable_hiding && col.enable_hiding;
1101            if visible {
1102                super::set_column_visible(&mut next, &col.id, true);
1103            } else {
1104                super::set_column_visible(&mut next, &col.id, !can_hide);
1105            }
1106        }
1107        next
1108    }
1109
1110    pub fn is_some_rows_expanded(&self) -> bool {
1111        super::is_some_rows_expanded(&self.state.expanding)
1112    }
1113
1114    pub fn toggled_all_rows_expanded(&self, value: Option<bool>) -> super::ExpandingState {
1115        let value = value.unwrap_or_else(|| !self.is_all_rows_expanded());
1116        let mut next = self.state.expanding.clone();
1117        super::set_all_rows_expanded(&mut next, value);
1118        next
1119    }
1120
1121    pub fn toggled_row_expanded(
1122        &self,
1123        row_key: RowKey,
1124        value: Option<bool>,
1125    ) -> super::ExpandingState {
1126        self.row_expanding_updater(row_key, value)
1127            .apply(&self.state.expanding)
1128    }
1129
1130    pub fn row_expanding_updater(
1131        &self,
1132        row_key: RowKey,
1133        value: Option<bool>,
1134    ) -> super::Updater<super::ExpandingState> {
1135        let visible_row_keys: Vec<RowKey> = if self.state.grouping.is_empty() {
1136            self.row_model().rows_by_key().keys().copied().collect()
1137        } else {
1138            let grouped = self.grouped_row_model();
1139            grouped
1140                .flat_rows()
1141                .iter()
1142                .filter_map(|&index| grouped.row(index).map(|row| row.key))
1143                .collect()
1144        };
1145        super::Updater::Func(Arc::new(move |old| {
1146            let mut next = old.clone();
1147            let exists = super::is_row_expanded(row_key, &next);
1148            let expanded_value = value.unwrap_or(!exists);
1149
1150            match &mut next {
1151                super::ExpandingState::All => {
1152                    if expanded_value {
1153                        return next;
1154                    }
1155
1156                    let mut keys: HashSet<RowKey> = visible_row_keys.iter().copied().collect();
1157                    keys.remove(&row_key);
1158                    next = super::ExpandingState::Keys(keys);
1159                }
1160                super::ExpandingState::Keys(keys) => {
1161                    if expanded_value {
1162                        keys.insert(row_key);
1163                    } else {
1164                        keys.remove(&row_key);
1165                    }
1166                }
1167            }
1168
1169            next
1170        }))
1171    }
1172
1173    pub fn row_expanding_updater_by_id(
1174        &self,
1175        row_id: &str,
1176        search_all: bool,
1177        value: Option<bool>,
1178    ) -> Option<super::Updater<super::ExpandingState>> {
1179        let row_key = self.row_key_for_id(row_id, search_all)?;
1180        Some(self.row_expanding_updater(row_key, value))
1181    }
1182
1183    pub fn toggled_row_expanded_by_id(
1184        &self,
1185        row_id: &str,
1186        search_all: bool,
1187        value: Option<bool>,
1188    ) -> Option<super::ExpandingState> {
1189        let updater = self.row_expanding_updater_by_id(row_id, search_all, value)?;
1190        Some(updater.apply(&self.state.expanding))
1191    }
1192
1193    /// TanStack-aligned: `autoResetAll ?? autoResetExpanded ?? !manualExpanding`.
1194    pub fn should_auto_reset_expanded(&self) -> bool {
1195        self.options
1196            .auto_reset_all
1197            .or(self.options.auto_reset_expanded)
1198            .unwrap_or(!self.options.manual_expanding)
1199    }
1200
1201    /// TanStack-aligned: `table.resetExpanded(defaultState?)`.
1202    pub fn reset_expanded(&self, default_state: bool) -> super::ExpandingState {
1203        if default_state {
1204            super::ExpandingState::default()
1205        } else {
1206            self.initial_state.expanding.clone()
1207        }
1208    }
1209
1210    /// TanStack-aligned: `autoResetAll ?? autoResetPageIndex ?? !manualPagination`.
1211    pub fn should_auto_reset_page_index(&self) -> bool {
1212        self.options
1213            .auto_reset_all
1214            .or(self.options.auto_reset_page_index)
1215            .unwrap_or(!self.options.manual_pagination)
1216    }
1217
1218    /// TanStack-aligned: `table.resetPageIndex(defaultState?)`.
1219    pub fn reset_page_index(&self, default_state: bool) -> super::PaginationState {
1220        let value = if default_state {
1221            0
1222        } else {
1223            self.initial_state.pagination.page_index as i32
1224        };
1225        self.set_page_index(value)
1226    }
1227
1228    /// TanStack-aligned: `table.resetPageSize(defaultState?)`.
1229    pub fn reset_page_size(&self, default_state: bool) -> super::PaginationState {
1230        let value = if default_state {
1231            10
1232        } else {
1233            self.initial_state.pagination.page_size as i32
1234        };
1235        self.set_page_size(value)
1236    }
1237
1238    /// TanStack-aligned: `table.resetPagination(defaultState?)`.
1239    pub fn reset_pagination(&self, default_state: bool) -> super::PaginationState {
1240        if default_state {
1241            super::PaginationState::default()
1242        } else {
1243            self.initial_state.pagination
1244        }
1245    }
1246
1247    /// TanStack-aligned: `table.setPageIndex(updater)`, with clamping based on `options.pageCount`.
1248    pub fn set_page_index(&self, page_index: i32) -> super::PaginationState {
1249        super::PaginationState {
1250            page_index: Self::clamp_page_index(page_index, self.options.page_count),
1251            page_size: self.state.pagination.page_size,
1252        }
1253    }
1254
1255    pub fn pagination_updater_set_page_index(
1256        &self,
1257        page_index: i32,
1258    ) -> super::Updater<super::PaginationState> {
1259        let page_count_hint = self.options.page_count;
1260        super::Updater::Func(Arc::new(move |old| super::PaginationState {
1261            page_index: Self::clamp_page_index(page_index, page_count_hint),
1262            page_size: old.page_size,
1263        }))
1264    }
1265
1266    /// TanStack-aligned: `table.setPageSize(updater)`.
1267    pub fn set_page_size(&self, page_size: i32) -> super::PaginationState {
1268        let page_size = (page_size as i64).max(1) as usize;
1269        let top_row_index = self
1270            .state
1271            .pagination
1272            .page_size
1273            .saturating_mul(self.state.pagination.page_index);
1274        let page_index = top_row_index / page_size;
1275        super::PaginationState {
1276            page_index,
1277            page_size,
1278        }
1279    }
1280
1281    pub fn pagination_updater_set_page_size(
1282        &self,
1283        page_size: i32,
1284    ) -> super::Updater<super::PaginationState> {
1285        super::Updater::Func(Arc::new(move |old| {
1286            let page_size = (page_size as i64).max(1) as usize;
1287            let top_row_index = old.page_size.saturating_mul(old.page_index);
1288            let page_index = top_row_index / page_size;
1289            super::PaginationState {
1290                page_index,
1291                page_size,
1292            }
1293        }))
1294    }
1295
1296    /// TanStack-aligned: `table.previousPage()`.
1297    pub fn previous_page(&self) -> super::PaginationState {
1298        let current = self.state.pagination.page_index as i32;
1299        self.set_page_index(current.saturating_sub(1))
1300    }
1301
1302    /// TanStack-aligned: `table.nextPage()`.
1303    pub fn next_page(&self) -> super::PaginationState {
1304        let current = self.state.pagination.page_index as i32;
1305        self.set_page_index(current.saturating_add(1))
1306    }
1307
1308    /// TanStack-aligned: `table.firstPage()`.
1309    pub fn first_page(&self) -> super::PaginationState {
1310        self.set_page_index(0)
1311    }
1312
1313    /// TanStack-aligned: `table.lastPage()`.
1314    pub fn last_page(&self) -> super::PaginationState {
1315        self.set_page_index(self.page_count().saturating_sub(1))
1316    }
1317
1318    /// TanStack-aligned: `table.getRowCount()`.
1319    pub fn row_count(&self) -> usize {
1320        self.options
1321            .row_count
1322            .unwrap_or_else(|| self.pre_pagination_row_model().root_rows().len())
1323    }
1324
1325    /// TanStack-aligned: `table.getPageCount()`.
1326    pub fn page_count(&self) -> i32 {
1327        if let Some(page_count) = self.options.page_count {
1328            return page_count;
1329        }
1330        let page_size = self.state.pagination.page_size;
1331        if page_size == 0 {
1332            return 0;
1333        }
1334        let count = self.row_count().div_ceil(page_size);
1335        i32::try_from(count).unwrap_or(i32::MAX)
1336    }
1337
1338    /// TanStack-aligned: `table.getCanPreviousPage()`.
1339    pub fn can_previous_page(&self) -> bool {
1340        self.state.pagination.page_index > 0
1341    }
1342
1343    /// TanStack-aligned: `table.getCanNextPage()`.
1344    pub fn can_next_page(&self) -> bool {
1345        let page_index = self.state.pagination.page_index;
1346        let page_count = self.page_count();
1347        if page_count == -1 {
1348            return true;
1349        }
1350        if page_count == 0 {
1351            return false;
1352        }
1353        let Ok(page_count) = usize::try_from(page_count) else {
1354            return false;
1355        };
1356        page_index + 1 < page_count
1357    }
1358
1359    /// TanStack-aligned: `table.getPageOptions()`.
1360    pub fn page_options(&self) -> Vec<usize> {
1361        let page_count = self.page_count();
1362        if page_count <= 0 {
1363            return Vec::new();
1364        }
1365        let Ok(page_count) = usize::try_from(page_count) else {
1366            return Vec::new();
1367        };
1368        (0..page_count).collect()
1369    }
1370
1371    fn clamp_page_index(page_index: i32, page_count_hint: Option<i32>) -> usize {
1372        let max = match page_count_hint {
1373            None => i32::MAX,
1374            Some(-1) => i32::MAX,
1375            Some(n) => n.saturating_sub(1),
1376        };
1377        if max < 0 {
1378            return 0;
1379        }
1380        page_index.max(0).min(max) as usize
1381    }
1382
1383    pub fn grouping(&self) -> &super::GroupingState {
1384        &self.state.grouping
1385    }
1386
1387    pub fn column_can_group(&self, column_id: &str) -> Option<bool> {
1388        let col = self.column(column_id)?;
1389        Some(super::column_can_group(self.options, col))
1390    }
1391
1392    pub fn is_column_grouped(&self, column_id: &str) -> Option<bool> {
1393        let col = self.column(column_id)?;
1394        Some(super::is_column_grouped(&self.state.grouping, &col.id))
1395    }
1396
1397    pub fn column_grouped_index(&self, column_id: &str) -> Option<usize> {
1398        let col = self.column(column_id)?;
1399        super::grouped_index(&self.state.grouping, &col.id)
1400    }
1401
1402    pub fn toggled_column_grouping(
1403        &self,
1404        column_id: &str,
1405        grouped: Option<bool>,
1406    ) -> Option<super::GroupingState> {
1407        let col = self.column(column_id)?;
1408        Some(super::toggled_column_grouping_value(
1409            &self.state.grouping,
1410            &col.id,
1411            grouped,
1412        ))
1413    }
1414
1415    /// TanStack-aligned: compute the `Updater` that a controlled `onGroupingChange` hook would
1416    /// receive for a "toggle this column" interaction.
1417    pub fn grouping_updater(
1418        &self,
1419        column_id: &str,
1420        grouped: Option<bool>,
1421    ) -> Option<super::Updater<super::GroupingState>> {
1422        let col = self.column(column_id)?;
1423        let id = col.id.clone();
1424        Some(super::Updater::Func(Arc::new(move |old| {
1425            super::toggled_column_grouping_value(old, &id, grouped)
1426        })))
1427    }
1428
1429    /// TanStack-aligned: compute the `Updater` that a controlled `onGroupingChange` hook would
1430    /// receive for a `getToggleGroupingHandler()` interaction.
1431    pub fn grouping_handler_updater(
1432        &self,
1433        column_id: &str,
1434    ) -> Option<super::Updater<super::GroupingState>> {
1435        let col = self.column(column_id)?;
1436        if !super::column_can_group(self.options, col) {
1437            return Some(super::Updater::Func(Arc::new(|old| old.clone())));
1438        }
1439        self.grouping_updater(column_id, None)
1440    }
1441
1442    pub fn pre_grouped_row_model(&self) -> &RowModel<'a, TData> {
1443        self.filtered_row_model()
1444    }
1445
1446    fn can_compute_grouped_row_model(&self) -> bool {
1447        if self.state.grouping.is_empty() {
1448            return false;
1449        }
1450
1451        self.state.grouping.iter().all(|column_id| {
1452            self.column(column_id.as_ref()).is_some_and(|column| {
1453                column.facet_key_fn.is_some() || column.facet_str_fn.is_some()
1454            })
1455        })
1456    }
1457
1458    pub fn grouped_row_model(&self) -> &super::GroupedRowModel {
1459        if self.options.manual_grouping
1460            || self.state.grouping.is_empty()
1461            || !self.can_compute_grouped_row_model()
1462        {
1463            return self
1464                .grouped_row_model
1465                .get_or_init(|| super::grouped_row_model_from_leaf(self.pre_grouped_row_model()));
1466        }
1467
1468        self.grouped_row_model.get_or_init(|| {
1469            if let Some(get_grouped_row_model) = self.get_grouped_row_model.as_ref() {
1470                return get_grouped_row_model(
1471                    self.pre_grouped_row_model(),
1472                    &self.columns,
1473                    &self.state.grouping,
1474                );
1475            }
1476
1477            super::group_row_model(
1478                self.pre_grouped_row_model(),
1479                &self.columns,
1480                &self.state.grouping,
1481            )
1482        })
1483    }
1484
1485    fn grouped_row_original(&self, grouped_row: &super::GroupedRow) -> Option<&'a TData> {
1486        let row_key = match &grouped_row.kind {
1487            super::GroupedRowKind::Leaf { row_key } => *row_key,
1488            super::GroupedRowKind::Group {
1489                first_leaf_row_key, ..
1490            } => *first_leaf_row_key,
1491        };
1492        let core = self.core_row_model();
1493        core.row_by_key(row_key)
1494            .and_then(|index| core.row(index).map(|row| row.original))
1495    }
1496
1497    fn grouped_row_index_in_core(&self, grouped_row: &super::GroupedRow, fallback: usize) -> usize {
1498        let row_key = match &grouped_row.kind {
1499            super::GroupedRowKind::Leaf { row_key } => *row_key,
1500            super::GroupedRowKind::Group {
1501                first_leaf_row_key, ..
1502            } => *first_leaf_row_key,
1503        };
1504        let core = self.core_row_model();
1505        core.row_by_key(row_key)
1506            .and_then(|index| core.row(index).map(|row| row.index))
1507            .unwrap_or(fallback)
1508    }
1509
1510    fn build_grouped_row_model_as_row_model(
1511        &self,
1512        sorting: &[super::SortSpec],
1513        preserve_grouped_flat_rows: bool,
1514    ) -> RowModel<'a, TData> {
1515        let grouped = self.grouped_row_model();
1516
1517        let mut row_model = RowModel {
1518            root_rows: Vec::new(),
1519            flat_rows: Vec::new(),
1520            rows_by_key: HashMap::new(),
1521            rows_by_id: HashMap::new(),
1522            arena: Vec::new(),
1523        };
1524
1525        if grouped.root_rows().is_empty() {
1526            return row_model;
1527        }
1528
1529        let mut root_grouped_rows: Vec<super::GroupedRowIndex> = grouped.root_rows().to_vec();
1530        let mut sorted_children_by_grouped_index: HashMap<
1531            super::GroupedRowIndex,
1532            Vec<super::GroupedRowIndex>,
1533        > = HashMap::new();
1534
1535        let mut row_index_by_key: HashMap<RowKey, usize> = HashMap::new();
1536        let core = self.core_row_model();
1537        for &index in core.flat_rows() {
1538            let Some(row) = core.row(index) else {
1539                continue;
1540            };
1541            row_index_by_key.entry(row.key).or_insert(row.index);
1542        }
1543
1544        if !sorting.is_empty() {
1545            super::sort_grouped_row_indices_in_place(
1546                grouped,
1547                root_grouped_rows.as_mut_slice(),
1548                sorting,
1549                self.columns.as_slice(),
1550                self.data,
1551                &row_index_by_key,
1552                self.grouped_u64_aggregations(),
1553                self.grouped_aggregations_any(),
1554            );
1555
1556            fn sort_children_recursive<TData>(
1557                table: &Table<'_, TData>,
1558                grouped: &super::GroupedRowModel,
1559                node: super::GroupedRowIndex,
1560                sorting: &[super::SortSpec],
1561                columns: &[super::ColumnDef<TData>],
1562                data: &[TData],
1563                row_index_by_key: &HashMap<RowKey, usize>,
1564                sorted_children_by_grouped_index: &mut HashMap<
1565                    super::GroupedRowIndex,
1566                    Vec<super::GroupedRowIndex>,
1567                >,
1568            ) {
1569                let Some(row) = grouped.row(node) else {
1570                    return;
1571                };
1572                if row.sub_rows.is_empty() {
1573                    return;
1574                }
1575
1576                let mut children = row.sub_rows.clone();
1577                super::sort_grouped_row_indices_in_place(
1578                    grouped,
1579                    children.as_mut_slice(),
1580                    sorting,
1581                    columns,
1582                    data,
1583                    row_index_by_key,
1584                    table.grouped_u64_aggregations(),
1585                    table.grouped_aggregations_any(),
1586                );
1587
1588                for &child in &children {
1589                    sort_children_recursive(
1590                        table,
1591                        grouped,
1592                        child,
1593                        sorting,
1594                        columns,
1595                        data,
1596                        row_index_by_key,
1597                        sorted_children_by_grouped_index,
1598                    );
1599                }
1600
1601                sorted_children_by_grouped_index.insert(node, children);
1602            }
1603
1604            for &root in &root_grouped_rows {
1605                sort_children_recursive(
1606                    self,
1607                    grouped,
1608                    root,
1609                    sorting,
1610                    self.columns.as_slice(),
1611                    self.data,
1612                    &row_index_by_key,
1613                    &mut sorted_children_by_grouped_index,
1614                );
1615            }
1616        }
1617
1618        let mut grouped_to_row_index: HashMap<super::GroupedRowIndex, RowIndex> = HashMap::new();
1619
1620        fn materialize_grouped_row<'a, TData>(
1621            table: &Table<'a, TData>,
1622            grouped: &super::GroupedRowModel,
1623            grouped_index: super::GroupedRowIndex,
1624            parent: Option<RowIndex>,
1625            parent_key: Option<RowKey>,
1626            sorted_children_by_grouped_index: &HashMap<
1627                super::GroupedRowIndex,
1628                Vec<super::GroupedRowIndex>,
1629            >,
1630            row_model: &mut RowModel<'a, TData>,
1631            grouped_to_row_index: &mut HashMap<super::GroupedRowIndex, RowIndex>,
1632        ) -> Option<RowIndex> {
1633            let grouped_row = grouped.row(grouped_index)?;
1634            let original = table.grouped_row_original(grouped_row)?;
1635            let depth = u16::try_from(grouped_row.depth).unwrap_or(u16::MAX);
1636            let index = match &grouped_row.kind {
1637                super::GroupedRowKind::Leaf { .. } => {
1638                    table.grouped_row_index_in_core(grouped_row, grouped_index)
1639                }
1640                super::GroupedRowKind::Group { .. } => grouped_index,
1641            };
1642
1643            let row_index = row_model.arena.len();
1644            row_model.arena.push(Row {
1645                id: grouped_row.id.clone(),
1646                key: grouped_row.key,
1647                original,
1648                index,
1649                depth,
1650                parent,
1651                parent_key,
1652                sub_rows: Vec::new(),
1653            });
1654
1655            row_model.rows_by_key.insert(grouped_row.key, row_index);
1656            row_model
1657                .rows_by_id
1658                .insert(grouped_row.id.clone(), row_index);
1659            grouped_to_row_index.insert(grouped_index, row_index);
1660
1661            if parent.is_none() {
1662                row_model.root_rows.push(row_index);
1663            }
1664
1665            row_model.flat_rows.push(row_index);
1666
1667            let grouped_children = sorted_children_by_grouped_index
1668                .get(&grouped_index)
1669                .map(|children| children.as_slice())
1670                .unwrap_or_else(|| grouped_row.sub_rows.as_slice());
1671
1672            let mut row_children: Vec<RowIndex> = Vec::with_capacity(grouped_children.len());
1673            for &child_grouped_index in grouped_children {
1674                let Some(child_row_index) = materialize_grouped_row(
1675                    table,
1676                    grouped,
1677                    child_grouped_index,
1678                    Some(row_index),
1679                    Some(grouped_row.key),
1680                    sorted_children_by_grouped_index,
1681                    row_model,
1682                    grouped_to_row_index,
1683                ) else {
1684                    continue;
1685                };
1686                row_children.push(child_row_index);
1687            }
1688
1689            if let Some(row) = row_model.arena.get_mut(row_index) {
1690                row.sub_rows = row_children;
1691            }
1692
1693            Some(row_index)
1694        }
1695
1696        for &root_grouped_index in &root_grouped_rows {
1697            let _ = materialize_grouped_row(
1698                self,
1699                grouped,
1700                root_grouped_index,
1701                None,
1702                None,
1703                &sorted_children_by_grouped_index,
1704                &mut row_model,
1705                &mut grouped_to_row_index,
1706            );
1707        }
1708
1709        if preserve_grouped_flat_rows {
1710            row_model.flat_rows.clear();
1711            for &grouped_index in grouped.flat_rows() {
1712                let Some(row_index) = grouped_to_row_index.get(&grouped_index).copied() else {
1713                    continue;
1714                };
1715                row_model.flat_rows.push(row_index);
1716            }
1717        }
1718
1719        row_model
1720    }
1721
1722    fn grouped_pre_sorted_row_model(&self) -> &RowModel<'a, TData> {
1723        self.grouped_pre_sorted_row_model
1724            .get_or_init(|| self.build_grouped_row_model_as_row_model(&[], true))
1725    }
1726
1727    fn grouped_sorted_row_model(&self) -> &RowModel<'a, TData> {
1728        if self.state.sorting.is_empty() {
1729            return self.grouped_pre_sorted_row_model();
1730        }
1731        self.grouped_sorted_row_model.get_or_init(|| {
1732            self.build_grouped_row_model_as_row_model(self.state.sorting.as_slice(), false)
1733        })
1734    }
1735
1736    pub fn grouped_u64_aggregations(&self) -> &HashMap<RowKey, Arc<[(super::ColumnId, u64)]>> {
1737        self.grouped_u64_aggregations.get_or_init(|| {
1738            if self.options.manual_grouping || self.state.grouping.is_empty() {
1739                return Default::default();
1740            }
1741
1742            let grouped = self.grouped_row_model();
1743
1744            let grouped_columns: std::collections::HashSet<&str> =
1745                self.state.grouping.iter().map(|c| c.as_ref()).collect();
1746
1747            let mut agg_columns: Vec<super::ColumnDef<TData>> = Vec::new();
1748            for col in &self.columns {
1749                if grouped_columns.contains(col.id.as_ref()) {
1750                    continue;
1751                }
1752
1753                let aggregation = if col.aggregation != super::Aggregation::None {
1754                    col.aggregation
1755                } else if col.value_u64_fn.is_some() || col.facet_key_fn.is_some() {
1756                    // TanStack default column def uses `aggregationFn: 'auto'`, which picks `sum`
1757                    // for numeric values.
1758                    super::Aggregation::SumU64
1759                } else {
1760                    continue;
1761                };
1762
1763                let mut next = col.clone();
1764                next.aggregation = aggregation;
1765                agg_columns.push(next);
1766            }
1767
1768            let agg_refs: Vec<&super::ColumnDef<TData>> = agg_columns.iter().collect();
1769            super::compute_grouped_u64_aggregations_from_core(
1770                grouped,
1771                self.core_row_model(),
1772                agg_refs.as_slice(),
1773            )
1774        })
1775    }
1776
1777    pub fn grouped_aggregations_any(
1778        &self,
1779    ) -> &HashMap<RowKey, Arc<[(super::ColumnId, super::TanStackValue)]>> {
1780        self.grouped_any_aggregations.get_or_init(|| {
1781            if self.options.manual_grouping || self.state.grouping.is_empty() {
1782                return Default::default();
1783            }
1784
1785            let grouped = self.grouped_row_model();
1786            if grouped.flat_rows().is_empty() {
1787                return Default::default();
1788            }
1789
1790            let grouped_columns: HashSet<&str> =
1791                self.state.grouping.iter().map(|c| c.as_ref()).collect();
1792
1793            let agg_columns: Vec<&super::ColumnDef<TData>> = self
1794                .columns
1795                .iter()
1796                .filter(|c| !grouped_columns.contains(c.id.as_ref()))
1797                .collect();
1798
1799            if agg_columns.is_empty() {
1800                return Default::default();
1801            }
1802
1803            fn leaf_row_keys(model: &super::GroupedRowModel, node: usize, out: &mut Vec<RowKey>) {
1804                let Some(row) = model.row(node) else {
1805                    return;
1806                };
1807                match &row.kind {
1808                    super::GroupedRowKind::Leaf { row_key } => out.push(*row_key),
1809                    super::GroupedRowKind::Group { .. } => {
1810                        for &child in &row.sub_rows {
1811                            leaf_row_keys(model, child, out);
1812                        }
1813                    }
1814                }
1815            }
1816
1817            let core = self.core_row_model();
1818            let mut visited: HashSet<RowKey> = Default::default();
1819            let mut out: HashMap<RowKey, Arc<[(super::ColumnId, super::TanStackValue)]>> =
1820                Default::default();
1821
1822            for &node in grouped.flat_rows() {
1823                let Some(row) = grouped.row(node) else {
1824                    continue;
1825                };
1826                if !matches!(row.kind, super::GroupedRowKind::Group { .. }) {
1827                    continue;
1828                }
1829                if !visited.insert(row.key) {
1830                    continue;
1831                }
1832
1833                let mut leaf_keys: Vec<RowKey> = Vec::new();
1834                leaf_row_keys(grouped, node, &mut leaf_keys);
1835
1836                let mut values: Vec<(super::ColumnId, super::TanStackValue)> = Vec::new();
1837
1838                for col in &agg_columns {
1839                    let mut leaf_values: Vec<super::TanStackValue> = Vec::new();
1840                    for key in &leaf_keys {
1841                        let Some(core_index) = core.row_by_key(*key) else {
1842                            continue;
1843                        };
1844                        let Some(item) = core.row(core_index).map(|r| r.original) else {
1845                            continue;
1846                        };
1847                        leaf_values.push(self.tanstack_value_for_item(col, item));
1848                    }
1849
1850                    let v = match &col.aggregation_fn {
1851                        super::AggregationFnSpec::None => super::TanStackValue::Undefined,
1852                        super::AggregationFnSpec::Auto => {
1853                            if let Some(builtin) = super::resolve_auto_aggregation(&leaf_values) {
1854                                super::apply_builtin_aggregation(builtin, &leaf_values)
1855                            } else {
1856                                super::TanStackValue::Undefined
1857                            }
1858                        }
1859                        super::AggregationFnSpec::BuiltIn(builtin) => {
1860                            super::apply_builtin_aggregation(*builtin, &leaf_values)
1861                        }
1862                        super::AggregationFnSpec::Named(key) => {
1863                            if let Some(custom) = self.aggregation_fns.get(key) {
1864                                custom(col.id.as_ref(), &leaf_values)
1865                            } else if let Some(builtin) =
1866                                super::BuiltInAggregationFn::from_tanstack_key(key.as_ref())
1867                            {
1868                                super::apply_builtin_aggregation(builtin, &leaf_values)
1869                            } else {
1870                                super::TanStackValue::Undefined
1871                            }
1872                        }
1873                    };
1874
1875                    values.push((col.id.clone(), v));
1876                }
1877
1878                out.insert(row.key, Arc::from(values.into_boxed_slice()));
1879            }
1880
1881            out
1882        })
1883    }
1884
1885    pub fn is_all_rows_expanded(&self) -> bool {
1886        match &self.state.expanding {
1887            super::ExpandingState::All => true,
1888            super::ExpandingState::Keys(keys) if keys.is_empty() => false,
1889            _ => {
1890                let model = self.row_model();
1891                model
1892                    .flat_rows()
1893                    .iter()
1894                    .filter_map(|&i| model.row(i))
1895                    .all(|r| self.row_is_expanded_for_row(r))
1896            }
1897        }
1898    }
1899
1900    fn row_is_expanded_for_row(&self, row: &Row<'a, TData>) -> bool {
1901        self.get_is_row_expanded
1902            .as_ref()
1903            .map(|f| f(row.key, row.original))
1904            .unwrap_or_else(|| super::is_row_expanded(row.key, &self.state.expanding))
1905    }
1906
1907    fn row_is_expanded_for_key_and_original(&self, row_key: RowKey, original: &TData) -> bool {
1908        self.get_is_row_expanded
1909            .as_ref()
1910            .map(|f| f(row_key, original))
1911            .unwrap_or_else(|| super::is_row_expanded(row_key, &self.state.expanding))
1912    }
1913
1914    fn row_can_expand_for_row(&self, row: &Row<'a, TData>) -> bool {
1915        self.get_row_can_expand
1916            .as_ref()
1917            .map(|f| f(row.key, row.original))
1918            .unwrap_or_else(|| self.options.enable_expanding && !row.sub_rows.is_empty())
1919    }
1920
1921    pub fn can_some_rows_expand(&self) -> bool {
1922        let model = self.pre_pagination_row_model();
1923        model
1924            .flat_rows()
1925            .iter()
1926            .filter_map(|&i| model.row(i))
1927            .any(|r| self.row_can_expand_for_row(r))
1928    }
1929
1930    pub fn expanded_depth(&self) -> u16 {
1931        super::expanded_depth(self.pre_expanded_row_model(), &self.state.expanding)
1932    }
1933
1934    pub fn row_can_expand(&self, row_key: RowKey) -> bool {
1935        let model = self.core_row_model();
1936        model
1937            .row_by_key(row_key)
1938            .and_then(|i| model.row(i))
1939            .is_some_and(|r| self.row_can_expand_for_row(r))
1940    }
1941
1942    pub fn row_is_all_parents_expanded(&self, row_key: RowKey) -> bool {
1943        let model = self.core_row_model();
1944        let Some(mut current) = model.row_by_key(row_key) else {
1945            return false;
1946        };
1947        loop {
1948            let Some(r) = model.row(current) else {
1949                return true;
1950            };
1951            let Some(parent) = r.parent else {
1952                return true;
1953            };
1954            let Some(parent_row) = model.row(parent) else {
1955                return true;
1956            };
1957            if !self.row_is_expanded_for_row(parent_row) {
1958                return false;
1959            }
1960            current = parent;
1961        }
1962    }
1963
1964    pub fn is_some_rows_pinned(&self, position: Option<super::RowPinPosition>) -> bool {
1965        super::is_some_rows_pinned(&self.state.row_pinning, position)
1966    }
1967
1968    /// TanStack-aligned: `table.resetRowPinning(defaultState?)`.
1969    pub fn reset_row_pinning(&self, default_state: bool) -> super::RowPinningState {
1970        if default_state {
1971            super::RowPinningState::default()
1972        } else {
1973            self.initial_state.row_pinning.clone()
1974        }
1975    }
1976
1977    pub fn is_some_columns_pinned(&self, position: Option<super::ColumnPinPosition>) -> bool {
1978        super::is_some_columns_pinned(&self.state.column_pinning, position)
1979    }
1980
1981    /// TanStack-aligned: `table.resetColumnPinning(defaultState?)`.
1982    pub fn reset_column_pinning(&self, default_state: bool) -> super::ColumnPinningState {
1983        if default_state {
1984            super::ColumnPinningState::default()
1985        } else {
1986            self.initial_state.column_pinning.clone()
1987        }
1988    }
1989
1990    /// TanStack-aligned: `table.resetRowSelection(defaultState?)`.
1991    pub fn reset_row_selection(&self, default_state: bool) -> super::RowSelectionState {
1992        if default_state {
1993            super::RowSelectionState::default()
1994        } else {
1995            self.initial_state.row_selection.clone()
1996        }
1997    }
1998
1999    /// TanStack-aligned: `table.resetSorting(defaultState?)`.
2000    pub fn reset_sorting(&self, default_state: bool) -> super::SortingState {
2001        if default_state {
2002            super::SortingState::default()
2003        } else {
2004            self.initial_state.sorting.clone()
2005        }
2006    }
2007
2008    /// Returns a stable, memoized ordering of the root row list after filtering + sorting.
2009    ///
2010    /// This helper exists to support "rebuild each frame" callers: they can rebuild a fresh `Table`
2011    /// while keeping a persistent memo cache (TanStack-style) outside the ephemeral instance.
2012    ///
2013    /// Notes:
2014    /// - The returned ordering is based on the **flat core row model** (no grouping, no sub-rows).
2015    /// - Cache invalidation is driven by `items_revision` plus a dependency snapshot derived from
2016    ///   `state`, `options`, and the configured column/filter/sort surfaces.
2017    pub fn tanstack_sorted_flat_row_order_with_cache(
2018        &self,
2019        items_revision: u64,
2020        cache: &mut super::TanStackSortedFlatRowOrderCache,
2021    ) -> (Arc<[super::FlatRowOrderEntry]>, bool) {
2022        let deps = super::TanStackSortedFlatRowOrderDeps {
2023            items_revision,
2024            data_len: self.data.len(),
2025            sorting: self.state.sorting.clone(),
2026            column_filters: self.state.column_filters.clone(),
2027            global_filter: self.state.global_filter.clone(),
2028            options: self.options,
2029            global_filter_fn: self.global_filter_fn.clone(),
2030            has_get_column_can_global_filter: self.get_column_can_global_filter.is_some(),
2031        };
2032
2033        let (order, recomputed) = cache.sorted_order(
2034            self.data,
2035            &self.columns,
2036            self.get_row_key.as_ref(),
2037            &self.filter_fns,
2038            &self.sorting_fns,
2039            self.get_column_can_global_filter.as_deref(),
2040            deps,
2041        );
2042        (order.clone(), recomputed)
2043    }
2044
2045    /// Returns the final **ungrouped** row-model ordering (rows + flatRows) with a persistent
2046    /// external memo cache.
2047    ///
2048    /// This helper exists to support "rebuild each frame" callers: they can rebuild a fresh
2049    /// `Table` every frame while keeping a persistent memo cache (TanStack-style) outside the
2050    /// ephemeral instance.
2051    ///
2052    /// Returns `None` when grouping is active, since grouped row models contain non-core rows (group
2053    /// headers) whose stable-key strategy is a separate concern.
2054    pub fn tanstack_ungrouped_row_model_order_with_cache(
2055        &self,
2056        items_revision: u64,
2057        cache: &mut super::TanStackUngroupedRowModelOrderCache,
2058    ) -> Option<(Arc<super::TanStackRowModelOrderSnapshot>, bool)> {
2059        if !self.state.grouping.is_empty() && !self.options.manual_grouping {
2060            return None;
2061        }
2062
2063        let deps = super::TanStackUngroupedRowModelOrderDeps {
2064            items_revision,
2065            data_len: self.data.len(),
2066            sorting: self.state.sorting.clone(),
2067            column_filters: self.state.column_filters.clone(),
2068            global_filter: self.state.global_filter.clone(),
2069            expanding: self.state.expanding.clone(),
2070            pagination: self.state.pagination,
2071            options: self.options,
2072            global_filter_fn: self.global_filter_fn.clone(),
2073            has_get_column_can_global_filter: self.get_column_can_global_filter.is_some(),
2074        };
2075
2076        debug_assert_eq!(deps.data_len, self.data.len());
2077        debug_assert_eq!(
2078            deps.has_get_column_can_global_filter,
2079            self.get_column_can_global_filter.is_some()
2080        );
2081
2082        let signature = super::tanstack_memo::columns_signature(&self.columns);
2083        let (value, recomputed) = cache.ungrouped_order(signature, deps, || {
2084            let model = self.row_model();
2085            let rows: Vec<super::RowKey> = model
2086                .root_rows()
2087                .iter()
2088                .filter_map(|&i| model.row(i).map(|r| r.key))
2089                .collect();
2090            let flat_rows: Vec<super::RowKey> = model
2091                .flat_rows()
2092                .iter()
2093                .filter_map(|&i| model.row(i).map(|r| r.key))
2094                .collect();
2095            super::TanStackRowModelOrderSnapshot {
2096                rows: Arc::from(rows.into_boxed_slice()),
2097                flat_rows: Arc::from(flat_rows.into_boxed_slice()),
2098            }
2099        });
2100
2101        Some((value.clone(), recomputed))
2102    }
2103
2104    /// TanStack-aligned: `column.getCanSort()`.
2105    pub fn column_can_sort(&self, column_id: &str) -> Option<bool> {
2106        let col = self.column(column_id)?;
2107        let has_sort_value_source = col.sort_cmp.is_some() || col.sort_value.is_some();
2108        Some(self.options.enable_sorting && col.enable_sorting && has_sort_value_source)
2109    }
2110
2111    /// TanStack-aligned: `column.getCanMultiSort()`.
2112    pub fn column_can_multi_sort(&self, column_id: &str) -> Option<bool> {
2113        let col = self.column(column_id)?;
2114        let has_sort_value_source = col.sort_cmp.is_some() || col.sort_value.is_some();
2115
2116        // TanStack: `columnDef.enableMultiSort ?? table.options.enableMultiSort ?? !!accessorFn`
2117        // We treat “accessorFn exists” as “has a sort value source”.
2118        if has_sort_value_source {
2119            Some(self.options.enable_multi_sort && col.enable_multi_sort)
2120        } else {
2121            Some(false)
2122        }
2123    }
2124
2125    /// TanStack-aligned: `column.getAutoSortDir()`.
2126    ///
2127    /// Returns `true` for `desc` and `false` for `asc`.
2128    pub fn column_auto_sort_dir_desc_tanstack(&self, column_id: &str) -> Option<bool> {
2129        let col = self.column(column_id)?;
2130
2131        // Upstream uses `firstRow.getValue(column.id)` (accessorFn). In the Rust engine, we only
2132        // have a stable “getValue” equivalent when the column provides `sort_value_by`.
2133        //
2134        // When `sort_value_by` is unavailable, we fall back to `desc` (matching upstream’s
2135        // empty-row behavior where the first value is `undefined` and `typeof undefined !== "string"`).
2136        let Some(get_value) = col.sort_value.as_ref() else {
2137            return Some(true);
2138        };
2139
2140        let model = self.filtered_row_model();
2141        let first_value = model
2142            .flat_rows()
2143            .first()
2144            .and_then(|&i| model.row(i))
2145            .map(|r| (get_value)(r.original));
2146
2147        let desc = !matches!(first_value, Some(super::TanStackValue::String(_)));
2148        Some(desc)
2149    }
2150
2151    /// TanStack-aligned: `column.getFirstSortDir()`.
2152    ///
2153    /// Returns `true` for `desc` and `false` for `asc`. Returns `None` when we cannot infer the
2154    /// direction (because the column has neither an explicit `sortDescFirst` override nor a stable
2155    /// `sort_value_by` surface for auto inference).
2156    pub fn column_first_sort_dir_desc_tanstack(&self, column_id: &str) -> Option<bool> {
2157        let col = self.column(column_id)?;
2158
2159        // Upstream:
2160        // `sortDescFirst = columnDef.sortDescFirst ?? table.options.sortDescFirst ?? (getAutoSortDir() === 'desc')`
2161        if let Some(explicit) = col.sort_desc_first.or(self.options.sort_desc_first) {
2162            return Some(explicit);
2163        }
2164
2165        self.column_auto_sort_dir_desc_tanstack(column_id)
2166    }
2167
2168    /// TanStack-aligned: `column.getNextSortingOrder(multi?)`.
2169    ///
2170    /// Returns:
2171    /// - `Some(Some(true))`  => `'desc'`
2172    /// - `Some(Some(false))` => `'asc'`
2173    /// - `Some(None)`        => `false` (remove)
2174    /// - `None`              => unknown column id
2175    pub fn column_next_sorting_order_desc_tanstack(
2176        &self,
2177        column_id: &str,
2178        multi: bool,
2179    ) -> Option<Option<bool>> {
2180        let first_desc = self.column_first_sort_dir_desc_tanstack(column_id)?;
2181        let is_sorted = super::sort_for_column(&self.state.sorting, column_id);
2182
2183        let enable_sorting_removal = self.options.enable_sorting_removal;
2184        let enable_multi_remove = self.options.enable_multi_remove;
2185
2186        match is_sorted {
2187            None => Some(Some(first_desc)),
2188            Some(current_desc) => {
2189                if current_desc != first_desc
2190                    && enable_sorting_removal
2191                    && (!multi || enable_multi_remove)
2192                {
2193                    return Some(None);
2194                }
2195                Some(Some(!current_desc))
2196            }
2197        }
2198    }
2199
2200    /// TanStack-aligned: `column.clearSorting()`.
2201    pub fn cleared_column_sorting(&self, column_id: &str) -> Option<super::SortingState> {
2202        self.column(column_id)?;
2203
2204        let mut next = self.state.sorting.clone();
2205        next.retain(|spec| spec.column.as_ref() != column_id);
2206        Some(next)
2207    }
2208
2209    /// TanStack-aligned: `column.getIsSorted()` (boolean form).
2210    pub fn column_is_sorted(&self, column_id: &str) -> Option<bool> {
2211        self.column(column_id)?;
2212        Some(
2213            self.state
2214                .sorting
2215                .iter()
2216                .any(|s| s.column.as_ref() == column_id),
2217        )
2218    }
2219
2220    /// TanStack-aligned: `column.getSortIndex()`.
2221    ///
2222    /// Returns `-1` when the column is not currently sorted. Returns `None` when the column id
2223    /// does not exist.
2224    pub fn column_sort_index(&self, column_id: &str) -> Option<i32> {
2225        self.column(column_id)?;
2226        Some(
2227            self.state
2228                .sorting
2229                .iter()
2230                .position(|s| s.column.as_ref() == column_id)
2231                .map(|i| i as i32)
2232                .unwrap_or(-1),
2233        )
2234    }
2235
2236    /// TanStack-aligned sorting state transition for `column.toggleSorting(...)`.
2237    ///
2238    /// This models the "direct toggle" policy (see `sorting.rs::toggle_sorting_tanstack`).
2239    pub fn sorting_updater_tanstack(
2240        &self,
2241        column_id: &str,
2242        multi: bool,
2243        auto_sort_dir_desc: bool,
2244    ) -> Option<super::Updater<super::SortingState>> {
2245        let col = self.column(column_id)?;
2246        let toggle_col = super::sorting::SortToggleColumn {
2247            id: col.id.clone(),
2248            enable_sorting: col.enable_sorting,
2249            enable_multi_sort: col.enable_multi_sort,
2250            sort_desc_first: col.sort_desc_first,
2251            has_sort_value_source: col.sort_cmp.is_some() || col.sort_value.is_some(),
2252        };
2253        let options = self.options;
2254        Some(super::Updater::Func(Arc::new(move |old| {
2255            let mut next = old.clone();
2256            super::sorting::toggle_sorting_state_tanstack(
2257                &mut next,
2258                &toggle_col,
2259                options,
2260                multi,
2261                auto_sort_dir_desc,
2262            );
2263            next
2264        })))
2265    }
2266
2267    /// TanStack-aligned sorting state transition for `column.getToggleSortingHandler()`.
2268    ///
2269    /// This models the "handler" policy (including `getCanSort` gating), aligning with
2270    /// `sorting.rs::toggle_sorting_handler_tanstack`.
2271    pub fn sorting_handler_updater_tanstack(
2272        &self,
2273        column_id: &str,
2274        event_multi: bool,
2275        auto_sort_dir_desc: bool,
2276    ) -> Option<super::Updater<super::SortingState>> {
2277        let col = self.column(column_id)?;
2278        let toggle_col = super::sorting::SortToggleColumn {
2279            id: col.id.clone(),
2280            enable_sorting: col.enable_sorting,
2281            enable_multi_sort: col.enable_multi_sort,
2282            sort_desc_first: col.sort_desc_first,
2283            has_sort_value_source: col.sort_cmp.is_some() || col.sort_value.is_some(),
2284        };
2285        let options = self.options;
2286        Some(super::Updater::Func(Arc::new(move |old| {
2287            let mut next = old.clone();
2288            super::sorting::toggle_sorting_state_handler_tanstack(
2289                &mut next,
2290                &toggle_col,
2291                options,
2292                event_multi,
2293                auto_sort_dir_desc,
2294            );
2295            next
2296        })))
2297    }
2298
2299    /// Convenience: apply [`Self::sorting_updater_tanstack`] to the current sorting state.
2300    pub fn toggled_column_sorting_tanstack(
2301        &self,
2302        column_id: &str,
2303        multi: bool,
2304        auto_sort_dir_desc: bool,
2305    ) -> Option<super::SortingState> {
2306        let updater = self.sorting_updater_tanstack(column_id, multi, auto_sort_dir_desc)?;
2307        Some(updater.apply(&self.state.sorting))
2308    }
2309
2310    /// Convenience: apply [`Self::sorting_handler_updater_tanstack`] to the current sorting state.
2311    pub fn toggled_column_sorting_handler_tanstack(
2312        &self,
2313        column_id: &str,
2314        event_multi: bool,
2315        auto_sort_dir_desc: bool,
2316    ) -> Option<super::SortingState> {
2317        let updater =
2318            self.sorting_handler_updater_tanstack(column_id, event_multi, auto_sort_dir_desc)?;
2319        Some(updater.apply(&self.state.sorting))
2320    }
2321
2322    /// TanStack-aligned: `table.resetColumnFilters(defaultState?)`.
2323    pub fn reset_column_filters(&self, default_state: bool) -> super::ColumnFiltersState {
2324        if default_state {
2325            super::ColumnFiltersState::default()
2326        } else {
2327            self.initial_state.column_filters.clone()
2328        }
2329    }
2330
2331    /// TanStack-aligned: `table.resetGlobalFilter(defaultState?)`.
2332    pub fn reset_global_filter(&self, default_state: bool) -> super::GlobalFilterState {
2333        if default_state {
2334            super::GlobalFilterState::default()
2335        } else {
2336            self.initial_state.global_filter.clone()
2337        }
2338    }
2339
2340    pub fn column_can_filter(&self, column_id: &str) -> Option<bool> {
2341        fn column_has_filter_value_source<TData>(col: &super::ColumnDef<TData>) -> bool {
2342            if col.filter_fn.is_some() || col.filter_fn_with_meta.is_some() {
2343                return true;
2344            }
2345            col.filtering_fn.is_some() && col.sort_value.is_some()
2346        }
2347
2348        let col = self.column(column_id)?;
2349        Some(
2350            self.options.enable_filters
2351                && self.options.enable_column_filters
2352                && col.enable_column_filter
2353                && column_has_filter_value_source(col),
2354        )
2355    }
2356
2357    pub fn column_filter_value(&self, column_id: &str) -> Option<&serde_json::Value> {
2358        self.column(column_id)?;
2359        self.state
2360            .column_filters
2361            .iter()
2362            .find(|f| f.column.as_ref() == column_id)
2363            .map(|f| &f.value)
2364    }
2365
2366    pub fn column_is_filtered(&self, column_id: &str) -> Option<bool> {
2367        self.column(column_id)?;
2368        Some(
2369            self.state
2370                .column_filters
2371                .iter()
2372                .any(|f| f.column.as_ref() == column_id),
2373        )
2374    }
2375
2376    /// TanStack-aligned: `column.getFilterIndex()`.
2377    ///
2378    /// Returns `-1` when the column is not currently filtered. Returns `None` when the column id
2379    /// does not exist.
2380    pub fn column_filter_index(&self, column_id: &str) -> Option<i32> {
2381        self.column(column_id)?;
2382        Some(
2383            self.state
2384                .column_filters
2385                .iter()
2386                .position(|f| f.column.as_ref() == column_id)
2387                .map(|i| i as i32)
2388                .unwrap_or(-1),
2389        )
2390    }
2391
2392    /// TanStack-aligned: `column.setFilterValue(value)` as a state updater.
2393    pub fn column_filters_updater_set_value(
2394        &self,
2395        column_id: &str,
2396        value: serde_json::Value,
2397    ) -> Option<super::Updater<super::ColumnFiltersState>> {
2398        let col = self.column(column_id)?;
2399        Some(super::filtering::column_filters_updater_set_value_tanstack(
2400            self.data,
2401            col,
2402            &self.filter_fns,
2403            value,
2404        ))
2405    }
2406
2407    /// TanStack-aligned: `table.setGlobalFilter(value)` as a state updater.
2408    pub fn global_filter_updater_set_value(
2409        &self,
2410        value: super::GlobalFilterState,
2411    ) -> super::Updater<super::GlobalFilterState> {
2412        super::filtering::global_filter_updater_set_value_tanstack(value)
2413    }
2414
2415    /// TanStack-aligned: `column.getCanGlobalFilter()`.
2416    pub fn column_can_global_filter(&self, column_id: &str) -> Option<bool> {
2417        let col = self.column(column_id)?;
2418        if !(self.options.enable_filters
2419            && self.options.enable_global_filter
2420            && col.enable_global_filter)
2421        {
2422            return Some(false);
2423        }
2424
2425        let first_row_original = self
2426            .pre_filtered_row_model()
2427            .flat_rows()
2428            .first()
2429            .and_then(|&i| self.pre_filtered_row_model().row(i))
2430            .map(|r| r.original);
2431
2432        let can_global_filter = match self.get_column_can_global_filter.as_deref() {
2433            Some(f) => first_row_original.is_some_and(|first| f(col, first)),
2434            None => match first_row_original {
2435                Some(first) => match col.sort_value.as_ref() {
2436                    Some(get_value) => matches!(
2437                        (get_value)(first),
2438                        super::TanStackValue::String(_) | super::TanStackValue::Number(_)
2439                    ),
2440                    None => col.filter_fn.is_some() || col.filter_fn_with_meta.is_some(),
2441                },
2442                None => false,
2443            },
2444        };
2445
2446        Some(can_global_filter)
2447    }
2448
2449    /// TanStack-aligned: `table.resetGrouping(defaultState?)`.
2450    pub fn reset_grouping(&self, default_state: bool) -> super::GroupingState {
2451        if default_state {
2452            super::GroupingState::default()
2453        } else {
2454            self.initial_state.grouping.clone()
2455        }
2456    }
2457
2458    /// TanStack-aligned: `table.resetColumnVisibility(defaultState?)`.
2459    pub fn reset_column_visibility(&self, default_state: bool) -> super::ColumnVisibilityState {
2460        if default_state {
2461            super::ColumnVisibilityState::default()
2462        } else {
2463            self.initial_state.column_visibility.clone()
2464        }
2465    }
2466
2467    /// TanStack-aligned: `table.resetColumnOrder(defaultState?)`.
2468    pub fn reset_column_order(&self, default_state: bool) -> super::ColumnOrderState {
2469        if default_state {
2470            super::ColumnOrderState::default()
2471        } else {
2472            self.initial_state.column_order.clone()
2473        }
2474    }
2475
2476    pub fn row_is_pinned(&self, row_key: RowKey) -> Option<super::RowPinPosition> {
2477        super::is_row_pinned(row_key, &self.state.row_pinning)
2478    }
2479
2480    /// TanStack-aligned: `row.getPinnedIndex()`.
2481    ///
2482    /// Returns `-1` when the row is not currently visible in its pinned region. Returns `None`
2483    /// when the row key does not exist.
2484    pub fn row_pinned_index(&self, row_key: RowKey) -> Option<i32> {
2485        let exists = if !self.state.grouping.is_empty() && !self.options.manual_grouping {
2486            let grouped = self.grouped_row_model();
2487            grouped.row_by_key(row_key).is_some()
2488                || self.core_row_model().row_by_key(row_key).is_some()
2489        } else {
2490            self.core_row_model().row_by_key(row_key).is_some()
2491        };
2492        if !exists {
2493            return None;
2494        }
2495
2496        let Some(position) = self.row_is_pinned(row_key) else {
2497            return Some(-1);
2498        };
2499
2500        let keys = match position {
2501            super::RowPinPosition::Top => self.top_row_keys(),
2502            super::RowPinPosition::Bottom => self.bottom_row_keys(),
2503        };
2504        Some(
2505            keys.iter()
2506                .position(|k| *k == row_key)
2507                .map(|i| i as i32)
2508                .unwrap_or(-1),
2509        )
2510    }
2511
2512    pub fn row_can_pin(&self, row_key: RowKey) -> Option<bool> {
2513        let core = self.core_row_model();
2514
2515        if let Some(index) = core.row_by_key(row_key) {
2516            let row = core.row(index)?;
2517            if let Some(enable_row_pinning) = self.enable_row_pinning.as_ref() {
2518                return Some(enable_row_pinning(row_key, row.original));
2519            }
2520            return Some(self.options.enable_row_pinning);
2521        }
2522
2523        if !self.state.grouping.is_empty() && !self.options.manual_grouping {
2524            let grouped = self.grouped_row_model();
2525            let index = grouped.row_by_key(row_key)?;
2526            let row = grouped.row(index)?;
2527            if let Some(enable_row_pinning) = self.enable_row_pinning.as_ref() {
2528                let first_leaf_key = match row.kind {
2529                    super::GroupedRowKind::Group {
2530                        first_leaf_row_key, ..
2531                    } => first_leaf_row_key,
2532                    super::GroupedRowKind::Leaf { row_key } => row_key,
2533                };
2534                let leaf_index = core.row_by_key(first_leaf_key)?;
2535                let leaf_row = core.row(leaf_index)?;
2536                return Some(enable_row_pinning(row_key, leaf_row.original));
2537            }
2538            return Some(self.options.enable_row_pinning);
2539        }
2540
2541        None
2542    }
2543
2544    pub fn row_pinning_updater(
2545        &self,
2546        row_key: RowKey,
2547        position: Option<super::RowPinPosition>,
2548        include_leaf_rows: bool,
2549        include_parent_rows: bool,
2550    ) -> super::Updater<super::RowPinningState> {
2551        fn pin_grouped_row_keys(
2552            grouped: &super::GroupedRowModel,
2553            row_key: RowKey,
2554            include_leaf_rows: bool,
2555            include_parent_rows: bool,
2556        ) -> Vec<RowKey> {
2557            let Some(row_index) = grouped.row_by_key(row_key) else {
2558                return vec![row_key];
2559            };
2560
2561            let mut keys: Vec<RowKey> = Vec::new();
2562
2563            if include_parent_rows {
2564                let mut parents_rev: Vec<RowKey> = Vec::new();
2565                let mut current = grouped.row(row_index);
2566                while let Some(row) = current {
2567                    let Some(parent) = row.parent else {
2568                        break;
2569                    };
2570                    let Some(parent_row) = grouped.row(parent) else {
2571                        break;
2572                    };
2573                    parents_rev.push(parent_row.key);
2574                    current = Some(parent_row);
2575                }
2576                parents_rev.reverse();
2577                keys.extend(parents_rev);
2578            }
2579
2580            keys.push(row_key);
2581
2582            if include_leaf_rows {
2583                fn push_descendant_keys(
2584                    grouped: &super::GroupedRowModel,
2585                    row: usize,
2586                    out: &mut Vec<RowKey>,
2587                ) {
2588                    let Some(r) = grouped.row(row) else {
2589                        return;
2590                    };
2591                    for &child in &r.sub_rows {
2592                        let Some(child_row) = grouped.row(child) else {
2593                            continue;
2594                        };
2595                        out.push(child_row.key);
2596                        push_descendant_keys(grouped, child, out);
2597                    }
2598                }
2599
2600                push_descendant_keys(grouped, row_index, &mut keys);
2601            }
2602
2603            let mut deduped: Vec<RowKey> = Vec::with_capacity(keys.len());
2604            let mut seen: HashSet<RowKey> = HashSet::new();
2605            for key in keys {
2606                if seen.insert(key) {
2607                    deduped.push(key);
2608                }
2609            }
2610
2611            deduped
2612        }
2613
2614        let keys = if self.state.grouping.is_empty() {
2615            super::pin_row_keys(
2616                self.core_row_model(),
2617                row_key,
2618                include_leaf_rows,
2619                include_parent_rows,
2620            )
2621        } else {
2622            let grouped = self.grouped_row_model();
2623            if grouped.row_by_key(row_key).is_some() {
2624                pin_grouped_row_keys(grouped, row_key, include_leaf_rows, include_parent_rows)
2625            } else {
2626                super::pin_row_keys(
2627                    self.core_row_model(),
2628                    row_key,
2629                    include_leaf_rows,
2630                    include_parent_rows,
2631                )
2632            }
2633        };
2634
2635        super::Updater::Func(Arc::new(move |old| {
2636            let mut next = old.clone();
2637            super::pin_rows(&mut next, position, keys.clone());
2638            next
2639        }))
2640    }
2641
2642    pub fn row_pinning_updater_by_id(
2643        &self,
2644        row_id: &str,
2645        search_all: bool,
2646        position: Option<super::RowPinPosition>,
2647        include_leaf_rows: bool,
2648        include_parent_rows: bool,
2649    ) -> Option<super::Updater<super::RowPinningState>> {
2650        let row_key = self.row_key_for_id(row_id, search_all)?;
2651        Some(self.row_pinning_updater(row_key, position, include_leaf_rows, include_parent_rows))
2652    }
2653
2654    pub fn top_row_keys(&self) -> Vec<RowKey> {
2655        self.pinned_row_keys(super::RowPinPosition::Top)
2656    }
2657
2658    pub fn top_row_ids(&self) -> Vec<RowId> {
2659        self.top_row_keys()
2660            .into_iter()
2661            .filter_map(|k| self.row_id_for_key(k))
2662            .collect()
2663    }
2664
2665    pub fn bottom_row_keys(&self) -> Vec<RowKey> {
2666        self.pinned_row_keys(super::RowPinPosition::Bottom)
2667    }
2668
2669    pub fn bottom_row_ids(&self) -> Vec<RowId> {
2670        self.bottom_row_keys()
2671            .into_iter()
2672            .filter_map(|k| self.row_id_for_key(k))
2673            .collect()
2674    }
2675
2676    pub fn center_row_keys(&self) -> Vec<RowKey> {
2677        if self.state.grouping.is_empty() || self.options.manual_grouping {
2678            let model = self.row_model();
2679            return super::center_row_keys(model.root_rows(), model, &self.state.row_pinning);
2680        }
2681
2682        let grouped = self.grouped_row_model();
2683        let mut roots: Vec<super::GroupedRowIndex> = grouped.root_rows().to_vec();
2684
2685        let core = self.core_row_model();
2686        let mut row_index_by_key: HashMap<RowKey, usize> = HashMap::new();
2687        for &index in core.flat_rows() {
2688            let Some(row) = core.row(index) else {
2689                continue;
2690            };
2691            row_index_by_key.entry(row.key).or_insert(row.index);
2692        }
2693
2694        super::sort_grouped_row_indices_in_place(
2695            grouped,
2696            &mut roots,
2697            self.state.sorting.as_slice(),
2698            self.columns.as_slice(),
2699            self.data,
2700            &row_index_by_key,
2701            self.grouped_u64_aggregations(),
2702            self.grouped_aggregations_any(),
2703        );
2704
2705        let visible_roots: &[super::GroupedRowIndex] = if self.options.manual_pagination {
2706            roots.as_slice()
2707        } else if self.state.pagination.page_size == 0 {
2708            &[]
2709        } else {
2710            let page_start = self
2711                .state
2712                .pagination
2713                .page_index
2714                .saturating_mul(self.state.pagination.page_size);
2715            let page_end = page_start.saturating_add(self.state.pagination.page_size);
2716            let start = page_start.min(roots.len());
2717            let end = page_end.min(roots.len());
2718            roots.get(start..end).unwrap_or_default()
2719        };
2720
2721        let mut pinned: HashSet<RowKey> = HashSet::new();
2722        pinned.extend(self.state.row_pinning.top.iter().copied());
2723        pinned.extend(self.state.row_pinning.bottom.iter().copied());
2724
2725        visible_roots
2726            .iter()
2727            .filter_map(|&i| grouped.row(i))
2728            .map(|row| row.key)
2729            .filter(|key| !pinned.contains(key))
2730            .collect()
2731    }
2732
2733    pub fn center_row_ids(&self) -> Vec<RowId> {
2734        self.center_row_keys()
2735            .into_iter()
2736            .filter_map(|k| self.row_id_for_key(k))
2737            .collect()
2738    }
2739
2740    fn pinned_row_keys(&self, position: super::RowPinPosition) -> Vec<RowKey> {
2741        let keys = match position {
2742            super::RowPinPosition::Top => self.state.row_pinning.top.as_slice(),
2743            super::RowPinPosition::Bottom => self.state.row_pinning.bottom.as_slice(),
2744        };
2745        if keys.is_empty() {
2746            return Vec::new();
2747        }
2748
2749        if !self.options.keep_pinned_rows {
2750            let visible: std::collections::HashSet<RowKey> = if self.state.grouping.is_empty() {
2751                let model = self.row_model();
2752                model
2753                    .root_rows()
2754                    .iter()
2755                    .filter_map(|&i| model.row(i).map(|r| r.key))
2756                    .collect()
2757            } else {
2758                let model = self.grouped_row_model();
2759                model
2760                    .root_rows()
2761                    .iter()
2762                    .filter_map(|&i| model.row(i).map(|r| r.key))
2763                    .collect()
2764            };
2765            return keys
2766                .iter()
2767                .copied()
2768                .filter(|k| visible.contains(k))
2769                .collect();
2770        }
2771
2772        if self.state.grouping.is_empty() {
2773            let core = self.core_row_model();
2774            return keys
2775                .iter()
2776                .copied()
2777                .filter(|k| {
2778                    core.row_by_key(*k).is_some_and(|i| {
2779                        super::row_is_all_parents_expanded(core, &self.state.expanding, i)
2780                    })
2781                })
2782                .collect();
2783        }
2784
2785        let grouped = self.grouped_row_model();
2786        keys.iter()
2787            .copied()
2788            .filter(|k| {
2789                grouped.row_by_key(*k).is_some_and(|mut i| {
2790                    loop {
2791                        let Some(r) = grouped.row(i) else {
2792                            return true;
2793                        };
2794                        let Some(parent) = r.parent else {
2795                            return true;
2796                        };
2797                        let Some(parent_row) = grouped.row(parent) else {
2798                            return true;
2799                        };
2800                        if !super::is_row_expanded(parent_row.key, &self.state.expanding) {
2801                            return false;
2802                        }
2803                        i = parent;
2804                    }
2805                })
2806            })
2807            .collect()
2808    }
2809
2810    pub fn ordered_columns(&self) -> Vec<&super::ColumnDef<TData>> {
2811        let ordered = super::order_columns(&self.columns, &self.state.column_order);
2812        let mode = self.options.grouped_column_mode;
2813        let grouping = self.state.grouping.as_slice();
2814
2815        if grouping.is_empty() || mode == super::GroupedColumnMode::None {
2816            return ordered;
2817        }
2818
2819        let is_grouped =
2820            |c: &&super::ColumnDef<TData>| grouping.iter().any(|id| id.as_ref() == c.id.as_ref());
2821
2822        let non_grouped: Vec<&super::ColumnDef<TData>> =
2823            ordered.iter().copied().filter(|c| !is_grouped(c)).collect();
2824
2825        if mode == super::GroupedColumnMode::Remove {
2826            return non_grouped;
2827        }
2828
2829        let by_id: HashMap<&str, &super::ColumnDef<TData>> = ordered
2830            .iter()
2831            .copied()
2832            .map(|c| (c.id.as_ref(), c))
2833            .collect();
2834        let mut grouped_cols: Vec<&super::ColumnDef<TData>> = Vec::new();
2835        for id in grouping {
2836            if let Some(col) = by_id.get(id.as_ref()).copied() {
2837                grouped_cols.push(col);
2838            }
2839        }
2840        grouped_cols.extend(non_grouped);
2841        grouped_cols
2842    }
2843
2844    pub fn column_order(&self) -> &super::ColumnOrderState {
2845        &self.state.column_order
2846    }
2847
2848    pub fn column_pinning(&self) -> &super::ColumnPinningState {
2849        &self.state.column_pinning
2850    }
2851
2852    pub fn column_can_order(&self, column_id: &str) -> Option<bool> {
2853        let col = self.column(column_id)?;
2854        Some(self.options.enable_column_ordering && col.enable_ordering)
2855    }
2856
2857    pub fn column_can_pin(&self, column_id: &str) -> Option<bool> {
2858        fn any_leaf_can_pin<TData>(
2859            col: &super::ColumnDef<TData>,
2860            enable_column_pinning: bool,
2861        ) -> bool {
2862            if col.columns.is_empty() {
2863                return enable_column_pinning && col.enable_pinning;
2864            }
2865            col.columns
2866                .iter()
2867                .any(|c| any_leaf_can_pin(c, enable_column_pinning))
2868        }
2869
2870        let col = self.find_column_in_tree(column_id)?;
2871        Some(any_leaf_can_pin(col, self.options.enable_column_pinning))
2872    }
2873
2874    pub fn column_pin_position(&self, column_id: &str) -> Option<super::ColumnPinPosition> {
2875        let leaf_ids = self.column_leaf_ids_for(column_id)?;
2876
2877        if leaf_ids
2878            .iter()
2879            .any(|id| self.state.column_pinning.left.iter().any(|c| c == id))
2880        {
2881            return Some(super::ColumnPinPosition::Left);
2882        }
2883        if leaf_ids
2884            .iter()
2885            .any(|id| self.state.column_pinning.right.iter().any(|c| c == id))
2886        {
2887            return Some(super::ColumnPinPosition::Right);
2888        }
2889        None
2890    }
2891
2892    /// TanStack-aligned: `column.getPinnedIndex()`.
2893    ///
2894    /// Returns `0` for unpinned columns (matching TanStack). Returns `-1` when the column is pinned
2895    /// via a group column but the group id is not present in the leaf-pinning state arrays.
2896    pub fn column_pinned_index(&self, column_id: &str) -> Option<i32> {
2897        self.find_column_in_tree(column_id)?;
2898
2899        let Some(position) = self.column_pin_position(column_id) else {
2900            return Some(0);
2901        };
2902
2903        let ids = match position {
2904            super::ColumnPinPosition::Left => self.state.column_pinning.left.as_slice(),
2905            super::ColumnPinPosition::Right => self.state.column_pinning.right.as_slice(),
2906        };
2907        Some(
2908            ids.iter()
2909                .position(|id| id.as_ref() == column_id)
2910                .map(|i| i as i32)
2911                .unwrap_or(-1),
2912        )
2913    }
2914
2915    pub fn column_pinning_updater(
2916        &self,
2917        column_id: &str,
2918        position: Option<super::ColumnPinPosition>,
2919    ) -> Option<super::Updater<super::ColumnPinningState>> {
2920        let leaf_ids = self.column_leaf_ids_for(column_id)?;
2921
2922        Some(super::Updater::Func(Arc::new(move |old| {
2923            super::pinned_columns(old, position, leaf_ids.clone())
2924        })))
2925    }
2926
2927    pub fn toggled_column_order_move(
2928        &self,
2929        column_id: &str,
2930        to_index: usize,
2931    ) -> Option<super::ColumnOrderState> {
2932        let col = self.column(column_id)?;
2933        if !(self.options.enable_column_ordering && col.enable_ordering) {
2934            return Some(self.state.column_order.clone());
2935        }
2936        Some(super::moved_column(
2937            &self.state.column_order,
2938            &col.id,
2939            to_index,
2940        ))
2941    }
2942
2943    pub fn toggled_column_pinning(
2944        &self,
2945        column_id: &str,
2946        position: Option<super::ColumnPinPosition>,
2947    ) -> Option<super::ColumnPinningState> {
2948        let updater = self.column_pinning_updater(column_id, position)?;
2949        Some(updater.apply(&self.state.column_pinning))
2950    }
2951
2952    pub fn visible_columns(&self) -> Vec<&super::ColumnDef<TData>> {
2953        self.ordered_columns()
2954            .into_iter()
2955            .filter(|c| super::is_column_visible(&self.state.column_visibility, &c.id))
2956            .collect()
2957    }
2958
2959    pub fn header_groups(&self) -> Vec<super::HeaderGroupSnapshot> {
2960        let (left, center, right) = self.pinned_visible_columns();
2961        let mut columns_to_group: Vec<std::sync::Arc<str>> = Vec::new();
2962        columns_to_group.extend(left.into_iter().map(|c| c.id.clone()));
2963        columns_to_group.extend(center.into_iter().map(|c| c.id.clone()));
2964        columns_to_group.extend(right.into_iter().map(|c| c.id.clone()));
2965
2966        let leaf_visible = |id: &str| {
2967            self.column(id)
2968                .is_some_and(|c| super::is_column_visible(&self.state.column_visibility, &c.id))
2969        };
2970
2971        super::build_header_groups(&self.column_tree, &columns_to_group, &leaf_visible, None)
2972    }
2973
2974    pub fn left_header_groups(&self) -> Vec<super::HeaderGroupSnapshot> {
2975        let (left, _, _) = self.pinned_visible_columns();
2976        let columns_to_group: Vec<std::sync::Arc<str>> =
2977            left.into_iter().map(|c| c.id.clone()).collect();
2978        let leaf_visible = |id: &str| {
2979            self.column(id)
2980                .is_some_and(|c| super::is_column_visible(&self.state.column_visibility, &c.id))
2981        };
2982        super::build_header_groups(
2983            &self.column_tree,
2984            &columns_to_group,
2985            &leaf_visible,
2986            Some("left"),
2987        )
2988    }
2989
2990    pub fn center_header_groups(&self) -> Vec<super::HeaderGroupSnapshot> {
2991        let (_, center, _) = self.pinned_visible_columns();
2992        let columns_to_group: Vec<std::sync::Arc<str>> =
2993            center.into_iter().map(|c| c.id.clone()).collect();
2994        let leaf_visible = |id: &str| {
2995            self.column(id)
2996                .is_some_and(|c| super::is_column_visible(&self.state.column_visibility, &c.id))
2997        };
2998        super::build_header_groups(
2999            &self.column_tree,
3000            &columns_to_group,
3001            &leaf_visible,
3002            Some("center"),
3003        )
3004    }
3005
3006    pub fn right_header_groups(&self) -> Vec<super::HeaderGroupSnapshot> {
3007        let (_, _, right) = self.pinned_visible_columns();
3008        let columns_to_group: Vec<std::sync::Arc<str>> =
3009            right.into_iter().map(|c| c.id.clone()).collect();
3010        let leaf_visible = |id: &str| {
3011            self.column(id)
3012                .is_some_and(|c| super::is_column_visible(&self.state.column_visibility, &c.id))
3013        };
3014        super::build_header_groups(
3015            &self.column_tree,
3016            &columns_to_group,
3017            &leaf_visible,
3018            Some("right"),
3019        )
3020    }
3021
3022    pub fn footer_groups(&self) -> Vec<super::HeaderGroupSnapshot> {
3023        let mut groups = self.header_groups();
3024        groups.reverse();
3025        groups
3026    }
3027
3028    pub fn left_footer_groups(&self) -> Vec<super::HeaderGroupSnapshot> {
3029        let mut groups = self.left_header_groups();
3030        groups.reverse();
3031        groups
3032    }
3033
3034    pub fn center_footer_groups(&self) -> Vec<super::HeaderGroupSnapshot> {
3035        let mut groups = self.center_header_groups();
3036        groups.reverse();
3037        groups
3038    }
3039
3040    pub fn right_footer_groups(&self) -> Vec<super::HeaderGroupSnapshot> {
3041        let mut groups = self.right_header_groups();
3042        groups.reverse();
3043        groups
3044    }
3045
3046    pub fn flat_headers(&self) -> Vec<super::HeaderSnapshot> {
3047        self.header_groups()
3048            .into_iter()
3049            .flat_map(|g| g.headers)
3050            .collect()
3051    }
3052
3053    pub fn left_flat_headers(&self) -> Vec<super::HeaderSnapshot> {
3054        self.left_header_groups()
3055            .into_iter()
3056            .flat_map(|g| g.headers)
3057            .collect()
3058    }
3059
3060    pub fn center_flat_headers(&self) -> Vec<super::HeaderSnapshot> {
3061        self.center_header_groups()
3062            .into_iter()
3063            .flat_map(|g| g.headers)
3064            .collect()
3065    }
3066
3067    pub fn right_flat_headers(&self) -> Vec<super::HeaderSnapshot> {
3068        self.right_header_groups()
3069            .into_iter()
3070            .flat_map(|g| g.headers)
3071            .collect()
3072    }
3073
3074    pub fn left_leaf_headers(&self) -> Vec<super::HeaderSnapshot> {
3075        self.left_flat_headers()
3076            .into_iter()
3077            .filter(|h| h.sub_header_ids.is_empty())
3078            .collect()
3079    }
3080
3081    pub fn center_leaf_headers(&self) -> Vec<super::HeaderSnapshot> {
3082        self.center_flat_headers()
3083            .into_iter()
3084            .filter(|h| h.sub_header_ids.is_empty())
3085            .collect()
3086    }
3087
3088    pub fn right_leaf_headers(&self) -> Vec<super::HeaderSnapshot> {
3089        self.right_flat_headers()
3090            .into_iter()
3091            .filter(|h| h.sub_header_ids.is_empty())
3092            .collect()
3093    }
3094
3095    /// TanStack-style `getLeafHeaders` (postorder traversal from top headers).
3096    pub fn leaf_headers(&self) -> Vec<super::HeaderSnapshot> {
3097        fn recurse(
3098            id: &Arc<str>,
3099            headers_by_id: &HashMap<Arc<str>, super::HeaderSnapshot>,
3100            out: &mut Vec<super::HeaderSnapshot>,
3101        ) {
3102            let Some(h) = headers_by_id.get(id) else {
3103                return;
3104            };
3105            for child in &h.sub_header_ids {
3106                recurse(child, headers_by_id, out);
3107            }
3108            out.push(h.clone());
3109        }
3110
3111        let left = self.left_header_groups();
3112        let center = self.center_header_groups();
3113        let right = self.right_header_groups();
3114
3115        let mut roots: Vec<Arc<str>> = Vec::new();
3116        for groups in [&left, &center, &right] {
3117            if let Some(top) = groups.first() {
3118                roots.extend(top.headers.iter().map(|h| h.id.clone()));
3119            }
3120        }
3121
3122        let mut headers_by_id: HashMap<Arc<str>, super::HeaderSnapshot> = HashMap::new();
3123        for groups in [left, center, right] {
3124            for g in groups {
3125                for h in g.headers {
3126                    headers_by_id.insert(h.id.clone(), h);
3127                }
3128            }
3129        }
3130
3131        let mut out = Vec::new();
3132        for root in roots {
3133            recurse(&root, &headers_by_id, &mut out);
3134        }
3135        out
3136    }
3137
3138    pub fn row_cells(&self, row_key: RowKey) -> Option<super::RowCellsSnapshot> {
3139        let row = self.row(row_key, true)?;
3140        let row_id = row.id.as_str();
3141
3142        let grouped_column_ids = self.state.grouping.as_slice();
3143        let mut row_grouping_column_id: Option<&str> = None;
3144        if !grouped_column_ids.is_empty() {
3145            let grouped = self.grouped_row_model();
3146            if let Some(grouped_row_index) = grouped.row_by_key(row.key)
3147                && let Some(grouped_row) = grouped.row(grouped_row_index)
3148                && let super::GroupedRowKind::Group {
3149                    grouping_column, ..
3150                } = &grouped_row.kind
3151            {
3152                row_grouping_column_id = Some(grouping_column.as_ref());
3153            }
3154        }
3155
3156        let row_has_sub_rows = !row.sub_rows.is_empty();
3157
3158        let all_leaf_columns = self.ordered_columns();
3159        let (left, center, right) = self.pinned_visible_columns();
3160
3161        Some(super::snapshot_cells_for_row(
3162            row_id,
3163            all_leaf_columns.as_slice(),
3164            left.as_slice(),
3165            center.as_slice(),
3166            right.as_slice(),
3167            grouped_column_ids,
3168            row_grouping_column_id,
3169            row_has_sub_rows,
3170        ))
3171    }
3172
3173    pub fn core_model_snapshot(&self) -> super::CoreModelSnapshot {
3174        fn snapshot_row_model_ids<'a, TData>(
3175            model: &RowModel<'a, TData>,
3176        ) -> super::RowModelIdSnapshot {
3177            let root: Vec<Arc<str>> = model
3178                .root_rows()
3179                .iter()
3180                .filter_map(|&i| model.row(i).map(|r| r.id.0.clone()))
3181                .collect();
3182            let flat: Vec<Arc<str>> = model
3183                .flat_rows()
3184                .iter()
3185                .filter_map(|&i| model.row(i).map(|r| r.id.0.clone()))
3186                .collect();
3187            super::RowModelIdSnapshot { root, flat }
3188        }
3189
3190        let column_tree = self.column_tree_snapshot();
3191
3192        let mut column_capabilities: std::collections::BTreeMap<
3193            Arc<str>,
3194            super::ColumnCapabilitySnapshot,
3195        > = std::collections::BTreeMap::new();
3196        for col in self.ordered_columns() {
3197            let id = col.id.clone();
3198            column_capabilities.insert(
3199                id.clone(),
3200                super::ColumnCapabilitySnapshot {
3201                    can_hide: self.column_can_hide(id.as_ref()).unwrap_or(false),
3202                    can_pin: self.column_can_pin(id.as_ref()).unwrap_or(false),
3203                    pin_position: self.column_pin_position(id.as_ref()),
3204                    pinned_index: self.column_pinned_index(id.as_ref()).unwrap_or(0),
3205                    can_resize: self.column_can_resize(id.as_ref()).unwrap_or(false),
3206                    is_visible: self.is_column_visible(id.as_ref()).unwrap_or(false),
3207                },
3208            );
3209        }
3210
3211        let all_leaf = self
3212            .ordered_columns()
3213            .into_iter()
3214            .map(|c| c.id.clone())
3215            .collect::<Vec<_>>();
3216        let visible = self
3217            .visible_columns()
3218            .into_iter()
3219            .map(|c| c.id.clone())
3220            .collect::<Vec<_>>();
3221        let (left, center, right) = self.pinned_visible_columns();
3222        let left_visible = left.into_iter().map(|c| c.id.clone()).collect::<Vec<_>>();
3223        let center_visible = center.into_iter().map(|c| c.id.clone()).collect::<Vec<_>>();
3224        let right_visible = right.into_iter().map(|c| c.id.clone()).collect::<Vec<_>>();
3225
3226        let all_flat = self
3227            .all_flat_columns()
3228            .into_iter()
3229            .map(|c| c.id.clone())
3230            .collect::<Vec<_>>();
3231        let visible_flat = self
3232            .visible_flat_columns()
3233            .into_iter()
3234            .map(|c| c.id.clone())
3235            .collect::<Vec<_>>();
3236
3237        let header_groups = self.header_groups();
3238        let left_header_groups = self.left_header_groups();
3239        let center_header_groups = self.center_header_groups();
3240        let right_header_groups = self.right_header_groups();
3241
3242        let mut header_size: std::collections::BTreeMap<Arc<str>, f32> =
3243            std::collections::BTreeMap::new();
3244        let mut header_start: std::collections::BTreeMap<Arc<str>, f32> =
3245            std::collections::BTreeMap::new();
3246
3247        for groups in [
3248            &header_groups,
3249            &left_header_groups,
3250            &center_header_groups,
3251            &right_header_groups,
3252        ] {
3253            for g in groups {
3254                for h in &g.headers {
3255                    let id = h.id.clone();
3256                    if let Some(size) = self.header_size(id.as_ref()) {
3257                        header_size.insert(id.clone(), size);
3258                    }
3259                    if let Some(start) = self.header_start(id.as_ref()) {
3260                        header_start.insert(id.clone(), start);
3261                    }
3262                }
3263            }
3264        }
3265
3266        let (left_total_size, center_total_size, right_total_size) = self.pinned_total_sizes();
3267
3268        let mut column_size: std::collections::BTreeMap<Arc<str>, f32> =
3269            std::collections::BTreeMap::new();
3270        let mut column_start_all: std::collections::BTreeMap<Arc<str>, f32> =
3271            std::collections::BTreeMap::new();
3272        let mut column_start_left: std::collections::BTreeMap<Arc<str>, Option<f32>> =
3273            std::collections::BTreeMap::new();
3274        let mut column_start_center: std::collections::BTreeMap<Arc<str>, Option<f32>> =
3275            std::collections::BTreeMap::new();
3276        let mut column_start_right: std::collections::BTreeMap<Arc<str>, Option<f32>> =
3277            std::collections::BTreeMap::new();
3278        let mut column_after_all: std::collections::BTreeMap<Arc<str>, f32> =
3279            std::collections::BTreeMap::new();
3280        let mut column_after_left: std::collections::BTreeMap<Arc<str>, Option<f32>> =
3281            std::collections::BTreeMap::new();
3282        let mut column_after_center: std::collections::BTreeMap<Arc<str>, Option<f32>> =
3283            std::collections::BTreeMap::new();
3284        let mut column_after_right: std::collections::BTreeMap<Arc<str>, Option<f32>> =
3285            std::collections::BTreeMap::new();
3286
3287        for col in self.ordered_columns() {
3288            let id = col.id.clone();
3289            column_size.insert(
3290                id.clone(),
3291                super::resolved_column_size(&self.state.column_sizing, col),
3292            );
3293            column_start_all.insert(
3294                id.clone(),
3295                self.column_start(id.as_ref(), super::ColumnSizingRegion::All)
3296                    .unwrap_or(0.0),
3297            );
3298            column_start_left.insert(
3299                id.clone(),
3300                self.column_start(id.as_ref(), super::ColumnSizingRegion::Left),
3301            );
3302            column_start_center.insert(
3303                id.clone(),
3304                self.column_start(id.as_ref(), super::ColumnSizingRegion::Center),
3305            );
3306            column_start_right.insert(
3307                id.clone(),
3308                self.column_start(id.as_ref(), super::ColumnSizingRegion::Right),
3309            );
3310
3311            column_after_all.insert(
3312                id.clone(),
3313                self.column_after(id.as_ref(), super::ColumnSizingRegion::All)
3314                    .unwrap_or(0.0),
3315            );
3316            column_after_left.insert(
3317                id.clone(),
3318                self.column_after(id.as_ref(), super::ColumnSizingRegion::Left),
3319            );
3320            column_after_center.insert(
3321                id.clone(),
3322                self.column_after(id.as_ref(), super::ColumnSizingRegion::Center),
3323            );
3324            column_after_right.insert(
3325                id.clone(),
3326                self.column_after(id.as_ref(), super::ColumnSizingRegion::Right),
3327            );
3328        }
3329
3330        let core = snapshot_row_model_ids(self.core_row_model());
3331        let row_model = snapshot_row_model_ids(self.row_model());
3332
3333        let mut cells: std::collections::BTreeMap<Arc<str>, super::RowCellsSnapshot> =
3334            std::collections::BTreeMap::new();
3335        for &row_i in self.row_model().root_rows() {
3336            let Some(row) = self.row_model().row(row_i) else {
3337                continue;
3338            };
3339            let row_id = row.id.0.clone();
3340            if let Some(snapshot) = self.row_cells(row.key) {
3341                cells.insert(row_id, snapshot);
3342            }
3343        }
3344
3345        super::CoreModelSnapshot {
3346            schema_version: 4,
3347            column_tree,
3348            column_capabilities,
3349            flat_columns: super::FlatColumnsSnapshot {
3350                all: all_flat,
3351                visible: visible_flat,
3352            },
3353            leaf_columns: super::LeafColumnsSnapshot {
3354                all: all_leaf,
3355                visible,
3356                left_visible,
3357                center_visible,
3358                right_visible,
3359            },
3360            header_groups,
3361            left_header_groups,
3362            center_header_groups,
3363            right_header_groups,
3364            header_sizing: super::HeaderSizingSnapshot {
3365                size: header_size,
3366                start: header_start,
3367            },
3368            leaf_column_sizing: super::LeafColumnSizingSnapshot {
3369                sizing: super::ColumnSizingSnapshot {
3370                    total_size: self.total_size(),
3371                    left_total_size,
3372                    center_total_size,
3373                    right_total_size,
3374                },
3375                size: column_size,
3376                start: super::ColumnStartSnapshot {
3377                    all: column_start_all,
3378                    left: column_start_left,
3379                    center: column_start_center,
3380                    right: column_start_right,
3381                },
3382                after: super::ColumnAfterSnapshot {
3383                    all: column_after_all,
3384                    left: column_after_left,
3385                    center: column_after_center,
3386                    right: column_after_right,
3387                },
3388            },
3389            rows: super::CoreRowsSnapshot { core, row_model },
3390            cells,
3391        }
3392    }
3393
3394    pub fn pinned_visible_columns(
3395        &self,
3396    ) -> (
3397        Vec<&super::ColumnDef<TData>>,
3398        Vec<&super::ColumnDef<TData>>,
3399        Vec<&super::ColumnDef<TData>>,
3400    ) {
3401        let visible = self.visible_columns();
3402        super::split_pinned_columns(visible.as_slice(), &self.state.column_pinning)
3403    }
3404
3405    /// TanStack-aligned: `table.getLeft/Center/RightLeafColumns()`.
3406    ///
3407    /// Note: this returns **leaf columns** split by pinning state and does not apply column
3408    /// visibility filtering. Use [`Self::pinned_visible_columns`] for visible-only splits.
3409    pub fn pinned_leaf_columns(
3410        &self,
3411    ) -> (
3412        Vec<&super::ColumnDef<TData>>,
3413        Vec<&super::ColumnDef<TData>>,
3414        Vec<&super::ColumnDef<TData>>,
3415    ) {
3416        let leaf = self.ordered_columns();
3417        super::split_pinned_columns(leaf.as_slice(), &self.state.column_pinning)
3418    }
3419
3420    pub fn left_leaf_columns(&self) -> Vec<&super::ColumnDef<TData>> {
3421        let (left, _, _) = self.pinned_leaf_columns();
3422        left
3423    }
3424
3425    pub fn center_leaf_columns(&self) -> Vec<&super::ColumnDef<TData>> {
3426        let (_, center, _) = self.pinned_leaf_columns();
3427        center
3428    }
3429
3430    pub fn right_leaf_columns(&self) -> Vec<&super::ColumnDef<TData>> {
3431        let (_, _, right) = self.pinned_leaf_columns();
3432        right
3433    }
3434
3435    pub fn column_size(&self, id: &str) -> Option<f32> {
3436        let col = self.column(id)?;
3437        Some(super::resolved_column_size(&self.state.column_sizing, col))
3438    }
3439
3440    pub fn column_sizing(&self) -> &super::ColumnSizingState {
3441        &self.state.column_sizing
3442    }
3443
3444    pub fn column_sizing_info(&self) -> &super::ColumnSizingInfoState {
3445        &self.state.column_sizing_info
3446    }
3447
3448    pub fn column_can_resize(&self, id: &str) -> Option<bool> {
3449        let col = self.column(id)?;
3450        Some(super::column_can_resize(self.options, col))
3451    }
3452
3453    pub fn is_column_resizing(&self, id: &str) -> Option<bool> {
3454        let col = self.column(id)?;
3455        Some(
3456            self.state
3457                .column_sizing_info
3458                .is_resizing_column
3459                .as_ref()
3460                .is_some_and(|active| active.as_ref() == col.id.as_ref()),
3461        )
3462    }
3463
3464    /// TanStack-aligned: remove an override size entry (falls back to column defaults).
3465    pub fn reset_column_size(&self, id: &str) -> Option<super::ColumnSizingState> {
3466        let col = self.column(id)?;
3467        let mut next = self.state.column_sizing.clone();
3468        next.remove(&col.id);
3469        Some(next)
3470    }
3471
3472    /// TanStack-aligned: `table.resetColumnSizing(defaultState?)`.
3473    pub fn reset_column_sizing(&self, default_state: bool) -> super::ColumnSizingState {
3474        if default_state {
3475            super::ColumnSizingState::default()
3476        } else {
3477            self.initial_state.column_sizing.clone()
3478        }
3479    }
3480
3481    /// TanStack-aligned: `table.resetHeaderSizeInfo(defaultState?)` (`columnSizingInfo` reset).
3482    pub fn reset_header_size_info(&self, default_state: bool) -> super::ColumnSizingInfoState {
3483        if default_state {
3484            super::ColumnSizingInfoState::default()
3485        } else {
3486            self.initial_state.column_sizing_info.clone()
3487        }
3488    }
3489
3490    pub fn started_column_resize(
3491        &self,
3492        id: &str,
3493        pointer_x: f32,
3494    ) -> Option<super::ColumnSizingInfoState> {
3495        let col = self.column(id)?;
3496        if !super::column_can_resize(self.options, col) {
3497            return Some(self.state.column_sizing_info.clone());
3498        }
3499
3500        let start = super::resolved_column_size(&self.state.column_sizing, col);
3501        let mut next = self.state.column_sizing_info.clone();
3502        super::begin_column_resize(
3503            &mut next,
3504            col.id.clone(),
3505            pointer_x,
3506            start,
3507            vec![(col.id.clone(), start)],
3508        );
3509        Some(next)
3510    }
3511
3512    pub fn dragged_column_resize(
3513        &self,
3514        pointer_x: f32,
3515    ) -> (super::ColumnSizingState, super::ColumnSizingInfoState) {
3516        let mut sizing = self.state.column_sizing.clone();
3517        let mut info = self.state.column_sizing_info.clone();
3518        super::drag_column_resize(
3519            self.options.column_resize_mode,
3520            self.options.column_resize_direction,
3521            &mut sizing,
3522            &mut info,
3523            pointer_x,
3524        );
3525        (sizing, info)
3526    }
3527
3528    pub fn ended_column_resize(
3529        &self,
3530        pointer_x: Option<f32>,
3531    ) -> (super::ColumnSizingState, super::ColumnSizingInfoState) {
3532        let mut sizing = self.state.column_sizing.clone();
3533        let mut info = self.state.column_sizing_info.clone();
3534        super::end_column_resize(
3535            self.options.column_resize_mode,
3536            self.options.column_resize_direction,
3537            &mut sizing,
3538            &mut info,
3539            pointer_x,
3540        );
3541        (sizing, info)
3542    }
3543
3544    pub fn total_size(&self) -> f32 {
3545        self.visible_columns()
3546            .into_iter()
3547            .map(|c| super::resolved_column_size(&self.state.column_sizing, c))
3548            .sum()
3549    }
3550
3551    pub fn pinned_total_sizes(&self) -> (f32, f32, f32) {
3552        let (left, center, right) = self.pinned_visible_columns();
3553        let sizing = &self.state.column_sizing;
3554
3555        let left = left
3556            .into_iter()
3557            .map(|c| super::resolved_column_size(sizing, c))
3558            .sum();
3559        let center = center
3560            .into_iter()
3561            .map(|c| super::resolved_column_size(sizing, c))
3562            .sum();
3563        let right = right
3564            .into_iter()
3565            .map(|c| super::resolved_column_size(sizing, c))
3566            .sum();
3567
3568        (left, center, right)
3569    }
3570
3571    pub fn left_total_size(&self) -> f32 {
3572        self.pinned_total_sizes().0
3573    }
3574
3575    pub fn center_total_size(&self) -> f32 {
3576        self.pinned_total_sizes().1
3577    }
3578
3579    pub fn right_total_size(&self) -> f32 {
3580        self.pinned_total_sizes().2
3581    }
3582
3583    /// TanStack-aligned: return the start offset (x) for a column within a sizing region.
3584    ///
3585    /// Notes:
3586    /// - For `All`, this is `column.getStart()` over TanStack's `_getVisibleLeafColumns(table)`.
3587    /// - For `Left`/`Center`/`Right`, this corresponds to `column.getStart(position)` with
3588    ///   fixture parity gating that only records values for columns that are pinned to that
3589    ///   region (or are in the center region).
3590    /// - When the column is not present in the visible list for the given region (e.g. hidden),
3591    ///   TanStack's `getIndex` returns `-1`, which results in `slice(0, -1)` behavior. We mirror
3592    ///   that by summing all but the last column in that region for `start`, and summing the
3593    ///   entire region for `after`.
3594    pub fn column_start(&self, column_id: &str, region: super::ColumnSizingRegion) -> Option<f32> {
3595        let col = self.column(column_id)?;
3596        let sizing = &self.state.column_sizing;
3597
3598        let pin_pos = self.column_pin_position(column_id);
3599        let (left, center, right) = self.pinned_visible_columns();
3600
3601        fn tanstack_start_for<'a, TData: 'a>(
3602            cols: impl IntoIterator<Item = &'a super::ColumnDef<TData>>,
3603            target: &super::ColumnDef<TData>,
3604            sizing: &super::ColumnSizingState,
3605        ) -> f32 {
3606            let cols: Vec<&super::ColumnDef<TData>> = cols.into_iter().collect();
3607            let idx = cols
3608                .iter()
3609                .position(|c| c.id.as_ref() == target.id.as_ref())
3610                .map(|i| i as isize)
3611                .unwrap_or(-1);
3612
3613            let end = if idx == -1 {
3614                cols.len().saturating_sub(1)
3615            } else {
3616                idx as usize
3617            };
3618
3619            let mut sum = 0.0;
3620            for c in cols.into_iter().take(end) {
3621                sum += super::resolved_column_size(sizing, c);
3622            }
3623            sum
3624        }
3625
3626        match region {
3627            super::ColumnSizingRegion::All => {
3628                let visible = self.visible_columns();
3629                Some(tanstack_start_for(visible, col, sizing))
3630            }
3631            super::ColumnSizingRegion::Left => {
3632                if pin_pos != Some(super::ColumnPinPosition::Left) {
3633                    return None;
3634                }
3635                Some(tanstack_start_for(left, col, sizing))
3636            }
3637            super::ColumnSizingRegion::Center => {
3638                if pin_pos.is_some() {
3639                    return None;
3640                }
3641                Some(tanstack_start_for(center, col, sizing))
3642            }
3643            super::ColumnSizingRegion::Right => {
3644                if pin_pos != Some(super::ColumnPinPosition::Right) {
3645                    return None;
3646                }
3647                Some(tanstack_start_for(right, col, sizing))
3648            }
3649        }
3650    }
3651
3652    /// TanStack-aligned: return the after offset (remaining width) for a column within a sizing region.
3653    pub fn column_after(&self, column_id: &str, region: super::ColumnSizingRegion) -> Option<f32> {
3654        let col = self.column(column_id)?;
3655        let sizing = &self.state.column_sizing;
3656
3657        let pin_pos = self.column_pin_position(column_id);
3658        let (left, center, right) = self.pinned_visible_columns();
3659
3660        fn tanstack_after_for<'a, TData: 'a>(
3661            cols: impl IntoIterator<Item = &'a super::ColumnDef<TData>>,
3662            target: &super::ColumnDef<TData>,
3663            sizing: &super::ColumnSizingState,
3664        ) -> f32 {
3665            let cols: Vec<&super::ColumnDef<TData>> = cols.into_iter().collect();
3666            let idx = cols
3667                .iter()
3668                .position(|c| c.id.as_ref() == target.id.as_ref())
3669                .map(|i| i as isize)
3670                .unwrap_or(-1);
3671
3672            let start = (idx + 1).max(0) as usize;
3673            let mut sum = 0.0;
3674            for c in cols.into_iter().skip(start) {
3675                sum += super::resolved_column_size(sizing, c);
3676            }
3677            sum
3678        }
3679
3680        match region {
3681            super::ColumnSizingRegion::All => {
3682                let visible = self.visible_columns();
3683                Some(tanstack_after_for(visible, col, sizing))
3684            }
3685            super::ColumnSizingRegion::Left => {
3686                if pin_pos != Some(super::ColumnPinPosition::Left) {
3687                    return None;
3688                }
3689                Some(tanstack_after_for(left, col, sizing))
3690            }
3691            super::ColumnSizingRegion::Center => {
3692                if pin_pos.is_some() {
3693                    return None;
3694                }
3695                Some(tanstack_after_for(center, col, sizing))
3696            }
3697            super::ColumnSizingRegion::Right => {
3698                if pin_pos != Some(super::ColumnPinPosition::Right) {
3699                    return None;
3700                }
3701                Some(tanstack_after_for(right, col, sizing))
3702            }
3703        }
3704    }
3705
3706    fn header_groups_for_sizing(&self, header_id: &str) -> Vec<super::HeaderGroupSnapshot> {
3707        if header_id.starts_with("left_") {
3708            return self.left_header_groups();
3709        }
3710        if header_id.starts_with("center_") {
3711            return self.center_header_groups();
3712        }
3713        if header_id.starts_with("right_") {
3714            return self.right_header_groups();
3715        }
3716        // TanStack note: Leaf header `id` is the column id even for pinned header families
3717        // (left/center/right). `header.getStart()` is computed within the header group, so for leaf
3718        // headers we need to choose the appropriate family based on current column pinning.
3719        if self.column(header_id).is_some() {
3720            return match self.column_pin_position(header_id) {
3721                Some(super::ColumnPinPosition::Left) => self.left_header_groups(),
3722                Some(super::ColumnPinPosition::Right) => self.right_header_groups(),
3723                None => self.center_header_groups(),
3724            };
3725        }
3726        self.header_groups()
3727    }
3728
3729    /// TanStack-aligned: `header.getSize()` for header groups (including placeholder headers).
3730    pub fn header_size(&self, header_id: &str) -> Option<f32> {
3731        let groups = self.header_groups_for_sizing(header_id);
3732        let mut by_id: HashMap<Arc<str>, super::HeaderSnapshot> = HashMap::new();
3733        for g in &groups {
3734            for h in &g.headers {
3735                by_id.insert(h.id.clone(), h.clone());
3736            }
3737        }
3738
3739        fn size_for<'a, TData>(
3740            table: &Table<'a, TData>,
3741            by_id: &HashMap<Arc<str>, super::HeaderSnapshot>,
3742            cache: &mut HashMap<Arc<str>, f32>,
3743            id: &Arc<str>,
3744        ) -> f32 {
3745            if let Some(v) = cache.get(id) {
3746                return *v;
3747            }
3748
3749            let Some(h) = by_id.get(id) else {
3750                cache.insert(id.clone(), 0.0);
3751                return 0.0;
3752            };
3753
3754            let size = if h.sub_header_ids.is_empty() {
3755                table
3756                    .column(h.column_id.as_ref())
3757                    .map(|c| super::resolved_column_size(&table.state.column_sizing, c))
3758                    .unwrap_or(0.0)
3759            } else {
3760                let mut sum = 0.0;
3761                for child in &h.sub_header_ids {
3762                    sum += size_for(table, by_id, cache, child);
3763                }
3764                sum
3765            };
3766
3767            cache.insert(id.clone(), size);
3768            size
3769        }
3770
3771        let id = Arc::<str>::from(header_id);
3772        if !by_id.contains_key(&id) {
3773            return None;
3774        }
3775        let mut cache: HashMap<Arc<str>, f32> = HashMap::new();
3776        Some(size_for(self, &by_id, &mut cache, &id))
3777    }
3778
3779    /// TanStack-aligned: `header.getStart()` computed within its header group.
3780    pub fn header_start(&self, header_id: &str) -> Option<f32> {
3781        let groups = self.header_groups_for_sizing(header_id);
3782        if groups.is_empty() {
3783            return None;
3784        }
3785
3786        let mut by_id: HashMap<Arc<str>, super::HeaderSnapshot> = HashMap::new();
3787        for g in &groups {
3788            for h in &g.headers {
3789                by_id.insert(h.id.clone(), h.clone());
3790            }
3791        }
3792
3793        fn size_for<'a, TData>(
3794            table: &Table<'a, TData>,
3795            by_id: &HashMap<Arc<str>, super::HeaderSnapshot>,
3796            cache: &mut HashMap<Arc<str>, f32>,
3797            id: &Arc<str>,
3798        ) -> f32 {
3799            if let Some(v) = cache.get(id) {
3800                return *v;
3801            }
3802
3803            let Some(h) = by_id.get(id) else {
3804                cache.insert(id.clone(), 0.0);
3805                return 0.0;
3806            };
3807
3808            let size = if h.sub_header_ids.is_empty() {
3809                table
3810                    .column(h.column_id.as_ref())
3811                    .map(|c| super::resolved_column_size(&table.state.column_sizing, c))
3812                    .unwrap_or(0.0)
3813            } else {
3814                let mut sum = 0.0;
3815                for child in &h.sub_header_ids {
3816                    sum += size_for(table, by_id, cache, child);
3817                }
3818                sum
3819            };
3820
3821            cache.insert(id.clone(), size);
3822            size
3823        }
3824
3825        let mut cache: HashMap<Arc<str>, f32> = HashMap::new();
3826
3827        for g in &groups {
3828            let mut index: Option<usize> = None;
3829            for (i, h) in g.headers.iter().enumerate() {
3830                if h.id.as_ref() == header_id {
3831                    index = Some(i);
3832                    break;
3833                }
3834            }
3835
3836            let Some(index) = index else {
3837                continue;
3838            };
3839
3840            let mut start = 0.0;
3841            for h in g.headers.iter().take(index) {
3842                start += size_for(self, &by_id, &mut cache, &h.id);
3843            }
3844            return Some(start);
3845        }
3846
3847        None
3848    }
3849
3850    pub fn core_row_model(&self) -> &RowModel<'a, TData> {
3851        self.core_row_model.get_or_init(|| {
3852            build_core_row_model(
3853                self.data,
3854                &*self.get_row_key,
3855                self.get_row_id.as_deref(),
3856                self.get_sub_rows.as_deref(),
3857            )
3858        })
3859    }
3860
3861    pub fn pre_filtered_row_model(&self) -> &RowModel<'a, TData> {
3862        self.core_row_model()
3863    }
3864
3865    pub fn filtered_row_model(&self) -> &RowModel<'a, TData> {
3866        if self.options.manual_filtering || self.filtered_row_model_override_pre_filtered {
3867            return self.pre_filtered_row_model();
3868        }
3869        self.filtered_row_model.get_or_init(|| {
3870            super::filter_row_model(
3871                self.pre_filtered_row_model(),
3872                &self.columns,
3873                &self.state.column_filters,
3874                self.state.global_filter.clone(),
3875                self.options,
3876                &self.filter_fns,
3877                &self.global_filter_fn,
3878                self.get_column_can_global_filter.as_deref(),
3879            )
3880        })
3881    }
3882
3883    pub fn row_filter_state_snapshot(&self) -> super::RowFilterStateSnapshot {
3884        if self.options.manual_filtering || self.filtered_row_model_override_pre_filtered {
3885            return super::RowFilterStateSnapshot::default();
3886        }
3887        super::evaluate_row_filter_state(
3888            self.pre_filtered_row_model(),
3889            &self.columns,
3890            &self.state.column_filters,
3891            self.state.global_filter.clone(),
3892            self.options,
3893            &self.filter_fns,
3894            &self.global_filter_fn,
3895            self.get_column_can_global_filter.as_deref(),
3896        )
3897    }
3898
3899    pub fn pre_sorted_row_model(&self) -> &RowModel<'a, TData> {
3900        if !self.options.manual_grouping && !self.state.grouping.is_empty() {
3901            return self.grouped_pre_sorted_row_model();
3902        }
3903        self.filtered_row_model()
3904    }
3905
3906    pub fn sorted_row_model(&self) -> &RowModel<'a, TData> {
3907        if self.options.manual_sorting || self.sorted_row_model_override_pre_sorted {
3908            return self.pre_sorted_row_model();
3909        }
3910        if !self.options.manual_grouping && !self.state.grouping.is_empty() {
3911            return self.grouped_sorted_row_model();
3912        }
3913        self.sorted_row_model.get_or_init(|| {
3914            super::sort_row_model(
3915                self.pre_sorted_row_model(),
3916                &self.columns,
3917                &self.state.sorting,
3918                &self.sorting_fns,
3919            )
3920        })
3921    }
3922
3923    pub fn pre_pagination_row_model(&self) -> &RowModel<'a, TData> {
3924        if self.options.paginate_expanded_rows {
3925            self.expanded_row_model()
3926        } else {
3927            self.sorted_row_model()
3928        }
3929    }
3930
3931    pub fn pre_expanded_row_model(&self) -> &RowModel<'a, TData> {
3932        self.sorted_row_model()
3933    }
3934
3935    pub fn expanded_row_model(&self) -> &RowModel<'a, TData> {
3936        if !self.options.paginate_expanded_rows {
3937            return self.pre_expanded_row_model();
3938        }
3939        if !self.options.manual_grouping && !self.state.grouping.is_empty() {
3940            return self.pre_expanded_row_model();
3941        }
3942        if self.options.manual_expanding {
3943            return self.pre_expanded_row_model();
3944        }
3945        if self.expanded_row_model_override_pre_expanded {
3946            return self.pre_expanded_row_model();
3947        }
3948        if !super::is_some_rows_expanded(&self.state.expanding) {
3949            return self.pre_expanded_row_model();
3950        }
3951        self.expanded_row_model.get_or_init(|| {
3952            super::expand_row_model(
3953                self.pre_expanded_row_model(),
3954                &self.state.expanding,
3955                |row_key, original| self.row_is_expanded_for_key_and_original(row_key, original),
3956            )
3957        })
3958    }
3959
3960    pub fn row_model(&self) -> &RowModel<'a, TData> {
3961        if self.options.manual_pagination {
3962            return self.pre_pagination_row_model();
3963        }
3964        if self.pagination_row_model_override_pre_pagination {
3965            return self.pre_pagination_row_model();
3966        }
3967        if self.options.paginate_expanded_rows {
3968            return self.paginated_row_model.get_or_init(|| {
3969                super::paginate_row_model(self.pre_pagination_row_model(), self.state.pagination)
3970            });
3971        }
3972
3973        let paginated = self.paginated_row_model.get_or_init(|| {
3974            super::paginate_row_model(self.pre_pagination_row_model(), self.state.pagination)
3975        });
3976        self.expanded_paginated_row_model.get_or_init(|| {
3977            let mut out =
3978                super::expand_row_model(paginated, &self.state.expanding, |row_key, original| {
3979                    self.row_is_expanded_for_key_and_original(row_key, original)
3980                });
3981            rebuild_flat_rows_from_roots_including_duplicates(&mut out);
3982            out
3983        })
3984    }
3985
3986    pub fn pre_selected_row_model(&self) -> &RowModel<'a, TData> {
3987        self.core_row_model()
3988    }
3989
3990    pub fn selected_row_model(&self) -> &RowModel<'a, TData> {
3991        self.selected_row_model.get_or_init(|| {
3992            super::select_rows_fn(self.pre_selected_row_model(), &self.state.row_selection)
3993        })
3994    }
3995
3996    pub fn filtered_selected_row_model(&self) -> &RowModel<'a, TData> {
3997        self.filtered_selected_row_model.get_or_init(|| {
3998            if self.state.row_selection.is_empty() {
3999                return RowModel {
4000                    root_rows: Vec::new(),
4001                    flat_rows: Vec::new(),
4002                    rows_by_key: HashMap::new(),
4003                    rows_by_id: HashMap::new(),
4004                    arena: Vec::new(),
4005                };
4006            }
4007            super::select_rows_fn(self.filtered_row_model(), &self.state.row_selection)
4008        })
4009    }
4010
4011    pub fn grouped_selected_row_model(&self) -> &RowModel<'a, TData> {
4012        self.grouped_selected_row_model.get_or_init(|| {
4013            if self.state.row_selection.is_empty() {
4014                return RowModel {
4015                    root_rows: Vec::new(),
4016                    flat_rows: Vec::new(),
4017                    rows_by_key: HashMap::new(),
4018                    rows_by_id: HashMap::new(),
4019                    arena: Vec::new(),
4020                };
4021            }
4022            super::select_rows_fn(self.sorted_row_model(), &self.state.row_selection)
4023        })
4024    }
4025
4026    pub fn page_selected_row_model(&self) -> &RowModel<'a, TData> {
4027        self.page_selected_row_model.get_or_init(|| {
4028            if self.state.row_selection.is_empty() {
4029                return RowModel {
4030                    root_rows: Vec::new(),
4031                    flat_rows: Vec::new(),
4032                    rows_by_key: HashMap::new(),
4033                    rows_by_id: HashMap::new(),
4034                    arena: Vec::new(),
4035                };
4036            }
4037            super::select_rows_fn(self.row_model(), &self.state.row_selection)
4038        })
4039    }
4040
4041    pub fn row_is_selected(&self, row_key: RowKey) -> bool {
4042        super::is_row_selected(row_key, &self.state.row_selection)
4043    }
4044
4045    fn row_can_select_for_row(&self, row_key: RowKey, row: &Row<'a, TData>) -> bool {
4046        if let Some(enable_row_selection) = self.enable_row_selection.as_ref() {
4047            return enable_row_selection(row_key, row.original);
4048        }
4049        self.options.enable_row_selection
4050    }
4051
4052    fn row_can_multi_select_for_row(&self, row_key: RowKey, row: &Row<'a, TData>) -> bool {
4053        if let Some(enable_multi_row_selection) = self.enable_multi_row_selection.as_ref() {
4054            return enable_multi_row_selection(row_key, row.original);
4055        }
4056        self.options.enable_multi_row_selection
4057    }
4058
4059    fn row_can_select_sub_rows_for_row(&self, row_key: RowKey, row: &Row<'a, TData>) -> bool {
4060        if let Some(enable_sub_row_selection) = self.enable_sub_row_selection.as_ref() {
4061            return enable_sub_row_selection(row_key, row.original);
4062        }
4063        self.options.enable_sub_row_selection
4064    }
4065
4066    pub fn row_is_some_selected(&self, row_key: RowKey) -> bool {
4067        let can_select =
4068            |row_key: RowKey, row: &Row<'a, TData>| self.row_can_select_for_row(row_key, row);
4069        self.core_row_model().row_by_key(row_key).is_some_and(|i| {
4070            super::row_is_some_selected(
4071                self.core_row_model(),
4072                &self.state.row_selection,
4073                i,
4074                &can_select,
4075            )
4076        })
4077    }
4078
4079    pub fn row_is_all_sub_rows_selected(&self, row_key: RowKey) -> bool {
4080        let can_select =
4081            |row_key: RowKey, row: &Row<'a, TData>| self.row_can_select_for_row(row_key, row);
4082        self.core_row_model().row_by_key(row_key).is_some_and(|i| {
4083            super::row_is_all_sub_rows_selected(
4084                self.core_row_model(),
4085                &self.state.row_selection,
4086                i,
4087                &can_select,
4088            )
4089        })
4090    }
4091
4092    pub fn row_selection_updater(
4093        &self,
4094        row_key: RowKey,
4095        value: Option<bool>,
4096        select_children: bool,
4097    ) -> super::Updater<super::RowSelectionState> {
4098        #[derive(Clone)]
4099        struct SelectionRowMeta {
4100            sub_rows: Vec<RowKey>,
4101            can_select: bool,
4102            can_multi_select: bool,
4103            can_select_sub_rows: bool,
4104        }
4105
4106        fn mutate_row_selection(
4107            row_meta_by_key: &HashMap<RowKey, SelectionRowMeta>,
4108            selection: &mut super::RowSelectionState,
4109            row_key: RowKey,
4110            value: bool,
4111            include_children: bool,
4112        ) {
4113            let Some(row_meta) = row_meta_by_key.get(&row_key) else {
4114                return;
4115            };
4116
4117            if value {
4118                if !row_meta.can_multi_select {
4119                    selection.clear();
4120                }
4121                if row_meta.can_select {
4122                    selection.insert(row_key);
4123                }
4124            } else {
4125                selection.remove(&row_key);
4126            }
4127
4128            if include_children && row_meta.can_select_sub_rows {
4129                for child_key in &row_meta.sub_rows {
4130                    mutate_row_selection(
4131                        row_meta_by_key,
4132                        selection,
4133                        *child_key,
4134                        value,
4135                        include_children,
4136                    );
4137                }
4138            }
4139        }
4140
4141        let mut row_meta_by_key: HashMap<RowKey, SelectionRowMeta> = HashMap::new();
4142        let core = self.core_row_model();
4143        for row in core.arena() {
4144            let sub_rows = row
4145                .sub_rows
4146                .iter()
4147                .filter_map(|&index| core.row(index).map(|child| child.key))
4148                .collect::<Vec<_>>();
4149            row_meta_by_key.insert(
4150                row.key,
4151                SelectionRowMeta {
4152                    sub_rows,
4153                    can_select: self.row_can_select_for_row(row.key, row),
4154                    can_multi_select: self.row_can_multi_select_for_row(row.key, row),
4155                    can_select_sub_rows: self.row_can_select_sub_rows_for_row(row.key, row),
4156                },
4157            );
4158        }
4159
4160        if !self.state.grouping.is_empty() {
4161            let grouped = self.grouped_row_model();
4162            for &row_index in grouped.flat_rows() {
4163                let Some(row) = grouped.row(row_index) else {
4164                    continue;
4165                };
4166                let sub_rows = row
4167                    .sub_rows
4168                    .iter()
4169                    .filter_map(|&index| grouped.row(index).map(|child| child.key))
4170                    .collect::<Vec<_>>();
4171
4172                let leaf_original = match &row.kind {
4173                    super::GroupedRowKind::Leaf { row_key } => core
4174                        .row_by_key(*row_key)
4175                        .and_then(|index| core.row(index).map(|row| row.original)),
4176                    super::GroupedRowKind::Group {
4177                        first_leaf_row_key, ..
4178                    } => core
4179                        .row_by_key(*first_leaf_row_key)
4180                        .and_then(|index| core.row(index).map(|row| row.original)),
4181                };
4182
4183                let can_select = match leaf_original {
4184                    Some(original) => {
4185                        if let Some(enable_row_selection) = self.enable_row_selection.as_ref() {
4186                            enable_row_selection(row.key, original)
4187                        } else {
4188                            self.options.enable_row_selection
4189                        }
4190                    }
4191                    None => self.options.enable_row_selection,
4192                };
4193                let can_multi_select = match leaf_original {
4194                    Some(original) => {
4195                        if let Some(enable_multi_row_selection) =
4196                            self.enable_multi_row_selection.as_ref()
4197                        {
4198                            enable_multi_row_selection(row.key, original)
4199                        } else {
4200                            self.options.enable_multi_row_selection
4201                        }
4202                    }
4203                    None => self.options.enable_multi_row_selection,
4204                };
4205                let can_select_sub_rows = match leaf_original {
4206                    Some(original) => {
4207                        if let Some(enable_sub_row_selection) =
4208                            self.enable_sub_row_selection.as_ref()
4209                        {
4210                            enable_sub_row_selection(row.key, original)
4211                        } else {
4212                            self.options.enable_sub_row_selection
4213                        }
4214                    }
4215                    None => self.options.enable_sub_row_selection,
4216                };
4217
4218                row_meta_by_key.insert(
4219                    row.key,
4220                    SelectionRowMeta {
4221                        sub_rows,
4222                        can_select,
4223                        can_multi_select,
4224                        can_select_sub_rows,
4225                    },
4226                );
4227            }
4228        }
4229
4230        super::Updater::Func(Arc::new(move |old| {
4231            let current = old.contains(&row_key);
4232            let value = value.unwrap_or(!current);
4233
4234            let mut next = old.clone();
4235
4236            if let Some(row_meta) = row_meta_by_key.get(&row_key)
4237                && row_meta.can_select
4238                && current == value
4239            {
4240                return next;
4241            }
4242
4243            mutate_row_selection(&row_meta_by_key, &mut next, row_key, value, select_children);
4244            next
4245        }))
4246    }
4247
4248    pub fn row_selection_updater_by_id(
4249        &self,
4250        row_id: &str,
4251        search_all: bool,
4252        value: Option<bool>,
4253        select_children: bool,
4254    ) -> Option<super::Updater<super::RowSelectionState>> {
4255        let row_key = self.row_key_for_id(row_id, search_all)?;
4256        Some(self.row_selection_updater(row_key, value, select_children))
4257    }
4258
4259    pub fn toggled_row_selected_by_id(
4260        &self,
4261        row_id: &str,
4262        search_all: bool,
4263        value: Option<bool>,
4264        select_children: bool,
4265    ) -> Option<super::RowSelectionState> {
4266        let updater =
4267            self.row_selection_updater_by_id(row_id, search_all, value, select_children)?;
4268        Some(updater.apply(&self.state.row_selection))
4269    }
4270
4271    pub fn toggled_row_selected(
4272        &self,
4273        row_key: RowKey,
4274        value: Option<bool>,
4275        select_children: bool,
4276    ) -> super::RowSelectionState {
4277        self.row_selection_updater(row_key, value, select_children)
4278            .apply(&self.state.row_selection)
4279    }
4280
4281    pub fn is_all_rows_selected(&self) -> bool {
4282        let can_select =
4283            |row_key: RowKey, row: &Row<'a, TData>| self.row_can_select_for_row(row_key, row);
4284        super::is_all_rows_selected(
4285            self.filtered_row_model(),
4286            &self.state.row_selection,
4287            &can_select,
4288        )
4289    }
4290
4291    pub fn is_some_rows_selected(&self) -> bool {
4292        super::is_some_rows_selected(self.filtered_row_model(), &self.state.row_selection)
4293    }
4294
4295    pub fn is_all_page_rows_selected(&self) -> bool {
4296        let can_select =
4297            |row_key: RowKey, row: &Row<'a, TData>| self.row_can_select_for_row(row_key, row);
4298        super::is_all_page_rows_selected(self.row_model(), &self.state.row_selection, &can_select)
4299    }
4300
4301    pub fn is_some_page_rows_selected(&self) -> bool {
4302        let can_select =
4303            |row_key: RowKey, row: &Row<'a, TData>| self.row_can_select_for_row(row_key, row);
4304        super::is_some_page_rows_selected(
4305            self.row_model(),
4306            self.core_row_model(),
4307            &self.state.row_selection,
4308            &can_select,
4309        )
4310    }
4311
4312    pub fn filtered_row_count(&self) -> usize {
4313        self.filtered_row_model().root_rows().len()
4314    }
4315
4316    pub fn filtered_flat_row_count(&self) -> usize {
4317        self.filtered_row_model().flat_rows().len()
4318    }
4319
4320    pub fn filtered_selected_row_count(&self) -> usize {
4321        super::selected_root_row_count(self.filtered_row_model(), &self.state.row_selection)
4322    }
4323
4324    pub fn filtered_selected_flat_row_count(&self) -> usize {
4325        super::selected_flat_row_count(self.filtered_row_model(), &self.state.row_selection)
4326    }
4327
4328    /// TanStack-aligned: `row.getCanSelect()`.
4329    pub fn row_can_select(&self, row_key: RowKey) -> bool {
4330        let model = self.core_row_model();
4331        model
4332            .row_by_key(row_key)
4333            .and_then(|i| model.row(i))
4334            .is_some_and(|r| self.row_can_select_for_row(row_key, r))
4335    }
4336
4337    /// TanStack-aligned: `row.getCanMultiSelect()`.
4338    pub fn row_can_multi_select(&self, row_key: RowKey) -> bool {
4339        let model = self.core_row_model();
4340        model
4341            .row_by_key(row_key)
4342            .and_then(|i| model.row(i))
4343            .is_some_and(|r| self.row_can_multi_select_for_row(row_key, r))
4344    }
4345
4346    /// TanStack-aligned: `row.getCanSelectSubRows()`.
4347    pub fn row_can_select_sub_rows(&self, row_key: RowKey) -> bool {
4348        let model = self.core_row_model();
4349        model
4350            .row_by_key(row_key)
4351            .and_then(|i| model.row(i))
4352            .is_some_and(|r| self.row_can_select_sub_rows_for_row(row_key, r))
4353    }
4354
4355    pub fn row_can_select_by_id(&self, row_id: &str, search_all: bool) -> Option<bool> {
4356        let row = self.row_by_id(row_id, search_all)?;
4357        Some(self.row_can_select_for_row(row.key, row))
4358    }
4359
4360    pub fn row_can_multi_select_by_id(&self, row_id: &str, search_all: bool) -> Option<bool> {
4361        let row = self.row_by_id(row_id, search_all)?;
4362        Some(self.row_can_multi_select_for_row(row.key, row))
4363    }
4364
4365    pub fn row_can_select_sub_rows_by_id(&self, row_id: &str, search_all: bool) -> Option<bool> {
4366        let row = self.row_by_id(row_id, search_all)?;
4367        Some(self.row_can_select_sub_rows_for_row(row.key, row))
4368    }
4369
4370    pub fn toggled_all_rows_selected(&self, value: Option<bool>) -> super::RowSelectionState {
4371        let can_select =
4372            |row_key: RowKey, row: &Row<'a, TData>| self.row_can_select_for_row(row_key, row);
4373        super::toggle_all_rows_selected(
4374            self.filtered_row_model(),
4375            &self.state.row_selection,
4376            value,
4377            &can_select,
4378        )
4379    }
4380
4381    pub fn toggled_all_page_rows_selected(&self, value: Option<bool>) -> super::RowSelectionState {
4382        let can_select =
4383            |row_key: RowKey, row: &Row<'a, TData>| self.row_can_select_for_row(row_key, row);
4384        let can_multi =
4385            |row_key: RowKey, row: &Row<'a, TData>| self.row_can_multi_select_for_row(row_key, row);
4386        let can_sub = |row_key: RowKey, row: &Row<'a, TData>| {
4387            self.row_can_select_sub_rows_for_row(row_key, row)
4388        };
4389        super::toggle_all_page_rows_selected(
4390            self.row_model(),
4391            self.core_row_model(),
4392            &self.state.row_selection,
4393            value,
4394            &can_select,
4395            &can_multi,
4396            &can_sub,
4397        )
4398    }
4399
4400    pub fn faceted_row_model(&self, column_id: &str) -> Option<&RowModel<'a, TData>> {
4401        let column_index = self.column_index(column_id)?;
4402
4403        if self.options.manual_filtering || self.filtered_row_model_override_pre_filtered {
4404            return Some(self.pre_filtered_row_model());
4405        }
4406
4407        let caches = self.faceted_row_model_by_column.get_or_init(|| {
4408            let mut v = Vec::with_capacity(self.columns.len());
4409            for _ in 0..self.columns.len() {
4410                v.push(OnceCell::new());
4411            }
4412            v
4413        });
4414
4415        Some(caches[column_index].get_or_init(|| {
4416            super::faceted_row_model_excluding(
4417                self.pre_filtered_row_model(),
4418                &self.columns,
4419                &self.state.column_filters,
4420                self.state.global_filter.clone(),
4421                self.options,
4422                &self.filter_fns,
4423                &self.global_filter_fn,
4424                self.get_column_can_global_filter.as_deref(),
4425                Some(column_id),
4426            )
4427        }))
4428    }
4429
4430    pub fn faceted_unique_values(&self, column_id: &str) -> Option<&super::FacetCounts> {
4431        let column_index = self.column_index(column_id)?;
4432        let column = self.column(column_id)?;
4433
4434        let caches = self.faceted_unique_values_by_column.get_or_init(|| {
4435            let mut v = Vec::with_capacity(self.columns.len());
4436            for _ in 0..self.columns.len() {
4437                v.push(OnceCell::new());
4438            }
4439            v
4440        });
4441
4442        Some(caches[column_index].get_or_init(|| {
4443            let model = self
4444                .faceted_row_model(column_id)
4445                .unwrap_or_else(|| self.row_model());
4446            super::faceted_unique_values(model, column)
4447        }))
4448    }
4449
4450    pub fn faceted_unique_value_labels(&self, column_id: &str) -> Option<&super::FacetLabels<'a>> {
4451        let column_index = self.column_index(column_id)?;
4452        let column = self.column(column_id)?;
4453
4454        let caches = self.faceted_unique_labels_by_column.get_or_init(|| {
4455            let mut v = Vec::with_capacity(self.columns.len());
4456            for _ in 0..self.columns.len() {
4457                v.push(OnceCell::new());
4458            }
4459            v
4460        });
4461
4462        Some(caches[column_index].get_or_init(|| {
4463            let model = self
4464                .faceted_row_model(column_id)
4465                .unwrap_or_else(|| self.row_model());
4466            super::faceted_unique_value_labels(model, column)
4467        }))
4468    }
4469
4470    pub fn faceted_min_max_u64(&self, column_id: &str) -> Option<(u64, u64)> {
4471        let column_index = self.column_index(column_id)?;
4472        let column = self.column(column_id)?;
4473
4474        let caches = self.faceted_min_max_u64_by_column.get_or_init(|| {
4475            let mut v = Vec::with_capacity(self.columns.len());
4476            for _ in 0..self.columns.len() {
4477                v.push(OnceCell::new());
4478            }
4479            v
4480        });
4481
4482        *caches[column_index].get_or_init(|| {
4483            let model = self
4484                .faceted_row_model(column_id)
4485                .unwrap_or_else(|| self.row_model());
4486            super::faceted_min_max_u64(model, column)
4487        })
4488    }
4489
4490    /// TanStack-aligned: `table.getGlobalFacetedRowModel()`.
4491    ///
4492    /// Notes:
4493    /// - Matches upstream behavior by returning `pre_filtered_row_model()` when `manualFiltering=true`.
4494    /// - When no override is provided, this defaults to `filtered_row_model()` (which already
4495    ///   respects `manualFiltering`).
4496    pub fn global_faceted_row_model(&self) -> &RowModel<'a, TData> {
4497        if self.options.manual_filtering {
4498            return self.pre_filtered_row_model();
4499        }
4500
4501        let Some(get) = self.get_global_faceted_row_model.as_ref() else {
4502            return self.filtered_row_model();
4503        };
4504
4505        self.global_faceted_row_model.get_or_init(|| get(self))
4506    }
4507
4508    /// TanStack-aligned: `table.getGlobalFacetedUniqueValues()`.
4509    ///
4510    /// Notes:
4511    /// - Upstream does **not** gate this by `manualFiltering`; only the row model surface is gated.
4512    pub fn global_faceted_unique_values(&self) -> &super::FacetCounts {
4513        if let Some(get) = self.get_global_faceted_unique_values.as_ref() {
4514            return self.global_faceted_unique_values.get_or_init(|| get(self));
4515        }
4516
4517        self.global_faceted_unique_values
4518            .get_or_init(super::FacetCounts::new)
4519    }
4520
4521    /// TanStack-aligned: `table.getGlobalFacetedMinMaxValues()` (u64-only mapping).
4522    ///
4523    /// Notes:
4524    /// - Upstream does **not** gate this by `manualFiltering`; only the row model surface is gated.
4525    pub fn global_faceted_min_max_u64(&self) -> Option<(u64, u64)> {
4526        if let Some(get) = self.get_global_faceted_min_max_u64.as_ref() {
4527            return *self.global_faceted_min_max_u64.get_or_init(|| get(self));
4528        }
4529
4530        *self.global_faceted_min_max_u64.get_or_init(|| None)
4531    }
4532}
4533
4534impl<'a, TData> Table<'a, TData> {
4535    fn find_column_in_tree(&self, column_id: &str) -> Option<&super::ColumnDef<TData>> {
4536        fn find<'c, TData>(
4537            cols: &'c [super::ColumnDef<TData>],
4538            id: &str,
4539        ) -> Option<&'c super::ColumnDef<TData>> {
4540            for col in cols {
4541                if col.id.as_ref() == id {
4542                    return Some(col);
4543                }
4544                if let Some(found) = find(&col.columns, id) {
4545                    return Some(found);
4546                }
4547            }
4548            None
4549        }
4550
4551        find(self.column_tree.as_slice(), column_id)
4552    }
4553
4554    /// TanStack-aligned: `table.getColumn(columnId)`.
4555    ///
4556    /// Note: unlike [`Self::column`], this searches the full column tree and can return group
4557    /// columns as well as leaf columns.
4558    pub fn column_any(&self, column_id: &str) -> Option<&super::ColumnDef<TData>> {
4559        self.find_column_in_tree(column_id)
4560    }
4561
4562    /// TanStack-aligned: `table.getAllColumns()` snapshot surface.
4563    pub fn column_tree_snapshot(&self) -> Vec<super::ColumnNodeSnapshot> {
4564        fn push_column_nodes<TData>(
4565            cols: &[super::ColumnDef<TData>],
4566            depth: usize,
4567            parent_id: Option<Arc<str>>,
4568            out: &mut Vec<super::ColumnNodeSnapshot>,
4569        ) {
4570            for col in cols {
4571                let id = col.id.clone();
4572                let child_ids: Vec<Arc<str>> = col.columns.iter().map(|c| c.id.clone()).collect();
4573                out.push(super::ColumnNodeSnapshot {
4574                    id: id.clone(),
4575                    depth,
4576                    parent_id: parent_id.clone(),
4577                    child_ids,
4578                });
4579                if !col.columns.is_empty() {
4580                    push_column_nodes(&col.columns, depth + 1, Some(id), out);
4581                }
4582            }
4583        }
4584
4585        let mut out = Vec::new();
4586        push_column_nodes(&self.column_tree, 0, None, &mut out);
4587        out
4588    }
4589
4590    /// TanStack-aligned: `table.getColumn(columnId)` structural snapshot.
4591    pub fn column_node_snapshot(&self, column_id: &str) -> Option<super::ColumnNodeSnapshot> {
4592        fn find<TData>(
4593            cols: &[super::ColumnDef<TData>],
4594            depth: usize,
4595            parent_id: Option<Arc<str>>,
4596            id: &str,
4597        ) -> Option<super::ColumnNodeSnapshot> {
4598            for col in cols {
4599                let col_id = col.id.clone();
4600                let next_parent = Some(col_id.clone());
4601                if col_id.as_ref() == id {
4602                    let child_ids: Vec<Arc<str>> =
4603                        col.columns.iter().map(|c| c.id.clone()).collect();
4604                    return Some(super::ColumnNodeSnapshot {
4605                        id: col_id,
4606                        depth,
4607                        parent_id,
4608                        child_ids,
4609                    });
4610                }
4611
4612                if !col.columns.is_empty()
4613                    && let Some(found) = find(&col.columns, depth + 1, next_parent, id)
4614                {
4615                    return Some(found);
4616                }
4617            }
4618            None
4619        }
4620
4621        find(self.column_tree.as_slice(), 0, None, column_id)
4622    }
4623
4624    fn column_leaf_ids_for(&self, column_id: &str) -> Option<Vec<super::ColumnId>> {
4625        fn push_leaf_ids<TData>(col: &super::ColumnDef<TData>, out: &mut Vec<super::ColumnId>) {
4626            if col.columns.is_empty() {
4627                out.push(col.id.clone());
4628                return;
4629            }
4630            for c in &col.columns {
4631                push_leaf_ids(c, out);
4632            }
4633        }
4634
4635        let col = self.find_column_in_tree(column_id)?;
4636        let mut leaf_ids = Vec::new();
4637        push_leaf_ids(col, &mut leaf_ids);
4638        Some(leaf_ids)
4639    }
4640}
4641
4642fn splitmix64(mut z: u64) -> u64 {
4643    z = z.wrapping_add(0x9e37_79b9_7f4a_7c15);
4644    z = (z ^ (z >> 30)).wrapping_mul(0xbf58_476d_1ce4_e5b9);
4645    z = (z ^ (z >> 27)).wrapping_mul(0x94d0_49bb_1331_11eb);
4646    z ^ (z >> 31)
4647}
4648
4649fn default_row_key_for_index_path<TData>(
4650    _: &TData,
4651    index: usize,
4652    parent: Option<&RowKey>,
4653) -> RowKey {
4654    if let Some(parent) = parent {
4655        // Mix parent and child index in an order-sensitive way (avoid trivial collisions like
4656        // `(parent=0, i=1)` vs `(parent=1, i=0)` that happen with XOR).
4657        let z = parent
4658            .0
4659            .wrapping_mul(0x9e37_79b9_7f4a_7c15)
4660            .wrapping_add((index as u64).wrapping_add(0xbf58_476d_1ce4_e5b9));
4661        RowKey(splitmix64(z))
4662    } else {
4663        RowKey::from_index(index)
4664    }
4665}
4666
4667fn build_core_row_model<'a, TData>(
4668    data: &'a [TData],
4669    get_row_key: &dyn Fn(&TData, usize, Option<&RowKey>) -> RowKey,
4670    get_row_id: Option<&dyn Fn(&TData, usize, Option<&RowId>) -> RowId>,
4671    get_sub_rows: Option<&dyn for<'r> Fn(&'r TData, usize) -> Option<&'r [TData]>>,
4672) -> RowModel<'a, TData> {
4673    let mut root_rows: Vec<RowIndex> = Vec::new();
4674    let mut flat_rows: Vec<RowIndex> = Vec::new();
4675    let mut rows_by_key: HashMap<RowKey, RowIndex> = HashMap::new();
4676    let mut rows_by_id: HashMap<RowId, RowIndex> = HashMap::new();
4677    let mut arena: Vec<Row<'a, TData>> = Vec::new();
4678
4679    fn access_rows<'a, TData>(
4680        original_rows: &'a [TData],
4681        depth: u16,
4682        parent: Option<RowIndex>,
4683        parent_key: Option<&RowKey>,
4684        parent_id: Option<RowId>,
4685        get_row_key: &dyn Fn(&TData, usize, Option<&RowKey>) -> RowKey,
4686        get_row_id: Option<&dyn Fn(&TData, usize, Option<&RowId>) -> RowId>,
4687        get_sub_rows: Option<&dyn for<'r> Fn(&'r TData, usize) -> Option<&'r [TData]>>,
4688        root_out: &mut Vec<RowIndex>,
4689        flat_out: &mut Vec<RowIndex>,
4690        rows_by_key: &mut HashMap<RowKey, RowIndex>,
4691        rows_by_id: &mut HashMap<RowId, RowIndex>,
4692        arena: &mut Vec<Row<'a, TData>>,
4693    ) -> Vec<RowIndex> {
4694        let mut rows: Vec<RowIndex> = Vec::with_capacity(original_rows.len());
4695        for (index, original) in original_rows.iter().enumerate() {
4696            let key = get_row_key(original, index, parent_key);
4697            let id = match get_row_id {
4698                None => match parent_id.as_ref() {
4699                    None => RowId::new(index.to_string()),
4700                    Some(parent_id) => RowId::new(format!("{}.{}", parent_id.as_str(), index)),
4701                },
4702                Some(f) => f(original, index, parent_id.as_ref()),
4703            };
4704            let row_index = arena.len();
4705            arena.push(Row {
4706                id: id.clone(),
4707                key,
4708                original,
4709                index,
4710                depth,
4711                parent,
4712                parent_key: parent_key.copied(),
4713                sub_rows: Vec::new(),
4714            });
4715            flat_out.push(row_index);
4716            rows_by_key.insert(key, row_index);
4717            rows_by_id.insert(id, row_index);
4718            rows.push(row_index);
4719
4720            if let Some(get_sub_rows) = get_sub_rows
4721                && let Some(sub) = get_sub_rows(original, index)
4722                && !sub.is_empty()
4723            {
4724                let next_parent_id = arena[row_index].id.clone();
4725                let children = access_rows(
4726                    sub,
4727                    depth.saturating_add(1),
4728                    Some(row_index),
4729                    Some(&key),
4730                    Some(next_parent_id),
4731                    get_row_key,
4732                    get_row_id,
4733                    Some(get_sub_rows),
4734                    root_out,
4735                    flat_out,
4736                    rows_by_key,
4737                    rows_by_id,
4738                    arena,
4739                );
4740                if let Some(row) = arena.get_mut(row_index) {
4741                    row.sub_rows = children;
4742                }
4743            }
4744        }
4745
4746        if depth == 0 {
4747            root_out.extend_from_slice(&rows);
4748        }
4749
4750        rows
4751    }
4752
4753    let _ = access_rows(
4754        data,
4755        0,
4756        None,
4757        None,
4758        None,
4759        get_row_key,
4760        get_row_id,
4761        get_sub_rows,
4762        &mut root_rows,
4763        &mut flat_rows,
4764        &mut rows_by_key,
4765        &mut rows_by_id,
4766        &mut arena,
4767    );
4768
4769    RowModel {
4770        root_rows,
4771        flat_rows,
4772        rows_by_key,
4773        rows_by_id,
4774        arena,
4775    }
4776}
4777
4778#[cfg(test)]
4779mod tests {
4780    use super::*;
4781    use crate::table::is_column_visible;
4782    use crate::table::{
4783        ColumnDef, ColumnFilter, ColumnId, ColumnPinPosition, ColumnSizingRegion, PaginationState,
4784        SortSpec, TableOptions, TableState, TanStackValue, create_column_helper,
4785    };
4786    use std::sync::Arc;
4787
4788    #[derive(Debug, Clone)]
4789    struct Person {
4790        #[allow(dead_code)]
4791        name: String,
4792        sub_rows: Option<Vec<Person>>,
4793    }
4794
4795    fn make_people(rows: usize, sub_rows: usize) -> Vec<Person> {
4796        (0..rows)
4797            .map(|i| Person {
4798                name: format!("Person {i}"),
4799                sub_rows: (sub_rows > 0).then(|| {
4800                    (0..sub_rows)
4801                        .map(|j| Person {
4802                            name: format!("Person {i}.{j}"),
4803                            sub_rows: None,
4804                        })
4805                        .collect()
4806                }),
4807            })
4808            .collect()
4809    }
4810
4811    #[test]
4812    fn core_row_model_produces_flat_rows_and_key_map() {
4813        let data = make_people(3, 0);
4814        let table = Table::builder(&data).build();
4815        let model = table.core_row_model();
4816
4817        assert_eq!(model.root_rows().len(), 3);
4818        assert_eq!(model.flat_rows().len(), 3);
4819        assert!(model.row_by_key(RowKey::from_index(0)).is_some());
4820        assert!(model.row_by_key(RowKey::from_index(1)).is_some());
4821        assert!(model.row_by_key(RowKey::from_index(2)).is_some());
4822    }
4823
4824    #[test]
4825    fn core_row_model_recurses_into_sub_rows_and_assigns_unique_keys() {
4826        let data = make_people(3, 2);
4827        let table = Table::builder(&data)
4828            .get_sub_rows(|p, _| p.sub_rows.as_deref())
4829            .build();
4830        let model = table.core_row_model();
4831
4832        assert_eq!(model.root_rows().len(), 3);
4833        assert_eq!(model.flat_rows().len(), 3 + 3 * 2);
4834
4835        let root_0 = model.row(model.root_rows()[0]).expect("root row 0");
4836        assert_eq!(root_0.sub_rows.len(), 2);
4837
4838        let c0 = model.row(root_0.sub_rows[0]).expect("root 0 child 0").key;
4839        let c1 = model.row(root_0.sub_rows[1]).expect("root 0 child 1").key;
4840        assert_ne!(c0, c1);
4841        assert_ne!(c0, root_0.key);
4842        assert_ne!(c1, root_0.key);
4843        assert!(model.row_by_key(c0).is_some());
4844        assert!(model.row_by_key(c1).is_some());
4845    }
4846
4847    #[test]
4848    fn core_row_model_allows_custom_stable_row_keys() {
4849        let data = make_people(2, 0);
4850        let table = Table::builder(&data)
4851            .get_row_key(|_p, i, _parent| RowKey(10_000 + i as u64))
4852            .build();
4853        let model = table.core_row_model();
4854
4855        assert!(model.row_by_key(RowKey(10_000)).is_some());
4856        assert!(model.row_by_key(RowKey(10_001)).is_some());
4857        assert!(model.row_by_key(RowKey::from_index(0)).is_none());
4858    }
4859
4860    #[derive(Debug, Clone)]
4861    struct Item {
4862        value: i32,
4863    }
4864
4865    #[test]
4866    fn table_sorted_row_model_uses_state_sorting() {
4867        let data = vec![Item { value: 2 }, Item { value: 1 }, Item { value: 3 }];
4868
4869        let helper = create_column_helper::<Item>();
4870        let columns = vec![helper.accessor("value", |it| it.value)];
4871
4872        let table = Table::builder(&data)
4873            .columns(columns)
4874            .state(TableState {
4875                sorting: vec![SortSpec {
4876                    column: "value".into(),
4877                    desc: false,
4878                }],
4879                ..TableState::default()
4880            })
4881            .build();
4882
4883        let sorted = table.sorted_row_model();
4884        let keys = sorted
4885            .root_rows()
4886            .iter()
4887            .filter_map(|&i| sorted.row(i).map(|r| r.key.0))
4888            .collect::<Vec<_>>();
4889
4890        assert_eq!(keys, vec![1, 0, 2]);
4891        assert!(std::ptr::eq(sorted, table.sorted_row_model()));
4892    }
4893
4894    #[test]
4895    fn row_unique_values_uses_column_hook_or_falls_back_to_single_get_value() {
4896        #[derive(Debug, Clone)]
4897        struct UniqueItem {
4898            tags: Vec<&'static str>,
4899        }
4900
4901        let data = vec![UniqueItem {
4902            tags: vec!["a", "b"],
4903        }];
4904
4905        let col = ColumnDef::new("tags")
4906            .sort_value_by(|it: &UniqueItem| {
4907                TanStackValue::Array(
4908                    it.tags
4909                        .iter()
4910                        .map(|s| TanStackValue::String(Arc::<str>::from(*s)))
4911                        .collect(),
4912                )
4913            })
4914            .unique_values_by(|it: &UniqueItem, _index| {
4915                it.tags
4916                    .iter()
4917                    .map(|s| TanStackValue::String(Arc::<str>::from(*s)))
4918                    .collect()
4919            });
4920
4921        let table = Table::builder(&data).columns(vec![col]).build();
4922        let row_key = RowKey::from_index(0);
4923        assert_eq!(
4924            table.row_unique_values(row_key, "tags").unwrap(),
4925            vec![
4926                TanStackValue::String(Arc::<str>::from("a")),
4927                TanStackValue::String(Arc::<str>::from("b")),
4928            ]
4929        );
4930
4931        let col = ColumnDef::new("tags").sort_value_by(|it: &UniqueItem| {
4932            TanStackValue::Array(
4933                it.tags
4934                    .iter()
4935                    .map(|s| TanStackValue::String(Arc::<str>::from(*s)))
4936                    .collect(),
4937            )
4938        });
4939
4940        let table = Table::builder(&data).columns(vec![col]).build();
4941        assert_eq!(
4942            table.row_unique_values(row_key, "tags").unwrap(),
4943            vec![TanStackValue::Array(vec![
4944                TanStackValue::String(Arc::<str>::from("a")),
4945                TanStackValue::String(Arc::<str>::from("b")),
4946            ])]
4947        );
4948
4949        let col = ColumnDef::<UniqueItem>::new("missing_accessor");
4950        let table = Table::builder(&data).columns(vec![col]).build();
4951        assert_eq!(table.row_unique_values(row_key, "missing_accessor"), None);
4952    }
4953
4954    #[test]
4955    fn table_row_model_applies_pagination_after_sorting() {
4956        let data = vec![
4957            Item { value: 2 },
4958            Item { value: 1 },
4959            Item { value: 3 },
4960            Item { value: 0 },
4961        ];
4962
4963        let helper = create_column_helper::<Item>();
4964        let columns = vec![helper.accessor("value", |it| it.value)];
4965
4966        let table = Table::builder(&data)
4967            .columns(columns)
4968            .state(TableState {
4969                sorting: vec![SortSpec {
4970                    column: "value".into(),
4971                    desc: false,
4972                }],
4973                pagination: PaginationState {
4974                    page_index: 0,
4975                    page_size: 2,
4976                },
4977                ..TableState::default()
4978            })
4979            .build();
4980
4981        let paged = table.row_model();
4982        let keys = paged
4983            .root_rows()
4984            .iter()
4985            .filter_map(|&i| paged.row(i).map(|r| r.key.0))
4986            .collect::<Vec<_>>();
4987
4988        assert_eq!(keys, vec![3, 1]);
4989        assert!(std::ptr::eq(paged, table.row_model()));
4990    }
4991
4992    #[test]
4993    fn table_selected_row_model_uses_state_row_selection() {
4994        let data = make_people(3, 0);
4995        let table = Table::builder(&data)
4996            .state(TableState {
4997                row_selection: [RowKey(1)].into_iter().collect(),
4998                ..TableState::default()
4999            })
5000            .build();
5001
5002        let selected = table.selected_row_model();
5003        assert_eq!(selected.root_rows().len(), 1);
5004        assert!(selected.row_by_key(RowKey(1)).is_some());
5005        assert!(std::ptr::eq(selected, table.selected_row_model()));
5006    }
5007
5008    #[test]
5009    fn table_visible_columns_respects_order_then_visibility() {
5010        let data = vec![Item { value: 1 }];
5011
5012        let helper = create_column_helper::<Item>();
5013        let columns = vec![
5014            helper.clone().accessor("a", |it| it.value),
5015            helper.clone().accessor("b", |it| it.value),
5016            helper.accessor("c", |it| it.value),
5017        ];
5018
5019        let mut state = TableState::default();
5020        state.column_order = vec!["c".into(), "a".into()];
5021        state.column_visibility.insert("b".into(), false);
5022
5023        let table = Table::builder(&data).columns(columns).state(state).build();
5024        let visible = table.visible_columns();
5025        let ids = visible.iter().map(|c| c.id.as_ref()).collect::<Vec<_>>();
5026
5027        assert_eq!(ids, vec!["c", "a"]);
5028    }
5029
5030    #[test]
5031    fn table_column_size_reads_from_state() {
5032        let data = vec![Item { value: 1 }];
5033
5034        let helper = create_column_helper::<Item>();
5035        let columns = vec![helper.accessor("value", |it| it.value)];
5036
5037        let mut state = TableState::default();
5038        state.column_sizing.insert("value".into(), 120.0);
5039
5040        let table = Table::builder(&data).columns(columns).state(state).build();
5041        assert_eq!(table.column_size("value"), Some(120.0));
5042        assert_eq!(table.column_size("missing"), None);
5043    }
5044
5045    #[derive(Debug, Clone)]
5046    struct TreeNode {
5047        id: u64,
5048        children: Vec<TreeNode>,
5049    }
5050
5051    #[test]
5052    fn expanding_default_is_collapsed_and_does_not_allocate() {
5053        let data = vec![
5054            TreeNode {
5055                id: 1,
5056                children: vec![
5057                    TreeNode {
5058                        id: 10,
5059                        children: Vec::new(),
5060                    },
5061                    TreeNode {
5062                        id: 11,
5063                        children: Vec::new(),
5064                    },
5065                ],
5066            },
5067            TreeNode {
5068                id: 2,
5069                children: vec![TreeNode {
5070                    id: 20,
5071                    children: Vec::new(),
5072                }],
5073            },
5074        ];
5075
5076        let table = Table::builder(&data)
5077            .get_row_key(|n, _i, _parent| RowKey(n.id))
5078            .get_sub_rows(|n, _i| Some(n.children.as_slice()))
5079            .build();
5080
5081        let pre = table.pre_expanded_row_model();
5082        let expanded = table.expanded_row_model();
5083        assert!(
5084            std::ptr::eq(pre, expanded),
5085            "empty expanding should not allocate"
5086        );
5087        assert_eq!(expanded.root_rows().len(), 2, "collapsed shows only roots");
5088    }
5089
5090    #[test]
5091    fn expanding_flattens_visible_rows_under_expanded_parents() {
5092        let data = vec![
5093            TreeNode {
5094                id: 1,
5095                children: vec![
5096                    TreeNode {
5097                        id: 10,
5098                        children: Vec::new(),
5099                    },
5100                    TreeNode {
5101                        id: 11,
5102                        children: Vec::new(),
5103                    },
5104                ],
5105            },
5106            TreeNode {
5107                id: 2,
5108                children: vec![TreeNode {
5109                    id: 20,
5110                    children: Vec::new(),
5111                }],
5112            },
5113        ];
5114
5115        let mut state = TableState::default();
5116        state.expanding = [RowKey(1)].into_iter().collect();
5117
5118        let table = Table::builder(&data)
5119            .get_row_key(|n, _i, _parent| RowKey(n.id))
5120            .get_sub_rows(|n, _i| Some(n.children.as_slice()))
5121            .state(state)
5122            .build();
5123
5124        let expanded = table.expanded_row_model();
5125        let keys = expanded
5126            .root_rows()
5127            .iter()
5128            .filter_map(|&i| expanded.row(i).map(|r| r.key.0))
5129            .collect::<Vec<_>>();
5130
5131        assert_eq!(keys, vec![1, 10, 11, 2]);
5132    }
5133
5134    #[test]
5135    fn paginate_expanded_rows_true_counts_children_in_pages() {
5136        let data = vec![
5137            TreeNode {
5138                id: 1,
5139                children: vec![
5140                    TreeNode {
5141                        id: 10,
5142                        children: Vec::new(),
5143                    },
5144                    TreeNode {
5145                        id: 11,
5146                        children: Vec::new(),
5147                    },
5148                ],
5149            },
5150            TreeNode {
5151                id: 2,
5152                children: vec![TreeNode {
5153                    id: 20,
5154                    children: Vec::new(),
5155                }],
5156            },
5157        ];
5158
5159        let mut state = TableState::default();
5160        state.expanding = [RowKey(1)].into_iter().collect();
5161        state.pagination = PaginationState {
5162            page_index: 0,
5163            page_size: 2,
5164        };
5165
5166        let table = Table::builder(&data)
5167            .get_row_key(|n, _i, _parent| RowKey(n.id))
5168            .get_sub_rows(|n, _i| Some(n.children.as_slice()))
5169            .state(state)
5170            .build();
5171
5172        let model = table.row_model();
5173        let keys = model
5174            .root_rows()
5175            .iter()
5176            .filter_map(|&i| model.row(i).map(|r| r.key.0))
5177            .collect::<Vec<_>>();
5178        assert_eq!(keys, vec![1, 10]);
5179    }
5180
5181    #[test]
5182    fn paginate_expanded_rows_false_expands_within_parent_page() {
5183        let data = vec![
5184            TreeNode {
5185                id: 1,
5186                children: vec![
5187                    TreeNode {
5188                        id: 10,
5189                        children: Vec::new(),
5190                    },
5191                    TreeNode {
5192                        id: 11,
5193                        children: Vec::new(),
5194                    },
5195                ],
5196            },
5197            TreeNode {
5198                id: 2,
5199                children: vec![TreeNode {
5200                    id: 20,
5201                    children: Vec::new(),
5202                }],
5203            },
5204        ];
5205
5206        let mut state = TableState::default();
5207        state.expanding = [RowKey(1)].into_iter().collect();
5208        state.pagination = PaginationState {
5209            page_index: 0,
5210            page_size: 1,
5211        };
5212
5213        let table = Table::builder(&data)
5214            .get_row_key(|n, _i, _parent| RowKey(n.id))
5215            .get_sub_rows(|n, _i| Some(n.children.as_slice()))
5216            .state(state)
5217            .options(TableOptions {
5218                paginate_expanded_rows: false,
5219                ..Default::default()
5220            })
5221            .build();
5222
5223        let model = table.row_model();
5224        let keys = model
5225            .root_rows()
5226            .iter()
5227            .filter_map(|&i| model.row(i).map(|r| r.key.0))
5228            .collect::<Vec<_>>();
5229        assert_eq!(keys, vec![1, 10, 11]);
5230    }
5231
5232    #[test]
5233    fn manual_filtering_skips_filtered_row_model() {
5234        #[derive(Debug, Clone)]
5235        struct Item {
5236            label: Arc<str>,
5237        }
5238
5239        let data = vec![
5240            Item {
5241                label: Arc::from("a"),
5242            },
5243            Item {
5244                label: Arc::from("b"),
5245            },
5246        ];
5247
5248        let helper = create_column_helper::<Item>();
5249        let columns = vec![
5250            helper
5251                .accessor("label", |it| it.label.clone())
5252                .filter_by(|row, q| row.label.as_ref() == q),
5253        ];
5254
5255        let mut state = TableState::default();
5256        state.global_filter = Some(serde_json::Value::String("b".to_string()));
5257
5258        let table = Table::builder(&data)
5259            .columns(columns)
5260            .state(state)
5261            .options(TableOptions {
5262                manual_filtering: true,
5263                ..Default::default()
5264            })
5265            .build();
5266
5267        assert!(std::ptr::eq(
5268            table.filtered_row_model(),
5269            table.core_row_model()
5270        ));
5271    }
5272
5273    #[test]
5274    fn filtered_row_model_override_skips_filtered_and_faceted_row_models() {
5275        #[derive(Debug, Clone)]
5276        struct Item {
5277            label: Arc<str>,
5278        }
5279
5280        let data = vec![
5281            Item {
5282                label: Arc::from("a"),
5283            },
5284            Item {
5285                label: Arc::from("b"),
5286            },
5287        ];
5288
5289        let helper = create_column_helper::<Item>();
5290        let columns = vec![
5291            helper
5292                .accessor("label", |it| it.label.clone())
5293                .filter_by(|row, q| row.label.as_ref() == q),
5294        ];
5295
5296        let mut state = TableState::default();
5297        state.column_filters = vec![ColumnFilter {
5298            column: "label".into(),
5299            value: serde_json::Value::String("b".to_string()),
5300        }];
5301
5302        let table = Table::builder(&data)
5303            .columns(columns)
5304            .state(state)
5305            .override_filtered_row_model_pre_filtered()
5306            .build();
5307
5308        assert!(std::ptr::eq(
5309            table.filtered_row_model(),
5310            table.pre_filtered_row_model()
5311        ));
5312        let faceted = table.faceted_row_model("label").expect("faceted row model");
5313        assert!(std::ptr::eq(faceted, table.pre_filtered_row_model()));
5314
5315        let snapshot = table.row_filter_state_snapshot();
5316        assert!(snapshot.filterable_ids.is_empty());
5317        assert!(snapshot.row_column_filters.is_empty());
5318        assert!(snapshot.row_column_filters_meta.is_empty());
5319    }
5320
5321    #[test]
5322    fn manual_sorting_skips_sorted_row_model() {
5323        #[derive(Debug, Clone)]
5324        struct Item {
5325            value: i32,
5326        }
5327
5328        let data = vec![Item { value: 2 }, Item { value: 1 }];
5329        let helper = create_column_helper::<Item>();
5330        let columns = vec![helper.accessor("value", |it| it.value)];
5331
5332        let mut state = TableState::default();
5333        state.sorting = vec![SortSpec {
5334            column: "value".into(),
5335            desc: false,
5336        }];
5337
5338        let table = Table::builder(&data)
5339            .columns(columns)
5340            .state(state)
5341            .options(TableOptions {
5342                manual_sorting: true,
5343                ..Default::default()
5344            })
5345            .build();
5346
5347        assert!(std::ptr::eq(
5348            table.sorted_row_model(),
5349            table.pre_sorted_row_model()
5350        ));
5351    }
5352
5353    #[test]
5354    fn sorted_row_model_override_skips_sorted_row_model() {
5355        #[derive(Debug, Clone)]
5356        struct Item {
5357            value: i32,
5358        }
5359
5360        let data = vec![Item { value: 2 }, Item { value: 1 }];
5361        let helper = create_column_helper::<Item>();
5362        let columns = vec![helper.accessor("value", |it| it.value)];
5363
5364        let mut state = TableState::default();
5365        state.sorting = vec![SortSpec {
5366            column: "value".into(),
5367            desc: false,
5368        }];
5369
5370        let table = Table::builder(&data)
5371            .columns(columns)
5372            .state(state)
5373            .override_sorted_row_model_pre_sorted()
5374            .build();
5375
5376        assert!(std::ptr::eq(
5377            table.sorted_row_model(),
5378            table.pre_sorted_row_model()
5379        ));
5380    }
5381
5382    #[test]
5383    fn manual_pagination_skips_row_model() {
5384        #[derive(Debug, Clone)]
5385        struct Item {
5386            #[allow(dead_code)]
5387            value: i32,
5388        }
5389
5390        let data = (0..20).map(|i| Item { value: i }).collect::<Vec<_>>();
5391        let mut state = TableState::default();
5392        state.pagination = PaginationState {
5393            page_index: 1,
5394            page_size: 5,
5395        };
5396
5397        let table = Table::builder(&data)
5398            .state(state)
5399            .options(TableOptions {
5400                manual_pagination: true,
5401                ..Default::default()
5402            })
5403            .build();
5404
5405        assert!(std::ptr::eq(
5406            table.row_model(),
5407            table.pre_pagination_row_model()
5408        ));
5409    }
5410
5411    #[test]
5412    fn table_expanding_all_marks_all_rows_expanded() {
5413        let data = vec![TreeNode {
5414            id: 1,
5415            children: vec![TreeNode {
5416                id: 10,
5417                children: Vec::new(),
5418            }],
5419        }];
5420
5421        let mut state = TableState::default();
5422        state.expanding = crate::table::ExpandingState::All;
5423
5424        let table = Table::builder(&data)
5425            .get_row_key(|n, _i, _parent| RowKey(n.id))
5426            .get_sub_rows(|n, _i| Some(n.children.as_slice()))
5427            .state(state)
5428            .build();
5429
5430        assert!(table.is_some_rows_expanded());
5431        assert!(table.is_all_rows_expanded());
5432        assert!(table.row_can_expand(RowKey(1)));
5433        assert!(table.row_is_all_parents_expanded(RowKey(10)));
5434    }
5435
5436    #[test]
5437    fn keep_pinned_rows_true_keeps_pins_across_pagination() {
5438        #[derive(Debug, Clone)]
5439        struct Item {
5440            #[allow(dead_code)]
5441            value: usize,
5442        }
5443
5444        let data = (0..20).map(|i| Item { value: i }).collect::<Vec<_>>();
5445
5446        let mut state = TableState::default();
5447        state.pagination = PaginationState {
5448            page_index: 1,
5449            page_size: 5,
5450        };
5451        state.row_pinning.top = vec![RowKey(1)];
5452
5453        let table_keep = Table::builder(&data)
5454            .state(state.clone())
5455            .options(TableOptions {
5456                keep_pinned_rows: true,
5457                ..Default::default()
5458            })
5459            .build();
5460        assert_eq!(table_keep.top_row_keys(), vec![RowKey(1)]);
5461
5462        let table_no_keep = Table::builder(&data)
5463            .state(state)
5464            .options(TableOptions {
5465                keep_pinned_rows: false,
5466                ..Default::default()
5467            })
5468            .build();
5469        assert!(table_no_keep.top_row_keys().is_empty());
5470    }
5471
5472    #[test]
5473    fn keep_pinned_rows_true_respects_expanded_parents() {
5474        let data = vec![TreeNode {
5475            id: 1,
5476            children: vec![TreeNode {
5477                id: 10,
5478                children: Vec::new(),
5479            }],
5480        }];
5481
5482        let mut collapsed = TableState::default();
5483        collapsed.row_pinning.top = vec![RowKey(10)];
5484
5485        let table_collapsed = Table::builder(&data)
5486            .get_row_key(|n, _i, _p| RowKey(n.id))
5487            .get_sub_rows(|n, _i| Some(n.children.as_slice()))
5488            .state(collapsed)
5489            .options(TableOptions {
5490                keep_pinned_rows: true,
5491                ..Default::default()
5492            })
5493            .build();
5494        assert!(table_collapsed.top_row_keys().is_empty());
5495
5496        let mut expanded = TableState::default();
5497        expanded.expanding = [RowKey(1)].into_iter().collect();
5498        expanded.row_pinning.top = vec![RowKey(10)];
5499
5500        let table_expanded = Table::builder(&data)
5501            .get_row_key(|n, _i, _p| RowKey(n.id))
5502            .get_sub_rows(|n, _i| Some(n.children.as_slice()))
5503            .state(expanded)
5504            .options(TableOptions {
5505                keep_pinned_rows: true,
5506                ..Default::default()
5507            })
5508            .build();
5509        assert_eq!(table_expanded.top_row_keys(), vec![RowKey(10)]);
5510    }
5511
5512    #[test]
5513    fn center_row_keys_excludes_pinned_rows() {
5514        #[derive(Debug, Clone)]
5515        struct Item {
5516            #[allow(dead_code)]
5517            value: usize,
5518        }
5519
5520        let data = (0..5).map(|i| Item { value: i }).collect::<Vec<_>>();
5521        let mut state = TableState::default();
5522        state.row_pinning.top = vec![RowKey(0)];
5523        state.row_pinning.bottom = vec![RowKey(4)];
5524
5525        let table = Table::builder(&data).state(state).build();
5526        assert_eq!(
5527            table.center_row_keys(),
5528            vec![RowKey(1), RowKey(2), RowKey(3)]
5529        );
5530    }
5531
5532    #[test]
5533    fn table_faceting_excludes_own_filter_and_can_return_labels() {
5534        #[derive(Debug, Clone)]
5535        struct Item {
5536            status_key: u64,
5537            status_label: Arc<str>,
5538            role_key: u64,
5539            role_label: Arc<str>,
5540        }
5541
5542        let data = vec![
5543            Item {
5544                status_key: 1,
5545                status_label: "A".into(),
5546                role_key: 10,
5547                role_label: "X".into(),
5548            },
5549            Item {
5550                status_key: 2,
5551                status_label: "B".into(),
5552                role_key: 10,
5553                role_label: "X".into(),
5554            },
5555            Item {
5556                status_key: 1,
5557                status_label: "A".into(),
5558                role_key: 20,
5559                role_label: "Y".into(),
5560            },
5561        ];
5562
5563        let status = ColumnDef::new("status")
5564            .filter_by(|it: &Item, q| it.status_label.as_ref() == q)
5565            .facet_key_by(|it: &Item| it.status_key)
5566            .facet_str_by(|it: &Item| it.status_label.as_ref());
5567        let role = ColumnDef::new("role")
5568            .filter_by(|it: &Item, q| it.role_label.as_ref() == q)
5569            .facet_key_by(|it: &Item| it.role_key)
5570            .facet_str_by(|it: &Item| it.role_label.as_ref());
5571
5572        let mut state = TableState::default();
5573        state.column_filters = vec![
5574            ColumnFilter {
5575                column: "status".into(),
5576                value: serde_json::Value::from("A"),
5577            },
5578            ColumnFilter {
5579                column: "role".into(),
5580                value: serde_json::Value::from("X"),
5581            },
5582        ];
5583
5584        let table = Table::builder(&data)
5585            .columns(vec![status, role])
5586            .state(state)
5587            .build();
5588
5589        let counts = table.faceted_unique_values("status").unwrap();
5590        assert_eq!(counts.get(&1).copied(), Some(1));
5591        assert_eq!(counts.get(&2).copied(), Some(1));
5592
5593        let labels = table.faceted_unique_value_labels("status").unwrap();
5594        assert_eq!(labels.get(&1).copied(), Some("A"));
5595        assert_eq!(labels.get(&2).copied(), Some("B"));
5596
5597        assert_eq!(table.faceted_min_max_u64("status"), Some((1, 2)));
5598    }
5599
5600    #[test]
5601    fn table_row_selection_page_toggle_and_indeterminate_queries() {
5602        #[derive(Debug, Clone)]
5603        struct Item {
5604            #[allow(dead_code)]
5605            value: usize,
5606        }
5607
5608        let data = (0..10).map(|i| Item { value: i }).collect::<Vec<_>>();
5609        let mut state = TableState::default();
5610        state.pagination = PaginationState {
5611            page_index: 0,
5612            page_size: 3,
5613        };
5614
5615        let table = Table::builder(&data).state(state).build();
5616        assert!(!table.is_all_page_rows_selected());
5617        assert!(!table.is_some_page_rows_selected());
5618
5619        let selection = table.toggled_all_page_rows_selected(None);
5620        let table2 = Table::builder(&data)
5621            .state(TableState {
5622                pagination: table.state().pagination,
5623                row_selection: selection,
5624                ..TableState::default()
5625            })
5626            .build();
5627        assert!(table2.is_all_page_rows_selected());
5628        assert!(!table2.is_some_page_rows_selected());
5629
5630        let selection = table2.toggled_all_page_rows_selected(None);
5631        assert!(selection.is_empty());
5632    }
5633
5634    #[test]
5635    fn table_row_selection_filtered_selected_counts_match_filtered_rows() {
5636        #[derive(Debug, Clone)]
5637        struct Item {
5638            status: Arc<str>,
5639        }
5640
5641        let data = vec![
5642            Item { status: "A".into() },
5643            Item { status: "B".into() },
5644            Item { status: "A".into() },
5645        ];
5646
5647        let status = ColumnDef::new("status").filter_by(|it: &Item, q| it.status.as_ref() == q);
5648
5649        let mut state = TableState::default();
5650        state.column_filters = vec![ColumnFilter {
5651            column: "status".into(),
5652            value: serde_json::Value::from("A"),
5653        }];
5654        state.row_selection = [RowKey::from_index(0)].into_iter().collect();
5655
5656        let table = Table::builder(&data)
5657            .columns(vec![status])
5658            .state(state)
5659            .build();
5660
5661        assert_eq!(table.filtered_row_count(), 2);
5662        assert_eq!(table.filtered_selected_row_count(), 1);
5663        assert_eq!(table.filtered_selected_flat_row_count(), 1);
5664        assert!(table.is_some_rows_selected());
5665        assert!(!table.is_all_rows_selected());
5666
5667        let selection = table.toggled_all_rows_selected(Some(true));
5668        let table2 = Table::builder(&data)
5669            .columns(vec![
5670                ColumnDef::new("status").filter_by(|it: &Item, q| it.status.as_ref() == q),
5671            ])
5672            .state(TableState {
5673                column_filters: table.state().column_filters.clone(),
5674                row_selection: selection,
5675                ..TableState::default()
5676            })
5677            .build();
5678
5679        assert!(table2.is_all_rows_selected());
5680        assert!(!table2.is_some_rows_selected());
5681    }
5682
5683    #[test]
5684    fn table_filtered_selected_row_model_intersects_filtered_rows() {
5685        #[derive(Debug, Clone)]
5686        struct Item {
5687            status: Arc<str>,
5688        }
5689
5690        let data = vec![
5691            Item { status: "A".into() },
5692            Item { status: "B".into() },
5693            Item { status: "A".into() },
5694        ];
5695
5696        let status = ColumnDef::new("status").filter_by(|it: &Item, q| it.status.as_ref() == q);
5697
5698        let mut state = TableState::default();
5699        state.column_filters = vec![ColumnFilter {
5700            column: "status".into(),
5701            value: serde_json::Value::from("A"),
5702        }];
5703        state.row_selection = [RowKey::from_index(0), RowKey::from_index(1)]
5704            .into_iter()
5705            .collect();
5706
5707        let table = Table::builder(&data)
5708            .columns(vec![status])
5709            .state(state)
5710            .build();
5711
5712        let selected = table.filtered_selected_row_model();
5713        assert_eq!(selected.root_rows().len(), 1);
5714        assert!(selected.row_by_key(RowKey::from_index(0)).is_some());
5715        assert!(std::ptr::eq(selected, table.filtered_selected_row_model()));
5716    }
5717
5718    #[test]
5719    fn table_page_selected_row_model_only_includes_page_rows() {
5720        #[derive(Debug, Clone)]
5721        struct Item {
5722            #[allow(dead_code)]
5723            value: usize,
5724        }
5725
5726        let data = (0..10).map(|i| Item { value: i }).collect::<Vec<_>>();
5727        let mut state = TableState::default();
5728        state.pagination = PaginationState {
5729            page_index: 1,
5730            page_size: 3,
5731        };
5732        state.row_selection = [RowKey::from_index(0), RowKey::from_index(4)]
5733            .into_iter()
5734            .collect();
5735
5736        let table = Table::builder(&data).state(state).build();
5737
5738        let selected = table.page_selected_row_model();
5739        assert_eq!(selected.root_rows().len(), 1);
5740        assert!(selected.row_by_key(RowKey::from_index(4)).is_some());
5741        assert!(std::ptr::eq(selected, table.page_selected_row_model()));
5742    }
5743
5744    #[test]
5745    fn table_column_visibility_toggle_respects_enable_hiding() {
5746        #[derive(Debug, Clone)]
5747        struct Item {
5748            #[allow(dead_code)]
5749            value: usize,
5750        }
5751
5752        let data = vec![Item { value: 1 }];
5753        let columns = vec![
5754            ColumnDef::new("a").enable_hiding(true),
5755            ColumnDef::new("b").enable_hiding(false),
5756        ];
5757
5758        let table = Table::builder(&data).columns(columns).build();
5759        assert_eq!(table.column_can_hide("a"), Some(true));
5760        assert_eq!(table.column_can_hide("b"), Some(false));
5761        assert_eq!(table.is_column_visible("a"), Some(true));
5762
5763        let next = table.toggled_column_visibility("a", Some(false)).unwrap();
5764        assert!(!is_column_visible(&next, &ColumnId::from("a")));
5765
5766        let next_b = table.toggled_column_visibility("b", Some(false)).unwrap();
5767        assert!(is_column_visible(&next_b, &ColumnId::from("b")));
5768    }
5769
5770    #[test]
5771    fn table_toggle_all_columns_visible_keeps_non_hideable_visible_when_hiding_all() {
5772        #[derive(Debug, Clone)]
5773        struct Item {
5774            #[allow(dead_code)]
5775            value: usize,
5776        }
5777
5778        let data = vec![Item { value: 1 }];
5779        let columns = vec![
5780            ColumnDef::new("a").enable_hiding(true),
5781            ColumnDef::new("b").enable_hiding(false),
5782        ];
5783
5784        let table = Table::builder(&data).columns(columns).build();
5785        let next = table.toggled_all_columns_visible(Some(false));
5786
5787        assert!(!is_column_visible(&next, &ColumnId::from("a")));
5788        assert!(is_column_visible(&next, &ColumnId::from("b")));
5789    }
5790
5791    #[test]
5792    fn table_column_order_move_respects_enable_column_ordering() {
5793        #[derive(Debug, Clone)]
5794        struct Item {
5795            #[allow(dead_code)]
5796            value: usize,
5797        }
5798
5799        let data = vec![Item { value: 1 }];
5800        let columns = vec![
5801            ColumnDef::new("a").enable_ordering(true),
5802            ColumnDef::new("b").enable_ordering(false),
5803            ColumnDef::new("c").enable_ordering(true),
5804        ];
5805
5806        let mut state = TableState::default();
5807        state.column_order = vec!["a".into(), "b".into(), "c".into()];
5808        let table = Table::builder(&data).columns(columns).state(state).build();
5809
5810        let next = table.toggled_column_order_move("a", 2).unwrap();
5811        assert_eq!(
5812            next.iter().map(|c| c.as_ref()).collect::<Vec<_>>(),
5813            vec!["b", "c", "a"]
5814        );
5815
5816        let next_b = table.toggled_column_order_move("b", 0).unwrap();
5817        assert_eq!(
5818            next_b.iter().map(|c| c.as_ref()).collect::<Vec<_>>(),
5819            vec!["a", "b", "c"]
5820        );
5821    }
5822
5823    #[test]
5824    fn table_column_pinning_respects_enable_column_pinning() {
5825        #[derive(Debug, Clone)]
5826        struct Item {
5827            #[allow(dead_code)]
5828            value: usize,
5829        }
5830
5831        let data = vec![Item { value: 1 }];
5832        let columns = vec![
5833            ColumnDef::new("a").enable_pinning(true),
5834            ColumnDef::new("b").enable_pinning(false),
5835        ];
5836        let table = Table::builder(&data).columns(columns).build();
5837
5838        let next = table
5839            .toggled_column_pinning("a", Some(ColumnPinPosition::Left))
5840            .unwrap();
5841        assert_eq!(
5842            next.left.iter().map(|c| c.as_ref()).collect::<Vec<_>>(),
5843            vec!["a"]
5844        );
5845
5846        let next_b = table
5847            .toggled_column_pinning("b", Some(ColumnPinPosition::Right))
5848            .unwrap();
5849        assert!(next_b.left.is_empty());
5850        assert_eq!(
5851            next_b.right.iter().map(|c| c.as_ref()).collect::<Vec<_>>(),
5852            vec!["b"]
5853        );
5854    }
5855
5856    #[test]
5857    fn table_column_sizing_totals_and_start_offsets_respect_pinning_and_order() {
5858        #[derive(Debug, Clone)]
5859        struct Item {
5860            #[allow(dead_code)]
5861            value: usize,
5862        }
5863
5864        let data = vec![Item { value: 1 }];
5865        let columns = vec![
5866            ColumnDef::new("a").size(100.0),
5867            ColumnDef::new("b").size(50.0),
5868            ColumnDef::new("c").size(25.0),
5869        ];
5870
5871        let mut state = TableState::default();
5872        state.column_order = vec!["b".into(), "c".into(), "a".into()];
5873        state.column_pinning.left = vec!["b".into()];
5874        state.column_pinning.right = vec!["a".into()];
5875
5876        let table = Table::builder(&data).columns(columns).state(state).build();
5877
5878        assert_eq!(table.left_total_size(), 50.0);
5879        assert_eq!(table.center_total_size(), 25.0);
5880        assert_eq!(table.right_total_size(), 100.0);
5881        assert_eq!(table.total_size(), 175.0);
5882
5883        assert_eq!(table.column_start("b", ColumnSizingRegion::All), Some(0.0));
5884        assert_eq!(table.column_start("c", ColumnSizingRegion::All), Some(50.0));
5885        assert_eq!(table.column_start("a", ColumnSizingRegion::All), Some(75.0));
5886
5887        assert_eq!(
5888            table.column_after("b", ColumnSizingRegion::All),
5889            Some(125.0)
5890        );
5891        assert_eq!(
5892            table.column_after("c", ColumnSizingRegion::All),
5893            Some(100.0)
5894        );
5895        assert_eq!(table.column_after("a", ColumnSizingRegion::All), Some(0.0));
5896
5897        assert_eq!(table.column_start("b", ColumnSizingRegion::Left), Some(0.0));
5898        assert_eq!(table.column_start("c", ColumnSizingRegion::Left), None);
5899        assert_eq!(
5900            table.column_start("c", ColumnSizingRegion::Center),
5901            Some(0.0)
5902        );
5903        assert_eq!(
5904            table.column_start("a", ColumnSizingRegion::Right),
5905            Some(0.0)
5906        );
5907
5908        assert_eq!(table.column_after("b", ColumnSizingRegion::Left), Some(0.0));
5909        assert_eq!(table.column_after("c", ColumnSizingRegion::Left), None);
5910        assert_eq!(
5911            table.column_after("c", ColumnSizingRegion::Center),
5912            Some(0.0)
5913        );
5914        assert_eq!(
5915            table.column_after("a", ColumnSizingRegion::Right),
5916            Some(0.0)
5917        );
5918    }
5919}