guise-ui 1.5.2

A component library for gpui, Zed's GPU-accelerated UI framework: a themed palette, sizing tokens, 130+ composable components, a reactive state layer, and an in-app Safari-style inspector.
Documentation
//! `Table` — a simple data table of string cells.

use gpui::prelude::*;
use gpui::{div, px, App, Div, FontWeight, Hsla, IntoElement, SharedString, Window};

use crate::devtools::Probed;
use crate::theme::{theme, ColorName, Size};

/// One table cell. A free fn (not a closure) so it can be reused by both the
/// header and body iterators without being moved twice.
fn cell(value: SharedString, color: Hsla, weight: FontWeight, font: f32) -> Div {
  div()
    .flex_1()
    .px(px(12.0))
    .py(px(8.0))
    .text_size(px(font))
    .text_color(color)
    .font_weight(weight)
    .child(value)
}

/// A data table. Cells are plain strings; columns size
/// equally.
#[derive(IntoElement)]
pub struct Table {
  head: Vec<SharedString>,
  rows: Vec<Vec<SharedString>>,
  striped: bool,
  highlight_on_hover: bool,
  with_border: bool,
}

impl Table {
  pub fn new() -> Self {
    Table {
      head: Vec::new(),
      rows: Vec::new(),
      striped: false,
      highlight_on_hover: false,
      with_border: false,
    }
  }

  pub fn head<I, S>(mut self, head: I) -> Self
  where
    I: IntoIterator<Item = S>,
    S: Into<SharedString>,
  {
    self.head = head.into_iter().map(Into::into).collect();
    self
  }

  pub fn row<I, S>(mut self, row: I) -> Self
  where
    I: IntoIterator<Item = S>,
    S: Into<SharedString>,
  {
    self.rows.push(row.into_iter().map(Into::into).collect());
    self
  }

  pub fn striped(mut self, striped: bool) -> Self {
    self.striped = striped;
    self
  }

  pub fn highlight_on_hover(mut self, highlight: bool) -> Self {
    self.highlight_on_hover = highlight;
    self
  }

  pub fn with_border(mut self, with_border: bool) -> Self {
    self.with_border = with_border;
    self
  }
}

impl Default for Table {
  fn default() -> Self {
    Table::new()
  }
}

impl RenderOnce for Table {
  fn render(self, _window: &mut Window, cx: &mut App) -> impl IntoElement {
    let t = theme(cx);
    let font = t.font_size(Size::Sm);
    let text = t.text().hsla();
    let dimmed = t.dimmed().hsla();
    let line = t.border().hsla();
    let stripe = t.surface_hover().hsla();
    let hover = t
      .color(ColorName::Gray, if t.scheme.is_dark() { 6 } else { 1 })
      .hsla();
    let radius = t.radius(Size::Sm);
    let striped = self.striped;
    let highlight = self.highlight_on_hover;

    let header = div().flex().border_b_1().border_color(line).children(
      self
        .head
        .into_iter()
        .map(move |h| cell(h, dimmed, FontWeight::SEMIBOLD, font)),
    );

    let body = self.rows.into_iter().enumerate().map(move |(i, row)| {
      let mut tr = div().flex().border_b_1().border_color(line).children(
        row
          .into_iter()
          .map(move |c| cell(c, text, FontWeight::NORMAL, font)),
      );
      if striped && i % 2 == 1 {
        tr = tr.bg(stripe);
      }
      if highlight {
        tr.id(("guise-table-row", i))
          .hover(move |s| s.bg(hover))
          .into_any_element()
      } else {
        tr.into_any_element()
      }
    });

    let mut table = div()
      .flex()
      .flex_col()
      .rounded(px(radius))
      .child(header)
      .children(body);
    if self.with_border {
      table = table.border_1().border_color(line);
    }
    table.probe("Table")
  }
}