Skip to main content

tablo_core/column/
list.rs

1//! [`ListColumn`]: the values a list column stores, a badge each.
2
3use derive_where::derive_where;
4use toasty::stmt::List;
5use topcoat::{context::Cx, view::*};
6
7use super::{
8    Column, ColumnBase, ColumnWidth,
9    cell::{TextCell, badges_cell},
10};
11use crate::{Lens, values::FormScalar};
12
13/// A column of a `Vec` of [`FormScalar`] values that one list column stores, such as a
14/// [multiple choice](crate::ChoiceField::multiple)'s.
15///
16/// A table cell and a detail page show a badge per value, in the stored order: its
17/// [`FormScalar::to_label`], an [`Options`](crate::Options) enum's label with its color or icon,
18/// or any other type's form spelling. An empty list shows the dash an empty text does. The text,
19/// as an export writes it, joins the labels with `, `.
20///
21/// ```rust
22/// # #[derive(Debug, Clone, toasty::Model, tablo_core::ModelColumns)]
23/// # struct Post {
24/// #     #[key] #[auto] id: uuid::Uuid,
25/// #     keywords: Vec<String>,
26/// # }
27/// tablo_core::ListColumn::new(tablo_core::lens!(Post.keywords));
28/// ```
29#[derive_where(Clone, Debug)]
30pub struct ListColumn<M, T> {
31    lens: Lens<M, Vec<T>, List<T>>,
32    base: ColumnBase,
33}
34
35impl<M, T> ListColumn<M, T>
36where
37    M: toasty::schema::Model,
38    T: FormScalar,
39{
40    /// Bind the column to the list `lens` reads.
41    pub fn new(lens: Lens<M, Vec<T>, List<T>>) -> Self {
42        Self {
43            base: ColumnBase::new(lens.path(), ColumnWidth::Grow),
44            lens,
45        }
46    }
47
48    base_builders!();
49}
50
51impl<M, T> Column<M> for ListColumn<M, T>
52where
53    M: toasty::schema::Model + Send + Sync + 'static,
54    T: FormScalar + Send + Sync + 'static,
55{
56    base_column_methods!();
57
58    fn text(&self, cx: &Cx, row: &M) -> String {
59        self.lens
60            .read(row)
61            .iter()
62            .map(|value| value.to_label().localize(cx))
63            .collect::<Vec<_>>()
64            .join(", ")
65    }
66
67    fn cell<'a>(&self, cx: &'a Cx, row: &M) -> BoxView<'a> {
68        let badges = self
69            .lens
70            .read(row)
71            .iter()
72            .map(|value| {
73                let color = value.color();
74                TextCell {
75                    text: value.to_label().localize(cx),
76                    color,
77                    badge: true,
78                    icon: value.icon().map(|icon| (icon, color)),
79                    ..TextCell::default()
80                }
81            })
82            .collect();
83        badges_cell(cx, badges)
84    }
85}
86
87impl<M, T> crate::column::ColumnInternals<M> for ListColumn<M, T>
88where
89    M: toasty::schema::Model + Send + Sync + 'static,
90    T: FormScalar + Send + Sync + 'static,
91{
92    base_internals!(binding);
93}