Lattice Grid Buy a licence

developer guide

Time Scrubber: Replay a Data Grid Over Time

A live grid can be wound back: the scrubber replays the feed, the table shows what it held at that moment with the changes marked, and letting go returns it to now.

Developer guidePresenting and saved views › Time Scrubber: Replay a Data Grid Over Time

Time scrubber

Move the grid back through recent data changes: what did this look like a minute ago, before that number moved.

Scrubbing

grid.timeline.attach();      // start recording; the window fills from here

grid.timeline.seek(5);       // stand five changes back
grid.timeline.step(-1);      // one further back
grid.timeline.at();          // the moment being shown
grid.timeline.toLive();      // return, applying everything stepped over

grid.timeline.detach();

Attaching puts a control on the grid. A slider along the bottom with two readings beside it: how long ago, and the clock time. Relative answers the question actually being asked; absolute is what someone reads out to the person next to them. It moves the grid while the handle is dragged rather than on release, and it turns accent-coloured the moment you are off live, because a grid quietly showing stale data is the failure this control can cause. It removes itself on detach().

It reads the data, not your actions. Undo history records what the user did (sorts, filters, edits) which is rarely the question. This reads the change log: a bounded, timestamped, deliberately unmerged record of everything that arrived, so the intermediate states are all still there to move between.

Nothing is scrubbable before attach(). What a value used to be is not recoverable after the fact (no other part of the grid remembers it) so recording has to be switched on before there is a past to move through. It is off by default because reading a row per key on every change is real cost on a hot feed, and paying it for a scrubber nobody opened would be the wrong default.

Scrubbed back, the grid is not live. Changes keep being recorded but are not applied, because applying them would fight the position being held. Returning to the present applies everything missed.

Value changes reverse; row additions and removals do not. An add would need a removal and a remove would need re-insertion at its old position, and neither is recoverable from what the log holds. A window containing them scrubs over the value changes and leaves the row set alone: stated plainly because the alternative is a scrubber that silently half-works.

What moved is marked. Seeking compares each affected row before and after and marks the cells whose value changed, in --lattice-timeline-changed. Without it a scrub is nearly unreadable: the grid moves, and on a row twelve columns wide the one number you are hunting for goes past unseen.

The mark is held, not flashed. It stays until the next seek clears it. Every other transient signal in the grid fades on a timer, and this one deliberately does not, a scrub is someone hunting for what changed, and a highlight they can miss while reading the other end of the row helps nobody. timeline:seeking fires before any change is applied, so a five-step drag clears once and marks once rather than strobing per entry.

It compares column values, not raw fields. A computed column has no field of its own; diffing the source row would leave it silently unmarked while its number visibly moved. Reading through the column instead costs a little more and marks what the viewer can actually see change, which is the only definition of "changed" that matters here.

The window is bounded by rows, not only by changes. A cap on entries alone does not bound memory, because an entry is not a fixed size: one carrying a single changed cell and one carrying a fifty-thousand-row batch both count as one. So the log holds at most updates.logLimit changes (2000) and updates.logRows rows between them (100,000), dropping oldest-first on whichever it meets. It matters more here than it looks: the log is what keeps superseded row objects alive after the source has swapped in their replacements, so on a feed delivering five thousand rows a batch an entry-only cap retains twenty million of them. The one exception is a single change larger than the whole cap, which is kept: emptying the log would be worse than being briefly over, and it would drop the newest change rather than the oldest. Watch held against heldLimit in grid.updates.stats(); rows is a lifetime total and says nothing about memory.

Charts scrub like anything else. A sparkline column redraws to the series the row held at that point, and its cell is marked with the rest. The exception is the delta renderer: it samples on a wall-clock timer and compares against its own previous sample, so it reads a seek as a genuine movement and draws an arrow for it. Keep it off a grid you intend to scrub.