Repository navigation
v2.0.0
Major Changes
-
238470cThanks @Psvensso! - Breaking. The"batch"edit mode is now"draft", reworked: a held draft
renders its value through the column's owncellrenderer (incellConfirm
too), an entered new row stays in the grid as a value row until Save all, and
the edit lane becomes the change indicator and per-row revert - a state icon
(new/edited/deleted) beside Revert, Restore or remove. Nothing reaches a
callback beforeedit.submitAll(): the lane's per-row save and
Delete-to-clear no longer commit under draft mode.Before After editing.mode: "batch"editing.mode: "draft"editing.onCommitBatchediting.onCommitDraftsTMDataGridEditCommitBatchArgsTMDataGridEditCommitDraftsArgsedit.state.newRows[]{ tempId }{ tempId, confirmed }New:
editing.newRowsSticky(entered rows scroll by default), row
data-dirty, entry-rowdata-new/data-confirmed, partsrow-state/
revert-row,edit.state.rows[id].values, labelsrevertRow/
rowStateNew/rowStateEdited/rowStateDeleted,--dg-row-new-bg.
The edit lane now appears under draft mode withoutonCommitDrafts.Fixed: Restore on a deletion-marked row was unclickable in real browsers
(pointer-events). -
#44
feba241Thanks @Psvensso! - Breaking.editing.mode: "draft"is removed.editinghas two axes:
mode("cell" | "cellConfirm" | "row") picks what counts as a commit, and
draft: trueparks commits in the draft store foredit.saveDrafts().
Closes #43.- Migrate
{ mode: "draft" }to{ mode: "row", draft: true }- or pair
draftwith any other mode. TMDataGridEditModenarrows to the three modes,TMDataGridFeatureFlags
gainseditDraft, and commit args never carrysource: "draft".- Leaving a cell commits under
"cell"(parks underdraft),"cellConfirm"
no longer commits on Tab, and leaving an entry row never commits it. TMDataGrid.EditActionsis renamedTMDataGrid.DraftActions, with the
TMDataGridDraftActions*exports renamed to match.- Fixed: a row that fails validation on the way out keeps its error marks
until the failing value changes;edit.state.rows[id].errorMessages
carries the texts.
- Migrate
-
c662c8aThanks @Psvensso! - The editing options ofuseTMDataGridare namespaced under oneediting
object, matching the 2.0meta.editcolumn namespacing.editMode: Xbecomesediting: { mode: X }onEditCommitbecomesediting.onCommitonEditCommitBatchbecomesediting.onCommitBatchrowValidators,isRowEditable,newRowDefaults,onRowAddand
onRowDeletekeep their names and move insideeditinggetRowIdstays top-level and is still required onceeditingis set
TMDataGridEditingOptionsis now the type of theeditingobject itself.
TMDataGridEditMode, theeditengine and the feature flags are unchanged. -
#133
8cabb3dThanks @Psvensso! - Per-row results from the batch edit calls:edit.commitAll()resolves{ ok, committed, open }.edit.saveDrafts()resolves{ ok, saved, kept, reopened }; a check likeif (await grid.edit.saveDrafts())is now always true - read.ok.addRows' result gainsok.- The
onSaveDraftsreturn type is renamedTMDataGridSaveDraftsResponse;TMDataGridSaveDraftsResultis now the typesaveDrafts()resolves. - Without
onSaveDrafts, a deletion whoseonRowDeletethrows keeps its mark and is reported inkept;saveDrafts()no longer rejects and drops the mark.
-
#37
d989de1Thanks @Psvensso! - Breaking. Draft mode gets a real draft store, and the verbs are split to
match: a row is open (undecided form state) until it is committed, and only
committed rows are saved.edit.commitAll()submits every open row;edit.saveDrafts()sends the
draft store.edit.submitAll()is deprecated and now does both in turn -
what it always did in effect.editing.onCommitDraftsis renamedediting.onSaveDrafts. The old name is
still honoured; the new one wins if both are set.edit.addRows(rows, { commit })adds a batch in one write.commit: true
submits each row as it lands, which is the import case: valid rows commit,
invalid ones stay open carrying their errors, and the result says which went
which way.newRows[].confirmedis nownewRows[].committed, and the entry row's
data-confirmedattribute isdata-committed.edit.stategains
committedRowIds.TMDataGridEditCommitDraftsArgsis renamedTMDataGridSaveDraftsArgs, with
the old name kept as a deprecated alias.
Two behaviour changes to know about:
Save no longer sweeps rows that were never OK'd. It sends the draft store
and leaves open rows alone - they keep what was typed and stay open for the
next save.EditActionscounts the draft store on Save and shows how many rows
are still open beside it. Enter in draft mode now commits the row instead of
only closing the editor, so the ordinary keyboard flow still fills the store.
Calledit.commitAll()before saving to get the old sweep.Column validation no longer depends on a mounted editor.
meta.edit.validate
ran on the editor, so a commit with no editor on screen - an import, a
programmatic commit, Delete-to-clear on a cell that was never opened - skipped
it and could write past the rule. The engine now runs the column rules itself
at commit. Existing grids may see commits refused that previously went through;
those were the rule being bypassed. -
#138
482db6cThanks @Psvensso! - These internal helpers are no longer exported:isSameCell,resolveCellMove,ResolveCellMoveArgs,TMDataGridCellCoords,TMDataGridCellNav,boundsCellCount,boundsEdges,isWithinBounds,getDefaultOperator,getColumnFilterControl,isColumnEditableForRow,isColumnReorderable,measureColumnContentWidth,TMDataGridColumnLayoutandtmDataGridFeatures(theTMDataGridFeaturestype stays).
The replacements are listed in the 2.0 migration guide.
Every remaining export is documented. -
#130
1dbfbd1Thanks @Psvensso! - The deprecated API surface is removed:cellExportonTMDataGrid.Table- useexportOptionsonuseTMDataGrid.exportGridToCsv- useexportGrid.TMDataGridCellExportOptions,DEFAULT_CELL_EXPORT_OPTIONS,fromCellExportOptions- useTMDataGridExportOptionsandDEFAULT_EXPORT_OPTIONS.buildCellMatrix,buildGridCellMatrix,BuildCellMatrixArgs,TMDataGridCellMatrix- usebuildExportData,BuildExportDataArgsandTMDataGridExportData.toExcelCsv- usecsvExcelFormat.downloadTextFile- usedownloadFile.labels.exportCsv- uselabels.exportCells.editing.onCommitDrafts- useediting.onSaveDrafts.rowsandaddedin theonSaveDraftspayload - useupdatedandcreated.TMDataGridEditCommitDraftsArgs- useTMDataGridSaveDraftsArgs.edit.submitAll()- useedit.commitAll()thenedit.saveDrafts().state.pendingCountinTMDataGrid.DraftActions- usestate.draftCountorstate.openCount.
-
#71
77b0819Thanks @Psvensso! - TanStack Table 9.2.4: the peer dependencies@tanstack/react-tableand@tanstack/table-coremove from^9.0.0-beta.21to^9.2.4, and column pinning follows TanStack's rename from physical to logical sides.Upgrading:
- Install
@tanstack/react-table@^9.2.4and@tanstack/table-core@^9.2.4. The@tanstack/storeand@tanstack/react-storepeers move to^0.11.1, the range table-core requires; a second store copy at 0.11.0 stops the grid from re-rendering on external atoms. columnPinningis{ start, end }instead of{ left, right }: ininitialState, in a controlledstate.columnPinningand itsonColumnPinningChange, and wherevertable.store.state.columnPinningis read.column.pin("left" | "right")iscolumn.pin("start" | "end"),column.getIsPinned()returns"start","end"orfalse, and the table'sgetLeft*/getRight*methods aregetStart*/getEnd*. TanStack lists every rename innode_modules/@tanstack/table-core/skills/migrate-v8-to-v9/SKILL.md, section 5.getColumnRegionreturns"start" | "center" | "end", andTMDataGridColumnRegionmatches.- Settings saved under
settingsKeyby 1.x with the oldleft/rightkeys are read and migrated; nothing to do. - A custom
aggregationFnon a column definition is now aconstructAggregationFn({ aggregate })definition rather than a bare function. The named built-ins ("sum","min", ...) andTMDataGridAggregationNameare unchanged. - The menu labels, the
data-pinnedcell attribute and the sticky CSS keepleft/right: they name the physical side.
- Install
-
#131
2e57099Thanks @Psvensso! -meta.optionsandmeta.edit.enabledcallbacks receive rows typed asTDatawhen the column is built withcreateTMDataGridColumnHelper<TData>();row.originalneeds no cast. A callback annotated with the untyped row type no longer compiles there - drop the annotation.TMDataGridColumnMeta,TMDataGridOptionsSource,TMDataGridOptionsArgsandTMDataGridColumnEditOptionstake an optionalTData, and the helper type is exported asTMDataGridColumnHelper. -
#61
9425688Thanks @Psvensso! - Breaking. The filter panel is no longer welded toTMDataGrid.Table. A new
filtersoption picks the surface:surface: "popup"(the default) floats the panel over the rows, as before.surface: "sidebar"puts it beside them, inside the grid frame. Starts open,
and takessidebarSideandsidebarWidth.surface: "none"renders no panel and noFilterButton, leaving a
hand-placedTMDataGrid.FilterPanelas the only one on the page.inHeader: trueadds a header row of per-column value controls, each with a
funnel button for its operator. Independent ofsurface. The column menu's
Filter item and the filtered-header funnel come off with it.
TMDataGrid.FilterPanelis now a plain block of controls with no title, no
close button and no open state - the popup and the sidebar own that chrome. It
takeslayout="row" | "stacked", and passes that through to every value
control.What breaks:
TMDataGrid.FilterPanelrenders whenever it is mounted. It used to hide
itself unlessui.state.filterPanelOpen, so a hand-placed one now shows
permanently, and shows alongside the popup unless you also set
surface: "none".filter-panel-closemoved out offilter-panel; it is now a child of
filter-popup/filter-sidebar. A test scoping the close button inside the
panel has to be re-pointed.TMDataGridLabelsgained a requiredfilterOperatorFor. A complete
translation typed asTMDataGridLabelsno longer compiles until it is added;
TMDataGridLabelsOverrideis unaffected.TMDataGridApigained a requiredfilters, andTMDataGridFilterControlArgs
a requiredlayout- both break code that builds one of these by hand rather
than spreading, which in practice means test doubles.ui.state.filterPanelColumnIdis now the panel's alone; the header row reads
the newheaderFilterColumnId. The actions arefocusPanelFilterand
focusHeaderFilter.
New exports:
TMDataGridFiltersOptions,TMDataGridFiltersSettings,
TMDataGridFilterSurface,TMDataGridFilterSidebarSide,
TMDataGridFilterControlLayout,TMDataGridFilterPanelLayout,
TMDataGridFilterPanelProps,TMDataGridFilterValueShapeand
filterValueShape.Also fixed: a header group now spans the columns under it instead of sitting in
one track, and stacked header rows no longer pin to the same edge and paint
over each other. -
#22
b2110d4Thanks @Psvensso! - Breaking. Column meta groups its editing and filtering fields into two
namespaces,meta.editandmeta.filter, named after theeditengine and the
filter panel they configure. A newmeta.edit.mapValuemaps a value on its way
into the draft, and the deprecatedautoFocuson the editor contract is gone.Column meta had grown flat across four concerns at once, so a field name had to
carry its own stage:editableandfilterControlsat besidelabeland
alignwith nothing but the prefix to say which part of the grid read them.
Grouping them puts what a column is at the top level and what a stage
does with it inside that stage's namespace, and it mirrors the runtime API,
where editing has beenedit.begin()/edit.commit()/edit.storeall along.Before After meta.editablemeta.edit.enabledmeta.editFieldmeta.edit.fieldmeta.editormeta.edit.editormeta.validatemeta.edit.validatemeta.filterControlmeta.filter.controlmeta.defaultFilterOperatormeta.filter.defaultOperator// Before meta: { type: "number", defaultFilterOperator: "between", editable: false } // After meta: { type: "number", filter: { defaultOperator: "between" }, edit: { enabled: false }, }
meta.typeandmeta.optionsstay at the top level: one declaration of each
feeds the filter panel and the cell editor alike, and moving either into a
namespace would mean declaring it twice. Every old field is a compile error
after the upgrade, sotscnames each site to change.meta.edit.mapValuemaps every value an editor writes, before it reaches
the draft: uppercase a code, strip spaces from an IBAN, clamp a number into
range. It runs per write, so a text input maps per keystroke and a select per
pick, and what it returns is what the cell shows, what the validators judge and
what commits.meta: { edit: { mapValue: ({ value }) => typeof value === "string" ? value.toUpperCase() : value, }, }
The map is applied in the editor host, around the field every editor writes
through, so one declaration covers the six built-in editors, a custom
meta.edit.editor, and the character that opened the editor when typing started
the edit. The value an editor opens with is deliberately not mapped, since that
would rewrite stored data nobody edited and swallow the select-all that lets the
first keystroke replace the value; neither isedit.clearCell(), which writes
the type's empty value through the form rather than through an editor. The
built-in string and number editors keep the caret where it was typed across a
mapped write, which a hand-rolled editor previously had to solve for itself.TMDataGridEditorArgs.autoFocusis removed, as 1.1.1 said it would be. The
grid has placed the caret itself since then, so an editor that ignored the prop
already behaved correctly and one that honoured it loses nothing. A row opened
byedit.addRow()now gets the same treatment: its caret lands in the first
editable cell whether that cell holds a built-in editor or your own, which the
oldautoFocuspath only managed for the built-ins.New exported types:
TMDataGridColumnEditOptions,TMDataGridColumnFilterOptions,
TMDataGridEditValueMapandTMDataGridEditValueMapArgs. New exported readers:
getColumnFilterControlandisColumnEditableForRow.
Minor Changes
-
#50
e36c7c7Thanks @Psvensso! - Programmatic edits and a column allowlist.edit.setCellValue(rowId, columnId, value)andedit.setRowValues(rowId, values)write through the edit engine, so a toolbar action or bulk fill lands in the draft store as a typed edit does - change markers, per-row revert and all. Rows need not be mounted.meta.edit.validateruns;meta.edit.mapValuedoes not, as withclearCell.editing.columnsnames the columns that take edits, instead of switching every other column off withmeta.edit.enabled: false. Unset, every column mapping to a data path stays editable.edit.isColumnEditable(column)answers the column's half of the rule with no row in hand.aggregateColumnreads the filtered model'sflatRows, so a tree built withgetSubRowstotals its children instead of only its roots. Flat and grouped grids are unchanged.- The
numbereditor no longer writesNaNwhen the text is not yet a number - partial input such as-or1eleaves the field empty and stays on screen.
-
e71f679Thanks @Psvensso! - Underediting.draft,edit.deleteRowmarks idempotently instead of toggling:
deleting a marked row again leaves it marked, and the newedit.restoreRow(rowId)
is the undo (the lane's Restore now calls it). A deletion mark is also refused
for ids the grid cannot save - an unknown id, or an entry row's temp id named
again after the entry was discarded - which used to inflate the Save count.- New
edit.deleteRows(rowIds)-deleteRowover a list in one call, safe to
feed a selection as it stands. - A cancel or delete racing a pending commit no longer leaves a ghost id in
committedRowIds.
- New
-
#50
e1f413aThanks @Psvensso! -editing.tableValidators- cross-row validation.onSubmit/onSubmitAsyncreceive{ value, rowId, isNew, rows }, whererowsis the collection as it would stand if the commit landed: every draft overlaid, entry rows appended, deletion-marked rows removed.- Same result vocabulary as
rowValidators; pathed issues land on the committing row's cells, pathless ones on the row. - Runs at every commit after the row's own validators, and again per parked row during
saveDrafts, so a draft a later edit invalidated blocks the save.
-
#110
f353e0dThanks @Psvensso! - A committed row is data, not a form: a row that commits into the draft store drops its TanStack Form and is held as a snapshot of its values, so ten thousand imported rows cost a few megabytes instead of about eighty. Reopening a committed row -begin,setCellValue,setRowValues,clearCell- builds a fresh form seeded with the committed values. Closes #81.- Breaking.
edit.getForm(rowId)returnsundefinedfor a committed row. A drawer over one callsbeginfirst. saveDraftsre-runsediting.tableValidatorsonly; column rules androwValidatorsran at commit on the same values. A committed row that fails a table rule at Save, or whoseonCommit/onRowAddrejects on the per-row path, is reopened with the error instead of staying committed with errors on its form.
- Breaking.
-
#116
713d8faThanks @Psvensso! -edit.state.isSavingistruewhilesaveDraftsis in flight, andTMDataGrid.DraftActions' Save shows it as its loading state.renderActionsreceives it asstate.isSaving. Part of #89. -
#114
795b964Thanks @Psvensso! - The edit engine and the table's own state now stay in step underediting.draft.- A commit no longer resets the page index or collapses open details panels and groups. The grid sets TanStack's
autoResetPageIndexandautoResetExpandedtofalseand resets the page on a query change itself:resetPageOnQueryChangenow defaults totrueon every grid and also covers grouping. - A deletion-marked row is read-only and not selectable until restored:
begin,setCellValue,setRowValuesandclearCellrefuse it, the keyboard cannot open it, its checkbox is disabled, select-all skips it, and the mark drops it fromrowSelection. An editor open on the row is cancelled by the mark; a committed edit stays under it for Restore. saveDraftssends a row that is edited and marked indeletedonly, never inupdatedas well, and the per-row path no longer callsonCommitbeforeonRowDeletefor it. The Save count counts it once.- A refetch that no longer returns a row drops that row's draft, open editor and deletion mark. Not under
manualPaginationormanualFiltering, where a missing row is on another page. - A row the engine takes out of the table leaves
expandedandrowPinningtoo, not onlyrowSelection. - Export leaves deletion-marked rows out.
- A commit no longer resets the page index or collapses open details panels and groups. The grid sets TanStack's
-
#53
b6e2876Thanks @Psvensso! - The body is one tab stop in each direction, bracketed by twotab-guardparts.
A control inside a body cell needs notabIndexof its own.- Inside a row, Tab walks its controls - the open editors, the buttons in its cells and the edit lane's save and cancel - and past the last one the cursor moves to the next row's first cell.
- Breaking.
useCellControlTabIndexis removed. Drop thetabIndexit fed; nothing replaces it. - Pressing a control inside a cell keeps the selected block instead of collapsing it to that cell.
- Scrolling a focused row into view accounts for the sticky header, the pinned rows and the summary row.
-
#70
007c308Thanks @Psvensso! - Export.TMDataGrid.Menu.ExportandTMDataGrid.Menu.ExportSelected- menu items downloading every filtered row, or the selected rows in grid order, in the grid's format. Props override the format, file name, headers and label per item.useTMDataGridExport- the export as click handlers (exportAll,exportSelected,selectedCount,canExportSelected) for a control of your own.exportGrid,buildExportData,writeExportFile- the same export from outside a component.- Formats:
csvExcelFormat(the default, as before),csvFormat,tsvFormat,jsonFormat;TMDataGridExportFormatfor one of your own. exportOptionsonuseTMDataGrid- format, file name, header row andcolumns("visible","all"or ids) for every export, the cell-range menu included.columns="custom"on the menu items opens a column picker: every exportable column, the visible ones ticked and the hidden ones marked, select all with a count, a search box from six columns, Export and Cancel.ui.state.exportPicker,ui.actions.openExportPicker/closeExportPicker,getExportableColumns.- Column meta
enableExportandexportValue. - The text formats prefix a value that a spreadsheet would run as a formula;
escapeFormulas: falseon the format turns it off. data-dg-part:menu-export,menu-export-selected,export-picker,export-picker-hint,export-picker-search,export-column,export-column-all,export-picker-count,export-picker-confirm,export-picker-cancel.- Labels:
exportAll,exportSelected(count),exportPickerTitle(format),exportPickerHint(selected),exportPickerConfirm,exportPickerCancel,exportPickerSelectAll,exportPickerCount(checked, total),exportPickerHidden,exportCells(the cell-range item, wasexportCsv). - Deprecated, removed in the next beta:
cellExportonTMDataGrid.Table,exportGridToCsv,TMDataGridCellExportOptions,DEFAULT_CELL_EXPORT_OPTIONS,buildCellMatrix,buildGridCellMatrix,TMDataGridCellMatrix,toExcelCsv,downloadTextFile,labels.exportCsv. - New package
@jielga/tmdatagrid-xlsx:xlsxFormat()writes an Excel workbook with typed cells, on exceljs.
-
#65
8f69befThanks @Psvensso! -meta.filter.operatorsnarrows the operators a column offers to a subset of
its type's, for columns backed by an endpoint that answers only some of them.
The panel dropdown and the header funnel show only those; a fresh filter opens
on the type's default when it is offered, else on the first offered operator.
New exportgetColumnOperators(column). -
#56
fe68bb6Thanks @Psvensso! - The grid menu.TMDataGrid.Menu- the toolbar burger, a MantineMenufilled with your own items.TMDataGrid.Menu.Columns,.ColumnToggles,.ShowHideAll,.ResetLayout- the column chooser as menu items, for any MantineMenuinside the grid.- Breaking.
TMDataGrid.ColumnsButtonis gone; render<TMDataGrid.Menu><TMDataGrid.Menu.Columns /></TMDataGrid.Menu>.TMDataGrid.ColumnsPanelstays for hosts that are not a menu. - Breaking.
ui.columnsPanelOpen,setColumnsPanelOpenandtoggleColumnsPanelare gone; the header menu's "Manage columns" is a submenu now, and itsinternalItemsentry is aMenu.Sub. data-dg-part:menu-buttonadded,columns-buttondropped.- Labels:
menuButtonadded;columnsResetreads "Reset layout".
-
#53
401dde9Thanks @Psvensso! - New:edit.getRowValues(rowId)andedit.getRows()- rows as shown, drafts overlaid, deletion marks and entry rows flagged rather than filtered. New typeTMDataGridEditRowSnapshot. -
#54
36657fdThanks @Psvensso! - Underediting.draft, committed drafts and committed new rows are the table's
rows: they sort, filter, group and aggregate on their draft values, and
edit.getRows()andediting.tableValidatorsread the same collection.
editing.newRowsStickykeeps committed new rows in the entry block instead.- Body rows publish
data-new; a committed new row is arowpart, no longer
anentry-row, anddata-dg-entry-flow-blockis gone. - New
TMDataGridEditState.committedValues- the draft store's values, kept
across a reopen so the row holds its place. - Row callbacks now receive the draft as
row.original; a new row carries its
temp id.
- Body rows publish
-
#42
3e0c861Thanks @Psvensso! -onSaveDraftscan save part of the draft store. Closes #33.- The payload keys are renamed:
rowsis nowupdated,addedis now
created,deletedis unchanged. The old names are still filled and are
deprecated; they are removed in a later beta. - Returning
{ updated, created, deleted }fromonSaveDraftskeeps the ids
reportedfalseand clears the rest. Each key takesfalsefor the whole
bucket or a map of id to result; an id the map does not name saved. A kept
row stays committed, so the nextsaveDrafts()retries it, and
saveDrafts()resolvesfalsewhen anything was kept. Returning nothing
saves everything and throwing saves nothing, both unchanged. - Body rows and entry rows carry
data-draftwhile committed into the draft
store.data-dirtycontinues to mark any row with values typed in. saveDrafts()called while a save is in flight joins it instead of sending
the same payload again. Previously a double-clicked Save could create every
pending entry row twice.
- The payload keys are renamed:
-
#49
001dd75Thanks @Psvensso! -TMDataGrid.DraftActions'renderActionscan take the user to a row that is
still open. Closes #46.state.openRowIdsis the ids behindopenCount, in the order the grid
opened them.actions.scrollToRowisgrid.scrollToRow, passed through.actions.scrollToFirstOpenRow(align?)scrolls to the first open row in
display order - which need not beopenRowIds[0]- and answers whether one
was reached. An open entry row or a pinned open row answerstruewithout
scrolling.
Controls.OpenRowsNoteis unchanged: it is a label, not a button.Docs: the
DraftActionsslot table listed neitherdraftCount,openCount,
commitAllnorOpenRowsNote, andscrollerRefwas documented as the scroll
container element, which it has never been. -
#28
acd8b0cThanks @Psvensso! - Generated lanes are no longer user settings.- The checkbox and edit lanes are
enableHiding: false, like the other three. - "Manage columns" lists only hideable columns; a column with
enableHiding: falseis left out rather than shown disabled. - Show/hide all writes only the columns it lists. It used
table.toggleAllColumnsVisible, which writes every leaf column: "show all"
published the tree column and "hide all" forced it visible. - A column pinned right lands to the left of the edit lane.
- A stale
columnVisibilityentry for a generated lane - persisted before
this release, or passed ininitialState- is dropped at mount and on
Reset layout, since nothing in the grid could bring the lane back.
New export:
keepGeneratedColumnsOutermost,isGeneratedColumn. - The checkbox and edit lanes are
-
#125
5717faeThanks @Psvensso! - NewhasPendingEdits(state)export:truewhile the grid holds unsaved work (an open row with a changed value, an entry row, the draft store, or a save in flight). Use it asuseSelector(grid.edit.store, hasPendingEdits)to block navigation. -
#59
1b375bbThanks @Psvensso! - Server-side ergonomics, from building the server-backed search recipe.- Under
manualPaginationthe grid takes itself back to the first page when the query changes - a column filter, the quick search or the sort - in the same event as the change, so one request goes out. A filter row with an empty value is not a query change.resetPageOnQueryChange: falseswitches it off. activeColumnFilters(columnFilters | table)returns the filters that narrow anything, withvaluetyped asTMDataGridFilterValueinstead ofunknown.Controls.PageNumberrenders "Page 3 of 200" for arenderPaginationlayout, with apageNumberlabel. Not in the default footer.SummaryCountrenders the matched count alone undermanualFilteringormanualPaginationwithoutmeta.totalRowCount. The fallback denominator was the current page, so a server-side grid read "25 / 25".- The controlled-state sync no longer publishes the table store during the consumer's render, which React reported as "Cannot update a component (…) while rendering a different component (…)" on the first sort or filter of any grid owning a state slice. Subscribers to
table.storeare now notified in a microtask after that render pass - the same retiming applies to the grid's own grouping, persistence and resize-preview subscriptions. - The last column's resize divider no longer hangs 5px past the last track, which put a permanent horizontal scrollbar under a grid whose columns fit.
- A column resolving
meta.options: "faceted"undermanualFilteringormanualPaginationwarns once: the distinct values of one page are not the distinct values of the result set.
- Under
-
#123
8e20062Thanks @Psvensso! - Boolean state attributes are present only while they apply.data-selected,data-highlighted,data-grouped,data-deleted,data-dirty,data-draft,data-newanddata-stripedon a row,data-committedon an entry row,data-focusedanddata-selectedon a cell,data-activeon a header cell, and the grid's other booleandata-*attributes are rendered as"true"while the state holds and omitted otherwise, instead of always present as"true"or"false".[data-x="true"]selectors andtoHaveAttribute("data-x", "true")assertions keep working; a[data-x="false"]selector becomes:not([data-x])andtoHaveAttribute("data-x", "false")becomesnot.toHaveAttribute("data-x"). -
#37
d2741a6Thanks @Psvensso! -edit.addRowtakes the values the entry row starts from.addRow(values)overridesediting.newRowDefaultsfield by field, so one call
opens the default row and another opens it filled in - or duplicates an existing
row by passing it whole.addRow()is unchanged. -
#67
849bfa3Thanks @Psvensso! -TMDataGrid.Toolbar,Spacer,Footer,FilterPanel,FilterPillsand
ColumnsPaneltake Mantine'sBoxProps- style props such asmb,pxand
hiddenFrom, plusclassName,styleandmod- set on their root element.
TMDataGrid.ToolbargainswithBottomBorder, a 1px line in the theme's
default border colour under the toolbar, off by default.
New typesTMDataGridToolbarPropsandTMDataGridColumnsPanelProps.
Patch Changes
-
#128
1aee8daThanks @Psvensso! - Docs: a card view recipe - the grid's state rendered as virtualized cards instead ofTMDataGrid.Table. -
#70
a95b8c3Thanks @Psvensso! -TMDataGrid.Menu.ColumnsandTMDataGrid.ColumnsPanelshow their search box from six hideable columns.
searchableisTMDataGridColumnSearchable(boolean | "auto"), default"auto";truekeeps the box on a shorter list. -
#132
6720246Thanks @Psvensso! - Docs: a Column header menu page, and the docs sidebar grouped by grid part. -
#135
de9e8c1Thanks @Psvensso! - Docs: a migration guide from the 2.0 beta to 2.0.0. -
#138
b680b1cThanks @Psvensso! - Docs: Editing split into Editing, Draft store, and Adding and deleting rows. -
#40
dc3aac9Thanks @Psvensso! - Fixed: a controlledstateslice built inline in the render body caused an
infinite render loop. TanStack comparesoptions.stateslices by identity on
every render; the grid now forwards the previous render's value for a slice
whose contents are unchanged.- A controlled slice passed without its
onXChangelogs a console warning in
development. Without the callback the slice cannot change; use
initialStatefor a starting value. - A controlled
columnVisibilityno longer hides the generated columns. The
tree column's entry is managed by the grid and followsgrouping. - The tree column's visibility entry is seeded into an external
atoms.columnVisibilityat mount. Previously the tree column rendered empty
in an ungrouped grid when an atom owned the slice. - A
statekey set toundefinedis ignored instead of being written into
the table state. Datevalues in controlled state compare by time.
- A controlled slice passed without its
-
60f8292Thanks @Psvensso! - An entry row blocked by validation now shows it: the ✓ turns red with the message in its tooltip, as the Save does, and a cell a pathed issue names carriesdata-invalidand the red corner. -
#119
5497b65Thanks @Psvensso! - Fixed: an entry row rendered nothing in the cells it does not open, a display column or one withmeta.edit.enabledoff, both when first added and when a committed new row was reopened. They now render through the column's owncellrenderer over the row as shown, as on a body row. -
#73
f2d4faeThanks @Psvensso! -edit.addRows(rows, { commit: true })underediting.draftis one publish for the whole import: the rows are validated together and land in the draft store in the same render that shows them. Ten thousand rows take about a second; before, each row cost a render and a copy of the store, so the same import took minutes.saveDrafts,commitAll,cancelAllanddeleteRowspublish once for their batch the same way.- The engine no longer registers its row forms with TanStack Form devtools - three
windowlisteners per row, and a broadcast on every change of every row.
- The engine no longer registers its row forms with TanStack Form devtools - three
-
19e21e4Thanks @Psvensso! - The column toggles in the grid menu show a checkbox in both states, so an unchecked column no longer reads as a plain action next to your own items. Show/Hide All shows an indeterminate box, and saysaria-checked="mixed", when some columns are hidden. -
#69
3f17285Thanks @Psvensso! - The reference documentation ships in the package underdocs/, importable as@jielga/tmdatagrid/docs/<page>.md. -
#28
acc190bThanks @Psvensso! ---dg-radiussets the frame's corner radius, defaulting to
--mantine-radius-md.<TMDataGrid {...grid} style={{ "--dg-radius": 0 }} />
-
#56
f94f2a6Thanks @Psvensso! - ---row-bgis painted over the theme body colour, so a translucent value no longer leaves pinned columns and pinned rows see-through.- The summary row sits at the bottom edge when the rows do not fill the body.
- A cell's validation message shows in a tooltip on the editor instead of as text under the input.
- Under
mode: "cell", leaving a cell with a value the validators refuse keeps the editor open instead of closing it on the refused value.
-
0c5b921Thanks @Psvensso! - Column resizing is smooth again, and no longer jumps on mouse down.- A drag starts from the width the column is rendered with, not its declared
size. The jump could also drop the divider onto a neighbouring header and
start a column move, which swallowed the mouse up and left the resize running
after the button was released. - A running drag is painted on the grid's own column tracks instead of through
state, so nothing re-renders while the pointer moves. columnResizeModenow defaults to"onEnd": the width reachescolumnSizing
when the pointer is released. Set"onChange"to publish it on every move,
at the cost of a render of the grid for each one.
- A drag starts from the width the column is rendered with, not its declared
-
#127
0651ba6Thanks @Psvensso! - - Testing contract:header-resizeon the column resize handle andfilter-pill-removeon a filter pill's ✕. The Testing page now documents the portaled surfaces,data-dg-scroll-containerand component tests with Playwright stories, and itsDataGridpage object is the one the grid's own Playwright suite runs.- Fix: one click on a header's sort arrow advanced the sort two steps.
- Fix: the
TMDataGrid.Menubutton now carriesaria-expandedandaria-controls; the tooltip between it and the menu target swallowed them.
-
2e107adThanks @Psvensso! - Column resizing, the cell range drag and the filter popup's click-away now work when the grid is rendered through a portal into a window opened withwindow.open.
Their listeners attach to the grid's own document and window rather than the global ones.Focus checks, editor focus handling, select-column click detection and autosize measurement use the grid's own document and window as well, so they behave the same in a popup window as inline.
-
#28
aa3aeacThanks @Psvensso! -meta.autoSizewaits for the column's first cells.It ran once on the mounting commit, so a grid whose rows are fetched had a
header and no cells to measure and the column kept that width. The
double-click gesture and the Autosize column menu item are unchanged. -
#112
2a7c318Thanks @Psvensso! - A row the engine takes out of the table now leavesrowSelectionwith it: an entry row discarded bydeleteRow,deleteRowsorcancel, or saved bysaveDrafts, and a marked row once its deletion is saved. A stale id used to keep the select-all box indeterminate and the selection non-empty after a bulk delete of selected rows. -
cd5839dThanks @Psvensso! - The toolbar summary count no longer wraps - it stays on one line. -
7fbb5d6Thanks @Psvensso! - Committed entry rows underediting.draft:- A cell that takes no edit (
meta.edit.enabled: false, or a column with no field) no longer reopens the row on double-click, the same as a body cell. - The row now gets the value-row padding, border and
--dg-row-new-bgtint; the stylesheet still keyed on the olddata-confirmedname.
- A cell that takes no edit (
-
#53
7e472b1Thanks @Psvensso! - The cell focus ring no longer paints over a pinned column: a focused cell that is not pinned now scrolls under the pinned lanes, and a focused pinned cell keeps its ring above the row sliding past it. -
7b9f309Thanks @Psvensso! - Swedish labels: selecting is "Välj", not "Markera" - "Välj alla", "Välj alla rader", "Välj rad", "Välj grupp". -
8932623Thanks @Psvensso! - Testing docs for editing flows: where an added row's temporary id goes at ✓ and at Save, how a test finds the row afterwards, and recipes for validation, cell and row edits, deletions and the draft store, with the matching page-object methods. A newtesting-editingskill carries the same. -
#50
f2a5c6dThanks @Psvensso! - A livesize(ormeta.rowHeight) change now re-estimates virtualized row heights, so the scroll range follows the new density instead of keeping the old one.