api reference
Data View API Reference
createDataView: items laid out as template-drawn cards that wrap into rows, with selection, keyboard navigation in two dimensions, and drag to reorder or between views.
API reference › The data view module
All 29 pages Everything on one page → Developer guide →
The data view module
modules/dataview is the list view's card-grid sibling: items laid out as
cards that wrap into rows, each card drawn by your own template. A
person selects cards (single and multi), moves through them with the arrow keys in two dimensions,
and drags a card to a new position; a drop indicator shows where it lands. It builds on nothing
but the DOM - there is no grid factory to inject - and draws only the rows of cards in
(or just beyond) the viewport, so 5,000 items cost the same as a screenful.
import { createDataView } from '@toclocoinc/lattice-grid/modules/dataview';
const view = createDataView(document.querySelector('#cards'), { // give the host a height
data: [{ id: 1, name: 'Ada' }, { id: 2, name: 'Grace' }],
template: (item) => `<h4>${item.name}</h4>`,
itemWidth: 180, itemHeight: 96, gap: 12, // or columns: 4 to fill the width
selection: 'multi',
dragMode: 'both',
});
Layout. Cards are itemWidth by itemHeight (default 200 by
120) with gap (default 8) between them, and as many as fit go on a row. Set
columns: n instead and exactly n cards share the width, so the row is always
full; itemWidth is then ignored. The view resizes with its host. Give the host element
a height - the view fills it and scrolls inside it.
The list view's API, reused. view.data, view.selection and
the event names are the list view's, and live in one shared file both modules import, so
serialize(), filter(), sort() and the rest mean the same thing
on both and always read back what is on screen. A returned template string goes through the same
sanitiser the grid applies to template output; a returned element is used as it is.
Keyboard and pointer. A click selects a card; the platform modifier
(Cmd on macOS, Ctrl elsewhere) toggles one into a 'multi' selection and Shift extends
a range. One card is in the tab order; the arrow keys move between cards in two dimensions (Left and
Right wrap across rows, Up and Down keep the column), Home and End go to the ends of a row (with the
platform modifier, of the whole set), PageUp and PageDown move a screenful, Space toggles, Enter
activates (itemDoubleClick) and Shift with an arrow extends the selection. In
'single' mode the selection follows the focus.
Drag and drop. dragMode is the list view's:
'source' lets a card be picked up (reordering within the view, or sending it to another),
'target' receives, 'both' does either and false (the default)
neither; dragCopy: true leaves the source card behind. A card dropped before the
card whose left half the pointer is in lands there; afterDrop reports the new order and
serialize() already matches the screen. A drag between two data views, or to and from a
listview list, goes through the target's single beforeDrop: return
false (or a Promise of it) and both sides are left exactly as they were. A reorder
within one view is refused, with the reason announced, while a sort or filter is active, because the
drop position would have no single meaning in the stored order.
Accessibility. The view is an ARIA grid of rows of
gridcell cards with aria-rowcount, aria-colcount,
aria-rowindex and aria-colindex, and aria-selected when
selection is on. With no items it shows emptyText (default “No items”).
| Member | Description |
|---|---|
| createDataView(el, config) | Create the view. config.template is required; the host needs a height. |
| data.parse / add / remove / removeAll / update / getItem / getIndex / getLength / forEach / serialize / filter / sort | The list view's Data API, over the view's own items: serialize() is always the order on screen, and filter(fn) hides without losing. |
| selection.add(id) / selection.remove(id) / selection.getId() / selection.clear() | Select, deselect, read (bare id/null in 'single' mode, always an array in 'multi'), or clear the selection. |
| on(name, fn) / off(name, fn) | Events, the list view's: itemClick, itemDoubleClick, the cancellable beforeDrop, afterDrop, dragOut, selectionChange, and template-bubbled input/change. |
| destroy() | Remove the view's DOM and listeners, drop every handler. The host still owns the element. |
Layout, the data API and selection, executed
Cards per row from a width and from a column count, five items seeded, an
insert, a patch, a sort, a filter that hides without losing anything, selection, and the redrawn
cards in the same order as serialize() - run headless on every build with the
suite's own DOM test double.
const { createTestDom } = await import('../packages/dom/src/renderer/testdom.js');
const { createDataView } = await import('../packages/modules/dataview/index.js');
const { computeColumns } = await import('../packages/modules/dataview/layout.js');
const fit = computeColumns({ width: 640, itemWidth: 100, gap: 10 }).columns; // 5 cards of 100 and 4 gaps of 10 = 540
const filled = computeColumns({ width: 640, columns: 4, gap: 10 }).cardWidth; // (640 - 3 * 10) / 4
const seen = [];
const host = createTestDom().root;
const view = createDataView(host, {
data: [{ id: 1, name: 'Ada' }, { id: 2, name: 'Grace' }, { id: 3, name: 'Linus' }],
key: 'id',
template: (item) => `<h4>${item.name}</h4>`,
itemWidth: 100,
itemHeight: 60,
columns: 2,
gap: 10,
css: 'person',
selection: 'multi',
dragMode: 'both',
dragCopy: false,
emptyText: 'No people',
});
view.on('selectionChange', (e) => seen.push(`sel:${e.ids.join(',')}`));
view.data.add({ id: 4, name: 'Margaret' }, 1);
view.data.update(2, { name: 'Grace H.' });
const added = view.data.serialize().map((i) => i.id).join(',');
view.data.sort('name', 'desc');
const sorted = view.data.serialize().map((i) => i.id).join(',');
view.data.filter((item) => item.name.length < 6);
const shown = `${view.data.serialize().length}/${view.data.getLength()}`;
view.data.filter();
view.data.sort();
view.selection.add([1, 3]);
view.selection.remove(1);
const selected = view.selection.getId().join(',');
view.selection.clear();
await Promise.resolve(); // the cards redraw once, after a burst of changes
const cards = [...host.querySelectorAll('.lat-dv-card.person')]
.sort((a, b) => a.getAttribute('data-index') - b.getAttribute('data-index'))
.map((c) => c.getAttribute('data-key')).join(',');
const rows = host.querySelector('[role="grid"]').getAttribute('aria-rowcount');
view.destroy();
return [fit, filled, added, sorted, shown, selected, cards, rows, seen.join('|')].join(' || ');
Type reference
Generated from the type declarations, so it always matches the release. Each surface lists its properties, its methods and the events it raises as three tables; an option or value type lists its members once.
The data view module
DataViewConfig
The configuration for {@link createDataView}: items laid out as cards that wrap into rows, each card drawn by `template`. Cards are `itemWidth` wide and as many as fit go on a row, or `columns` of them share the width.
| Property | Type | Description |
|---|---|---|
| data | DataViewItem[] | The initial items. Default `[]`. (optional) |
| key | string | The field each item's identity comes from. Default `'id'`. (optional) |
| itemWidth | number | Card width in pixels; as many cards as fit go on a row. Default `200`. Ignored when `columns` is set. (optional) |
| itemHeight | number | Card height in pixels. Default `120`. (optional) |
| columns | number | A fixed number of cards per row; the cards share the width equally, so `columns: n` fills it. (optional) |
| gap | number | The gap between cards, in pixels. Default `8`. (optional) |
| css | string | string[] | Class name(s) added to every card. (optional) |
| selection | false | 'single' | 'multi' | Selection mode. Default `false`. (optional) |
| dragMode | false | 'source' | 'target' | 'both' | This view's role in a drag: `'source'` and `'both'` reorder within the view and send to another; `'target'` and `'both'` receive. Default `false`. (optional) |
| dragCopy | boolean | `true` leaves the source card in place on a drop between views (copy, not move). Default `false`. (optional) |
| emptyText | string | HTMLElement | The empty-state message or element. (optional) |
| Method | Signature | Parameters | Returns | Description |
|---|---|---|---|---|
| template | (item: DataViewItem) => string | HTMLElement | item: DataViewItem | => string | HTMLElement | Called per item to draw its card; a returned string goes through the grid's template sanitiser, an element is used as is. |
DataViewDataApi
`view.data`: the list view's Data API, over the view's own items. `serialize()` is always the order on screen.
| Method | Signature | Parameters | Returns | Description |
|---|---|---|---|---|
| parse | (items: DataViewItem[]): void | items: DataViewItem[] | void | Replace the whole dataset, keyed by `DataViewConfig.key`. |
| add | (item: DataViewItem, index?: number): void | item: DataViewItemindex?: number | void | Insert an item at a display index (default: end). |
| remove | (id: unknown): void | id: unknown | void | Remove the item with this identity. |
| removeAll | (): void | - | void | Remove every item. |
| update | (id: unknown, patch: Partial<DataViewItem>): void | id: unknownpatch: Partial<DataViewItem> | void | Shallow-merge `patch` into the item identified by `id`. |
| getItem | (id: unknown): DataViewItem | undefined | id: unknown | DataViewItem | undefined | The item stored under an identity, or undefined. |
| getIndex | (id: unknown): number | id: unknown | number | The item's current display index, or -1 when not currently displayed. |
| getLength | (): number | - | number | The total stored item count, filtered or not. |
| forEach | (fn: (item: DataViewItem, index: number) => void): void | fn: (item: DataViewItem, index: number) => void | void | Iterate the items, in current display order. |
| serialize | (): DataViewItem[] | - | DataViewItem[] | The items in current on-screen order. |
| filter | (fn?: (item: DataViewItem) => boolean): void | fn?: (item: DataViewItem) => boolean | void | Hide items a predicate refuses, without removing them; call with no arguments to clear. |
| sort | (fnOrField?: ((a: DataViewItem, b: DataViewItem) => number) | string, dir?: 'asc' | 'desc'): void | fnOrField?: ((a: DataViewItem, b: DataViewItem) => number) | string, dir?: 'asc' | 'desc' | void | Change display order: a comparator, or a field name with `dir` (default `'asc'`). |
DataViewSelectionApi
`view.selection`: the list view's selection API, over the view's own selection.
| Method | Signature | Parameters | Returns | Description |
|---|---|---|---|---|
| add | (id: unknown | unknown[]): void | id: unknown | unknown[] | void | Select one item, or several, alongside whatever is already selected. |
| remove | (id: unknown | unknown[]): void | id: unknown | unknown[] | void | Deselect one item, or several. |
| getId | (): unknown | unknown[] | null | - | unknown | unknown[] | null | In `'single'` mode: the selected id, or null. In `'multi'` mode: always an array. |
| clear | (): void | - | void | Deselect everything. |
DataViewItemEvent
The payload `itemClick`/`itemDoubleClick` carry.
| Property | Type | Description |
|---|---|---|
| id | unknown | The clicked card's item identity. |
| item | DataViewItem | null | The clicked item, or null. |
| index | number | The card's display index. |
| originalEvent | unknown | The originating DOM event. |
DataViewBeforeDropEvent
The cancellable `beforeDrop` payload: return `false` (or a Promise of it) to veto, leaving both views unchanged.
| Property | Type | Description |
|---|---|---|
| source | DataViewPeer | null | The view the dragged card came from. |
| target | DataViewPeer | This view, the drop target. |
| ids | unknown[] | The dragged item's identity, as a one-element array. |
| index | number | The display index the card would be inserted at. |
DataViewAfterDropEvent
The `afterDrop` payload: a move or copy between two views, or a reorder within one, committed.
| Property | Type | Description |
|---|---|---|
| source | DataViewPeer | null | The view the card came from. |
| target | DataViewPeer | null | The view the card landed in. |
| ids | unknown[] | The moved/copied item's identity, as a one-element array. |
| index | number | The display index the card landed at in the target. |
DataViewDragOutEvent
The `dragOut` payload: a drag moved out of this view.
| Property | Type | Description |
|---|---|---|
| id | unknown | The dragged item's identity. |
| index | number | The card's display index before the drag. |
DataViewSelectionChangeEvent
The `selectionChange` payload: always an array, whatever the selection mode.
| Property | Type | Description |
|---|---|---|
| ids | unknown[] | The selected ids. |
DataViewTemplateEvent
The `input`/`change` payload: an event bubbled from inside a card's template, carrying the item's identity.
| Property | Type | Description |
|---|---|---|
| id | unknown | The card's item identity. |
| item | DataViewItem | null | The card's item, or null. |
| originalEvent | unknown | The originating DOM event. |
DataViewEventPayloads
The payload each data view event name carries.
| Property | Type | Description |
|---|---|---|
| itemClick | DataViewItemEvent | A card was clicked. |
| itemDoubleClick | DataViewItemEvent | A card was double-clicked, or Enter was pressed on it. |
| beforeDrop | DataViewBeforeDropEvent | A drop between views is about to land; return `false` (or a Promise of it) to veto it. |
| afterDrop | DataViewAfterDropEvent | A reorder, move or copy committed. |
| dragOut | DataViewDragOutEvent | A drag moved out of this view. |
| selectionChange | DataViewSelectionChangeEvent | The selection changed. |
| input | DataViewTemplateEvent | An `input` event bubbled from inside a card's template. |
| change | DataViewTemplateEvent | A `change` event bubbled from inside a card's template. |
DataView
The instance {@link createDataView} returns.
Properties
| Property | Type | Description |
|---|---|---|
| data | DataViewDataApi | The Data API. |
| selection | DataViewSelectionApi | The selection API. |
Methods
| Method | Signature | Parameters | Returns | Description |
|---|---|---|---|---|
| on | <K extends DataViewEventName>(name: K, fn: (payload: DataViewEventPayloads[K]) => void): () => void | name: Kfn: (payload: DataViewEventPayloads[K]) => void | () => void | Subscribe to a data view event; returns an unsubscribe function. |
| off | <K extends DataViewEventName>(name: K, fn: (payload: DataViewEventPayloads[K]) => void): void | name: Kfn: (payload: DataViewEventPayloads[K]) => void | void | Remove an event handler registered with `on`. |
| destroy | (): void | - | void | Tear the view down: remove its DOM and listeners, drop handlers. The host still owns the element. |
Events
This surface raises events, but the names are not declared: on() accepts any string. They cannot be listed here until the declaration names them.