A table with a search box, sortable headers, a pagination footer and row selection, in the
two looks LineOne shows: advanced (the Table Advanced page: a title bar with a search
that slides open, the table as a card, "Show 10 entries" and a pager underneath) and grid
(the Grid Table page: a search box on the right, sort arrows, "Showing 1 to 10 of 15
results" and Previous/Next).
The LiveView owns the state: it filters, sorts and slices the rows and gives the component the current page; the component only renders it and sends events back.
<.data_table
id="users"
rows={@users}
search={@search}
on_search="search"
sort_by={@sort_by}
sort_dir={@sort_dir}
on_sort="sort"
page={@page}
page_size={10}
total={@total}
on_page="paginate"
>
<:title>Users Table</:title>
<:col :let={user} label="Name" sort_key="name">{user.name}</:col>
<:col :let={user} label="Email">{user.email}</:col>
</.data_table>
def handle_event("search", %{"search" => q}, socket), do: ...
def handle_event("sort", %{"column" => col, "direction" => dir}, socket), do: ...
def handle_event("paginate", %{"page" => page}, socket), do: ...Rows may be a stream (rows={@streams.users}); the body then renders with
phx-update="stream" and total drives the footer. :col, :action, :expand,
:empty, row_id, row_key, row_item, row_click and selection work as in
LineaUI.Components.Table.table/1.
Summary
Functions
Renders a data table. The LiveView passes the rows of the current page and handles the events below.
The JS that opens and closes the filter panel of the data table with id (the :filters
slot also receives it).
LineOne's collapsing search: a magnifier button that slides a borderless input open, as in the
title bar of the Table Advanced page. data_table/1 uses it, and it works on its own next to
any title.
Functions
Renders a data table. The LiveView passes the rows of the current page and handles the events below.
<.data_table
id="contacts"
variant="grid"
rows={@rows}
total={@total}
page={@page}
on_page="paginate"
search={@search}
on_search="search"
sort_by={@sort_by}
sort_dir={@sort_dir}
on_sort="sort"
row_key={& &1.id}
selected={@selected}
on_select="select"
on_select_all="select_all"
>
<:col :let={c} label="Name" sort_key="name">{c.name}</:col>
<:col :let={c} label="Email" sort_key="email">{c.email}</:col>
</.data_table>Events, each an event name or a %JS{} (sent to target when given):
on_search— the search input changed or was submitted:%{"search" => query}.on_sort— a sortable header was clicked:%{"column" => sort_key, "direction" => "asc" | "desc"}, the direction to sort by next.on_page— a page was chosen:%{"page" => "3"}(a string, as everyphx-value-*). Withpage_path(a function of the page number) the pager renderspatchlinks instead.on_page_size— the "Show N entries" select changed:%{"page_size" => "30"}.on_select— a row checkbox:%{"id" => key, "selected" => "true" | "false"}.on_select_all— the header checkbox:%{"selected" => "true" | "false"}.
The footer shows when on_page or page_path is given (unless footer={false}); the search
when on_search is.
Attributes
id(:string) (required)rows(:any) (required) - The rows of the current page, a list or a stream.variant(:string) - Defaults to"advanced". Must be one of"advanced", or"grid".row_id(:any) - A function giving each row's DOM id; streams default to the stream id. Defaults tonil.row_key(:any) - A function giving a row's selection key; defaults to its DOM id. Defaults tonil.row_item(:any) - Maps each row before the slots get it. Defaults to&Function.identity/1.row_click(:any) - A function of the row returning aJSor an event name. Defaults tonil.hoverable(:boolean) - Highlights the hovered row; on by default, off for expandable rows. Defaults tonil.search(:string) - The current query. Defaults to"".search_placeholder(:string) - Placeholder of the search input. Defaults tonil.on_search(:any) - See the events above. Defaults tonil.sort_by(:string) - Thesort_keyof the sorted column. Defaults tonil.sort_dir(:string) - Direction ofsort_by. Defaults to"asc". Must be one of"asc", or"desc".on_sort(:any) - See the events above. Defaults tonil.page(:integer) - The current page, from 1. Defaults to1.page_size(:integer) - Rows per page. Defaults to10.total(:integer) - Rows across every page. Defaults to the row count. Defaults tonil.on_page(:any) - See the events above. Defaults tonil.page_path(:any) - A function of the page number returning a path. Defaults tonil.footer(:boolean) -falseleaves out the footer (pager, summary and page-size select) even whenon_pageis given, for a table with only a search. Defaults totrue.compact(:boolean) - Tighter cells, seetable/1. Defaults tofalse.headless(:boolean) - No<thead>, seetable/1. Defaults tofalse.borderless(:boolean) - No row rules, seetable/1. Defaults tofalse.page_sizes(:list) - Options of the "Show N entries" select. Defaults to[].on_page_size(:any) - See the events above. Defaults tonil.pager(:string) - The pager look of theadvancedvariant. Defaults to"rounded". Must be one of"rounded","pill", or"circle".show_label(:string) - Text before the page-size select. Defaults to"Show".entries_label(:string) - Text after the page-size select. Defaults to"entries".prev_label(:string) - Text of thegridpager's first button. Defaults to"Previous".next_label(:string) - Text of thegridpager's last button. Defaults to"Next".selected(:list) - Keys of the selected rows. Defaults to[].all_selected(:boolean) - Whether the header checkbox is checked; computed for lists. Defaults tonil.on_select(:any) - See the events above. Defaults tonil.on_select_all(:any) - See the events above. Defaults tonil.expand_label(:string) - Header of the expand column. Defaults tonil.target(:any) -phx-targetof every event. Defaults tonil.class(:any) - Defaults tonil.- Global attributes are accepted.
Slots
col(required) - A column; receives the row (afterrow_item). Accepts attributes:label(:string) - Header text.class(:any) - Extra classes of the column's cells (the header takeshead_class).head_class(:any) - Extra classes of the column's header.align(:string) - Text alignment of the column's header and cells. Must be one of"left","center", or"right".sort_key(:string) - Makes the header sortable; sent as"column"inon_sort.
title- Theadvancedvariant's heading.actions- Buttons at the right of theadvancedtitle bar.filters- A filter panel theadvancedtitle bar's filter button opens. Receives theJSthat toggles it.summary- Replaces the "1 - 10 of 15 entries" text; receives%{first: n, last: n, total: n}.action- Cells appended to every row.expand- Detail under each row, seeLineaUI.Components.Table.table/1.empty- Shown whenrowsis an empty list.
@spec data_table_toggle_filters(Phoenix.LiveView.JS.t(), String.t()) :: Phoenix.LiveView.JS.t()
The JS that opens and closes the filter panel of the data table with id (the :filters
slot also receives it).
<.button phx-click={data_table_toggle_filters("projects")}>Filters</.button>
LineOne's collapsing search: a magnifier button that slides a borderless input open, as in the
title bar of the Table Advanced page. data_table/1 uses it, and it works on its own next to
any title.
<div class="flex items-center justify-between">
<h2>Users</h2>
<.table_search id="users" value={@search} on_search="search" />
</div>Events: on_search (an event name or %JS{}, sent to target when given) receives
%{"search" => query} on every change (debounced 300ms) and on submit. The input has the
DOM id #{id}-search and the form #{id}-search-form. The input starts open when value
is not empty.
Attributes
id(:string) (required)value(:string) - The current query. Defaults to"".on_search(:any) - Event name or%JS{}for the query changes. Defaults tonil.target(:any) -phx-targetofon_search. Defaults tonil.placeholder(:string) - Placeholder and label; "Search here..." by default. Defaults tonil.class(:any) - Defaults tonil.- Global attributes are accepted.