tablo_core/column/
boolean.rs1use derive_where::derive_where;
4use toasty::stmt::OrderByExpr;
5use topcoat::{context::Cx, icon::icon, view::*};
6
7use super::{Column, ColumnBase, ColumnWidth, sort_order};
8use crate::Lens;
9
10#[derive_where(Clone, Debug)]
18pub struct BooleanColumn<M> {
19 lens: Lens<M, bool>,
20 base: ColumnBase,
21 sortable: bool,
22 labels: (crate::Text, crate::Text),
23}
24
25impl<M> BooleanColumn<M>
26where
27 M: toasty::schema::Model,
28{
29 pub fn new(lens: Lens<M, bool>) -> Self {
31 Self {
32 base: ColumnBase::new(lens.path(), ColumnWidth::Auto),
33 lens,
34 sortable: false,
35 labels: (
36 crate::Text::message("tablo-yes"),
37 crate::Text::message("tablo-no"),
38 ),
39 }
40 }
41
42 base_builders!(sortable);
43
44 pub fn labels(mut self, yes: impl Into<crate::Text>, no: impl Into<crate::Text>) -> Self {
46 self.labels = (yes.into(), no.into());
47 self
48 }
49}
50
51impl<M> Column<M> for BooleanColumn<M>
52where
53 M: toasty::schema::Model + Send + Sync + 'static,
54{
55 base_column_methods!(binding);
56
57 fn text(&self, cx: &Cx, row: &M) -> String {
58 if *self.lens.read(row) {
59 self.labels.0.localize(cx)
60 } else {
61 self.labels.1.localize(cx)
62 }
63 }
64
65 fn cell<'a>(&self, cx: &'a Cx, row: &M) -> BoxView<'a> {
66 let value = *self.lens.read(row);
67 let text = self.text(cx, row);
68 let (data, class) = if value {
70 (tablo_ui::icons::CIRCLE_CHECK, "size-4 text-success")
71 } else {
72 (tablo_ui::icons::MINUS, "size-4 text-muted-foreground/60")
73 };
74 view! {
75 cx =>
76 <span class="inline-flex items-center" data-boolean=(value.to_string())>
77 icon(
78 data: data,
79 attrs: attributes! { class=(class) aria-hidden="true" }
80 )
81 <span class="sr-only">(text)</span>
82 </span>
83 }
84 .boxed()
85 }
86
87 fn is_sortable(&self) -> bool {
88 self.sortable
89 }
90
91 fn order_by(&self, descending: bool) -> Option<OrderByExpr> {
92 self.sortable
93 .then(|| sort_order(self.lens.path(), descending))
94 }
95}