Drill-Down
Drill-down lets users move between summary and detail inside the Pivot structure. In the current implementation there are two separate drill-down concepts: grouped row expansion and generated column collapse.
Source code
// src/components/pivot/PivotDrillDown.ts
import { defineCustomElements } from '@revolist/revogrid/loader';
defineCustomElements();
import NumberColumnType from '@revolist/revogrid-column-numeral';
import { currentTheme } from '../composables/useRandomData';
import type { DataType } from '@revolist/revogrid';
import { PIVOT_DRILLDOWN, PIVOT_DRILLDOWN_PLUGINS, PIVOT_DRILLDOWN_ROWS } from '../sys-data/pivot.drilldown';
export function load(parentSelector: string, rows: any[] | { isDark?: boolean } = PIVOT_DRILLDOWN_ROWS) {
const { isDark } = currentTheme();
// Ensure rows is an array with data, otherwise fallback to default data
const data = Array.isArray(rows) && rows.length > 0 ? rows : PIVOT_DRILLDOWN_ROWS;
const grid = document.createElement('revo-grid') as any;
grid.className = 'grow h-full w-full cell-border';
grid.range = true;
grid.resize = true;
grid.filter = true;
grid.colSize = 180;
grid.readonly = true;
grid.hideAttribution = true;
grid.theme = isDark() ? 'darkCompact' : 'compact';
grid.columnTypes = {
currency: new NumberColumnType('$0,0.00'),
};
grid.plugins = PIVOT_DRILLDOWN_PLUGINS;
grid.columns = [];
Object.assign(grid, {
pivot: { ...PIVOT_DRILLDOWN },
})
document.querySelector(parentSelector)?.appendChild(grid);
// Set source last after DOM attachment and config
grid.source = data;
return () => {
grid.remove();
};
}
<template>
<RevoGrid
class="grow h-full cell-border"
hide-attribution
range
resize
filter
:colSize="180"
:source="rows"
:pivot.prop="pivot"
:theme="isDark ? 'darkCompact' : 'compact'"
:plugins="plugins"
:column-types="columnTypes"
readonly
/>
</template>
<script setup lang="ts">
import { computed, ref } from 'vue';
import RevoGrid from '@revolist/vue3-datagrid';
import NumberColumnType from '@revolist/revogrid-column-numeral';
import { currentThemeVue } from '../composables/useRandomData';
import { PIVOT_DRILLDOWN, PIVOT_DRILLDOWN_PLUGINS, PIVOT_DRILLDOWN_ROWS } from '../sys-data/pivot.drilldown';
const { isDark } = currentThemeVue();
const rows = ref(PIVOT_DRILLDOWN_ROWS);
const plugins = PIVOT_DRILLDOWN_PLUGINS;
const columnTypes = ref({
currency: new NumberColumnType('$0,0.00'),
});
const pivot = computed(() => ({
...PIVOT_DRILLDOWN,
}));
</script>
// src/components/pivot/PivotDrillDown.tsx
import { useMemo } from 'react';
import { RevoGrid } from '@revolist/react-datagrid';
import NumberColumnType from '@revolist/revogrid-column-numeral';
import { currentTheme } from '../composables/useRandomData';
import { PIVOT_DRILLDOWN, PIVOT_DRILLDOWN_PLUGINS, PIVOT_DRILLDOWN_ROWS } from '../sys-data/pivot.drilldown';
interface PivotDrillDownProps {
rows?: any[];
}
function PivotDrillDown({ rows }: PivotDrillDownProps) {
const { isDark } = currentTheme();
const data = useMemo(() => (Array.isArray(rows) && rows.length > 0 ? rows : PIVOT_DRILLDOWN_ROWS), [rows]);
const columnTypes = useMemo(
() => ({
currency: new NumberColumnType('$0,0.00'),
}),
[],
);
const pivot = useMemo(
() => ({ ...PIVOT_DRILLDOWN }),
[],
);
return (
<RevoGrid
className="grow h-full cell-border"
hideAttribution
range
resize
filter
colSize={180}
source={data}
columns={[]}
pivot={pivot}
theme={isDark() ? 'darkCompact' : 'compact'}
plugins={PIVOT_DRILLDOWN_PLUGINS}
columnTypes={columnTypes}
readonly
/>
);
}
export default PivotDrillDown;
import { Component, ViewEncapsulation, NO_ERRORS_SCHEMA } from '@angular/core';
import { RevoGrid } from '@revolist/angular-datagrid';
import NumberColumnType from '@revolist/revogrid-column-numeral';
import { currentTheme } from '../composables/useRandomData';
import { PIVOT_DRILLDOWN, PIVOT_DRILLDOWN_PLUGINS, PIVOT_DRILLDOWN_ROWS } from '../sys-data/pivot.drilldown';
@Component({
selector: 'pivot-drilldown-grid',
standalone: true,
imports: [RevoGrid],
template: `
<revo-grid
class="grow h-full cell-border"
[hideAttribution]="true"
[range]="true"
[resize]="true"
[filter]="true"
[colSize]="180"
[source]="rows"
[pivot]="pivot"
[theme]="theme"
[plugins]="plugins"
[columnTypes]="columnTypes"
[readonly]="true"
></revo-grid>
`,
encapsulation: ViewEncapsulation.None,
// Allows Angular demos to bind RevoGrid plugin props that are not wrapper inputs.
schemas: [NO_ERRORS_SCHEMA],
})
export class PivotDrillDownGridComponent {
rows = PIVOT_DRILLDOWN_ROWS;
theme = currentTheme().isDark() ? 'darkCompact' : 'compact';
columnTypes = {
currency: new NumberColumnType('$0,0.00'),
};
plugins = PIVOT_DRILLDOWN_PLUGINS;
pivot = { ...PIVOT_DRILLDOWN };
}
import type { GridPlugin } from '@revolist/revogrid';
import { PivotPlugin, type PivotConfig } from '@revolist/revogrid-enterprise';
import { AdvanceFilterPlugin, ColumnCollapsePlugin, RowOddPlugin, commonAggregators } from '@revolist/revogrid-pro';
import { PIVOT_TIME_RANGE_ROWS } from './pivot.shared';
export const PIVOT_DRILLDOWN_ROWS = PIVOT_TIME_RANGE_ROWS;
export const PIVOT_DRILLDOWN_PLUGINS: GridPlugin[] = [
PivotPlugin,
ColumnCollapsePlugin,
AdvanceFilterPlugin,
RowOddPlugin,
] as any[];
export const PIVOT_DRILLDOWN: PivotConfig = {
dimensions: [
{ prop: 'region', sortable: true },
{ prop: 'rep', sortable: true },
{ prop: 'year', sortable: true },
{ prop: 'quarter', sortable: true },
{
prop: 'sales',
name: 'Sales',
columnType: 'currency',
aggregators: {
sum: commonAggregators.sum,
},
},
],
rows: ['region', 'rep'],
columns: ['year', 'quarter'],
values: [{ prop: 'sales', aggregator: 'sum' }],
collapsed: true,
groupAggregations: true,
columnCollapse: {
collapsed: true,
aggregator: 'sum',
},
};
Row Drill-Down
Section titled “Row Drill-Down”Row drill-down is enabled when:
- the effective row hierarchy has more than one level
collapsedis a boolean,expandedstate exists, orrowAxisLayoutis'compact'
const pivot: PivotConfig = { rows: ['region', 'rep'], values: [{ prop: 'sales', aggregator: 'sum' }], collapsed: true,};Behavior:
collapsed: truestarts grouped rows collapsedcollapsed: falsestarts grouped rows expandedexpandedstores per-group overrides using grouped path keys- active row drill-down uses one compact Rows column by default
rowAxisLayout: 'tabular'keeps one visible column per configured row field
Compact mode changes only the generated grid columns. The Pivot model, field
panel, persisted configuration, and remote requests continue to use separate
region and rep dimensions.
const pivot: PivotConfig = { rows: ['region', 'rep'], values: [{ prop: 'sales', aggregator: 'sum' }], collapsed: true, rowAxisLayout: 'tabular', // opt back into separate Region and Rep columns};PivotPlugin translates the Pivot row structure into RevoGrid grouping options, then emits pivot-config-update when users expand or collapse groups.
Grouped Row Aggregates
Section titled “Grouped Row Aggregates”Set groupAggregations: true to render aggregate values inside grouped Pivot rows.
const pivot: PivotConfig = { rows: ['region', 'rep'], columns: ['year'], values: [{ prop: 'sales', aggregator: 'sum' }], collapsed: true, groupAggregations: true,};Behavior:
- the grouped row still shows the expand/collapse UI and group label
- generated Pivot value columns show the aggregate for the current grouped branch
- nested groups aggregate only their own descendants
- collapsed groups still show their aggregate values
If you hide the grouped row field column (for example with hide-columns="region"), you can move the grouped label into the first visible row cell:
const pivot: PivotConfig = { rows: ['region', 'product'], columns: ['quarter'], values: [{ prop: 'revenue', aggregator: 'sum' }], collapsed: true, rowAxisLayout: 'tabular', groupAggregations: true, groupLabelColumn: 'firstVisible',};groupLabelColumn modes:
'grouped'(default): keep label in the grouped field column'firstVisible': render label in the first currently visible row cell
Current limitation:
groupAggregationscurrently applies to the client-side Pivot path- server-side Pivot still renders grouped labels only unless backend-precomputed grouped aggregates are added
Column Drill-Down
Section titled “Column Drill-Down”Column drill-down is built into Pivot whenever columns is non-empty. Generated
groups start collapsed without additional configuration.
columns: ['year', 'quarter', 'month']Behavior:
- generated Pivot column groups become collapsible
- generated groups start collapsed at the highest level and expand one level at a time
- set
columnCollapse: falseor{ enabled: false }to disable column drill-down - set
columnCollapse: { collapsed: false }to start with all generated groups expanded - use
columnLevelsto override collapse, subtotal, filter, and sort behavior independently at each zero-based column level - collapsed groups render a placeholder header/cell
- placeholder cells use a collapsed-bucket aggregator, not a naive sum of visible child cells
- expanded subtotal columns appear before descendant columns by default, keeping the parent aggregate visible and actionable
columnLevels[level].subtotalValuescan keep that aggregate to one value field—or any ordered subset—when the Pivot has multiple measures- expanding reveals one level only; descendants and sibling branches keep their current state
- aggregate filters and sorting remain attached to the same analytical group when its compact and expanded presentations switch
Persisted state for generated groups lives in collapsedColumns.
See Column Totals With Drill-Down for the complete subtotal/collapse behavior matrix, precedence rules, and screenshots.
Related Options
Section titled “Related Options”collapsed: row drill-down defaultexpanded: persisted row group expansion stategroupAggregations: render aggregate values inside grouped client-side Pivot rowsgroupLabelColumn: choose where grouped row labels render ('grouped'or'firstVisible')columnCollapse: enable and configure generated column collapsecolumnLevels: configure each generated column hierarchy levelcollapsedColumns: persisted generated column collapse state
Placeholder Behavior
Section titled “Placeholder Behavior”When a column group is collapsed, Pivot can render:
- a default placeholder header based on the group name
- a custom placeholder header or cell template
- a custom collapsed aggregator, globally or per value field
This is useful when a collapsed analytical bucket should still show a meaningful aggregate instead of just hiding all child columns.
State Persistence
Section titled “State Persistence”PivotPlugin emits pivot-config-update as users interact with grouped rows or collapsible columns. That event is the right place to persist the current Pivot state if you want drill-down state to survive reloads.
Common Mistakes
Section titled “Common Mistakes”- Expecting row drill-down with only one row field.
- Expecting grouped rows to show metric values without enabling
groupAggregations. - Expecting
columnCollapseto work without generated column groups. - Treating
expandedas UI row indexes. It is persisted grouped state, not viewport position.
Continue with Sorting And Filtering or Plugin Lifecycle.