Skip to main content

fret_ui_headless/table/
column_ordering.rs

1use std::collections::HashMap;
2
3use super::{ColumnDef, ColumnId};
4
5/// TanStack-compatible column order: an ordered list of column ids.
6///
7/// Any columns not present in this list keep their original relative order and
8/// are appended to the end.
9pub type ColumnOrderState = Vec<ColumnId>;
10
11pub fn set_column_order(order: &mut ColumnOrderState, ids: impl IntoIterator<Item = ColumnId>) {
12    order.clear();
13    for id in ids {
14        order.push(id);
15    }
16}
17
18pub fn set_column_order_for<'a>(
19    order: &mut ColumnOrderState,
20    ids: impl IntoIterator<Item = &'a str>,
21) {
22    set_column_order(order, ids.into_iter().map(ColumnId::from));
23}
24
25pub fn move_column(order: &mut ColumnOrderState, id: &ColumnId, to_index: usize) {
26    if order.is_empty() {
27        order.push(id.clone());
28        return;
29    }
30
31    let from = order.iter().position(|c| c.as_ref() == id.as_ref());
32    let mut next: Vec<ColumnId> = Vec::with_capacity(order.len() + 1);
33
34    for (idx, c) in order.iter().enumerate() {
35        if Some(idx) == from {
36            continue;
37        }
38        next.push(c.clone());
39    }
40
41    let insert_at = to_index.min(next.len());
42    next.insert(insert_at, id.clone());
43    *order = next;
44}
45
46pub fn moved_column(order: &ColumnOrderState, id: &ColumnId, to_index: usize) -> ColumnOrderState {
47    let mut next = order.clone();
48    move_column(&mut next, id, to_index);
49    next
50}
51
52pub fn order_columns<'c, TData>(
53    columns: &'c [ColumnDef<TData>],
54    order: &[ColumnId],
55) -> Vec<&'c ColumnDef<TData>> {
56    if columns.is_empty() {
57        return Vec::new();
58    }
59    if order.is_empty() {
60        return columns.iter().collect();
61    }
62
63    let mut remaining: HashMap<&str, &ColumnDef<TData>> =
64        columns.iter().map(|c| (c.id.as_ref(), c)).collect();
65
66    let mut out: Vec<&ColumnDef<TData>> = Vec::with_capacity(columns.len());
67    for id in order {
68        if let Some(col) = remaining.remove(id.as_ref()) {
69            out.push(col);
70        }
71    }
72
73    for col in columns {
74        if remaining.remove(col.id.as_ref()).is_some() {
75            out.push(col);
76        }
77    }
78
79    out
80}
81
82#[cfg(test)]
83mod tests {
84    use super::*;
85
86    #[test]
87    fn order_columns_respects_explicit_order_then_appends_rest() {
88        #[derive(Debug)]
89        struct Item;
90
91        let columns = vec![
92            ColumnDef::<Item>::new("a"),
93            ColumnDef::<Item>::new("b"),
94            ColumnDef::<Item>::new("c"),
95        ];
96
97        let out = order_columns(&columns, &[ColumnId::from("c"), ColumnId::from("a")]);
98        let ids = out.iter().map(|c| c.id.as_ref()).collect::<Vec<_>>();
99
100        assert_eq!(ids, vec!["c", "a", "b"]);
101    }
102
103    #[test]
104    fn order_columns_ignores_unknown_ids() {
105        #[derive(Debug)]
106        struct Item;
107
108        let columns = vec![ColumnDef::<Item>::new("a"), ColumnDef::<Item>::new("b")];
109        let out = order_columns(&columns, &[ColumnId::from("x"), ColumnId::from("b")]);
110
111        let ids = out.iter().map(|c| c.id.as_ref()).collect::<Vec<_>>();
112        assert_eq!(ids, vec!["b", "a"]);
113    }
114
115    #[test]
116    fn set_column_order_preserves_input_order_including_duplicates() {
117        let mut order: ColumnOrderState = vec!["a".into()];
118        set_column_order_for(&mut order, ["b", "a", "b"]);
119        assert_eq!(
120            order.iter().map(|c| c.as_ref()).collect::<Vec<_>>(),
121            vec!["b", "a", "b"]
122        );
123    }
124
125    #[test]
126    fn move_column_inserts_when_missing_and_reorders_when_present() {
127        let mut order: ColumnOrderState = vec!["a".into(), "b".into(), "c".into()];
128        move_column(&mut order, &ColumnId::from("b"), 0);
129        assert_eq!(
130            order.iter().map(|c| c.as_ref()).collect::<Vec<_>>(),
131            vec!["b", "a", "c"]
132        );
133
134        move_column(&mut order, &ColumnId::from("x"), 1);
135        assert_eq!(
136            order.iter().map(|c| c.as_ref()).collect::<Vec<_>>(),
137            vec!["b", "x", "a", "c"]
138        );
139    }
140}