Skip to main content

DataTable

Struct DataTable 

Source
pub struct DataTable<S, T> { /* private fields */ }
Expand description

A table of the rows of a list signal, sorted, filtered, and paged in the browser.

Each row is keyed by key, as in each_keyed, so a row keeps its element, and its checkbox its focus, when sorting moves it. Everything the table does runs in the browser on the list signal and the TableState:

  • A column added with DataTable::sortable_column has a button in its header that sorts the rows, and the header’s aria-sort says which way.
  • DataTable::filter adds a text field and shows the rows whose text contains what the visitor types, ignoring case.
  • DataTable::paginate shows a fixed number of the rows that pass the filter, with buttons for the previous and the next page.
  • DataTable::selectable adds a column of checkboxes that keep the keys of the selected rows in a list signal, under a header checkbox that selects or clears all of them. The header follows the WAI-ARIA checkbox pattern with aria-checked="mixed" while some rows are selected.

A page past the last row, which the list reaches by shrinking from outside the table, returns the table to its first page.

The header checkbox selects every row of the list, including rows the filter or the page hides. A selected key stays in the list after its row leaves the table, until the header clears the selection or the page removes the key. Rows are counted on each change of the list, the filter, or the page, and the work grows with the square of the number of rows while a filter and pagination are both on, so a list of thousands of rows belongs in a ServerTable, which receives one page at a time from the server.

use hypersig::{Page, Signals};
use hypersig_ui::{Column, DataTable, TableState};
use serde::{Deserialize, Serialize};

#[derive(Signals, Serialize, Deserialize)]
struct User {
    id: i32,
    email: String,
}

#[derive(Signals, Serialize, Deserialize)]
struct Users {
    users: Vec<User>,
    chosen: Vec<i32>,
    table: TableState,
}

let table = DataTable::new("Users", Users::table(), Users::users(), |user| user.at(User::id()))
    .selectable(Users::chosen(), |user| user.at(User::id()))
    .filter("Filter emails", |user| user.at(User::email()))
    .paginate(10)
    .sortable_column(Column::text("Email", User::email()), |user| user.at(User::email()))
    .column(Column::number("Id", User::id()));
let users = vec![User { id: 1, email: "ada@example.com".into() }];
let html = Page::new("Users", Users { users, chosen: vec![1], table: TableState::default() })
    .child(table)
    .render();
assert!(html.contains(r#"aria-sort="none""#));
assert!(html.contains(r#"aria-checked="true""#));

Implementations§

Source§

impl<S: 'static, T: 'static> DataTable<S, T>

Source

pub fn new<K: 'static, E: IntoExpr<S, K>>( label: impl Into<String>, state: Sig<S, TableState>, rows: Sig<S, Vec<T>>, key: impl FnOnce(Item<S, T>) -> E + 'static, ) -> Self

An empty table named label, listing rows by key, with its state in state.

The name is for assistive technology, and the table has no columns until DataTable::column adds them.

Source

pub fn column(self, column: Column<S, T>) -> Self

Adds column after the columns added so far.

Source

pub fn sortable_column<K: Comparable, E: IntoExpr<S, K>>( self, column: Column<S, T>, key: impl Fn(Item<S, T>) -> E + 'static, ) -> Self

Adds column, whose header sorts the rows by key, after the columns added so far.

The compiler requires the key to be a number or a string, because the runtime orders only those. The first press on the header sorts the rows ascending, and a press on a column sorted ascending sorts it descending. Sorting reorders the list signal itself and keeps the order of rows with equal keys, so sorting by one column and then by another orders the rows by the second and then the first. The order holds until the list changes. A list the server patches afterwards arrives in the server’s order, while the header still marks the last sort.

Source

pub fn selectable<K: PartialEq + 'static, E: IntoExpr<S, K>>( self, selected: Sig<S, Vec<K>>, key: impl Fn(Item<S, T>) -> E + 'static, ) -> Self

Adds a first column of checkboxes that keep the key of each selected row in selected.

The keys live in a signal of the page, apart from the rows, so a selection needs no field in the row type and outlasts a patch of the rows. The keys are compared as JSON, as each_keyed compares them.

Source

pub fn expandable<K: PartialEq + 'static, E: IntoExpr<S, K>>( self, expanded: Sig<S, Vec<K>>, key: impl Fn(Item<S, T>) -> E + 'static, details: impl Fn(Item<S, T>) -> El<S> + 'static, ) -> Self

Adds a first column of buttons that show details of a row under it, keeping the key of each open row in expanded.

The button carries aria-expanded, as the WAI-ARIA disclosure pattern asks, and the details fill a row of their own under the row, across every column. Each row and its details are a <tbody> of their own, so the two move together when the rows are sorted.

Source

pub fn filter<E: IntoExpr<S, String>>( self, label: impl Into<String>, text: impl Fn(Item<S, T>) -> E + 'static, ) -> Self

Adds a field named label that shows the rows whose text contains its text.

The field sits above the table. The match ignores case, and typing returns the table to its first page.

Source

pub fn paginate(self, size: i32) -> Self

Shows size rows at a time, with buttons for the previous and next page.

Below the table, the range of rows shown is written as 1–5 / 12.

§Panics

Panics if size is less than 1.

Source

pub fn text(self, text: TableText) -> Self

Replaces the words the table shows and names its controls with, English until set.

Source

pub fn max_height(self, height: Length) -> Self

Keeps the table at most height high, scrolling its rows under a header that stays in place.

Source

pub fn render(self) -> El<S>

The filter field, the table, and the page buttons, in a <div>.

Trait Implementations§

Source§

impl<S, T> Debug for DataTable<S, T>

Source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
Source§

impl<S: 'static, T: 'static> From<DataTable<S, T>> for El<S>

Source§

fn from(table: DataTable<S, T>) -> Self

Converts to this type from the input type.
Source§

impl<S: 'static, T: 'static> From<DataTable<S, T>> for Node<S>

Source§

fn from(table: DataTable<S, T>) -> Self

Converts to this type from the input type.

Auto Trait Implementations§

§

impl<S, T> !RefUnwindSafe for DataTable<S, T>

§

impl<S, T> !Send for DataTable<S, T>

§

impl<S, T> !Sync for DataTable<S, T>

§

impl<S, T> !UnwindSafe for DataTable<S, T>

§

impl<S, T> Freeze for DataTable<S, T>

§

impl<S, T> Unpin for DataTable<S, T>

§

impl<S, T> UnsafeUnpin for DataTable<S, T>

Blanket Implementations§

Source§

impl<T> Any for T
where T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

Source§

impl<T, U> Into<U> for T
where U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

That is, this conversion is whatever the implementation of From<T> for U chooses to do.

Source§

impl<T, U> TryFrom<U> for T
where U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.