@toclocoinc/lattice-grid 1.63.3 → 1.65.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 +4 -2
- package/docs/API.html +408 -25
- package/docs/CHART-CODES.md +24 -0
- package/docs/api-detail.html +115 -5
- package/lattice-grid.d.ts +333 -205
- package/lattice-grid.esm.min.js +155 -96
- package/lattice-grid.min.cjs +155 -96
- package/lattice-grid.min.js +155 -96
- package/modules/ai.d.ts +10 -13
- 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 +2 -2
- package/modules/angular.esm.min.js +433 -10
- package/modules/angular.min.cjs +433 -10
- package/modules/angular.min.js +433 -10
- package/modules/chart-alluvial.d.ts +2 -2
- 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 +2 -2
- 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 +2 -2
- 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 +2 -2
- 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 +2 -2
- 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 +2 -2
- 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 +2 -2
- 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 +2 -2
- 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 +2 -2
- 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 +2 -2
- 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 +2 -2
- 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 +2 -2
- 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 +2 -2
- 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 +2 -2
- 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 +2 -2
- 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 +2 -2
- 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 +2 -2
- 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 +2 -2
- 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 +4 -5
- 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 +5 -6
- 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 +37 -43
- 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 +2 -2
- package/modules/geo-europe-nuts.esm.min.js +1 -1
- package/modules/geo-uk.d.ts +2 -2
- package/modules/geo-uk.esm.min.js +1 -1
- package/modules/geo-us-states.d.ts +2 -2
- package/modules/geo-us-states.esm.min.js +1 -1
- package/modules/geo-world-110m.d.ts +2 -2
- package/modules/geo-world-110m.esm.min.js +1 -1
- package/modules/geo-world-50m.d.ts +2 -2
- package/modules/geo-world-50m.esm.min.js +1 -1
- package/modules/htmx.d.ts +2 -2
- package/modules/htmx.esm.min.js +155 -96
- package/modules/htmx.min.cjs +155 -96
- package/modules/htmx.min.js +155 -96
- package/modules/kanban.d.ts +14 -14
- 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 +57 -4
- 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 +2 -2
- 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 +4 -5
- 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 +2 -3
- 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 +2 -3
- 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 +2 -2
- package/modules/webcomponent.esm.min.js +155 -96
- package/modules/webcomponent.min.cjs +155 -96
- package/modules/webcomponent.min.js +155 -96
- package/package.json +1 -1
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
|
package/docs/api-detail.html
CHANGED
|
@@ -437,7 +437,7 @@
|
|
|
437
437
|
<div class="shell">
|
|
438
438
|
<aside class="rail">
|
|
439
439
|
<p class="rail__brand">Lattice Grid</p>
|
|
440
|
-
<p class="rail__sub">Developer guide · v1.
|
|
440
|
+
<p class="rail__sub">Developer guide · v1.65.0</p>
|
|
441
441
|
<nav>
|
|
442
442
|
<div class="rail__group">
|
|
443
443
|
<span class="rail__label">Start here</span>
|
|
@@ -554,7 +554,7 @@
|
|
|
554
554
|
<a href="API.html">reference tables</a> are the shorter version for when you already know.
|
|
555
555
|
</p>
|
|
556
556
|
<p class="chips">
|
|
557
|
-
<span class="chip">Version 1.
|
|
557
|
+
<span class="chip">Version 1.65.0</span>
|
|
558
558
|
<span class="chip">Zero dependencies</span>
|
|
559
559
|
<span class="chip">No build step</span>
|
|
560
560
|
</p>
|
|
@@ -1369,7 +1369,7 @@ off(); <span class="cmt">// every subscrip
|
|
|
1369
1369
|
</p>
|
|
1370
1370
|
<div class="example">
|
|
1371
1371
|
<p class="example__label">Which version am I running?</p>
|
|
1372
|
-
<pre><code>grid.getVersion(); <span class="cmt">// '1.
|
|
1372
|
+
<pre><code>grid.getVersion(); <span class="cmt">// '1.65.0'</span>
|
|
1373
1373
|
LatticeGrid.getVersion(); <span class="cmt">// the same, when you have no grid to hand</span></code></pre>
|
|
1374
1374
|
</div>
|
|
1375
1375
|
<p class="lead-in">
|
|
@@ -7896,6 +7896,116 @@ grid.diff.before('CIR-100042', 'capacity');</code></pre>
|
|
|
7896
7896
|
highlight cannot serve both.
|
|
7897
7897
|
</p>
|
|
7898
7898
|
|
|
7899
|
+
<h3 id="network-map-guide">A network map: icon nodes, links coloured by their value</h3>
|
|
7900
|
+
<p class="lead-in">
|
|
7901
|
+
A <code>network</code> chart draws the rows as a graph — two columns name the endpoints
|
|
7902
|
+
and a third carries the value on the link. Until 1.64 every node was a plain circle, every
|
|
7903
|
+
edge the same grey, and the layout went wherever the simulation put it, so the picture a
|
|
7904
|
+
network team actually draws — core on top, regions below, an icon per device, a colour
|
|
7905
|
+
per circuit — could not be expressed. Three options change that, and every one of them
|
|
7906
|
+
carries a fact that only the host has.
|
|
7907
|
+
</p>
|
|
7908
|
+
<div class="example">
|
|
7909
|
+
<p class="example__label">One <code>nodes</code> list, one rule set, one chart config</p>
|
|
7910
|
+
<pre><code>createGrid(el, {
|
|
7911
|
+
columns: [{ field: 'from' }, { field: 'to' }, { field: 'load', type: 'number' }],
|
|
7912
|
+
rows, <span class="cmt">// one row per circuit</span>
|
|
7913
|
+
selection: 'multiple',
|
|
7914
|
+
<span class="cmt">// The rules live on the column. The cells and the links read the same ones.</span>
|
|
7915
|
+
formatting: {
|
|
7916
|
+
load: [
|
|
7917
|
+
{ label: 'Healthy', when: { op: 'lt', value: 40 }, style: { background: '#107c41' } },
|
|
7918
|
+
{ label: 'Busy', when: { op: 'lt', value: 80 }, style: { background: '#f0b400' } },
|
|
7919
|
+
{ label: 'Saturated', when: { op: 'gte', value: 80 }, style: { background: '#a4262c' } },
|
|
7920
|
+
],
|
|
7921
|
+
},
|
|
7922
|
+
});
|
|
7923
|
+
|
|
7924
|
+
createChart({
|
|
7925
|
+
grid, container: '#topology', type: 'network',
|
|
7926
|
+
source: 'from', target: 'to', y: { col: 'load', fn: 'sum' },
|
|
7927
|
+
selection: true,
|
|
7928
|
+
nodes: [
|
|
7929
|
+
<span class="cmt">// x/y are fractions of the plot: both given pins the node there.</span>
|
|
7930
|
+
{ id: 'core-1', label: 'Core', icon: 'router', x: 0.3, y: 0.15 },
|
|
7931
|
+
{ id: 'core-2', label: 'Core', icon: 'router', x: 0.7, y: 0.15 },
|
|
7932
|
+
{ id: 'emea', label: 'EMEA', icon: 'hub', x: 0.2, y: 0.8 },
|
|
7933
|
+
{ id: 'amer', label: 'AMER', icon: 'hub', x: 0.5, y: 0.8 },
|
|
7934
|
+
{ id: 'apac', label: 'APAC', icon: 'hub', x: 0.8, y: 0.8 },
|
|
7935
|
+
],
|
|
7936
|
+
});</code></pre>
|
|
7937
|
+
</div>
|
|
7938
|
+
<div class="why">
|
|
7939
|
+
<p><strong>The icons come from the grid, not from the chart.</strong> <code>icon</code> is a
|
|
7940
|
+
name in the grid’s own sprite registry — a built-in, or one you registered —
|
|
7941
|
+
and the chart reads it through <code>grid.icons</code>, the grid it is bound to. That is
|
|
7942
|
+
deliberate and it is the only route that works: the charts module ships as its own bundle, so
|
|
7943
|
+
an <code>import</code> of the registry there would hand the chart a <em>second</em>, empty
|
|
7944
|
+
copy, and a glyph you registered would be invisible to it. One registry, reached through the
|
|
7945
|
+
one object both sides already share. Unknown names warn once and draw a plain disc rather
|
|
7946
|
+
than nothing.</p>
|
|
7947
|
+
<p><strong>Pinning steers the layout without replacing it.</strong> A node with both
|
|
7948
|
+
<code>x</code> and <code>y</code> is a fixed body: it still pushes its neighbours apart and
|
|
7949
|
+
still pulls on its links, and the integration step skips it. Everything unpinned settles
|
|
7950
|
+
around it by the same deterministic relaxation as before — and pinning one node does
|
|
7951
|
+
not reshuffle the others, because the layout’s seeding draws for every node whether it
|
|
7952
|
+
is pinned or not, precisely so that it cannot. Half a position is not a position: a node with
|
|
7953
|
+
only <code>x</code> is laid out.</p>
|
|
7954
|
+
<p><strong>Parallel links are not summed.</strong> Three rows between the same pair are three
|
|
7955
|
+
lines, offset 4 px apart, symmetric about the pair’s own line, in row order. One
|
|
7956
|
+
line carrying their total would be a number nothing measured — three circuits at 40% do
|
|
7957
|
+
not make one at 120% — and the pointer picks out the line you are over rather than the
|
|
7958
|
+
pair, so each one’s own value is readable. Links are undirected: no arrowheads, and
|
|
7959
|
+
<code>A,B</code> is the same pair as <code>B,A</code>.</p>
|
|
7960
|
+
<p><strong>There is no chart-level threshold option, on purpose.</strong> The colour comes
|
|
7961
|
+
from <code>grid.formatting.styleFor(col, value)</code> — <code>background</code>, then
|
|
7962
|
+
<code>backgroundColor</code>, then <code>color</code>; a gradient is not a colour and is not
|
|
7963
|
+
read. A second place to say “red above 80” is a second place for the chart and the
|
|
7964
|
+
cell to disagree. The legend lists only the rules that fired, with their own labels and
|
|
7965
|
+
swatches, and changing a rule recolours the links on the next frame without the layout
|
|
7966
|
+
re-running, so nothing moves.</p>
|
|
7967
|
+
<p><strong>Clicking acts on rows, because a link is a row.</strong> With
|
|
7968
|
+
<code>selection: true</code>, clicking a link selects its row and clicking a node selects
|
|
7969
|
+
every row it is an end of; the grid’s selection then lights those marks and dims the
|
|
7970
|
+
rest. The types whose marks are <em>aggregates</em> are deliberately untouched: selecting the
|
|
7971
|
+
forty rows behind a bar is not what a click on a bar means.</p>
|
|
7972
|
+
</div>
|
|
7973
|
+
|
|
7974
|
+
<p class="lead-in">
|
|
7975
|
+
The glyphs those nodes draw are yours to supply. <code>icons</code> on the grid configuration
|
|
7976
|
+
registers SVG sprites by name, before the first paint, into the same registry the built-in
|
|
7977
|
+
chevrons and sort arrows live in — so a name you register is usable anywhere a glyph
|
|
7978
|
+
name is: a column’s <code>icon</code> decoration, a rail action’s
|
|
7979
|
+
<code>icon</code>, a network node’s <code>icon</code>. Registering a built-in name
|
|
7980
|
+
overrides it, which is how the expander chevron becomes your own mark. A sprite is a view box
|
|
7981
|
+
and its path data; one filled path on the house 16×16 box is the common case, so a bare
|
|
7982
|
+
path string is read as exactly that. <code>grid.icons</code> reads the registry back.
|
|
7983
|
+
</p>
|
|
7984
|
+
<div class="example">
|
|
7985
|
+
<p class="example__label">Registering your own glyphs, executed</p>
|
|
7986
|
+
<pre data-run="js" data-expect="router 2 stroke | hub 1 fill" data-covers="config:icons"><code><span class="kw">const</span> { createTestDom } = <span class="kw">await</span> import('../packages/dom/src/renderer/testdom.js');
|
|
7987
|
+
<span class="kw">const</span> { root } = createTestDom();
|
|
7988
|
+
<span class="kw">const</span> { createGrid } = <span class="kw">await</span> import('../packages/dom/src/index.js');
|
|
7989
|
+
|
|
7990
|
+
<span class="kw">const</span> grid = createGrid(root, {
|
|
7991
|
+
rowKey: 'id',
|
|
7992
|
+
rows: [{ id: 'core-1', load: 41 }],
|
|
7993
|
+
columns: [{ field: 'id' }, { field: 'load', type: 'number' }],
|
|
7994
|
+
<span class="cmt">// Your own sprites, by name, alongside the built-in set.</span>
|
|
7995
|
+
icons: {
|
|
7996
|
+
router: { viewBox: '0 0 16 16', paths: ['M2 6h12v6H2Z', 'M5 6V3h6v3'], paint: 'stroke' },
|
|
7997
|
+
hub: 'M8 2a6 6 0 1 0 0 12A6 6 0 0 0 8 2Z', <span class="cmt">// one filled path is enough</span>
|
|
7998
|
+
},
|
|
7999
|
+
});
|
|
8000
|
+
|
|
8001
|
+
<span class="cmt">// Read back what the registry holds: the normalised glyph, whichever form registered it.</span>
|
|
8002
|
+
<span class="kw">const</span> router = grid.icons.get('router');
|
|
8003
|
+
<span class="kw">const</span> hub = grid.icons.get('hub');
|
|
8004
|
+
grid.destroy();
|
|
8005
|
+
|
|
8006
|
+
<span class="kw">return</span> `router ${router.paths.length} ${router.paint} | hub ${hub.paths.length} ${hub.paint}`;</code></pre>
|
|
8007
|
+
</div>
|
|
8008
|
+
|
|
7899
8009
|
<h3 id="removed-rows">Showing what was deleted</h3>
|
|
7900
8010
|
<div class="example">
|
|
7901
8011
|
<p class="example__label">A deletion is a change too</p>
|
|
@@ -8078,7 +8188,7 @@ grid.import.apply(preview);</code></pre>
|
|
|
8078
8188
|
<tr><td class="name">createKPI</td><td class="desc">A KPI / stat-tile view (module <code>kpi</code>) of a dataset as a panel of aggregate tiles — each tile a <code>sum</code>, <code>avg</code>, <code>min</code>, <code>max</code>, <code>count</code>, <code>countDistinct</code> or a <code>custom</code> reducer over the routed rows, with an optional <code>filter</code> predicate, number <code>format</code> (<code>number</code>/<code>currency</code>/<code>percent</code>/<code>compact</code>), a <code>baseline</code> for a delta, semantic threshold bands (<code>thresholds</code> with two cut points and a direction, or an explicit <code>bands</code> list, giving a <code>good</code>/<code>warn</code>/<code>critical</code> status kept separate from any accent), and an optional <code>sparkline</code> series. It is a dataset viewer like any other: it consumes data through the same keyed-diff <code>rows.apply({ add, update, remove })</code> contract a grid exposes, so a Data Router can <code>attach(value, kpi)</code> and drive a KPI panel beside a grid, a kanban and a chart off one feed. Updates are incremental — a delta adjusts each tile's running accumulator by only the rows it carries (an add contributes, a remove reverses, an update reverses-then-contributes), the two bounded exceptions being a <code>min</code>/<code>max</code> whose current extreme is removed (a rescan of that tile's own value multiset) and a <code>custom</code> reducer (recomputed over the filtered store, an arbitrary function having no inverse). Each tile is a labelled <code><figure></code>, focusable and keyboard-activatable, its value announced, the sparkline respecting <code>prefers-reduced-motion</code>; a <code>tile:click</code> event (also from the keyboard) lets a host drill down or filter a routed grid. Pass <code>null</code> as the element for a headless panel that computes the same tile model without a DOM. Not a dashboard layout engine (that is the parked dashboard generator) and no charting beyond the minimal sparkline (that is the charts module).</td></tr>
|
|
8079
8189
|
<tr><td class="name">createAI</td><td class="desc">Create an AI narrative / insights controller (module <code>ai</code>) over a live grid. It produces a short, plain-language narrative of the grid's <em>computed</em> figures — a per-KPI / per-chart / per-column <code>explain</code>, or an <code>insights</code> panel over the current filtered view. The grid makes no AI call of its own: <code>createAI</code> imports no provider SDK, holds no key, and makes no network request; it calls one host callback, <code>ask({ system, messages, prompt, tools?, schema?, signal })</code> — your model, your key, your privacy decision — the same philosophy as the data adapters. Two grounding paths feed one guard: where the provider offers tool-use the model is given a curated read-only tool set (<code>getSchema</code>, <code>getProfile</code>, <code>getStatistics</code>, <code>getForecast</code>, <code>runQuery</code>) and the grid's own engine computes what it asks for; otherwise the module builds a facts packet from <code>grid.statistics</code>/the profile/forecasts/view counts and passes it in the prompt. Every figure in the narrative is reconciled against the values the engine produced this render — an ungrounded number is stripped before display (the number-reconciliation guard), so a hallucinated figure never reaches the user. The prompt is constrained to narrate-only; the layer is read-only and never mutates data. <code>redact</code> (a column id, a list, or a predicate) and <code>maxRows</code> bound what the module hands <code>ask()</code>, and the module sends nothing itself; an <code>ask()</code> error surfaces a friendly message and the grid stays fully usable, AI being additive rather than load-bearing. Complementary to <code>grid.ai</code> (the intent/plan skill layer): pass no <code>ask</code> and it adopts the grid's configured <code>ai.ask</code>, running the facts-packet path over it. Pass a headless grid for a DOM-free narrative; <code>facts(target)</code> returns the exact grounded packet without calling <code>ask()</code>. UMD global <code>LatticeGridAI</code>.</td></tr>
|
|
8080
8190
|
<tr><td class="name">createTabs</td><td class="desc">Create a tabbed grid (module <code>tabs</code>): a <code>role="tablist"</code> strip above a stack of <code>role="tabpanel"</code> regions, each hosting its own, independently-configured <code>createGrid</code> instance — "configure each tab as per a normal grid" rather than one grid whose state is swapped (<code>ColumnModel#applyState</code> only repositions/hides/resizes existing columns by id; it carries no field, type or row data, so a state-swap only works when every tab shares one schema). <code>createGrid</code> is injected (<code>createTabs(el, { createGrid, tabs })</code>), the same pattern the React/Vue/Svelte adapters use, so the module imports no engine code and adds nothing to a page that does not load it. A tab that names <code>from: '<tabId>'</code> gets a <code>source: { mode: 'derived', from: <the parent tab’s live grid>, where, group, join, … }</code> wired for it automatically — reusing the shipped derived-source mechanism rather than a new config-inheritance one — and activating a derived tab materialises its whole ancestor chain first; a cyclic <code>from</code> graph is refused (naming the exact cycle) when <code>createTabs</code> is called, not at first click. A tab’s grid mounts on first activation and then stays alive, hidden, so its scroll/selection/filters/sort/grouping/expansion — and an open cell/row editor, left exactly as it was, uncommitted and undiscarded — survive a switch natively; <code>destroy()</code> tears every mounted tab down. The strip is a real tablist with <code>aria-selected</code>, a roving <code>tabindex</code>, and manual-activation keyboard handling (arrows/Home/End move focus, Enter/Space or a click activates). Events: <code>tab:changed</code>, a cancellable <code>beforeTabChange</code> paired with <code>tabChange:cancelled</code>. UMD global <code>LatticeGridTabs</code>.</td></tr>
|
|
8081
|
-
<tr><td class="name">createLayout</td><td class="desc">Create a reconfigurable dashboard layout (module <code>layout</code>): a cell grid inside an element, and a set of windows on it that a user can move, resize and close by drag <em>or</em> by keyboard — the surface a customer would otherwise reach for GridStack to get. It is <strong>payload-agnostic</strong>: a window body is a <code>div</code> with an <code>id</code> that the module creates, sizes and never reads, so it imports no engine code at all (not even <code>createGrid</code>) and
|
|
8191
|
+
<tr><td class="name">createLayout</td><td class="desc">Create a reconfigurable dashboard layout (module <code>layout</code>): a cell grid inside an element, and a set of windows on it that a user can move, resize and close by drag <em>or</em> by keyboard — the surface a customer would otherwise reach for GridStack to get. It is <strong>payload-agnostic</strong>: a window body is a <code>div</code> with an <code>id</code> that the module creates, sizes and never reads, so it imports no engine code at all (not even <code>createGrid</code>) and the whole module is about 17,500 bytes gzipped on the built bundle, of which roughly 2KB is the shared module runtime. <code>columns</code>/<code>rows</code> divide the element; <code>overflowX</code> and <code>overflowY</code> are <em>independent</em> axes, each <code>'static'</code> (tracks divide the container with <code>minmax(0, 1fr)</code>) or <code>'scroll'</code> (tracks take a fixed <code>columnWidth</code>/<code>rowHeight</code> and the canvas extends past the viewport, so a column keeps the size it asked for — measured: shrinking a 600px host to 300px leaves a 200px column at 200px). Spacing takes a real CSS length: a number of pixels, or <code>'200px'</code>, <code>'25%'</code>, <code>'1fr'</code>, <code>'2rem'</code>; anything else is refused by name and replaced by the default, because the value reaches an inline style. Windows are placed by 1-based <code>xPos</code>/<code>yPos</code>/<code>xSize</code>/<code>ySize</code>, or auto-placed in the first free cell; <code>chrome</code> defaults on, and <code>closable</code>/<code>movable</code>/<code>resizable</code> all default <em>off</em>, so a fixed dashboard is fixed without opting out. <code>compact: 'vertical'</code> pushes displaced windows down then pulls them up (<code>window:moved</code> carries both <code>to</code> and <code>landed</code>); <code>'none'</code> keeps every window where it is put. <code>closable</code>/<code>movable</code>/<code>resizable</code> each also take a <em>layout-level</em> default of the same name, which a window's own boolean overrides, and <code>setInteractive(true|false|{movable, resizable, closable})</code> changes that default at runtime — the “Edit layout” button — without destroying the layout or any payload in it, with <code>getInteractive()</code> reading it back. Locking always wins and unlocking never overrides an opt-out: <code>setInteractive(false)</code> pins a window that declared <code>movable: true</code>, while <code>setInteractive(true)</code> leaves a window that declared <code>movable: false</code> pinned. The config key and the method are deliberately different: <code>movable: false</code> in the config states the <em>default</em> for windows that declare nothing and takes nothing away from one that opted in, whereas <code>setInteractive(false)</code> is an <em>active lock</em>. <code>getInteractive()</code> is three-valued — <code>undefined</code> for unset, <code>true</code>, or <code>false</code> for a lock — and a key carrying <code>undefined</code> is treated as absent, so <code>setInteractive(getInteractive())</code> is a no-op in every state. It moves both halves of the enforcement, the rendered handles <em>and</em> the pointer and keyboard gesture checks, and fires no event because a mode is not an arrangement — <code>getLayout()</code> neither carries it nor restores it. A locked layout is not a read-only dashboard: what is inside a window is configured with that payload's own settings. <code>maximise(id)</code>, <code>minimise(id)</code> and <code>restore(id)</code> are the display modes, with <code>maximised()</code> and <code>minimised()</code> reading them back: maximise fills the <em>layout host</em> rather than the browser window (no <code>position: fixed</code>, whose containing block is the nearest ancestor with a <code>transform</code> or a <code>contain</code>; no reparenting; nothing that can disturb the page around the dashboard), hides the other windows, runs <strong>no compaction at all</strong> and keeps the payload container as the very same DOM node — and <kbd>Escape</kbd> restores it from anywhere inside the layout. <code>minimise</code> draws a window as a single row and hides its payload while its chrome stays to carry the way back, so the windows below pull up on screen; in the arrangement nothing moves at all, because the collapse is a projection of the dashboard rather than a change to it, so restoring gives back exactly the arrangement that was there in <em>any</em> order and with any number of other windows still collapsed. A <code>chrome: false</code> window is refused by name. Both controls are opt-in per window (<code>maximisable</code>, <code>minimisable</code>) with a layout-level default of the same name, and <code>setInteractive()</code> deliberately does not touch either: a mode is not an arrangement, so neither appears in <code>getLayout()</code>, which reports the underlying placement in both states. Keyboard parity with the drag: a focusable handle per window running the kanban board's grab/move/drop/cancel model, with a polite live region announcing grabbed, every tentative position, dropped, cancelled and reverted. It owns exactly <strong>one</strong> <code>ResizeObserver</code> for the whole layout, over two targets, and tells payloads their new content box through <code>window:resized</code> — it never calls into a payload, because it cannot know what one is. Events: <code>window:moved</code>, <code>window:resized</code>, <code>window:closed</code>, <code>layout:changed</code>, the cancellable <code>beforeWindowMove</code>/<code>beforeWindowResize</code>/<code>beforeWindowClose</code> and their <code>*:cancelled</code> pairs; drag progress is not emitted per frame. <code>getLayout()</code>/<code>setLayout()</code> round-trip the arrangement as plain JSON, and <code>getState()</code>/<code>setState()</code> are the versioned pair. Closing a window does <strong>not</strong> destroy its payload — the container is handed back on <code>window:closed</code> and the host owns that lifecycle. UMD global <code>LatticeGridLayout</code>.</td></tr>
|
|
8082
8192
|
<tr><td class="name">createDevtools</td><td class="desc">Mount the devtools panel against a grid, including its accessibility checks.</td></tr>
|
|
8083
8193
|
<tr><td class="name">createGantt</td><td class="desc">Create a project-planning Gantt controller (module <code>gantt</code>) over a task list and a dependency list. A CPM engine (<code>computeSchedule</code>) computes each task's early/late start and finish, its slack and the zero-float critical path, honouring the four link types (<code>LINK_TYPES</code>: FS/SS/FF/SF) with lag, and recomputes on every edit — emitting <code>schedule</code> or, on a dependency cycle or bad input, <code>error</code> (a code from <code>SCHEDULE_ERROR</code>). Milestones are zero-duration points; summary (WBS) tasks are derived from their children (earliest start, latest finish, weighted progress) rather than scheduled; <code>findViolations</code> flags a task placed earlier than its predecessors allow, and <code>toISODate</code> maps an engine day-number back to a calendar date.</td></tr>
|
|
8084
8194
|
<tr><td class="name">createLatticeGridElement</td><td class="desc">Build the element class without registering it, for a custom registry.</td></tr>
|
|
@@ -9408,7 +9518,7 @@ grid.licence.state(); <span class="cmt">// 'licensed' | 'localhost' | 'trial'<
|
|
|
9408
9518
|
|
|
9409
9519
|
<footer>
|
|
9410
9520
|
<p>
|
|
9411
|
-
Lattice Grid 1.
|
|
9521
|
+
Lattice Grid 1.65.0 · Copyright © 2026 TOCLOCO Inc. All rights reserved.
|
|
9412
9522
|
Written against the shipped source. Where this guide and the code disagree, the code wins,
|
|
9413
9523
|
please <a href="https://www.latticegrid.dev">tell us</a>.
|
|
9414
9524
|
</p>
|