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

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:

<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

Version 1.20.0

Version 1.19.1

Version 1.19.0

Version 1.18.1

Version 1.18.0

Version 1.17.1

Version 1.17.0

Version 1.16.3

Version 1.16.2

Version 1.16.1

Version 1.16.0

Version 1.15.2

Version 1.15.1

Version 1.15.0

Version 1.14.1

Version 1.14.0

Version 1.13.0

Version 1.12.0

Version 1.11.1

Version 1.11.0

Version 1.10.0

Version 1.9.2

Version 1.9.1

Version 1.9.0

Version 1.8.0

Version 1.7.1

Version 1.7.0

Version 1.6.1

Version 1.6.0

Version 1.5.0

Version 1.4.0

Version 1.3.0

Version 1.2.0

Version 1.1.0

Version 1.0.0

License

MIT