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
92            .color(ColorName::Gray, if t.scheme.is_dark() { 6 } else { 1 })
93            .hsla();
94        let radius = t.radius(Size::Sm);
95        let striped = self.striped;
96        let highlight = self.highlight_on_hover;
97
98        let header = div().flex().border_b_1().border_color(line).children(
99            self.head
100                .into_iter()
101                .map(move |h| cell(h, dimmed, FontWeight::SEMIBOLD, font)),
102        );
103
104        let body = self.rows.into_iter().enumerate().map(move |(i, row)| {
105            let mut tr = div().flex().border_b_1().border_color(line).children(
106                row.into_iter()
107                    .map(move |c| cell(c, text, FontWeight::NORMAL, font)),
108            );
109            if striped && i % 2 == 1 {
110                tr = tr.bg(stripe);
111            }
112            if highlight {
113                tr.id(("guise-table-row", i))
114                    .hover(move |s| s.bg(hover))
115                    .into_any_element()
116            } else {
117                tr.into_any_element()
118            }
119        });
120
121        let mut table = div()
122            .flex()
123            .flex_col()
124            .rounded(px(radius))
125            .child(header)
126            .children(body);
127        if self.with_border {
128            table = table.border_1().border_color(line);
129        }
130        table
131    }
132}