Data Table

A table built from rows of data and its ui-column children. Each column is a real header cell, and the table renders a row per item with one cell per column, so it reads as a table before JavaScript and to a screen reader. The header stays in view while the rows scroll; frozen on a column and frozen-rows on the table keep columns and leading rows in view too. Give the table a bounded height to scroll its rows. For a table written by hand, use Table.

Orders table

A captioned table with a column per field and figures set along the end of each row.

Orders
Customer Items Total
Ada Park 3 $124.00
Ben Ortiz 1 $18.50
Chen Wu 5 $302.10

Code

<link
  rel="component"
  href="@threadlabs/looma/components/ui-data-table/ui-data-table.html"
>
<link
  rel="component"
  href="@threadlabs/looma/components/ui-column/ui-column.html"
>

<ui-data-table
  caption="Orders"
  rows="[{ id: 1, customer: 'Ada Park', items: 3, total: '$124.00' }, { id: 2, customer: 'Ben Ortiz', items: 1, total: '$18.50' }, { id: 3, customer: 'Chen Wu', items: 5, total: '$302.10' }]"
>
  <ui-column
    field="customer"
    header="Customer"
  ></ui-column>
  <ui-column
    field="items"
    header="Items"
    align="end"
  ></ui-column>
  <ui-column
    field="total"
    header="Total"
    align="end"
  ></ui-column>
</ui-data-table>
<script setup>
  import { UiColumn, UiDataTable } from "@threadlabs/looma";
</script>

<template>
  <UiDataTable
    caption="Orders"
    :rows="[{ id: 1, customer: 'Ada Park', items: 3, total: '$124.00' }, { id: 2, customer: 'Ben Ortiz', items: 1, total: '$18.50' }, { id: 3, customer: 'Chen Wu', items: 5, total: '$302.10' }]"
  >
    <UiColumn
      field="customer"
      header="Customer"
    />
    <UiColumn
      field="items"
      header="Items"
      align="end"
    />
    <UiColumn
      field="total"
      header="Total"
      align="end"
    />
  </UiDataTable>
</template>
import { UiColumn, UiDataTable } from "@threadlabs/looma";

export default function Example() {
  return <UiDataTable
    caption="Orders"
    rows={[{"id":1,"customer":"Ada Park","items":3,"total":"$124.00"},{"id":2,"customer":"Ben Ortiz","items":1,"total":"$18.50"},{"id":3,"customer":"Chen Wu","items":5,"total":"$302.10"}]}
  >
    <UiColumn
      field="customer"
      header="Customer"
    />
    <UiColumn
      field="items"
      header="Items"
      align="end"
    />
    <UiColumn
      field="total"
      header="Total"
      align="end"
    />
  </UiDataTable>;
}
/** @jsxImportSource preact */
import { UiColumn, UiDataTable } from "@threadlabs/looma";

export default function Example() {
  return <UiDataTable
    caption="Orders"
    rows={[{"id":1,"customer":"Ada Park","items":3,"total":"$124.00"},{"id":2,"customer":"Ben Ortiz","items":1,"total":"$18.50"},{"id":3,"customer":"Chen Wu","items":5,"total":"$302.10"}]}
  >
    <UiColumn
      field="customer"
      header="Customer"
    />
    <UiColumn
      field="items"
      header="Items"
      align="end"
    />
    <UiColumn
      field="total"
      header="Total"
      align="end"
    />
  </UiDataTable>;
}
<script>
  import { UiColumn, UiDataTable } from "@threadlabs/looma";
</script>

<UiDataTable
  caption="Orders"
  rows={[{"id":1,"customer":"Ada Park","items":3,"total":"$124.00"},{"id":2,"customer":"Ben Ortiz","items":1,"total":"$18.50"},{"id":3,"customer":"Chen Wu","items":5,"total":"$302.10"}]}
>
  <UiColumn
    field="customer"
    header="Customer"
  />
  <UiColumn
    field="items"
    header="Items"
    align="end"
  />
  <UiColumn
    field="total"
    header="Total"
    align="end"
  />
</UiDataTable>
import { Component } from "@angular/core";
import { UiColumn, UiDataTable } from "@threadlabs/looma";

@Component({
  selector: "example-view",
  standalone: true,
  imports: [UiDataTable, UiColumn],
  template: `
    <div
      ui-data-table
      [caption]="'Orders'"
      [rows]="[{ id: 1, customer: 'Ada Park', items: 3, total: '$124.00' }, { id: 2, customer: 'Ben Ortiz', items: 1, total: '$18.50' }, { id: 3, customer: 'Chen Wu', items: 5, total: '$302.10' }]"
    >
      <th
        ui-column
        [field]="'customer'"
        [header]="'Customer'"
      ></th>
      <th
        ui-column
        [field]="'items'"
        [header]="'Items'"
        [align]="'end'"
      ></th>
      <th
        ui-column
        [field]="'total'"
        [header]="'Total'"
        [align]="'end'"
      ></th>
    </div>
  `,
})
export default class Example {}

Columns as markup

Each ui-column names the row field it shows and its header. align="end" sets a column of figures along the end of the row.

Orders
Customer Items Total
Ada Park 3 $124.00
Ben Ortiz 1 $18.50
Chen Wu 5 $302.10

Code

<link
  rel="component"
  href="@threadlabs/looma/components/ui-data-table/ui-data-table.html"
>
<link
  rel="component"
  href="@threadlabs/looma/components/ui-column/ui-column.html"
>

<ui-data-table
  caption="Orders"
  rows="[{ id: 1, customer: 'Ada Park', items: 3, total: '$124.00' }, { id: 2, customer: 'Ben Ortiz', items: 1, total: '$18.50' }, { id: 3, customer: 'Chen Wu', items: 5, total: '$302.10' }]"
>
  <ui-column
    field="customer"
    header="Customer"
  ></ui-column>
  <ui-column
    field="items"
    header="Items"
    align="end"
  ></ui-column>
  <ui-column
    field="total"
    header="Total"
    align="end"
  ></ui-column>
</ui-data-table>
<script setup>
  import { UiColumn, UiDataTable } from "@threadlabs/looma";
</script>

<template>
  <UiDataTable
    caption="Orders"
    :rows="[{ id: 1, customer: 'Ada Park', items: 3, total: '$124.00' }, { id: 2, customer: 'Ben Ortiz', items: 1, total: '$18.50' }, { id: 3, customer: 'Chen Wu', items: 5, total: '$302.10' }]"
  >
    <UiColumn
      field="customer"
      header="Customer"
    />
    <UiColumn
      field="items"
      header="Items"
      align="end"
    />
    <UiColumn
      field="total"
      header="Total"
      align="end"
    />
  </UiDataTable>
</template>
import { UiColumn, UiDataTable } from "@threadlabs/looma";

export default function Example() {
  return <UiDataTable
    caption="Orders"
    rows={[{"id":1,"customer":"Ada Park","items":3,"total":"$124.00"},{"id":2,"customer":"Ben Ortiz","items":1,"total":"$18.50"},{"id":3,"customer":"Chen Wu","items":5,"total":"$302.10"}]}
  >
    <UiColumn
      field="customer"
      header="Customer"
    />
    <UiColumn
      field="items"
      header="Items"
      align="end"
    />
    <UiColumn
      field="total"
      header="Total"
      align="end"
    />
  </UiDataTable>;
}
/** @jsxImportSource preact */
import { UiColumn, UiDataTable } from "@threadlabs/looma";

export default function Example() {
  return <UiDataTable
    caption="Orders"
    rows={[{"id":1,"customer":"Ada Park","items":3,"total":"$124.00"},{"id":2,"customer":"Ben Ortiz","items":1,"total":"$18.50"},{"id":3,"customer":"Chen Wu","items":5,"total":"$302.10"}]}
  >
    <UiColumn
      field="customer"
      header="Customer"
    />
    <UiColumn
      field="items"
      header="Items"
      align="end"
    />
    <UiColumn
      field="total"
      header="Total"
      align="end"
    />
  </UiDataTable>;
}
<script>
  import { UiColumn, UiDataTable } from "@threadlabs/looma";
</script>

<UiDataTable
  caption="Orders"
  rows={[{"id":1,"customer":"Ada Park","items":3,"total":"$124.00"},{"id":2,"customer":"Ben Ortiz","items":1,"total":"$18.50"},{"id":3,"customer":"Chen Wu","items":5,"total":"$302.10"}]}
>
  <UiColumn
    field="customer"
    header="Customer"
  />
  <UiColumn
    field="items"
    header="Items"
    align="end"
  />
  <UiColumn
    field="total"
    header="Total"
    align="end"
  />
</UiDataTable>
import { Component } from "@angular/core";
import { UiColumn, UiDataTable } from "@threadlabs/looma";

@Component({
  selector: "example-view",
  standalone: true,
  imports: [UiDataTable, UiColumn],
  template: `
    <div
      ui-data-table
      [caption]="'Orders'"
      [rows]="[{ id: 1, customer: 'Ada Park', items: 3, total: '$124.00' }, { id: 2, customer: 'Ben Ortiz', items: 1, total: '$18.50' }, { id: 3, customer: 'Chen Wu', items: 5, total: '$302.10' }]"
    >
      <th
        ui-column
        [field]="'customer'"
        [header]="'Customer'"
      ></th>
      <th
        ui-column
        [field]="'items'"
        [header]="'Items'"
        [align]="'end'"
      ></th>
      <th
        ui-column
        [field]="'total'"
        [header]="'Total'"
        [align]="'end'"
      ></th>
    </div>
  `,
})
export default class Example {}

Cell content

A column's cell template renders once per row and reads the row as $row. A header slot replaces the header text with markup.

Team
Name Role Active
Ada Park Design Active
Ben Ortiz Engineering Away

Code

<link
  rel="component"
  href="@threadlabs/looma/components/ui-data-table/ui-data-table.html"
>
<link
  rel="component"
  href="@threadlabs/looma/components/ui-column/ui-column.html"
>
<link
  rel="component"
  href="@threadlabs/looma/components/ui-badge/ui-badge.html"
>

<ui-data-table
  caption="Team"
  rows="[{ id: 1, name: 'Ada Park', role: 'Design', active: true }, { id: 2, name: 'Ben Ortiz', role: 'Engineering', active: false }]"
>
  <ui-column
    field="name"
    header="Name"
  ></ui-column>
  <ui-column
    field="role"
    header="Role"
  ></ui-column>
  <ui-column>
    <abbr
      slot="header"
      title="Signed in this week"
    >Active</abbr>
    <template
      slot="cell"
    >
      <ui-badge
        from:tone="$row.active ? 'success' : 'neutral'"
      >{$row.active ? 'Active' : 'Away'}</ui-badge>
    </template>
  </ui-column>
</ui-data-table>
<script setup>
  import { UiBadge, UiColumn, UiDataTable } from "@threadlabs/looma";
</script>

<template>
  <UiDataTable
    caption="Team"
    :rows="[{ id: 1, name: 'Ada Park', role: 'Design', active: true }, { id: 2, name: 'Ben Ortiz', role: 'Engineering', active: false }]"
  >
    <UiColumn
      field="name"
      header="Name"
    />
    <UiColumn
      field="role"
      header="Role"
    />
    <UiColumn>
      <template #header>
        <abbr
          title="Signed in this week"
        >Active</abbr>
      </template>
      <template #cell>
        <UiBadge
          from:tone="$row.active ? 'success' : 'neutral'"
        >&#123;$row.active ? 'Active' : 'Away'}</UiBadge>
      </template>
    </UiColumn>
  </UiDataTable>
</template>
import { UiBadge, UiColumn, UiDataTable } from "@threadlabs/looma";

export default function Example() {
  return <UiDataTable
    caption="Team"
    rows={[{"id":1,"name":"Ada Park","role":"Design","active":true},{"id":2,"name":"Ben Ortiz","role":"Engineering","active":false}]}
  >
    <UiColumn
      field="name"
      header="Name"
    />
    <UiColumn
      field="role"
      header="Role"
    />
    <UiColumn
      slots={{
        header: <abbr
          title="Signed in this week"
        >Active</abbr>,
        cell: <UiBadge
          from:tone="$row.active ? 'success' : 'neutral'"
        >{"{"}$row.active ? 'Active' : 'Away'{"}"}</UiBadge>,
      }}
    >{" "}{" "}{" "}</UiColumn>
  </UiDataTable>;
}
/** @jsxImportSource preact */
import { UiBadge, UiColumn, UiDataTable } from "@threadlabs/looma";

export default function Example() {
  return <UiDataTable
    caption="Team"
    rows={[{"id":1,"name":"Ada Park","role":"Design","active":true},{"id":2,"name":"Ben Ortiz","role":"Engineering","active":false}]}
  >
    <UiColumn
      field="name"
      header="Name"
    />
    <UiColumn
      field="role"
      header="Role"
    />
    <UiColumn
      slots={{
        header: <abbr
          title="Signed in this week"
        >Active</abbr>,
        cell: <UiBadge
          from:tone="$row.active ? 'success' : 'neutral'"
        >{"{"}$row.active ? 'Active' : 'Away'{"}"}</UiBadge>,
      }}
    >{" "}{" "}{" "}</UiColumn>
  </UiDataTable>;
}
<script>
  import { UiBadge, UiColumn, UiDataTable } from "@threadlabs/looma";
</script>

{#snippet header()}
  <abbr
    title="Signed in this week"
  >Active</abbr>
{/snippet}

{#snippet cell()}
  <UiBadge
    from:tone="$row.active ? 'success' : 'neutral'"
  >{'{'}$row.active ? 'Active' : 'Away'}</UiBadge>
{/snippet}

<UiDataTable
  caption="Team"
  rows={[{"id":1,"name":"Ada Park","role":"Design","active":true},{"id":2,"name":"Ben Ortiz","role":"Engineering","active":false}]}
>
  <UiColumn
    field="name"
    header="Name"
  />
  <UiColumn
    field="role"
    header="Role"
  />
  <UiColumn
    slots={{ header, cell }}
  >   </UiColumn>
</UiDataTable>
import { Component } from "@angular/core";
import { UiBadge, UiColumn, UiDataTable } from "@threadlabs/looma";

@Component({
  selector: "example-view",
  standalone: true,
  imports: [UiDataTable, UiColumn, UiBadge],
  template: `
    <div
      ui-data-table
      [caption]="'Team'"
      [rows]="[{ id: 1, name: 'Ada Park', role: 'Design', active: true }, { id: 2, name: 'Ben Ortiz', role: 'Engineering', active: false }]"
    >
      <th
        ui-column
        [field]="'name'"
        [header]="'Name'"
      ></th>
      <th
        ui-column
        [field]="'role'"
        [header]="'Role'"
      ></th>
      <th
        ui-column
      >
        <ng-container ngProjectAs="[slot='header']">
          <abbr
            title="Signed in this week"
          >Active</abbr>
        </ng-container>
        <ng-container ngProjectAs="[slot='cell']">
          <span
            ui-badge
            from:tone="$row.active ? 'success' : 'neutral'"
          >&#123;$row.active ? 'Active' : 'Away'&#125;</span>
        </ng-container>
      </th>
    </div>
  `,
})
export default class Example {}

Frozen columns and rows

The header stays in view while the rows scroll. frozen="start" or frozen="end" keeps a column in view while the table scrolls sideways, and frozen-rows keeps leading rows beneath the header. A Stack with a height bounds the table here, so its rows scroll.

Regional sales
Region First quarter Second quarter Third quarter Fourth quarter Total
All regions 4210 4630 4980 5410 19230
North 1020 1130 1210 1340 4700
South 980 1040 1150 1260 4430
East 1110 1250 1320 1430 5110
West 1100 1210 1300 1380 4990
Central 640 700 760 820 2920
Coastal 590 650 700 760 2700

Code

<link
  rel="component"
  href="@threadlabs/looma/components/ui-stack/ui-stack.html"
>
<link
  rel="component"
  href="@threadlabs/looma/components/ui-data-table/ui-data-table.html"
>
<link
  rel="component"
  href="@threadlabs/looma/components/ui-column/ui-column.html"
>

<ui-stack
  height="224"
>
  <ui-data-table
    caption="Regional sales"
    frozen-rows="1"
    rows="[{ id: 0, region: 'All regions', q1: 4210, q2: 4630, q3: 4980, q4: 5410, total: 19230 }, { id: 1, region: 'North', q1: 1020, q2: 1130, q3: 1210, q4: 1340, total: 4700 }, { id: 2, region: 'South', q1: 980, q2: 1040, q3: 1150, q4: 1260, total: 4430 }, { id: 3, region: 'East', q1: 1110, q2: 1250, q3: 1320, q4: 1430, total: 5110 }, { id: 4, region: 'West', q1: 1100, q2: 1210, q3: 1300, q4: 1380, total: 4990 }, { id: 5, region: 'Central', q1: 640, q2: 700, q3: 760, q4: 820, total: 2920 }, { id: 6, region: 'Coastal', q1: 590, q2: 650, q3: 700, q4: 760, total: 2700 }]"
  >
    <ui-column
      field="region"
      header="Region"
      frozen="start"
      width="9rem"
    ></ui-column>
    <ui-column
      field="q1"
      header="First quarter"
      align="end"
      width="10rem"
    ></ui-column>
    <ui-column
      field="q2"
      header="Second quarter"
      align="end"
      width="10rem"
    ></ui-column>
    <ui-column
      field="q3"
      header="Third quarter"
      align="end"
      width="10rem"
    ></ui-column>
    <ui-column
      field="q4"
      header="Fourth quarter"
      align="end"
      width="10rem"
    ></ui-column>
    <ui-column
      field="total"
      header="Total"
      align="end"
      frozen="end"
      width="7rem"
    ></ui-column>
  </ui-data-table>
</ui-stack>
<script setup>
  import { UiColumn, UiDataTable, UiStack } from "@threadlabs/looma";
</script>

<template>
  <UiStack
    :height="224"
  >
    <UiDataTable
      caption="Regional sales"
      :frozen-rows="1"
      :rows="[{ id: 0, region: 'All regions', q1: 4210, q2: 4630, q3: 4980, q4: 5410, total: 19230 }, { id: 1, region: 'North', q1: 1020, q2: 1130, q3: 1210, q4: 1340, total: 4700 }, { id: 2, region: 'South', q1: 980, q2: 1040, q3: 1150, q4: 1260, total: 4430 }, { id: 3, region: 'East', q1: 1110, q2: 1250, q3: 1320, q4: 1430, total: 5110 }, { id: 4, region: 'West', q1: 1100, q2: 1210, q3: 1300, q4: 1380, total: 4990 }, { id: 5, region: 'Central', q1: 640, q2: 700, q3: 760, q4: 820, total: 2920 }, { id: 6, region: 'Coastal', q1: 590, q2: 650, q3: 700, q4: 760, total: 2700 }]"
    >
      <UiColumn
        field="region"
        header="Region"
        frozen="start"
        width="9rem"
      />
      <UiColumn
        field="q1"
        header="First quarter"
        align="end"
        width="10rem"
      />
      <UiColumn
        field="q2"
        header="Second quarter"
        align="end"
        width="10rem"
      />
      <UiColumn
        field="q3"
        header="Third quarter"
        align="end"
        width="10rem"
      />
      <UiColumn
        field="q4"
        header="Fourth quarter"
        align="end"
        width="10rem"
      />
      <UiColumn
        field="total"
        header="Total"
        align="end"
        frozen="end"
        width="7rem"
      />
    </UiDataTable>
  </UiStack>
</template>
import { UiColumn, UiDataTable, UiStack } from "@threadlabs/looma";

export default function Example() {
  return <UiStack
    height={224}
  >
    <UiDataTable
      caption="Regional sales"
      frozenRows={1}
      rows={[{"id":0,"region":"All regions","q1":4210,"q2":4630,"q3":4980,"q4":5410,"total":19230},{"id":1,"region":"North","q1":1020,"q2":1130,"q3":1210,"q4":1340,"total":4700},{"id":2,"region":"South","q1":980,"q2":1040,"q3":1150,"q4":1260,"total":4430},{"id":3,"region":"East","q1":1110,"q2":1250,"q3":1320,"q4":1430,"total":5110},{"id":4,"region":"West","q1":1100,"q2":1210,"q3":1300,"q4":1380,"total":4990},{"id":5,"region":"Central","q1":640,"q2":700,"q3":760,"q4":820,"total":2920},{"id":6,"region":"Coastal","q1":590,"q2":650,"q3":700,"q4":760,"total":2700}]}
    >
      <UiColumn
        field="region"
        header="Region"
        frozen="start"
        width="9rem"
      />
      <UiColumn
        field="q1"
        header="First quarter"
        align="end"
        width="10rem"
      />
      <UiColumn
        field="q2"
        header="Second quarter"
        align="end"
        width="10rem"
      />
      <UiColumn
        field="q3"
        header="Third quarter"
        align="end"
        width="10rem"
      />
      <UiColumn
        field="q4"
        header="Fourth quarter"
        align="end"
        width="10rem"
      />
      <UiColumn
        field="total"
        header="Total"
        align="end"
        frozen="end"
        width="7rem"
      />
    </UiDataTable>
  </UiStack>;
}
/** @jsxImportSource preact */
import { UiColumn, UiDataTable, UiStack } from "@threadlabs/looma";

export default function Example() {
  return <UiStack
    height={224}
  >
    <UiDataTable
      caption="Regional sales"
      frozenRows={1}
      rows={[{"id":0,"region":"All regions","q1":4210,"q2":4630,"q3":4980,"q4":5410,"total":19230},{"id":1,"region":"North","q1":1020,"q2":1130,"q3":1210,"q4":1340,"total":4700},{"id":2,"region":"South","q1":980,"q2":1040,"q3":1150,"q4":1260,"total":4430},{"id":3,"region":"East","q1":1110,"q2":1250,"q3":1320,"q4":1430,"total":5110},{"id":4,"region":"West","q1":1100,"q2":1210,"q3":1300,"q4":1380,"total":4990},{"id":5,"region":"Central","q1":640,"q2":700,"q3":760,"q4":820,"total":2920},{"id":6,"region":"Coastal","q1":590,"q2":650,"q3":700,"q4":760,"total":2700}]}
    >
      <UiColumn
        field="region"
        header="Region"
        frozen="start"
        width="9rem"
      />
      <UiColumn
        field="q1"
        header="First quarter"
        align="end"
        width="10rem"
      />
      <UiColumn
        field="q2"
        header="Second quarter"
        align="end"
        width="10rem"
      />
      <UiColumn
        field="q3"
        header="Third quarter"
        align="end"
        width="10rem"
      />
      <UiColumn
        field="q4"
        header="Fourth quarter"
        align="end"
        width="10rem"
      />
      <UiColumn
        field="total"
        header="Total"
        align="end"
        frozen="end"
        width="7rem"
      />
    </UiDataTable>
  </UiStack>;
}
<script>
  import { UiColumn, UiDataTable, UiStack } from "@threadlabs/looma";
</script>

<UiStack
  height={224}
>
  <UiDataTable
    caption="Regional sales"
    frozenRows={1}
    rows={[{"id":0,"region":"All regions","q1":4210,"q2":4630,"q3":4980,"q4":5410,"total":19230},{"id":1,"region":"North","q1":1020,"q2":1130,"q3":1210,"q4":1340,"total":4700},{"id":2,"region":"South","q1":980,"q2":1040,"q3":1150,"q4":1260,"total":4430},{"id":3,"region":"East","q1":1110,"q2":1250,"q3":1320,"q4":1430,"total":5110},{"id":4,"region":"West","q1":1100,"q2":1210,"q3":1300,"q4":1380,"total":4990},{"id":5,"region":"Central","q1":640,"q2":700,"q3":760,"q4":820,"total":2920},{"id":6,"region":"Coastal","q1":590,"q2":650,"q3":700,"q4":760,"total":2700}]}
  >
    <UiColumn
      field="region"
      header="Region"
      frozen="start"
      width="9rem"
    />
    <UiColumn
      field="q1"
      header="First quarter"
      align="end"
      width="10rem"
    />
    <UiColumn
      field="q2"
      header="Second quarter"
      align="end"
      width="10rem"
    />
    <UiColumn
      field="q3"
      header="Third quarter"
      align="end"
      width="10rem"
    />
    <UiColumn
      field="q4"
      header="Fourth quarter"
      align="end"
      width="10rem"
    />
    <UiColumn
      field="total"
      header="Total"
      align="end"
      frozen="end"
      width="7rem"
    />
  </UiDataTable>
</UiStack>
import { Component } from "@angular/core";
import { UiColumn, UiDataTable, UiStack } from "@threadlabs/looma";

@Component({
  selector: "example-view",
  standalone: true,
  imports: [UiStack, UiDataTable, UiColumn],
  template: `
    <div
      ui-stack
      [height]="224"
    >
      <div
        ui-data-table
        [caption]="'Regional sales'"
        [frozenRows]="1"
        [rows]="[{ id: 0, region: 'All regions', q1: 4210, q2: 4630, q3: 4980, q4: 5410, total: 19230 }, { id: 1, region: 'North', q1: 1020, q2: 1130, q3: 1210, q4: 1340, total: 4700 }, { id: 2, region: 'South', q1: 980, q2: 1040, q3: 1150, q4: 1260, total: 4430 }, { id: 3, region: 'East', q1: 1110, q2: 1250, q3: 1320, q4: 1430, total: 5110 }, { id: 4, region: 'West', q1: 1100, q2: 1210, q3: 1300, q4: 1380, total: 4990 }, { id: 5, region: 'Central', q1: 640, q2: 700, q3: 760, q4: 820, total: 2920 }, { id: 6, region: 'Coastal', q1: 590, q2: 650, q3: 700, q4: 760, total: 2700 }]"
      >
        <th
          ui-column
          [field]="'region'"
          [header]="'Region'"
          [frozen]="'start'"
          [width]="'9rem'"
        ></th>
        <th
          ui-column
          [field]="'q1'"
          [header]="'First quarter'"
          [align]="'end'"
          [width]="'10rem'"
        ></th>
        <th
          ui-column
          [field]="'q2'"
          [header]="'Second quarter'"
          [align]="'end'"
          [width]="'10rem'"
        ></th>
        <th
          ui-column
          [field]="'q3'"
          [header]="'Third quarter'"
          [align]="'end'"
          [width]="'10rem'"
        ></th>
        <th
          ui-column
          [field]="'q4'"
          [header]="'Fourth quarter'"
          [align]="'end'"
          [width]="'10rem'"
        ></th>
        <th
          ui-column
          [field]="'total'"
          [header]="'Total'"
          [align]="'end'"
          [frozen]="'end'"
          [width]="'7rem'"
        ></th>
      </div>
    </div>
  `,
})
export default class Example {}

Field, header and cell content

A column shows its field of each row under its header, or its cell template, which reads the row as $row.

Tasks
Task Hours Status
Draft the brief 3 Done
Review the layout 1 Open

Code

<link
  rel="component"
  href="@threadlabs/looma/components/ui-data-table/ui-data-table.html"
>
<link
  rel="component"
  href="@threadlabs/looma/components/ui-column/ui-column.html"
>
<link
  rel="component"
  href="@threadlabs/looma/components/ui-badge/ui-badge.html"
>

<ui-data-table
  caption="Tasks"
  rows="[{ id: 1, task: 'Draft the brief', hours: 3, done: true }, { id: 2, task: 'Review the layout', hours: 1, done: false }]"
>
  <ui-column
    field="task"
    header="Task"
  ></ui-column>
  <ui-column
    field="hours"
    header="Hours"
    align="end"
  ></ui-column>
  <ui-column
    header="Status"
  >
    <template
      slot="cell"
    >
      <ui-badge
        from:tone="$row.done ? 'success' : 'neutral'"
      >{$row.done ? 'Done' : 'Open'}</ui-badge>
    </template>
  </ui-column>
</ui-data-table>
<script setup>
  import { UiBadge, UiColumn, UiDataTable } from "@threadlabs/looma";
</script>

<template>
  <UiDataTable
    caption="Tasks"
    :rows="[{ id: 1, task: 'Draft the brief', hours: 3, done: true }, { id: 2, task: 'Review the layout', hours: 1, done: false }]"
  >
    <UiColumn
      field="task"
      header="Task"
    />
    <UiColumn
      field="hours"
      header="Hours"
      align="end"
    />
    <UiColumn
      header="Status"
    >
      <template #cell>
        <UiBadge
          from:tone="$row.done ? 'success' : 'neutral'"
        >&#123;$row.done ? 'Done' : 'Open'}</UiBadge>
      </template>
    </UiColumn>
  </UiDataTable>
</template>
import { UiBadge, UiColumn, UiDataTable } from "@threadlabs/looma";

export default function Example() {
  return <UiDataTable
    caption="Tasks"
    rows={[{"id":1,"task":"Draft the brief","hours":3,"done":true},{"id":2,"task":"Review the layout","hours":1,"done":false}]}
  >
    <UiColumn
      field="task"
      header="Task"
    />
    <UiColumn
      field="hours"
      header="Hours"
      align="end"
    />
    <UiColumn
      header="Status"
      slots={{
        cell: <UiBadge
          from:tone="$row.done ? 'success' : 'neutral'"
        >{"{"}$row.done ? 'Done' : 'Open'{"}"}</UiBadge>,
      }}
    >{" "}{" "}</UiColumn>
  </UiDataTable>;
}
/** @jsxImportSource preact */
import { UiBadge, UiColumn, UiDataTable } from "@threadlabs/looma";

export default function Example() {
  return <UiDataTable
    caption="Tasks"
    rows={[{"id":1,"task":"Draft the brief","hours":3,"done":true},{"id":2,"task":"Review the layout","hours":1,"done":false}]}
  >
    <UiColumn
      field="task"
      header="Task"
    />
    <UiColumn
      field="hours"
      header="Hours"
      align="end"
    />
    <UiColumn
      header="Status"
      slots={{
        cell: <UiBadge
          from:tone="$row.done ? 'success' : 'neutral'"
        >{"{"}$row.done ? 'Done' : 'Open'{"}"}</UiBadge>,
      }}
    >{" "}{" "}</UiColumn>
  </UiDataTable>;
}
<script>
  import { UiBadge, UiColumn, UiDataTable } from "@threadlabs/looma";
</script>

{#snippet cell()}
  <UiBadge
    from:tone="$row.done ? 'success' : 'neutral'"
  >{'{'}$row.done ? 'Done' : 'Open'}</UiBadge>
{/snippet}

<UiDataTable
  caption="Tasks"
  rows={[{"id":1,"task":"Draft the brief","hours":3,"done":true},{"id":2,"task":"Review the layout","hours":1,"done":false}]}
>
  <UiColumn
    field="task"
    header="Task"
  />
  <UiColumn
    field="hours"
    header="Hours"
    align="end"
  />
  <UiColumn
    header="Status"
    slots={{ cell }}
  >  </UiColumn>
</UiDataTable>
import { Component } from "@angular/core";
import { UiBadge, UiColumn, UiDataTable } from "@threadlabs/looma";

@Component({
  selector: "example-view",
  standalone: true,
  imports: [UiDataTable, UiColumn, UiBadge],
  template: `
    <div
      ui-data-table
      [caption]="'Tasks'"
      [rows]="[{ id: 1, task: 'Draft the brief', hours: 3, done: true }, { id: 2, task: 'Review the layout', hours: 1, done: false }]"
    >
      <th
        ui-column
        [field]="'task'"
        [header]="'Task'"
      ></th>
      <th
        ui-column
        [field]="'hours'"
        [header]="'Hours'"
        [align]="'end'"
      ></th>
      <th
        ui-column
        [header]="'Status'"
      >
        <ng-container ngProjectAs="[slot='cell']">
          <span
            ui-badge
            from:tone="$row.done ? 'success' : 'neutral'"
          >&#123;$row.done ? 'Done' : 'Open'&#125;</span>
        </ng-container>
      </th>
    </div>
  `,
})
export default class Example {}

API

Package: @threadlabs/looma · Native root: div

Attributes

NameDescriptionPropertyTypeDefaultAllowed values
captionNames the table. A screen reader announces it with the table, and it names the scrolling region.captionstring——
densityHow tall each row is. comfortable fits a control of the standard height; compact fits a small one, for long tables to scan.densitykeywordcomfortablecomfortable, compact
frozen-rowsHow many leading rows stay in view beneath the header while the rows scroll. Frozen rows suit totals or a pinned item.frozenRowsinteger0—
row-keyThe row field that identifies each row, so a row keeps its cells, and the state of any control in them, when the rows change order.rowKeystringid—
rowsOne entry per table row. Each column shows its field of the row, or its own cell content.rowslist(object)[]—

Framework props

NameTypeDefaultAllowed values
captionstring——
density"comfortable" | "compact"comfortablecomfortable, compact
frozenRowsnumber0—
rowKeystringid—
rowsreadonly ({ readonly [name: string]: unknown })[][]—

Events

No events.

Slots

NamePurpose
defaultDefault child content.

Design tokens

Set shared theme properties on a theme boundary. Component hooks apply only to their owning component.

Component tokens

No component tokens.

Shared tokens

TokenFallback
--ui-border-divider—
--ui-border-subtle—
--ui-border-width—
--ui-control-size—
--ui-control-size-sm—
--ui-focus-ring—
--ui-focus-width—
--ui-font-medium—
--ui-font-size-sm—
--ui-line-height—
--ui-space-0-5—
--ui-space-1—
--ui-space-2—
--ui-space-3—
--ui-surface—
--ui-text-primary—
--ui-text-secondary—

Column API

One column of a ui-data-table. Its root is the column's header cell. The table shows the column's field of each row, or the column's cell content, which reads the row as $row.

Package: @threadlabs/looma · Native root: th

Attributes

NameDescriptionPropertyTypeDefaultAllowed values
alignWhere the header and every cell set their content along the row, such as end for a column of figures.alignkeywordstartstart, center, end
fieldThe row field each cell shows when the column gives no cell content.fieldstring——
frozenKeeps the column in view while the table scrolls sideways. start holds it at the inline start edge, beside any frozen columns before it; end holds it at the inline end edge, beside any frozen columns after it.frozenkeywordnonenone, start, end
headerThe header text. Content in the header slot replaces it, for markup such as an abbreviation.headerstring——
widthThe column's width as a CSS length, such as 12rem. The column sizes to its content when it is not set.widthstring——

Framework props

NameTypeDefaultAllowed values
align"start" | "center" | "end"startstart, center, end
fieldstring——
frozen"none" | "start" | "end"nonenone, start, end
headerstring——
widthstring——

Events

No events.

Slots

NamePurpose
headerNamed content for the header region.
cellNamed content for the cell region.

Design tokens

Set shared theme properties on a theme boundary. Component hooks apply only to their owning component.

Component tokens

No component tokens.

Shared tokens

TokenFallback
--ui-border-divider—
--ui-border-width—
--ui-font-medium—
--ui-space-2—
--ui-space-3—
--ui-surface—
--ui-text-muted—