Lattice Grid Buy a licence

developer guide

End-User Formatting Controls in a Data Grid

grid.formatting opens the same rule engine to the reader: they pick the column, the test and the look from a panel, see it applied at once, and keep it, because their rules travel in the saved view along with sort, filter and column order.

Developer guideColumns and cell rendering › End-User Formatting Controls in a Data Grid

Formatting an end user can change

compileRules() above compiles at configuration time into cell.style, which you write and a user cannot reach. grid.formatting holds the same rules as runtime state instead: the cell layer asks it on every paint, so a rule added while the grid is running takes effect on the next frame.

Rules as state

grid.formatting.add('margin', { when: { op: 'lt', value: 0 }, style: { background: '#fbeceb' } });
grid.formatting.add('*',      { when: { op: 'blank' },        style: { background: '#f1f3f5' } });

grid.formatting.list('margin');        // [{ id, when, style }, …] in evaluation order
grid.formatting.move('margin', id, 0); // order decides which rule wins
grid.formatting.update('margin', id, { enabled: false });
grid.formatting.clear('margin');

Two scopes, one ordered list. A rule sits on a column id or on '*' for every column. Evaluation joins them: grid-wide first, then the column's own, so a column rule can override a grid-wide one, and stopIfTrue means the same thing across the join as it does within either half.

Saved views and undo came free. The rules are a section of GridState, and both saved views and the undo timeline are built on that. Nothing in the formatting model knows either exists.

Rules must be JSON. style cannot be a function here, because the rules are serialised into views and undo slices. Config-time cell.style still takes one, which is the right home for a rule a user should not be able to change.

Both paths coexist. Where a column has a cell.style and a runtime rule matches, the two are merged and written once; the runtime rule wins for the properties it names and leaves the rest of your styling alone.

Group rows are not formatted. A group row summarises many values rather than being an instance of one, which is the same reason decoration is dropped for it.

The panel

createGrid(el, { toolPanel: { side: 'right', panels: ['columns', 'filters', 'formatting'] } });

The panel is a form over that array and nothing more: every control is one call into grid.formatting, which is what makes each gesture undoable without the panel knowing undo exists. It exposes ordering because ordering is meaning: dragging a rule up can change which of two colours a cell takes.

Icon sets and data bars are column decorations rather than cell styles, the bar and icon decorations already render them, and driving those from the panel needs a runtime column-decoration API that persists and undoes alongside the rules. Building it as a second bar implementation inside the rule engine was the alternative, and the wrong one.