1use super::*;
5
6pub struct DataGridColumn<E: Model, T> {
8 pub(super) grid: DataGrid<E>,
9 pub(super) field: Field<E, T>,
10 pub(super) _type: PhantomData<fn() -> T>,
11}
12
13impl<E: Model, T> DataGridColumn<E, T> {
14 fn last(&mut self) -> &mut Column<E> {
15 self.grid
16 .columns
17 .last_mut()
18 .expect("DataGridColumn always has a column")
19 }
20
21 pub fn width(mut self, px: u32) -> Self {
23 self.last().width = Some(px);
24 self
25 }
26
27 pub fn flex(mut self, weight: u32) -> Self {
29 self.last().flex = Some(weight);
30 self
31 }
32
33 pub fn filter(mut self) -> Self {
35 self.last().filter = true;
36 self
37 }
38
39 pub fn no_sort(mut self) -> Self {
44 self.last().sort = false;
45 self
46 }
47
48 fn aggregate(mut self, op: Aggregate) -> Self {
49 let column = self.last();
50 if !column.aggregates.contains(&op) {
51 column.aggregates.push(op);
52 }
53 self
54 }
55
56 pub fn sum(self) -> Self
59 where
60 T: SupportsSum,
61 {
62 self.aggregate(Aggregate::Sum)
63 }
64
65 pub fn avg(self) -> Self
67 where
68 T: SupportsSum,
69 {
70 self.aggregate(Aggregate::Avg)
71 }
72
73 pub fn min(self) -> Self
75 where
76 T: SupportsMinMaxTotal,
77 {
78 self.aggregate(Aggregate::Min)
79 }
80
81 pub fn max(self) -> Self
83 where
84 T: SupportsMinMaxTotal,
85 {
86 self.aggregate(Aggregate::Max)
87 }
88
89 pub fn pin_start(mut self) -> Self {
92 self.last().pin = Some(Pin::Start);
93 self
94 }
95
96 pub fn pin_end(mut self) -> Self {
98 self.last().pin = Some(Pin::End);
99 self
100 }
101
102 pub fn hidden(mut self) -> Self {
104 self.last().hidden = true;
105 self
106 }
107
108 pub fn editable(mut self) -> Self {
112 self.last().editable = true;
113 self
114 }
115
116 pub fn cell(mut self, renderer: impl CellRenderer<T>) -> Self
118 where
119 T: 'static,
120 {
121 let field = self.field;
122 self.last().cell = Some(Box::new(move |row, r| renderer.render(field.get(row), r)));
123 self
124 }
125
126 pub fn badge(self, tone: impl Fn(&T) -> Tone + Send + Sync + 'static) -> Self
130 where
131 T: ChoiceType + FieldType + 'static,
132 {
133 let kind = self.field.meta().kind;
134 self.cell(move |v: &T, r: &Render| {
135 format!(
136 r#"<span class="z-badge z-tone-{}">{}</span>"#,
137 tone(v).code(),
138 r.value(&v.to_value(), &kind)
139 )
140 })
141 }
142
143 pub fn progress(self, max: i64) -> Self
146 where
147 T: SupportsSum + FieldType + 'static,
148 {
149 let kind = self.field.meta().kind;
150 self.cell(move |v: &T, r: &Render| {
151 let value = v.to_value();
152 let amount = match value {
153 Value::Int(n) => n as f64,
154 Value::Money(m) => m.minor as f64 / 100.0,
155 _ => return String::new(),
156 };
157 let pct = if max > 0 { (amount / max as f64 * 100.0).clamp(0.0, 100.0) } else { 0.0 };
158 format!(
159 r#"<span class="z-progress"><span class="z-progress-track" aria-hidden="true"><span class="z-progress-bar" style="--z-pct:{pct:.1}%"></span></span><span class="z-progress-text">{}</span></span>"#,
160 r.value(&value, &kind)
161 )
162 })
163 }
164
165 pub fn link(mut self, effect: impl Fn(&E) -> Effect + Send + Sync + 'static) -> Self
167 where
168 T: FieldType + 'static,
169 {
170 let field = self.field;
171 let kind = field.meta().kind;
172 self.last().cell = Some(Box::new(move |row, r| {
173 format!(
174 r#"<button type="button" class="z-link" tabindex="-1" data-zeyn-effect="{}">{}</button>"#,
175 attr(&effect(row)),
176 r.value(&field.get(row).to_value(), &kind)
177 )
178 }));
179 self
180 }
181
182 pub fn check_icon(self) -> Self
185 where
186 T: IsBool + FieldType + 'static,
187 {
188 self.cell(|v: &T, r: &Render| match v.to_value() {
189 Value::Bool(b) => format!(
190 r#"<span class="z-bool z-bool-{}" role="img" aria-label="{}"></span>"#,
191 if b { "yes" } else { "no" },
192 r.text(&Text::key(if b { "zeyn.yes" } else { "zeyn.no" }))
193 ),
194 _ => String::new(),
195 })
196 }
197
198 pub fn relative_time(self) -> Self
201 where
202 T: IsMoment + FieldType + 'static,
203 {
204 self.cell(|v: &T, r: &Render| match v.to_value() {
205 Value::DateTime(d) => {
206 let exact = d.format(r.tz, r.locale);
207 format!(r#"<time datetime="{d}" title="{exact}" data-zeyn-relative>{exact}</time>"#)
208 }
209 _ => String::new(),
210 })
211 }
212
213 pub fn count(self) -> Self {
215 self.aggregate(Aggregate::Count)
216 }
217
218 pub fn column<U: FieldType + 'static>(self, field: Field<E, U>) -> DataGridColumn<E, U> {
219 self.grid.column(field)
220 }
221
222 pub fn mode(self, mode: DataMode) -> DataGrid<E> {
223 self.grid.mode(mode)
224 }
225
226 pub fn paging(self, paging: Paging) -> DataGrid<E> {
227 self.grid.paging(paging)
228 }
229
230 pub fn row_style(
231 self,
232 style: impl Fn(&E) -> Option<RowStyle> + Send + Sync + 'static,
233 ) -> DataGrid<E> {
234 self.grid.row_style(style)
235 }
236
237 pub fn on_open(self, open: impl Fn(&E) -> Effect + Send + Sync + 'static) -> DataGrid<E> {
238 self.grid.on_open(open)
239 }
240
241 pub fn row_action<A>(
242 self,
243 label: impl Into<Text>,
244 action: A,
245 input: impl Fn(&E) -> A::Input + Send + Sync + 'static,
246 ) -> DataGrid<E>
247 where
248 A: Action,
249 A::Output: Outcome,
250 {
251 self.grid
252 .row_action(label, action, input)
253 }
254
255 pub fn selectable(self) -> DataGrid<E> {
256 self.grid.selectable()
257 }
258
259 pub fn row_menu(
260 self,
261 menu: impl Fn(&E) -> crate::menu::ContextMenu + Send + Sync + 'static,
262 ) -> DataGrid<E> {
263 self.grid.row_menu(menu)
264 }
265
266 pub fn exportable(self) -> DataGrid<E> {
267 self.grid.exportable()
268 }
269
270 pub fn edit_with<A>(self, save: A) -> DataGrid<E>
271 where
272 A: crate::form::FormAction<E> + Action<Input = Valid<E>>,
273 A::Output: Outcome,
274 {
275 self.grid.edit_with(save)
276 }
277
278 pub fn bulk_action<A, K>(
279 self,
280 label: impl Into<Text>,
281 action: A,
282 key: Field<E, K>,
283 ) -> DataGrid<E>
284 where
285 A: BulkAction<K> + Action<Input = Vec<K>>,
286 A::Output: Outcome,
287 K: serde::de::DeserializeOwned + Send + 'static,
288 {
289 self.grid
290 .bulk_action(label, action, key)
291 }
292
293 pub fn detail<S: ScreenDef>(
294 self,
295 detail: impl Fn(&E) -> (S, S::Params) + Send + Sync + 'static,
296 ) -> DataGrid<E> {
297 self.grid.detail(detail)
298 }
299
300 pub fn scope<U: FieldType>(self, field: Field<E, U>, value: U) -> DataGrid<E> {
301 self.grid.scope(field, value)
302 }
303
304 pub fn sort_by<U: FieldType>(self, field: Field<E, U>, desc: bool) -> DataGrid<E> {
305 self.grid.sort_by(field, desc)
306 }
307
308 pub fn group_by<U: FieldType>(self, field: Field<E, U>) -> DataGrid<E> {
309 self.grid.group_by(field)
310 }
311
312 pub fn tree<K: FieldType>(self, parent: Field<E, Option<K>>) -> DataGrid<E>
313 where
314 Option<K>: FieldType,
315 {
316 self.grid.tree(parent)
317 }
318
319 pub fn done(self) -> DataGrid<E> {
321 self.grid
322 }
323}
324
325impl<E: Model, T> From<DataGridColumn<E, T>> for DataGrid<E> {
326 fn from(c: DataGridColumn<E, T>) -> Self {
327 c.grid
328 }
329}
330
331impl<E: Model, T> From<DataGridColumn<E, T>> for Node {
332 fn from(c: DataGridColumn<E, T>) -> Self {
333 Node::new(c.grid)
334 }
335}