tablo_core/column/
repeater.rs1use derive_where::derive_where;
4use toasty::stmt::List;
5use topcoat::{context::Cx, view::*};
6
7use super::{Column, ColumnBase, ColumnWidth};
8use crate::{
9 Lens,
10 schema::{FormInput, read_only, value_cell},
11 values::FormValues,
12};
13
14#[derive_where(Clone, Debug)]
30pub struct RepeaterColumn<M, T> {
31 lens: Lens<M, Vec<T>, List<T>>,
32 base: ColumnBase,
33}
34
35impl<M, T> RepeaterColumn<M, T>
36where
37 M: toasty::schema::Model,
38 T: FormInput,
39{
40 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 fn spelled(&self, row: &M) -> Vec<FormValues> {
52 self.lens
53 .read(row)
54 .iter()
55 .map(|item| {
56 let mut values = FormValues::new();
57 item.write(&mut values);
58 values
59 })
60 .collect()
61 }
62}
63
64impl<M, T> Column<M> for RepeaterColumn<M, T>
65where
66 M: toasty::schema::Model + Send + Sync + 'static,
67 T: FormInput,
68{
69 base_column_methods!();
70
71 fn text(&self, cx: &Cx, row: &M) -> String {
72 let schema = T::schema();
73 self.spelled(row)
74 .iter()
75 .map(|values| {
76 schema
77 .fields()
78 .filter_map(|field| {
79 let value = values.text(field.name()).unwrap_or_default();
80 let read = field.read(cx, value)?;
81 Some(format!("{}: {read}", field.label().localize(cx)))
82 })
83 .collect::<Vec<_>>()
84 .join(", ")
85 })
86 .collect::<Vec<_>>()
87 .join("; ")
88 }
89
90 fn entry<'a>(&self, cx: &'a Cx, row: &M) -> BoxView<'a> {
91 let label = Column::label(self);
92 let items = self.spelled(row);
93 if items.is_empty() {
94 return read_only(cx, &label, value_cell(cx, ""));
95 }
96 let schema = T::schema();
97 let items: Vec<BoxView<'a>> = items
98 .iter()
99 .map(|values| {
100 let fields: Vec<BoxView<'a>> = schema
101 .fields()
102 .map(|field| field.display(cx, values.text(field.name()).unwrap_or_default()))
103 .collect();
104 view! {
105 <li class="flex flex-col gap-3 rounded-lg border border-border p-4">
106 for field in fields {
107 (field)
108 }
109 </li>
110 }
111 .boxed()
112 })
113 .collect();
114 read_only(
115 cx,
116 &label,
117 view! {
118 <ol class="flex flex-col gap-3 whitespace-normal">
119 for item in items {
120 (item)
121 }
122 </ol>
123 }
124 .boxed(),
125 )
126 }
127}
128
129impl<M, T> crate::column::ColumnInternals<M> for RepeaterColumn<M, T>
130where
131 M: toasty::schema::Model + Send + Sync + 'static,
132 T: FormInput,
133{
134 base_internals!(binding);
135}