use derive_where::derive_where;
use jiff::tz::TimeZone;
use toasty::stmt::OrderByExpr;
use topcoat::{context::Cx, view::*};
use super::{Column, ColumnBase, ColumnWidth, sort_order};
use crate::{Lens, values::FormScalar};
#[derive_where(Clone, Debug)]
pub struct DateColumn<M, T> {
lens: Lens<M, T>,
base: ColumnBase,
sortable: bool,
}
pub trait DateValue: Sized + Send + Sync + 'static {
fn calendar_date(&self, zone: &TimeZone) -> Option<jiff::civil::Date>;
fn stored_text(&self) -> String {
self.calendar_date(&TimeZone::UTC)
.map(|day| day.to_string())
.unwrap_or_default()
}
}
impl DateValue for jiff::civil::Date {
fn calendar_date(&self, _zone: &TimeZone) -> Option<jiff::civil::Date> {
Some(*self)
}
fn stored_text(&self) -> String {
FormScalar::to_form(self)
}
}
impl DateValue for jiff::civil::DateTime {
fn calendar_date(&self, _zone: &TimeZone) -> Option<jiff::civil::Date> {
Some(self.date())
}
fn stored_text(&self) -> String {
FormScalar::to_form(self)
}
}
impl DateValue for jiff::Timestamp {
fn calendar_date(&self, zone: &TimeZone) -> Option<jiff::civil::Date> {
Some(self.to_zoned(zone.clone()).date())
}
fn stored_text(&self) -> String {
FormScalar::to_form(self)
}
}
impl<T: DateValue> DateValue for Option<T> {
fn calendar_date(&self, zone: &TimeZone) -> Option<jiff::civil::Date> {
self.as_ref().and_then(|value| value.calendar_date(zone))
}
fn stored_text(&self) -> String {
self.as_ref()
.map(DateValue::stored_text)
.unwrap_or_default()
}
}
impl<M, T> DateColumn<M, T>
where
M: toasty::schema::Model,
{
pub fn new(lens: Lens<M, T>) -> Self
where
T: DateValue,
{
Self {
base: ColumnBase::new(lens.path(), ColumnWidth::Auto),
lens,
sortable: false,
}
}
base_builders!(sortable);
}
impl<M, T> Column<M> for DateColumn<M, T>
where
M: toasty::schema::Model + Send + Sync + 'static,
T: DateValue,
{
base_column_methods!();
fn text(&self, _cx: &Cx, row: &M) -> String {
self.lens.read(row).stored_text()
}
fn cell<'a>(&self, cx: &'a Cx, row: &M) -> BoxView<'a> {
let day = self
.lens
.read(row)
.calendar_date(&crate::time_zone(cx))
.map(|day| {
crate::t!(
cx,
"tablo-column-date",
year = day.year(),
month = day.month(),
day = day.day()
)
})
.unwrap_or_default();
crate::schema::value_cell(cx, &day)
}
fn order_by(&self, descending: bool) -> Option<OrderByExpr> {
self.sortable
.then(|| sort_order(self.lens.path(), descending))
}
}
impl<M, T> crate::column::ColumnInternals<M> for DateColumn<M, T>
where
M: toasty::schema::Model + Send + Sync + 'static,
T: DateValue,
{
base_internals!(binding);
}