Catalog Tables

<.table>

ConnieUI.Tables

Documents

Title & creator Signer Status
Director Agreement — L. Hansen
Sofie Lindqvist
Lars Hansen
Feature Film — "Northern Lights"
Signed
Talent Release — M. Olsen
Sofie Lindqvist
Maja Olsen
Carlsberg — Summer Campaign
Pending
Location Permit — Nyhavn
Sofie Lindqvist
Peter Krogh
Documentary — "The Sound"
Pending
Composer Agreement — A. Riis
Sofie Lindqvist
Astrid Riis
Documentary — "The Sound"
Signed
NDA — Post-production House
Sofie Lindqvist
Thomas Vinter
Feature Film — "Northern Lights"
Expired
Crew Contract — Gaffer
Sofie Lindqvist
Sofie Lindqvist
Carlsberg — Summer Campaign
Draft

6 documents

HEEx
<div class="w-4xl">
  <ConnieUI.Tables.table id="catalog-table-documents" rows={@contracts} row_click={&JS.push("open", value: %{id: &1.id})}>
    <:header>
      <ConnieUI.Layouts.hdist>
        <:col>
          <ConnieUI.Typography.heading level="h2">Documents</ConnieUI.Typography.heading>
        </:col>
        <:col>
          <ConnieUI.Buttons.button color="secondary" icon_start="icon-folder-download">
            Export
          </ConnieUI.Buttons.button>
        </:col>
      </ConnieUI.Layouts.hdist>
    </:header>
    <:col :let={contract} label="Title & creator" class="max-w-0 w-[40%]">
      <.title_cell contract={contract} />
    </:col>
    <:col :let={contract} label="Signer" class="max-w-0 w-[35%]">
      <.signer_cell contract={contract} />
    </:col>
    <:col :let={contract} label="Status">
      <ConnieUI.Badges.contract_status_badge status={contract.status} />
    </:col>
    <:col :let={_contract} no_click class="w-18">
      <ConnieUI.Buttons.mini icon="icon-dots-vertical" aria_label="More" click={%JS{}} />
    </:col>
    <:footer>
      <ConnieUI.Typography.muted>6 documents</ConnieUI.Typography.muted>
    </:footer>
  </ConnieUI.Tables.table>
</div>

Variant matrix

Every combination of one or two attributes, side by side.

Controls

:string
:string

table density

use fixed table layout

enable horizontal scroll on overflow

Fill the height the caller gives the table, and scroll the rows inside it. The table is a flex column, the rows are the part that stretches, and the header row stays put as they scroll. Nothing happens on its own: the caller has to hand it a height (a flex chain down from something like `h-dvh`), and without one the rows are as tall as they always were. A filling table marks its root with `data-table-fill`, so a page shell can bound itself to the window only while one is on screen (a `has-[[data-table-fill]]:` variant) rather than being told by every page that renders one. What this is for is the horizontal scrollbar. A wide table on a long page puts it below the last row, which is off screen, so the reader never learns there is more table to the right. Bounding the height puts the scrollbar at the bottom of the screen, where it is visible from the first row on. Two details come with the sticky header. Borders switch to `border-separate`, because collapsed borders belong to the table rather than to the cells and a sticky row scrolls out from under its own bottom border; with no spacing the line is drawn in the same place, on the cell, where it travels with it. And the header cells go to `z-2`, so they cover the rows passing under them, and carry the header's background themselves: `thead` keeps its own, but a section's background stays where the section sits in the table and does not travel with a cell that has come unstuck from it. A frozen column is the one thing that needs to know about this. Its cells are already `z-2` to cover the columns passing beside them, and the cell that is both wants to be above the rest of the header as well as above its own column, so give it a `header_class` of `z-3`. It is set on the cell rather than worked out here because only the caller knows which column is frozen.

Set by the example

rows row_id row_click row_item row_class columns

Docs

No @doc for this component.

Attribute Type Default Description
id :string
rows * :list
row_id :any nil
the function for generating the row id
row_click :any nil
the function for handling phx-click on each row
row_item :any &Function.identity/1
the function for mapping each row before calling the :col slot
size :string "md"
table density sm | md
fixed :boolean false
use fixed table layout
scroll :boolean true
enable horizontal scroll on overflow
fill :boolean false
Fill the height the caller gives the table, and scroll the rows inside it. The table is a flex column, the rows are the part that stretches, and the header row stays put as they scroll. Nothing happens on its own: the caller has to hand it a height (a flex chain down from something like `h-dvh`), and without one the rows are as tall as they always were. A filling table marks its root with `data-table-fill`, so a page shell can bound itself to the window only while one is on screen (a `has-[[data-table-fill]]:` variant) rather than being told by every page that renders one. What this is for is the horizontal scrollbar. A wide table on a long page puts it below the last row, which is off screen, so the reader never learns there is more table to the right. Bounding the height puts the scrollbar at the bottom of the screen, where it is visible from the first row on. Two details come with the sticky header. Borders switch to `border-separate`, because collapsed borders belong to the table rather than to the cells and a sticky row scrolls out from under its own bottom border; with no spacing the line is drawn in the same place, on the cell, where it travels with it. And the header cells go to `z-2`, so they cover the rows passing under them, and carry the header's background themselves: `thead` keeps its own, but a section's background stays where the section sits in the table and does not travel with a cell that has come unstuck from it. A frozen column is the one thing that needs to know about this. Its cells are already `z-2` to cover the columns passing beside them, and the cell that is both wants to be above the rest of the header as well as above its own column, so give it a `header_class` of `z-3`. It is set on the cell rather than worked out here because only the caller knows which column is frozen.
row_class :any nil
classes for the whole `<tr>`, either a value or a 1-arity function of the row. The `:col` slot's `row_class` styles one `<td>`; this styles the row.
columns :any nil
The `:col` keys to show **and the order to show them in**, as a list from `ConnieUI.Columns.visible/1`. A keyed column outside it is left out of the header and every row alike; the ones in it are rendered in this sequence rather than in the order they are declared. `nil`, the default, shows every column in declaration order, so a table that has no column picker passes nothing and behaves as it always did.
Slot Slot attributes Description
:header
:footer
:list_end Content after the last row, inside the rows' scroll area, so it scrolls into view with the end of the list rather than staying put like `:footer`. It is where `ConnieUI.Pagination`'s `load_more` goes.
:empty
:group A group's row, for a `ConnieUI.Pagination.Group` marker among the rows (see `ConnieUI.Pagination`'s `group`). `:let` gives the marker. It is one `<tr>` whose single `<th scope="rowgroup">` spans every shown column, the frozen selection column included: the group labels the rows under it as a whole, and a box there would read as selecting the group. Its content stays at the left edge as the columns scroll sideways under it, so keep it to the group's name and what goes beside it (a menu, a link). It is not clickable through `row_click`, has no selection box, and scrolls with the rows rather than sticking under the header when the table fills. Every group but the first opens with a thick divider in the table's tertiary background, so each group reads as a block of its own rather than as one more row. The first sits right under the column headers, which already separate it. Without this slot a marker is a hidden row, so a table that is not grouped can be handed a grouped stream.
:col * header_class, row_class, class, cell_class, label, header, no_click, header_col, key