ApexGrid 3.5 and Enterprise 0.7: Server-Side Depth, Nested Filters, and a Dependency Diet
Two months of work landed in one release pair. apex-grid 3.5.0 is a feature and accessibility release with a dependency diet; apex-grid-enterprise 0.7.0 is four features, the largest of which closes the last real gap in how the grid handles data it cannot hold.
No exported symbol was removed or renamed in either. Everything new in enterprise is opt-in, and <apex-grid-enterprise> stays a drop-in replacement for <apex-grid>.
npm install apex-grid@3.5.0 apex-grid-enterprise@0.7.0
Community: 3.5.0
Right-to-left
dir="rtl" on the grid or any ancestor now mirrors it completely.
<html dir="rtl">
<apex-grid></apex-grid>
</html>
Direction is read from the computed style, not from the attribute on the grid itself, so an inherited dir works and a page that switches direction at runtime switches the grid with it.
The stylesheets turned out not to be the work: they were already written in logical properties, with 48 inline-size, 19 inset-inline-start, 9 border-inline-end and exactly one physical left (correct as it stands, since it positions a fixed-position panel from a viewport coordinate computed in JavaScript). What did not mirror was the code reasoning in physical pixels or physical key names, which was four places:
- Arrow keys are screen directions, not column order. In RTL the column to the left is the next one, so the mapping onto previous and next inverts. Everything else in navigation is order-based and needed no change.
- Column resize measured width from the left edge. The inline-start edge is the one that stays put, so in RTL width comes off the right edge and the column grows as the pointer moves left.
- Column reorder waited for the cursor to cross the target's midpoint in a fixed direction. Moving toward a later column means moving right in LTR and left in RTL, so the comparison flips.
- The range fill handle is drawn at the cell's inline-end corner, so it already mirrored to the physical left, while the grab band still tested the physical right. The dot and the grabbable area were on opposite sides. The enterprise chart-range handle had the same split.
grid.applyEdits(edits)
A real programmatic batch write. Until now the only write path drove the editor, so a bulk update meant a thousand undo entries and a thousand re-renders.
const result = grid.applyEdits(
grid.selectedRows.map((row) => ({
rowIndex: grid.pageItems.indexOf(row),
column: 'discount',
value: 0,
}))
);
// { applied: 12, unchanged: 3, invalid: 0, cancelled: 0, skipped: 1 }
Every write goes through the same choke point interactive editing uses, so each one still emits the cancellable cellValueChanging and then cellValueChanged, and still runs the column's validators. What differs is that the whole set lands as one undo step and triggers one pipeline run.
It returns a tally rather than a count, because "nothing changed" and "everything was rejected" are different answers and a caller usually needs to tell them apart. Rows out of range and columns the user could not edit either are skipped rather than thrown on, so a partially stale batch still applies what it can.
Three accessibility items
prefers-reduced-motionis honoured. Every transition routes through one of the--ag-dur-*tokens, and those zero under the media query: 24 transitions across 9 stylesheets go instant, end states unchanged.- Forced colors. Forced-colors modes paint no
box-shadow, and the grid leaned on inset shadows for exactly the indicators that matter most, so in Windows High Contrast a keyboard user had no visible cursor and no visible selection. The active-cell ring, invalid-cell ring, selected-row bar and every toolbar and filter focus ring are now restated as outlines, which forced colors recolours rather than drops. - WCAG 2.2 AA 2.5.8 (24x24 targets). The selection cell forwards its click to the checkbox rather than growing a deliberately small mark, and header action buttons reach 24x24 through the invisible chip that already expanded them, so the packed header layout is unchanged. Paginator buttons grow from 22 to 24, which makes the paginator bar 2px taller.
The Ignite UI dependencies are gone
Core carried igniteui-webcomponents as a runtime dependency and enterprise as a peer, so every consumer installed a whole component library (plus @floating-ui) for one inert pass-through call. The grid registers no Ignite UI element. igniteui-theming was a Sass dependency for exactly one function.
Five packages leave the lockfile, and the compiled CSS is 3,718 bytes smaller with every rem() output byte-identical. The install is now one package:
npm install apex-grid
The --igx-* custom-property level went with it. Every colour rule resolved through three levels and the first never fired, because that prefix is a different package's naming convention. --ag-* overrides and the --ig-primary-* auto-tint hooks behave exactly as before, so a page that defines an Ignite UI palette itself still tints the grid; it just no longer has that library installed on the grid's account.
setup({ theme }) is the one API affected. It only ever forwarded to configureTheme(), which was already documented as not affecting the grid's appearance, so it is now inert and warns once. It still compiles, so no consumer code breaks.
The Custom Elements Manifest is authoritative
<apex-grid> was missing from custom-elements.json entirely: no tagName, not flagged as a custom element, zero named events. It now resolves customElement, tagName, all 29 events, the theme attribute and the public properties, and a build-time drift check fails the build when any of them diverge.
Editor tooling reads this for completions, and it is what the react-apex-grid wrapper package generates from.
Fixed
- A grid could settle with zero rendered rows at first paint, silently, while reporting a correct
ariaRowCountand a correctly sized scroll region (#31). The virtualizer clears its rendered range when its viewport measures zero-height, which happens when the grid is laid out while off-screen, and it had no signal that would make it measure again: an element that moves into view, or a window that resizes around a fixed-size host, fires neither a resize nor a scroll. An iframe sized by its host page after load hits this. The body now watches its own intersection with the window and re-measures when it gains a visible slice with nothing rendered. - A grid moved to another parent left its body permanently stale, still rendering the old rows after
datawas reassigned.
Enterprise: 0.7.0
Server-side row model
The infinite row model serves one flat list. This one serves a hierarchy: the grid asks a ServerSideDataSource for one group level at a time and fetches a group's children only when that group is expanded, with the server computing the aggregates shown on the group rows.
grid.serverSideRowModel = {
datasource,
rowGroupCols: ['region', 'country'],
valueCols: { revenue: ['sum'] },
};
- Grouping and aggregation through
serverSideRowModel, withexpandServerGroup,collapseServerGroup,refreshServerSideandisServerSideRowModel. - Server-side pivot through
pivotCols: the server returnspivotResultFieldsand the grid installs them as the value columns. - Intra-group block pagination. Opt in with
blockSizeand a group's children arrive in windows, with unloaded rows rendered as placeholders and blocks fetched as the virtualizer scrolls into them.grid.isRowLoadingcovers them. - Group rows carry
aria-levelandaria-expanded.
It is mutually exclusive with the infinite row model and with client-side grouping and pivoting, since the server owns the shaping.
Advanced filter builder
A per-column filter cannot express "A and (B or C)", because there is nowhere to put the parentheses. <apex-grid-filter-builder> is a nested AND / OR visual query builder, backed by a pure, DOM-free model and evaluator that reuses the existing operand tables rather than inventing a second vocabulary.
applyAdvancedFilter(), clearAdvancedFilter() and advancedFilterModel evaluate client-side through the data pipeline's filter hook, so there is no core change. The model is plain JSON, so it round-trips through storage or a URL without a serializer. While a model is active it owns column filtering.
Advanced filter builder · demo
Pivoting v2
Opt-in grand total and subtotals, spanning column headers built on core column groups, multi-field pivotOn, and expandable nested row groups under one auto group column with indent and chevrons, where each parent carries its subtree aggregate. Column width and pin state survive a re-pivot. getPivotColumnGroups(), getPivotMeta() and PIVOT_GROUP_KEY are exposed, and the whole surface is localized.
The in-grid chart range handle
Charting a selection used to take a snapshot and forget where it came from. The source range now stays outlined in the grid with a bottom-right drag handle, and pulling it resizes the range and live-redraws the linked chart. That is the spreadsheet mental model, and it was the last real UX gap in the charts arc.
grid.totalRow
A grand-total row over the whole view. Row grouping totals a group's leaves and pivot has its own grand total, but a flat or grouped grid had no way to answer "what is the sum of this column". Configured with the same AggregationConfig as everything else, positionable top or bottom, and it follows filtering, so a filtered grid totals what it shows.
Shift+Arrow range extension
Range selection was pointer-only. Shift with the arrow keys now grows and shrinks the focus corner, Shift+Home/End reach the row edges, and adding Ctrl/Cmd reaches the grid corners, with the anchor staying put. This was the highest item left on the July accessibility audit's backlog.
Changed and fixed
- ApexCharts 7.x is supported. The optional
apexchartspeer now accepts^5.15.0 || ^6.0.0 || ^7.0.0. The full enterprise suite passes identically against 5.16.0 and 7.x, and 5.x remains supported. - A forged licence key could permanently lift the watermark. Verification is asynchronous while the watermark decision is synchronous, so a well-formed forgery read as valid, removed the watermark, and was never re-checked once the signature verdict flipped. The grid now re-reconciles on licence state change.
- In RTL, the range fill handle and the chart-range handle were grabbable on the wrong side from the dot they draw.
- The licence watermark is painted by
apex-commons'Watermarkinstead of a private copy. The documentedlicense-watermarkCSS part is preserved.
Upgrading
npm install apex-grid@3.5.0 apex-grid-enterprise@0.7.0
- Drop
igniteui-webcomponentsfrom your install if you added it only for the grid. - Remove
setup({ theme })if you call it. It compiles but does nothing, and warns once. - Check nothing of yours reads
--igx-*. That level never resolved, so in practice nothing can, but it is worth a grep. - Everything else is additive. No exported symbol was removed or renamed.
Both packages are published to npm with provenance. Full detail in the core and enterprise changelogs.
Frequently asked questions
Do I still need to install igniteui-webcomponents with ApexGrid?
No. As of 3.5.0 the Ignite UI dependencies are gone entirely: `npm install apex-grid` is the whole install, and the grid declares no peer dependencies at all (lit and its companions are ordinary dependencies now). Five packages leave your lockfile. The `--ig-primary-*` auto-tint hooks survive, because they are only CSS fallbacks, so a page that defines that palette itself still tints the grid.
How do I update many ApexGrid cells at once without flooding the undo stack?
Use `grid.applyEdits(edits)`, added in 3.5.0. Each write still emits the cancellable `cellValueChanging` and then `cellValueChanged`, and still runs the column's validators, but the whole batch lands as one undo step and one pipeline run instead of one per cell. It returns a per-outcome tally (`applied`, `unchanged`, `invalid`, `cancelled`, `skipped`) rather than a count, so you can tell a rejected batch from a no-op one.
What is the difference between the ApexGrid infinite row model and the server-side row model?
The infinite row model serves one flat list, fetching fixed-size blocks of rows as the user scrolls. The server-side row model, new in enterprise 0.7.0, serves a hierarchy: the grid asks for one group level at a time and fetches a group's children only when that group is expanded, with the server computing the aggregates shown on the group rows. It also supports server-side pivot through `pivotCols` and intra-group block pagination through `blockSize`. The two are mutually exclusive, as is client-side grouping and pivoting, because in this model the server owns the shaping.
Does ApexGrid support right-to-left languages?
Yes, from 3.5.0. Set `dir="rtl"` on the grid or any ancestor and it mirrors completely. Direction is read from the computed style rather than the attribute on the grid itself, so an inherited `dir` on `<html>` or a wrapper works with no configuration. Column order, pinning, resize, reorder, the arrow keys and the enterprise range handles all mirror.
Does ApexGrid Enterprise work with ApexCharts 7?
Yes, from enterprise 0.7.0, whose optional `apexcharts` peer range is `^5.15.0 || ^6.0.0 || ^7.0.0`. The full enterprise suite passes identically against 5.16.0 and 7.x, and 5.x remains supported. Releases through 0.6.1 declared `^5.15.0 || ^6.0.0` and produced an npm peer warning on ApexCharts 7, though the grid still worked.