gp-grid-logo

Core migration to version 1.0

Migrate shared gp-grid APIs from 0.23.x to 1.0, covering GridCore namespaces, column state, geometry, custom adapters, row sizing, writes, filters, and labels.

Use this reference when migrating direct GridCore calls, shared types, or a custom adapter from 0.23.x to 1.0. For component integration, start with the React, Vue, or Angular guide.

Core exports

isSameDay and rawValueKey are no longer exported. Public filter evaluators remain available from @gp-grid/core.

GridCore namespaces

The root of GridCore keeps lifecycle and loading only: initialize, destroy, onBatchInstruction, setViewport, setDataSource, refresh, refreshFromTransaction, plus the geometry, selection, fill, input, highlight and sortFilter members. Everything else moved into a namespace, with no forwarders. The same object is what a wrapper ref exposes as core.

// before
core.getRowCount(); core.setCellValue(row, col, value); core.startEdit(row, col);
// after
core.rows.getCount(); core.cells.setValue(row, col, value); core.edit.start(row, col);
BeforeAfter
getRowCount()rows.getCount()
getRowId(i)rows.getId(i)
getRowData(i)rows.getData(i)
hasRow(i)rows.has(i)
getViewRow(i)rows.getViewRow(i)
getRecordById(id)rows.getRecordById(id)
isWritable()rows.isWritable()
getSlotGeneration(i) / isSlotGenerationCurrent(i, g) / refreshSlotData()the same names on rows
getCellValue(row, col)cells.getValue(row, col)
setCellValue(row, col, value)cells.setValue(row, col, value)
getFieldValue(i, field)cells.getFieldValue(i, field)
getCellBounds(rowId, columnId, space?)cells.getBounds(rowId, columnId, space?)
startEdit / updateEditValue / commitEdit / cancelEdit / getEditStateedit.start / edit.updateValue / edit.commit / edit.cancel / edit.getState
startPeek / stopPeek / getPeekStatethe same names on edit
pasteClipboardText(text)edit.paste(text)
getColumns() / setColumns(columns)columns.get() / columns.set(columns)
setColumnWidth(i, width) / moveColumn(from, to) / setColumnPinned(id, pin)columns.setWidth / columns.move / columns.setPinned
getColumnState() / setColumnState(u) / resetColumnState(ids?)columns.getState() / columns.setState(u) / columns.resetState(ids?)
setColumnLayout(mode)columns.setLayout(mode)
setFreezeRows(config?) / freezeRowsThrough(i) / getFrozenRows()frozenRows.set / frozenRows.freezeThrough / frozenRows.get
commitRowDrag(from, to) / isRowDragEntireRow()rowDrag.commit / rowDrag.isEntireRow
setSort, setFilter, getSortModel, getFilterModel, hasActiveFilter, openFilterPopup, closeFilterPopupthe same names on sortFilter
setScrollTopOverride(top) / isScalingActive()viewport.setTopOverride / viewport.isScaling
getScrollRatio() / getMaxFlingVelocity() / getRowHeight()the same names on viewport
getTotalWidth()geometry.getColumnLayout().totalWidth
getTotalHeight()geometry.getContentSize().height (header excluded)
getColumnPositions()geometry.getColumnLayout().columns[i].offset
getVisibleRowRange() (inclusive)geometry.getVisibleRowWindow() (half-open)
getHeaderHeight()removed; the header height is the headerHeight option

sortFilter.setSort, setFilter and openFilterPopup ignore calls while a load is in flight, as the removed root copies did.

Column definitions and column state

Definitions are immutable input. A resize, a move, a hide or a pin never mutates the caller's objects; the live width, order, visibility and pin per column live in the core, keyed by column id (colId ?? field), and come back through columns.getState().

// before: the grid mutated the caller's definition
columns[2].width = newWidth;
// after: an override by id, reset to return to the definition
core.columns.setState([{ columnId: "city", width: 240 }]);
core.columns.resetState(["city"]);
core.columns.getState(); // [{ columnId: "city", resolvedWidth: 317, ... }]
  • columns.setState accepts identity-keyed updates such as { columnId: "city", width: 240 }; hidden and pinned follow the same shape.
  • Replacing columns reconciles by id in one batch and keeps the core instance: retained columns keep their user state, sort and filter; removed ones drop theirs. A definition width or hidden change applies only while the column has no user override for that property.
  • columns.set, columns.move, columns.setPinned, columns.setState and columns.resetState return a ColumnSchemaResult ({ status: "applied" | "unchanged" } or { status: "rejected", error }) instead of void; an object implementing GridColumnsApi must return one.
  • ColumnStateSnapshot.width is present only while an explicit pixel override exists; resolvedWidth is the displayed width (0 while hidden) and is output-only. Restoring a saved snapshot after further edits needs resetState() first.
  • A declared or overridden width that is not positive and finite falls back to 50 px and warns once per column id.

Interaction events

Column and row interaction callbacks receive one identity-bearing object. See the migration guide for your wrapper for its callback syntax.

  • ColumnResizedEvent: columnId, width, and viewIndex.
  • ColumnMovedEvent: columnId, fromViewIndex, and toViewIndex.
  • RowDragEndEvent: rowId, fromViewIndex, and toViewIndex.
  • CellValueChangedEvent gained columnId.
  • Edit callbacks carry an editId; callbacks from a closed editor cannot act on a newer edit.

Indices, counts and ARIA rows

  • Row and column windows use exclusive end indices: geometry.getVisibleRowWindow() replaces the inclusive getVisibleRowRange().
  • ARIA rows number the header first in every wrapper: the flat header row is aria-rowindex="1", band b is b + 1, a body row is its view index plus the header row count plus 1, and aria-rowcount includes the header rows. Headers and cells carry aria-colindex; the root carries role="grid" with aria-colcount and aria-rowcount, and role="treegrid" while a hierarchy is bound.
  • Under a hierarchy rows.getCount() counts view rows (groups and totals included).

Geometry is core-owned

Widths, offsets, row bounds and hit testing are resolved once in core and published; a wrapper or custom adapter renders what it is given and never recomputes.

Removed: calculateScaledColumnPositions, ColumnScrollGeometry, VisibleColumnInfo, InputHandlerDeps, InputHandler.updateDeps, GridCore.getRowTranslateY, getScrollTopForRow, getRowIndexAtDisplayY, getColumnPositions, getVisibleRowRange, computeStoredWidthForDisplayed.

// before
const { positions, widths } = calculateScaledColumnPositions(columns, viewportWidth);
core.input.updateDeps({ getColumnPositions: () => positions });
// after: render the published layout, query the geometry
for (const column of state.layout.columns) {
  cell.style.insetInlineStart = `${column.offset}px`;
  cell.style.width = `${column.width}px`;
}
core.geometry.getCellBounds(rowIndex, layoutIndex, "viewport");
core.geometry.hitTest({ x, y });
core.geometry.getScrollTarget(row, col);
  • columnLayout: "fit" | "fixed" selects the width mode (fit is still the default and expands proportionally, never shrinks); columns.setLayout(mode) changes it at runtime.
  • An explicit columnState or columnLayout input seeds the first render, so the server render and the first client frame agree. The unmeasured viewport width is 0 until a wrapper reports one.
  • A custom adapter renders state.columnWindow (start, center, end, each in its own region container, positioned by regionOffset) instead of every state.layout.columns entry, and keys cells and headers by columnId.
  • DragState.lineX and dropIndicatorX are viewport x; an adapter no longer subtracts scrollLeft.
  • ContainerBounds describes the client box with an inline-start-relative scrollLeft and an optional rtl; build it with readContainerBounds(element).
  • InputEventAdapter.wheel(event, dampening) takes the WheelEvent; DragState.rowResize is required (null outside a row drag).
import { readContainerBounds, toPointerEventData } from "@gp-grid/core";
core.input.handleDragMove(toPointerEventData(event), readContainerBounds(bodyElement));

Row sizing

  • MOVE_SLOT carries a required height; SlotData.height and SlotState.height are required. A consumer that builds those objects by hand sets them.
  • Wrappers write no inline cell height: a cell fills its row through the shipped .gp-grid-cell { height: 100% } rule and the row box keeps the pixel height of slot.height. A custom stylesheet that sized cells itself can drop that.
  • A row's height is set by the application (core.rowHeights), by a row resize or by a one-shot fit; the grid never sizes a row from its content.

Frozen rows and the header

  • frozenRows.set({ count }) freezes the first displayed rows under the fixed header; frozenRows.get() reports the effective count. The header never scrolls.
  • Frozen rows render in their own block and are published in state.rowRegions; a custom adapter identifies a frozen slot by SlotData.region, never by rowIndex < frozenCount.
  • isRowVisible(row, range, frozenCount = 0) treats the frozen prefix as always visible; existing two-argument callers keep their behavior.

Labels

GridLabels gained required fields: pinLeftColumn, pinRightColumn, unpinColumn, frozenRowsLimited, columnSchemaErrors (GridColumnSchemaErrorLabels) and rowGroups (GridRowGroupLabels, with loadFailed). A full GridLabels literal must include them. GridLabelOverrides stays partial and merges the nested objects one level deep, so an application that overrides a few labels changes nothing.

// a partial override is unchanged
const labels: GridLabelOverrides = { rowGroups: { grandTotal: "Totale" } };

Hierarchies, groups and pivots

  • ViewRow<TData> is a union: a record row (kind: "record", id, depth, viewIndex, record?) or a group or total row. A flat row is a record row of depth 0; narrow on kind before reading record.
  • GridState.hierarchical is required (false while flat) for a consumer that builds a GridState by hand.
  • Group and total rows are read-only: rows.getData is undefined on them, edit.start returns false, a paste or fill over them reports CellWriteRejectedEvent.reason: "not-a-record", and rowDrag.commit under a hierarchy reports "derived-view". No row drag starts while a hierarchy is bound.
const row = core.rows.getViewRow(i);
if (row?.kind === "record") use(row.record);

Writes, editor commits and history

  • An editor commit is coerced by the column's cellDataType, like a paste: "60000" on a number column stores 60000; a draft that cannot be converted writes nothing and reports onWriteRejected with reason: "type-mismatch", operation: "edit". A 0.x grid stored the draft string.
  • A paste reports the targets it skipped (skipped, reason: "not-editable") through onWriteCommitted, one event per edit, paste, fill or cells.setValue; undo and redo are opt-in through history: { maxEntries }.
  • A columnar source stays read-only: every write is refused and reported with reason: "read-only-source".

Transactions and refresh

refreshFromTransaction(result?) takes the transaction result the mutable source reports. A batch made only of updates redraws the rows that changed without a query, so a row whose sorted or filtered field changed keeps its place until the next query; call refresh() to re-sort now. Adds, removes and clear() reload as before. The wrappers pass the result themselves.

source.updateCell(id, "amount", 99);
await source.flushTransactions();
await core.refresh(); // only if the row must move to its sorted place at once

Filters

The grouped filter model (groups, one level of combination) is unchanged from 0.23. The flat 0.x shape (conditions with nextOperator) is still accepted by sortFilter.setFilter and evaluateColumnFilter and normalized on entry; sortFilter.getFilterModel() and DataSourceRequest.filter always return the grouped shape. normalizeColumnFilterModel(legacy) converts a stored filter explicitly.

Server rendering

Import and construction touch no browser global. Supply initial viewport sizes when rendering on the server. Configured headerHeight and band heights determine server sizes; client measurements refine them after mount.

Browsers and keyboard

The release qualification covers React, Vue, and Angular on Chrome, Firefox 144, and WebKit 26. All documented cell and group keys work, including resize, move, fit, editing, selection, clipboard, and history shortcuts. Sort, filter-popup, and pin controls are pointer-only header targets in 1.0. Header keyboard focus remains unsupported.

Source: the gp-grid 1.0 migration guide.

On this page