<vn-grid-toolbar> — Usage Guide
1. Load the script (after the grid)
<script src="vanilla-grid/vanilla-grid.js"></script>
<script src="vanilla-grid-toolbar/vanilla-grid-toolbar.js"></script>
One <script> tag is all a host needs: vanilla-grid-toolbar.js auto-loads
vanilla-grid-toolbar-events.js itself 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
Implementation § Loading & bundling.
2. Link a toolbar to a grid
<vn-grid id="ordersGrid" selection-mode="multiple" row-key-field="Id"></vn-grid>
<vn-grid-toolbar grid="ordersGrid"> … </vn-grid-toolbar>
Or programmatically: toolbar.setGrid('ordersGrid') / toolbar.grid = gridEl.
3. Author the two state templates
<vn-grid-toolbar grid="ordersGrid">
<template data-vn-grid-toolbar="empty">
<vn-grid-toolbar-status></vn-grid-toolbar-status>
<vn-grid-toolbar-search placeholder="Search orders…" delay="300"></vn-grid-toolbar-search>
<vn-grid-toolbar-button command="autoFitAllColumns" title-key="autoFitTitle">⤢</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>
</template>
</vn-grid-toolbar>
4. Built-in items
| Element | Drives | Notes |
|---|---|---|
<vn-grid-toolbar-status> |
counts / total / busy / selection | localizable composite line, derived from grid state; uses the theme's grid body-row font (family + size). |
<vn-grid-toolbar-status-message> |
toolbar.setStatusMessage(text, severity) |
host-driven free text; uses the grid body font + main text color for the theme; renders as plain grid text for info/success, only error tints the text (no background); survives template swaps. |
<vn-grid-toolbar-spacer grow separator> |
— (presentational) | inert layout spacer; grow absorbs free space (push left/right groups apart — it does not centre anything, see §4d); separator shows a theme-owned | divider (--vn-grid-toolbar-icon-separator); both optional and combinable. |
<vn-grid-toolbar-group align="start|center|end"> |
— (presentational) | layout wrapper that clusters related items so they move as one unit; a start/end pair genuinely centres a center group between them. See §4d. |
<vn-grid-toolbar-info> |
toolbar.setInfoMessage(text) (or static text attr) |
non-clickable, button-shaped "i" indicator; reveals the text as a themed hover/focus tooltip; hides when empty; survives template swaps. Pass \n-separated lines to render a bulleted list (one concept per line) instead of one run-on line. Shares <vn-grid-toolbar-search>'s edge-aware placement, so an item near the row's right end opens its tooltip leftward instead of overhanging. |
<vn-grid-toolbar-search delay="" placeholder="" expandable> |
gridElement.search(term) |
debounced. While a term is set, both variants show an accent border and an inline ✕ clear button; ✕ / Escape dispatch search('') immediately (no debounce). Boolean expandable switches to the Carbon-style expandable look: collapsed to a lens button, expanding into the row's free space on click; a non-empty term pins it open. Escape clears first, collapses second (expandable only). See §4b. When the attached DataManager restricts search to a subset of fields, hovering the item (while unfocused) reveals a tooltip listing them, hidden as soon as the item is focused / in use — no attribute, see §4c. |
<vn-grid-toolbar-export scope="selected|all" variant="" disabled-when="" show-when="" hide-when=""> |
exportToExcel |
exports an .xlsx file of scope's rows. Auto-disabled until selection when scope="selected", and while the grid is busy (state.busy — filter/sort/search/reload in flight, or a load-more prefetch). Handles the export's promise: a cancelled export or a click during a running one is ignored, any other failure is logged (the grid's own overlay shows it to the user). Shares <vn-grid-toolbar-command>'s button styling + variant catalog (default secondary), so it matches its sibling command buttons across themes — set variant to match the neighbouring commands (e.g. ghost). disabled-when adds to the built-in disable rules rather than overriding them (can't re-enable an export with nothing to export, or one that's busy-gated); show-when/hide-when behave exactly like <vn-grid-toolbar-command>'s. |
<vn-grid-toolbar-command command="" …> |
standard or custom command | rich command button — localized label, themed icon (left/right), variant; see §6b. |
<vn-grid-toolbar-button command="" title-key="" disabled-when=""> |
allow-list command | minimal button; re-based on -command (ghost variant, slotted text). |
<vn-grid-toolbar-selection-count hide-when-empty> |
selectedCount |
badge/text. |
<vn-grid-toolbar-clear-selection variant=""> |
clearSelection |
shown only when there is a selection; shares <vn-grid-toolbar-command>'s variant catalog (default secondary), see §6b. |
4a. Standard search — inline ✕ clear button
The default (always-visible) <vn-grid-toolbar-search> shows an inline ✕
clear button overlaid on the input's right edge whenever a term is set, and
switches the input border to the accent color, so an active refinement is
never invisible. Clicking ✕ (or pressing Escape with text present) clears
the term and dispatches search('') immediately, bypassing the delay
debounce; focus stays in the input. The ✕ is the item's own button (the
native WebKit search-cancel glyph is suppressed), localizable via the
searchClearLabel i18n key.
4b. Expandable search (<vn-grid-toolbar-search expandable>)
The boolean expandable attribute opts the search item into the
IBM Carbon DataTable presentation, with three class-driven visual states:
- collapsed (initial) — only a lens icon button is visible (theme glyph
--vn-grid-toolbar-icon-search); the input is not rendered/tabbable. - expanded-empty — clicking the lens expands the box into the toolbar
row's free space (an animated
flex-grow; siblings keep their size) and focuses the input. Losing focus with an empty input collapses it back. - expanded-active — a non-empty term switches the border to the accent
color, shows an inline ✕ clear button at the far right, and pins the box
open even when unfocused (an active refinement is never hidden). ✕ (and
Escape) clears the term and dispatchessearch('')immediately, bypassing thedelaydebounce; ✕ also collapses and refocuses the lens, whileEscapeon an already-empty input collapses.
Everything else is unchanged from the default variant: delay, placeholder,
the search command routing, and the external-term re-sync. Localize the two
button labels via the searchExpandLabel / searchClearLabel i18n keys. The
expand/collapse duration is themable via
--vn-grid-toolbar-search-expand-duration (default 0.15s; disabled under
prefers-reduced-motion: reduce).
4c. Search-fields tooltip
Both search presentations automatically reveal a hover hint (shown on hover
while the item is unfocused) when
gridElement.getSearchFields() reports a restricted field list — e.g. a
StaticDataManager constructed with searchFields: [...], or an
ODataDataManager constructed with searchMode: 'filter', searchFields: [...]. No attribute: it's driven entirely by the attached DataManager and
hidden whenever search is unrestricted (the default). Each field renders as
its column's header label (matched against gridElement.grid.columns by
key/field, or by a dotted path's first segment — e.g. Hometown.Name against
a column keyed Hometown), falling back to the raw field string when no
column matches; localize via the searchFieldsTooltip i18n key (default
'Searching: {fields}').
Two behaviors keep the tooltip out of the way:
- Hidden as soon as the item is focused / in use — it is a hover hint,
not a while-you-type overlay. Clicking or tabbing into the input (or the
expandablelens auto-expanding and focusing) hides it, so it never pins permanently over the grid header below while the user types. This also covers the expanded box, which stays focused for the whole interaction. - Edge-aware alignment — the tooltip is left-anchored by default, but
flips to right-anchored when the item sits close enough to the toolbar's
(or viewport's) right edge that the left-anchored box would be clipped by
the host app's
overflow: hiddenpanels. Placement is settled as soon as the hint is built and re-settled whenever the item's presentation changes (theexpandableinput ⇄ lens flip), not at hover time: the hidden box is still laid out, so a tooltip left overhanging the viewport would widen the page and raise a horizontal scrollbar before anyone hovers it. Nothing to configure — the item watches its own geometry and re-anchors whenever it changes, so placement is correct after a late-arriving stylesheet, a container resize or a window resize, without the tooltip ever being revealed.
const dm = new ODataDataManager({
baseUrl: '/api/Orders',
searchMode: 'filter',
searchFields: ['ShipName', 'ShipCity', 'Customer.CompanyName']
});
gridElement.setDataManager(dm);
// No column is keyed "ShipName"/"ShipCity"/"Customer.CompanyName" in this
// example, so the raw field strings are shown as-is:
// "Searching: ShipName, ShipCity and Customer.CompanyName"
// A grid with a column { key: 'ShipCity', label: 'Ship City' } would show
// that column's header label instead of the raw "ShipCity" field.
4d. Layout: groups vs spacer grow
Two idioms push items apart in the toolbar row, and they are not interchangeable:
<vn-grid-toolbar-spacer grow>absorbs free space between the items on either side of it. One spacer between a left block and a right block pushes them to the row's two ends — the common two-block layout, and less markup than wrapping both sides in groups.<vn-grid-toolbar-group align="start|center|end">is for genuinely centring a group of items. Aspacer growpair does not centre what sits between them — two equal spacers split the row's free space equally, which centres the middle group in the gap between the two side blocks, not in the row. Whenever the two side blocks differ in width the middle group sits off-centre at rest, and whenever any sibling grows (e.g. anexpandablesearch opening) the group visibly slides, by half of whatever the sibling gained. Reach for astart/center/endgroup triple instead:<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>The
start/endpair always takes an equal share of the row's free space (flex: 1 1 0), so thecentergroup's midpoint is the row's midpoint regardless of what either side contains — and stays there while a sibling like theexpandablesearch grows, because it now grows into its own group's share of the row instead of taking room from the centre.The side groups carry a
min-width: min-contentfloor: a side that genuinely cannot fit its equal share grows past it and pushes the centre, rather than overflowing the row. The centre group stays shrinkable (flex: 0 1 auto; min-width: 0— simply not settingflex-shrink: 0), so an oversized centre gives way rather than forcing a document scrollbar.<vn-grid-toolbar-group>with noalignis a plain, non-elastic cluster (flex: 0 0 auto) — useful purely to group related items so they share spacing and wrap as a unit, with no zone role.
Groups and spacers compose freely; a group is just another flex item. Pick a
spacer grow pair for the common "push these two blocks apart" layout, and a
group triple whenever something in the row must actually stay centred.
5. Custom sub-components
Extend window.VanillaGridToolbarItem, implement contextChanged(), and use the
helpers (this.state, this.gridElement, this.selectedKeys,
this.send(command, arg), this.t(key, params)).
5b. Command buttons (<vn-grid-toolbar-command>)
A declarative button that dispatches a standard grid command (run directly) or a custom command (handled by your app), with a localized label, an optional theme-aware icon, and a visual variant. See Implementation § Commands.
<!-- standard command, runs the grid method directly -->
<vn-grid-toolbar-command command="exportToExcel" arg='{"scope":"all"}'
label-key="exportAllLabel" icon="download" icon-position="right" variant="primary">
</vn-grid-toolbar-command>
<!-- custom command, handled by a host listener -->
<vn-grid-toolbar-command command="archiveSelected" label-key="archiveLabel"
icon="clear" variant="danger" show-when="hasSelection">
</vn-grid-toolbar-command>
Every command click fires a cancelable vn-grid-toolbar-command event (it
bubbles), whose detail carries { command, arg, grid, gridElement, toolbar, state, selectedKeys, selectedRows, source }. If the command is allow-listed/
registered it also runs as the default action, unless a listener calls
preventDefault():
toolbar.addEventListener('vn-grid-toolbar-command', (e) => {
if (e.detail.command === 'archiveSelected') {
myApi.archive(e.detail.selectedKeys).then(() => e.detail.gridElement.reload());
}
// veto a standard command:
if (e.detail.command === 'clearPersistedSettings' && !confirm('Reset?')) e.preventDefault();
});
Because it is a standard custom element + DOM event, it works from React /
Angular / Vue (bind the event; pass object args via the .commandArg property).
React ≤18 attaches the listener via a ref.
Icons are theme-owned --vn-grid-toolbar-icon-<name> CSS variables masked
over currentColor (so they inherit the theme color and the selection-bar
white-flip). Built-ins: download, refresh, reset, auto-fit, view,
filter, clear, search, info, close, chevron-down, separator. (filter is
descending bars — the shared glyph for a filtered list and a sort order — with a
small corner ✕; it's the clearColumnFiltersAndSorting glyph, so it reads as
remove both the filters and the sort. The Material theme overrides it with a
Google filter_list_off-style variant: the descending bars struck through by a
full diagonal slash, deliberately distinct from its grid-header filter_alt
funnel — the header funnel applies a filter, this glyph removes them.) Extend with
VanillaGridToolbarElement.registerIcon('star', '<svg…>'), or supply a one-off
inline <svg data-vn-icon> child (multi-color glyphs).
Icon placement relative to the label is per-command via icon-position="left|right"
(default left). The Carbon and Carbon Dark themes invert this for every
command (icon trails the text) to match Carbon's button conventions — a
theme-scoped rule in themes/vn-grid-toolbar-carbon[-dark].css, no markup change.
<vn-grid-toolbar-button> is the minimal alias (ghost variant, slotted text).
<vn-grid-toolbar-clear-selection> renders through the same button classes as
<vn-grid-toolbar-command> and accepts the same variant attribute (default
secondary, with accent-tinted text on the secondary/ghost variants), so
it automatically follows every theme's radius/icon-order/formatting rules
instead of drifting from its sibling command buttons.
6. Localization
toolbar.setMessages({ statusLoadedTotal: 'Mostrati {loaded}/{total}', searchPlaceholder: 'Cerca…' });
toolbar.locale = 'it';
The toolbar renders a few strings itself (e.g. the <vn-grid-toolbar-clear-selection>
button label, key clearSelectionLabel). These come from the built-in English
default bundle until you override them with setMessages — so a localized app
should push translated values for them, otherwise the default (Clear) is shown.
people-cities-js does this in localizeStaticUiTexts().
7. Theming
The toolbar mirrors the grid's theme automatically. Override with
<vn-grid-toolbar theme="carbon-dark"> or a theme-css-path.
<vn-grid-toolbar-status> text color by selection state: the status line
picks one of two theme tokens depending on whether the grid has a selection —
--vn-grid-toolbar-info-color while nothing is selected (also used for the
transient Loading… line) and --vn-grid-toolbar-success-color once a
selection exists. Each theme tunes the pair: the light themes (default,
fiori, fluent, material) show black with no selection and the theme's blue
once selected; carbon shows black → white and carbon-dark white → white (the
selection bar turns solid blue); glow and glow-dark keep their indigo no-
selection color and switch to black / white respectively when selected.
<vn-grid-toolbar-clear-selection> ("Cancel") text color: on its
low-emphasis secondary/ghost variants the button text uses
--vn-grid-toolbar-clear-color, which defaults to the theme accent. The light
themes (default, material, fiori, fluent) override it to black; carbon
and carbon-dark pin it to white on their blue selection bar (set on the
selected content so it resolves in the button's own context); glow-dark pins
it to white; the remaining themes (glow) keep the accent tint.
Host icon buttons: prefer <vn-grid-toolbar-command> (§6b) — its masked icons
already inherit --vn-grid-toolbar-btn-color (re-mapped to white in the Carbon
selection bar) with no per-host CSS. If you must hand-roll a raw <button>, color
its SVG with stroke="currentColor" and set
color: var(--vn-grid-toolbar-btn-color) so it flips with the component-owned
controls instead of staying dark.
Framework note: Vue, React, Angular (and other non-HTML-parser renderers)
The toolbar renders its states by cloning the .content DocumentFragment of
its <template data-vn-grid-toolbar="…"> children. Framework-rendered children
of a <template> element land in its childNodes — not in .content
(only the HTML parser populates that) — so templates authored inside a Vue/JSX
render tree clone as empty. Route the template markup through innerHTML
instead (Vue: v-html with a static app-authored string; React/Angular:
create the HTMLTemplateElement programmatically and assign innerHTML),
which parses it as HTML and populates .content. See
wikipedia-pages-vue/src/components/VnGridToolbar.vue for a complete Vue wrapper
(attribute forwarding, vn-grid-toolbar-command event bridge, imperative API
via defineExpose), github-repos-react/src/vanilla/VnGridToolbar.tsx for the
React equivalent (a thin component exposing a ref handle), and
northwind-orders-angular/src/app/vanilla/vn-grid-toolbar.directive.ts for the
Angular equivalent (an element-selector directive).
Templates may also be appended after the element is already connected —
frameworks like React and Angular attach the element to the document before
host code can add children. The toolbar re-reads its state templates whenever the grid
link is (re)established (grid attribute set/changed, setGrid()) and on
every refresh(), so the reliable late order is: append the <template>
children first, then set the grid attribute (or call refresh() if the grid
was already linked).
Reference integration
See people-cities-js (People tab): the .status-container is a full-width
<vn-grid-toolbar> with empty/selected templates containing a host-driven
<vn-grid-toolbar-status-message> followed by a
<vn-grid-toolbar-spacer grow> that pushes the trailing controls to the right
edge, a plain (non-expandable) <vn-grid-toolbar-search delay="600"> in both
templates, a <vn-grid-toolbar-export scope="selected"> (the built-in
item — no host sub-component needed), <vn-grid-toolbar-command>
buttons for the standard commands (reload, autoFitAllColumns,
clearColumnFiltersAndSorting, clearPersistedSettings), a custom
command="viewSelection"
(gated via disabled-when="!hasSingleSelection", handled by the app through the
vn-grid-toolbar-command event to open the detail panel),
<vn-grid-toolbar-clear-selection>, and a <vn-grid-toolbar-info> whose tooltip
carries the response conversion-strategy / size / compression details. The app feeds the status
line and info tooltip through peopleToolbar.setStatusMessage() /
setInfoMessage(), pushes the command titles/labels via setMessages(), and
overrides the standard exportToExcel command via
peopleToolbar.registerCommand('exportToExcel', …) so the app-specific file
name/sheet name and device-aware headersOnly (touch devices) apply — the
theme-matched styling itself is automatic (exportToExcel()'s themeStyle
default), no bespoke sub-component needed
(see exportGridSelection() in toolbar-helpers.js).
northwind-orders-js / -3 / -4 follow the same pattern (status-message +
standard commands in the empty template; export + a custom viewSelection
command + clear-selection in the selected template; exportToExcel overridden
via registerCommand, viewSelection handled through the
vn-grid-toolbar-command event; the host-driven status line appends the live
selection count).
northwind-orders-js additionally hosts a
<vn-grid-toolbar-search expandable delay="600"> in both templates — the
item re-syncs from the grid's search term after a template swap, so the
entered text survives selection changes (and, being expandable, a non-empty
term re-renders the fresh clone already expanded; a transient empty expansion
is intentionally not preserved across swaps).
wikipedia-pages-vue is the Vue integration (see the framework note above).