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