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);| Before | After |
|---|---|
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 / getEditState | edit.start / edit.updateValue / edit.commit / edit.cancel / edit.getState |
startPeek / stopPeek / getPeekState | the 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, closeFilterPopup | the 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.setStateaccepts identity-keyed updates such as{ columnId: "city", width: 240 };hiddenandpinnedfollow the same shape.- Replacing
columnsreconciles by id in one batch and keeps the core instance: retained columns keep their user state, sort and filter; removed ones drop theirs. A definitionwidthorhiddenchange applies only while the column has no user override for that property. columns.set,columns.move,columns.setPinned,columns.setStateandcolumns.resetStatereturn aColumnSchemaResult({ status: "applied" | "unchanged" }or{ status: "rejected", error }) instead ofvoid; an object implementingGridColumnsApimust return one.ColumnStateSnapshot.widthis present only while an explicit pixel override exists;resolvedWidthis the displayed width (0while hidden) and is output-only. Restoring a saved snapshot after further edits needsresetState()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, andviewIndex.ColumnMovedEvent:columnId,fromViewIndex, andtoViewIndex.RowDragEndEvent:rowId,fromViewIndex, andtoViewIndex.CellValueChangedEventgainedcolumnId.- 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 inclusivegetVisibleRowRange(). - ARIA rows number the header first in every wrapper: the flat header row is
aria-rowindex="1", bandbisb + 1, a body row is its view index plus the header row count plus 1, andaria-rowcountincludes the header rows. Headers and cells carryaria-colindex; the root carriesrole="grid"witharia-colcountandaria-rowcount, androle="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 (fitis still the default and expands proportionally, never shrinks);columns.setLayout(mode)changes it at runtime.- An explicit
columnStateorcolumnLayoutinput seeds the first render, so the server render and the first client frame agree. The unmeasured viewport width is0until a wrapper reports one. - A custom adapter renders
state.columnWindow(start,center,end, each in its own region container, positioned byregionOffset) instead of everystate.layout.columnsentry, and keys cells and headers bycolumnId. DragState.lineXanddropIndicatorXare viewport x; an adapter no longer subtractsscrollLeft.ContainerBoundsdescribes the client box with an inline-start-relativescrollLeftand an optionalrtl; build it withreadContainerBounds(element).InputEventAdapter.wheel(event, dampening)takes theWheelEvent;DragState.rowResizeis required (nulloutside a row drag).
import { readContainerBounds, toPointerEventData } from "@gp-grid/core";
core.input.handleDragMove(toPointerEventData(event), readContainerBounds(bodyElement));Row sizing
MOVE_SLOTcarries a requiredheight;SlotData.heightandSlotState.heightare 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 ofslot.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 bySlotData.region, never byrowIndex < 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 onkindbefore readingrecord.GridState.hierarchicalis required (falsewhile flat) for a consumer that builds aGridStateby hand.- Group and total rows are read-only:
rows.getDataisundefinedon them,edit.startreturnsfalse, a paste or fill over them reportsCellWriteRejectedEvent.reason: "not-a-record", androwDrag.commitunder 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 stores60000; a draft that cannot be converted writes nothing and reportsonWriteRejectedwithreason: "type-mismatch",operation: "edit". A 0.x grid stored the draft string. - A paste reports the targets it skipped (
skipped,reason: "not-editable") throughonWriteCommitted, one event per edit, paste, fill orcells.setValue; undo and redo are opt-in throughhistory: { 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 onceFilters
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.