sveltelean Table
Versionv0.2.0 GitHub

The engine takes an optional getRowModel; groupBy() is one. It runs the same search, filters and sorting as the default model, buckets the sorted rows by one or more columns, computes the aggregates the columns declare, and hands the result to the same pagination. Group rows are nodes with a stable id derived from the group path, so an expanded group stays expanded when the rows underneath re-sort.

This is what "supporting a feature does not mean shipping it" looks like in a build: the grouping entry measures 2148 B gzip and the pivot entry 526 B gzip. A production build of a plain DataTable contains neither module: the table-basic fixture asserts both exclusions in its module graph.

entry points
import { groupBy, summarize, aggregate } from '@svelte-lean/table/grouping';
import { pivot } from '@svelte-lean/table/pivot';

// A grouped row model: register it on the table.
<DataTable getRowModel={groupBy(['team', 'region'])} … />

Row grouping

Derive group rows and aggregates with the optional grouping entry point.

Row grouping
Revenue: 60531Progress: 45
Amara Okafor
Engineering
Europe
$4,501
20%
Selin Aydın
Engineering
Americas
$36,177
72%
Hana Kowalska
Engineering
Asia
$19,853
43%
Revenue: 84288Progress: 58
Revenue: 60045Progress: 71
Revenue: 83802Progress: 57

RowGrouping.svelte
<script lang="ts">
	import { DataTable } from '@svelte-lean/table';
	import { groupBy } from '@svelte-lean/table/grouping';
	import { memberColumns, pick } from '$lib/demo/columns';
	import { people } from '$lib/demo/data';

	const data = people(12);
	// revenue carries aggregate: 'sum' and progress aggregate: 'mean'; group rows show both.
	const columns = pick(memberColumns(), ['name', 'team', 'region', 'revenue', 'progress']);
</script>

<DataTable
	{data}
	{columns}
	getRowId={(row) => row.id}
	getRowModel={groupBy(['team'])}
	state={{ expanded: ['__group:["team:string:\"Engineering\""]'] }}
	pagination={false}
	label="Members by team"
/>
Rows grouped by team. Each group row is a header cell with a toggle button carrying aria-expanded, the member count and the aggregates of the revenue (sum) and progress (mean) columns. The Engineering group starts expanded through state.expanded and the group id format.
  • Nested grouping is a list of column ids in order; each level adds a depth.
  • Tree input (getSubRows) and grouping are separate row models; combining them throws.
  • Search and filters apply to the rows before grouping, so an empty group disappears.

Summaries

Render totals aligned with visible and pinned columns.

Summaries
Amara Okafor
Engineering
$4,501
20%
Jonas Lindqvist
Design
$12,420
33%
Mei Tanaka
Operations
$20,339
46%
Rafael Duarte
Growth
$28,258
59%
Selin Aydın
Engineering
$36,177
72%
Noah Brenner
Design
$44,096
85%
Priya Raman
Operations
$4,015
98%
Elias Moreau
Growth
$11,934
30%
Total$161,74055% mean

Summaries.svelte
<script lang="ts">
	import { DataTable } from '@svelte-lean/table';
	import { summarize } from '@svelte-lean/table/grouping';
	import { memberColumns, pick } from '$lib/demo/columns';
	import { money, people } from '$lib/demo/data';

	const data = people(8);
	const columns = pick(memberColumns(), ['name', 'team', 'revenue', 'progress']);
	// summarize() returns one value per column with an aggregate; format it for the tfoot.
	const totals = summarize(data, columns);
	const summary = {
		name: 'Total',
		revenue: money.format(Number(totals.revenue)),
		progress: `${Math.round(Number(totals.progress))}% mean`
	};
</script>

<DataTable {data} {columns} getRowId={(row) => row.id} {summary} pagination={false} label="Members" />
summarize() returns one value per column that declares an aggregate; the specimen formats them and passes the object as summary, which renders a tfoot row keyed by column id. Pinned columns and hidden columns are respected because the row is rendered from the visible columns.

Pivot

Turn long-form records into a compact cross-tab using an optional pure helper.

Pivot
Engineering
$10,587
$105,615
$56,643
$172,845
Design
$80,400
$34,344
$129,372
$244,116
Operations
$9,129
$104,157
$58,101
$171,387
Growth
$81,858
$32,886
$127,914
$242,658

Pivot.svelte
<script lang="ts">
	import { DataTable } from '@svelte-lean/table';
	import { pivot } from '@svelte-lean/table/pivot';
	import { money, people } from '$lib/demo/data';

	// Long-form members become one row per team with one column per region.
	const result = pivot(people(36), {
		row: (item) => item.team,
		column: (item) => item.region,
		value: (item) => item.revenue,
		aggregate: 'sum',
		rowLabel: 'Team'
	});
	const columns = result.columns.map((column) =>
		column.id === 'label' ? column : { ...column, format: (v: unknown) => money.format(Number(v)) }
	);
</script>

<DataTable data={result.data} {columns} getRowId={(row) => row.id} bordered pagination={false} label="Revenue by team and region" />
pivot() returns rows and columns for a second DataTable: one row per team, one column per region, a pinned label column and a total. The aggregate is the same function grouping uses. No workbook or chart dependency is involved.

API

NameOfTypeDefaultDescription
getRowModelData and identity(context: ModelContext<T>) => RowModel<T>createRowModelReplaces the client row model; groupBy() from the grouping entry is the shipped alternative.
summaryEvents and compositionRecord<string, string | number>undefinedA <tfoot> row keyed by leaf column id; summarize() from the grouping entry produces one.
aggregateSort, filter and aggregate'sum' | 'mean' | 'min' | 'max' | 'count' | ((values: unknown[]) => unknown)undefinedAggregate used by groupBy() and summarize().
expandedTableStateRowId[][]Expanded details, tree and group row ids.
AggregateEvents and positions'sum' | 'mean' | 'min' | 'max' | 'count' | ((values: unknown[]) => unknown)–An aggregate name or function.
RowNode<T>Row model{ id; data?; depth; children; kind: 'row' | 'group'; label?; count?; aggregates? }–A rendered row or a group row.
@svelte-lean/table/groupingEntry pointsgroupBy · summarize · aggregate–A grouped row model for getRowModel, a totals object for summary, and the aggregate function both use.
@svelte-lean/table/pivotEntry pointspivot–Long-form records to a cross-tab: rows, columns and totals for a second DataTable.