Lattice Grid Buy a licence

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

Building…
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>