The admin data table — one Flop-driven table to replace the per-app
admin_table copies. API deliberately mirrors the enventory/skusync
baseline so swaps are mechanical.
<.data_table id="orders" rows={@orders} meta={@meta} path={~p"/orders"}>
<:col label="Order" field={:reference} sortable :let={o}>{o.reference}</:col>
<:col label="Total" field={:total} sortable td_class="lui-td-num" :let={o}>
{o.total}
</:col>
<:bulk_action label="Delete" icon="trash" event="bulk-delete" />
<:row_action :let={o}>…</:row_action>
<:empty>No orders yet.</:empty>
</.data_table>metais aFlop.Meta(duck-typed — any map withflop,params,current_page,total_pages,page_size,total_countworks), so lantern_ui carries no flop dependency.- Sorting, pagination, and page size are patch navigation against
path— table state lives in the URL. Existing query params (meta.params, e.g. filters) are preserved. - Selection is server-owned: rows emit
toggle_select(phx-value-id), the header checkbox emitsselect_all_page, the bulk bar emitsclear_selectionand eachbulk_action'sevent— all totarget(defaults to the parent LiveView). Same contract as the baseline.
Search, filter bar, tabs, and the stat overview arrive as chrome slots in a follow-up; this is the core.
Summary
Functions
The aria-sort value for a sortable column: "ascending", "descending", or
"none".
Functions
Attributes
id(:string) (required) - Stable DOM id for the table root and chrome.rows(:list) (required) - Current page of row structs/maps to render.meta(:map) (required) - Flop.Meta (or same-shaped map).path(:string) (required) - base path for sort/pagination patches.selected_ids(:any) - MapSet (or enumerable) of selected row ids. Defaults toMapSet.new([]).row_id(:any) - row -> id fn; defaults to & &1.id. Defaults tonil.show_checkboxes(:boolean) - Show row selection checkboxes and bulk bar. Defaults totrue.target(:any) - LiveView target for selection and bulk events. Defaults tonil.title(:string) - Optional table heading above the toolbar. Defaults tonil.subtitle(:string) - Secondary line under the table title. Defaults tonil.info_modal_id(:string) - modal id the title's info button opens (LanternUI.open_dialog). Defaults tonil.page_size_options(:list) - Choices for the page-size control. Defaults to[10, 25, 50, 100].search_field(:atom) - Flop filter field the built-in search box binds to. Defaults tonil.search_op(:string) - Flop op for the search filter. Defaults to"ilike".search_placeholder(:string) - Placeholder for the toolbar search input. Defaults to"Search…".view(:string) - active view when a :card or :list_item slot is given. The switcher always offers exactly two, never three:list+cardson a page with a :list_item slot, andtable+cardson one without (otherwise the grid would be a dead end).tableis also the rendering used when a page supplies neither slot. Defaults to"table". Must be one of"table","cards", or"list".flush(:boolean) - Drop the panel chrome — border, radius, shadow, raised surface — and pull the header, toolbar, cells and pagination out to the page gutter, so the table reads as the page rather than as a card sitting on it. This is what the list and card views already do for themselves;flushis how a table view asks for it. Use it when the table IS the page. Defaults tofalse.class(:any) - Extra classes merged onto the root element. Defaults tonil.fill(:boolean) - Stretch to the parent's height (the parent must be a flex column with a definite height), scroll the table body, and pin the header/overview/pagination. Without it the table is its natural content height. Defaults tofalse.- Global attributes are accepted. Arbitrary HTML/
phx-*attributes passed through.
Slots
header_action- Actions rendered in the title row (right side).toolbar- Extra controls in the filter/search toolbar.col(required) - One data column; body is rendered per row via :let. Accepts attributes:label(:string) - Header label for the column.field(:atom) - Flop sort field when sortable.sortable(:boolean) - Enable sort patch links on the header.class(:any) - Extra classes on the header cell.td_class(:any) - Extra classes on body cells for this column.
bulk_action- Action shown in the bulk bar when rows are selected. Accepts attributes:label(:string) - Button label for the bulk action.icon(:string) - Optional leading icon name.event(:string) - phx-click event name fired to target.color(:string) - Button color token (e.g. danger).
row_action- Per-row trailing actions cell; receives the row via :let.empty- Empty-state content when there are no rows.stat- Overview metric card above the table. Accepts attributes:label(:string) - Stat caption under or beside the value.value(:any) - Primary metric value to display.icon(:string) - Optional heroicon name shown top-right of the label row.subtitle(:string) - Optional muted caption under the value.href(:string) - Optional link target wrapping the stat.class(:any) - Extra classes on this stat card.
tab- Filter preset tab in the table chrome. Accepts attributes:label(:string) - Tab label text.count(:integer) - Optional badge count beside the label.filters(:list) - Flop filter preset, e.g. [%{field: "status", value: "pending"}]; omit for the unfiltered tab.
filter- Toolbar filter control bound to a Flop field. Accepts attributes:field(:atom) - Flop filter field atom.label(:string) - Accessible/visible label for the control.type(:atom) - :select (default) | :text | :range.op(:string) - Flop filter op (e.g. ==, ilike); defaults by type.options(:list) - Select options as values or {label, value} tuples.prompt(:string) - Blank/prompt option label for select filters.placeholder(:string) - Placeholder for text filters.multiple(:boolean) - multi-select filter (Flop op: in).searchable(:boolean) - search box in the filter's listbox.
card- per-row card rendering for the cards view; enables the view toggle.list_item- per-row simple list rendering; enables the list view.
The aria-sort value for a sortable column: "ascending", "descending", or
"none".
ARIA requires this on the header cell (the <th>), not on the inner sort
link, and only on columns that are actually sortable — "none" means "sortable
but not the current sort key", which is a different claim from a plain column
with no sort affordance at all.