Data table
A table of the rows of a list signal under typed columns, which sorts, filters, pages, and selects rows in the browser, or asks the server for one page at a time.
Result
use hypersig::{NumberFormat, Page, Signals};
use hypersig_ui::{Column, DataTable, TableState, TableText};
use serde::{Deserialize, Serialize};
#[derive(Signals, Serialize, Deserialize)]
pub struct Payment {
id: i32,
email: String,
amount: f64,
}
#[derive(Signals, Serialize, Deserialize)]
pub struct Ledger {
payments: Vec<Payment>,
chosen: Vec<i32>,
table: TableState,
}
pub fn page() -> Page<Ledger> {
let payments = [
("ken99@example.com", 316.0),
("abe45@example.com", 242.0),
("monserrat44@example.com", 837.0),
("silas22@example.com", 874.0),
("carmella@example.com", 721.0),
("jason78@example.com", 450.0),
("sarah23@example.com", 120.5),
]
.into_iter()
.zip(1..)
.map(|((email, amount), id)| Payment {
id,
email: email.to_owned(),
amount,
})
.collect();
let table = DataTable::new(
"Payments",
Ledger::table(),
Ledger::payments(),
|payment| payment.at(Payment::id()),
)
.selectable(Ledger::chosen(), |payment| payment.at(Payment::id()))
.filter(
"Filter emails",
|payment| payment.at(Payment::email()),
)
.paginate(5)
.text(TableText {
empty: "No results."
.to_owned(),
select_row: "Select row"
.to_owned(),
select_all: "Select all rows"
.to_owned(),
previous_page: "Previous page"
.to_owned(),
next_page: "Next page"
.to_owned(),
expand_row: "Show row details"
.to_owned(),
})
.sortable_column(
Column::text(
"Email",
Payment::email(),
),
|payment| payment.at(Payment::email()),
)
.sortable_column(
Column::formatted(
"Amount",
Payment::amount(),
NumberFormat::new().digits(2).prefix("$"),
),
|payment| payment.at(Payment::amount()),
);
Page::new(
"Payments",
Ledger {
payments,
chosen: Vec::new(),
table: TableState::default(),
},
)
.child(table)
}Accessibility
It is a <table> whose headers have scope="col". A sortable column's header holds a button and carries aria-sort, which says whether the rows are sorted ascending, descending, or not by it. The header checkbox is a button with role="checkbox" whose aria-checked is mixed while some rows are selected, since a native checkbox's indeterminate state is a property no attribute sets.
A column is built from a field of the row type, so a field of another struct does not compile, and the key that sortable_column sorts by must be a number or a string. The table keeps its filter text, page, and sort column in a TableState field of the page's signals, and sorting reorders the list signal itself. The selection is a list of the selected rows' keys in another signal of the page, so the row type holds no field for it and a ServerTable keeps it across pages.
The browser counts the rows on each change, and with a filter and pagination both on, the work grows with the square of the number of rows. A list of thousands of rows belongs in a ServerTable. Its filter field, sortable headers, and page buttons write the TableState and then call an action with it, and the action answers with a patch of the page's rows and the total. A sortable column takes an enum with #[derive(Segment)] as its key, and the server reads it back from the state with TableState::sorting. The demo below runs in the browser alone, since this site has no server, and examples/ui has a ServerTable beside its DataTable.
To keep a ServerTable's state in the URL, give the page a route whose search parameters are the application's own struct with #[derive(Search)] and Option fields, so the address without a query string still loads. The page handler builds the TableState from it with TableState::with_sorting, and the action answers with Patches::replace_url of the URL its state names, so a reload or a shared link shows the same page of rows. TableState itself is not a search struct, because a URL without one of its fields would get a 400.
| Key | What it does |
|---|---|
| Space | Toggles the focused checkbox. On the header checkbox, selects every row, or clears them once all are selected. |
| Enter, Space | On a column header, sorts the rows by that column. On the button at the start of a row, shows or hides the row's details. |
API
| Item | Summary |
|---|---|
DataTable::new | An empty table named label, listing rows by key, with its state in state. |
column | Adds column after the columns added so far. |
sortable_column | Adds column, whose header sorts the rows by key, after the columns added so far. |
selectable | Adds a first column of checkboxes that keep the key of each selected row in selected. |
expandable | Adds a first column of buttons that show details of a row under it, keeping the key of each open row in expanded. |
filter | Adds a field named label that shows the rows whose text contains its text. |
paginate | Shows size rows at a time, with buttons for the previous and next page. |
max_height | Keeps the table at most height high, scrolling its rows under a header that stays in place. |
Column::text | A column headed header that shows the text field field of each row. |
Column::number | A column headed header that shows the number field field, aligned to the end. |
Column::formatted | A column headed header that shows the number field field written in format. |
Column::custom | A column headed header whose cells hold what render returns for each row. |
visible | Shows the column only while when is true. |
TableState | The signals a DataTable or ServerTable keeps: filter text, page, and sort. |
TableText | The words a DataTable or ServerTable shows or names its controls with. |
ServerTable::new | An empty table named label, listing rows by key, which runs load to fetch rows. |
ServerTable::sortable_column | Adds column, whose header asks the server to sort by key, after the columns added so far. |
ServerTable::selectable | Adds a first column of checkboxes that keep the key of each selected row in selected. |
ServerTable::filter | Adds a field named label whose text the server filters the rows by. |
ServerTable::paginate | Shows pages of size rows out of total, with buttons for the previous and next page. |
TableState::sorting | The key of the column a ServerTable is sorted by, and its order. |