@toclocoinc/lattice-grid 1.63.3 → 1.64.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +2 -1
- package/docs/API.html +285 -24
- package/docs/CHART-CODES.md +24 -0
- package/docs/api-detail.html +80 -5
- package/lattice-grid.d.ts +93 -1
- package/lattice-grid.esm.min.js +154 -95
- package/lattice-grid.min.cjs +154 -95
- package/lattice-grid.min.js +154 -95
- package/modules/ai.d.ts +1 -1
- package/modules/ai.esm.min.js +3 -6
- package/modules/ai.min.cjs +3 -6
- package/modules/ai.min.js +3 -6
- package/modules/angular.d.ts +1 -1
- package/modules/angular.esm.min.js +1 -4
- package/modules/angular.min.cjs +1 -4
- package/modules/angular.min.js +1 -4
- package/modules/chart-alluvial.d.ts +1 -1
- package/modules/chart-alluvial.esm.min.js +1 -1
- package/modules/chart-alluvial.min.cjs +1 -1
- package/modules/chart-alluvial.min.js +1 -1
- package/modules/chart-arc.d.ts +1 -1
- package/modules/chart-arc.esm.min.js +1 -1
- package/modules/chart-arc.min.cjs +1 -1
- package/modules/chart-arc.min.js +1 -1
- package/modules/chart-bubblemap.d.ts +1 -1
- package/modules/chart-bubblemap.esm.min.js +1 -1
- package/modules/chart-bubblemap.min.cjs +1 -1
- package/modules/chart-bubblemap.min.js +1 -1
- package/modules/chart-bump.d.ts +1 -1
- package/modules/chart-bump.esm.min.js +1 -1
- package/modules/chart-bump.min.cjs +1 -1
- package/modules/chart-bump.min.js +1 -1
- package/modules/chart-calendar.d.ts +1 -1
- package/modules/chart-calendar.esm.min.js +1 -1
- package/modules/chart-calendar.min.cjs +1 -1
- package/modules/chart-calendar.min.js +1 -1
- package/modules/chart-decomposition.d.ts +1 -1
- package/modules/chart-decomposition.esm.min.js +1 -1
- package/modules/chart-decomposition.min.cjs +1 -1
- package/modules/chart-decomposition.min.js +1 -1
- package/modules/chart-diverging.d.ts +1 -1
- package/modules/chart-diverging.esm.min.js +1 -1
- package/modules/chart-diverging.min.cjs +1 -1
- package/modules/chart-diverging.min.js +1 -1
- package/modules/chart-dumbbell.d.ts +1 -1
- package/modules/chart-dumbbell.esm.min.js +1 -1
- package/modules/chart-dumbbell.min.cjs +1 -1
- package/modules/chart-dumbbell.min.js +1 -1
- package/modules/chart-fan.d.ts +1 -1
- package/modules/chart-fan.esm.min.js +1 -1
- package/modules/chart-fan.min.cjs +1 -1
- package/modules/chart-fan.min.js +1 -1
- package/modules/chart-hexbin.d.ts +1 -1
- package/modules/chart-hexbin.esm.min.js +1 -1
- package/modules/chart-hexbin.min.cjs +1 -1
- package/modules/chart-hexbin.min.js +1 -1
- package/modules/chart-hexmap.d.ts +1 -1
- package/modules/chart-hexmap.esm.min.js +1 -1
- package/modules/chart-hexmap.min.cjs +1 -1
- package/modules/chart-hexmap.min.js +1 -1
- package/modules/chart-icicle.d.ts +1 -1
- package/modules/chart-icicle.esm.min.js +1 -1
- package/modules/chart-icicle.min.cjs +1 -1
- package/modules/chart-icicle.min.js +1 -1
- package/modules/chart-markermap.d.ts +29 -0
- package/modules/chart-markermap.esm.min.js +313 -0
- package/modules/chart-markermap.min.cjs +317 -0
- package/modules/chart-markermap.min.js +317 -0
- package/modules/chart-parallel.d.ts +1 -1
- package/modules/chart-parallel.esm.min.js +1 -1
- package/modules/chart-parallel.min.cjs +1 -1
- package/modules/chart-parallel.min.js +1 -1
- package/modules/chart-ridgeline.d.ts +1 -1
- package/modules/chart-ridgeline.esm.min.js +1 -1
- package/modules/chart-ridgeline.min.cjs +1 -1
- package/modules/chart-ridgeline.min.js +1 -1
- package/modules/chart-roc.d.ts +1 -1
- package/modules/chart-roc.esm.min.js +1 -1
- package/modules/chart-roc.min.cjs +1 -1
- package/modules/chart-roc.min.js +1 -1
- package/modules/chart-slope.d.ts +1 -1
- package/modules/chart-slope.esm.min.js +1 -1
- package/modules/chart-slope.min.cjs +1 -1
- package/modules/chart-slope.min.js +1 -1
- package/modules/chart-splom.d.ts +1 -1
- package/modules/chart-splom.esm.min.js +1 -1
- package/modules/chart-splom.min.cjs +1 -1
- package/modules/chart-splom.min.js +1 -1
- package/modules/chart-waffle.d.ts +1 -1
- package/modules/chart-waffle.esm.min.js +1 -1
- package/modules/chart-waffle.min.cjs +1 -1
- package/modules/chart-waffle.min.js +1 -1
- package/modules/charts.d.ts +1 -1
- package/modules/charts.esm.min.js +395 -33
- package/modules/charts.min.cjs +395 -33
- package/modules/charts.min.js +395 -33
- package/modules/data-router.d.ts +1 -1
- package/modules/data-router.esm.min.js +3 -6
- package/modules/data-router.min.cjs +3 -6
- package/modules/data-router.min.js +3 -6
- package/modules/devtools.d.ts +1 -1
- package/modules/devtools.esm.min.js +1 -4
- package/modules/devtools.min.cjs +1 -4
- package/modules/devtools.min.js +1 -4
- package/modules/dhtmlx-compat.d.ts +1 -1
- package/modules/dhtmlx-compat.esm.min.js +3 -6
- package/modules/dhtmlx-compat.min.cjs +3 -6
- package/modules/dhtmlx-compat.min.js +3 -6
- package/modules/gantt.d.ts +1 -1
- package/modules/gantt.esm.min.js +3 -6
- package/modules/gantt.min.cjs +3 -6
- package/modules/gantt.min.js +3 -6
- package/modules/geo-europe-nuts.d.ts +1 -1
- package/modules/geo-europe-nuts.esm.min.js +1 -1
- package/modules/geo-uk.d.ts +1 -1
- package/modules/geo-uk.esm.min.js +1 -1
- package/modules/geo-us-states.d.ts +1 -1
- package/modules/geo-us-states.esm.min.js +1 -1
- package/modules/geo-world-110m.d.ts +1 -1
- package/modules/geo-world-110m.esm.min.js +1 -1
- package/modules/geo-world-50m.d.ts +1 -1
- package/modules/geo-world-50m.esm.min.js +1 -1
- package/modules/htmx.d.ts +1 -1
- package/modules/htmx.esm.min.js +154 -95
- package/modules/htmx.min.cjs +154 -95
- package/modules/htmx.min.js +154 -95
- package/modules/kanban.d.ts +1 -1
- package/modules/kanban.esm.min.js +3 -6
- package/modules/kanban.min.cjs +3 -6
- package/modules/kanban.min.js +3 -6
- package/modules/kpi.d.ts +56 -3
- package/modules/kpi.esm.min.js +202 -10
- package/modules/kpi.min.cjs +202 -10
- package/modules/kpi.min.js +202 -10
- package/modules/layout.d.ts +1 -1
- package/modules/layout.esm.min.js +3 -6
- package/modules/layout.min.cjs +3 -6
- package/modules/layout.min.js +3 -6
- package/modules/mock-socket.d.ts +1 -1
- package/modules/mock-socket.esm.min.js +1 -4
- package/modules/mock-socket.min.cjs +1 -4
- package/modules/mock-socket.min.js +1 -4
- package/modules/react.d.ts +1 -1
- package/modules/react.esm.min.js +3 -6
- package/modules/react.min.cjs +3 -6
- package/modules/react.min.js +3 -6
- package/modules/svelte.d.ts +1 -1
- package/modules/svelte.esm.min.js +1 -4
- package/modules/svelte.min.cjs +1 -4
- package/modules/svelte.min.js +1 -4
- package/modules/tabs.d.ts +1 -1
- package/modules/tabs.esm.min.js +3 -6
- package/modules/tabs.min.cjs +3 -6
- package/modules/tabs.min.js +3 -6
- package/modules/vue.d.ts +1 -1
- package/modules/vue.esm.min.js +1 -4
- package/modules/vue.min.cjs +1 -4
- package/modules/vue.min.js +1 -4
- package/modules/webcomponent.d.ts +1 -1
- package/modules/webcomponent.esm.min.js +154 -95
- package/modules/webcomponent.min.cjs +154 -95
- package/modules/webcomponent.min.js +154 -95
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
dependencies, no build step required. Optional adapters for React, Vue, Svelte
|
|
5
5
|
and Web Components ship alongside it.
|
|
6
6
|
|
|
7
|
-
Version 1.
|
|
7
|
+
Version 1.64.0 · [latticegrid.dev](https://www.latticegrid.dev) · TOCLOCO Inc
|
|
8
8
|
|
|
9
9
|
---
|
|
10
10
|
|
|
@@ -410,6 +410,7 @@ the UMD build), and registers the `type` shown.
|
|
|
410
410
|
| `chart-hexbin` → `hexbin` | Hexbin / 2D-density plot. |
|
|
411
411
|
| `chart-hexmap` → `hexmap` | Hexbin map. |
|
|
412
412
|
| `chart-icicle` → `icicle` | Icicle chart. |
|
|
413
|
+
| `chart-markermap` → `markermap` | Marker map: a labelled figure per location, filled from the value column's conditional-formatting rules. |
|
|
413
414
|
| `chart-parallel` → `parallel` | Parallel coordinates. |
|
|
414
415
|
| `chart-ridgeline` → `ridgeline` | Ridgeline (joy) plot. |
|
|
415
416
|
| `chart-roc` → `roc` | ROC / PR / calibration curves. |
|
package/docs/API.html
CHANGED
|
@@ -360,7 +360,7 @@
|
|
|
360
360
|
<div class="shell">
|
|
361
361
|
<aside class="rail">
|
|
362
362
|
<p class="rail__brand">Lattice Grid</p>
|
|
363
|
-
<p class="rail__sub">API reference · v1.
|
|
363
|
+
<p class="rail__sub">API reference · v1.64.0</p>
|
|
364
364
|
<nav>
|
|
365
365
|
<div class="rail__group">
|
|
366
366
|
<span class="rail__label">Start</span>
|
|
@@ -444,7 +444,7 @@
|
|
|
444
444
|
</header>
|
|
445
445
|
|
|
446
446
|
<p class="chips">
|
|
447
|
-
<span class="chip">Version 1.
|
|
447
|
+
<span class="chip">Version 1.64.0</span>
|
|
448
448
|
<span class="chip">Zero dependencies</span>
|
|
449
449
|
<span class="chip"><a href="api-detail.html">Developer guide →</a></span>
|
|
450
450
|
</p>
|
|
@@ -1393,7 +1393,7 @@ grid.destroy();
|
|
|
1393
1393
|
<table>
|
|
1394
1394
|
<thead><tr><th>Member</th><th>Returns</th><th>Description</th></tr></thead>
|
|
1395
1395
|
<tbody>
|
|
1396
|
-
<tr><td class="sig">getVersion()</td><td class="type">string</td><td class="desc">The version this grid came from, e.g. <code>'1.
|
|
1396
|
+
<tr><td class="sig">getVersion()</td><td class="type">string</td><td class="desc">The version this grid came from, e.g. <code>'1.64.0'</code>. Also on the module as <code>getVersion()</code>, for when you have no grid to hand.</td></tr>
|
|
1397
1397
|
<tr><td class="sig">get(key)</td><td class="type">unknown</td><td class="desc">Read any configuration key.</td></tr>
|
|
1398
1398
|
<tr><td class="sig">set(key, value)</td><td class="type">void</td><td class="desc">Write one key. Every key is live; nothing needs a rebuild.</td></tr>
|
|
1399
1399
|
<tr><td class="sig">setAll(values)</td><td class="type">void</td><td class="desc">Write several in one pass. Emits one <code>config:changed</code> for the batch, not one per key.</td></tr>
|
|
@@ -5164,7 +5164,7 @@ const chart = createChart({
|
|
|
5164
5164
|
<tr><td class="sig">Part to whole</td><td><code>pie</code>, <code>donut</code>, <code>sunburst</code>, <code>treemap</code></td><td><code>x</code>, <code>y</code>; or the grid's grouping, see below</td></tr>
|
|
5165
5165
|
<tr><td class="sig">Specialist</td><td><code>radar</code>, <code>gauge</code>, <code>funnel</code>, <code>candlestick</code></td><td>varies; candlestick takes four <code>measures</code> in open, high, low, close order</td></tr>
|
|
5166
5166
|
<tr><td class="sig">Geographic</td><td><code>geomap</code></td><td><code>x</code> as an ISO code, <code>y</code> as the value</td></tr>
|
|
5167
|
-
<tr><td class="sig">Flow</td><td><code>sankey</code>, <code>chord</code>, <code>network</code></td><td><code>source</code>, <code>target</code>, <code>y</code></td></tr>
|
|
5167
|
+
<tr><td class="sig">Flow</td><td><code>sankey</code>, <code>chord</code>, <code>network</code></td><td><code>source</code>, <code>target</code>, <code>y</code>; a <code>network</code> also takes <code>nodes</code> — see <a href="#network-map">Network diagrams</a></td></tr>
|
|
5168
5168
|
<tr><td class="sig">Over time</td><td><code>stream</code>, <code>marimekko</code>, <code>violin</code>, <code>gantt</code></td><td>varies; gantt takes <code>label</code>, <code>start</code>, <code>end</code></td></tr>
|
|
5169
5169
|
</tbody>
|
|
5170
5170
|
</table>
|
|
@@ -5211,6 +5211,9 @@ const chart = createChart({
|
|
|
5211
5211
|
<tr><td class="name">code / codeProperty</td><td class="type">string</td><td class="desc">A geomap's ISO code column, and the property carrying the code in your <code>shapes</code>.</td></tr>
|
|
5212
5212
|
<tr><td class="name">columns / method / values</td><td class="type">string[] / string / boolean</td><td class="desc">Correlogram: which columns to correlate, by <code>pearson</code>, <code>spearman</code> or <code>kendall</code>, and whether to print the coefficients in the cells.</td></tr>
|
|
5213
5213
|
<tr><td class="name">iterations</td><td class="type">number</td><td class="desc">Network layouts: how many relaxation passes to run.</td></tr>
|
|
5214
|
+
<tr><td class="name">nodes</td><td class="type">ChartNode[]</td><td class="desc">A <code>network</code>'s nodes, named by you rather than inferred from the rows: <code>{ id, label, icon, x, y }</code>. <code>id</code> matches a value in the <code>source</code> or <code>target</code> column; <code>icon</code> is any name in the grid's icon registry; <code>x</code>/<code>y</code> are fractions of the plot (0 to 1) and <strong>pin</strong> the node there, out of the force simulation. A node listed here that appears in no row is still drawn. See <a href="#network-map">Network diagrams</a>.</td></tr>
|
|
5215
|
+
<tr><td class="name">icon</td><td class="type">string</td><td class="desc">The default glyph for a <code>network</code> node that names none of its own. Unset, an undeclared node is a plain disc.</td></tr>
|
|
5216
|
+
<tr><td class="name">linkWidth</td><td class="type">number</td><td class="desc">Fix a <code>network</code> link's stroke width in pixels. Unset, width follows the link's value as a share of the heaviest link.</td></tr>
|
|
5214
5217
|
<tr><td class="name">spec / baseline / rules / confidence</td><td class="type">object / number / string / number</td><td class="desc">Control and capability charts: a tolerance overriding the column's own <code>spec</code>, how many leading readings fix the control limits, which rule set judges the violations (<code>westernElectric</code> or <code>nelson</code>), and the level for the capability interval.</td></tr>
|
|
5215
5218
|
</tbody>
|
|
5216
5219
|
</table>
|
|
@@ -5486,6 +5489,97 @@ createChart({ grid, container: '#us', type: 'geomap', code: 'state', y: 'sales',
|
|
|
5486
5489
|
|
|
5487
5490
|
<div class="note"><p>The module imports nothing from the grid: <code>createChart</code> is handed a grid rather than importing one. That is what keeps the charts bundle to the drawing, and it is why the grid must be created first, and why a chart cannot outlive it.</p></div>
|
|
5488
5491
|
|
|
5492
|
+
<h3 id="network-map">Network diagrams — icon nodes, links coloured by their value</h3>
|
|
5493
|
+
<p>A <code>network</code> draws the grid's rows as a graph: <code>source</code> and <code>target</code> name the two endpoint columns and <code>y</code> carries the value on the link between them. Three things make it a picture of <em>your</em> network rather than a generic hairball, and each is a fact only you have.</p>
|
|
5494
|
+
<p><strong>Nodes you name.</strong> <code>nodes: [{ id, label, icon, x, y }]</code> gives a node a glyph from the grid's own icon registry — a built-in name, or one you registered — and a label drawn beneath it. A node that appears in the rows but not in <code>nodes</code> takes the chart's <code>icon</code> default (a plain disc when there is none) and its own id as its label. A node listed in <code>nodes</code> that appears in <em>no</em> row is still drawn: a device with no links is a fact worth seeing. The glyphs are SVG paths from the same registry the cells paint from, so they are sharp at any chart size and take the chart's theme colours.</p>
|
|
5495
|
+
<p><strong>Positions you choose.</strong> <code>x</code> and <code>y</code> are fractions of the plot, measured from its top-left. A node giving <em>both</em> is <strong>pinned</strong> there and takes no part in the force simulation; everything else is laid out around it by the same deterministic relaxation as before, so “core on top, regions below” needs no hand-placed SVG. Half a position is not a position: a node with only <code>x</code> is laid out. A fraction outside 0 to 1 clamps to the edge of the plot rather than drawing where nobody can see it. Pinning one node never reshuffles the others — the layout's seeding draws for every node, pinned or not, precisely so that it cannot.</p>
|
|
5496
|
+
<p><strong>Colours from the rules you already wrote.</strong> Each link's stroke comes from the value column's own conditional-formatting rules, through <code>grid.formatting.styleFor(col, value)</code> — the colour order is <code>background</code>, then <code>backgroundColor</code>, then <code>color</code>; a gradient (a data bar, an icon set) is not a colour and is not read. A link no rule matches keeps the chart's default link colour. <strong>There is no chart-level threshold option, deliberately</strong>: a second place to say “red above 80” is a second place for the chart and the cell to disagree. The legend lists the rules that actually fired, with their own labels and their own swatches; a rule that matched nothing is not advertised. Change a rule and the links recolour on the next frame without the layout re-running, so nothing moves.</p>
|
|
5497
|
+
<p><strong>Links are undirected, and parallel links stay parallel.</strong> There are no arrowheads, and <code>A,B</code> is the same pair as <code>B,A</code> — a cable has two ends and no direction. <strong>Several rows between the same pair are drawn as several lines</strong>, side by side, offset perpendicular to the pair by 4 px and symmetric about it, in row order, each with its own value and its own colour. They are <em>not</em> summed: three circuits between two sites are three readings, and one line carrying 120% would be a number nothing measured. The tooltip on any one line names both endpoints and that line's own value, and the pointer picks out the line you are actually over rather than the pair. Width follows the value unless <code>linkWidth</code> fixes it.</p>
|
|
5498
|
+
<p><strong>Linked like every other chart.</strong> The graph is drawn from the grid's filtered rows and follows filter and sort. With <code>selection: true</code>, clicking a link selects its row and clicking a node selects every row it is an end of; the grid's selection then lights those links and nodes and dims the rest. A <code>click</code> handler still fires first and can <code>preventDefault()</code>.</p>
|
|
5499
|
+
|
|
5500
|
+
<h4 id="network-map-example">The picture, executed</h4>
|
|
5501
|
+
<p class="section-note">Two core routers pinned across the top, three regional routers pinned below, two circuits between every pair, and one rule set on the <code>load</code> column doing all the colouring. Thirteen rows, thirteen lines, three colours, five glyphs, and a legend that names the three rules.</p>
|
|
5502
|
+
<pre data-run="js" data-expect="13 links, #107c41/#a4262c/#f0b400 | pinned true | gap 4 | 5 icons | Healthy,Busy,Saturated" data-covers="export:createChart export:createGrid config:formatting config:selection"><code>const { createTestDom } = await import('../packages/dom/src/renderer/testdom.js');
|
|
5503
|
+
const { createGrid } = await import('../packages/dom/src/index.js');
|
|
5504
|
+
const { createChart } = await import('../packages/modules/charts/index.js');
|
|
5505
|
+
|
|
5506
|
+
const { document, root } = createTestDom({ width: 640, height: 420 });
|
|
5507
|
+
|
|
5508
|
+
// Two circuits between every core and every region: six pairs, twelve rows.
|
|
5509
|
+
const rows = [];
|
|
5510
|
+
for (const core of ['core-1', 'core-2']) {
|
|
5511
|
+
for (const site of ['emea', 'amer', 'apac']) {
|
|
5512
|
+
for (const [n, load] of [['a', 18], ['b', 92]]) {
|
|
5513
|
+
rows.push({ id: `${core}/${site}/${n}`, from: core, to: site, load });
|
|
5514
|
+
}
|
|
5515
|
+
}
|
|
5516
|
+
}
|
|
5517
|
+
rows.push({ id: 'core-1/core-2/x', from: 'core-1', to: 'core-2', load: 61 });
|
|
5518
|
+
|
|
5519
|
+
const grid = createGrid(root, {
|
|
5520
|
+
rowKey: 'id',
|
|
5521
|
+
selection: 'multiple',
|
|
5522
|
+
columns: [{ field: 'from' }, { field: 'to' }, { field: 'load', type: 'number' }],
|
|
5523
|
+
rows,
|
|
5524
|
+
// One rule set on the column. The cells and the links read it together.
|
|
5525
|
+
formatting: {
|
|
5526
|
+
load: [
|
|
5527
|
+
{ id: 'ok', label: 'Healthy', when: { op: 'lt', value: 40 }, style: { background: '#107c41' } },
|
|
5528
|
+
{ id: 'busy', label: 'Busy', when: { op: 'lt', value: 80 }, style: { background: '#f0b400' } },
|
|
5529
|
+
{ id: 'hot', label: 'Saturated', when: { op: 'gte', value: 80 }, style: { background: '#a4262c' } },
|
|
5530
|
+
],
|
|
5531
|
+
},
|
|
5532
|
+
});
|
|
5533
|
+
|
|
5534
|
+
const container = document.createElement('div');
|
|
5535
|
+
container.rect = { width: 600, height: 400, top: 0, left: 0 };
|
|
5536
|
+
root.appendChild(container);
|
|
5537
|
+
|
|
5538
|
+
const chart = createChart({
|
|
5539
|
+
grid, container, type: 'network', source: 'from', target: 'to',
|
|
5540
|
+
y: { col: 'load', fn: 'sum' }, selection: true,
|
|
5541
|
+
nodes: [
|
|
5542
|
+
{ id: 'core-1', label: 'Core', icon: 'square', x: 0.3, y: 0.15 },
|
|
5543
|
+
{ id: 'core-2', label: 'Core', icon: 'square', x: 0.7, y: 0.15 },
|
|
5544
|
+
{ id: 'emea', label: 'EMEA', icon: 'circleFilled', x: 0.2, y: 0.8 },
|
|
5545
|
+
{ id: 'amer', label: 'AMER', icon: 'circleFilled', x: 0.5, y: 0.8 },
|
|
5546
|
+
{ id: 'apac', label: 'APAC', icon: 'circleFilled', x: 0.8, y: 0.8 },
|
|
5547
|
+
],
|
|
5548
|
+
});
|
|
5549
|
+
|
|
5550
|
+
const find = (tag, cls) => [...container.querySelectorAll(tag)]
|
|
5551
|
+
.filter((n) => (n.getAttribute('class') || '').includes(cls));
|
|
5552
|
+
const at = (key) => find('circle', '__node').find((c) => c.getAttribute('data-node') === key);
|
|
5553
|
+
const num = (el, name) => Number(el.getAttribute(name));
|
|
5554
|
+
|
|
5555
|
+
// The picture: two cores on one row, three regions on another below them.
|
|
5556
|
+
const top = [at('core-1'), at('core-2')].map((c) => num(c, 'cy'));
|
|
5557
|
+
const bottom = ['emea', 'amer', 'apac'].map((k) => num(at(k), 'cy'));
|
|
5558
|
+
const rowsPinned = top[0] === top[1] && bottom.every((y) => y === bottom[0]) && bottom[0] > top[0]
|
|
5559
|
+
&& num(at('core-1'), 'cx') < num(at('core-2'), 'cx');
|
|
5560
|
+
|
|
5561
|
+
// Every link its own line, coloured by the rule its value matched.
|
|
5562
|
+
const edges = find('path', '__edge');
|
|
5563
|
+
const stroke = (e) => ((e.getAttribute('style') || '').match(/stroke:\s*([^;]+)/) || [])[1];
|
|
5564
|
+
const colours = [...new Set(edges.map(stroke))].sort();
|
|
5565
|
+
|
|
5566
|
+
// Two circuits between core-1 and emea, drawn side by side 4px apart.
|
|
5567
|
+
const ends = (d) => d.match(/-?\d+(?:\.\d+)?/g).map(Number);
|
|
5568
|
+
const pair = edges.slice(0, 2).map((e) => ends(e.getAttribute('d')));
|
|
5569
|
+
const gap = Math.round(Math.hypot(pair[0][0] - pair[1][0], pair[0][1] - pair[1][1]));
|
|
5570
|
+
|
|
5571
|
+
// Five glyphs, drawn from the registry the host extended.
|
|
5572
|
+
const glyphs = find('path', '__node-icon').length;
|
|
5573
|
+
|
|
5574
|
+
// The legend names the rules that fired, not a palette.
|
|
5575
|
+
const legend = [...container.querySelectorAll('button')]
|
|
5576
|
+
.filter((b) => (b.getAttribute('class') || '').includes('__legend-item'))
|
|
5577
|
+
.map((b) => b.textContent).join(',');
|
|
5578
|
+
|
|
5579
|
+
chart.destroy();
|
|
5580
|
+
grid.destroy();
|
|
5581
|
+
return `${edges.length} links, ${colours.join('/')} | pinned ${rowsPinned} | gap ${gap} | ${glyphs} icons | ${legend}`;</code></pre>
|
|
5582
|
+
|
|
5489
5583
|
<h3 id="chart-extension-types">Extension chart types — pay only for what you draw</h3>
|
|
5490
5584
|
<p>The base charts bundle draws the built-in <code>TYPES</code> and nothing else. A new chart type is a <strong>separate, opt-in module</strong> a caller imports only if they use it (BACKLOG-0000886, on the slim-core seam BACKLOG-0000884). Importing it self-registers the type with the base module through <code>registerChartType</code>; the base <code>Chart</code> consults that registry for any type it does not draw natively. Because the base never imports the extension, the base bundle <strong>does not grow</strong> for a type a caller never uses.</p>
|
|
5491
5585
|
<pre><code><span class="kw">import</span> '@toclocoinc/lattice-grid/modules/charts'; <span class="cmt">// the base</span>
|
|
@@ -5601,6 +5695,62 @@ grid.destroy();
|
|
|
5601
5695
|
typeof hexmap.drawHexMap, typeof hexmap.bindHexMap,
|
|
5602
5696
|
].join(' | ');</code></pre>
|
|
5603
5697
|
|
|
5698
|
+
<h3 id="chart-markermap">Map markers — a figure per location, coloured by its own rule</h3>
|
|
5699
|
+
<p><code>modules/chart-markermap</code> registers <code>markermap</code>: one marker per row, placed by <code>lon</code>/<code>lat</code> over a geometry pack's outlines, showing the row's <code>label</code> and its <code>value</code> beside the dot. Two things come from the grid rather than from the chart, and that is the whole point of the type. The <strong>number</strong> is the value column's own formatted cell text, so a percentage, a currency or a unit reads on the map exactly as it reads in the table. The <strong>colour</strong> is whatever <code>grid.formatting.styleFor(valueColumn, value)</code> returns for that row — the rule's <code>background</code>, or its <code>color</code> where it sets no background — so a red / amber / green availability wall is one rule set on one column plus one chart configuration. There is deliberately no chart-level thresholds option and no colour column: the rules are the one source, and a legend lists the rules that actually fired, with each rule's own swatch.</p>
|
|
5700
|
+
<p>With <code>shapes</code> it draws the pack's regions underneath, through the pack's own <code>projection</code>, and pans and zooms exactly as a <a href="#geometry-packs">geomap</a> of that pack does; without <code>shapes</code> the markers fall back to the projection alone. A row whose coordinates are absent, non-numeric or outside ±180 / ±90 draws no marker and is counted in <code>chart.data().unplaced</code>, which the map also writes under itself. Labels are deconflicted by trying four positions in a fixed order — right of the dot, then left, then above, then below — and a label with nowhere to go is dropped rather than overprinted; <code>labels: false</code> turns them all off on a dense map and leaves the tooltip, which carries the name, the value, the coordinates and the status. With <code>selection: true</code> a click on a marker selects that row in the grid, and the grid's selection emphasises the marker.</p>
|
|
5701
|
+
<pre data-run="js" data-expect="#1b7f3b #1b7f3b #c8a415 #c0392b | London 99.95% / Sydney 99.91% / Frankfurt 99.62% / New York 98.05% | unplaced 0" data-covers="export:drawMarkerMap export:bindMarkerMap"><code><span class="kw">const</span> { createTestDom } = <span class="kw">await</span> import('../packages/dom/src/renderer/testdom.js');
|
|
5702
|
+
<span class="kw">const</span> { document, root } = createTestDom({ width: 700, height: 460 });
|
|
5703
|
+
<span class="kw">const</span> panel = document.createElement('div');
|
|
5704
|
+
panel.rect = { width: 700, height: 460, top: 0, left: 0 };
|
|
5705
|
+
root.appendChild(panel);
|
|
5706
|
+
|
|
5707
|
+
<span class="kw">const</span> { createHeadlessGrid } = <span class="kw">await</span> import('../packages/core/src/index.js');
|
|
5708
|
+
<span class="kw">const</span> { createChart } = <span class="kw">await</span> import('../packages/modules/charts/index.js');
|
|
5709
|
+
<span class="cmt">// Importing the module registers `markermap`; its drawer is drawMarkerMap.</span>
|
|
5710
|
+
<span class="kw">const</span> markermap = <span class="kw">await</span> import('../packages/modules/chart-markermap/index.js');
|
|
5711
|
+
<span class="kw">const</span> { pack } = <span class="kw">await</span> import('../packages/modules/geo-world-110m/index.js');
|
|
5712
|
+
|
|
5713
|
+
<span class="kw">const</span> grid = createHeadlessGrid({
|
|
5714
|
+
rowKey: 'id',
|
|
5715
|
+
columns: [
|
|
5716
|
+
{ field: 'site', type: 'text' }, { field: 'lng', type: 'number' },
|
|
5717
|
+
{ field: 'lat', type: 'number' }, { field: 'avail', type: 'number', format: '0.00%' },
|
|
5718
|
+
],
|
|
5719
|
+
rows: [
|
|
5720
|
+
{ id: 'ldn', site: 'London', lng: -0.13, lat: 51.5, avail: 0.9995 },
|
|
5721
|
+
{ id: 'syd', site: 'Sydney', lng: 151.2, lat: -33.87, avail: 0.9991 },
|
|
5722
|
+
{ id: 'fra', site: 'Frankfurt', lng: 8.68, lat: 50.11, avail: 0.9962 },
|
|
5723
|
+
{ id: 'nyc', site: 'New York', lng: -74.0, lat: 40.71, avail: 0.9805 },
|
|
5724
|
+
],
|
|
5725
|
+
});
|
|
5726
|
+
<span class="cmt">// Three rules on the column. Nothing below repeats a threshold or a colour.</span>
|
|
5727
|
+
grid.formatting.add('avail', { when: { op: 'gte', value: 0.999 }, style: { background: '#1b7f3b' }, label: 'Healthy' });
|
|
5728
|
+
grid.formatting.add('avail', { when: { op: 'gte', value: 0.99 }, style: { background: '#c8a415' }, label: 'Watch' });
|
|
5729
|
+
grid.formatting.add('avail', { when: { op: 'lt', value: 0.99 }, style: { background: '#c0392b' }, label: 'Breached' });
|
|
5730
|
+
|
|
5731
|
+
<span class="kw">const</span> chart = createChart({
|
|
5732
|
+
grid, container: panel, type: 'markermap',
|
|
5733
|
+
lon: 'lng', lat: 'lat', label: 'site', value: 'avail', shapes: pack,
|
|
5734
|
+
});
|
|
5735
|
+
|
|
5736
|
+
<span class="cmt">// What was painted: a fill per marker, and the text beside each dot.</span>
|
|
5737
|
+
<span class="kw">const</span> fills = [];
|
|
5738
|
+
<span class="kw">const</span> labels = [];
|
|
5739
|
+
<span class="kw">const</span> walk = (node) => {
|
|
5740
|
+
<span class="kw">for</span> (<span class="kw">const</span> child <span class="kw">of</span> node.children || []) {
|
|
5741
|
+
<span class="kw">const</span> cls = String(child.getAttribute('class') || '');
|
|
5742
|
+
<span class="kw">if</span> (cls.includes('markermap-dot')) fills.push(child.getAttribute('fill'));
|
|
5743
|
+
<span class="kw">if</span> (cls.includes('data-label')) labels.push(child.textContent);
|
|
5744
|
+
walk(child);
|
|
5745
|
+
}
|
|
5746
|
+
};
|
|
5747
|
+
walk(chart.element);
|
|
5748
|
+
|
|
5749
|
+
<span class="cmt">// The binder is public too, for a host that wants the placed rows itself.</span>
|
|
5750
|
+
<span class="kw">const</span> bound = markermap.bindMarkerMap(grid, { lon: 'lng', lat: 'lat', label: 'site', value: 'avail' });
|
|
5751
|
+
chart.destroy();
|
|
5752
|
+
<span class="kw">return</span> `${fills.join(' ')} | ${labels.join(' / ')} | unplaced ${bound.unplaced}`;</code></pre>
|
|
5753
|
+
|
|
5604
5754
|
<h2 id="datarouter">The data router</h2>
|
|
5605
5755
|
<p><code>modules/data-router</code> is a host-layer demultiplexer: it takes <strong>one</strong> arriving stream or dataset, splits it by what each record <em>is</em>, and routes each partition to its own grid — or to a headless grid driving a chart. One round-trip, or one live feed, hydrates a whole screen of grids that each see only their slice. It is optional, imports nothing from the grid, and adds no core hook: every grid is driven through the <strong>public</strong> incremental path, <code>grid.rows.apply({ add, update, remove })</code>. <strong>The router never opens a connection itself — the host owns the connection (a <code>WebSocket</code>, SSE, CDC, a message bus, a plain fetch), and the router owns everything once a message has arrived</strong>; see <a href="#datarouter-websocket-example">a live WebSocket feed</a> for the worked, runnable integration.</p>
|
|
5606
5756
|
<pre><code>import { createDataRouter } from '@toclocoinc/lattice-grid/modules/data-router';
|
|
@@ -5869,7 +6019,7 @@ socketA.close();
|
|
|
5869
6019
|
<span class="kw">const</span> droppedBefore = router.dropped;
|
|
5870
6020
|
<span class="kw">const</span> socketB = <span class="kw">new</span> MockWebSocket({ feed: feedB(), rate: 5, jitter: 0, snapshotDelay: 5 });
|
|
5871
6021
|
wireRouter(router, socketB);
|
|
5872
|
-
<span class="kw">await</span> wait(
|
|
6022
|
+
<span class="kw">await</span> wait(80);
|
|
5873
6023
|
<span class="kw">const</span> afterReconnect = g.rows.value('a', 'label'); <span class="cmt">// A@4 — only the new delta advanced it</span>
|
|
5874
6024
|
<span class="kw">const</span> replaysDropped = router.dropped - droppedBefore; <span class="cmt">// 2 — both replays dropped</span>
|
|
5875
6025
|
socketB.close();
|
|
@@ -6681,6 +6831,52 @@ const kpi = createKPI(document.querySelector('#kpis'), {
|
|
|
6681
6831
|
</table>
|
|
6682
6832
|
</div>
|
|
6683
6833
|
<p><strong>Interaction is light and host-driven.</strong> A tile emits <code>tile:click</code> (also from the keyboard) carrying the tile model, so a host can drill down or, in a demo, filter a routed grid — the wiring lives in the host, not the module. This is deliberately not a dashboard layout engine (that is the parked dashboard generator) and charting beyond a minimal sparkline belongs to the charts module.</p>
|
|
6834
|
+
<h3 id="kpi-clock">The clock tile: the device clock, not an aggregate</h3>
|
|
6835
|
+
<p><code>{ kind: 'clock', label, timeZone?, locale?, seconds?, date? }</code> in <code>tiles</code> renders a tile that shows the current date and time instead of a figure — the date on one line, the time on the next, e.g. <code>Mon, 21 Apr 2025</code> over <code>14:32:18</code> — styled like any other tile, so a panel of zone clocks beside a panel of figures reads as one visual system with no host CSS.</p>
|
|
6836
|
+
<pre><code>const kpi = createKPI(document.querySelector('#kpis'), {
|
|
6837
|
+
tiles: [
|
|
6838
|
+
{ kind: 'clock', label: 'London', timeZone: 'Europe/London' },
|
|
6839
|
+
{ kind: 'clock', label: 'New York', timeZone: 'America/New_York', locale: 'en-US' },
|
|
6840
|
+
{ id: 'open', label: 'Open deals', aggregation: 'count', filter: (r) => r.stage !== 'won' },
|
|
6841
|
+
],
|
|
6842
|
+
});</code></pre>
|
|
6843
|
+
<p><strong>Where the time comes from.</strong> The device clock, read every second in exact alignment with the second boundary — not a fixed <code>setInterval(1000)</code>, which drifts — so every clock tile on a panel ticks in the same repaint. One timer serves the <em>whole panel</em>, not one per tile: a panel of three zone clocks runs one shared interval, not three. The timer stops when the panel is <code>destroy()</code>ed or the document goes into the background (<code>document.hidden</code>) and resumes correctly — catching up immediately, then re-aligning — when the tab returns. A panel with no clock tile starts no timer at all.</p>
|
|
6844
|
+
<p><strong>Zone and format.</strong> <code>timeZone</code> is any IANA zone name; omitted, the tile shows the viewer's local time. A name <code>Intl.DateTimeFormat</code> does not recognise is reported through the usual <code>[lattice]</code> diagnostics warning, by name, and the tile falls back to local time rather than rendering nothing. The date and time are formatted for <code>locale</code> — the tile's own, else the panel's <code>locale</code>, else the browser's default — entirely through <code>Intl.DateTimeFormat</code>: a 24-hour clock where the locale uses one, 12-hour with an AM/PM marker where it does not, because that is the locale's own convention rather than a second option to set. <code>seconds: false</code> drops the seconds from the time line; <code>date: false</code> drops the date line entirely. Every formatter is built once, at tile resolution, and reused on every tick.</p>
|
|
6845
|
+
<p><strong>Inert everywhere a stat tile measures.</strong> A clock tile takes none of a stat tile's measurement options — <code>aggregation</code>, <code>field</code>, <code>format</code>, <code>thresholds</code>, <code>bands</code>, <code>target</code>, <code>baseline</code>, <code>sparkline</code> — because a tile that measures nothing has nothing for them to apply to. Supplying any of them is reported as a configuration warning by name and ignored: the tile still renders the clock, nothing else. It contributes nothing to the panel's totals, to a parent's rolled-up status in a <a href="#kpi-tree">tree</a> (its own <code>status</code> is always <code>null</code>, never <code>unknown</code> — a clock tile is never “not measured”, it always has a reading) and nothing to what a host reads out of <code>onChange</code> beyond its own text. It otherwise behaves exactly like any other tile: it sits in <code>tiles()</code>/<code>tile(id)</code> and a <a href="#kpi-tree">tree</a> alongside stat tiles, responds to <code>columns</code>, fires <code>tile:click</code>/<code>tile:dblclick</code>/<code>tile:contextmenu</code>, and its accessible name (<code>aria-label</code>) carries the same date and time text the two visible lines show.</p>
|
|
6846
|
+
<h3 id="kpi-clock-example">A clock tile, two zones, executed</h3>
|
|
6847
|
+
<p class="section-note">Structural assertions only — the clock reads the real device clock, so a doc example run on every build cannot pin a literal
|
|
6848
|
+
time without freezing it. The <a href="#kpi-clock">formatting itself</a> is pinned for two locales and two zones with a fixed clock
|
|
6849
|
+
in the test suite. Run headless on every build.</p>
|
|
6850
|
+
<pre data-run="js" data-expect="clock clock null null null | true true | clock" data-covers="export:createKPI"><code><span class="kw">const</span> { createKPI } = <span class="kw">await</span> import('../packages/modules/kpi/index.js');
|
|
6851
|
+
|
|
6852
|
+
<span class="kw">const</span> kpi = createKPI(null, {
|
|
6853
|
+
tiles: [
|
|
6854
|
+
{ kind: 'clock', id: 'london', label: 'London', timeZone: 'Europe/London', locale: 'en-GB' },
|
|
6855
|
+
<span class="cmt">// aggregation/thresholds are measurement options a clock tile refuses (warned, ignored):</span>
|
|
6856
|
+
{ kind: 'clock', id: 'ny', label: 'New York', timeZone: 'America/New_York', locale: 'en-US',
|
|
6857
|
+
aggregation: 'sum', thresholds: { warn: 1, critical: 2 } },
|
|
6858
|
+
],
|
|
6859
|
+
});
|
|
6860
|
+
|
|
6861
|
+
<span class="kw">const</span> london = kpi.tile('london');
|
|
6862
|
+
<span class="kw">const</span> ny = kpi.tile('ny');
|
|
6863
|
+
|
|
6864
|
+
<span class="cmt">// kind, status and bar are the same three neutral values on every clock tile,</span>
|
|
6865
|
+
<span class="cmt">// whatever was supplied for the ignored options above (String(), because</span>
|
|
6866
|
+
<span class="cmt">// Array#join renders null as '' rather than 'null'):</span>
|
|
6867
|
+
<span class="kw">const</span> shapes = [london.kind, ny.kind, String(london.status), String(ny.status), String(ny.bar)].join(' '); <span class="cmt">// clock clock null null null</span>
|
|
6868
|
+
|
|
6869
|
+
<span class="cmt">// The date and time lines hold the shape the formatting spec promises:</span>
|
|
6870
|
+
<span class="kw">const</span> shaped = [
|
|
6871
|
+
/^[A-Za-z]{3}, \d{2} [A-Za-z]{3,4} \d{4}$/.test(london.clock.date),
|
|
6872
|
+
/^\d{2}:\d{2}:\d{2}(\s?[AP]M)?$/.test(ny.clock.time),
|
|
6873
|
+
].join(' '); <span class="cmt">// true true</span>
|
|
6874
|
+
|
|
6875
|
+
<span class="cmt">// The ignored `aggregation: 'sum'` never took effect:</span>
|
|
6876
|
+
<span class="kw">const</span> ignoredAgg = ny.aggregation; <span class="cmt">// clock</span>
|
|
6877
|
+
|
|
6878
|
+
kpi.destroy();
|
|
6879
|
+
<span class="kw">return</span> [shapes, shaped, ignoredAgg].join(' | ');</code></pre>
|
|
6684
6880
|
<h3 id="kpi-tree">The KPI tree: top-level items that expand to the indicators beneath them</h3>
|
|
6685
6881
|
<p>Set <code>tree</code> and the panel becomes a <strong>rail</strong> instead of a grid of tiles: a small number of top-level items, each expanding to the indicators underneath it, with the parent telling you at a glance whether anything below needs attention. <code>Compute</code> expands to <code>psi</code> and <code>cpu</code>; collapsed, it still shows you that one of them is in breach.</p>
|
|
6686
6882
|
<pre><code>const kpi = createKPI(document.querySelector('#rail'), {
|
|
@@ -6877,7 +7073,7 @@ grid.destroy();
|
|
|
6877
7073
|
<span class="kw">return</span> `${by['risk.atRisk'].display} at risk | SPI ${by['risk.spi'].display} | ${by['risk.sla.breaches'].display} breaches`;</code></pre>
|
|
6878
7074
|
|
|
6879
7075
|
<h2 id="tabs">The tabbed grid</h2>
|
|
6880
|
-
<p><code>modules/tabs</code> is an opt-in top-of-grid tab strip where <strong>each tab is its own full, independently-configured grid instance</strong> — "configure each tab as per a normal grid" rather than one grid whose state is swapped. That is a deliberate rejection of the cheaper alternative: <code>grid.state.get()</code>/<code>.apply()</code> only repositions, hides, resizes and sorts <strong>existing</strong> columns by id (no field, type, editor or row data), so a state-swap only works when every tab shares one column schema and one source — strictly less than the ask. A tab may instead declare <code>from: '<tabId>'</code> plus a narrowing (<code>where</code>, <code>group</code>, <code>join</code>, …), and the module wires a <code>source: { mode: 'derived', from: <the parent tab's live grid>, … }</code> for it — the shipped derived-source mechanism, not a new config-inheritance one. <code>createGrid</code> is <strong>injected</strong> (the same pattern the React/Vue/Svelte adapters use), so the module imports no engine code regardless of how it is loaded — its own minified ESM build (<code>tabs.esm.min.js</code>) is
|
|
7076
|
+
<p><code>modules/tabs</code> is an opt-in top-of-grid tab strip where <strong>each tab is its own full, independently-configured grid instance</strong> — "configure each tab as per a normal grid" rather than one grid whose state is swapped. That is a deliberate rejection of the cheaper alternative: <code>grid.state.get()</code>/<code>.apply()</code> only repositions, hides, resizes and sorts <strong>existing</strong> columns by id (no field, type, editor or row data), so a state-swap only works when every tab shares one column schema and one source — strictly less than the ask. A tab may instead declare <code>from: '<tabId>'</code> plus a narrowing (<code>where</code>, <code>group</code>, <code>join</code>, …), and the module wires a <code>source: { mode: 'derived', from: <the parent tab's live grid>, … }</code> for it — the shipped derived-source mechanism, not a new config-inheritance one. <code>createGrid</code> is <strong>injected</strong> (the same pattern the React/Vue/Svelte adapters use), so the module imports no engine code regardless of how it is loaded — its own minified ESM build (<code>tabs.esm.min.js</code>) is about 12KB gzipped — the module and nothing else. A module bundle carries its own code plus a small shared runtime, not the engine: the framework adapters are 3–11KB, the KPI module about 50KB, while a module that inlines the whole engine (the web component, htmx) ships at roughly 760KB.</p>
|
|
6881
7077
|
<pre><code>import { createGrid } from '@toclocoinc/lattice-grid';
|
|
6882
7078
|
import { createTabs } from '@toclocoinc/lattice-grid/modules/tabs';
|
|
6883
7079
|
|
|
@@ -7019,7 +7215,7 @@ tabs.destroy();
|
|
|
7019
7215
|
|
|
7020
7216
|
<h2 id="layout">The dashboard layout</h2>
|
|
7021
7217
|
<p><code>modules/layout</code> is an opt-in <strong>reconfigurable dashboard surface</strong>: a cell grid inside an element, and a set of windows placed on it that a user can move, resize and close — by drag <em>or</em> by keyboard. It is the thing a customer would otherwise reach for GridStack or react-grid-layout to get, which means a second dependency, a second sizing model, and a seam where the viewers in it do not resize properly.</p>
|
|
7022
|
-
<p><strong>It is payload-agnostic, and that is the whole design.</strong> A window body is a <code>div</code> with an <code>id</code>. The module creates it, sizes it, and never reads or writes its contents — it does not import <code>createGrid</code>, does not know what a payload is, and never calls into one. What it does instead is emit <code>window:resized</code> with the measured content box, which is the contract. That rule is what keeps
|
|
7218
|
+
<p><strong>It is payload-agnostic, and that is the whole design.</strong> A window body is a <code>div</code> with an <code>id</code>. The module creates it, sizes it, and never reads or writes its contents — it does not import <code>createGrid</code>, does not know what a payload is, and never calls into one. What it does instead is emit <code>window:resized</code> with the measured content box, which is the contract. That rule is what keeps the whole module to <strong>about 17,500 bytes gzipped</strong> (measured on the built bundle: its own code over a shared module runtime of roughly 2KB) and what makes it usable for a payload we have not written yet.</p>
|
|
7023
7219
|
<pre><code>import { createLayout } from '@toclocoinc/lattice-grid/modules/layout';
|
|
7024
7220
|
|
|
7025
7221
|
const layout = createLayout(document.querySelector('#dash'), {
|
|
@@ -8685,6 +8881,7 @@ return `next ${p.mean.toFixed(1)}; r2 ${f.r2.toFixed(1)}; band ${p.upper - p.low
|
|
|
8685
8881
|
<tr><td class="name">text</td><td class="type">string</td><td class="desc">The label of a `text` mark. Required for `text`, ignored for other types. <small>(optional)</small></td></tr>
|
|
8686
8882
|
<tr><td class="name">fontSize</td><td class="type">number</td><td class="desc">A `text` mark's font size in content pixels (before presentation scale). Defaults to 14. <small>(optional)</small></td></tr>
|
|
8687
8883
|
<tr><td class="name">background</td><td class="type">string</td><td class="desc">An optional backing colour drawn behind a `text` mark's label. <small>(optional)</small></td></tr>
|
|
8884
|
+
<tr><td class="name">region</td><td class="type">'start' | 'centre' | 'end'</td><td class="desc">Which columns the mark belongs to: a pinned region holds still while the grid scrolls sideways, the centre moves with it. Set from where a stroke began; omitted (the centre) for every mark that is not over a pinned column, so a mark saved before this existed reads unchanged. <small>(optional)</small></td></tr>
|
|
8688
8885
|
</tbody>
|
|
8689
8886
|
</table>
|
|
8690
8887
|
</div>
|
|
@@ -8959,6 +9156,20 @@ return `next ${p.mean.toFixed(1)}; r2 ${f.r2.toFixed(1)}; band ${p.upper - p.low
|
|
|
8959
9156
|
</tbody>
|
|
8960
9157
|
</table>
|
|
8961
9158
|
</div>
|
|
9159
|
+
<h3 id="type-ChartNode">ChartNode</h3>
|
|
9160
|
+
<p class="section-note">One node of a `network` chart, as the host declares it. `x` and `y` are fractions of the plot, 0 to 1, measured from its top-left. A node giving both is **pinned** there and takes no part in the force simulation; the rest are laid out around it, deterministically. Giving only one of the two is not a position and the node is laid out.</p>
|
|
9161
|
+
<div class="table-wrap">
|
|
9162
|
+
<table>
|
|
9163
|
+
<thead><tr><th>Member</th><th>Type</th><th>Description</th></tr></thead>
|
|
9164
|
+
<tbody>
|
|
9165
|
+
<tr><td class="name">id</td><td class="type">string</td><td class="desc">Matches a value in the `source` or `target` column.</td></tr>
|
|
9166
|
+
<tr><td class="name">label</td><td class="type">string</td><td class="desc">Drawn beneath the node. The id is used when this is absent. <small>(optional)</small></td></tr>
|
|
9167
|
+
<tr><td class="name">icon</td><td class="type">string</td><td class="desc">A name in the grid's icon registry, drawn inside the node's disc. <small>(optional)</small></td></tr>
|
|
9168
|
+
<tr><td class="name">x</td><td class="type">number</td><td class="desc">Where to pin it, as a fraction of the plot's width. <small>(optional)</small></td></tr>
|
|
9169
|
+
<tr><td class="name">y</td><td class="type">number</td><td class="desc">Where to pin it, as a fraction of the plot's height. <small>(optional)</small></td></tr>
|
|
9170
|
+
</tbody>
|
|
9171
|
+
</table>
|
|
9172
|
+
</div>
|
|
8962
9173
|
<h3 id="type-ChartSpec">ChartSpec</h3>
|
|
8963
9174
|
<p class="section-note">What a chart draws and how. `grid` and `container` are required; everything else describes the chart. A chart reads the grid's *filtered* rows, so it follows the grid without being told to.</p>
|
|
8964
9175
|
<div class="table-wrap">
|
|
@@ -8997,6 +9208,9 @@ return `next ${p.mean.toFixed(1)}; r2 ${f.r2.toFixed(1)}; band ${p.upper - p.low
|
|
|
8997
9208
|
<tr><td class="name">diverging</td><td class="type">boolean</td><td class="desc">A diverging colour ramp, for heatmap and geomap. <small>(optional)</small></td></tr>
|
|
8998
9209
|
<tr><td class="name">shapes</td><td class="type">unknown</td><td class="desc">Country outlines, for a geomap drawing countries rather than continents. Either GeoJSON, an object of code to SVG path data, or a geometry {@link GeoPack} imported from an optional `modules/geo-*` package (BACKLOG-0001321) — as the pack itself, or as `{ pack: id }` once its module has been imported and registered. <small>(optional)</small></td></tr>
|
|
8999
9210
|
<tr><td class="name">codeProperty</td><td class="type">string</td><td class="desc"><small>(optional)</small></td></tr>
|
|
9211
|
+
<tr><td class="name">lon</td><td class="type">string</td><td class="desc">The longitude column, for the types that place a row by where it is rather than by a code: `markermap`, `bubblemap` and `hexmap`. Degrees east, -180 to 180; a row outside that, or with no reading, is left off the map and counted. <small>(optional)</small></td></tr>
|
|
9212
|
+
<tr><td class="name">lat</td><td class="type">string</td><td class="desc">The latitude column, beside {@link ChartSpec.lon}. Degrees north, -90 to 90, on the same terms. <small>(optional)</small></td></tr>
|
|
9213
|
+
<tr><td class="name">value</td><td class="type">string</td><td class="desc">The measure a `markermap` writes beside each dot and colours it by. Its text is the column's own formatted cell text and its colour is whatever the column's conditional-formatting rules give that value, so a map and the table beside it say the same thing about the same number. <small>(optional)</small></td></tr>
|
|
9000
9214
|
<tr><td class="name">layer</td><td class="type">string</td><td class="desc">Which layer of a multi-layer geometry pack to draw — the UK pack, for instance, ships `regions`, `local-authorities` and `constituencies` together (BACKLOG-0001321). Ignored for a single-layer pack. <small>(optional)</small></td></tr>
|
|
9001
9215
|
<tr><td class="name">projection</td><td class="type">'equalEarth' | 'robinson' | 'mercator' | 'equirectangular' | 'albers'</td><td class="desc">The map projection a geomap draws through (BACKLOG-0001321): `'equalEarth'` (the default for a world), `'robinson'`, `'mercator'`, `'equirectangular'`, `'albers'`, `'transverseMercator'`, or a projection function of the caller's own `(lon: number, lat: number) => [number, number]`. Left unset, a geometry pack draws through the projection it declares. <small>(optional)</small></td></tr>
|
|
9002
9216
|
<tr><td class="name">projectionOptions</td><td class="type">{ parallels?: [number, number]; centre?: [number, number] }</td><td class="desc">Parameters for the projections that take them: `parallels` and `centre` for `albers`, `centre` for `transverseMercator` (BACKLOG-0001321). <small>(optional)</small></td></tr>
|
|
@@ -9023,6 +9237,9 @@ return `next ${p.mean.toFixed(1)}; r2 ${f.r2.toFixed(1)}; band ${p.upper - p.low
|
|
|
9023
9237
|
<tr><td class="name">method</td><td class="type">'pearson' | 'spearman' | 'kendall'</td><td class="desc"><small>(optional)</small></td></tr>
|
|
9024
9238
|
<tr><td class="name">values</td><td class="type">boolean</td><td class="desc"><small>(optional)</small></td></tr>
|
|
9025
9239
|
<tr><td class="name">iterations</td><td class="type">number</td><td class="desc">Network layouts: how many relaxation passes to run. <small>(optional)</small></td></tr>
|
|
9240
|
+
<tr><td class="name">nodes</td><td class="type">ChartNode[]</td><td class="desc">The nodes of a `network`, named by the host rather than inferred from the rows: an icon per device, a label, and a position the layout must honour. A node listed here that appears in no row is still drawn. A node in the rows that is not listed here takes the chart's `icon` default and its own id as its label. <small>(optional)</small></td></tr>
|
|
9241
|
+
<tr><td class="name">icon</td><td class="type">string</td><td class="desc">The default glyph for a `network` node that names none of its own: any name in the grid's icon registry (see {@link Grid.icons}). Unset, a node with no icon is a plain disc. <small>(optional)</small></td></tr>
|
|
9242
|
+
<tr><td class="name">linkWidth</td><td class="type">number</td><td class="desc">A `network` link's stroke width in pixels, fixed. Unset, width follows the link's value as a share of the heaviest link, as it always has. <small>(optional)</small></td></tr>
|
|
9026
9243
|
<tr><td class="name">spec</td><td class="type">{ lower?: number; upper?: number; target?: number }</td><td class="desc">Control and capability charts: a tolerance overriding the column's own `spec`, how many leading readings fix the control limits, which rule set the violations are judged against, and the level for the capability interval. <small>(optional)</small></td></tr>
|
|
9027
9244
|
<tr><td class="name">baseline</td><td class="type">number</td><td class="desc"><small>(optional)</small></td></tr>
|
|
9028
9245
|
<tr><td class="name">rules</td><td class="type">'westernElectric' | 'nelson'</td><td class="desc"><small>(optional)</small></td></tr>
|
|
@@ -10840,6 +11057,7 @@ return `next ${p.mean.toFixed(1)}; r2 ${f.r2.toFixed(1)}; band ${p.upper - p.low
|
|
|
10840
11057
|
<tr><td class="name">setPinnedRows</td><td class="type">(rows: unknown[], opts?: { edge?: 'top' | 'bottom' }): void</td><td class="desc">Pin rows above or below the scrolling body. The rows render through the ordinary column pipeline but are not part of the data: not counted, sorted, filtered, grouped, selectable or exported. Pass a new array rather than mutating the one you passed before: array identity is how the grid knows the pinned rows have changed.</td></tr>
|
|
10841
11058
|
<tr><td class="name">getPinnedRows</td><td class="type">(opts?: { edge?: 'top' | 'bottom' }): unknown[]</td><td class="desc">The objects currently pinned at one edge, as a copy.</td></tr>
|
|
10842
11059
|
<tr><td class="name">form</td><td class="type">RowFormApi</td><td class="desc">The row form. Declines when `rowForm` is not configured. <small>(read-only)</small></td></tr>
|
|
11060
|
+
<tr><td class="name">icons</td><td class="type">IconRegistryApi</td><td class="desc">The grid's icon registry, read-only. The same sprite set `registerIcon` writes to and every cell paints from, reachable from the grid instance so that code outside the grid bundle — an optional module drawing its own glyph, a network chart putting a `router` on a node — draws from the one registry rather than a second, empty copy of it. Register with `registerIcon` or `config.icons`, as before. <small>(read-only)</small></td></tr>
|
|
10843
11061
|
<tr><td class="name">getVersion</td><td class="type">(): string</td><td class="desc">The library version.</td></tr>
|
|
10844
11062
|
<tr><td class="name">destroy</td><td class="type">(): void</td><td class="desc">Release everything: listeners, timers, workers and the DOM the grid made.</td></tr>
|
|
10845
11063
|
</tbody>
|
|
@@ -11167,6 +11385,29 @@ return `next ${p.mean.toFixed(1)}; r2 ${f.r2.toFixed(1)}; band ${p.upper - p.low
|
|
|
11167
11385
|
</tbody>
|
|
11168
11386
|
</table>
|
|
11169
11387
|
</div>
|
|
11388
|
+
<h3 id="type-IconGlyph">IconGlyph</h3>
|
|
11389
|
+
<p class="section-note">One sprite: its view box, its path data, and how it is painted.</p>
|
|
11390
|
+
<div class="table-wrap">
|
|
11391
|
+
<table>
|
|
11392
|
+
<thead><tr><th>Member</th><th>Type</th><th>Description</th></tr></thead>
|
|
11393
|
+
<tbody>
|
|
11394
|
+
<tr><td class="name">viewBox</td><td class="type">string</td><td class="desc"></td></tr>
|
|
11395
|
+
<tr><td class="name">paths</td><td class="type">string[]</td><td class="desc"></td></tr>
|
|
11396
|
+
<tr><td class="name">paint</td><td class="type">'stroke' | 'fill'</td><td class="desc"></td></tr>
|
|
11397
|
+
</tbody>
|
|
11398
|
+
</table>
|
|
11399
|
+
</div>
|
|
11400
|
+
<h3 id="type-IconRegistryApi">IconRegistryApi</h3>
|
|
11401
|
+
<p class="section-note">Read access to the grid's icon sprite set (see {@link Grid.icons}).</p>
|
|
11402
|
+
<div class="table-wrap">
|
|
11403
|
+
<table>
|
|
11404
|
+
<thead><tr><th>Member</th><th>Type</th><th>Description</th></tr></thead>
|
|
11405
|
+
<tbody>
|
|
11406
|
+
<tr><td class="name">get</td><td class="type">(name: string): IconGlyph | null</td><td class="desc">One glyph, as a copy, or null when the name is not registered.</td></tr>
|
|
11407
|
+
<tr><td class="name">names</td><td class="type">(): string[]</td><td class="desc">Every registered name, in registration order.</td></tr>
|
|
11408
|
+
</tbody>
|
|
11409
|
+
</table>
|
|
11410
|
+
</div>
|
|
11170
11411
|
<h3 id="type-IconSetSpec">IconSetSpec</h3>
|
|
11171
11412
|
<p class="section-note">An icon set (BACKLOG-0000955): a glyph placed beside the value by the band it falls in. Drawn as a `background-image` with padding, so it too needs no extra element and stays a plain style value. `set` names a built-in — `'arrows'`, `'trafficLights'` or `'ratings'` (see {@link ICON_SETS}) — or supply your own ordered `icons` (SVG documents, data URIs or `url(...)` values). Bands are split at `thresholds` (ascending, one fewer than the icons); without them the column's distribution is cut into equal-count bands. `reverse` flips the order so a high value can read as red.</p>
|
|
11172
11413
|
<div class="table-wrap">
|
|
@@ -11633,6 +11874,22 @@ return `next ${p.mean.toFixed(1)}; r2 ${f.r2.toFixed(1)}; band ${p.upper - p.low
|
|
|
11633
11874
|
</tbody>
|
|
11634
11875
|
</table>
|
|
11635
11876
|
</div>
|
|
11877
|
+
<h3 id="type-KPIClockTile">KPIClockTile</h3>
|
|
11878
|
+
<p class="section-note">A clock tile: the device clock, not an aggregate (BACKLOG-0001640) — the date on one line and the time on the next, ticking once a second from one shared panel timer. It takes none of a stat tile's measurement options (`aggregation`, `field`, `format`, `thresholds`, `bands`, `target`, `baseline`, `sparkline`): supplying any of them is reported as a configuration warning by name and ignored, because a tile that measures nothing has nothing for them to apply to.</p>
|
|
11879
|
+
<div class="table-wrap">
|
|
11880
|
+
<table>
|
|
11881
|
+
<thead><tr><th>Member</th><th>Type</th><th>Description</th></tr></thead>
|
|
11882
|
+
<tbody>
|
|
11883
|
+
<tr><td class="name">kind</td><td class="type">'clock'</td><td class="desc">Discriminates a clock tile from an aggregate stat tile.</td></tr>
|
|
11884
|
+
<tr><td class="name">id</td><td class="type">string</td><td class="desc">A stable identity for the tile (defaults to the label, then the index). <small>(optional)</small></td></tr>
|
|
11885
|
+
<tr><td class="name">label</td><td class="type">string</td><td class="desc">The tile's accessible label (e.g. the city or zone it names). <small>(optional)</small></td></tr>
|
|
11886
|
+
<tr><td class="name">timeZone</td><td class="type">string</td><td class="desc">Any IANA zone name (`'Europe/London'`). Omitted, the tile shows the viewer's local time. A name `Intl.DateTimeFormat` does not recognise is reported through the usual diagnostics warning and the tile falls back to local time rather than rendering nothing. <small>(optional)</small></td></tr>
|
|
11887
|
+
<tr><td class="name">locale</td><td class="type">string</td><td class="desc">The locale the date and time are formatted in — the tile's own, else the panel's `KPIConfig.locale`, else the browser's default. A 24-hour clock or a 12-hour one with an AM/PM marker follows from the locale itself (`Intl.DateTimeFormat`'s own convention), never a separate option. <small>(optional)</small></td></tr>
|
|
11888
|
+
<tr><td class="name">seconds</td><td class="type">boolean</td><td class="desc">Show the seconds on the time line. Default `true`. <small>(optional)</small></td></tr>
|
|
11889
|
+
<tr><td class="name">date</td><td class="type">boolean</td><td class="desc">Show the date line at all. Default `true`. <small>(optional)</small></td></tr>
|
|
11890
|
+
</tbody>
|
|
11891
|
+
</table>
|
|
11892
|
+
</div>
|
|
11636
11893
|
<h3 id="type-KPIConfig">KPIConfig</h3>
|
|
11637
11894
|
<p class="section-note">KPI panel configuration.</p>
|
|
11638
11895
|
<div class="table-wrap">
|
|
@@ -11647,6 +11904,7 @@ return `next ${p.mean.toFixed(1)}; r2 ${f.r2.toFixed(1)}; band ${p.upper - p.low
|
|
|
11647
11904
|
<tr><td class="name">columns</td><td class="type">number</td><td class="desc"><small>(optional)</small></td></tr>
|
|
11648
11905
|
<tr><td class="name">ariaLabel</td><td class="type">string</td><td class="desc"><small>(optional)</small></td></tr>
|
|
11649
11906
|
<tr><td class="name">nullText</td><td class="type">string</td><td class="desc"><small>(optional)</small></td></tr>
|
|
11907
|
+
<tr><td class="name">locale</td><td class="type">string</td><td class="desc">The default locale a clock tile formats in when the tile itself declares none (BACKLOG-0001640); falls back to the browser's default. No effect on a stat tile, which takes its own `format.locale`. <small>(optional)</small></td></tr>
|
|
11650
11908
|
<tr><td class="name">tree</td><td class="type">KPITreeConfig | false</td><td class="desc">Arrange the tiles as a hierarchy; `false` keeps the panel flat. <small>(optional)</small></td></tr>
|
|
11651
11909
|
<tr><td class="name">messages</td><td class="type">{ t(key: string, params?: Record<string, unknown>): string }</td><td class="desc">The catalogue the panel's own text is read from. A panel routinely has no grid to borrow one off — two of its three input modes have none — so this is the first-class way to translate it. A grid's own `messages` satisfies the shape; a key it does not carry falls back to English. <small>(optional)</small></td></tr>
|
|
11652
11910
|
<tr><td class="name">onTileClick</td><td class="type">(event: KPIEvent) => void</td><td class="desc"><small>(optional)</small></td></tr>
|
|
@@ -11717,24 +11975,13 @@ return `next ${p.mean.toFixed(1)}; r2 ${f.r2.toFixed(1)}; band ${p.upper - p.low
|
|
|
11717
11975
|
</tbody>
|
|
11718
11976
|
</table>
|
|
11719
11977
|
</div>
|
|
11720
|
-
<h3 id="type-
|
|
11721
|
-
<p class="section-note">
|
|
11722
|
-
<div class="table-wrap">
|
|
11723
|
-
<table>
|
|
11724
|
-
<thead><tr><th>Member</th><th>Type</th><th>Description</th></tr></thead>
|
|
11725
|
-
<tbody>
|
|
11726
|
-
<tr><td class="name">warn</td><td class="type">number</td><td class="desc"></td></tr>
|
|
11727
|
-
<tr><td class="name">critical</td><td class="type">number</td><td class="desc"></td></tr>
|
|
11728
|
-
<tr><td class="name">direction</td><td class="type">'higherIsBetter' | 'lowerIsBetter'</td><td class="desc"><small>(optional)</small></td></tr>
|
|
11729
|
-
</tbody>
|
|
11730
|
-
</table>
|
|
11731
|
-
</div>
|
|
11732
|
-
<h3 id="type-KPITile">KPITile</h3>
|
|
11733
|
-
<p class="section-note">One tile: an aggregate over the routed rows, with optional filter, format, threshold and trend.</p>
|
|
11978
|
+
<h3 id="type-KPIStatTile">KPIStatTile</h3>
|
|
11979
|
+
<p class="section-note">An aggregate stat tile: the routed rows reduced to one number, with optional filter, format, threshold and trend.</p>
|
|
11734
11980
|
<div class="table-wrap">
|
|
11735
11981
|
<table>
|
|
11736
11982
|
<thead><tr><th>Member</th><th>Type</th><th>Description</th></tr></thead>
|
|
11737
11983
|
<tbody>
|
|
11984
|
+
<tr><td class="name">kind</td><td class="type">'stat'</td><td class="desc">Absent, or `'stat'`: the default tile kind. <small>(optional)</small></td></tr>
|
|
11738
11985
|
<tr><td class="name">id</td><td class="type">string</td><td class="desc">A stable identity for the tile (defaults to the label, then the index). <small>(optional)</small></td></tr>
|
|
11739
11986
|
<tr><td class="name">label</td><td class="type">string</td><td class="desc">The tile's accessible label. <small>(optional)</small></td></tr>
|
|
11740
11987
|
<tr><td class="name">aggregation</td><td class="type">KPIAggregation | ((rows: KPIRow[], tile: object) => unknown)</td><td class="desc">The aggregation kind, or a reducer `(rows, tile) => value` for a custom tile. <small>(optional)</small></td></tr>
|
|
@@ -11750,6 +11997,18 @@ return `next ${p.mean.toFixed(1)}; r2 ${f.r2.toFixed(1)}; band ${p.upper - p.low
|
|
|
11750
11997
|
</tbody>
|
|
11751
11998
|
</table>
|
|
11752
11999
|
</div>
|
|
12000
|
+
<h3 id="type-KPIThresholds">KPIThresholds</h3>
|
|
12001
|
+
<p class="section-note">A semantic threshold: two cut points and a direction. `higherIsBetter` (the default) makes a value at/above `warn` good, at/above `critical` a warning, below it critical; `lowerIsBetter` mirrors it. Colour is a host concern.</p>
|
|
12002
|
+
<div class="table-wrap">
|
|
12003
|
+
<table>
|
|
12004
|
+
<thead><tr><th>Member</th><th>Type</th><th>Description</th></tr></thead>
|
|
12005
|
+
<tbody>
|
|
12006
|
+
<tr><td class="name">warn</td><td class="type">number</td><td class="desc"></td></tr>
|
|
12007
|
+
<tr><td class="name">critical</td><td class="type">number</td><td class="desc"></td></tr>
|
|
12008
|
+
<tr><td class="name">direction</td><td class="type">'higherIsBetter' | 'lowerIsBetter'</td><td class="desc"><small>(optional)</small></td></tr>
|
|
12009
|
+
</tbody>
|
|
12010
|
+
</table>
|
|
12011
|
+
</div>
|
|
11753
12012
|
<h3 id="type-KPITileModel">KPITileModel</h3>
|
|
11754
12013
|
<p class="section-note">A computed tile, as it appears in the model.</p>
|
|
11755
12014
|
<div class="table-wrap">
|
|
@@ -11758,10 +12017,12 @@ return `next ${p.mean.toFixed(1)}; r2 ${f.r2.toFixed(1)}; band ${p.upper - p.low
|
|
|
11758
12017
|
<tbody>
|
|
11759
12018
|
<tr><td class="name">id</td><td class="type">string</td><td class="desc"></td></tr>
|
|
11760
12019
|
<tr><td class="name">label</td><td class="type">string</td><td class="desc"></td></tr>
|
|
12020
|
+
<tr><td class="name">kind</td><td class="type">'stat' | 'clock'</td><td class="desc">`'stat'` for an aggregate tile, `'clock'` for a clock tile (BACKLOG-0001640).</td></tr>
|
|
11761
12021
|
<tr><td class="name">aggregation</td><td class="type">string</td><td class="desc"></td></tr>
|
|
11762
12022
|
<tr><td class="name">field</td><td class="type">string</td><td class="desc"><small>(optional)</small></td></tr>
|
|
11763
|
-
<tr><td class="name">value</td><td class="type">unknown</td><td class="desc"
|
|
11764
|
-
<tr><td class="name">formatted</td><td class="type">string</td><td class="desc"
|
|
12023
|
+
<tr><td class="name">value</td><td class="type">unknown</td><td class="desc">For a clock tile, the read instant as epoch milliseconds.</td></tr>
|
|
12024
|
+
<tr><td class="name">formatted</td><td class="type">string</td><td class="desc">For a clock tile, the date and time text joined by a space (the same text `clock.date` and `clock.time` carry separately).</td></tr>
|
|
12025
|
+
<tr><td class="name">clock</td><td class="type">{ date: string | null; time: string }</td><td class="desc">Present only on a clock tile: the date and time lines rendered separately. `date` is `null` when the tile was given `date: false`. <small>(optional)</small></td></tr>
|
|
11765
12026
|
<tr><td class="name">status</td><td class="type">'good' | 'warn' | 'critical' | 'unknown' | null</td><td class="desc">The tile's semantic band, or `unknown` when the tile measured nothing. `unknown` is decided from data presence before any threshold is consulted: an aggregation over nothing returns the identity of its operation (`sum` and `count` return 0), and 0 is a number a threshold grades, so without it an empty panel would report as a healthy one. Two things make a tile `unknown`: the panel holds no rows at all, or the tile's `field` names no column on the bound grid, so it never read a cell to reduce over. A tile whose `filter` matches none of the rows the panel *does* hold is neither — it has measured a real zero and is banded normally. `null` means the tile has no thresholds or bands configured.</td></tr>
|
|
11766
12027
|
<tr><td class="name">target</td><td class="type">number</td><td class="desc"><small>(optional)</small></td></tr>
|
|
11767
12028
|
<tr><td class="name">baseline</td><td class="type">number</td><td class="desc"><small>(optional)</small></td></tr>
|
|
@@ -13804,7 +14065,7 @@ return `next ${p.mean.toFixed(1)}; r2 ${f.r2.toFixed(1)}; band ${p.upper - p.low
|
|
|
13804
14065
|
<!-- END GENERATED TYPE REFERENCE -->
|
|
13805
14066
|
|
|
13806
14067
|
<footer>
|
|
13807
|
-
Lattice Grid 1.
|
|
14068
|
+
Lattice Grid 1.64.0 · Copyright © 2026 TOCLOCO Inc. All rights reserved.
|
|
13808
14069
|
This document describes the behaviour of the shipped library. Where this guide and the code
|
|
13809
14070
|
disagree, the code wins: please <a href="https://www.latticegrid.dev">tell us</a>.
|
|
13810
14071
|
</footer>
|
package/docs/CHART-CODES.md
CHANGED
|
@@ -253,6 +253,30 @@ createChart({ grid, container: '#map', type: 'bubblemap', lon: 'lng', lat: 'lat'
|
|
|
253
253
|
size: 'sales', shapes: us });
|
|
254
254
|
```
|
|
255
255
|
|
|
256
|
+
### Against a marker map
|
|
257
|
+
|
|
258
|
+
`chart-markermap` places a labelled figure per location on the same packs, and
|
|
259
|
+
takes its colour from the **value column's own conditional-formatting rules**
|
|
260
|
+
rather than from anything on the chart. Set the rules once, on the column, and
|
|
261
|
+
the cell and the marker agree; the legend lists the rules that fired. The
|
|
262
|
+
coordinates are `lon`/`lat` in degrees, the name is `label`, and the number is
|
|
263
|
+
`value`, written in the column's own format.
|
|
264
|
+
|
|
265
|
+
```js
|
|
266
|
+
import '@toclocoinc/lattice-grid/modules/chart-markermap';
|
|
267
|
+
import { pack as world } from '@toclocoinc/lattice-grid/modules/geo-world-110m';
|
|
268
|
+
|
|
269
|
+
grid.formatting.add('availability', {
|
|
270
|
+
when: { op: 'gte', value: 0.999 }, style: { background: '#1b7f3b' }, label: 'Healthy',
|
|
271
|
+
});
|
|
272
|
+
createChart({ grid, container: '#map', type: 'markermap', lon: 'lng', lat: 'lat',
|
|
273
|
+
label: 'site', value: 'availability', shapes: world, selection: true });
|
|
274
|
+
```
|
|
275
|
+
|
|
276
|
+
A row whose `lon`/`lat` is absent, non-numeric or outside ±180 / ±90 draws no
|
|
277
|
+
marker; the count is on `chart.data().unplaced` and written under the map.
|
|
278
|
+
`labels: false` drops the text and keeps the tooltip.
|
|
279
|
+
|
|
256
280
|
---
|
|
257
281
|
|
|
258
282
|
## Country codes
|