Skip to content

Smart Panel

SmartPanelPlugin adds a compact status surface that stays synchronized with RevoGrid. It reads selection, focus, row visibility, tree/group nodes, pinned viewports, checkbox row selection, formulas, and source changes from grid providers instead of inspecting rendered DOM cells.

Use the defaults for spreadsheet-style selection statistics, or replace the complete item list with application-specific formulas and resolvers. The panel can sit directly below the grid or render into an application-owned host.

RevoGrid with a selected revenue range and a Smart Panel showing rows, range, cells, count, sum, average, minimum, and maximum.

The default panel adds row context on the left and selection statistics on the right. Items appear only when their visibility conditions are met.

Install the plugin and enable range selection:

import { SmartPanelPlugin } from '@revolist/revogrid-pro';
const grid = document.querySelector<HTMLRevoGridElement>('revo-grid')!;
grid.range = true;
grid.plugins = [SmartPanelPlugin];

With no smartPanel configuration, the plugin creates a panel immediately after <revo-grid>. It always shows the number of main data rows. When a range contains more than one cell it can also show:

  • A1-style range address
  • Selected cell and numeric-value counts
  • Sum, average, minimum, and maximum
  • Visible versus total rows when filtering, tree collapse, grouping, or other trimming hides rows

The panel refreshes after edits, source and column updates, filtering, sorting, trimming, pagination, row order, focus/range changes, multi-range changes, checkbox row selection, and tree-state changes. Bursts are coalesced before the provider snapshot is rebuilt.

Replace items to control exactly what appears:

grid.smartPanel = {
locale: 'en-US',
numberFormat: {
maximumFractionDigits: 0,
},
items: [
{
id: 'rows',
label: 'Rows',
metric: 'visibleRows',
align: 'left',
},
{
id: 'range',
label: 'Range',
metric: 'rangeAddress',
align: 'left',
visible: ({ snapshot }) => snapshot.selectedCells.length > 1,
},
{
id: 'revenue',
label: 'Selected revenue',
formula: '=SUM(SELECTION)',
align: 'right',
format: value => `$${Number(value).toLocaleString('en-US')}`,
},
],
};

Every item needs a stable id and one value source: metric, formula, or resolve. If more than one is supplied, resolution uses resolve, then formula, then metric.

MetricResult
selectedCellsUnique selected cells across the active and multi-ranges
selectedRowsCheckbox-selected rows when RowSelectPlugin is installed; otherwise rows represented by the cell range
visibleSelectedRowsSelected rows that remain visible after trimming
selectedColumnsUnique columns represented by the selected cells
visibleRows / totalRows / hiddenRowsMain data-row counts; synthetic grouping rows are excluded
visibleNodes / totalNodesMain row-store nodes, including synthetic tree or grouping nodes
focusedCell / focusedColumnCurrent A1 address or column label
rangeAddressOne or more A1 ranges, with pinned viewport ownership when needed
numericCount, sum, average, min, maxFinite numeric values from selected cells
distinctUnique selected values

Formula items use the same evaluator as RevoGrid formulas. Two dynamic names are available:

  • SELECTION — all values in the current item scope
  • VALUES — an equivalent alias that reads naturally for row/column formulas
grid.smartPanel = {
items: [
{ id: 'median', label: 'Median', formula: '=MEDIAN(SELECTION)' },
{ id: 'spread', label: 'Spread', formula: '=MAX(VALUES)-MIN(VALUES)' },
{
id: 'visible-arr',
label: 'Visible ARR',
formula: '=SUM(VALUES)',
scope: 'visibleRows',
column: 'arr',
},
{
id: 'all-arr',
label: 'All ARR',
formula: '=SUM(VALUES)',
scope: 'allRows',
column: 'arr',
},
],
};

scope defaults to selection. For visibleRows and allRows, set column to evaluate one field. Without column, the panel flattens enumerable values from every row in that scope.

Custom resolvers receive the complete provider-backed snapshot and may be synchronous or asynchronous:

grid.smartPanel = {
className: 'operations-panel',
items: [
{
id: 'visibility',
label: 'Visible accounts',
align: 'left',
resolve: ({ snapshot }) =>
`${snapshot.visibleRowCount} of ${snapshot.totalRowCount}`,
},
{
id: 'regions',
label: 'Regions',
align: 'center',
resolve: ({ snapshot }) =>
new Set(snapshot.visibleRows.map(row => row.region)).size,
},
{
id: 'pipeline',
label: 'Pipeline',
scope: 'visibleRows',
column: 'pipeline',
formula: '=SUM(VALUES)',
align: 'right',
format: value => `$${Number(value).toLocaleString('en-US')}`,
},
{
id: 'health',
label: 'Health',
align: 'right',
resolve: ({ snapshot }) =>
snapshot.visibleRows.every(row => row.health === 'On track')
? 'On track'
: 'Needs attention',
className: 'operations-panel__health',
},
],
};
A customized RevoGrid Smart Panel showing visible accounts, regions, pipeline value, and portfolio health.

Formula items and application resolvers can share the same left, center, and right aligned panel.

The resolver context contains:

FieldDescription
gridHost HTMLRevoGridElement
providersRevoGrid plugin providers for advanced integrations
snapshotOne immutable-by-convention snapshot shared by every item in the refresh
itemCurrent item configuration
valuesValues resolved from the item’s scope and optional column

snapshot includes ranges, selected cells and raw/evaluated values, checkbox and range rows, selected columns, visible/all main rows, visible/all rows across pinned stores, data-row and node counts, focus metadata, and a revision number.

PropertyDefaultPurpose
labelnoneMuted text before the value
alignrightPlaces the item in the left, center, or right group
orderdeclaration indexOrders items inside an alignment group
priority0Secondary sort and a data-priority styling hook
visibletrueBoolean or snapshot predicate controlling whether the item renders
formatpanel formatterConverts the resolved value into display text
rendernoneReturns a safe Node or text string for custom presentation
classNamenoneAdds an application class to the item
titlenoneStatic string or context callback for the native title

Strings returned from render or a resolver are inserted as text, never as raw HTML. Return a DOM Node when richer content is required:

{
id: 'status',
render: value => {
const badge = document.createElement('strong');
badge.className = 'status-badge';
badge.textContent = String(value);
return badge;
},
resolve: ({ snapshot }) =>
snapshot.hiddenRowCount ? 'Filtered' : 'Complete',
}

Pass an element or a callback when layout belongs to the application:

<div class="grid-layout">
<revo-grid id="accounts"></revo-grid>
<aside id="grid-insights"></aside>
</div>
grid.smartPanel = {
host: () => document.querySelector('#grid-insights'),
items,
};
grid.plugins = [SmartPanelPlugin];

The plugin never removes a user-owned host. Assign a new smartPanel object or call plugin.setHost(nextHost) to move the existing component. Passing null returns it to a plugin-owned host immediately after the grid.

PropertyDefaultDescription
hostpanel after gridElement or callback providing the panel container
itemsspreadsheet defaultsComplete ordered item configuration
classNamenoneAdditional class on the panel host
ariaLabelGrid summaryAccessible name for the status region
hiddenWhenEmptyfalseHides the host when no configured items resolve visibly
localebrowser localeLocale passed to Intl.NumberFormat
numberFormat{ maximumFractionDigits: 2 }Default numeric formatting options
formatValuebuilt-in formatterPanel-wide value formatter used before item-level format
onErrornoneReceives an isolated item/formula error
schedulemicrotaskUse animationFrame to align refreshes with visual frame updates

Assign false to hide the installed panel without removing the plugin:

grid.smartPanel = false;

Direct property assignments are observable. The legacy additionalData.smartPanel configuration remains available for compatibility, but new integrations should use grid.smartPanel.

Retrieve the plugin instance through RevoGrid’s plugin service:

const smartPanel = (await grid.getPlugins())
.find(plugin => plugin instanceof SmartPanelPlugin);
const snapshot = smartPanel?.getSnapshot();
const items = smartPanel?.getItems();
await smartPanel?.refresh();
smartPanel?.setHost(document.querySelector('#grid-insights'));
MethodPurpose
getSnapshot()Returns the last committed provider snapshot
getItems()Returns a copy of the last resolved item list
getComponent()Returns the presentation-only SmartPanelComponent
refresh()Immediately rebuilds and renders the snapshot; resolves when committed
setHost(host)Moves the panel to a user host or back to an owned host with null
EventDetailPurpose
smartpanelchange{ snapshot, items }Fires after the latest asynchronous refresh commits
smartpanelerror{ error, item }Reports one failed item while healthy items continue rendering

When async item resolvers overlap, only the newest refresh commits. Use onError or smartpanelerror for telemetry without taking down the rest of the panel.

The Pro stylesheet includes responsive wrapping at 720px. Customize the panel through its public variables and stable classes:

.operations-panel {
--revo-smart-panel-background: #101615;
--revo-smart-panel-color: #d7e2de;
--revo-smart-panel-border: #2a3834;
--revo-smart-panel-gap: 24px;
border-radius: 12px;
&__health .revo-smart-panel__value {
color: #32d89b;
}
}
SelectorRole
.revo-smart-panelHost status region
.revo-smart-panel__group--leftLeft item group
.revo-smart-panel__group--centerCenter item group
.revo-smart-panel__group--rightRight item group
.revo-smart-panel__itemOne resolved item
.revo-smart-panel__labelItem label
.revo-smart-panel__valueText or custom rendered value

Spreadsheet status

Keep the defaults for range address, count, sum, average, min, and max.

Filtered data status

Combine visible/total row metrics with formulas scoped to visibleRows.

Tree and grouping

Use node metrics beside data-row metrics to distinguish structural rows.

Application insights

Mount into a dashboard shell and add async resolvers or custom Nodes.

The component uses role="status", aria-live="polite", and the configured accessible label. Keep item labels concise so selection gestures do not create overly verbose screen-reader announcements.