table: Set fixed height to table head to fix some case table head disappear bug. (#300)
Closes #299 - Improved sort icon to better color. - Improved table story. - Disable `horizontal_scroll_handle.scroll_to_item` on select_col, there have a bug need to fix.
This commit is contained in:
parent
494e6d15f3
commit
ce10a0340c
4 changed files with 128 additions and 61 deletions
|
|
@ -2,9 +2,11 @@ use std::time::{self, Duration};
|
||||||
|
|
||||||
use fake::{Fake, Faker};
|
use fake::{Fake, Faker};
|
||||||
use gpui::{
|
use gpui::{
|
||||||
div, AnyElement, ClickEvent, IntoElement, ParentElement, Pixels, Render, SharedString, Styled,
|
div, impl_actions, AnyElement, ClickEvent, InteractiveElement, IntoElement, ParentElement,
|
||||||
Timer, View, ViewContext, VisualContext as _, WindowContext,
|
Pixels, Render, SharedString, Styled, Timer, View, ViewContext, VisualContext as _,
|
||||||
|
WindowContext,
|
||||||
};
|
};
|
||||||
|
use serde::Deserialize;
|
||||||
use ui::{
|
use ui::{
|
||||||
button::{Button, ButtonStyled},
|
button::{Button, ButtonStyled},
|
||||||
checkbox::Checkbox,
|
checkbox::Checkbox,
|
||||||
|
|
@ -12,11 +14,17 @@ use ui::{
|
||||||
indicator::Indicator,
|
indicator::Indicator,
|
||||||
input::{InputEvent, TextInput},
|
input::{InputEvent, TextInput},
|
||||||
label::Label,
|
label::Label,
|
||||||
|
popup_menu::PopupMenuExt,
|
||||||
prelude::FluentBuilder as _,
|
prelude::FluentBuilder as _,
|
||||||
table::{ColFixed, ColSort, Table, TableDelegate, TableEvent},
|
table::{ColFixed, ColSort, Table, TableDelegate, TableEvent},
|
||||||
v_flex, Selectable, Sizable, Size,
|
v_flex, Selectable, Sizable, Size,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
#[derive(Clone, PartialEq, Eq, Deserialize)]
|
||||||
|
struct ChangeSize(Size);
|
||||||
|
|
||||||
|
impl_actions!(table_story, [ChangeSize]);
|
||||||
|
|
||||||
#[derive(Clone, Debug, Default)]
|
#[derive(Clone, Debug, Default)]
|
||||||
struct Stock {
|
struct Stock {
|
||||||
id: usize,
|
id: usize,
|
||||||
|
|
@ -166,6 +174,7 @@ struct StockTableDelegate {
|
||||||
col_sort: bool,
|
col_sort: bool,
|
||||||
col_selection: bool,
|
col_selection: bool,
|
||||||
loading: bool,
|
loading: bool,
|
||||||
|
fixed_cols: bool,
|
||||||
is_eof: bool,
|
is_eof: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -232,6 +241,7 @@ impl StockTableDelegate {
|
||||||
col_order: true,
|
col_order: true,
|
||||||
col_sort: true,
|
col_sort: true,
|
||||||
col_selection: true,
|
col_selection: true,
|
||||||
|
fixed_cols: false,
|
||||||
loading: false,
|
loading: false,
|
||||||
is_eof: false,
|
is_eof: false,
|
||||||
}
|
}
|
||||||
|
|
@ -250,9 +260,10 @@ impl StockTableDelegate {
|
||||||
let right_num = ((val - val.floor()) * 1000.).floor() as i32;
|
let right_num = ((val - val.floor()) * 1000.).floor() as i32;
|
||||||
|
|
||||||
let this = if right_num % 3 == 0 {
|
let this = if right_num % 3 == 0 {
|
||||||
this.text_color(ui::red_600()).bg(ui::red_50())
|
this.text_color(ui::red_600()).bg(ui::red_50().opacity(0.6))
|
||||||
} else if right_num % 3 == 1 {
|
} else if right_num % 3 == 1 {
|
||||||
this.text_color(ui::green_600()).bg(ui::green_50())
|
this.text_color(ui::green_600())
|
||||||
|
.bg(ui::green_50().opacity(0.6))
|
||||||
} else {
|
} else {
|
||||||
this
|
this
|
||||||
};
|
};
|
||||||
|
|
@ -287,6 +298,10 @@ impl TableDelegate for StockTableDelegate {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn col_fixed(&self, col_ix: usize) -> Option<ui::table::ColFixed> {
|
fn col_fixed(&self, col_ix: usize) -> Option<ui::table::ColFixed> {
|
||||||
|
if !self.fixed_cols {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
|
||||||
if col_ix < 4 {
|
if col_ix < 4 {
|
||||||
Some(ColFixed::Left)
|
Some(ColFixed::Left)
|
||||||
} else {
|
} else {
|
||||||
|
|
@ -548,40 +563,35 @@ impl TableStory {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn toggle_loop_selection(&mut self, checked: &bool, cx: &mut ViewContext<Self>) {
|
fn toggle_loop_selection(&mut self, checked: &bool, cx: &mut ViewContext<Self>) {
|
||||||
let table = self.table.clone();
|
self.table.update(cx, |table, cx| {
|
||||||
table.update(cx, |table, cx| {
|
|
||||||
table.delegate_mut().loop_selection = *checked;
|
table.delegate_mut().loop_selection = *checked;
|
||||||
cx.notify();
|
cx.notify();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
fn toggle_col_resize(&mut self, checked: &bool, cx: &mut ViewContext<Self>) {
|
fn toggle_col_resize(&mut self, checked: &bool, cx: &mut ViewContext<Self>) {
|
||||||
let table = self.table.clone();
|
self.table.update(cx, |table, cx| {
|
||||||
table.update(cx, |table, cx| {
|
|
||||||
table.delegate_mut().col_resize = *checked;
|
table.delegate_mut().col_resize = *checked;
|
||||||
cx.notify();
|
cx.notify();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
fn toggle_col_order(&mut self, checked: &bool, cx: &mut ViewContext<Self>) {
|
fn toggle_col_order(&mut self, checked: &bool, cx: &mut ViewContext<Self>) {
|
||||||
let table = self.table.clone();
|
self.table.update(cx, |table, cx| {
|
||||||
table.update(cx, |table, cx| {
|
|
||||||
table.delegate_mut().col_order = *checked;
|
table.delegate_mut().col_order = *checked;
|
||||||
cx.notify();
|
cx.notify();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
fn toggle_col_sort(&mut self, checked: &bool, cx: &mut ViewContext<Self>) {
|
fn toggle_col_sort(&mut self, checked: &bool, cx: &mut ViewContext<Self>) {
|
||||||
let table = self.table.clone();
|
self.table.update(cx, |table, cx| {
|
||||||
table.update(cx, |table, cx| {
|
|
||||||
table.delegate_mut().col_sort = *checked;
|
table.delegate_mut().col_sort = *checked;
|
||||||
cx.notify();
|
cx.notify();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
fn toggle_col_selection(&mut self, checked: &bool, cx: &mut ViewContext<Self>) {
|
fn toggle_col_selection(&mut self, checked: &bool, cx: &mut ViewContext<Self>) {
|
||||||
let table = self.table.clone();
|
self.table.update(cx, |table, cx| {
|
||||||
table.update(cx, |table, cx| {
|
|
||||||
table.delegate_mut().col_selection = *checked;
|
table.delegate_mut().col_selection = *checked;
|
||||||
cx.notify();
|
cx.notify();
|
||||||
});
|
});
|
||||||
|
|
@ -590,24 +600,24 @@ impl TableStory {
|
||||||
fn toggle_stripe(&mut self, checked: &bool, cx: &mut ViewContext<Self>) {
|
fn toggle_stripe(&mut self, checked: &bool, cx: &mut ViewContext<Self>) {
|
||||||
self.stripe = *checked;
|
self.stripe = *checked;
|
||||||
let stripe = self.stripe;
|
let stripe = self.stripe;
|
||||||
let table = self.table.clone();
|
self.table.update(cx, |table, cx| {
|
||||||
table.update(cx, |table, cx| {
|
|
||||||
table.set_stripe(stripe, cx);
|
table.set_stripe(stripe, cx);
|
||||||
cx.notify();
|
cx.notify();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
fn toggle_size(&mut self, _: &ClickEvent, cx: &mut ViewContext<Self>) {
|
fn toggle_fixed_cols(&mut self, checked: &bool, cx: &mut ViewContext<Self>) {
|
||||||
self.size = match self.size {
|
|
||||||
Size::XSmall => Size::Small,
|
|
||||||
Size::Small => Size::Medium,
|
|
||||||
Size::Medium => Size::Large,
|
|
||||||
Size::Large => Size::XSmall,
|
|
||||||
_ => Size::default(),
|
|
||||||
};
|
|
||||||
|
|
||||||
self.table.update(cx, |table, cx| {
|
self.table.update(cx, |table, cx| {
|
||||||
table.set_size(self.size, cx);
|
table.delegate_mut().fixed_cols = *checked;
|
||||||
|
table.refresh(cx);
|
||||||
|
cx.notify();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
fn on_change_size(&mut self, a: &ChangeSize, cx: &mut ViewContext<Self>) {
|
||||||
|
self.size = a.0;
|
||||||
|
self.table.update(cx, |table, cx| {
|
||||||
|
table.set_size(a.0, cx);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -635,14 +645,46 @@ impl TableStory {
|
||||||
impl Render for TableStory {
|
impl Render for TableStory {
|
||||||
fn render(&mut self, cx: &mut ViewContext<Self>) -> impl gpui::IntoElement {
|
fn render(&mut self, cx: &mut ViewContext<Self>) -> impl gpui::IntoElement {
|
||||||
let delegate = self.table.read(cx).delegate();
|
let delegate = self.table.read(cx).delegate();
|
||||||
|
let size = self.size;
|
||||||
|
|
||||||
v_flex()
|
v_flex()
|
||||||
|
.on_action(cx.listener(Self::on_change_size))
|
||||||
.size_full()
|
.size_full()
|
||||||
|
.text_sm()
|
||||||
.gap_2()
|
.gap_2()
|
||||||
.child(
|
.child(
|
||||||
h_flex()
|
h_flex()
|
||||||
.items_center()
|
.items_center()
|
||||||
.gap_2()
|
.gap_3()
|
||||||
|
.flex_wrap()
|
||||||
|
.child(
|
||||||
|
Button::new("size")
|
||||||
|
.compact()
|
||||||
|
.outline()
|
||||||
|
.label(format!("size: {:?}", self.size))
|
||||||
|
.popup_menu(move |menu, cx| {
|
||||||
|
menu.menu_with_check(
|
||||||
|
"Large",
|
||||||
|
size == Size::Large,
|
||||||
|
Box::new(ChangeSize(Size::Large)),
|
||||||
|
)
|
||||||
|
.menu_with_check(
|
||||||
|
"Medium",
|
||||||
|
size == Size::Medium,
|
||||||
|
Box::new(ChangeSize(Size::Medium)),
|
||||||
|
)
|
||||||
|
.menu_with_check(
|
||||||
|
"Small",
|
||||||
|
size == Size::Small,
|
||||||
|
Box::new(ChangeSize(Size::Small)),
|
||||||
|
)
|
||||||
|
.menu_with_check(
|
||||||
|
"XSmall",
|
||||||
|
size == Size::XSmall,
|
||||||
|
Box::new(ChangeSize(Size::XSmall)),
|
||||||
|
)
|
||||||
|
}),
|
||||||
|
)
|
||||||
.child(
|
.child(
|
||||||
Checkbox::new("loop-selection")
|
Checkbox::new("loop-selection")
|
||||||
.label("Loop Selection")
|
.label("Loop Selection")
|
||||||
|
|
@ -680,12 +722,10 @@ impl Render for TableStory {
|
||||||
.on_click(cx.listener(Self::toggle_stripe)),
|
.on_click(cx.listener(Self::toggle_stripe)),
|
||||||
)
|
)
|
||||||
.child(
|
.child(
|
||||||
Button::new("size")
|
Checkbox::new("fixed-cols")
|
||||||
.small()
|
.label("Fixed Columns")
|
||||||
.compact()
|
.selected(delegate.fixed_cols)
|
||||||
.outline()
|
.on_click(cx.listener(Self::toggle_fixed_cols)),
|
||||||
.label(format!("size: {:?}", self.size))
|
|
||||||
.on_click(cx.listener(Self::toggle_size)),
|
|
||||||
)
|
)
|
||||||
.child(
|
.child(
|
||||||
Checkbox::new("refresh-data")
|
Checkbox::new("refresh-data")
|
||||||
|
|
|
||||||
|
|
@ -5,6 +5,7 @@ use crate::{
|
||||||
theme::ActiveTheme,
|
theme::ActiveTheme,
|
||||||
};
|
};
|
||||||
use gpui::{div, px, Axis, Div, Element, EntityId, FocusHandle, Pixels, Styled, WindowContext};
|
use gpui::{div, px, Axis, Div, Element, EntityId, FocusHandle, Pixels, Styled, WindowContext};
|
||||||
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
/// Returns a `Div` as horizontal flex layout.
|
/// Returns a `Div` as horizontal flex layout.
|
||||||
pub fn h_flex() -> Div {
|
pub fn h_flex() -> Div {
|
||||||
|
|
@ -133,7 +134,7 @@ pub trait StyledExt: Styled + Sized {
|
||||||
impl<E: Styled> StyledExt for E {}
|
impl<E: Styled> StyledExt for E {}
|
||||||
|
|
||||||
/// A size for elements.
|
/// A size for elements.
|
||||||
#[derive(Clone, Default, Copy, PartialEq, Eq, Debug)]
|
#[derive(Clone, Default, Copy, PartialEq, Eq, Debug, Deserialize, Serialize)]
|
||||||
pub enum Size {
|
pub enum Size {
|
||||||
Size(Pixels),
|
Size(Pixels),
|
||||||
XSmall,
|
XSmall,
|
||||||
|
|
|
||||||
|
|
@ -336,9 +336,9 @@ where
|
||||||
self.selected_col = Some(col_ix);
|
self.selected_col = Some(col_ix);
|
||||||
if let Some(col_ix) = self.selected_col {
|
if let Some(col_ix) = self.selected_col {
|
||||||
// TODO: Fix scroll to selected col, this was not working after fixed col.
|
// TODO: Fix scroll to selected col, this was not working after fixed col.
|
||||||
if self.col_groups[col_ix].fixed.is_none() {
|
// if self.col_groups[col_ix].fixed.is_none() {
|
||||||
self.horizontal_scroll_handle.scroll_to_item(col_ix);
|
// self.horizontal_scroll_handle.scroll_to_item(col_ix);
|
||||||
}
|
// }
|
||||||
}
|
}
|
||||||
cx.emit(TableEvent::SelectCol(col_ix));
|
cx.emit(TableEvent::SelectCol(col_ix));
|
||||||
cx.notify();
|
cx.notify();
|
||||||
|
|
@ -426,8 +426,8 @@ where
|
||||||
.whitespace_nowrap()
|
.whitespace_nowrap()
|
||||||
.map(|this| match self.size {
|
.map(|this| match self.size {
|
||||||
Size::XSmall => this.text_sm().py_0p5().px_1(),
|
Size::XSmall => this.text_sm().py_0p5().px_1(),
|
||||||
Size::Small => this.text_sm().py_1().px_1p5(),
|
Size::Small => this.text_sm().py(px(3.)).px_1p5(),
|
||||||
Size::Large => this.py_1p5().px_3(),
|
Size::Large => this.py_2().px_3(),
|
||||||
_ => this.py_1().px_2(),
|
_ => this.py_1().px_2(),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
@ -658,10 +658,10 @@ where
|
||||||
|
|
||||||
let sort = sort.unwrap();
|
let sort = sort.unwrap();
|
||||||
|
|
||||||
let icon = match sort {
|
let (icon, is_on) = match sort {
|
||||||
ColSort::Ascending => IconName::SortAscending,
|
ColSort::Ascending => (IconName::SortAscending, true),
|
||||||
ColSort::Descending => IconName::SortDescending,
|
ColSort::Descending => (IconName::SortDescending, true),
|
||||||
ColSort::Default => IconName::ChevronsUpDown,
|
ColSort::Default => (IconName::ChevronsUpDown, false),
|
||||||
};
|
};
|
||||||
|
|
||||||
Some(
|
Some(
|
||||||
|
|
@ -671,8 +671,12 @@ where
|
||||||
.ml_2()
|
.ml_2()
|
||||||
.p(px(2.))
|
.p(px(2.))
|
||||||
.rounded_sm()
|
.rounded_sm()
|
||||||
.hover(|this| this.bg(cx.theme().secondary))
|
.map(|this| match is_on {
|
||||||
.active(|this| this.bg(cx.theme().secondary_active))
|
true => this,
|
||||||
|
false => this.opacity(0.5),
|
||||||
|
})
|
||||||
|
.hover(|this| this.bg(cx.theme().secondary).opacity(7.))
|
||||||
|
.active(|this| this.bg(cx.theme().secondary_active).opacity(1.))
|
||||||
.on_mouse_down(MouseButton::Left, |_, cx| cx.stop_propagation())
|
.on_mouse_down(MouseButton::Left, |_, cx| cx.stop_propagation())
|
||||||
.on_click(cx.listener(move |table, _, cx| table.perform_sort(col_ix, cx)))
|
.on_click(cx.listener(move |table, _, cx| table.perform_sort(col_ix, cx)))
|
||||||
.child(
|
.child(
|
||||||
|
|
@ -787,31 +791,45 @@ where
|
||||||
) -> impl IntoElement {
|
) -> impl IntoElement {
|
||||||
let view = cx.view().clone();
|
let view = cx.view().clone();
|
||||||
let horizontal_scroll_handle = self.horizontal_scroll_handle.clone();
|
let horizontal_scroll_handle = self.horizontal_scroll_handle.clone();
|
||||||
|
let fixed_cols_count = self
|
||||||
|
.col_groups
|
||||||
|
.iter()
|
||||||
|
.filter(|col| col.fixed.is_some())
|
||||||
|
.count();
|
||||||
|
|
||||||
h_flex()
|
h_flex()
|
||||||
.w_full()
|
.w_full()
|
||||||
|
.map(|this| match self.size {
|
||||||
|
Size::Large => this.h_10(),
|
||||||
|
Size::Small => this.h(px(30.)),
|
||||||
|
Size::XSmall => this.h(px(26.)),
|
||||||
|
_ => this.h_8(),
|
||||||
|
})
|
||||||
.flex_shrink_0()
|
.flex_shrink_0()
|
||||||
.border_b_1()
|
.border_b_1()
|
||||||
.border_color(cx.theme().border)
|
.border_color(cx.theme().border)
|
||||||
.child(
|
.text_color(cx.theme().table_head_foreground)
|
||||||
|
.when(fixed_cols_count > 0, |this| {
|
||||||
// Render left fixed columns
|
// Render left fixed columns
|
||||||
h_flex()
|
this.child(
|
||||||
.id("table-head-fixed-left")
|
h_flex()
|
||||||
.h_full()
|
.id("table-head-fixed-left")
|
||||||
.bg(cx.theme().table_head)
|
.h_full()
|
||||||
.border_r_1()
|
.bg(cx.theme().table_head)
|
||||||
.border_color(cx.theme().border)
|
.border_r_1()
|
||||||
.children(
|
.border_color(cx.theme().border)
|
||||||
self.col_groups
|
.children(
|
||||||
.iter()
|
self.col_groups
|
||||||
.filter(|col| col.fixed == Some(ColFixed::Left))
|
.iter()
|
||||||
.enumerate()
|
.filter(|col| col.fixed == Some(ColFixed::Left))
|
||||||
.map(|(col_ix, _)| self.render_th(col_ix, cx)),
|
.enumerate()
|
||||||
),
|
.map(|(col_ix, _)| self.render_th(col_ix, cx)),
|
||||||
)
|
),
|
||||||
|
)
|
||||||
|
})
|
||||||
.child(
|
.child(
|
||||||
// Render other normal columns
|
// Render other normal columns
|
||||||
uniform_list(view.clone(), "table-uniform-list-head", 1, {
|
uniform_list(view.clone(), "table-head-uniform-list", 1, {
|
||||||
let horizontal_scroll_handle = horizontal_scroll_handle.clone();
|
let horizontal_scroll_handle = horizontal_scroll_handle.clone();
|
||||||
let view = view.clone();
|
let view = view.clone();
|
||||||
move |table, _, cx| {
|
move |table, _, cx| {
|
||||||
|
|
@ -856,6 +874,12 @@ where
|
||||||
.map(|this| vec![this])
|
.map(|this| vec![this])
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
.map(|this| match self.size {
|
||||||
|
Size::Large => this.h_10(),
|
||||||
|
Size::Small => this.h(px(30.)),
|
||||||
|
Size::XSmall => this.h(px(26.)),
|
||||||
|
_ => this.h_8(),
|
||||||
|
})
|
||||||
.h_full()
|
.h_full()
|
||||||
.flex_1(),
|
.flex_1(),
|
||||||
)
|
)
|
||||||
|
|
|
||||||
|
|
@ -315,6 +315,7 @@ pub struct Theme {
|
||||||
pub table: Hsla,
|
pub table: Hsla,
|
||||||
pub table_even: Hsla,
|
pub table_even: Hsla,
|
||||||
pub table_head: Hsla,
|
pub table_head: Hsla,
|
||||||
|
pub table_head_foreground: Hsla,
|
||||||
pub table_row_border: Hsla,
|
pub table_row_border: Hsla,
|
||||||
pub table_active: Hsla,
|
pub table_active: Hsla,
|
||||||
pub table_hover: Hsla,
|
pub table_hover: Hsla,
|
||||||
|
|
@ -398,6 +399,7 @@ impl From<Colors> for Theme {
|
||||||
table_active: colors.list_active,
|
table_active: colors.list_active,
|
||||||
table_hover: colors.list_active.opacity(0.8),
|
table_hover: colors.list_active.opacity(0.8),
|
||||||
table_row_border: colors.border.opacity(0.5),
|
table_row_border: colors.border.opacity(0.5),
|
||||||
|
table_head_foreground: colors.foreground.opacity(0.7),
|
||||||
link: colors.link,
|
link: colors.link,
|
||||||
link_hover: colors.link.lighten(0.2),
|
link_hover: colors.link.lighten(0.2),
|
||||||
link_active: colors.link.darken(0.2),
|
link_active: colors.link.darken(0.2),
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue