Skip to content

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
TypeScriptts
// 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();
  };
}
Vuevue
<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>
Reacttsx
// 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;
Angularts
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 };
}
Data & Configts
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 is enabled when:

  • the effective row hierarchy has more than one level
  • collapsed is a boolean, expanded state exists, or rowAxisLayout is 'compact'
const pivot: PivotConfig = {
rows: ['region', 'rep'],
values: [{ prop: 'sales', aggregator: 'sum' }],
collapsed: true,
};

Behavior:

  • collapsed: true starts grouped rows collapsed
  • collapsed: false starts grouped rows expanded
  • expanded stores 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.

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:

  • groupAggregations currently 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 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: false or { enabled: false } to disable column drill-down
  • set columnCollapse: { collapsed: false } to start with all generated groups expanded
  • use columnLevels to 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].subtotalValues can 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.

  • collapsed: row drill-down default
  • expanded: persisted row group expansion state
  • groupAggregations: render aggregate values inside grouped client-side Pivot rows
  • groupLabelColumn: choose where grouped row labels render ('grouped' or 'firstVisible')
  • columnCollapse: enable and configure generated column collapse
  • columnLevels: configure each generated column hierarchy level
  • collapsedColumns: persisted generated column collapse state

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.

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.

  • Expecting row drill-down with only one row field.
  • Expecting grouped rows to show metric values without enabling groupAggregations.
  • Expecting columnCollapse to work without generated column groups.
  • Treating expanded as UI row indexes. It is persisted grouped state, not viewport position.

Continue with Sorting And Filtering or Plugin Lifecycle.