T14c Tables: DataGrid, Spreadsheet with formulas, merges, frozen panes and fill, TreeTable, PivotTable
This commit is contained in:
14 files changed
+1914
-10
No files matched your search
@@ -0,0 +1,214 @@
|
||||
use std::{collections::HashMap, rc::Rc};
|
||||
|
||||
use ely_gpui_component::{
|
||||
tables::{Aggregate, Cell, Column, DataGrid, PivotTable, Spreadsheet, TreeRow, TreeTable},
|
||||
typography::Caption,
|
||||
};
|
||||
use gpui::{App, IntoElement, ParentElement, SharedString, Styled, Window, div, px, rems};
|
||||
|
||||
use crate::{
|
||||
probe::probe,
|
||||
ui::{keep, section, set},
|
||||
};
|
||||
|
||||
type Cells = HashMap<(usize, usize), SharedString>;
|
||||
|
||||
pub fn data_grid(window: &mut Window, cx: &mut App) -> impl IntoElement + use<> {
|
||||
let kept = keep(
|
||||
"grid-rows",
|
||||
|| {
|
||||
[
|
||||
["Rent", "2400", "2400", "2400"],
|
||||
["Salaries", "18000", "18500", "19000"],
|
||||
["Software", "1200", "1250", "1300"],
|
||||
["Travel", "900", "400", "1600"],
|
||||
]
|
||||
.map(|row| row.map(SharedString::from).to_vec())
|
||||
.to_vec()
|
||||
},
|
||||
window,
|
||||
cx,
|
||||
);
|
||||
let (rows, store) = (kept.read(cx).clone(), kept.clone());
|
||||
section(
|
||||
"DataGrid",
|
||||
"Cells to edit like a spreadsheet's. Arrows walk, Shift stretches a range, typing or a double press edits, Delete clears, and Cmd-C and Cmd-V copy and paste.",
|
||||
cx,
|
||||
)
|
||||
.child(probe(
|
||||
"grid",
|
||||
div().child(
|
||||
DataGrid::new("grid", ["Item", "Q1", "Q2", "Q3"], rows)
|
||||
.on_change(move |edits, _, cx| {
|
||||
let mut next = store.read(cx).clone();
|
||||
for (row, col, text) in edits {
|
||||
next[*row][*col] = text.clone();
|
||||
}
|
||||
set(&store, next, cx)
|
||||
})
|
||||
.w(px(386.))
|
||||
.h(px(150.)),
|
||||
),
|
||||
))
|
||||
}
|
||||
|
||||
fn budget() -> Cells {
|
||||
let mut cells = Cells::new();
|
||||
let mut put = |row: usize, col: usize, text: &str| {
|
||||
cells.insert((row, col), text.to_string().into());
|
||||
};
|
||||
put(0, 0, "Quarterly budget");
|
||||
for (col, title) in ["Item", "Q1", "Q2", "Q3", "Q4", "Total"].iter().enumerate() {
|
||||
put(1, col, title);
|
||||
}
|
||||
let items = [
|
||||
("Rent", 2400.0),
|
||||
("Salaries", 18000.0),
|
||||
("Software", 1200.0),
|
||||
("Travel", 900.0),
|
||||
("Marketing", 3000.0),
|
||||
];
|
||||
for (ix, (item, base)) in items.iter().enumerate() {
|
||||
let row = ix + 2;
|
||||
put(row, 0, item);
|
||||
for quarter in 0..4 {
|
||||
put(
|
||||
row,
|
||||
quarter + 1,
|
||||
&format!("{:.0}", base * (1.0 + 0.04 * quarter as f64)),
|
||||
);
|
||||
}
|
||||
put(row, 5, &format!("=SUM(B{0}:E{0})", row + 1));
|
||||
}
|
||||
put(7, 0, "Total");
|
||||
for col in 1..6 {
|
||||
let letter = (b'A' + col as u8) as char;
|
||||
put(7, col, &format!("=SUM({letter}3:{letter}7)"));
|
||||
}
|
||||
put(8, 0, "Share of rent");
|
||||
put(8, 5, "=F3/F8*100");
|
||||
cells
|
||||
}
|
||||
|
||||
pub fn spreadsheet(window: &mut Window, cx: &mut App) -> impl IntoElement + use<> {
|
||||
let kept = keep("sheet-cells", || Rc::new(budget()), window, cx);
|
||||
let (cells, store) = (kept.read(cx).clone(), kept.clone());
|
||||
section(
|
||||
"Spreadsheet",
|
||||
"Lettered columns and numbered rows. Formulas such as =SUM(B3:B7) recalculate as cells change; the title spans six merged cells; the first two rows and the first column stay put. Drag the dot at the range's corner to fill a series or carry a formula on.",
|
||||
cx,
|
||||
)
|
||||
.child(probe(
|
||||
"sheet",
|
||||
div().child(
|
||||
Spreadsheet::new("sheet", 40, 12)
|
||||
.cells(cells)
|
||||
.merge((0, 0), (0, 5))
|
||||
.freeze(2, 1)
|
||||
.on_change(move |edits, _, cx| {
|
||||
let mut next = (**store.read(cx)).clone();
|
||||
for (row, col, text) in edits {
|
||||
if text.is_empty() {
|
||||
next.remove(&(*row, *col));
|
||||
} else {
|
||||
next.insert((*row, *col), text.clone());
|
||||
}
|
||||
}
|
||||
set(&store, Rc::new(next), cx)
|
||||
})
|
||||
.w(px(640.))
|
||||
.h(px(300.)),
|
||||
),
|
||||
))
|
||||
}
|
||||
|
||||
pub fn tree_table(cx: &mut App) -> impl IntoElement + use<> {
|
||||
let folder = |key: &str, name: &str, size: f64, files: f64, children: Vec<TreeRow>| {
|
||||
TreeRow::new(
|
||||
key.to_string(),
|
||||
[name.to_string().into(), size.into(), files.into()],
|
||||
)
|
||||
.children(children)
|
||||
};
|
||||
let leaf = |key: &str, name: &str, size: f64| {
|
||||
TreeRow::new(
|
||||
key.to_string(),
|
||||
[name.to_string().into(), size.into(), 1.0.into()],
|
||||
)
|
||||
};
|
||||
let rows = [
|
||||
folder(
|
||||
"src",
|
||||
"src",
|
||||
842.0,
|
||||
64.0,
|
||||
vec![
|
||||
folder(
|
||||
"ui",
|
||||
"ui",
|
||||
512.0,
|
||||
38.0,
|
||||
vec![
|
||||
leaf("button", "button.rs", 18.0),
|
||||
leaf("table", "table.rs", 41.0),
|
||||
],
|
||||
),
|
||||
leaf("main", "main.rs", 6.0),
|
||||
],
|
||||
),
|
||||
folder(
|
||||
"assets",
|
||||
"assets",
|
||||
2480.0,
|
||||
12.0,
|
||||
vec![
|
||||
leaf("inter", "Inter.ttf", 804.0),
|
||||
leaf("mono", "JetBrains Mono.ttf", 268.0),
|
||||
],
|
||||
),
|
||||
leaf("cargo", "Cargo.toml", 2.0),
|
||||
];
|
||||
section(
|
||||
"TreeTable",
|
||||
"Rows that open and close under columns; the first column steps in by depth.",
|
||||
cx,
|
||||
)
|
||||
.child(
|
||||
div().w(px(560.)).child(
|
||||
TreeTable::new(
|
||||
"sizes",
|
||||
[
|
||||
Column::new("name", "Name"),
|
||||
Column::new("size", "Size")
|
||||
.end()
|
||||
.suffix(" KB")
|
||||
.width(rems(7.)),
|
||||
Column::new("files", "Files").end().width(rems(5.)),
|
||||
],
|
||||
rows,
|
||||
)
|
||||
.open(["src"]),
|
||||
),
|
||||
)
|
||||
}
|
||||
|
||||
pub fn pivot(cx: &mut App) -> impl IntoElement + use<> {
|
||||
let records: Vec<Vec<Cell>> = [
|
||||
("Europe", "Q1", 120.0),
|
||||
("Europe", "Q2", 132.0),
|
||||
("Europe", "Q3", 128.0),
|
||||
("Americas", "Q1", 180.0),
|
||||
("Americas", "Q2", 171.0),
|
||||
("Americas", "Q3", 199.0),
|
||||
("Asia", "Q1", 90.0),
|
||||
("Asia", "Q2", 118.0),
|
||||
("Asia", "Q3", 141.0),
|
||||
("Europe", "Q3", 22.0),
|
||||
]
|
||||
.into_iter()
|
||||
.map(|(region, quarter, sales)| vec![region.into(), quarter.into(), sales.into()])
|
||||
.collect();
|
||||
section("PivotTable", "Sales cross-tabbed: regions down the side, quarters across, summed where they meet, with totals.", cx)
|
||||
.child(div().w(px(560.)).child(PivotTable::new(["Region", "Quarter", "Sales"], records).pivot("Region", "Quarter", "Sales", Aggregate::Sum)))
|
||||
.child(Caption::new("Sales in thousands."))
|
||||
}
|
||||
@@ -1,5 +1,6 @@
|
||||
mod core;
|
||||
mod features;
|
||||
mod grids;
|
||||
|
||||
use gpui::{AnyElement, App, IntoElement, ParentElement, Window, div};
|
||||
|
||||
@@ -43,6 +44,14 @@ const SCRIPT: &[Step] = &[
|
||||
Step::UpAt("grouped", 60.0, 54.0),
|
||||
Step::Wait(300),
|
||||
Step::Shot("folded"),
|
||||
Step::Hover("sheet"),
|
||||
Step::Wait(300),
|
||||
Step::DownAt("sheet", 184.0, 294.0),
|
||||
Step::UpAt("sheet", 184.0, 294.0),
|
||||
Step::Type("=F8/4"),
|
||||
Step::Key("enter"),
|
||||
Step::Wait(300),
|
||||
Step::Shot("sheet-formula"),
|
||||
Step::Rest,
|
||||
];
|
||||
|
||||
@@ -53,6 +62,10 @@ fn render(window: &mut Window, cx: &mut App) -> AnyElement {
|
||||
.child(features::wide(cx))
|
||||
.child(features::rows(window, cx))
|
||||
.child(features::grouped(cx))
|
||||
.child(grids::data_grid(window, cx))
|
||||
.child(grids::spreadsheet(window, cx))
|
||||
.child(grids::tree_table(cx))
|
||||
.child(grids::pivot(cx))
|
||||
.child(core::long(window, cx))
|
||||
.child(core::heatmap(cx))
|
||||
.into_any_element()
|
||||
|
||||
Reference in new issue
Block a user