tablo_core/column/
date.rs1use derive_where::derive_where;
4use jiff::tz::TimeZone;
5use toasty::stmt::OrderByExpr;
6use topcoat::{context::Cx, view::*};
7
8use super::{Column, ColumnBase, ColumnWidth, sort_order};
9use crate::{Lens, values::FormScalar};
10
11#[derive_where(Clone, Debug)]
24pub struct DateColumn<M, T> {
25 lens: Lens<M, T>,
26 base: ColumnBase,
27 sortable: bool,
28}
29
30pub trait DateValue: Sized + Send + Sync + 'static {
35 fn calendar_date(&self, zone: &TimeZone) -> Option<jiff::civil::Date>;
38
39 fn stored_text(&self) -> String {
42 self.calendar_date(&TimeZone::UTC)
43 .map(|day| day.to_string())
44 .unwrap_or_default()
45 }
46}
47
48impl DateValue for jiff::civil::Date {
49 fn calendar_date(&self, _zone: &TimeZone) -> Option<jiff::civil::Date> {
50 Some(*self)
51 }
52
53 fn stored_text(&self) -> String {
54 FormScalar::to_form(self)
55 }
56}
57
58impl DateValue for jiff::civil::DateTime {
59 fn calendar_date(&self, _zone: &TimeZone) -> Option<jiff::civil::Date> {
60 Some(self.date())
61 }
62
63 fn stored_text(&self) -> String {
64 FormScalar::to_form(self)
65 }
66}
67
68impl DateValue for jiff::Timestamp {
69 fn calendar_date(&self, zone: &TimeZone) -> Option<jiff::civil::Date> {
70 Some(self.to_zoned(zone.clone()).date())
71 }
72
73 fn stored_text(&self) -> String {
74 FormScalar::to_form(self)
75 }
76}
77
78impl<T: DateValue> DateValue for Option<T> {
79 fn calendar_date(&self, zone: &TimeZone) -> Option<jiff::civil::Date> {
80 self.as_ref().and_then(|value| value.calendar_date(zone))
81 }
82
83 fn stored_text(&self) -> String {
84 self.as_ref()
85 .map(DateValue::stored_text)
86 .unwrap_or_default()
87 }
88}
89
90impl<M, T> DateColumn<M, T>
91where
92 M: toasty::schema::Model,
93{
94 pub fn new(lens: Lens<M, T>) -> Self
96 where
97 T: DateValue,
98 {
99 Self {
100 base: ColumnBase::new(lens.path(), ColumnWidth::Auto),
101 lens,
102 sortable: false,
103 }
104 }
105
106 base_builders!(sortable);
107}
108
109impl<M, T> Column<M> for DateColumn<M, T>
110where
111 M: toasty::schema::Model + Send + Sync + 'static,
112 T: DateValue,
113{
114 base_column_methods!();
115
116 fn text(&self, _cx: &Cx, row: &M) -> String {
117 self.lens.read(row).stored_text()
118 }
119
120 fn cell<'a>(&self, cx: &'a Cx, row: &M) -> BoxView<'a> {
121 let day = self
122 .lens
123 .read(row)
124 .calendar_date(&crate::time_zone(cx))
125 .map(|day| {
126 crate::t!(
127 cx,
128 "tablo-column-date",
129 year = day.year(),
130 month = day.month(),
131 day = day.day()
132 )
133 })
134 .unwrap_or_default();
135 crate::schema::value_cell(cx, &day)
136 }
137
138 fn order_by(&self, descending: bool) -> Option<OrderByExpr> {
139 self.sortable
140 .then(|| sort_order(self.lens.path(), descending))
141 }
142}
143
144impl<M, T> crate::column::ColumnInternals<M> for DateColumn<M, T>
145where
146 M: toasty::schema::Model + Send + Sync + 'static,
147 T: DateValue,
148{
149 base_internals!(binding);
150}