table: Improve sort icon color and fix stripe row display. (#578)
- Improve sort icon color: <img width="139" alt="image" src="https://github.com/user-attachments/assets/6374c26a-4555-4735-953b-dbd961fe5b31" /> - Avoid duplicate border when enabled fixed column. <img width="587" alt="image" src="https://github.com/user-attachments/assets/6e186d29-5d8a-404e-b160-20c2af6a16fa" /> TODO - [x] The extra rows not show when `stripe` is true.
This commit is contained in:
parent
649e765729
commit
e658aeee96
4 changed files with 160 additions and 101 deletions
|
|
@ -1,4 +1,15 @@
|
||||||
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-chevrons-up-down">
|
<svg
|
||||||
<path opacity="0.5" d="m7 15 5 5 5-5"/>
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
<path d="m7 9 5-5 5 5"/>
|
width="24"
|
||||||
|
height="24"
|
||||||
|
viewBox="0 0 24 24"
|
||||||
|
fill="none"
|
||||||
|
stroke="currentColor"
|
||||||
|
stroke-width="2"
|
||||||
|
stroke-linecap="round"
|
||||||
|
stroke-linejoin="round"
|
||||||
|
class="lucide lucide-chevrons-up-down"
|
||||||
|
>
|
||||||
|
<path opacity="0.2" d="m7 15 5 5 5-5" />
|
||||||
|
<path d="m7 9 5-5 5 5" />
|
||||||
</svg>
|
</svg>
|
||||||
|
|
|
||||||
|
Before Width: | Height: | Size: 297 B After Width: | Height: | Size: 340 B |
|
|
@ -1,4 +1,15 @@
|
||||||
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-chevrons-up-down">
|
<svg
|
||||||
<path d="m7 15 5 5 5-5"/>
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
<path opacity="0.5" d="m7 9 5-5 5 5"/>
|
width="24"
|
||||||
|
height="24"
|
||||||
|
viewBox="0 0 24 24"
|
||||||
|
fill="none"
|
||||||
|
stroke="currentColor"
|
||||||
|
stroke-width="2"
|
||||||
|
stroke-linecap="round"
|
||||||
|
stroke-linejoin="round"
|
||||||
|
class="lucide lucide-chevrons-up-down"
|
||||||
|
>
|
||||||
|
<path d="m7 15 5 5 5-5" />
|
||||||
|
<path opacity="0.2" d="m7 9 5-5 5 5" />
|
||||||
</svg>
|
</svg>
|
||||||
|
|
|
||||||
|
Before Width: | Height: | Size: 297 B After Width: | Height: | Size: 340 B |
|
|
@ -200,52 +200,44 @@ impl StockTableDelegate {
|
||||||
Column::new("price", "Price", Some(ColSort::Default)),
|
Column::new("price", "Price", Some(ColSort::Default)),
|
||||||
Column::new("change", "Chg", Some(ColSort::Default)),
|
Column::new("change", "Chg", Some(ColSort::Default)),
|
||||||
Column::new("change_percent", "Chg%", Some(ColSort::Default)),
|
Column::new("change_percent", "Chg%", Some(ColSort::Default)),
|
||||||
Column::new("volume", "Volume", Some(ColSort::Default)),
|
Column::new("volume", "Volume", None),
|
||||||
Column::new("turnover", "Turnover", Some(ColSort::Default)),
|
Column::new("turnover", "Turnover", None),
|
||||||
Column::new("market_cap", "Market Cap", Some(ColSort::Default)),
|
Column::new("market_cap", "Market Cap", None),
|
||||||
Column::new("ttm", "TTM", Some(ColSort::Default)),
|
Column::new("ttm", "TTM", None),
|
||||||
Column::new("five_mins_ranking", "5m Ranking", Some(ColSort::Default)),
|
Column::new("five_mins_ranking", "5m Ranking", None),
|
||||||
Column::new("th60_days_ranking", "60d Ranking", Some(ColSort::Default)),
|
Column::new("th60_days_ranking", "60d Ranking", None),
|
||||||
Column::new("year_change_percent", "Year Chg%", Some(ColSort::Default)),
|
Column::new("year_change_percent", "Year Chg%", None),
|
||||||
Column::new("bid", "Bid", Some(ColSort::Default)),
|
Column::new("bid", "Bid", None),
|
||||||
Column::new("bid_volume", "Bid Vol", Some(ColSort::Default)),
|
Column::new("bid_volume", "Bid Vol", None),
|
||||||
Column::new("ask", "Ask", Some(ColSort::Default)),
|
Column::new("ask", "Ask", None),
|
||||||
Column::new("ask_volume", "Ask Vol", Some(ColSort::Default)),
|
Column::new("ask_volume", "Ask Vol", None),
|
||||||
Column::new("open", "Open", Some(ColSort::Default)),
|
Column::new("open", "Open", None),
|
||||||
Column::new("prev_close", "Prev Close", Some(ColSort::Default)),
|
Column::new("prev_close", "Prev Close", None),
|
||||||
Column::new("high", "High", Some(ColSort::Default)),
|
Column::new("high", "High", None),
|
||||||
Column::new("low", "Low", Some(ColSort::Default)),
|
Column::new("low", "Low", None),
|
||||||
Column::new("turnover_rate", "Turnover Rate", Some(ColSort::Default)),
|
Column::new("turnover_rate", "Turnover Rate", None),
|
||||||
Column::new("rise_rate", "Rise Rate", Some(ColSort::Default)),
|
Column::new("rise_rate", "Rise Rate", None),
|
||||||
Column::new("amplitude", "Amplitude", Some(ColSort::Default)),
|
Column::new("amplitude", "Amplitude", None),
|
||||||
Column::new("pe_status", "P/E", Some(ColSort::Default)),
|
Column::new("pe_status", "P/E", None),
|
||||||
Column::new("pb_status", "P/B", Some(ColSort::Default)),
|
Column::new("pb_status", "P/B", None),
|
||||||
Column::new("volume_ratio", "Volume Ratio", Some(ColSort::Default)),
|
Column::new("volume_ratio", "Volume Ratio", None),
|
||||||
Column::new("bid_ask_ratio", "Bid Ask Ratio", Some(ColSort::Default)),
|
Column::new("bid_ask_ratio", "Bid Ask Ratio", None),
|
||||||
Column::new(
|
Column::new("latest_pre_close", "Latest Pre Close", None),
|
||||||
"latest_pre_close",
|
Column::new("latest_post_close", "Latest Post Close", None),
|
||||||
"Latest Pre Close",
|
Column::new("pre_market_cap", "Pre Mkt Cap", None),
|
||||||
Some(ColSort::Default),
|
Column::new("pre_market_percent", "Pre Mkt%", None),
|
||||||
),
|
Column::new("pre_market_change", "Pre Mkt Chg", None),
|
||||||
Column::new(
|
Column::new("post_market_cap", "Post Mkt Cap", None),
|
||||||
"latest_post_close",
|
Column::new("post_market_percent", "Post Mkt%", None),
|
||||||
"Latest Post Close",
|
Column::new("post_market_change", "Post Mkt Chg", None),
|
||||||
Some(ColSort::Default),
|
Column::new("float_cap", "Float Cap", None),
|
||||||
),
|
Column::new("shares", "Shares", None),
|
||||||
Column::new("pre_market_cap", "Pre Mkt Cap", Some(ColSort::Default)),
|
Column::new("shares_float", "Float Shares", None),
|
||||||
Column::new("pre_market_percent", "Pre Mkt%", Some(ColSort::Default)),
|
Column::new("day_5_ranking", "5d Ranking", None),
|
||||||
Column::new("pre_market_change", "Pre Mkt Chg", Some(ColSort::Default)),
|
Column::new("day_10_ranking", "10d Ranking", None),
|
||||||
Column::new("post_market_cap", "Post Mkt Cap", Some(ColSort::Default)),
|
Column::new("day_30_ranking", "30d Ranking", None),
|
||||||
Column::new("post_market_percent", "Post Mkt%", Some(ColSort::Default)),
|
Column::new("day_120_ranking", "120d Ranking", None),
|
||||||
Column::new("post_market_change", "Post Mkt Chg", Some(ColSort::Default)),
|
Column::new("day_250_ranking", "250d Ranking", None),
|
||||||
Column::new("float_cap", "Float Cap", Some(ColSort::Default)),
|
|
||||||
Column::new("shares", "Shares", Some(ColSort::Default)),
|
|
||||||
Column::new("shares_float", "Float Shares", Some(ColSort::Default)),
|
|
||||||
Column::new("day_5_ranking", "5d Ranking", Some(ColSort::Default)),
|
|
||||||
Column::new("day_10_ranking", "10d Ranking", Some(ColSort::Default)),
|
|
||||||
Column::new("day_30_ranking", "30d Ranking", Some(ColSort::Default)),
|
|
||||||
Column::new("day_120_ranking", "120d Ranking", Some(ColSort::Default)),
|
|
||||||
Column::new("day_250_ranking", "250d Ranking", Some(ColSort::Default)),
|
|
||||||
],
|
],
|
||||||
loop_selection: true,
|
loop_selection: true,
|
||||||
col_resize: true,
|
col_resize: true,
|
||||||
|
|
@ -263,7 +255,7 @@ impl StockTableDelegate {
|
||||||
|
|
||||||
fn update_stocks(&mut self, size: usize) {
|
fn update_stocks(&mut self, size: usize) {
|
||||||
self.stocks = random_stocks(size);
|
self.stocks = random_stocks(size);
|
||||||
self.is_eof = false;
|
self.is_eof = size <= 50;
|
||||||
self.loading = false;
|
self.loading = false;
|
||||||
self.full_loading = false;
|
self.full_loading = false;
|
||||||
}
|
}
|
||||||
|
|
@ -477,6 +469,17 @@ impl TableDelegate for StockTableDelegate {
|
||||||
ColSort::Descending => b.id.cmp(&a.id),
|
ColSort::Descending => b.id.cmp(&a.id),
|
||||||
_ => a.id.cmp(&b.id),
|
_ => a.id.cmp(&b.id),
|
||||||
}),
|
}),
|
||||||
|
"symbol" => self.stocks.sort_by(|a, b| match sort {
|
||||||
|
ColSort::Descending => b.symbol.cmp(&a.symbol),
|
||||||
|
_ => a.id.cmp(&b.id),
|
||||||
|
}),
|
||||||
|
"change" | "change_percent" => self.stocks.sort_by(|a, b| match sort {
|
||||||
|
ColSort::Descending => b
|
||||||
|
.change
|
||||||
|
.partial_cmp(&a.change)
|
||||||
|
.unwrap_or(std::cmp::Ordering::Equal),
|
||||||
|
_ => a.id.cmp(&b.id),
|
||||||
|
}),
|
||||||
_ => {}
|
_ => {}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -652,6 +655,7 @@ impl TableStory {
|
||||||
fn toggle_col_resize(&mut self, checked: &bool, cx: &mut ViewContext<Self>) {
|
fn toggle_col_resize(&mut self, checked: &bool, cx: &mut ViewContext<Self>) {
|
||||||
self.table.update(cx, |table, cx| {
|
self.table.update(cx, |table, cx| {
|
||||||
table.delegate_mut().col_resize = *checked;
|
table.delegate_mut().col_resize = *checked;
|
||||||
|
table.refresh(cx);
|
||||||
cx.notify();
|
cx.notify();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
@ -659,6 +663,7 @@ impl TableStory {
|
||||||
fn toggle_col_order(&mut self, checked: &bool, cx: &mut ViewContext<Self>) {
|
fn toggle_col_order(&mut self, checked: &bool, cx: &mut ViewContext<Self>) {
|
||||||
self.table.update(cx, |table, cx| {
|
self.table.update(cx, |table, cx| {
|
||||||
table.delegate_mut().col_order = *checked;
|
table.delegate_mut().col_order = *checked;
|
||||||
|
table.refresh(cx);
|
||||||
cx.notify();
|
cx.notify();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
@ -666,6 +671,7 @@ impl TableStory {
|
||||||
fn toggle_col_sort(&mut self, checked: &bool, cx: &mut ViewContext<Self>) {
|
fn toggle_col_sort(&mut self, checked: &bool, cx: &mut ViewContext<Self>) {
|
||||||
self.table.update(cx, |table, cx| {
|
self.table.update(cx, |table, cx| {
|
||||||
table.delegate_mut().col_sort = *checked;
|
table.delegate_mut().col_sort = *checked;
|
||||||
|
table.refresh(cx);
|
||||||
cx.notify();
|
cx.notify();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
@ -673,6 +679,7 @@ impl TableStory {
|
||||||
fn toggle_col_selection(&mut self, checked: &bool, cx: &mut ViewContext<Self>) {
|
fn toggle_col_selection(&mut self, checked: &bool, cx: &mut ViewContext<Self>) {
|
||||||
self.table.update(cx, |table, cx| {
|
self.table.update(cx, |table, cx| {
|
||||||
table.delegate_mut().col_selection = *checked;
|
table.delegate_mut().col_selection = *checked;
|
||||||
|
table.refresh(cx);
|
||||||
cx.notify();
|
cx.notify();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -753,9 +753,14 @@ where
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
fn render_cell(&self, col_ix: usize, _cx: &mut ViewContext<Self>) -> Div {
|
fn render_cell(&self, col_ix: usize, _cx: &mut ViewContext<Self>) -> Div {
|
||||||
let col_width = self.col_groups[col_ix].width;
|
let Some(col_group) = self.col_groups.get(col_ix) else {
|
||||||
let col_padding = self.col_groups[col_ix].padding;
|
return div();
|
||||||
|
};
|
||||||
|
|
||||||
|
let col_width = col_group.width;
|
||||||
|
let col_padding = col_group.padding;
|
||||||
|
|
||||||
div()
|
div()
|
||||||
.w(col_width)
|
.w(col_width)
|
||||||
|
|
@ -1037,11 +1042,9 @@ where
|
||||||
// Render left fixed columns
|
// Render left fixed columns
|
||||||
this.child(
|
this.child(
|
||||||
h_flex()
|
h_flex()
|
||||||
.id("table-head-fixed-left")
|
.relative()
|
||||||
.h_full()
|
.h_full()
|
||||||
.bg(cx.theme().table_head)
|
.bg(cx.theme().table_head)
|
||||||
.border_r_1()
|
|
||||||
.border_color(cx.theme().border)
|
|
||||||
.children(
|
.children(
|
||||||
self.col_groups
|
self.col_groups
|
||||||
.iter()
|
.iter()
|
||||||
|
|
@ -1049,6 +1052,18 @@ where
|
||||||
.enumerate()
|
.enumerate()
|
||||||
.map(|(col_ix, _)| self.render_th(col_ix, cx)),
|
.map(|(col_ix, _)| self.render_th(col_ix, cx)),
|
||||||
)
|
)
|
||||||
|
.child(
|
||||||
|
// Fixed columns border
|
||||||
|
div()
|
||||||
|
.absolute()
|
||||||
|
.top_0()
|
||||||
|
.right_0()
|
||||||
|
.bottom_0()
|
||||||
|
.w_0()
|
||||||
|
.flex_shrink_0()
|
||||||
|
.border_r_1()
|
||||||
|
.border_color(cx.theme().border),
|
||||||
|
)
|
||||||
.child(
|
.child(
|
||||||
canvas(
|
canvas(
|
||||||
move |bounds, cx| {
|
move |bounds, cx| {
|
||||||
|
|
@ -1102,6 +1117,7 @@ where
|
||||||
row_ix: usize,
|
row_ix: usize,
|
||||||
rows_count: usize,
|
rows_count: usize,
|
||||||
left_cols_count: usize,
|
left_cols_count: usize,
|
||||||
|
col_sizes: Rc<Vec<gpui::Size<Pixels>>>,
|
||||||
cols_count: usize,
|
cols_count: usize,
|
||||||
cx: &mut ViewContext<Self>,
|
cx: &mut ViewContext<Self>,
|
||||||
) -> impl IntoElement {
|
) -> impl IntoElement {
|
||||||
|
|
@ -1109,13 +1125,6 @@ where
|
||||||
let is_stripe_row = self.stripe && row_ix % 2 != 0;
|
let is_stripe_row = self.stripe && row_ix % 2 != 0;
|
||||||
let is_selected = self.selected_row == Some(row_ix);
|
let is_selected = self.selected_row == Some(row_ix);
|
||||||
let view = cx.view().clone();
|
let view = cx.view().clone();
|
||||||
let col_sizes: Rc<Vec<gpui::Size<Pixels>>> = Rc::new(
|
|
||||||
self.col_groups
|
|
||||||
.iter()
|
|
||||||
.skip(left_cols_count)
|
|
||||||
.map(|col| col.bounds.size)
|
|
||||||
.collect(),
|
|
||||||
);
|
|
||||||
|
|
||||||
if row_ix < rows_count {
|
if row_ix < rows_count {
|
||||||
self.delegate
|
self.delegate
|
||||||
|
|
@ -1135,13 +1144,12 @@ where
|
||||||
this.bg(cx.theme().table_hover)
|
this.bg(cx.theme().table_hover)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
.children(if left_cols_count > 0 {
|
.when(left_cols_count > 0, |this| {
|
||||||
// Left fixed columns
|
// Left fixed columns
|
||||||
Some(
|
this.child(
|
||||||
h_flex()
|
h_flex()
|
||||||
|
.relative()
|
||||||
.h_full()
|
.h_full()
|
||||||
.border_r_1()
|
|
||||||
.border_color(cx.theme().table_row_border)
|
|
||||||
.children({
|
.children({
|
||||||
let mut items = Vec::with_capacity(left_cols_count);
|
let mut items = Vec::with_capacity(left_cols_count);
|
||||||
|
|
||||||
|
|
@ -1155,10 +1163,20 @@ where
|
||||||
});
|
});
|
||||||
|
|
||||||
items
|
items
|
||||||
}),
|
})
|
||||||
|
.child(
|
||||||
|
// Fixed columns border
|
||||||
|
div()
|
||||||
|
.absolute()
|
||||||
|
.top_0()
|
||||||
|
.right_0()
|
||||||
|
.bottom_0()
|
||||||
|
.w_0()
|
||||||
|
.flex_shrink_0()
|
||||||
|
.border_r_1()
|
||||||
|
.border_color(cx.theme().border),
|
||||||
|
),
|
||||||
)
|
)
|
||||||
} else {
|
|
||||||
None
|
|
||||||
})
|
})
|
||||||
.child(
|
.child(
|
||||||
h_flex()
|
h_flex()
|
||||||
|
|
@ -1265,6 +1283,34 @@ where
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Calculate the extra rows needed to fill the table empty space when `stripe` is true.
|
||||||
|
fn calculate_extra_rows_needed(&self, rows_count: usize) -> usize {
|
||||||
|
if !self.stripe {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut extra_rows_needed = 0;
|
||||||
|
|
||||||
|
let row_height = self.size.table_row_height();
|
||||||
|
let total_height = self
|
||||||
|
.vertical_scroll_handle
|
||||||
|
.0
|
||||||
|
.borrow()
|
||||||
|
.base_handle
|
||||||
|
.bounds()
|
||||||
|
.size
|
||||||
|
.height;
|
||||||
|
|
||||||
|
let actual_height = row_height * rows_count as f32;
|
||||||
|
let remaining_height = total_height - actual_height;
|
||||||
|
|
||||||
|
if remaining_height > px(0.) {
|
||||||
|
extra_rows_needed = (remaining_height / row_height).ceil() as usize;
|
||||||
|
}
|
||||||
|
|
||||||
|
extra_rows_needed
|
||||||
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
fn measure_render_td(
|
fn measure_render_td(
|
||||||
&mut self,
|
&mut self,
|
||||||
|
|
@ -1341,35 +1387,7 @@ where
|
||||||
let left_cols_count = self.fixed_cols.left;
|
let left_cols_count = self.fixed_cols.left;
|
||||||
let rows_count = self.delegate.rows_count(cx);
|
let rows_count = self.delegate.rows_count(cx);
|
||||||
let loading = self.delegate.loading(cx);
|
let loading = self.delegate.loading(cx);
|
||||||
|
let extra_rows_needed = self.calculate_extra_rows_needed(rows_count);
|
||||||
let row_height = self
|
|
||||||
.vertical_scroll_handle
|
|
||||||
.0
|
|
||||||
.borrow()
|
|
||||||
.last_item_size
|
|
||||||
.map(|size| size.item.height);
|
|
||||||
let total_height = self
|
|
||||||
.vertical_scroll_handle
|
|
||||||
.0
|
|
||||||
.borrow()
|
|
||||||
.base_handle
|
|
||||||
.bounds()
|
|
||||||
.size
|
|
||||||
.height;
|
|
||||||
|
|
||||||
// Calculate the extra rows needed to fill the table for stripe style.
|
|
||||||
let mut extra_rows_needed = 0;
|
|
||||||
if self.stripe {
|
|
||||||
if let Some(row_height) = row_height {
|
|
||||||
if row_height > px(0.) {
|
|
||||||
let actual_height = row_height * rows_count as f32;
|
|
||||||
let remaining_height = total_height - actual_height;
|
|
||||||
if remaining_height > px(0.) {
|
|
||||||
extra_rows_needed = (remaining_height / row_height).ceil() as usize;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
let inner_table = v_flex()
|
let inner_table = v_flex()
|
||||||
.key_context("Table")
|
.key_context("Table")
|
||||||
|
|
@ -1405,6 +1423,17 @@ where
|
||||||
rows_count + extra_rows_needed,
|
rows_count + extra_rows_needed,
|
||||||
{
|
{
|
||||||
move |table, visible_range, cx| {
|
move |table, visible_range, cx| {
|
||||||
|
// We must calculate the col sizes here, because the col sizes
|
||||||
|
// need render_th first, then that method will set the bounds of each col.
|
||||||
|
let col_sizes: Rc<Vec<gpui::Size<Pixels>>> = Rc::new(
|
||||||
|
table
|
||||||
|
.col_groups
|
||||||
|
.iter()
|
||||||
|
.skip(left_cols_count)
|
||||||
|
.map(|col| col.bounds.size)
|
||||||
|
.collect(),
|
||||||
|
);
|
||||||
|
|
||||||
table.load_more_if_need(rows_count, visible_range.end, cx);
|
table.load_more_if_need(rows_count, visible_range.end, cx);
|
||||||
table.update_visible_range_if_need(
|
table.update_visible_range_if_need(
|
||||||
visible_range.clone(),
|
visible_range.clone(),
|
||||||
|
|
@ -1430,6 +1459,7 @@ where
|
||||||
row_ix,
|
row_ix,
|
||||||
rows_count,
|
rows_count,
|
||||||
left_cols_count,
|
left_cols_count,
|
||||||
|
col_sizes.clone(),
|
||||||
cols_count,
|
cols_count,
|
||||||
cx,
|
cx,
|
||||||
));
|
));
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue