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
101        .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
109          .into_iter()
110          .map(move |c| cell(c, text, FontWeight::NORMAL, font)),
111      );
112      if striped && i % 2 == 1 {
113        tr = tr.bg(stripe);
114      }
115      if highlight {
116        tr.id(("guise-table-row", i))
117          .hover(move |s| s.bg(hover))
118          .into_any_element()
119      } else {
120        tr.into_any_element()
121      }
122    });
123
124    let mut table = div()
125      .flex()
126      .flex_col()
127      .rounded(px(radius))
128      .child(header)
129      .children(body);
130    if self.with_border {
131      table = table.border_1().border_color(line);
132    }
133    table.probe("Table")
134  }
135}