demo D227
Cross-filtering, the path back up
Two summary panels over one book; click a row in either and it filters the book, so the other narrows
crossFilter: true · crossFilter.toggle
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 } 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 bookColumns = [
{ field: 'rep', title: 'Rep' },
{ field: 'region', title: 'Region' },
{ field: 'amount', title: 'Deal', type: 'number', format: USD, total: 'sum' },
];
const regionColumns = [
{ field: 'region', title: 'Region' },
{ field: 'revenue', title: 'Revenue', type: 'number', format: USD },
];
const repColumns = [
{ field: 'rep', title: 'Rep' },
{ field: 'revenue', title: 'Revenue', type: 'number', format: USD },
];
const rows = [/* e.g. [{ id: 'S0', rep: 'Ana', region: 'EMEA', amount: 1830 }, ...] */];
function App() {
const bookRef = React.useRef(null);
const regionRef = React.useRef(null);
const repRef = React.useRef(null);
const [byRegion, setByRegion] = React.useState(null);
const [byRep, setByRep] = React.useState(null);
// Each summary panel derives from the live book and follows its filter.
// crossFilter: true lets a panel push the clicked group onto the book as an
// ordinary filter, so the other panel re-derives and both narrow each other
// while staying whole.
React.useEffect(() => {
const book = bookRef.current && bookRef.current.grid;
if (!book) return;
const panel = (col) => ({ mode: 'derived', from: book, follow: 'filtered', refresh: 'live', groupBy: col, crossFilter: true,
select: { revenue: { of: 'amount', fn: 'sum' }, deals: { fn: 'count' } }, sort: [{ col: 'revenue', dir: 'desc' }] });
setByRegion(panel('region'));
setByRep(panel('rep'));
}, []);
// A click on a panel row toggles that group as the book's filter.
React.useEffect(() => {
const g = regionRef.current && regionRef.current.grid;
if (g) g.on('row:click', (e) => g.crossFilter.toggle(e.key));
}, [byRegion]);
React.useEffect(() => {
const g = repRef.current && repRef.current.grid;
if (g) g.on('row:click', (e) => g.crossFilter.toggle(e.key));
}, [byRep]);
return (
<>
<LatticeGrid
ref={bookRef}
rowKey="id"
toolPanel={{ side: 'left', panels: ['filters', 'columns'] }}
columns={bookColumns}
rows={rows}
style={{ height: '300px' }}
/>
<div style={{ display: 'flex', gap: '16px', marginTop: '16px' }}>
{byRegion && <LatticeGrid ref={regionRef} autoHeight source={byRegion} columns={regionColumns} style={{ flex: 1 }} />}
{byRep && <LatticeGrid ref={repRef} autoHeight source={byRep} columns={repColumns} style={{ flex: 1 }} />}
</div>
</>
);
}
createRoot(document.getElementById('app')).render(<App />);
</script>