Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 15 additions & 1 deletion crates/component/src/table/data_table.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ use crate::{
};
use gpui::{
App, Edges, Entity, Focusable, InteractiveElement, IntoElement, KeyBinding, ParentElement,
RenderOnce, Styled, Window, div, prelude::FluentBuilder,
Pixels, RenderOnce, Styled, Window, div, prelude::FluentBuilder,
};
use gpui_base::TestSupportExt as _;

Expand Down Expand Up @@ -37,6 +37,8 @@ pub(super) struct TableOptions {
pub(super) bordered: bool,
/// The cell size of the table.
pub(super) size: Size,
/// Height of each header row. `None` uses [`Size::table_row_height`].
pub(super) header_row_height: Option<Pixels>,
}

impl Default for TableOptions {
Expand All @@ -46,6 +48,7 @@ impl Default for TableOptions {
stripe: false,
bordered: true,
size: Size::default(),
header_row_height: None,
}
}
}
Expand Down Expand Up @@ -117,6 +120,17 @@ where
self
}

/// Set the height of each header row.
///
/// Column-group rows use the same height, so the whole header is this
/// height times the number of header rows. Body rows keep the height from
/// [`Size::table_row_height`]. When this is unset, header rows use that
/// same height.
pub fn header_row_height(mut self, height: impl Into<Pixels>) -> Self {
self.options.header_row_height = Some(height.into());
self
}

/// Set scrollbar visibility.
pub fn scrollbar_visible(mut self, vertical: bool, horizontal: bool) -> Self {
self.options.scrollbar_visible = Edges {
Expand Down
26 changes: 20 additions & 6 deletions crates/component/src/table/state.rs
Original file line number Diff line number Diff line change
Expand Up @@ -771,11 +771,24 @@ where
.count()
}

/// One header row. An unset header height matches the body rows.
fn header_row_height(&self) -> Pixels {
self.options
.header_row_height
.unwrap_or(self.options.size.table_row_height())
}

/// Every header row, including column-group rows. An empty layout still
/// reserves one row, matching the vertical scrollbar's top inset.
fn header_band_height(&self) -> Pixels {
self.header_row_height() * self.header_layout.len().max(1) as f32
}

fn page_item_count(&self) -> usize {
let row_height = self.options.size.table_row_height();
let height = self.bounds.size.height;
let count = (height / row_height).floor() as usize;
count.saturating_sub(1).max(1)
let body_height = (self.bounds.size.height - self.header_band_height()).max(px(0.));
let count = (body_height / row_height).floor() as usize;
count.max(1)
}

fn on_row_right_click(
Expand Down Expand Up @@ -1821,6 +1834,7 @@ where
self.calculate_visible_leaf_col_range(left_columns_count);

let layout_len = self.header_layout.len();
let header_row_height = self.header_row_height();

// Reset fixed head columns bounds, if no fixed columns are present
if left_columns_count == 0 {
Expand Down Expand Up @@ -1881,7 +1895,7 @@ where
layout.iter().enumerate().map(|(_row_ix, row_cells)| {
h_flex()
.min_w_full()
.h(self.options.size.table_row_height())
.h(header_row_height)
.border_b_1()
.border_color(cx.theme().border)
.children(row_cells.iter().filter_map(|cell| {
Expand Down Expand Up @@ -1942,7 +1956,7 @@ where
let is_leaf_row = row_ix + 1 == layout_len;
h_flex()
.min_w_full()
.h(self.options.size.table_row_height())
.h(header_row_height)
.border_b_1()
.border_color(cx.theme().border)
.map(|this| {
Expand Down Expand Up @@ -2395,7 +2409,7 @@ where
Some(
div()
.absolute()
.top(self.options.size.table_row_height() * self.header_layout.len().max(1) as f32)
.top(self.header_band_height())
.right_0()
.bottom_0()
.w(Scrollbar::width())
Expand Down
45 changes: 45 additions & 0 deletions crates/story/src/stories/data_table_story.rs
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,10 @@ use crate::story_toolbar_group;
#[action(namespace = data_table_story, no_json)]
struct ChangeSize(Size);

#[derive(Action, Clone, PartialEq, Eq, Deserialize)]
#[action(namespace = data_table_story, no_json)]
struct ChangeHeaderRowHeight(Option<Pixels>);

#[derive(Action, Clone, PartialEq, Eq, Deserialize)]
#[action(namespace = data_table_story, no_json)]
struct ChangeRows(usize);
Expand Down Expand Up @@ -898,6 +902,7 @@ pub struct DataTableStory {
stripe: bool,
refresh_data: bool,
size: Size,
header_row_height: Option<Pixels>,

_subscriptions: Vec<Subscription>,
_load_task: Task<()>,
Expand Down Expand Up @@ -980,6 +985,7 @@ impl DataTableStory {
stripe: false,
refresh_data: false,
size: Size::default(),
header_row_height: None,
_subscriptions,
_load_task,
_load_rows_task: Task::ready(()),
Expand Down Expand Up @@ -1059,6 +1065,16 @@ impl DataTableStory {
cx.notify();
}

fn on_change_header_row_height(
&mut self,
a: &ChangeHeaderRowHeight,
_: &mut Window,
cx: &mut Context<Self>,
) {
self.header_row_height = a.0;
cx.notify();
}

fn toggle_refresh_data(&mut self, checked: &bool, _: &mut Window, cx: &mut Context<Self>) {
self.refresh_data = *checked;
cx.notify();
Expand Down Expand Up @@ -1173,6 +1189,7 @@ impl Render for DataTableStory {
let rows_count = delegate.rows_count(cx);
let columns_count = delegate.columns_count(cx);
let size = self.size;
let header_row_height = self.header_row_height;
let loop_selection = table.loop_selection;
let col_resizable = table.col_resizable;
let col_movable = table.col_movable;
Expand All @@ -1190,6 +1207,7 @@ impl Render for DataTableStory {

v_flex()
.on_action(cx.listener(Self::on_change_size))
.on_action(cx.listener(Self::on_change_header_row_height))
.on_action(cx.listener(|this, action: &ChangeRows, _, cx| {
if action.0 == this.table.read(cx).delegate().stocks.len() {
return;
Expand Down Expand Up @@ -1335,6 +1353,30 @@ impl Render for DataTableStory {
)
},
)
.dropdown_child(
Button::new("header-row-height").label(format!(
"Header Row Height: {}",
header_row_height
.map_or("Default".to_string(), |height| height.to_string())
)),
move |menu, _, _| {
menu.menu_with_check(
"Default",
header_row_height.is_none(),
Box::new(ChangeHeaderRowHeight(None)),
)
.menu_with_check(
"40px",
header_row_height == Some(px(40.)),
Box::new(ChangeHeaderRowHeight(Some(px(40.)))),
)
.menu_with_check(
"48px",
header_row_height == Some(px(48.)),
Box::new(ChangeHeaderRowHeight(Some(px(48.)))),
)
},
)
.dropdown_child(
Button::new("rows").label(format!("Rows: {rows_count}")),
move |menu, _, _| {
Expand Down Expand Up @@ -1492,6 +1534,9 @@ impl Render for DataTableStory {
.child(
DataTable::new(&self.table)
.with_size(self.size)
.when_some(self.header_row_height, |this, height| {
this.header_row_height(height)
})
.stripe(self.stripe),
)
.child(
Expand Down
5 changes: 3 additions & 2 deletions website/component/data-table.md
Original file line number Diff line number Diff line change
Expand Up @@ -438,7 +438,7 @@ impl TableDelegate for MyTableDelegate {

### Table Styling

Customize table appearance. `DataTable` implements `Sizable`: use preset sizes such as `.small()` and `.large()` for standard density, or pass a custom pixel size to set a uniform header and body row height.
Customize table appearance. `DataTable` implements `Sizable`: use preset sizes such as `.small()` and `.large()` for standard density, or pass a custom pixel size to set the row height. Header rows use the same height unless you set `header_row_height`, which applies to each header row, column-group rows included.

```rust
use gpui_kit::px;
Expand All @@ -450,7 +450,8 @@ let state = cx.new(|cx| {

// In render
DataTable::new(&state)
.with_size(px(48.)) // Custom uniform row height
.with_size(px(32.)) // Custom body row height
.header_row_height(px(48.)) // Taller header rows
.stripe(true) // Alternating row colors
.bordered(true) // Border around table
.scrollbar_visible(true, true) // Vertical, horizontal scrollbars
Expand Down
5 changes: 3 additions & 2 deletions website/zh-CN/component/data-table.md
Original file line number Diff line number Diff line change
Expand Up @@ -350,14 +350,15 @@ impl TableDelegate for MyTableDelegate {

## 表格样式

`DataTable` 实现了 `Sizable`:可以用 `.small()`、`.large()` 等预设尺寸调整表格密度,也可以传入自定义像素值来设置统一的表头和表体行高。
`DataTable` 实现了 `Sizable`:可以用 `.small()`、`.large()` 等预设尺寸调整表格密度,也可以传入自定义像素值来设置行高。表头默认沿用这个行高;用 `header_row_height` 可以单独设置每一行表头的高度,分组表头的每一行也使用这个高度。

```rust
use gpui_kit::px;
use gpui_kit::component::Sizable as _;

DataTable::new(&state)
.with_size(px(48.))
.with_size(px(32.))
.header_row_height(px(48.))
.stripe(true)
.bordered(true)
.scrollbar_visible(true, true)
Expand Down
Loading