Grouping & trees
Group rows with totals, nest rows as a tree, open detail panels, drag rows into order.
Code for this demo
import { createGrid, type ColumnDef, type GridApi, type GridOptions, type Row } from '@freegridjs/core';
import { makeRows } from '../data';
import { button, separator, text, tr, type DemoContext } from '../ui';
type Mode = 'group' | 'tree' | 'detail' | 'drag';
export default function mount(ctx: DemoContext) {
const { lang } = ctx;
const h = (en: string, ko: string) => tr(lang, { en, ko });
const money = { type: 'number', format: '#,##0', width: 120, aggFunc: 'sum' } as const;
const sales: ColumnDef[] = [
{ field: 'country', header: h('Country', '국가'), width: 150 },
{ field: 'company', header: h('Company', '회사'), width: 140 },
{ field: 'name', header: h('Name', '이름'), width: 170 },
{ field: 'q1', header: h('Q1 revenue', '1분기 매출'), ...money },
{ field: 'q2', header: h('Q2 revenue', '2분기 매출'), ...money },
{ field: 'total', header: h('Total', '합계'), ...money, width: 130 },
{ field: 'id', header: h('People', '인원'), type: 'number', width: 90, aggFunc: 'count' },
];
// An org chart: departments contain teams, teams contain people.
const org: Row[] = [
{ id: 'hq', parent: null, name: h('Head office', '본사'), role: '', budget: 1200 },
{ id: 'dev', parent: 'hq', name: h('Development', '개발본부'), role: '', budget: 640 },
{ id: 'web', parent: 'dev', name: h('Web team', '웹팀'), role: '', budget: 260 },
{ id: 'p1', parent: 'web', name: '김민준', role: h('Lead', '팀장'), budget: 0 },
{ id: 'p2', parent: 'web', name: '이서연', role: h('Engineer', '개발자'), budget: 0 },
{ id: 'app', parent: 'dev', name: h('App team', '앱팀'), role: '', budget: 380 },
{ id: 'p3', parent: 'app', name: '박지호', role: h('Engineer', '개발자'), budget: 0 },
{ id: 'biz', parent: 'hq', name: h('Business', '사업본부'), role: '', budget: 560 },
{ id: 'p4', parent: 'biz', name: '최유나', role: h('Manager', '매니저'), budget: 0 },
{ id: 'p5', parent: 'biz', name: '정하준', role: h('Sales', '영업'), budget: 0 },
];
let grid: GridApi | null = null;
const show = (mode: Mode) => {
grid?.destroy();
let options: GridOptions;
if (mode === 'tree') {
options = {
columns: [
{ field: 'name', header: h('Organization', '조직'), width: 260 },
{ field: 'role', header: h('Role', '직책'), width: 140 },
{ field: 'budget', header: h('Budget (M)', '예산(백만)'), type: 'number', format: '#,##0', width: 140 },
],
data: org.map(r => ({ ...r })),
getRowId: r => r.id,
treeData: { getParentId: r => r.parent },
groupDefaultExpanded: 1,
};
} else if (mode === 'detail') {
options = {
columns: sales.map(({ aggFunc, ...c }) => c),
data: makeRows(200),
detailHeight: 180,
detailRenderer: ({ row }) => {
// a small grid inside the panel: the person's quarters
const el = document.createElement('div');
el.style.height = '158px';
createGrid(el, {
columns: [
{ field: 'quarter', header: h('Quarter', '분기'), width: 120 },
{ field: 'revenue', header: h('Revenue', '매출'), type: 'number', format: '#,##0', width: 160 },
],
data: ['q1', 'q2', 'q3', 'q4'].map((q, i) => ({ quarter: `${i + 1}Q`, revenue: row[q] })),
locale: lang,
rowHeight: 26,
});
return el;
},
};
} else if (mode === 'drag') {
options = { columns: sales.slice(0, 4).map(({ aggFunc, ...c }) => c), data: makeRows(30), rowDrag: true };
} else {
options = { columns: sales, data: makeRows(1000), groupBy: ['country'], groupPanel: true };
}
grid = createGrid(ctx.host, { locale: lang, theme: 'board', ...options });
grid.on('groupToggle', e => ctx.log(`groupToggle → ${e.key}: ${e.expanded}`));
grid.on('groupChange', e => ctx.log(`groupChange → [${e.groupBy.join(', ')}]`));
grid.on('detailToggle', e => ctx.log(`detailToggle → ${e.row.name}: ${e.expanded}`));
grid.on('rowDragEnd', e => ctx.log(`rowDragEnd → ${e.row.name}: ${e.fromIndex} → ${e.toIndex}`));
};
text(ctx, h('Drag a column header into the bar above the grid to add a grouping level.',
'열 머리글을 그리드 위 막대로 끌어오면 묶음 단계가 늘어납니다.'));
separator(ctx);
button(ctx, h('Row grouping', '행 그룹핑'), () => show('group'));
button(ctx, h('Tree data', '트리 데이터'), () => show('tree'));
button(ctx, h('Master / detail', '마스터/디테일'), () => show('detail'));
button(ctx, h('Row drag', '행 드래그'), () => show('drag'));
separator(ctx);
button(ctx, h('Expand all', '모두 펼치기'), () => grid?.expandAll());
button(ctx, h('Collapse all', '모두 접기'), () => grid?.collapseAll());
button(ctx, h('Group by country, then company', '국가 → 회사로 묶기'), () => grid?.setGroupBy(['country', 'company']));
show('group');
return grid;
}