demo D23
Durations
Elapsed time that sorts numerically and reads as 3h 12m
type: 'duration'
The configuration
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@toclocoinc/lattice-grid@1.13.0/lattice-grid.min.css">
<script src="https://cdn.jsdelivr.net/npm/@toclocoinc/lattice-grid@1.13.0/lattice-grid.min.js"></script>
<div id="grid" style="height: 540px"></div>
<script>
const grid = LatticeGrid.createGrid(document.getElementById('grid'), {
rowKey: 'id',
toolPanel: {
side: 'left',
panels: ['columns', 'filters', 'views', 'quick'],
actions: ['undo', 'redo', 'export', 'restore', 'maximise'],
exportName: 'lattice-demo',
},
columns: [
{ field: 'pipeline', title: 'Pipeline', filter: { type: 'set' }, layout: { width: 130, pin: 'start' } },
{ field: 'stage', title: 'Stage', filter: { type: 'set' }, layout: { width: 130 } },
{
field: 'outcome', title: 'Outcome', filter: { type: 'set' }, layout: { width: 120 },
cell: { decoration: 'pill', variant: { map: { ok: 'success', retried: 'warning', failed: 'danger' } } },
},
// Milliseconds in, "3h 12m" out. The value stays a number, so the sort
// is numeric, the range filter takes numbers and the total is a sum.
{ field: 'elapsed', title: 'Elapsed', type: 'duration', layout: { width: 140 }, total: 'sum' },
{ field: 'queued', title: 'Queued', type: 'duration', layout: { width: 140 }, total: 'sum' },
{ field: 'cpu', title: 'CPU time', type: 'duration', layout: { width: 140 }, total: 'sum' },
{
field: 'elapsedText', title: 'Elapsed, as text', type: 'text', layout: { width: 170 },
// The same elapsed time as a person would have typed it. Sort this
// column and then the one three to its left: this is the case the
// type exists for.
filter: { type: 'text' },
},
{
id: 'share', title: 'CPU / elapsed', type: 'number', layout: { width: 150 },
format: { style: 'percent', decimals: 0 },
value: { deps: ['elapsed', 'cpu'], pure: true, compute: (d) => (d.elapsed ? d.cpu / d.elapsed : null) },
},
{ field: 'records', title: 'Records', type: 'number', layout: { width: 130 }, format: { notation: 'compact' }, total: 'sum' },
],
rows, // pipeline runs, durations stored in milliseconds
});
</script>
Formatting and sorting elapsed time correctly
Elapsed time is a poor fit for the number or date types: a raw second count is unreadable in a cell, and a formatted string like “3h 12m” sorts alphabetically rather than by duration, so “1h 5m” ends up after “10h 0m”. Lattice Grid’s type: 'duration' column stores the underlying value as seconds (or milliseconds, if you prefer) and keeps the comparator numeric while the renderer shows the short form a person actually reads, so a column of build times, session lengths or SLA countdowns sorts by true elapsed time even though every cell displays as hours and minutes rather than a number. A developer reaches for this type wherever a JavaScript data grid needs to show a span rather than a point: task duration, video length, uptime, time-to-resolution. Because the comparator works on the stored numeric value rather than the rendered string, sorting a column of durations costs the same as sorting any other numeric column, with no string-parsing step inserted per row, and it stays correct at any row count the grid virtualises.
The renderer also accepts a threshold so unusually long or short durations can carry a variant, letting a duration column double as an at-a-glance SLA indicator without a separate formatting rule.
How do you sort a duration column correctly in a data grid?
Store the duration as a plain number of seconds or milliseconds and let the grid’s type: 'duration' comparator sort on that value, rather than sorting the formatted string. Sorting the display text (“3h 12m”, “45m”) puts short-looking strings ahead of longer numeric durations; sorting the underlying seconds keeps the order numerically correct regardless of how compact the rendered label is.