Varendo

Table

Native table first; separate interactive-grid mode; sorting, paging, selection, virtualization and precise values.

FamilydataLicenseApache-2.0
StandaloneAvailable

Use the web element directly without the semantic runtime.

Semantic bindingAvailable

A registered presentation adapter can bind this component to host-owned meaning and state.

Automatic adaptationStandard recipe

A standard recipe can select this component when its task, bindings, data, and policy are eligible.

Import and live example

A captioned people table with identity selection, sortable columns, and a bounded page.

Interactive preview requires JavaScript.

View the complete example source
import {registerAeliqoElements} from "@varendo/web";
import {
  AeliqoCardCollectionElement,
  AeliqoDeltaElement,
  AeliqoDetailElement,
  AeliqoFilterBuilderElement,
  AeliqoKeyValueElement,
  AeliqoMetricElement,
  AeliqoRecordListElement,
  AeliqoSelectionSummaryElement,
  AeliqoTableElement,
  type AeliqoKeyValueItem,
  type AeliqoTableColumn,
  type AeliqoTableRow,
  } from "@varendo/web/data";

import type {ResultRef,
  Scalar,
} from "@varendo/core";
import type {AeliqoDataColumn, AeliqoDataScope, AeliqoFieldOption} from "@varendo/web/data";

const catalogRef: ResultRef = {
  id: "aeliqo-catalog-example",
  revision: "1",
  outputId: "people",
  queryDigest: "catalog-query",
  scopeDigest: "catalog-scope",
};
const catalogRows: readonly Readonly<Record<string, Scalar>>[] = [
  {id: "ada", name: "Ada Lovelace", team: "Research", date: "2026-09-08", amount: 120},
  {id: "lin", name: "Lin Chen", team: "Product", date: "2026-09-09", amount: 96},
  {id: "grace", name: "Grace Hopper", team: "Research", date: "2026-09-10", amount: 144},
];
const catalogColumns: readonly AeliqoDataColumn[] = [
  {key: "id", label: "ID", type: "text", sortable: true},
  {key: "name", label: "Name", type: "text", sortable: true},
  {key: "team", label: "Team", type: "text"},
  {key: "date", label: "Date", type: "date", sortable: true},
  {key: "amount", label: "Amount", type: "integer", align: "end"},
];
const catalogFields: readonly AeliqoFieldOption[] = [
  {id: "name", label: "Name", type: "text"},
  {id: "team", label: "Team", type: "text"},
  {id: "amount", label: "Amount", type: "integer"},
];
const catalogScope: AeliqoDataScope = {
  loaded: catalogRows.length,
  filteredTotal: catalogRows.length,
  populationDigest: catalogRef.scopeDigest,
  kind: "filtered",
  label: "Authorized people",
};
const tableColumns: readonly AeliqoTableColumn[] = catalogColumns.map(({key, label, sortable, align}) => ({key, label, ...(sortable === undefined ? {} : {sortable}), ...(align === undefined ? {} : {align})}));
const tableRows: readonly AeliqoTableRow[] = catalogRows.map((row) => ({id: row.id ?? null, name: row.name ?? null, team: row.team ?? null, date: row.date ?? null, amount: row.amount ?? null}));
const personKeys = ["string:3:ada", "string:5:grace"] as const;

registerAeliqoElements();

const host = document.querySelector<HTMLElement>("#aeliqo-example") ?? (() => {
  const created = document.createElement("div");
  created.id = "aeliqo-example";
  (document.body ?? document.documentElement).append(created);
  return created;
})();

function createCatalogRoot(container: HTMLElement): HTMLElement {
  const root = document.createElement("div");
  root.dataset.catalogSourceRoot = "true";
  root.style.display = "grid";
  root.style.gap = "12px";
  container.append(root);
  return root;
}

function createCatalogElement<T extends HTMLElement>(tagName: string, container: HTMLElement): T {
  const element = document.createElement(tagName) as T;
  element.setAttribute("data-aeliqo-theme", "light");
  container.append(element);
  return element;
}

function appendSlottedText(root: HTMLElement, slot: string, textContent: string): void {
  const content = document.createElement("span");
  content.slot = slot;
  content.textContent = textContent;
  root.append(content);
}

const root = createCatalogRoot(host);
(((root) => {
		const element = createCatalogElement<AeliqoTableElement>("aeliqo-table", root);
		element.caption = "People";
		element.columns = tableColumns;
		element.rows = tableRows;
		element.identity = ["id"];
		element.entity = "person";
		element.selection = "multiple";
		element.selectedKeys = [personKeys[0]];
		element.result = catalogRef;
		element.page = 1;
		element.pageSize = 10;
		element.totalRows = catalogRows.length;
		element.scope = catalogScope;
	}))(root);

Purpose

Show rows and columns in a native table, with an opt-in grid mode for cell navigation. Sorting, paging, and selection are requests the application answers.

When to use it

When to use a different component

Properties and defaults

Table remains native by default; mode and virtualization are explicit host choices with stable row identity.

Properties, ownership, and initial values
PropertyUse and ownershipDeclared typeInitial value
columnsDefines ordered visible fields, labels, formatting, alignment, and supported column interactions.readonly AeliqoTableColumn[][]
rowsSupplies the bounded records to render. The component never fetches or widens this set.readonly AeliqoTableRow[][]
captionProvides the visible table caption and accessible name for the data relationship.string''
emptyLabelProvides the text shown when no value or row is available.string'No rows to display.'
entityNames the catalog entity whose records and identities are displayed.string'row'
identityLists stable identity fields used for selection, detail targeting, and state transfer.readonly string[][]
selectionChooses none, single, or multiple selection behavior.AeliqoTableSelectionMode'none'
selectedKeysControls the current collection selection using stable encoded identities.readonly string[][]
resultSupplies the Result reference that binds displayed data to its query and scope evidence.ResultRef | undefinedundefined
modeSupplies mode to the component as application-owned input.AeliqoTableMode'table'
sortSupplies the current table sort so controls reflect authoritative host state.AeliqoSortState | undefinedundefined
pageControls the current one-based page.number1
pageSizeSets the number of rows represented by one table page.number0
totalRowsProvides the total known row count used for paging and scope disclosure.number | undefinedundefined
virtualizedEnables explicit windowed table rendering for large bounded results.booleanfalse
virtualStartSets the first row index represented by the supplied virtual window.number0
virtualCountSets the total virtual row count represented by the current window.number40
overscanSets the extra virtualized rows rendered outside the visible window.number4
scopeSupplies the visible authorization and population scope associated with the displayed value or rows.AeliqoDataScope | undefinedundefined
statusSupplies the explicit ready, loading, empty, partial, stale, or error state.AeliqoDataStatus'ready'
messageProvides the user-facing status, error, or recovery message.string''

Events

Events, payloads, timing, and application responsibilities
EventPayloadEmittedApplication responsibility
aeliqo-table-selectionStable selected identity keys plus result lineage.After table selection changes.Validate scope and update controlled selectedKeys.
aeliqo-table-sortThe requested column and direction.After a sortable column header is activated.Apply sorting in the data owner, then update rows and controlled sort.
aeliqo-table-pageThe requested page and page size.After table paging controls are activated.Fetch or select that page, then update rows, Result evidence, and page.
aeliqo-table-windowThe requested virtual start and count.When scrolling requires another virtual row window.Load the bounded window and update rows, virtualStart, and virtualCount.

Events cross the component boundary as user proposals. Validate their detail and current authority before applying a business effect.

Listen for aeliqo-table-selection

TypeScript example
const element = document.querySelector('aeliqo-table');<p>element?.addEventListener('aeliqo-table-selection', (event) => {
  const proposal = (event as CustomEvent<unknown>).detail;
  // Validate the proposal, update host state, then assign controlled properties.
  applyProposal(proposal);
});

Listen for aeliqo-table-sort

TypeScript example
const element = document.querySelector('aeliqo-table');</p>
<p>element?.addEventListener('aeliqo-table-sort', (event) => {
  const proposal = (event as CustomEvent<unknown>).detail;
  // Validate the proposal, update host state, then assign controlled properties.
  applyProposal(proposal);
});

Listen for aeliqo-table-page

TypeScript example
const element = document.querySelector('aeliqo-table');</p>
<p>element?.addEventListener('aeliqo-table-page', (event) => {
  const proposal = (event as CustomEvent<unknown>).detail;
  // Validate the proposal, update host state, then assign controlled properties.
  applyProposal(proposal);
});

Listen for aeliqo-table-window

TypeScript example
const element = document.querySelector('aeliqo-table');</p>
<p>element?.addEventListener('aeliqo-table-window', (event) => {
  const proposal = (event as CustomEvent<unknown>).detail;
  // Validate the proposal, update host state, then assign controlled properties.
  applyProposal(proposal);
});

States and failure handling

Relevant states:

Expected result: The table preserves caption, scope, sort/page requests, and selection identity without losing keyboard access.

Keyboard, focus, and accessibility

Keyboard behavior:

Exposed semantics:

Responsive behavior

Use the table’s bounded page or virtualization controls with wide data. Keep a scroll path or choose a compact view before labels become unreadable.

Style hooks

Performance limits

Generated TypeScript declaration

View generated declaration
export declare class AeliqoTableElement extends LitElement {
    static readonly aeliqoVersion = "0.1.0";
    static readonly properties: {
        columns: {
            attribute: boolean;
        };
        rows: {
            attribute: boolean;
        };
        caption: {
            type: StringConstructor;
        };
        emptyLabel: {
            attribute: string;
            type: StringConstructor;
        };
        entity: {
            type: StringConstructor;
        };
        identity: {
            attribute: boolean;
        };
        selection: {
            type: StringConstructor;
        };
        selectedKeys: {
            attribute: boolean;
        };
        result: {
            attribute: boolean;
        };
        mode: {
            type: StringConstructor;
        };
        sort: {
            attribute: boolean;
        };
        page: {
            type: NumberConstructor;
        };
        pageSize: {
            attribute: string;
            type: NumberConstructor;
        };
        totalRows: {
            attribute: string;
            type: NumberConstructor;
        };
        virtualized: {
            type: BooleanConstructor;
        };
        virtualStart: {
            attribute: string;
            type: NumberConstructor;
        };
        virtualCount: {
            attribute: string;
            type: NumberConstructor;
        };
        overscan: {
            type: NumberConstructor;
        };
        scope: {
            attribute: boolean;
        };
        status: {
            type: StringConstructor;
        };
        message: {
            type: StringConstructor;
        };
    };
    columns: readonly AeliqoTableColumn[];
    rows: readonly AeliqoTableRow[];
    caption: string;
    emptyLabel: string;
    entity: string;
    identity: readonly string[];
    selection: AeliqoTableSelectionMode;
    selectedKeys: readonly string[];
    result: ResultRef | undefined;
    mode: AeliqoTableMode;
    sort: AeliqoSortState | undefined;
    page: number;
    pageSize: number;
    totalRows: number | undefined;
    virtualized: boolean;
    virtualStart: number;
    virtualCount: number;
    overscan: number;
    scope: AeliqoDataScope | undefined;
    status: AeliqoDataStatus;
    message: string;
    protected render(): import("lit").TemplateResult<1>;
    private renderTable;
    private renderCaption;
    private renderTableHead;
    private renderTableRows;
    private renderEmptyRow;
    private renderHeader;
    private renderTableRow;
    private renderGrid;
    private renderGridRow;
    private renderGridCell;
    private renderGridHeader;
    private gridTemplate;
    private renderSelectionCell;
    private selectionLabelForKey;
    private gridColumnCount;
    private virtualWindow;
    private visibleRows;
    private readonly requestSort;
    private readonly requestPage;
    private readonly handleSelection;
    private gridFocus;
    private pendingGridFocus;
    protected updated(): void;
    private resolveGridFocus;
    private keyedGridRow;
    private fallbackGridRow;
    private rememberGridFocus;
    private rowKeyAt;
    private gridFocusAt;
    private findGridCell;
    private availableRowCount;
    private gridNavigationContext;
    private requestGridWindow;
    private readonly handleGridCellKeyDown;
    private requestVirtualGridFocus;
    static readonly styles: import("lit").CSSResult[];
}

Version

This page documents the Varendo 0.6 component contract.