Vanilla Grid Toolbar
A zero-dependency <vn-grid-toolbar> web component that renders a state-aware
toolbar bound to a vanilla-grid instance. It has
no data of its own — it observes a grid, exposes a structured state object, and
forwards user actions back to that grid.
Unlike vanilla-grid and vanilla-resize-box, the toolbar cannot function
standalone: it requires a linked grid.
Features
- Linked to one grid via a
grid="<id>"attribute, a.gridproperty, or a best-effort DOM fallback. Degrades cleanly (renders nothing / anunlinkedtemplate) when no grid resolves. - Two configurable states —
empty(no selection) andselected(≥1 row) — authored as host<template data-vn-grid-toolbar="…">blocks. Optionalunlinked/busytemplates. Templates are re-read on every grid relink andrefresh(), so they may also be appended after the element is connected (frameworks like Angular attach the element before host code can add children): append the templates first, then set thegridattribute. - Structured
ToolbarState(selection, counts, totals, busy/error, filter, search, derived predicates) pushed to templates and sub-components.busytracks the grid's whole working window, not just its fetches: it is closed byvn-grid-busy-changed, so a load or sort whose rows reorder behind the shimmer keeps the toolbar busy until the grid is genuinely idle. - Sub-component contract (
toolbarContextpush /VanillaGridToolbarItembase) for full-custom items, alongside the batteries-included items below. - Batteries-included items:
<vn-grid-toolbar-command>,<vn-grid-toolbar-status>,<vn-grid-toolbar-status-message>,<vn-grid-toolbar-search>(inline ✕ clear button while a term is set; optionally Carbon-styleexpandable, capped at amax-widthattribute (default 480px) so it never grows unbounded into the toolbar row; a hover hint surfaces the attached DataManager's restricted search fields, when any are configured — shown on hover whenever there's no term being typed (including an empty, focused box), hidden while a non-empty term has focus, which also covers the expanded box; placed edge-aware so it never overhangs the toolbar, at rest as well as when revealed),<vn-grid-toolbar-export>,<vn-grid-toolbar-button>,<vn-grid-toolbar-selection-count>,<vn-grid-toolbar-clear-selection>,<vn-grid-toolbar-spacer>,<vn-grid-toolbar-group align="start|center|end">(layout wrapper — astart/endpair genuinely centres acentergroup between them, unlike a<vn-grid-toolbar-spacer grow>pair),<vn-grid-toolbar-info>. - Own themes for all 8 grid themes; mirrors the linked grid's theme by name and follows it live.
- Own i18n channel (
messages/setMessages/locale). - Mobile focus-zoom hardening: the search input auto-sizes to 16px on coarse-pointer devices, avoiding iOS Safari's input-focus page zoom.
Installation
Load after vanilla-grid-element.js (it relies on <vn-grid>'s API and
ready()):
<script src="vanilla-grid/vanilla-grid.js"></script>
<script src="vanilla-grid-toolbar/vanilla-grid-toolbar.js"></script>
A single <script> tag is all a host needs: vanilla-grid-toolbar.js
auto-loads its sibling vanilla-grid-toolbar-events.js at runtime, exposing
window.VanillaGridToolbarReady (optional to await — <vn-grid-toolbar> and
its items are defined synchronously regardless). In production, load
vanilla-grid-toolbar.bundle.js instead — the built single-file artifact with
the auto-loader disabled. See
docs/vanilla-grid-toolbar/02-implementation.md.
Usage
<vn-grid id="ordersGrid" selection-mode="multiple" row-key-field="Id"></vn-grid>
<vn-grid-toolbar grid="ordersGrid">
<template data-vn-grid-toolbar="empty">
<vn-grid-toolbar-status></vn-grid-toolbar-status>
<vn-grid-toolbar-button command="reload" title-key="refreshTitle">↻</vn-grid-toolbar-button>
</template>
<template data-vn-grid-toolbar="selected">
<vn-grid-toolbar-selection-count></vn-grid-toolbar-selection-count>
<vn-grid-toolbar-export scope="selected"></vn-grid-toolbar-export>
<vn-grid-toolbar-clear-selection></vn-grid-toolbar-clear-selection>
<orders-bulk-actions></orders-bulk-actions>
</template>
</vn-grid-toolbar>
A custom sub-component receives the context push:
class OrdersBulkActions extends window.VanillaGridToolbarItem {
contextChanged() {
this.textContent = `Archive ${this.state.selectedCount} order(s)`;
}
connectedCallback() {
this.onclick = () => {
archive(this.selectedKeys); // host logic
this.send('clearSelection'); // command via the toolbar (R5)
};
}
}
customElements.define('orders-bulk-actions', OrdersBulkActions);
Attributes
| Attribute | Meaning |
|---|---|
grid |
id of the linked <vn-grid>. |
theme |
Theme name; mirrors the linked grid's theme when unset. |
theme-css-path |
Explicit theme CSS path override. |
locale |
Locale tag (host bookkeeping / item rendering). |
Properties
| Property | Type | Notes |
|---|---|---|
grid |
VanillaGridElement | string (set) → element (get) |
The linked grid. |
gridApi |
VanillaGrid (get) |
The underlying instance (gridElement.grid). |
state |
ToolbarState (get) |
Read-only snapshot. |
theme |
string |
Get/set theme name. |
locale |
string |
Get/set locale tag. |
messages |
Record<string,string> (get) |
Active localization bundle. |
Methods
| Method | Description |
|---|---|
ready() |
Resolves once linked to a ready grid and first-rendered. |
refresh() |
Force re-read of the state templates + grid state and re-render. |
setGrid(elOrId) |
Programmatic link. |
setMessages(map) |
Merge a localization bundle and re-render. |
t(key, params) |
Localize a key with {param} interpolation. |
setNumberFormatter(fn) |
Override how <vn-grid-toolbar-status> formats numbers (default: Intl.NumberFormat(locale)). Pass null/undefined to restore the default. |
formatNumber(value) |
Format a count via the resolved number formatter (the setNumberFormatter override, or the Intl.NumberFormat(locale) default). |
setStatusMessage(text, severity) |
Set the host-driven <vn-grid-toolbar-status-message> text (severity: info/success/error/null). Persists across template swaps. |
getStatusMessage() |
{ text, severity } of the current host message. |
setInfoMessage(text) |
Set the host-driven <vn-grid-toolbar-info> tooltip text (the "i" button hides when empty). Persists across template swaps. Pass \n-separated lines to render a bulleted list instead of one run-on line. |
getInfoMessage() |
The current host info text. |
registerCommand(name, fn) |
Register/override a host command. |
dispatchCommand(name, arg, source) |
Main command entry point — dispatches the cancelable vn-grid-toolbar-command event, then runs the allow-listed command via runCommand() unless preventDefault() was called. Used internally by every command binding/item. |
runCommand(name, arg) |
Invoke an allow-listed command directly, bypassing the vn-grid-toolbar-command event. |
Autonomous data-volume summary vs. host-driven message
Two status elements, each owning a distinct class of message — mount either or both:
<vn-grid-toolbar-status>is autonomous — it composes "Loaded {loaded}/{total} (scroll to load more)" + the selected-count suffix straight from grid state (getLoadedRowCount()/getTotalRowCount()/hasMoreRows()/ selection). The app never touches it. Numbers are grouped viatoolbar.formatNumber()(defaultIntl.NumberFormat(locale); override withtoolbar.setNumberFormatter(fn)to match a grid's ownformatInteger). By default it renders only the data summary — and renders nothing while the grid is busy with nothing loaded yet (the reset→requery window of a filter/search/reload, or the first load), because the counts there are torn-down state, not a measurement: "Loaded 0" never flashes up mid-operation, but a genuine empty result (load settled, nothing matched) still shows it. Add the booleanshow-transientattribute to show the genericstatusLoading/statusErrortext in those windows instead of blank (skip this if a<vn-grid-toolbar-status-message>is also mounted, to avoid double-rendering transient states).<vn-grid-toolbar-status-message>is host-driven — for messages the toolbar can't derive itself (env-change progress, metadata fetch, error text with app-specific detail). Place it anywhere in a state template and calltoolbar.setStatusMessage(text, severity). The message is stored on the toolbar (not the cloned element), so it survives empty↔selected swaps and the host keeps a stable reference (the toolbar).
<template data-vn-grid-toolbar="empty">
<vn-grid-toolbar-status></vn-grid-toolbar-status>
<vn-grid-toolbar-status-message></vn-grid-toolbar-status-message>
<vn-grid-toolbar-spacer grow></vn-grid-toolbar-spacer>
<vn-grid-toolbar-button command="reload">↻</vn-grid-toolbar-button>
</template>
toolbar.setMessages({ statusLoadedTotal: 'Loaded {loaded}/{total} rows' });
toolbar.setNumberFormatter((v) => formatInteger(v, currentLocale));
// row-count summary now self-updates off grid state — no app code needed.
toolbar.setStatusMessage('Fetching environment metadata…', 'info'); // transient, still host-driven
To push trailing controls to the right of a status item (a full-width status
bar), follow it with a growing <vn-grid-toolbar-spacer grow> (see below).
Spacer / separator
<vn-grid-toolbar-spacer> is an inert layout element. With no attributes it is a
small invisible gap. Add grow to make it absorb the row's free space — e.g. one
grow spacer between a left-hand and a right-hand command group pushes them to the
two ends of the toolbar. Add separator to reveal a theme-owned vertical divider
glyph (the --vn-grid-toolbar-icon-separator CSS custom property, overridable per
theme and masked over --vn-grid-toolbar-separator-color at
--vn-grid-toolbar-separator-width × --vn-grid-toolbar-separator-height) so it
reads as a | between controls. The two attributes combine.
grow pushes blocks apart — it does not centre. A spacer grow pair
centres whatever sits between them in the gap between the two side
blocks, not in the row: off-centre at rest whenever the side blocks differ in
width, and visibly sliding whenever any sibling between them grows. Reach for
<vn-grid-toolbar-group align="…"> (below) to genuinely centre a group.
Each theme tunes those tokens for its selection bar: the light themes (default,
material, fiori, glow, glow-dark, fluent) widen the divider and color it
with the theme's muted token for clear contrast; carbon / carbon-dark render a
bold white, near-full-height bar against the blue batch-action surface. The base
default (when a theme leaves the tokens unset) is a 14×20 px bar in the border
color.
<vn-grid-toolbar-command command="reload" icon="refresh"></vn-grid-toolbar-command>
<vn-grid-toolbar-spacer separator></vn-grid-toolbar-spacer> <!-- fixed "|" divider -->
<vn-grid-toolbar-command command="exportToExcel" icon="download"></vn-grid-toolbar-command>
<vn-grid-toolbar-spacer grow></vn-grid-toolbar-spacer> <!-- push the rest right -->
<vn-grid-toolbar-info></vn-grid-toolbar-info>
Layout group (<vn-grid-toolbar-group align="start|center|end">)
A layout wrapper that clusters related items so they move as one unit. With
no align it is a plain, non-elastic cluster (flex: 0 0 auto) — useful
purely for shared spacing/wrap behavior. align="start" and align="end"
mark the two side zones — always equal width to each other (flex: 1 1 0, with a min-width: min-content floor so a side that truly cannot fit
grows past its share and pushes the centre rather than overflowing the row)
— and align="center" marks the shrinkable middle zone (flex: 0 1 auto; min-width: 0). A start/end pair's equal-width guarantee is what makes a
center group between them sit at the row's true midpoint regardless of
what either side contains, and stay there while a sibling (e.g. an
expandable search) grows into its own group's share instead of a shared
spacer. Pure CSS, in flow — no measurement, no ResizeObserver.
<vn-grid-toolbar-group align="start">
<vn-grid-toolbar-status></vn-grid-toolbar-status>
</vn-grid-toolbar-group>
<vn-grid-toolbar-group align="center">
<my-theme-selector></my-theme-selector>
</vn-grid-toolbar-group>
<vn-grid-toolbar-group align="end">
<vn-grid-toolbar-search expandable></vn-grid-toolbar-search>
</vn-grid-toolbar-group>
Purely presentational, like <vn-grid-toolbar-spacer> — consumes no toolbar
context. Items nested inside a group still receive toolbarContext and react
to disabled-when/show-when/hide-when exactly as they would ungrouped.
Events
| Event | Detail |
|---|---|
vn-grid-toolbar-ready |
{} — first successful link + render. |
vn-grid-toolbar-state-changed |
{ state, previous } — every transition / value change. |
vn-grid-toolbar-command |
{ command, arg, grid, gridElement, toolbar, state, selectedKeys, selectedRows, source } — cancelable; fired by dispatchCommand() for every command (standard or custom) before the standard/registered handler runs. Call preventDefault() to veto the default action. |
Names live in the frozen VanillaGridToolbarEvents registry.
Command allow-list
Toolbar items and dispatchCommand() map to these already-public <vn-grid> methods:
clearSelection, setSelectedKeys, reload, exportToExcel,
autoFitAllColumns, clearColumnFiltersAndSorting, clearPersistedSettings, clearGrouping, search.
Extend with toolbar.registerCommand(name, fn).
Theming
Ships vanilla-grid-toolbar.css (structural) + themes/vn-grid-toolbar-<theme>.css
for all 8 built-in themes (default, material, fiori, carbon,
carbon-dark, glow, glow-dark, fluent). Each theme sets the --vn-grid-toolbar-*
custom properties to match the corresponding grid palette so the two read as one
component. Enumerate the names at runtime with
VanillaGridToolbarElement.getSupportedThemes(). The built-in list is a hand-kept
copy of the grid's — adding a built-in theme means adding it (and its stylesheet)
to both components.
When theme is unset the toolbar mirrors the linked grid's theme name and
follows it live. Resolution priority in _updateThemeStylesheet(): (1) the
toolbar's own theme-css-path; (2) the resolved theme name (own theme, else
the mirrored grid name) looked up first in the custom registry, else the built-in
themes/vn-grid-toolbar-<name>.css; (3) default.
Custom themes
VanillaGridToolbarElement.registerTheme(name, cssPath) registers the
toolbar-side stylesheet for a custom theme, mirroring
VanillaGridElement.registerTheme. A custom theme's grid and toolbar files
declare different token sets (--vn-grid-* on the grid vs. --vn-grid-toolbar-*
on the toolbar), so a host registers it on both components:
VanillaGridElement.registerTheme('corporate', '/themes/grid-corporate.css');
VanillaGridToolbarElement.registerTheme('corporate', '/themes/toolbar-corporate.css');
grid.setAttribute('theme', 'corporate'); // toolbar mirrors it → both link corporate
Divergence is supported (a toolbar and grid may run different themes). When the
toolbar's resolved theme differs from the grid's — an explicit divergent theme,
or a mirrored custom grid theme with no registered toolbar stylesheet (falls back
to default) — the toolbar emits a single console.info (not a warning; no
behavior change), deduped per (grid-theme, toolbar-theme) pair.
Multiple <vn-grid-toolbar> instances with different themes on the same page
each get their own theme <link>: the link's id is derived from the resolved
stylesheet URL, so instances resolving to the same stylesheet share one link
(no duplicate downloads) while instances resolving to different stylesheets
never overwrite each other. A refcount removes a link once the last instance
referencing it switches theme or disconnects. See
15-themes-implementation.md §6.1.2
for the equivalent grid-side behavior.
Changelog
Version 1.21.0
- Changed (
<vn-grid-toolbar-export>) — breaking for markup usingformat: theformatattribute is removed, followingvanilla-grid1.40.0, whoseexportToExcel()now always writes.xlsxand no longer offerscsvorods. The button sends{ scope }only. Migration: dropformat="…"; a leftover attribute is ignored. - Fixed (
<vn-grid-toolbar-export>): a failed export was an unhandled promise rejection, so the user saw the overlay vanish and nothing else. The button now handles the promiserunCommand()returns: it ignoresEXPORT_CANCELLED(the user pressed Cancel) andEXPORT_IN_PROGRESS, and logs any other failure through the toolbar logger, while the grid's overlay shows the reason. A customexportToExcelcommand registered withregisterCommand()must return the export's promise for this to apply.
Version 1.20.0
- Added:
<vn-grid-toolbar-group align="start|center|end">, a layout wrapper for genuinely centring a group of items. A<vn-grid-toolbar-spacer grow>pair does not centre what sits between them — two equal spacers split the row's free space equally, which centres the group in the gap between the two side blocks, not in the row, so the group sits off-centre at rest whenever the side blocks differ in width and visibly slides whenever any sibling grows. Astart/endgroup pair isflex: 1 1 0— always equal to each other, with amin-width: min-contentfloor — so acentergroup between them sits at the row's true midpoint regardless of what either side contains, and stays there while a sibling like anexpandablesearch grows into its own group's share of the row instead of a shared spacer. Pure CSS, in flow — no measurement, noResizeObserver. A bare<vn-grid-toolbar-group>(noalign) is a plain, non-elastic cluster, for grouping related items without claiming a zone. Purely presentational like<vn-grid-toolbar-spacer>— consumes no toolbar context; items nested inside a group still receivetoolbarContextand react todisabled-when/show-when/hide-whenexactly as they would ungrouped.<vn-grid-toolbar-spacer grow>is unchanged and still the simpler choice for the common two-block push-apart layout — see the README's "Spacer / separator" vs "Layout group" sections.vanilla-grid-toolbar.d.tsgainedVanillaGridToolbarGroupElement/window.VanillaGridToolbarGroup.
Version 1.19.1
Fixed (
<vn-grid-toolbar-search>): a horizontal document scrollbar appeared on load, with no interaction, in apps whose shell does not clip — visible insamples/grid-minimal-js/under the IBM Carbon / Carbon Dark themes as a second scrollbar stacked beneath the grid's own, plus a vertical one (the shells'height: 100dvhdoes not subtract the new scrollbar's height). It disappeared the moment the search lens was hovered, which made it look intermittent.The restricted-search-fields hint is hidden with
visibility: hidden, which suppresses painting but not layout — a hidden tooltip still contributes to the document's scrollable overflow area. It was left-anchored until the firstmouseenterre-measured it, so a 320px box hung off the right edge of the collapsed 30px Carbon lens from page load onward. Alignment is now computed when the hint's text changes and when the item's presentation changes (theexpandableinput ⇄ lens flip — the Carbon theme switch itself), not only at reveal time, and the stylesheet defaults the collapsed lens to right-anchored so the box cannot overhang before any JS runs.Fixed (
<vn-grid-toolbar-search>, Firefox): the same scrollbar survived that first fix on a load where the theme was already persisted — a plain reload rather than a theme switch. A custom element upgrades when its definition runs, not when its stylesheet applies: Firefox upgrades first, so the alignment pass measured an unstyled, full-width item and an uncapped tooltip, concluded the box fit left, and never re-measured once the sheet landed and collapsed the item to a 30px lens. Chromium applies the sheet before the upgrade, so it never reproduced there. Both items now watch their own geometry with aResizeObserverand re-anchor when it changes, which also subsumes container and window resizes. The placement specs now run in Firefox as well as Chromium (firefox-placementproject).Fixed (
<vn-grid-toolbar-info>): the same latent defect. Its tooltip used the identicalposition: absolute; left: 0; visibility: hiddenpattern with no edge-aware alignment at all, and was safe only because its one sample usage places the item at the row's left end. It now shares<vn-grid-toolbar-search>'s alignment, so an info item near the row's right end opens leftward instead of overhanging.No public API change;
vanilla-grid-toolbar.d.tsis unaffected. Internal only: the shared_vnToolbarAlignTooltip()helper, and the new--align-leftcounterpart to each tooltip's existing--align-rightclass.
Version 1.19.0
Fixed (
vanilla-grid-toolbar,ToolbarState): after any large load the toolbar stayed greyed out permanently —state.busyandstate.isLoadingstuck attruewhile the grid reportedisBusy() === false. Busy-gated items (disabled-when="busy",<vn-grid-toolbar-export>) stayed disabled, a declaredbusytemplate stayed on screen, and hand-writtentoolbarContextsetters keyed onstate.busynever re-enabled.The busy window was being closed by
vn-grid-loaded, which is a fetch event: the grid raises the deferred reorder's shimmer before that event is emitted, so the recompute meant to end the window read the grid as busy and cached it, and the shimmer's release fired no event at all. The toolbar now also recomputes onvn-grid-busy-changed(new invanilla-grid1.37.0), which fires on every real transition ofisBusy(). Reproduced from every entry point — initial load, reload, search, filter and a dataset swap — and fixed for all of them.Changed (
vanilla-grid-toolbar,ToolbarState.busy): a deferred header sort now raisesbusyfor the length of its shimmer. It previously reported nothing at all, becausevn-grid-sort-changedfires before the reorder begins. Busy-gated items now grey out during a large sort, as they already did during a large load.
Version 1.18.1
- Fixed (
vanilla-grid-toolbar): the toolbar could be left greyed out after a large regroup that no reload followed.ToolbarStatewas recomputed on the grid's selection, load, error, filter and sort events but not onvn-grid-group-changed— and a group change movesgroupState/hasGroupingand, when its reorder ran behind the shimmer, clearsbusyon its way out with no other event to announce it. The toolbar now subscribes tovn-grid-group-changedalongside the rest. This became reachable whenvanilla-grid1.36.0 made a layout reset stop reloading in the common case: the reload's ownvn-grid-loadedhad been masking the gap. No public API change.
Version 1.18.0
- Added (
vanilla-grid-toolbar, row grouping): two newToolbarStatepredicates,hasGrouping(the bound grid has an active requested group state — mirrorsgetGroupState().length > 0) andcanGroup, plus aclearGroupingstandard command that calls the grid'sclearGrouping(). Both predicates are usable fromdisabled-whenlike every existing one (e.g.disabled-when="!hasGrouping"), so a host can gate a control on whether the grid is currently grouped. Because a generic toolbar control has no specific column in scope,canGroupapproximates the dataset-completeness half of the grid's grouping gate while ungrouped, and reflects the active column's owncanGroupByColumn()result once grouped. Declared invanilla-grid-toolbar.d.ts.
Version 1.17.1
- Maintenance (
vanilla-grid-toolbar): documentation-only change. The three documents indocs/vanilla-grid-toolbar/are nowNN--prefixed in reading order —00-index.md,01-usage-guide.md,02-implementation.md— so the folder lists usage before internals;00-index.md's Documents section was reordered to match. The code comments invanilla-grid-toolbar.jsandvanilla-grid-toolbar-events.jsthat cite the implementation doc, and the README's "Loading & bundling" link, were repointed at the new file name. No public API, behavior, or styling changed.
Version 1.17.0
- Fixed the
<vn-grid-toolbar-search>search-fields tooltip never reappearing after clearing an in-progress term: the standard (non-expandable) presentation's reveal rule was focus-gated (:not(:focus-within)), so once the input took focus the hint stayed hidden regardless of what happened to the value — including backspacing a term back to empty — until the item was blurred and re-hovered. A second CSS reveal rule now shows the hint whenever the input is empty (:placeholder-shown), focused or not; it only hides while a non-empty term is present and focused. Theexpandablepresentation is unaffected (still hidden as soon as it expands). - Added a
max-widthattribute to<vn-grid-toolbar-search>(plain number, px; defaultVanillaGridToolbarSearch.DEFAULT_MAX_WIDTH = 480), capping how far theexpandablepresentation'sflex-grow: 999can consume a wide toolbar row's free space. Mirrors<vn-resize-box>'s ownmax-widthconvention, but ships a default rather than being unconstrained.
Version 1.16.3
- Maintenance (
vanilla-grid-toolbar): updated a code comment referencing the sample app now renamedpeople-cities-js(wassample-frontend-1), as part of a repo-wide rename of every sample folder fromsample-frontend-Nto a descriptive<domain>-<framework>name. No code or behaviour change.
Version 1.16.2
- Maintenance (
vanilla-grid-toolbar): removed a stale working-notes reference from the expandable-search block invanilla-grid-toolbar.css; the comment now describes the shipped behaviour on its own terms. De-flaked thestate.errorMessagePlaywright spec, whose baseline assertion sampled the toolbar's boot-time error state once instead of waiting for it to settle — the sample app's blocked boot fetch makes the toolbar flashisErrorbefore the first non-error recompute clears it. No behaviour change.
Version 1.16.1
- Changed (
vanilla-grid-toolbar, Material theme): theclearColumnFiltersAndSortingcommand glyph (--vn-grid-toolbar-icon-filter) is now a Googlefilter_list_off-style icon — descending filter bars struck through by a diagonal slash — replacing the legacy funnel-with-corner-✕. It is deliberately distinct from the grid header's Materialfilter_altfunnel: the header funnel applies a filter, this glyph removes the filters and the sort.
Version 1.16.0
- Removed the declarative binding engine (
data-vn-text/data-vn-show/data-vn-hide/data-vn-enable/data-vn-attr-<name>/data-vn-commanddata-vn-arg). It was a second, parallel way to do what the built-in items (<vn-grid-toolbar-command>,<vn-grid-toolbar-status>,<vn-grid-toolbar-selection-count>, …) and theVanillaGridToolbarItemsub-component contract already cover, with no consumer in any sample app or test. Templates that used these attributes should switch to the equivalent built-in item (e.g.<vn-grid-toolbar-selection-count>fordata-vn-text="selectedCount",<vn-grid-toolbar-command command="…">fordata-vn-command). Thedisabled-when/show-when/hide-whenstate predicates on<vn-grid-toolbar-command>/<vn-grid-toolbar-export>are unaffected.
state.errorMessagenow carries the grid's error text. It was previously alwaysnulldespite being documented and typed;<vn-grid-toolbar>now capturesvn-grid-error'sdetail.error.messageand exposes it on the error recompute (cleared on the next non-error recompute), so<vn-grid-toolbar-status show-transient>and hand-written items can surface real error text. No.d.tschange (the field was already declared).- Maintenance (
vanilla-grid-toolbar): internal cleanups with no host-visible effect — the unlinked zero-state now carriesbusy/loadStarted(matching the built state and theToolbarStatetype),busyis tracked by the render-skip equality check, and dead defensive code (a never-hit command-click dedupeWeakSet, a redundant_previousStatefield, duplicated theme-name resolution) was removed.<vn-grid-toolbar-spacer>no longer receives the per-recompute context push it never used.
Version 1.15.2
- Maintenance (
vanilla-grid,vanilla-grid-toolbar,vanilla-resize-box,people-cities-jsthroughnorthwind-orders-angular,build.js,docs/,tests/): repo-wide folder restructure — the threevanilla-*component folders moved undersrc/, andpeople-cities-jsthroughnorthwind-orders-angularmoved undersamples/. All internal script paths, test references, and documentation links were updated accordingly. No code or public API change.
Version 1.15.1
- The
<vn-grid-toolbar-search>search-fields tooltip is now a hover-only hint: it reveals on hover while the item is unfocused and hides as soon as the item takes focus / is in use (clicked or tabbed into, or theexpandablelens auto-expanding and focusing), so it no longer stays pinned over the input/grid header while the user types. Previously focus (not just the expanded state) also revealed it. No public API change. - Maintenance (
vanilla-grid-toolbar, docs): removed danglingspecs/references from earlier changelog entries and synced the search-fields tooltip wording acrossREADME.md,docs/vanilla-grid-toolbar/, and the.d.ts. No behavior change.
Version 1.15.0
- New search-fields tooltip on
<vn-grid-toolbar-search>: when the attached DataManager restricts free-text search to a subset of fields (gridElement.getSearchFields()— new invanilla-grid1.24.0 — returns a non-empty array), hovering or focusing the item reveals a tooltip listing them. Fully automatic, no new attribute; hidden whenever search is unrestricted (the default, so existing usages are visually unchanged). Reuses<vn-grid-toolbar-info>'s tooltip mechanics; new i18n keysearchFieldsTooltip(default'Searching: {fields}'). - Each listed field renders as its column's header label — resolved via
grid.getHeaderMainText(column)(honoring a host's customformatting.getHeaderMainTextoverride, e.g. an i18n-driven resolver), matched againstgrid.columnsby key/field exactly, then by a dotted path's first segment (Hometown.Name→ a column keyedHometown) — a field with no matching column falls back to the raw field string. - The tooltip stays out of the way: it is suppressed while the
expandablevariant is expanded (the focused, row-spanning box would otherwise pin it permanently over the grid header below — the collapsed lens and the standard presentation reveal it normally), and it right-aligns automatically when the item sits close enough to the toolbar's right edge that the default left-anchored placement would be clipped by the host app'soverflow: hiddenpanels. (Alignment was measured at reveal time here; 1.19.1 moved it earlier — see that entry.)
Version 1.14.1
- Fixed the Carbon/Carbon Dark toolbar rendering 6px shorter than the grid
header it sits above (42px vs. the grid's 48px) —
vn-grid-toolbar-carbon.css/vn-grid-toolbar-carbon-dark.cssnow set the toolbar content padding and command-button height so the toolbar bar aligns with the header bar beneath it. No public API changes.
Version 1.14.0
- New
ToolbarState.busyfield:isLoading || loadStarted— the whole busy window (a filter/sort/search/reload in flight, or an infinite-scroll load-more prefetch), bracketed correctly using only the two recomputes guaranteed to fire, unlike either field alone. Recommended predicate for item-leveldisabled-when/show-when/hide-whenand for hand-writtentoolbarContextsetters. <vn-grid-toolbar-export>now supportsdisabled-when/show-when/hide-when— the same state-predicate attributes<vn-grid-toolbar-command>already has. One deliberate difference:disabled-whenhere combines with the button's built-in auto-disable rules rather than overriding them — it can only add an extra disabling condition, never re-enable an export that has nothing valid to export or is busy-gated.show-when/hide-whenhave no existing default to protect and behave exactly like Command's.<vn-grid-toolbar-export>is now disabled by default while the grid is busy (state.busy), in addition to its existing selection/link check — exporting is comparatively heavy (potentially Worker-offloaded for large datasets) and shouldn't start against a row set that's mid reset→requery. This is a genuine behavior change, but additive only in the safe direction (can only newly disable clicks that were previously live, never enable ones that weren't) — except that it also disables Export during a plain infinite-scroll load-more prefetch, even though already-loaded rows stay valid throughout one; there is no attribute-based opt-out for that case today (see the spec for the full trade-off discussion). A host relying on exporting during a load-more prefetch would see this as a behavior change. No breaking change for thedisabled-when/show-when/hide-whenattributes themselves — absent attributes behave identically to before.
Version 1.13.0
<vn-grid-toolbar-status>no longer flashes "Loaded 0" mid-operation: while the grid is busy with nothing loaded yet — the reset→requery window of a filter/search/reload, or the very first load — the item now renders nothing by default instead of composing the summary from torn-down state (_loadedRowCountis reset to 0 before the re-query resolves, so the old "Loaded 0" was an implementation artifact contradicting the grid's own shimmer, not a measurement). A genuine empty result (load settled, nothing matched) still shows "Loaded 0".show-transientkeeps its meaning but now reads as "renderLoading…/Errorin those windows instead of blank"; hosts that previously added it only to avoid the "Loaded 0" flash (e.g.grid-minimal-js) can drop it. Guard:(isLoading || loadStarted) && loadedCount === 0— the same busy-window signalshow-transientalready used.
Version 1.12.0
- Mobile focus-zoom hardening:
.vn-grid-toolbar-search-inputraises its font-size tomax(current, 16px)under@media (pointer: coarse), so focusing the search box on a touch device no longer triggers iOS Safari's input-focus page zoom (which fires below 16px and persists after blur). Buttons/labels keep the theme's compact--vn-grid-toolbar-font-size. No.d.tschanges — CSS-only. Part of the mobile touch interaction hardening effort — seevanilla-grid's 1.18.0 changelog entry anddocs/vanilla-grid/00-index.md, "Mobile / touch integration". - Maintenance (
vanilla-grid,vanilla-grid-toolbar,people-cities-jsthroughgrid-minimal-js,build.js): fixed annpm run build:obfuscatebug wherevanilla-grid-toolbar.bundle.js(andvanilla-grid.bundle.js) concatenate multiple independently-obfuscated files, whosejavascript-obfuscator-generated top-level helper names could collide across chunks and corrupt string-array state at runtime. Every chunk now gets a uniqueidentifiersPrefix. Also:people-cities-jsthroughgrid-minimal-jsare no longer obfuscated under--obfuscate(minified only); seevanilla-grid's 1.18.0 changelog entry for the full writeup.
Version 1.11.1
- Fixed
<vn-grid-toolbar-status show-transient>failing to showLoading…during the reset→requery window of a filter/sort/reload — it fell through to a torn-down-stateLoaded 0. The grid emits itsLOADINGlifecycle event before its busy flag flips (andsetLoading(true)fires no recompute), soisBusy()still readfalseon the load-start recompute. Added a status-text-onlyloadStartedfield to the toolbar state (trueon theLOADING-triggered recompute), kept separate fromisLoadingso it does not perturb busy-template selection; the transient branch now keys offisLoading || loadStarted. NewloadStartedfield onToolbarStatein the.d.ts.
Version 1.11.0
- Added a toolbar custom-theme registry mirroring the grid's:
VanillaGridToolbarElement.registerTheme(name, cssPath)andVanillaGridToolbarElement.getSupportedThemes()(both added to the.d.ts). A host registers a custom theme on both components (grid + toolbar), and the toolbar's mirrored theme now resolves a registered custom stylesheet instead of silently falling back todefault._updateThemeStylesheet()resolves the href in priority order: owntheme-css-path→ custom registry → built-inSUPPORTED_THEMES→default. - The toolbar now emits a single
console.info(via the toolbar logger, not a warning — divergence is supported and behavior is unchanged) when its resolved theme differs from the linked grid's, deduped per(grid-theme, toolbar-theme)pair and reset on disconnect.
Version 1.10.0
<vn-grid-toolbar-export>now accepts avariantattribute/property (primary|secondary|ghost|danger, defaultsecondary) and renders with the shared.vn-grid-toolbar-commandbutton classes, so the export button matches its sibling command buttons — look, hover, focus, disabled state — across every theme. Added theVanillaGridToolbarExportElementtype (withscope/format/variant) to the.d.ts.- All toolbar text now shares one grid-base font. The per-theme
--vn-grid-toolbar-status-font-family/--vn-grid-toolbar-status-font-sizetokens (previously consumed only by the status text) are renamed to--vn-grid-toolbar-font-family/--vn-grid-toolbar-font-sizeand applied on thevn-grid-toolbarhost, so every text surface — status, status message, command buttons, search input, tooltip — inherits the same family + size as the grid's body rows (previously the command/search/tooltip text used a separate 0.82rem, page-inherited font). Hosts overriding the old--vn-grid-toolbar-status-font-*custom properties must switch to the new names. - Added a
--vn-grid-toolbar-clear-colortoken for<vn-grid-toolbar-clear-selection>'s text on its low-emphasis variants (defaults to the theme accent), and retuned the<vn-grid-toolbar-status>selection-state colours (--vn-grid-toolbar-info-color/--vn-grid-toolbar-success-color) so an unselected summary reads as plain black text and a live selection picks up the theme accent. - Maintenance (colour/text tuning, no API change): retuned toolbar colours and
text settings across the 8
themes/stylesheets and the basevanilla-grid-toolbar.css; rebound the export button variant inpeople-cities-js/-2/-3/-4andwikipedia-pages-vue.
Version 1.9.2
- Inline-comment fixes (comments only — no behavior change): removed a dangling
specs/reference from<vn-grid-toolbar-search>'s JSDoc, and corrected the fallback grid-link comment — the fallback links the nearest<vn-grid>that follows the toolbar in document order (else the first), not a "preceding" one.
Version 1.9.1
- Fixed: the
clearColumnFiltersAndSortingcommand was enabled by a non-empty search term, even though the command does not clear search — so clicking it under a search-only refinement did nothing yet left the button active. It now gates on a newToolbarState.hasFilterOrSortfield (isFiltered || isSorted, search excluded) viadisabled-when="!hasFilterOrSort". The broaderhasActiveRefinements(which still counts search) is retained for host-built "reset everything" controls. Rebound inpeople-cities-js/-2/-3/-5. - The
clearColumnFiltersAndSortingcommand's default glyph (--vn-grid-toolbar-icon-filter) changed from a funnel-with-✕ to descending bars with a corner ✕ — the descending-bars shape is the shared vocabulary for both a filtered list and a sort order, so the icon now reads as clearing both, not just filters. The Material theme keeps its funnel override.
Version 1.9.0
<vn-grid-toolbar-status>numbers (loaded/total/selected) are now formatted through a newtoolbar.formatNumber(value)/setNumberFormatter(fn)hook beforet()interpolation — defaultIntl.NumberFormat(locale)grouping, overridable to match a host's exactformatInteger. This makes the previously-unused autonomous element render identically to a hand-built"Loaded 1,000/500,000"string, so hosts no longer need to re-derive grid state to compose that line themselves.- Behavior change:
<vn-grid-toolbar-status>no longer renders the genericstatusLoading/statusErrortext by default — it renders only the data-volume summary (loaded/total/scroll-more/selected), so it can be mounted alongside a host-driven<vn-grid-toolbar-status-message>without the two duplicating transient state. Add the booleanshow-transientattribute to restore the old behavior. people-cities-js(people + cities),northwind-orders-js(orders), andgithub-repos-js(repos) now mount<vn-grid-toolbar-status>for the row-count line and deleted their bespokeapplyLoadedStatus-style composers; each still drives<vn-grid-toolbar-status-message>for transient env/error text.- Fixed:
<vn-grid-toolbar-status>'s loaded count no longer got stuck at the initial page during infinite scroll. The toolbar now also listens forvanilla-grid's newvn-grid-load-more-succeededevent (1.13.0) — the only signal for a scroll-triggered page append, sinceappendRows()dispatches no event of its own andvn-grid-loadedonly covers the initial load/reload path. Requiresvanilla-grid1.13.0+. - Removed each migrated frontend's redundant "Loading…" push into
<vn-grid-toolbar-status-message>ononBeforeFetch— the grid's own skeleton rows already communicate the busy state, so the extra host message next to the row-count summary was pure noise. - Fixed: on a toolbar that only authors one template state (e.g. no
selectedtemplate because the grid has no selection mode), built-in items (<vn-grid-toolbar-command>,-status,-search, etc.) could silently never receivetoolbarContext—<vn-grid-toolbar>itself was registered viacustomElements.define()before its item sub-components, so the first (and, for a single-state toolbar, only) synchronous render could clone the template while those tags were still plain, un-upgradedHTMLElements. Item tags are now registered strictly beforevn-grid-toolbaritself, so they're always upgraded in time. Host-authored custom items (see the sub-component contract example above) must still be registered before thevanilla-grid-toolbar.js<script>tag loads to benefit on a single-template-state toolbar.
Version 1.8.0
- Breaking: migrated to
vanilla-grid's push-based total-row-count state model (seevanilla-grid's own changelog). The status summary now listens forvn-grid-total-row-count-changed(wasvn-grid-total-rows-resolved) and readsgetTotalRowCount()(wasgetKnownTotalRows()). No shim — hosts on the old grid event/method see the toolbar's "of Y" total stop updating until they upgradevanilla-gridtoo.
Version 1.7.1
- Fixed: reparenting a
<vn-grid-toolbar>element (moving it to a new DOM parent) no longer drops its grid listeners and theme link.disconnectedCallbacknow defers teardown by one microtask and skips it if the element is still connected, mirroring<vn-grid>'s existing reparent tolerance.
Version 1.7.0
- New expandable search variant:
<vn-grid-toolbar-search expandable>(boolean attribute) renders the IBM Carbon DataTable look — collapsed to a lens icon button, expanding into the toolbar row's free space on click (animatedflex-grow; themable via--vn-grid-toolbar-search-expand-duration, disabled underprefers-reduced-motion). A non-empty term pins the box open with the accent border and an inline ✕ clear button that dispatchessearch('')immediately (bypassing thedelaydebounce); blur with an empty input collapses it back;Escapeclears first, collapses second. New i18n keyssearchExpandLabel/searchClearLabel; newVanillaGridToolbarSearchElementdeclaration in the.d.ts. Adopted bynorthwind-orders-jsin both toolbar templates. - The standard (always-visible)
<vn-grid-toolbar-search>now shows the same inline ✕ clear button, overlaid on the input's right edge, plus the accent border, whenever a term is set — an active refinement is never invisible. ✕ (orEscapewith text) clears the term and dispatchessearch('')immediately, bypassing thedelaydebounce; focus stays in the input. The ✕ is the item's own localizable button (searchClearLabel); the native WebKit search-cancel glyph is suppressed for both variants. Picked up automatically by every non-expandablehost —people-cities-js's toolbars included. - Maintenance: search adoption in the sample apps —
people-cities-js(People/Cities toolbars wired to the gridsearchcommand) andnorthwind-orders-js(expandable variant in both templates); Playwright coverage extended accordingly (grid-toolbar-search-item,toolbar-expandable-search,grid-toolbar, sample smoke specs).
Version 1.6.1
- Maintenance: adopted ESLint (see
eslint.config.js) and cleaned up the findings from its first run — removed a dead, never-referencedVN_TOOLBAR_BUILTIN_ICONSinternal constant fromvanilla-grid-toolbar.js(no public API impact) and normalized pre-existing quote-style drift in the same file (mechanical only — verified withgit diff --ignore-all-spaceand the full Playwright suite, no runtime behavior changed).
Version 1.6.0
- A host now only needs a single
<script src="vanilla-grid-toolbar.js">tag: the file self-locates viadocument.currentScriptand auto-loads its siblingvanilla-grid-toolbar-events.jsat runtime (mirroringvanilla-grid.js's own feature auto-loader), exposingwindow.VanillaGridToolbarReady. Awaiting it is optional —<vn-grid-toolbar>and all built-in items are still defined synchronously. Skippable viawindow.VanillaGridToolbarSkipAutoload = trueor adata-skip-autoloadattribute on the toolbar's<script>tag;vanilla-grid-toolbar.bundle.jssets the flag internally since it already concatenates both files. New public API:window.VanillaGridToolbarReadyandwindow.VanillaGridToolbarSkipAutoload(seevanilla-grid-toolbar.d.ts). build.jsnow produces a realvanilla-grid-toolbar.bundle.jsslim-dist artifact (events registry + toolbar concatenated, plus minified CSS/themes), and generalized its module-load-order and console-call audits to check bothvanilla-gridandvanilla-grid-toolbarbundles instead of only the grid's.- Maintenance:
people-cities-jsthroughwikipedia-pages-jsandwikipedia-pages-vuedropped their now-unnecessaryvanilla-grid-toolbar-events.jsscript tags and simplified a few loading status i18n strings (e.g. "Loading rows..." → "Loading...").
Version 1.5.0
<vn-grid-toolbar-clear-selection variant="">: now accepts the same variant catalog as<vn-grid-toolbar-command>(primary/secondary/ghost/danger, defaultsecondary) and renders through the same button classes, so it automatically follows every theme's radius/icon-order/formatting rules instead of a separate hardcoded button style.- Fixed:
variant="primary"rendered white-on-white (invisible) inside the Carbon / Carbon Dark "selected" batch-action bar. That bar remaps--vn-grid-toolbar-accentto white soghost/secondarybuttons stay legible against the blue surface, but theprimaryvariant's fill also reads that same token, producing a white button with white text/icon.themes/vn-grid-toolbar-carbon.css/-carbon-dark.cssnow set dedicated--vn-grid-toolbar-command-primary-bg/-coloroverrides in that state soprimarybuttons invert to a white pill with blue text instead. - Maintenance:
github-repos-js's toolbar command buttons switched tovariant="primary";people-cities-js's clear-selection button set tovariant="ghost".
Version 1.4.0
<vn-grid-toolbar-info>/setInfoMessage(text)now renders\n-separated text as a bulleted list (one concept per line) instead of a single run-on line, via safe DOM node construction (neverinnerHTML). Single-line text is unchanged.- Maintenance:
vanilla-grid-toolbarintegrated intonorthwind-orders-js,github-repos-js,wikipedia-pages-js(plain JS) andwikipedia-pages-vue(Vue, via a newVnGridToolbar.vuewrapper), replacing each app's hand-rolled status bar;build.jsupdated to copy the component into those frontends' dist output.
Version 1.3.0
- New
ToolbarStatefieldsisSortedandhasActiveRefinements(derived: true when an active column filter, an active sort, or a non-empty search term is present).hasActiveRefinementsis the predicate theclearColumnFiltersAndSortingcommand uses viadisabled-when="!hasActiveRefinements". - Added
<vn-grid>tag validation to both linking paths:setGrid(elOrId)and agrid="<id>"attribute that resolves to a non-<vn-grid>element now warn and fall back (to the programmatic.gridproperty / nearest<vn-grid>/null) instead of silently linking the wrong element. - Fixed
<vn-grid-toolbar-search>going stale: it now syncsthis._input.valuefromstate.searchTermwhenever the input is unfocused, not only when the input started out empty — so a host callinggridEl.search('')(or any other programmatic term change) while unfocused correctly clears/updates the visible text. - Performance: binding-node and
toolbarContext-target lists are now collected once per template clone and reused by_applyBindings()/_pushContextToSubtree()/_dropContextFromSubtree(), instead of re-runningquerySelectorAll('*')on every recompute;_recompute()also skips the render +vn-grid-toolbar-state-changeddispatch entirely when the freshly built state is unchanged from the currently-applied one. The publicrefresh()method is exempt (documented as a forced re-render). - Multi-instance theme
<link>support: the theme<link>is now keyed by its resolved stylesheet URL (with a refcount), not a single fixed id, so independent<vn-grid-toolbar>instances with different themes on the same page no longer fight over one shared link — instances resolving to the same stylesheet still share one<link>; instances resolving to different stylesheets each get their own, released when the last referencing instance switches theme or disconnects. See 15-themes-implementation.md §6.1.2. - Removed
<vn-grid-toolbar-export>'s long-press /alt-formatalternate-formatting gesture. The item now only supportsscope+format, dispatched on plainclick. <vn-grid-toolbar-spacer separator>: added--vn-grid-toolbar-separator-width/--vn-grid-toolbar-separator-heightCSS custom properties alongside the existing-colortoken, so each theme can tune the divider's size as well as its color (not just a fixed 14×20 px default).- Material theme: the
clearColumnFiltersAndSortingtoolbar icon now uses the theme's legacy straight-sided funnel glyph (matching the grid header's Material funnel) instead of the shared default lucide icon, keeping a corner-✕ cross so it still reads as "remove filters/sorting". - Fixed a crash surfaced by having more than one
<vn-grid-toolbar>on a page:attributeChangedCallback('grid', …)can fire beforeconnectedCallback()during custom-element upgrade (whengrid="…"is present in the initial HTML), which threwTypeError: Cannot read properties of null (reading 'has')in_resolveStateName()because_templateswasn't populated yet._resolveStateName()now guards against a null_templatesmap. - Maintenance:
build.jsnow buildsvanilla-grid-toolbaras a component (bundle + cache-busted HTML rewrite) alongsidevanilla-grid/vanilla-resize-box;people-cities-js's Cities tab was migrated from a set of manual toolbar buttons to a<vn-grid-toolbar>mirroring the People tab, and its now-dead pre-toolbar code (unused buttons, an orphaned tree-table module pair, unused exports/CSS) was removed.
Version 1.2.0
<vn-grid-toolbar-spacer>: an inert layout spacer / separator.growmakes it absorb the row's free space (e.g. to split a left-hand from a right-hand command group);separatorreveals a theme-owned vertical divider glyph (--vn-grid-toolbar-icon-separator, overridable per theme, masked over--vn-grid-toolbar-separator-color). Both attributes are optional and combine.<vn-grid-toolbar-status-message>: dropped thegrowattribute — use a trailing<vn-grid-toolbar-spacer grow>to fill the row instead.people-cities-jsupdated accordingly.- Removed the
selectAllstandard command from the allow-list (and itsselectAllLabeldefault message). It mapped togridElement.selectAll(), which<vn-grid>does not expose publicly, so the command was already a no-op. The header "select-all" checkbox is unaffected — it is wired inside the grid (_selectAll()/clearSelection()), not via the toolbar. Hosts that want a toolbar button can re-add one withtoolbar.registerCommand('selectAll', (gridEl) => gridEl.grid && gridEl.grid._selectAll()).
Version 1.1.0
<vn-grid-toolbar-command>: a declarative command button with a localized label, an optional theme-aware icon (left/right), andprimary/secondary/ghost/dangervariants. Dispatches standard allow-listed commands directly and custom commands via the new cancelablevn-grid-toolbar-commandevent (VanillaGridToolbarEvents.COMMAND), enabling host/framework logic andpreventDefault()veto of standard commands.toolbar.dispatchCommand(name, arg, source): every command path (items'send(),data-vn-command, the command element) now routes through it and emits the command event before the default action.- Icon registry: theme-owned
--vn-grid-toolbar-icon-<name>glyphs masked overcurrentColor, extensible viaVanillaGridToolbarElement.registerIcon(name, svg). <vn-grid-toolbar-button>re-based as a ghost-variant alias of<vn-grid-toolbar-command>(backward compatible).- Standard-command allow-list tidied: single
reload(wasreload/refresh/reloadDataManager) and singleautoFitAllColumns; theclearColumnFilterscommand is replaced byclearColumnFiltersAndSorting(clears filters + sort, preserves column layout — maps to the new<vn-grid>method).
Version 1.0.0
- Initial release of
<vn-grid-toolbar>: linking (R1), structuredToolbarState(R3), template-driven empty/selected rendering (R2),data-vn-*bindings + command allow-list withregisterCommand(R5), theVanillaGridToolbarItemsub-component contract, the batteries-included item library, an i18n channel, and all 8 themes (R4). <vn-grid-toolbar-status-message>+setStatusMessage()/getStatusMessage(): a host-driven, themed status line that survives template swaps — replaces an app's ad-hoc status bar.<vn-grid-toolbar-info>+setInfoMessage()/getInfoMessage(): a non-clickable, button-shaped "i" indicator that reveals host-supplied details as a themed hover/focus tooltip — replaces an app's ad-hoc "details" strip.- Companion additive
<vn-grid>API:getSearchTerm(),isBusy(),isLoading, and thevn-grid-total-row-count-changedevent.
License
MIT