demo D222
Chaining, three levels deep
sales → revenue per rep → top three and a profile, with tiles reading the middle level; one filter moves all of it
source: { mode: 'derived', from, groupBy, profile } · createStat
Loading a live grid…
The configuration
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@toclocoinc/lattice-grid@1.28.0/lattice-grid.min.css">
<div id="app"></div>
<script type="module">
import React from 'https://esm.sh/react@18';
import { createRoot } from 'https://esm.sh/react-dom@18/client';
import { createGrid, createStat } from 'https://cdn.jsdelivr.net/npm/@toclocoinc/lattice-grid@1.28.0/lattice-grid.esm.min.js';
import createLatticeGrid from 'https://cdn.jsdelivr.net/npm/@toclocoinc/lattice-grid@1.28.0/modules/react.esm.min.js';
const LatticeGrid = createLatticeGrid({ React, createGrid });
const USD = { style: 'currency', currency: 'USD', maximumFractionDigits: 0 };
const WHOLE = { maximumFractionDigits: 0 };
const bookColumns = [
{ field: 'rep', title: 'Rep' },
{ field: 'region', title: 'Region', filter: { type: 'set' } },
{ field: 'amount', title: 'Deal', type: 'number', format: USD, total: 'sum' },
];
const byRepColumns = [
{ field: 'rep', title: 'Rep' },
{ field: 'revenue', title: 'Revenue', type: 'number', format: USD },
{ field: 'deals', title: 'Deals', type: 'number' },
];
const top3Columns = [
{ field: 'rep', title: 'Rep' },
{ field: 'revenue', title: 'Revenue', type: 'number', format: USD },
];
const profileColumns = [
{ field: 'column', title: 'Measure' },
{ field: 'mean', title: 'Mean', type: 'number', format: WHOLE },
{ field: 'stddev', title: 'Spread', type: 'number', format: WHOLE },
{ field: 'min', title: 'Weakest', type: 'number', format: WHOLE },
{ field: 'max', title: 'Strongest', type: 'number', format: WHOLE },
];
const rows = [/* e.g. [{ id: 'S0', rep: 'Ana Bright', region: 'EMEA', amount: 2140 }, ...] */];
function App() {
const bookRef = React.useRef(null);
const byRepRef = React.useRef(null);
const tilesRef = React.useRef(null);
const [byRep, setByRep] = React.useState(null);
const [top3, setTop3] = React.useState(null);
const [profile, setProfile] = React.useState(null);
// Level 1: revenue per rep, derived from the book and following its filter.
// The group value is the identity, so there is no rowKey to give.
React.useEffect(() => {
const book = bookRef.current && bookRef.current.grid;
if (book) setByRep({ mode: 'derived', from: book, follow: 'filtered', refresh: 'live', groupBy: 'rep',
select: { revenue: { of: 'amount', fn: 'sum' }, deals: { fn: 'count' } },
sort: [{ col: 'revenue', dir: 'desc' }] });
}, []);
// Level 2: derived from byRep, not from the book, so the chain composes.
// Runs once byRep is a live instance, reached through its ref.
React.useEffect(() => {
const rep = byRepRef.current && byRepRef.current.grid;
if (!byRep || !rep) return;
// Top three reps, and a one-row profile transposing byRep's revenues.
setTop3({ mode: 'derived', from: rep, refresh: 'live', sort: [{ col: 'revenue', dir: 'desc' }], limit: 3 });
setProfile({ mode: 'derived', from: rep, profile: ['revenue'], refresh: 'live' });
// Each tile is one reduced figure over byRep. Concentration is a Gini
// ratio, so it renders as a plain number; show: 'rep' turns max into an
// argmax, naming the rep behind the figure.
const tile = () => tilesRef.current.appendChild(document.createElement('div'));
createStat({ grid: rep, container: tile(), title: 'Revenue concentration', of: 'revenue', fn: 'gini', goodWhen: 'down', footer: '0 = even, 1 = one rep has it all' });
createStat({ grid: rep, container: tile(), title: 'Best rep', of: 'revenue', fn: 'max', show: 'rep' });
createStat({ grid: rep, container: tile(), title: 'Weakest rep', of: 'revenue', fn: 'min', show: 'rep' });
createStat({ grid: rep, container: tile(), title: 'Spread across reps', of: 'revenue', fn: 'stddev' });
createStat({ grid: rep, container: tile(), title: 'Reps selling', fn: 'count' });
}, [byRep]);
return (
<>
<div ref={tilesRef} style={{ display: 'grid', gridTemplateColumns: 'repeat(5, 1fr)', gap: '12px', marginBottom: '16px' }} />
<LatticeGrid
ref={bookRef}
rowKey="id"
toolPanel={{ side: 'left', panels: ['filters', 'columns'] }}
columns={bookColumns}
rows={rows}
style={{ height: '320px' }}
/>
<div style={{ display: 'flex', gap: '16px', marginTop: '16px' }}>
{byRep && <LatticeGrid ref={byRepRef} autoHeight source={byRep} columns={byRepColumns} style={{ flex: 1 }} />}
{top3 && <LatticeGrid autoHeight source={top3} columns={top3Columns} style={{ flex: 1 }} />}
{profile && <LatticeGrid autoHeight source={profile} columns={profileColumns} style={{ flex: 1 }} />}
</div>
</>
);
}
createRoot(document.getElementById('app')).render(<App />);
</script>