demo D102
Scrubbing back through time
Drag back and watch the grid, and the charts, run backwards
grid.timeline
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',
selection: 'multiple',
toolPanel: {
side: 'left',
panels: ['columns', 'filters', 'views', 'quick'],
actions: ['pause', 'undo', 'redo', 'export', 'maximise'],
exportName: 'lattice-demo',
},
statusBar: { panels: ['rowCount', 'progress', 'updates', 'selectedCount'] },
highlightOnChange: { duration: 600 },
// A smaller log than the default, because this page holds it for as long as
// the visitor stays. Six hundred changes is a couple of minutes of a busy
// feed, which is more than enough to scrub through.
updates: { flush: 'frame', logLimit: 600, logRows: 40000 },
columns: [
{ field: 'symbol', title: 'Symbol', layout: { width: 130, pin: 'start' } },
{ field: 'desk', title: 'Desk', filter: { type: 'set' } },
{ field: 'region', title: 'Region', filter: { type: 'set' } },
{ field: 'price', title: 'Price', type: 'number', layout: { width: 120 },
format: { decimals: 4 }, filter: { type: 'number' } },
{ field: 'change', title: 'Change', type: 'number', layout: { width: 110 },
format: { style: 'percent', decimals: 2 } },
{ field: 'ticks', title: 'Ticks', type: 'number', layout: { width: 90 } },
{ field: 'volume', title: 'Volume', type: 'number', layout: { width: 120 },
format: { notation: 'compact' }, filter: { type: 'number' } },
{ field: 'status', title: 'Status', filter: { type: 'set' }, layout: { width: 110 },
cell: { decoration: 'pill', variant: { map: { open: 'success', halted: 'danger', settled: 'neutral' } } } },
],
rows, // 4,000 instruments
});
// Nothing is scrubbable until attach(): what a value used to be is not
// recoverable after the fact, so the recording has to start first.
grid.timeline.attach();
socket.onmessage = (event) => {
grid.rows.queue({ update: JSON.parse(event.data) });
};
</script>
Replaying a live feed backwards through a timeline
A time scrubber lets a developer drag a control backwards through a stream of updates and watch the grid, and any charts wired to the same rows, reconstruct the state the data held at that earlier point. It suits any live source that needs an audit trail alongside a current value: a trading blotter where a trader wants to see the book five minutes ago, or an incident dashboard where an engineer steps back through the minutes before an alert fired. Lattice Grid controls this through grid.timeline, which records each row change as it arrives and lets the caller move a playhead across that recorded sequence rather than re-querying a backend for a snapshot. Because the grid is a JavaScript data grid built on incremental row operations, replaying backwards is handled the same way live updates are handled forwards: each step reapplies or reverses a single row-level change against the existing DOM nodes, so scrubbing through thousands of recorded ticks does not force a full re-render at each frame. This keeps the drag responsive under a pointermove handler rather than settling only once released. The recorded window is bounded by how far back the caller chooses to retain history, so a dashboard can keep the last hour scrubbable without holding a full session’s worth of ticks in memory.
How do I let users scrub back through a live-updating grid?
Enable grid.timeline on the grid configuration. Lattice Grid then records each incoming row change and exposes a playhead you can bind to a slider or drag handler, moving the grid and any linked charts to the state they held at that point in the recorded sequence, without re-fetching data from the source.