Lattice Grid Buy a licence

demo D282

Data bars and icon sets as rules in React

Proportional bars and per-band glyphs painted by the rule engine, undoable and saved with a view

formatting rules: dataBar, iconSet

This grid paints proportional data bars and per-band icon sets from the same ordered rule list that drives its colour scales, and both can be undone and saved with a view. It turns a number into a length or a good, watch, act glyph you read at a glance, as live rules rather than a build step.

Building…
Loading a live grid…

This is the React version. The grid mounts through the createLatticeGrid adapter, which takes its configuration as ordinary props and hands back the live grid through a ref. The grid below is the same one every other tab runs.

The configuration

<link rel="stylesheet" href="https://cdn.latticegrid.dev/lattice-grid@1.107.0/lattice-grid.min.css">

<div id="grid"></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.latticegrid.dev/lattice-grid@1.107.0/lattice-grid.esm.min.js';
  import createLatticeGrid from 'https://cdn.latticegrid.dev/lattice-grid@1.107.0/modules/react.esm.min.js';

  const LatticeGrid = createLatticeGrid({ React, createGrid });

  // Rules keyed by column id. Data bars and icon sets are declarative, so the
  // same rules paint for a server-side export as for the screen.
  const formatting = {
    revenue: [{ id: 'revenue-bar', dataBar: { from: 'minmax', colour: '#5b9bd5' } }],
    delta: [{ id: 'delta-bar', dataBar: { min: -40, max: 40, colour: '#1a7f4b', negativeColour: '#c0392b' } }],
    sla: [{ id: 'sla-icons', iconSet: { set: 'trafficLights', thresholds: [90, 98] } }],
    trend: [{ id: 'trend-icons', iconSet: { set: 'arrows', thresholds: [-5, 5] } }],
  };

  const columns = [
    { field: 'service', title: 'Service', layout: { pin: 'start', width: 150 } },
    { field: 'region', title: 'Region', filter: { type: 'set' } },
    { field: 'revenue', title: 'Revenue', type: 'number', layout: { width: 200 }, format: { style: 'currency', currency: 'USD', decimals: 0 }, filter: { type: 'number' } },
    { field: 'delta', title: 'Change', type: 'number', layout: { width: 170 }, format: { decimals: 1, suffix: '%' } },
    { field: 'sla', title: 'SLA', type: 'number', layout: { width: 130 }, format: { decimals: 1, suffix: '%' } },
    { field: 'trend', title: 'Trend', type: 'number', layout: { width: 120 } },
  ];

  const rows = [/* services: service, region, revenue, delta, sla, trend */];

  function App() {
    return (
      <LatticeGrid
        rowKey="id"
        toolPanel={{ side: 'left', panels: ['formatting', 'columns', 'filters', 'views'] }}
        formatting={formatting}
        columns={columns}
        rows={rows}
        style={{ height: '480px' }}
      />
    );
  }

  createRoot(document.getElementById('grid')).render(<App />);
</script>

Data bars and icon sets that belong to the data

A data bar turns a number into a length you read at a glance, and an icon set turns it into a glyph that says good, watch, act. In Lattice Grid both are rules in the same ordered, per-column list that paints your colour scales, so a bar or an icon set is declared the way a threshold is: formatting: { revenue: [{ dataBar: { from: 'minmax' } }] } for a proportional bar, or { iconSet: { set: 'trafficLights', thresholds: [90, 98] } } for a per-band glyph. A bar can pin its own scale or derive it from the column, and a range that crosses zero grows both ways from a shared axis, growth to the right and contraction to the left in their own colours. Because a bar draws as a background gradient and an icon set as a background image, both sit behind the cell’s own text and need no extra element.

Reaching for the rule engine buys four things the visual keeps for free: it composes with a condition, so a bar can be painted only where another rule says it matters; it derives its bounds from the column over the rows on screen; it rides the undo timeline; and it travels in a saved view, so a person who arranges their grid keeps the bars and icons when they return. The same rule paints for a server-side export as for the browser, so a report generated on the server carries the identical bars and glyphs a reader saw on the page.

When do I use a formatting rule and when a runtime decoration?

Both the rule engine and the runtime decoration API (grid.columns.decorate) can draw a data bar or an icon set, and they are for two different jobs. Reach for a rule when the visual belongs to the data: a rule composes with your other conditions, derives its bounds from the column, rides the undo timeline, travels in a saved view, and paints the same for a server-side export as for the screen. Reach for a runtime decoration when the visual is a presentation choice you flip on after the grid is built and do not want saved with the view or placed on the undo timeline. Put simply: pick the rule when the formatting must survive a reload and reach an export; pick the decoration for a lighter touch you set on the page and clear again. Choosing on that one question keeps the two from feeling like the same feature offered twice.

How do I add a data bar or icon set to a column?

List it as a rule under the column in the formatting option: { dataBar: { from: 'minmax', colour: '#5b9bd5' } } draws a proportional bar across the column’s range, and { iconSet: { set: 'trafficLights', thresholds: [90, 98] } } places a glyph beside the value by the band it falls in. Pin a bar’s scale with min and max, or let from derive it from the column; name a built-in icon set (arrows, trafficLights, ratings) or supply your own glyphs. Because it is a rule, it is undoable, it is saved with the view, and it paints the same way for a server-side export as for the screen.