0.3.3 - 2026-10-11
| Change | Who is affected | What to do |
|---|---|---|
| A partially visible row draws its text at the row’s own centre; it used to be pulled up into the visible slice | Screens whose grid height leaves the last row half visible: that row’s text is now cut | Size the grid to a multiple of the row height to avoid the cut. Nothing relied on the old behaviour other than seeing that last row in full |
cellDecorator starts at textAlign: 'left', textBaseline: 'middle' and the body text colour | A decorator whose coordinates compensated for the alignment the previous column left behind | Drop the compensation and use the plain coordinates. This is the same change custom renderers got in 0.3.2 |
Added
-
getCellValue(rowIndex, field)returns one cell’s current value with unsaved edits applied, under the same rules asgetRowData():nullfor an unknown field or a row that is not loaded.grid.getCellValue(3, 'name'); // the same value as getRowData(3)?.name -
getTotalRows()returns the number of rows the grid is showing: filters applied, rows added withaddRow()counted, removed rows excluded, pinned copies aside. With pagination on it is the whole dataset rather than the current page. Until now a host had to keep its own copy of whatfetchMetareported. -
addRows(rows, opts?)inserts a list as one undo step and one redraw. CallingaddRow()in a loop leaves one undo step per row and redraws once per row.indexplaces the first row and the rest follow it in order; the return value is the index of each new row, and an empty list does nothing.grid.addRows([{ name: 'A' }, { name: 'B' }]); // appended, [n, n+1] grid.addRows([{ name: 'A' }, { name: 'B' }], { index: 2 }); // A at 2, B at 3 -
The
confirmUnsavedoption answers the “unsaved edits will be discarded” prompt that a filter, sort or quick-filter change raises, in place ofwindow.confirm. Returningtruediscards the edits exactly as confirming did. It has to answer synchronously, because the call that asks is synchronous up to that point; for a modal that resolves later, deal with the edits first and callsetSort()/setFilter()afterwards.new JHGrid({ // ... confirmUnsaved: (message) => myConfirmSync(message), });
Fixed
-
A partially visible row drew its cell text, its custom-renderer text and its row number on three different lines. The built-in cell text was pulled up to the centre of the visible slice while
args.text()and the row number stayed at the row box’s centre, so the text in one row stepped up and down across the columns; with pinned rows the lifted text ended up touching the row above it and the two read as one. All three now use the row’s own centre. A row taller than the band has no on-screen centre, so that case still follows the visible slice. -
printGrid({ full: true })failed withInvalid string lengthon a large table, for the same reason as the CSV export: the whole document was joined into one string before it was written to the popup. 800,000 rows of a fourteen-column table reached it. The document is now written as a head, one piece per 5,000 rows and a tail, so a partial write still leaves valid HTML. -
exportCsv({ full: true })failed withInvalid string lengthon a large table. Every line was joined into one string before it reachedBlob, and on a table with many rows and wide cells (an image column holds a data URI per row) that string ran past V8’s maximum string length, so the call threw before anything downloaded. The lines are now handed toBlobin slices of 10,000;Blobconcatenates its parts as bytes and has no such limit. -
cellDecoratorinheritedctx.textAlign,textBaselineandfillStylefrom the cell drawn just before it. After a right-aligned column, the coordinate infillText(..., x + padding, ...)was read as the end of the string, so the text was drawn over the column to the left. Decorators now get the same reset custom renderers got in 0.3.2. -
Of the four paths that draw the header, only the drag animation passed no group-label clamp; it now matches the other three. No arrangement was found where this changes the pixels: two layouts were tried, including dragging a column the scroll had cut, and removing the clamp again left the same image. It closes a difference between the four paths rather than a defect anyone could see.
-
CellDecoratorArgsdeclaredtext, so TypeScript acceptedargs.text()in a decorator although the runtime never passes it. It now extendsOmit<CellRendererArgs, 'text'>. A decorator draws on top of a cell that is already drawn, onctxwithx/y/w/has before.
Docs
browser-support.mdnow states that there is no RTL layout mode. The grid draws its cells on a canvas, wheredirection: rtlon an ancestor changes nothing; Arabic and Hebrew text inside a cell renders correctly, it is the column order and the chrome around it that do not mirror.