Lattice Grid Buy a licence

demo D36

Win-loss, gauge, range in htmx

The compact chart set for KPI grids

render: 'winloss' | 'gauge' | 'range'

This grid shows the compact chart set built for KPI columns: a win-loss strip, a gauge against a target, and a range marker. It fits a small visual signal into a normal row, where a full chart would take more space than the metric deserves.

Building…
Loading a live grid…

This is the htmx version. A server-rendered table is upgraded in place, so the page is readable before any script runs and gains the full grid when the module loads, which suits a progressively enhanced app.

The configuration

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

<!-- A real, server-rendered table: readable with JavaScript off. data-field on
     each header names the column that cell becomes, and data-id on each row is
     the key the grid carries it under. -->
<table id="winloss-gauge-range-table" data-lattice-grid>
  <thead>
    <tr>
      <th data-field="service">Service</th>
      <th data-field="team">Team</th>
      <th data-field="streak">Last 14 days</th>
      <th data-field="utilisation">Utilisation</th>
      <th data-field="spread">Latency spread</th>
    </tr>
  </thead>
  <tbody>
    <tr data-id="0"><td>fraud-edge</td><td>Commerce</td><td>[1,-1,1,-1,-1,1,1,-1,-1,1,1,-1,-1,-1]</td><td>27</td><td>[283,222,199,232,269,257,287,303,393]</td></tr>
    <tr data-id="1"><td>payments-stream</td><td>Growth</td><td>[1,1,-1,-1,1,1,-1,-1,-1,1,1,1,-1,-1]</td><td>74</td><td>[172,198,164,171,155,162,189,221,272]</td></tr>
    <tr data-id="2"><td>checkout-edge</td><td>Payments</td><td>[-1,-1,-1,-1,-1,1,-1,-1,-1,-1,1,1,-1,-1]</td><td>54</td><td>[87,63,65,75,89,68,58,59,41]</td></tr>
    <!-- the rest of the rows, rendered by your server -->
  </tbody>
</table>

<script type="module">
  import { readTable, hydrateTable } from 'https://cdn.latticegrid.dev/lattice-grid@1.107.0/modules/htmx.esm.min.js';

  // A table cell carries text, so the columns whose values are whole objects
  // travel as JSON and are read back here. Everything else
  // arrives as the number, boolean or string it already looks like.
  const rows = readTable(document.getElementById('winloss-gauge-range-table')).rows.map((row) => ({
    ...row,
    streak: JSON.parse(row.streak),
    spread: JSON.parse(row.spread),
  }));

  // The markup carries the rows; a server-rendered table's column types are
  // declared in the config hydrateTable takes. The grid replaces the table
  // where it stood.
  const grid = hydrateTable(document.getElementById('winloss-gauge-range-table'), {
    selection: 'multiple',
    toolPanel: {
      side: 'left',
      panels: ['columns', 'filters', 'views', 'quick'],
      actions: ['undo', 'redo', 'export', 'restore', 'maximise'],
      exportName: 'lattice-demo',
    },
    columns: [
      { field: 'service', title: 'Service', layout: { pin: 'start', width: 170 } },
      { field: 'team', title: 'Team', filter: { type: 'set' } },
      // Every mark the same height: the question a win/loss strip answers is
      // how often, never how much.
      { id: 'streak-last-14-days', field: 'streak', title: 'Last 14 days', layout: { width: 190 },
        sort: { enabled: false }, filter: { enabled: false }, cell: { render: 'winloss' } },
      { field: 'utilisation', title: 'Utilisation', type: 'number', layout: { width: 140 },
        format: { suffix: '%' }, cell: { render: 'gauge', props: { min: 0, max: 100 } } },
      // Nine readings drawn as a span with the middle marked, against a
      // pinned scale so the widest row is visibly the widest.
      { id: 'spread-latency-spread', field: 'spread', title: 'Latency spread', layout: { width: 220 },
        sort: { enabled: false }, filter: { enabled: false }, cell: { render: 'range', props: { min: 0, max: 1200 } } },
      { field: 'status', title: 'Status', layout: { width: 130 },
        cell: { render: 'pill', props: { variant: {
          map: { healthy: 'success', degraded: 'warning', failing: 'danger' },
          default: 'neutral',
        } } } },
    ],
    rows,
  });
</script>

Win-loss, gauge and range charts for compact KPI columns

Win-loss, gauge and range are three small chart types for a column of key performance figures, where a full chart would take more space than the metric deserves. A win-loss strip shows a run of wins and losses as a row of ticks, a gauge shows a single value against a minimum and maximum as an arc or dial, and a range shows a value’s position between a low and high bound as a bar. A developer reaches for these where a KPI grid needs to show, at a glance, whether a value is on target or trending well, across dozens of rows, without a dedicated chart panel per row. Lattice Grid selects the type with the render option, set to 'winloss', 'gauge' or 'range', so a column of quarterly targets can switch from a range bar to a gauge dial by changing one string on the column definition, with the value, minimum and maximum read from the row data. Each chart draws to a small canvas sized to the cell, and only cells for rows currently on screen are drawn, so a JavaScript data grid with several such columns keeps its scroll performance on a sheet of ten thousand rows. Colour thresholds are set once per column, so a gauge or range column stays consistent across every row without per-cell styling.

What is a gauge chart in a data grid column?

A gauge chart in a grid column draws a single value as an arc or dial against a set minimum and maximum, inside one cell, so a reader can judge at a glance whether a figure such as capacity used or a score achieved sits low, mid or high on its scale. In Lattice Grid, set render: 'gauge' on a column definition and supply the value, min and max from the row data; the grid draws the dial to a small canvas sized to the cell for each visible row.