Skip to main content

guise/data/
table.rs

1//! `Table` — a simple data table of string cells.
2
3use gpui::prelude::*;
4use gpui::{div, px, App, Div, FontWeight, Hsla, IntoElement, SharedString, Window};
5
6use crate::theme::{theme, ColorName, Size};
7
8/// One table cell. A free fn (not a closure) so it can be reused by both the
9/// header and body iterators without being moved twice.
10fn cell(value: SharedString, color: Hsla, weight: FontWeight, font: f32) -> Div {
11    div()
12        .flex_1()
13        .px(px(12.0))
14        .py(px(8.0))
15        .text_size(px(font))
16        .text_color(color)
17        .font_weight(weight)
18        .child(value)
19}
20
21/// A data table. The Mantine `Table`. Cells are plain strings; columns size
22/// equally.
23#[derive(IntoElement)]
24pub struct Table {
25    head: Vec<SharedString>,
26    rows: Vec<Vec<SharedString>>,
27    striped: bool,
28    highlight_on_hover: bool,
29    with_border: bool,
30}
31
32impl Table {
33    pub fn new() -> Self {
34        Table {
35            head: Vec::new(),
36            rows: Vec::new(),
37            striped: false,
38            highlight_on_hover: false,
39            with_border: false,
40        }
41    }
42
43    pub fn head<I, S>(mut self, head: I) -> Self
44    where
45        I: IntoIterator<Item = S>,
46        S: Into<SharedString>,
47    {
48        self.head = head.into_iter().map(Into::into).collect();
49        self
50    }
51
52    pub fn row<I, S>(mut self, row: I) -> Self
53    where
54        I: IntoIterator<Item = S>,
55        S: Into<SharedString>,
56    {
57        self.rows.push(row.into_iter().map(Into::into).collect());
58        self
59    }
60
61    pub fn striped(mut self, striped: bool) -> Self {
62        self.striped = striped;
63        self
64    }
65
66    pub fn highlight_on_hover(mut self, highlight: bool) -> Self {
67        self.highlight_on_hover = highlight;
68        self
69    }
70
71    pub fn with_border(mut self, with_border: bool) -> Self {
72        self.with_border = with_border;
73        self
74    }
75}
76
77impl Default for Table {
78    fn default() -> Self {
79        Table::new()
80    }
81}
82
83impl RenderOnce for Table {
84    fn render(self, _window: &mut Window, cx: &mut App) -> impl IntoElement {
85        let t = theme(cx);
86        let font = t.font_size(Size::Sm);
87        let text = t.text().hsla();
88        let dimmed = t.dimmed().hsla();
89        let line = t.border().hsla();
90        let stripe = t.surface_hover().hsla();
91        let hover = t.color(ColorName::Gray, if t.scheme.is_dark() { 6 } else { 1 }).hsla();
92        let radius = t.radius(Size::Sm);
93        let striped = self.striped;
94        let highlight = self.highlight_on_hover;
95
96        let header = div()
97            .flex()
98            .border_b_1()
99            .border_color(line)
100            .children(
101                self.head
102                    .into_iter()
103                    .map(move |h| cell(h, dimmed, FontWeight::SEMIBOLD, font)),
104            );
105
106        let body = self.rows.into_iter().enumerate().map(move |(i, row)| {
107            let mut tr = div().flex().border_b_1().border_color(line).children(
108                row.into_iter()
109                    .map(move |c| cell(c, text, FontWeight::NORMAL, font)),
110            );
111            if striped && i % 2 == 1 {
112                tr = tr.bg(stripe);
113            }
114            if highlight {
115                tr.id(("guise-table-row", i))
116                    .hover(move |s| s.bg(hover))
117                    .into_any_element()
118            } else {
119                tr.into_any_element()
120            }
121        });
122
123        let mut table = div()
124            .flex()
125            .flex_col()
126            .rounded(px(radius))
127            .child(header)
128            .children(body);
129        if self.with_border {
130            table = table.border_1().border_color(line);
131        }
132        table
133    }
134}