Lattice Grid Buy a licence

api reference

List API Reference

type: 'list': many values in one cell, each usable on its own - chips, a token editor, "any item matches" filters and four list operators.

API reference › The list module

All 18 pages Everything on one page → Developer guide →

Lists: the list module

import '@toclocoinc/lattice-grid/modules/list';

Importing the module registers the list data type with the page's registry (listModule is the module object, for a grid built on a registry of its own). Without it, type: 'list' warns type:list, naming this import, and the column is plain text.

A list column holds many values in one cell, each usable on its own: the tags on a ticket, the sizes a product comes in, the dates a meeting recurs on. typeOptions: { of } names the item type - any scalar data type, 'text' by default - and each item is read, formatted, parsed and compared as a value of that type. A name that is not a known scalar type warns once by name (type:of:<name>) and the items are text. A cell is an array of items; null and [] are both an empty cell. A single value where a list was expected is read as a one-item list and list:scalar:<column> says so once; an item the item type cannot read is left out of the stored list and list:item:<column> names it. Neither rewrites your row object.

columns: [
  { field: 'tags', type: 'list' },                                          // items are text
  { field: 'sizes', type: 'list', typeOptions: { of: 'number', joiner: '; ' } },
]

Display, editing, copying. A cell draws its items as chips in the list's order; the ones that do not fit the column's width collapse into a +3 chip, and the cell's tooltip lists every item. Each item is shown through the item type's own formatter. The editor is a token editor: type and press Enter to add an item, Backspace in the empty box removes the last one, and a pasted comma-separated string becomes one item per piece. Each piece is read by the item type's parser, and one it cannot read is refused by name and not stored. The edit goes through the ordinary edit contract, events and undo. Copy, CSV and Excel write the items joined by ', ', or by the column's joiner (typeOptions: { joiner: '; ' }); typed and pasted text is split on it and on commas.

Filtering on a list

One item at a time. Every operator the item type offers applies to a list column, and a row matches when any item does: eq 'gold' keeps a row whose list holds gold, contains 'ab' one with an item containing it, gt 5 on a list of numbers one with an item above five. The negations - ne, notContains, notIn, notBetween - mean no item matches, so ne 'gold' keeps a row with no gold in it, an empty list included. That is why the filter row's ordinary box and a set-filter tick just work on a list column. Text items follow the grid's text rules (case-insensitive unless caseSensitive).

OperatorOperandA row matches when
hasAnyan array of itemsthe list holds at least one of them
hasAllan array of itemsthe list holds every one of them (an empty array matches every row)
hasNonean array of itemsthe list holds none of them, an empty list included
listCount{ op: 'gt' | 'gte' | 'lt' | 'lte' | 'eq', value }the number of items compares as op says; an empty cell has none
blank / notBlanknonethe list is empty (or absent) / holds at least one item

The four list questions compare items exactly, case included, as SQL's list_has_any does. They are declared through DataType.operators, so a list column refuses an operator its item type does not offer, and every other column refuses these, with a [lattice] warning, from filters.set() and state.apply(). The column's filter panel offers the item type's operators, then Has any of, Has all of, Has none of and three item counts. The set filter and the facets tally each item on its own, and a chosen facet value filters with hasAny.

Sorting orders by the number of items, then by the first item under the item type's comparator; an empty cell sorts with the other empty cells. Grouping by a list column groups by the joined text, so gold, silver is one group; grouping by each item on its own is not offered. Excel receives the joined text in a text cell: Excel's own format for several values in one cell is not published, and will be written when it is. The AI schema describes the column as a list of its item type, with the operators above.

Pushed down. Over a DuckDB LIST column the same questions become the engine's list functions; see list pushdown.

A list column, executed

A list of tags and a list of sizes, filtered one item at a time and as whole lists, sorted, and exported with the column's joiner. Run headless on every build.

const { createHeadlessGrid } = await import('../packages/core/src/index.js');
// Importing the module registers the type; listModule is the module object.
const { listModule } = await import('../packages/modules/list/index.js');

const grid = createHeadlessGrid({
  rowKey: 'id',
  columns: [
    { field: 'id', type: 'number' },
    { field: 'tags', type: 'list' },
    { field: 'sizes', type: 'list', typeOptions: { of: 'number', joiner: '; ' } },
  ],
  rows: [
    { id: 1, tags: ['gold', 'silver'], sizes: [8, 10, 12] },
    { id: 2, tags: ['bronze'], sizes: [6] },
    { id: 3, tags: [], sizes: [14, 16] },
  ],
});
const ids = () => Array.from({ length: grid.rows.count() }, (_, i) => grid.rows.get(i).key).join(',');

grid.filters.set({ col: 'tags', op: 'eq', value: 'gold' });          // any item is gold
const gold = ids();
grid.filters.set({ col: 'sizes', op: 'gt', value: 12 });             // any size above 12
const big = ids();
grid.filters.set({ col: 'tags', op: 'ne', value: 'gold' });          // no item is gold
const notGold = ids();
grid.filters.set({ col: 'tags', op: 'hasAny', value: ['bronze', 'silver'] });
const either = ids();
grid.filters.set({ col: 'sizes', op: 'listCount', value: { op: 'gte', value: 2 } });
const several = ids();
grid.filters.clear();
grid.sort.set([{ col: 'sizes', dir: 'asc' }]);                       // by count, then first item
const bySize = ids();
const csv = grid.export.csv().split('\r\n').find((line) => line.startsWith('3,'));
grid.destroy();
return `${listModule.name} | ${gold} | ${big} | ${notGold} | ${either} | ${several} | ${bySize} | ${csv}`;