dioxus_tabular/
columns.rs

1#[cfg(feature = "export")]
2use crate::Exporter;
3use crate::{Row, TableColumn, TableContext};
4use dioxus::prelude::*;
5
6/// Trait automatically implemented for tuples of [`TableColumn`]s.
7///
8/// This trait is implemented for tuples of 1 to 12 columns via macros.
9/// You don't need to implement this trait manually.
10///
11/// # Example
12///
13/// ```
14/// # use dioxus_tabular::*;
15/// # #[derive(Clone, PartialEq)]
16/// # struct Col1;
17/// # #[derive(Clone, PartialEq)]
18/// # struct Col2;
19/// # #[derive(Clone, PartialEq)]
20/// # struct User { id: u32 }
21/// # impl Row for User {
22/// #     fn key(&self) -> impl Into<String> { self.id.to_string() }
23/// # }
24/// # impl TableColumn<User> for Col1 {
25/// #     fn column_name(&self) -> String { "col1".into() }
26/// #     fn render_header(&self, _: ColumnContext, _: Vec<dioxus::prelude::Attribute>) -> dioxus::prelude::Element { todo!() }
27/// #     fn render_cell(&self, _: ColumnContext, _: &User, _: Vec<dioxus::prelude::Attribute>) -> dioxus::prelude::Element { todo!() }
28/// # }
29/// # impl TableColumn<User> for Col2 {
30/// #     fn column_name(&self) -> String { "col2".into() }
31/// #     fn render_header(&self, _: ColumnContext, _: Vec<dioxus::prelude::Attribute>) -> dioxus::prelude::Element { todo!() }
32/// #     fn render_cell(&self, _: ColumnContext, _: &User, _: Vec<dioxus::prelude::Attribute>) -> dioxus::prelude::Element { todo!() }
33/// # }
34/// // Single column
35/// let cols1 = (Col1,);
36///
37/// // Multiple columns (tuple)
38/// let cols2 = (Col1, Col2);
39/// ```
40#[allow(clippy::type_complexity, reason = "to provide internal API")]
41pub trait Columns<R: Row>: Clone + PartialEq + 'static {
42    /// Returns the names of all columns.
43    fn column_names(&self) -> Vec<String>;
44    /// Returns header renderers for all columns.
45    fn headers(&self) -> Vec<Box<dyn Fn(&TableContext<Self>, Vec<Attribute>) -> Element + '_>>;
46    /// Returns cell renderers for all columns.
47    fn columns(&self) -> Vec<Box<dyn Fn(&TableContext<Self>, &R, Vec<Attribute>) -> Element + '_>>;
48    /// Returns true if the row passes all column filters.
49    fn filter(&self, row: &R) -> bool;
50    /// Returns comparators for all columns.
51    fn compare(&self) -> Vec<Box<dyn Fn(&R, &R) -> std::cmp::Ordering + '_>>;
52}
53
54/// Trait for columns that support serialization (export feature).
55///
56/// Automatically implemented for tuples of [`SerializableColumn`](crate::SerializableColumn)s.
57#[cfg(feature = "export")]
58pub trait SerializableColumns<R: Row>: Columns<R> {
59    /// Returns header serializers for all columns.
60    fn serialize_headers(&self) -> Vec<Box<dyn Fn() -> String + '_>>;
61    /// Returns cell serializers for all columns.
62    #[allow(clippy::type_complexity, reason = "to provide internal API")]
63    fn serialize_cell<E: Exporter>(
64        &self,
65    ) -> Vec<Box<dyn Fn(usize, usize, &R, &mut E) -> Result<(), E::Error> + '_>>;
66}
67
68macro_rules! columns {
69    ($($number:tt => $column:ident),*) => {
70        impl<$($column: TableColumn<R>),*, R: Row> Columns<R> for ($($column),*,) {
71            fn column_names(&self) -> Vec<String> {
72                vec![$(self.$number.column_name()),*]
73            }
74            fn headers(&self) -> Vec<Box<dyn Fn(&TableContext<Self>, Vec<Attribute>) -> Element + '_>> {
75                vec![$(Box::new(move |context, attributes| {
76                    self.$number.render_header(context.data.column_context($number), attributes)
77                })),*]
78            }
79            fn columns(&self) -> Vec<Box<dyn Fn(&TableContext<Self>, &R, Vec<Attribute>) -> Element + '_>> {
80                vec![$(Box::new(move |context, row, attributes| {
81                    self.$number.render_cell(context.data.column_context($number), row, attributes)
82                })),*]
83            }
84            fn filter(&self, row: &R) -> bool {
85                $(self.$number.filter(row) &&)* true
86            }
87            fn compare(&self) -> Vec<Box<dyn Fn(&R, &R) -> std::cmp::Ordering + '_>> {
88                vec![$(Box::new(move |a, b| self.$number.compare(a, b))),*]
89            }
90        }
91        #[cfg(feature = "export")]
92        serialize_columns!($($number => $column),*);
93    }
94}
95
96#[cfg(feature = "export")]
97macro_rules! serialize_columns {
98    ($($number:tt => $column:ident),*) => {
99        impl<$($column: crate::SerializableColumn<R>),*, R: Row> SerializableColumns<R> for ($($column),*,) {
100            fn serialize_headers(&self) -> Vec<Box<dyn Fn() -> String + '_>> {
101                vec![$(Box::new(move || self.$number.header())),*]
102            }
103            fn serialize_cell<Ex: Exporter>(&self) -> Vec<Box<dyn Fn(usize, usize, &R, &mut Ex) -> Result<(), Ex::Error> + '_>> {
104                vec![$(Box::new(move |row_index, col_index, row, exporter| exporter.serialize_cell(row_index, col_index, self.$number.serialize_cell(row)))),*]
105            }
106        }
107    }
108}
109
110columns!(0 => A);
111columns!(0 => A, 1 => B);
112columns!(0 => A, 1 => B, 2 => C);
113columns!(0 => A, 1 => B, 2 => C, 3 => D);
114columns!(0 => A, 1 => B, 2 => C, 3 => D, 4 => E);
115columns!(0 => A, 1 => B, 2 => C, 3 => D, 4 => E, 5 => F);
116columns!(0 => A, 1 => B, 2 => C, 3 => D, 4 => E, 5 => F, 6 => G);
117columns!(0 => A, 1 => B, 2 => C, 3 => D, 4 => E, 5 => F, 6 => G, 7 => H);
118columns!(0 => A, 1 => B, 2 => C, 3 => D, 4 => E, 5 => F, 6 => G, 7 => H, 8 => I);
119columns!(0 => A, 1 => B, 2 => C, 3 => D, 4 => E, 5 => F, 6 => G, 7 => H, 8 => I, 9 => J);
120columns!(0 => A, 1 => B, 2 => C, 3 => D, 4 => E, 5 => F, 6 => G, 7 => H, 8 => I, 9 => J, 10 => K);
121columns!(0 => A, 1 => B, 2 => C, 3 => D, 4 => E, 5 => F, 6 => G, 7 => H, 8 => I, 9 => J, 10 => K, 11 => L);