日本語

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

data_table.rs
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.

KeyWhat it does
SpaceToggles the focused checkbox. On the header checkbox, selects every row, or clears them once all are selected.
Enter, SpaceOn 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

ItemSummary
DataTable::newAn empty table named label, listing rows by key, with its state in state.
columnAdds column after the columns added so far.
sortable_columnAdds column, whose header sorts the rows by key, after the columns added so far.
selectableAdds a first column of checkboxes that keep the key of each selected row in selected.
expandableAdds a first column of buttons that show details of a row under it, keeping the key of each open row in expanded.
filterAdds a field named label that shows the rows whose text contains its text.
paginateShows size rows at a time, with buttons for the previous and next page.
max_heightKeeps the table at most height high, scrolling its rows under a header that stays in place.
Column::textA column headed header that shows the text field field of each row.
Column::numberA column headed header that shows the number field field, aligned to the end.
Column::formattedA column headed header that shows the number field field written in format.
Column::customA column headed header whose cells hold what render returns for each row.
visibleShows the column only while when is true.
TableStateThe signals a DataTable or ServerTable keeps: filter text, page, and sort.
TableTextThe words a DataTable or ServerTable shows or names its controls with.
ServerTable::newAn empty table named label, listing rows by key, which runs load to fetch rows.
ServerTable::sortable_columnAdds column, whose header asks the server to sort by key, after the columns added so far.
ServerTable::selectableAdds a first column of checkboxes that keep the key of each selected row in selected.
ServerTable::filterAdds a field named label whose text the server filters the rows by.
ServerTable::paginateShows pages of size rows out of total, with buttons for the previous and next page.
TableState::sortingThe key of the column a ServerTable is sorted by, and its order.