Cards, Columns, And Ordering
Canonical cards
Section titled “Canonical cards”Kanban reads and updates ordinary RevoGrid source rows. A card needs a stable ID, workflow-column value, and preferably a numeric order:
grid.source = [ { workId: 'PAY-104', summary: 'Retry declined payments', stage: 'active', rank: 1000, team: 'billing', },];
grid.kanban = { idField: 'workId', columnField: 'stage', orderField: 'rank', swimlaneField: 'team', columns: [ { prop: 'queued', name: 'Queued' }, { prop: 'active', name: 'Active' }, { prop: 'done', name: 'Done' }, ],};When a card has no rank, source order is used. The first move assigns fractional ranks between neighboring cards. If the available numeric gap becomes unsafe, Kanban rebalances only the destination bucket and reports the affected IDs in kanbancardmove.
Shared view fields
Section titled “Shared view fields”Use kanban.fields when the same application records also feed Scheduler or
Gantt. The keys describe field meaning and the values name properties in your
source rows:
grid.kanban = { fields: { id: 'id', title: 'name', status: 'workflowStatus', start: 'startDate', end: 'endDate', color: 'color', progress: 'percentDone', resourceId: 'owner', }, columns: [ { prop: 'todo', name: 'To do' }, { prop: 'doing', name: 'In progress' }, { prop: 'done', name: 'Done' }, ],};Kanban defaults these meanings to id, title, status, startDate,
endDate, color, progress, and assignees. An explicitly mapped shared
key takes precedence over the corresponding idField, columnField, or
card.*Field option. The older options remain available for existing boards.
The mapping only selects properties. It does not convert dates or progress
units. resourceId represents one resource identifier; Kanban also continues
to accept the established array form through card.assigneeField. Fields that
Kanban does not change, including application metadata, remain on the original
card. Moves, editor changes, remote updates, and undo/redo preserve authored
property names and do not add internal Kanban aliases to source rows.
Remote card pages
Section titled “Remote card pages”Use kanban.remote when the canonical collection should arrive from one
whole-board offset/limit stream:
const loadCards = async (skip: number, limit: number) => { const response = await fetch(`/api/cards?skip=${skip}&limit=${limit}`); const result = await response.json(); return { data: result.cards, total: result.total, };};
grid.kanban = { columns, remote: { loadData: loadCards, total: 100_000, chunkSize: 100, preloadThreshold: 0.75, placeholder: (index) => ({ id: `remote-${index}`, status: statuses[index % statuses.length], order: Math.floor(index / statuses.length), }), },};With total and placeholder, Kanban works like Infinity Scroll: it creates the
known number of lightweight positions, renders an animated skeleton for each
unloaded card row, and replaces the requested 100-card slice in place. The
placeholder only needs a unique temporary id, workflow column, swimlane when
configured, and rank fields. Its ID may differ from the returned server card ID;
the source offset owns replacement identity. The workflow, swimlane, and rank
values must match the corresponding server card. Scrolling directly to an
unloaded area requests that area’s chunk; approaching a loaded chunk boundary
preloads the next one.
Without placeholder, remote mode retains append paging: page zero replaces
the current cards and later pages append as the board nears its scroll boundary.
Both modes preserve loaded cards on errors and expose the same inline retry
action.
The result can be a card array or { data, total?, hasMore? }. In append mode,
a returned total takes precedence over hasMore; otherwise an explicit
hasMore wins, then the configured or previously returned total is consulted,
and only then does a short page end the stream. Indexed mode treats the
configured total as its fixed layout: every request must return its complete
requested slice, and response total or hasMore values must agree with that
layout. A mismatch keeps the slice retryable and emits the remote error event.
Local update and move operations preserve indexed server positions. Create and
delete are rejected in indexed mode because they would shift every later
offset; perform those operations on the server and call refreshRemote().
Replacing grid.source externally restarts indexed loading from page zero. In
append mode, local CRUD does not rewind the cursor and an external source
replacement adopts its length as the next offset. Both behaviors invalidate a
late response.
The loader type and indexed chunk behavior are shared with
InfinityScrollPlugin, so the same application adapter can power a table or
Kanban view. Kanban needs the additional placeholder factory because an
unloaded card’s workflow column and optional swimlane cannot be inferred from a
global total. The two plugins should not own one grid simultaneously because
Kanban projects cards into its own virtual rows.
Workflow columns
Section titled “Workflow columns”kanban.columns is the workflow schema; grid.columns remains the canonical
table/card-field schema. A card belongs to a workflow column when
card[columnField] === column.prop.
Workflow definitions reuse the safe RevoGrid header and sizing subset and add Kanban behavior such as WIP limits, collapse state, transition rules, metadata, and card filtering.
The array is optional and defaults to []. This supports loading workflow
metadata and cards independently: no columns plus no cards is an empty board,
while cards without configured columns use the managed Unmapped column by
default. Supplying workflow columns later rebuilds the projection from the same
canonical grid.source records.
columns: [ { prop: 'queued', name: 'Queued', size: 260 }, { prop: 'active', name: 'Active', minSize: 240, maxSize: 360, wipLimit: 4, allowedFrom: ['queued', 'active'], collapsible: true, },]Projected workflow columns keep the exact authored prop; no synthetic grid
property is introduced. If name is omitted, Kanban displays String(prop).
The default size is 288 and minSize is 220. The core column-move plugin
handles header dragging; kanbancolumnorderchange exposes the resulting prop
order for persistence.
Every workflow column is collapsible by default. Set collapsible: false to lock a column open, or use collapsed: true for its initial state. At runtime, setColumnCollapsed(prop, collapsed) changes one column and getCollapsedColumnProps() returns the active collapsed set. A collapsed column remains visible as a narrow rail with a vertical resolved-name badge, so users can restore it without losing board context.
Unknown and invalid data
Section titled “Unknown and invalid data”- Unknown statuses render in a localized Unmapped column by default. Set
unmappedColumn: falseonly when the host deliberately hides them. - Missing or duplicate card IDs produce blocking diagnostics. Those cards remain visible but cannot be moved.
- Duplicate column props produce a blocking configuration diagnostic.
- Invalid numeric ranks fall back to source order and produce a diagnostic.
Listen to kanbandiagnostics or call getDiagnostics() on the plugin during development and after remote data refreshes.
Counts
Section titled “Counts”Column and swimlane headers distinguish:
- visible count: cards passing RevoGrid filters, Kanban search, and the optional predicate;
- total count: all canonical cards in that bucket.
WIP limits always use total counts so a filter cannot accidentally permit over-capacity work.
In append mode, both counts describe the cards loaded so far. In indexed mode, known placeholder positions participate in total and visible counts, so loading a page does not change an unfiltered header count. Placeholders remain non-interactive, and active filters can exclude positions that cannot match. Enforce server-side rules in the application whenever Kanban does not have the authoritative records needed for the decision.