Active Foundation

Search documentation

Search all pages, headings and text, in the current package or topic or in all documentation.

Cells and utilities

Cell components, column helpers and the utility functions.

@workspace/active-grid 11.0.0Status unknowncontent differs from the recorded release 11.0.0baseline ce081dc3

The package's own reference

The text below is the package's own API reference, included unchanged. The complete list of exports, generated from the source, is on All exports.

Cell renderers, system-column factories, export functions, auto-size utilities, hooks, ContextMenuBuilder, EditingCommitPayload, and the translation/locale typing.

LookupGrid#

LookupGrid presents a single-record picker in a controlled dialog. Supply open, onOpenChange, title, columns, onSelect, t and locale. Server mode is the default and requires fetchFn; mode="client" requires data instead. Confirmation or a selectable row's double-click calls onSelect(record).

Optional inputs include gridId, getRowId, isRowSelectable(record), columnTypes, serverCapabilities, description, searchPlaceholder, maxWidth, gridHeight, initialPageSize and pageSizeOptions. The capability map is forwarded to the inner grid. The picker does not define a server query or authorize the selected record.

Cell Components#

typescript
import {  TextCell,  BadgeCell,  StatusCell,  DateCell,  LinkCell,  SelectionCell,  ExpanderCell,} from '@workspace/active-grid';

Pre-built cell renderers for use in custom column cell render functions.

ComponentDescription
TextCellDefault text renderer with truncation and title tooltip.
BadgeCellCategorical badge: type, category, department, source. One fixed presentation.
StatusCellStatus badge on a GridStatusTone, drawn with the --grid-status-* tokens.
DateCellLocale-aware date display. Date-only by default; see below.
LinkCellLink renderer: a native anchor, or the host's ui.linkComponent; see below.
SelectionCellCheckbox for row selection (used by getSelectionColumn).
ExpanderCellChevron toggle for row expanding (used by getExpanderColumn).

Calendar day versus instant#

DateCell carries a semantic prop with two values, and the choice is not cosmetic โ€” it decides whether the shown day may move with the reader's time zone.

semanticMeaningBehaviour
'date-only' (default)A calendar fact: birth, order, measurement or effective dateThe same day in every time zone
'datetime'A real moment: createdAt, an audit or execution timestampDirect DateCell uses runtime-local date-fns formatting; use the dateTime semantic column for presentation-zone formatting

Date-only is the default because that is what this cell actually receives: body-cell.tsx renders it for columns with meta.filterType: 'date', while a column that genuinely shows a timestamp goes through the dateTime column-type preset, whose semantic declaration is formatted by the cell orchestration.

TSX
<DateCell value={contract.startDate} />                      // calendar day<DateCell value={row.createdAt} semantic="datetime" />       // moment in time

The same distinction lives in the column-type presets: type: 'date' is a calendar day, type: 'dateTime' is an instant. Prefer those over a hand-written meta.filterType: 'date'.

Added 2026-08-12 (AUI-01). Until then the cell did new Date(value) for its whole Date | string | number contract. For an ISO date-only string that is UTC midnight, after which format() rendered the local calendar day: under TZ=America/New_York 2026-03-09 showed as 08-03-2026. Measured, not reasoned โ€” eight of the nine tests in date-cell.test.tsx went red as soon as the zone shifted. The parsing now runs through dateOnlyToLocalDate from @workspace/active-format, the one boundary the whole repo shares.

Status versus categorical#

The two badge cells are separate on purpose. BadgeCell presents a value that belongs to a set (a category, a type, a department); StatusCell presents a value that says how something is doing, on the five-tone axis GridStatusTone (neutral, info, success, warning, danger). A host maps each tone onto its own status colours through the --grid-status-<tone> and --grid-status-<tone>-text tokens.

BadgeCell has no variant or colour prop โ€” it had one until 9.0.0 and it was status semantics expressed in an axis that carries none (default is the theme's accent colour, not "good"). A custom cell that genuinely needs a coloured categorical badge renders the host's own badge component directly, so that choice is visibly outside the grid's badge contract.

TSX
// Declarative โ€” preferred. The column says what each value means; the grid// renders it. See api/columns.md ยง Status columns.meta: {  filterOptions: [{label: t('status.active'), value: 'ACTIVE'}],  statusTones: {ACTIVE: 'success', LAPSED: 'danger'},}
// Imperative โ€” for values that are not a stable key (derived or already// translated), where a valueโ†’tone map would break per locale.cell: (info) => (  <StatusCell    value={info.getValue() ? t('session.active') : t('session.expired')}    tone={info.getValue() ? 'success' : 'neutral'}  />),
PropTypeDescription
valuestring | nullThe text to display. Renders nothing when empty.
toneGridStatusToneRequired. The generic UI-tone; the caller maps to it.
labelMapRecord<string, string>Optional valueโ†’label resolution, as on BadgeCell.
classNamestringExtra classes on the badge.

LinkCell renders an internal link as a plain <a href>, so the browser navigates. A host with a client-side router passes its link component once, through the nearest GridDefaults:

TSX
// `hostLink`: your router's link component, e.g. the default export of next/link
<GridDefaults ui={{linkComponent: hostLink}}>{children}</GridDefaults>

The component receives {href, className, children} and nothing else (GridLinkProps; the type of the component is GridLinkComponent). A nearer GridDefaults replaces it for the grids below; a nearer one that sets other ui fields keeps it. An external link (isExternal) is always a native anchor that opens in a new tab.

TSX
cell: (info) => (  <LinkCell value={info.getValue()} href={(id) => `/policies/${id}`} />),
PropTypeDescription
valuestring | null | undefinedThe link text, and the argument of an href function.
hrefstring | ((value: string) => string)The destination.
isExternalbooleanNew tab, rel="noopener noreferrer", never the host link.
classNamestringExtra classes on the anchor.

System Column Factories#

typescript
import {getSelectionColumn, getExpanderColumn} from '@workspace/active-grid';
FunctionDescription
getSelectionColumn()Returns a column definition for row-selection checkboxes.
getExpanderColumn()Returns a column definition for row expand/collapse toggles.

System columns have enableSorting: false, enableResizing: false, and no header menu by default. They are excluded from export. Row-numbering is available via the showRowNumbers prop on <ActiveGrid> rather than a factory function.

ContextMenuBuilder#

typescript
import {  type ContextMenuBuilder,  type ContextMenuItem,  createContextMenu,} from '@workspace/active-grid';
typescript
type ContextMenuBuilder<TData> = (  row: TData | null,  context?: ContextMenuContext,) => ContextMenuItem<TData>[];
interface ContextMenuContext {  kind: 'range' | 'cell' | 'row'; // what the opened menu is about  columnId?: string; // the column of the cell it opened on  selection?: {rangeCount: number; cellCount: number}; // for a 'range' menu  selectedRowCount: number; // rows selected in the grid}
type ContextMenuItem<TData> =  | 'separator'  | {      label: string;      action?: (row: TData) => void; // optional for submenu triggers      icon?: React.ElementType;      variant?: 'default' | 'destructive';      disabled?: boolean;      disabledReason?: string;      shortcut?: string;      isLoading?: boolean;      keepOpen?: boolean;      permission?: string; // the RIGHT the viewer needs      visible?: (row: TData) => boolean; // relevance for THIS row      separatorBefore?: boolean; // explicit group boundary      children?: ContextMenuItem<TData>[]; // nested submenu items    };

When the builder runs, and what it is told#

The grid calls the builder for a row that stands for a record, when that row's menu opens, and passes the context as the second argument. A builder that declares only row keeps working unchanged; the argument is optional so a builder can still be called with a row alone. A row the grid generated (a client group row) never reaches the builder: its menu is the grid's copy of what its cells show.

kind is range when the menu opened inside a cell selection of more than one cell (selection then counts every range together), cell when it opened on a single cell, and row when there is no cell target (cell selection is off, or the menu opened outside the row's cells). The grid's own copy items follow the same kinds; the builder's items are the row's actions and stay in every kind.

typescript
const contextMenu: ContextMenuBuilder<Person> = (row, context) => {  if (!row) return [];  return [    {label: 'View details', action: (r) => open(r.id)},    ...(context?.kind === 'range' ? [] : [{label: 'Edit', action: edit}]),  ];};

Permission, visibility and disabled are three questions#

They are answered separately and must not be substituted for one another:

FieldQuestionResult when it fails
permissionMay this VIEWER perform the action at all?the item is absent
visibleIs the action relevant for THIS row/state?the item is absent
disabledCan it run right now, given the current state?shown, with a reason

An item carrying both permission and visible must pass both. Filtering happens before separator normalization, so a group that filters away completely leaves no orphaned rule behind.

Put a right in permission, never in visible: that is where the factory, the docs and the next reader look for it. visible is for row and state predicates ((row) => row.status === 'FAILED'). Both are client-side affordances -- the server action behind the item authorizes independently.

A declared permission is never ignored. It is answered by the hasPermission checker passed to createContextMenu, and nothing else:

ItemChecker answers yesChecker answers noNo checker
without permissionshownshownshown
with permission, via the factoryshownabsentabsent
with permission, direct builder----absent

So a menu that declares rights passes the viewer's checker to the factory. An item no checker answered is left out at the render boundary as well, and outside production the grid reports it once per opening through the <GridDefaults diagnostics> sink (warn, source ActiveGrid.ContextMenu, data: {label, permission}): a declared right without its check is a configuration error, and failing closed keeps it from offering an action the viewer may not take.

Items with a children array render as a submenu trigger with a chevron (>). action is optional for submenu parent items โ€” the trigger itself does not perform an action.

Permission filtering is recursive: createContextMenu filters both parent items and their children by hasPermission. If the parent is filtered out, its children are not rendered.

typescript
const contextMenu = createContextMenu<Person>({  hasPermission,  customItems: (row) => {    if (!row) return [];    return [      {        label: 'View details',        icon: Eye,        action: (r) => router.push(`/records/${r.id}`),      },      'separator' as const,      {        label: 'Change status',        icon: ArrowRightLeft,        permission: 'record.update',        children: transitions.map((status) => ({          label: `Status: ${status}`,          action: (r: Person) => changeStatus(r, status),        })),      },    ];  },});

The resolve pipeline#

Every row menu passes through one pipeline, in this order:

  1. filter on permission and visible, recursively into submenus;

  2. expand separatorBefore into a rule in the sequence;

  3. give the destructive tail its rule, when the menu itself makes that unambiguous;

  4. clean up: no leading rule, no trailing rule, never two in a row.

Filtering comes first because filtering is what strands rules. The pipeline is idempotent and never reorders, merges or drops an action. It is the one pipeline of the Active product family (ADR-058): it lives in @workspace/active-utils/menu-items, and Active Charts runs it over its menus too; the grid supplies the permission check as its keep step.

Submenus and reasons. children nests to any depth, each level its own submenu. A disabled item keeps its disabledReason as the pointer's title and as aria-description for assistive technology, on a submenu trigger as well; it stays focusable, so a keyboard reader hears why it cannot run.

createContextMenu runs it with the permission check, and the grid runs it again at the render boundary on whatever a builder hands it. That second place is what makes the contract hold product-wide: a menu passed directly as a ContextMenuBuilder never meets the factory, but it does meet the renderer. The renderer applies visible and the separator rules. It has no hasPermission, so it keeps an item that declares permission only when the factory approved it; the factory returns approved items as fresh copies and never mutates or marks the consumer's own objects.

Consumers declare plain items and write no clean-up of their own. There is nothing to import.

Destructive actions sit in their own block#

A destructive action, or a group of related lifecycle actions ending in one, belongs at the bottom of the consumer block, separated from the ordinary object actions by a single rule. The rule underneath it is drawn by the grid, which always closes the consumer block before its own Copy / Export / Reset items:

Text
ViewEditโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€Delete            <- variant: 'destructive'โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€         <- drawn by the gridCopyExport view (10)Reset columns

You usually declare nothing. The pipeline adds the rule when the menu ends in a destructive action and something precedes it. It deliberately does nothing when:

  • the menu does not end in a destructive action (there is no tail, and only the consumer knows the intended boundary -- use separatorBefore);

  • the destructive action is the first or only visible item (a rule above it would separate nothing);

  • a rule already opens that group.

A mixed lifecycle group stays whole. The tail starts at the FIRST destructive item of the last segment, not the last one, so a media block of delete, restore and delete-permanently is one group with one rule above it -- no rule falls between the restore item and the permanent delete, even though the restore item is not itself destructive.

Use separatorBefore for a group the pipeline cannot infer, for example one that does not end the menu. It carries the same name and meaning as GridSelectionAction.separatorBefore on the bulk bar: one vocabulary per package. A doubled or leading rule is removed afterwards, so declaring it defensively is harmless.

GridContextMenu#

typescript
import {GridContextMenu} from '@workspace/active-grid';

Wrapper component for the Base UI right-click menu. Most consumers do not import this directly โ€” the grid uses it internally when a contextMenu prop is supplied.

EditingCommitPayload#

typescript
import {type EditingCommitPayload} from '@workspace/active-grid';

Payload received by onDataCommit:

PropertyTypeDescription
rowIdstringID of the row being edited.
originalRowTDataRow data before editing.
changesRecord<string, unknown>Only the changed fields.
newDataTDataComplete row data after edits.

Related types: BatchCommitPayload, DirtyCellEntry, CellRange, CellSelectionState, EditMode โ€” for the batch/range-editing surface (advanced).

Spreadsheet-style editing#

Combine editTrigger="click" with commitOnBlur for spreadsheet-like behavior:

TSX
<ActiveGrid<Row>  gridId="editor"  mode="client"  data={rows}  columns={columns}  editMode="cell"  editTrigger="click"  commitOnBlur  onDataCommit={async (payload) => {    setRows((prev) =>      prev.map((row) =>        getRowId(row) === payload.rowId ? payload.newData : row,      ),    );  }}/>

Behavior: single click starts editing; clicking another cell commits + moves; clicking outside the grid commits; Enter commits and moves down; Tab commits and moves right; Escape cancels.

Export Functions#

typescript
import {  exportToCSV,  exportToCSVGrouped,  exportToCSVServerGrouped,  exportToJSON,  exportToJSONGrouped,  exportToJSONServerGrouped,  exportToExcel,  exportToExcelGrouped,  exportToExcelServerGrouped,  exportToPDF,  exportToPDFGrouped,  exportToPDFServerGrouped,  copyToClipboard,  GridExportError,  type GridExportErrorCode,  type GridExportErrorOptions,  type GridExportMode,  type GridExportRunOptions,  type GridExcelExportOptions,  type GridPdfExportOptions,  type GridExportFonts,  type GridExportFontFamily,  type GridExportFontFace,  type GridExportFontSource,  type GridExportDirection,  type GridClipboardOptions,  type ServerGroupedExportConfig,} from '@workspace/active-grid';
FunctionDescription
exportToCSV(rows, grid, filename, options?)Download CSV. Every field quoted, formulas neutralized.
exportToJSON(rows, grid, filename, options?)Download JSON file.
exportToExcel(rows, grid, filename, options?)Download Excel .xlsx with typed cells (optional xlsx, loaded on use). options.direction: 'rtl' opens the sheet right to left.
exportToPDF(rows, grid, filename, options)Download PDF (optional jspdf + jspdf-autotable). Presentation-only; options.fonts and options.direction (ADR-054).
copyToClipboard(rows, grid, options)Copy rows as TSV presentation copy. Formulas neutralized. A refused write resolves false and is reported to options.diagnostics (default: the console).

options on the CSV/JSON/XLSX functions is a GridExportRunOptions (GridExportMode union): omit it (or pass {mode: 'data'}) for the default DATA export โ€” machine/raw semantics, byte-stable under every viewer language โ€” or pass {mode: 'presentation', locale} for the explicit human-readable form. exportToExcel* takes a GridExcelExportOptions: the same options plus direction (GridExportDirection, 'ltr' | 'rtl'). PDF is presentation output by definition and takes a required GridPdfExportOptions ({locale, fonts?, direction?}); the clipboard is a presentation copy and takes a required GridClipboardOptions ({locale, includeHeaders?}). See guides/export.md for the full mode contract.

Fonts are PDF-only, by type. GridExportFonts (families in fallback order; each face source is bytes or a lazy host loader, with weight and style) is accepted by GridPdfExportOptions.fonts, the instance's export.fonts and GridDefaults' export.fonts, and by nothing else: the CSV, JSON and XLSX options have no font field. The shape is structurally the one of active-charts' ChartExportFonts, so one host object serves both. See guides/export.md.

Errors. An export the grid refuses, or whose optional backend is missing, fails with a GridExportError whose code is stable (GridExportErrorCode: 'EXPORT_DEPENDENCY_MISSING', 'EXPORT_FONT_REQUIRED', 'EXPORT_FONT_FORMAT_UNSUPPORTED', 'EXPORT_FONT_FACE_MISSING', 'EXPORT_FONT_SOURCE_INVALID', 'EXPORT_FONT_LOAD_FAILED', 'EXPORT_TEXT_UNSUPPORTED', 'EXPORT_GLYPH_COVERAGE') and whose metadata never holds exported text. See guides/export.md.

Outcome. Every function reports by itself: a synchronous one throws, an asynchronous one rejects, and a return means the file was handed to the browser. An export from the row menu reports through GridExportOptions.onExportComplete(format, scope) and onExportError(format, error, scope) (GridExportScope: 'page', 'loaded', 'selection'); see guides/export.md. No export is cancellable.

Each export function has -Grouped and -ServerGrouped variants for client-side and server-side grouped data respectively. The -ServerGrouped variants accept a ServerGroupedExportConfig parameter.

Delimited-output rules (CSV + clipboard)#

All CSV and TSV rendering goes through one internal boundary (src/utils/export-delimited.ts โ€” not part of the public API), so the rules below hold for data cells, header rows, group labels and group footers alike:

  • CSV: every field is quoted, including headers; internal quotes doubled per RFC 4180; embedded newlines kept inside the quoted field.

  • TSV: minimal quoting โ€” only values containing a tab, newline or " are quoted, so plain values paste cleanly into text targets.

  • Formula neutralization: a value a spreadsheet would execute gets a leading apostrophe. Always on, no opt-out. The output of formatNumber and the percent formatters is deliberately untouched in every supported locale, so negative amounts export unchanged.

XLSX, JSON and PDF do not use this boundary. XLSX writes typed cells; a string cell is stored as text and is never evaluated as a formula.

Full behaviour, including the measured limits, is in guides/export.md.

Auto-size Utilities#

typescript
import {useAutoSize} from '@workspace/active-grid';
HookDescription
useAutoSize(grid, options)React hook that triggers auto-size on data-changes. Use with autoSizeOnMount for first-render fit.

The two standalone auto-size functions exported in v3 were removed in v4.0.1 โ€” the useAutoSize hook is the canonical entry point. See CHANGELOG.md v4.0.1.

typescript
import {  type AutoSizeMode,  type AutoSizeOnMountOptions,  type AutoSizeOptions,  type AutoSizeResult,  type HeaderMeasureResult,  type MeasureContext,  DEFAULT_AUTO_SIZE_OPTIONS,  DEFAULT_AUTO_SIZE_ON_MOUNT,  getAutoSizeConfig,  normalizeAutoSizeOnMount,} from '@workspace/active-grid';

Three auto-size modes: 'fit-content', 'fit-viewport', 'balanced'.

Hooks#

typescript
import {  useColumnSizingVars,  useKeyboardNavigation,  useAutoSize,  useResponsiveColumns,  type UseResponsiveColumnsOptions,} from '@workspace/active-grid';
HookDescription
useColumnSizingVarsComputes CSS-variable map for column widths from engine state.
useKeyboardNavigationArrow-key navigation + scroll-to-focus. getCellProps returns onMouseDown (a primary press makes the cell active on the press) and onClick; spread both.
useAutoSizeTrigger auto-size on data-changes.
useResponsiveColumnsShow/hide columns based on container width breakpoints.

In most cases <ActiveGrid> handles these internally. They are exported for advanced consumers composing grid behavior outside of <ActiveGrid>.

i18n Typing#

typescript
import {  englishGridTranslator,  useGridTranslation,  useGridLocale,  type GridTranslateFunction,} from '@workspace/active-grid';
typescript
type GridTranslateFunction = (  key: string,  variables?: Record<string, string | number | undefined>,) => string;

Compatible with any i18n library. The translator comes from the grid's t prop, else the nearest <GridDefaults t> (see columns.md ยง GridDefaults); with neither, the grid throws a configuration error. A translator is authoritative: its answer is the text, including the raw key it returns for a key it does not carry, and the grid never falls back per key (ADR-034, ADR-045).

englishGridTranslator is the package's English translator and the key contract of every grid text (src/i18n/english-grid-translator.ts). A standalone host, a fixture or an example passes it on purpose (t={englishGridTranslator}); the grid never installs it itself. A key outside its table returns the raw key.

useGridTranslation() and useGridLocale() return the EFFECTIVE translator and presentation of the enclosing grid, including an instance override, for a custom cell or editor that renders grid-owned words or formats a value. Outside a grid both throw a configuration error; there is no default presentation (active-i18n D14).

Utility Exports#

typescript
import {  serializeDecimals,  clearAllGridStates,  resolveColumnBorderMode,} from '@workspace/active-grid';
FunctionDescription
serializeDecimals(rows)Convert Prisma Decimal instances to plain numbers/strings for client transit.
clearAllGridStates()Remove all active_grid_state_* keys from localStorage (e.g. on logout).
resolveColumnBorderMode(...)Internal helper for showColumnBorders (rarely needed by consumers).

Type Re-exports#

Grid objects and state slices#

The objects a consumer receives in renderers and callbacks, and the state slices it persists or controls, are declared by the package itself. They describe what a consumer may rely on and nothing about how the grid computes it.

typescript
import {  type Grid,  type Row,  type Column,  type Cell,  type Header,  type HeaderGroup,  type CellContext,  type HeaderContext,  type SortingState,  type ColumnFiltersState,  type GroupingState,  type SourceRow,  type GeneratedRow,  type SourceCell,  type GeneratedCell,  type GeneratedCellContext,} from '@workspace/active-grid';
TypeCarries
Grid<TData>row models, column and header accessors, state setters and per-slice resets
Row<TData>a SourceRow or a GeneratedRow; getIsGrouped() tells which and narrows
SourceRow<TData>a row that stands for a record: identity, hierarchy, values, cells, state, original
GeneratedRow<TData>a row grouping generated: the same members, no record, groupingColumnId/groupingValue
Column<TData, TValue>definition, accessor, visibility, pinning, sizing, sorting, filtering, grouping, facets
Cell<TData, TValue>a SourceCell or a GeneratedCell: the row and column it pairs, its value, predicates
Header<TData, TValue>, HeaderGroup<TData>the header hierarchy, column spans, sizes and the resize handler; no row span
CellContext / HeaderContextwhat a cell / header / footer renderer receives
GeneratedCellContextwhat an aggregatedCell renderer receives: a generated row, which has no record

A row a grid hands out stands for a record, or grouping generated it. Only a source row carries a record, and the types say so: row.original is TData on a SourceRow and undefined on a GeneratedRow. Code that can meet either narrows first:

typescript
sortFn: (left, right) =>  left.getIsGrouped() || right.getIsGrouped()    ? 0    : left.original.name.localeCompare(right.original.name, locale),

Where only source rows arrive the record needs no narrowing: a filter function receives a SourceRow, an aggregation's leaf rows are SourceRows, and a body cell renderer's CellContext carries a SourceRow. A comparator, an eligibility predicate (enableRowSelection), renderDetailPanel, getRowCanExpand and a row model can hand over a generated row.

Column, Cell, Header and the two contexts carry the column's value type: a renderer of a numeric column reads a number from getValue() without an annotation. Row.getValue(columnId) names a column by id, so the caller states the type (row.getValue<number>('amount')).

A collection the grid hands out (getRowModel().rows, getAllColumns(), row.getVisibleCells(), โ€ฆ) is read-only: build a new array to arrange it differently. Every slice reset takes an optional flag: resetSorting() restores the initial state, resetSorting(true) the grid's own default.

row.getLeafRows() returns every descendant of the row in preorder, interior rows included, not only rows without children. Identifiers are not policed for uniqueness: two records (or two columns) with the same identifier are both presented, and a lookup by that identifier answers the last one. Supply unique identifiers when uniqueness matters.

Row models and selection. Within one row model a logical row is one object: the rows a model presents, its flat rows and its lookup hand back the same object, and a flat row list holds each row once. Each selected-row model projects the model it names: getFilteredSelectedRowModel() holds only selected rows the filter kept, getGroupedSelectedRowModel() only rows the grouped presentation carries, and getPageSelectedRowModel() the selected rows of the current page.

Pinning and positions. A pinned row or column that no longer exists is ignored, so restored state may name rows that have gone. A pinned row the reader folded away, inside a collapsed row or group, is not presented. getPinnedIndex() answers -1 when not pinned and otherwise the zero-based position within the pinned section, for a row and a column alike. column.getIndex(position?) counts within one section: the column's own without an argument, the named one with it, and -1 for a column not laid out there. Offsets (column.getStart(), column.getAfter(), header.getStart()) are measured within the column's own section, because a pinned section does not scroll with the centre. A header has column spans only: a shallower column is carried up by placeholder headers.

Group rows and facets. A group row's id is its grouping path; when another row already carries that id, the smallest free suffix #2, #3, โ€ฆ is appended, so a group row never shadows a source row. column.getFacetedMinMaxValues() counts present finite numbers only and answers nothing when there are none. The grid has no global faceting: getGlobalFacetedRowModel(), getGlobalFacetedUniqueValues() and getGlobalFacetedMinMaxValues() throw instead of answering an empty result. getSortFn(), getFilterFn() and getAggregationFn() answer the function the grid actually applies. grid.getRow(id) for an id the grid does not carry throws an error naming the id, in every environment.

Other re-exported types#

typescript
import {  type GroupAggregation,  type GroupByColumn,  type GroupInfo,  type ServerGroupRow,  type ServerLoadMoreRow,  isServerGroupRow,  isServerLoadMoreRow,  type CellPosition,  type ColumnBorderMode,  type ColumnFilterType,  type FilterComponentProps,  type FacetOption,  type HeaderRendererProps,  type GridExportFormat,  type GridExportOptions,  type GridExportScope,  type GridMeta,  type GridQueryRequest,} from '@workspace/active-grid';

Editing types#

typescript
import {  type EditMode,  type EditingCommitPayload,  type BatchCommitPayload,  type DirtyCellEntry,  type CellPosition,  type CellRange,  type CellSelectionState,} from '@workspace/active-grid';
SymbolTypeDescription
EditMode'cell' | 'row' | 'batch'The three editing modes. Set via the editMode prop on <ActiveGrid>.
EditingCommitPayload<TData>{ rowId; originalRow; changes; newData }Argument to onDataCommit. Contains the diff and the merged row.
BatchCommitPayload<TData>{ changes: EditingCommitPayload<TData>[]; totalCellChanges: number }Argument to onBatchCommit (batch-mode). All pending edits in one payload.
DirtyCellEntry{ rowId; columnId; originalValue; currentValue; sequence; error? }One pending change in batch mode. Used by the internal batch-toolbar and dirty-tracking helpers.
CellPosition{ rowIndex: number; colIndex: number }Identifies a cell by numeric row and column indices.
CellRange{ startRow; startCol; endRow; endCol }A rectangular cell-range. Used by enableCellSelection.
CellSelectionState{ ranges: CellRange[]; activeRange: CellRange | null }The grid's internal selection shape. No callback receives it: onCellSelectionChange gets CellRange[].

This page as Markdown