Lattice Grid Buy a licence

react data grid · kpi tiles

React Grid: KPI Tiles

A row of live figures beside the grid, reading the same rows it holds: a running total, an average measured against a baseline, a count that changes colour past a threshold, and a sparkline, all moving together as the data changes.

Also in: Angular

Install and import

npm install @toclocoinc/lattice-grid
# free on localhost; a production domain needs a licence key (see /pricing/)

The files

lattice.ts

// lattice.ts
import React from 'react';
import { createGrid } from '@toclocoinc/lattice-grid';
import '@toclocoinc/lattice-grid/css';
import { createKPI } from '@toclocoinc/lattice-grid/modules/kpi';
import { createLatticeReact } from '@toclocoinc/lattice-grid/modules/react';

export const { LatticeGrid, LatticeKPI } = createLatticeReact({ React, createGrid, createKPI });

Pipeline.tsx

// Pipeline.tsx
import React from 'react';
import { createRoot } from 'react-dom/client';
import { LatticeGrid, LatticeKPI } from './lattice';

const MONEY = { style: 'currency', currency: 'USD', decimals: 0 };
const OPEN = { warn: 14, critical: 22, direction: 'lowerIsBetter' };

const columns = [
  { field: 'id', title: 'Ref', layout: { width: 96, pin: 'start' } },
  { field: 'region', title: 'Region', filter: { type: 'set' } },
  { field: 'stage', title: 'Stage', filter: { type: 'set' } },
  { field: 'value', title: 'Value', type: 'number', format: MONEY, total: 'sum' },
];

const rows = [/* deal records: id, region, stage, value */];

// Each tile is an aggregate over the same rows: a running total, an average
// against a baseline, a count with a good/warn/critical band, and a
// sparkline over the biggest deal.
const tiles = [
  { id: 'revenue', label: 'Pipeline value', aggregation: 'sum', field: 'value', format: 'currency' },
  { id: 'avg', label: 'Average deal vs $12k', aggregation: 'avg', field: 'value', format: { type: 'currency', decimals: 0 }, baseline: 12000 },
  { id: 'open', label: 'Still open', aggregation: 'count', filter: (r: any) => r.stage !== 'won', thresholds: OPEN },
  { id: 'biggest', label: 'Biggest deal', aggregation: 'max', field: 'value', format: 'currency', sparkline: 'value' },
];

function Pipeline() {
  const [grid, setGrid] = React.useState<any>(null);

  // The KPI panel takes the live grid as a prop, once the grid reports it is
  // ready, and reads the same rows a chart or a board would.
  return (
    <>
      {grid && <LatticeKPI grid={grid} columns={4} tiles={tiles} style={{ marginBottom: 12 }} />}
      <LatticeGrid rowKey="id" columns={columns} rows={rows} onGridReady={setGrid} style={{ height: '320px' }} />
    </>
  );
}

createRoot(document.getElementById('pipeline')!).render(<Pipeline />);

See it running

Building…
Loading a live grid…

Open this demo's React tab →

Working in React

createLatticeReact, not createLatticeGrid alone. A KPI panel is a second component, built from the same call as the grid so both share one React and one createGrid. Import only createKPI alongside it and nothing else about a board, a chart or the Gantt is pulled in.

The panel waits for its grid. A template ref cannot tell a sibling the grid has arrived, because writing to a ref re-renders nobody; onGridReady is the documented way a KPI panel (or a chart) learns which instance to read, which is why <LatticeKPI> is only rendered once grid is set.

Tiles update on a grid event, not a React one. A tile recomputes when the grid's own rows or filter changes, so filtering the grid moves every tile automatically, with no state of your own to keep in sync.

Related