@kanunilabs/datagrid / GridColumnDef
Interface: GridColumnDef<TRow>
Type Parameters
TRow
TRow = GridRowData
Properties
absoluteSort?
optionalabsoluteSort?:boolean
Order a number column by MAGNITUDE (rival analysis G-SORT-01): −50 and +50 sit together, and descending puts the largest deviation first whatever its sign — a variance or profit-and-loss column. The cells keep their sign; only the order changes. Blanks stay last, and two values of equal size keep their data order.
Number columns only, and applied to calculateSortValue when the column
has one. The same order comes back whether the grid sorts on the main
thread or in the worker. Ignored when the column has a comparator (the
comparator decides) and when the column is a grouping level, where −50 and
+50 are two different groups. A server-side DataSource sorts the way its
server does.
align?
optionalalign?:ColumnAlign
allowFormulas?
optionalallowFormulas?:boolean
Cell formulas (G-EDIT-01, Enterprise editing): a value in this column that
is a string starting with = is a spreadsheet formula. The data keeps the
formula text; the grid shows, sorts, filters, totals and exports its
RESULT. [Name] reads this row's column; A1 / A1:B3 typed in the
editor are the cells on screen (columns by visible position, rows by
position among the data rows) and are stored by column id and row key —
{price@17} — so a later sort or filter cannot repoint them. A stored
A1 that arrived with the data reads the DATA order instead. Client data
only. The editor of such a column is a text box, whatever its dataType.
band?
optionalband?:string|string[]
Caption(s) of the band row(s) above this column's header — 'Sales' for
one level, ['Contact', 'Address'] for two.
A PATH on the column rather than a nested column tree: the flat list is what carries width, order, pinning, visibility, persistence and virtualization, so keeping the model flat means banding costs nothing in the pipeline. Adjacent columns with the same path merge into one band cell.
bandShow?
optionalbandShow?:"open"|"closed"
Whether this column survives its band being collapsed (gap analysis P2.6).
- omitted — always shown; collapsing the band does not affect it
'open'— only while the band is OPEN (the detail columns)'closed'— only while the band is COLLAPSED (the summary that stands in for them)
A band with no column declaring either is not collapsible: there would be nothing to hide, and a toggle that does nothing is worse than no toggle.
calculateSortValue?
optionalcalculateSortValue?: (row) =>unknown
Sort on a derived value (e.g. custom ordering) instead of the cell value.
Parameters
row
TRow
Returns
unknown
chartRole?
optionalchartRole?:"category"|"series"|"time"|"excluded"
The column's part in the Enterprise chart panel (rival analysis, charts
§E.1 #12). 'category' / 'series' make it the panel's first choice for
that role; 'time' treats it as a date axis, grouped by calendar;
'excluded' keeps it out of the chart altogether — its pickers, "Chart by
this column" and charting a selection. Without it the panel decides by
data type.
colSpan?
optionalcolSpan?: (params) =>number
Draw this cell across the columns after it (G-CORE-03): return how many columns it covers, itself included — 1 (or anything below 2) for an ordinary cell. Asked per DATA row, so one row can carry a heading across the whole grid while the rest stay cell by cell.
A span stays inside its block (pinned left, scrolling, pinned right) and
stops at a sticky column; a longer answer is cut there. The covered
cells are not drawn and the keyboard steps over them, but they are still
the row's data: sorting, filtering, copying and export see every column.
Parameters
params
ColSpanParams<TRow>
Returns
number
comparator?
optionalcomparator?: (valueA,valueB,rowA,rowB,descending) =>number
Order this column by an arbitrary rule (gap analysis P1.8) — the escape
hatch for orderings no data type describes: a T-shirt size scale, a
domain-specific priority, a locale rule Intl.Collator gets wrong.
Prefer calculateSortValue when the rule can be expressed as a value to
sort BY. That keeps the fast paths; this one closes them for this column:
• the radix/typed-array sort cannot run — a byte order cannot be derived from a function, so the column falls back to a comparator sort; • the WORKER cannot run the query at all — functions are unclonable, so a sort touching this column stays on the main thread whatever the row count. Correctness is preserved; the speed is what is spent.
The values arrive exactly as the column produces them, null/undefined
INCLUDED: a custom order usually wants to place its own blanks, and the
built-in "nils last" rule would pre-empt that decision.
descending is informational — the grid already inverts the result for a
descending sort. It exists for rules that must NOT flip (keeping a
"Total" row last in both directions).
Parameters
valueA
unknown
valueB
unknown
rowA
TRow
rowB
TRow
descending
boolean
Returns
number
dataType?
optionaldataType?:GridDataType
dateFormat?
optionaldateFormat?:string
How a date column is written and typed — 'dd.MM.yyyy', 'MM/dd/yyyy',
'dd/MM/yy', any separator. Defaults to 'yyyy-MM-dd'.
Governs BOTH directions: the cell renders through it, and the editor masks keystrokes into it, so what the user types back is the shape they were shown. Ignored on any other data type.
editable?
optionaleditable?:boolean
Editable in an Enterprise grid with editing on. Defaults to true; set false for a computed or server-owned column so the editor never opens on it.
excelFormat?
optionalexcelFormat?:string
Excel number format for the styled export (e.g. '#,##0.00', '"$"#,##0',
'0.0%'). Without it a number column gets a plain thousands separator —
currency and percent are never guessed, because guessing changes what the
number means.
exportLink?
optionalexportLink?: (row) =>string|null|undefined
A link for this cell in Excel and PDF exports (G-EXP-01): the cell is
written as a clickable hyperlink to the URL returned. Only http:,
https:, mailto: and tel: are written — anything else (a
javascript: URL above all) is dropped and the cell stays plain text.
Parameters
row
TRow
Returns
string | null | undefined
field?
optionalfield?:string
Property read from the row object (dot paths NOT expanded — use valueGetter).
filterable?
optionalfilterable?:boolean
flex?
optionalflex?:number
Share of the grid's spare width (G-CORE-02). The columns WITHOUT flex
keep their width; what is left of the grid's width is divided among the
flex columns in proportion to their numbers — flex: 2 takes twice what
flex: 1 does — and divided again whenever the grid is resized or a column
is shown, hidden or resized. minWidth / maxWidth still hold; once the
flex columns are all at minWidth the grid scrolls sideways.
width is ignored while a column flexes. A user who drags a flex column's
edge, or best-fits that one column, gives it a fixed width: it stops
flexing (ColumnState.flex becomes 0), which a saved layout remembers.
Best-fitting ALL columns leaves the flex columns alone.
formula?
optionalformula?:string
A calculated column (G-CORE-01): a spreadsheet formula over this row's
columns, e.g. '[Price] * [Qty]' or 'IF([Stock] < 10; "Low"; "OK")'.
Columns are written [id] or [Header text]. The column sorts, filters,
groups, totals and exports like any other; it is not editable. A formula
that does not compile shows #NAME? (the reason is in
controller.getFormulaError(id)); one that reads itself, directly or
through another calculated column, shows #CYCLE!. dataType defaults
to what the formula produces. valueGetter, when also given, wins.
groupable?
optionalgroupable?:boolean
groupEdit?
optionalgroupEdit?:"proportional"|"even"| ((params) =>object[])
Let the user type a new GROUP total for this column (G-GRP-02,
Enterprise): double-clicking the subtotal under the column in a group row
(grouping.display: 'aligned' or 'column') opens it, and the new value
is shared out over the group's rows — 'proportional' keeps each row's
share, 'even' gives every row the same. Works where that subtotal is a
sum or an avg. A function decides the new row values itself.
Needs editing; rows the user may not change keep their value.
groupInterval?
optionalgroupInterval?:DateGroupInterval
Group a DATE column by calendar 'day', ISO 'week', 'month',
'quarter' or 'year' instead of by the exact instant — one banner per
month reading "October 2026" rather than one per timestamp. Applies to
client-side data; a server-side source groups the way its server does.
headerName?
optionalheaderName?:string
id?
optionalid?:string
Stable identity; defaults to field. Required when two columns share a field.
lookup?
optionallookup?:ColumnLookup<TRow>
Value list for a code→label column. Drives the select editor, and (for a static list) the displayed text.
maxWidth?
optionalmaxWidth?:number
minWidth?
optionalminWidth?:number
pin?
optionalpin?:ColumnPin
reorderable?
optionalreorderable?:boolean
resizable?
optionalresizable?:boolean
sortable?
optionalsortable?:boolean
sortCycle?
optionalsortCycle?:SortCycle
The directions a header click steps through on THIS column, overriding the
grid's sorting.cycle — ['desc', 'asc', null] to open a figures column at
its largest, ['asc', 'desc'] to never return to unsorted. See
SortCycle. Only the header click follows it; setSort can still set any
direction.
spanRows?
optionalspanRows?:boolean|SpanComparator<TRow>
Merge this column's cell down over consecutive rows holding the same value (gap analysis P3.1).
Computed over the CURRENT view, which is the point: sorting a column into
A A A B B is what makes the merge meaningful, and the same rows unsorted
merge nothing. Blanks never merge, a group banner interrupts a run, and a
run of one is left as an ordinary cell.
true compares with Object.is. Pass a function for values that need
their own notion of equality — a column of objects, or a case-insensitive
code.
The merged block is still N cells to everything else: selection, editing, the clipboard and export see the rows the data has. Only the DRAWING is merged, which is what a reader means by it.
sparkline?
optionalsparkline?:ColumnSparkline<TRow>
Draw this column's value as an in-cell chart (gap analysis P2.2).
The cell's value must be an ARRAY of numbers — a series per row. Config
lives here, next to band and lookup, because it is data description
rather than rendering: which numbers, and against what scale. The drawing
belongs to the adapter, and the packaged renderer is Enterprise.
valueFormatter?
optionalvalueFormatter?: (value,row) =>string
Display formatting hook (adapters render the returned string).
Parameters
value
unknown
row
TRow
Returns
string
valueGetter?
optionalvalueGetter?: (row) =>unknown
Derive the cell value; overrides field access.
Parameters
row
TRow
Returns
unknown
visible?
optionalvisible?:boolean
width?
optionalwidth?:number