@toclocoinc/lattice-grid 1.50.0 → 1.51.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 +1 -1
- package/docs/API.html +296 -7
- package/docs/api-detail.html +233 -1
- package/lattice-grid.d.ts +149 -6
- package/lattice-grid.esm.min.js +380 -67
- package/lattice-grid.min.cjs +380 -67
- package/lattice-grid.min.js +380 -67
- package/modules/ai.esm.min.js +4 -4
- package/modules/ai.min.cjs +4 -4
- package/modules/ai.min.js +4 -4
- package/modules/angular.esm.min.js +2 -2
- package/modules/angular.min.cjs +2 -2
- package/modules/angular.min.js +2 -2
- package/modules/chart-alluvial.esm.min.js +1 -1
- package/modules/chart-arc.esm.min.js +1 -1
- package/modules/chart-bubblemap.esm.min.js +1 -1
- package/modules/chart-bump.esm.min.js +1 -1
- package/modules/chart-calendar.esm.min.js +1 -1
- package/modules/chart-decomposition.esm.min.js +1 -1
- package/modules/chart-diverging.esm.min.js +1 -1
- package/modules/chart-dumbbell.esm.min.js +1 -1
- package/modules/chart-fan.esm.min.js +1 -1
- package/modules/chart-hexbin.esm.min.js +1 -1
- package/modules/chart-hexmap.esm.min.js +1 -1
- package/modules/chart-icicle.esm.min.js +1 -1
- package/modules/chart-parallel.esm.min.js +1 -1
- package/modules/chart-ridgeline.esm.min.js +1 -1
- package/modules/chart-roc.esm.min.js +1 -1
- package/modules/chart-slope.esm.min.js +1 -1
- package/modules/chart-splom.esm.min.js +1 -1
- package/modules/chart-waffle.esm.min.js +1 -1
- package/modules/charts.esm.min.js +4 -4
- package/modules/charts.min.cjs +4 -4
- package/modules/charts.min.js +4 -4
- package/modules/data-router.esm.min.js +7 -5
- package/modules/data-router.min.cjs +7 -5
- package/modules/data-router.min.js +7 -5
- package/modules/devtools.esm.min.js +2 -2
- package/modules/devtools.min.cjs +2 -2
- package/modules/devtools.min.js +2 -2
- package/modules/dhtmlx-compat.esm.min.js +4 -4
- package/modules/dhtmlx-compat.min.cjs +4 -4
- package/modules/dhtmlx-compat.min.js +4 -4
- package/modules/gantt.esm.min.js +4 -4
- package/modules/gantt.min.cjs +4 -4
- package/modules/gantt.min.js +4 -4
- package/modules/htmx.esm.min.js +380 -67
- package/modules/htmx.min.cjs +380 -67
- package/modules/htmx.min.js +380 -67
- package/modules/kanban.esm.min.js +4 -4
- package/modules/kanban.min.cjs +4 -4
- package/modules/kanban.min.js +4 -4
- package/modules/kpi.esm.min.js +30 -7
- package/modules/kpi.min.cjs +30 -7
- package/modules/kpi.min.js +30 -7
- package/modules/mock-socket.esm.min.js +2 -2
- package/modules/mock-socket.min.cjs +2 -2
- package/modules/mock-socket.min.js +2 -2
- package/modules/react.esm.min.js +2 -2
- package/modules/react.min.cjs +2 -2
- package/modules/react.min.js +2 -2
- package/modules/svelte.esm.min.js +2 -2
- package/modules/svelte.min.cjs +2 -2
- package/modules/svelte.min.js +2 -2
- package/modules/tabs.esm.min.js +20 -12
- package/modules/tabs.min.cjs +20 -12
- package/modules/tabs.min.js +20 -12
- package/modules/vue.esm.min.js +2 -2
- package/modules/vue.min.cjs +2 -2
- package/modules/vue.min.js +2 -2
- package/modules/webcomponent.esm.min.js +380 -67
- package/modules/webcomponent.min.cjs +380 -67
- package/modules/webcomponent.min.js +380 -67
- package/package.json +1 -1
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.51.0</p>
|
|
441
441
|
<nav>
|
|
442
442
|
<div class="rail__group">
|
|
443
443
|
<span class="rail__label">Start here</span>
|
|
@@ -473,6 +473,7 @@
|
|
|
473
473
|
<span class="rail__label">Connected grids</span>
|
|
474
474
|
<a href="#pushdown">Querying an engine</a>
|
|
475
475
|
<a href="#derived">Derived grids</a>
|
|
476
|
+
<a href="#derived-union">Combining several grids</a>
|
|
476
477
|
<a href="#cross-filter">Cross-filtering</a>
|
|
477
478
|
<a href="#joins">Joining two grids</a>
|
|
478
479
|
<a href="#aggregate-safety">Aggregate safety</a>
|
|
@@ -3325,6 +3326,81 @@ createGrid(right, {
|
|
|
3325
3326
|
</table>
|
|
3326
3327
|
</div>
|
|
3327
3328
|
|
|
3329
|
+
<h2 id="derived-union">Combining several grids into one</h2>
|
|
3330
|
+
<p class="lead-in">
|
|
3331
|
+
A <code>join</code> matches two grids on a shared key. Some questions have no key to share at
|
|
3332
|
+
all — "the worst performers across two regional datasets" when the two regions use
|
|
3333
|
+
unrelated ids — and for those, <code>from</code> takes an array of sources instead of one
|
|
3334
|
+
grid: every source is read, concatenated in the order you declared them, and only then does the
|
|
3335
|
+
rest of the pipeline run, once, over the combined set.
|
|
3336
|
+
</p>
|
|
3337
|
+
<div class="example">
|
|
3338
|
+
<p class="example__label">Worst performers, two unrelated datasets</p>
|
|
3339
|
+
<pre><code>createGrid(right, {
|
|
3340
|
+
columns: [{ id: 'title' }, { id: 'severity', type: 'number' }, { id: '__source' }],
|
|
3341
|
+
source: {
|
|
3342
|
+
mode: 'derived',
|
|
3343
|
+
from: [
|
|
3344
|
+
{ grid: eastIncidents, label: 'east' },
|
|
3345
|
+
{ grid: westIncidents, label: 'west', map: (row) => ({ title: row.name, severity: row.rating }) },
|
|
3346
|
+
],
|
|
3347
|
+
sort: [{ col: 'severity', dir: 'desc' }],
|
|
3348
|
+
limit: 10,
|
|
3349
|
+
},
|
|
3350
|
+
});</code></pre>
|
|
3351
|
+
</div>
|
|
3352
|
+
<p>
|
|
3353
|
+
<code>label</code> defaults to the source's position in the array (<code>'0'</code>,
|
|
3354
|
+
<code>'1'</code>, …), and <code>follow</code> is independent per source — filtering
|
|
3355
|
+
one narrows only its own contribution, exactly as a lone <code>from</code> follows its grid
|
|
3356
|
+
today. A source with differently-named fields uses <code>map</code> to project them into a
|
|
3357
|
+
common shape before it joins the rest.
|
|
3358
|
+
</p>
|
|
3359
|
+
<div class="why">
|
|
3360
|
+
<p><strong><code>__source</code> is not optional.</strong> Every combined row carries it —
|
|
3361
|
+
the entry's <code>label</code>, or its index when unlabelled — and it is an ordinary field
|
|
3362
|
+
to <code>where</code>, <code>groupBy</code> and <code>select</code>. Leaving it out would mean a
|
|
3363
|
+
combined list that cannot say where any row came from, which defeats most of the reason to
|
|
3364
|
+
combine several sources in the first place.</p>
|
|
3365
|
+
<p><strong>The union of fields, never a merge.</strong> A field only one source has is
|
|
3366
|
+
<code>undefined</code> on the others' rows, not fabricated and not type-coerced — two
|
|
3367
|
+
sources disagreeing about what a field means is yours to resolve with <code>map</code>, not
|
|
3368
|
+
something the union guesses at. And there is no dedup: two sources reporting the same fact both
|
|
3369
|
+
appear as separate rows. There is no UNION-vs-UNION-ALL distinction to draw; reach for
|
|
3370
|
+
<code>join</code> when rows should be matched on a key rather than stacked.</p>
|
|
3371
|
+
<p><strong>The key is namespaced, only where it needs to be.</strong> Two sources can easily
|
|
3372
|
+
share row identifiers, so the derived <code>__key</code> is qualified by the source tag when
|
|
3373
|
+
nothing is grouped. Grouped, <code>__key</code> is the group value exactly as it always has
|
|
3374
|
+
been, and rows from different sources landing in the same group is what grouping a union is
|
|
3375
|
+
<em>for</em>, not a collision.</p>
|
|
3376
|
+
<p><strong>An empty source is fine; a broken one is loud.</strong> A source with no rows
|
|
3377
|
+
contributes nothing. A source that throws while being read is named in a console warning and
|
|
3378
|
+
skipped for that pass — a silently missing source would make "worst across both" quietly
|
|
3379
|
+
wrong, so it is reported rather than swallowed.</p>
|
|
3380
|
+
<p><strong>A cycle is refused when the source is built.</strong> If a union's sources include
|
|
3381
|
+
the grid being derived, directly or through a chain of other derived grids, it is refused up
|
|
3382
|
+
front, naming the offending source, rather than being recursed into.</p>
|
|
3383
|
+
<p><strong>A union never patches — know the cost before you reach for one at scale.</strong>
|
|
3384
|
+
A lone <code>from</code> maintains its grouping incrementally: an edit that names the rows it
|
|
3385
|
+
touched re-reduces only the groups those rows belong to. A union does not do this for any of its
|
|
3386
|
+
sources — every change on <em>any</em> parent re-reads and re-derives the <em>whole</em>
|
|
3387
|
+
combined set from scratch. On a synthetic 200,000-row union across four sources, one row changed
|
|
3388
|
+
on one parent cost on the order of <strong>800 ms–3 s</strong> (machine-dependent;
|
|
3389
|
+
run <code>node bench/union-parents.mjs</code> against your own shape), against a few
|
|
3390
|
+
milliseconds for the equivalent patched change on a lone <code>from</code> over the same row
|
|
3391
|
+
count. And because a union is watched by as many independent change streams as it has sources,
|
|
3392
|
+
that full-rescan cost is paid once <em>per source that moves</em>, not once per union —
|
|
3393
|
+
four active parents each firing their own updates pay it four times over. Below a few tens of
|
|
3394
|
+
thousands of combined rows this is unlikely to matter; above that, or with several sources each
|
|
3395
|
+
updating on their own live feed, budget for it, keep <code>refresh</code> away from every tick
|
|
3396
|
+
(<code>idle</code>, or a debounce), and prefer fewer, larger sources over many small ones where
|
|
3397
|
+
the shape of your data allows it.</p>
|
|
3398
|
+
<p><strong>Not supported alongside a union.</strong> <code>crossFilter</code> has no single
|
|
3399
|
+
target once there is more than one parent; <code>profile</code> reduces one grid's own columns.
|
|
3400
|
+
Both are refused with a warning rather than guessed at, and the top-level <code>follow</code> is
|
|
3401
|
+
ignored in favour of each source's own.</p>
|
|
3402
|
+
</div>
|
|
3403
|
+
|
|
3328
3404
|
<h2 id="cross-filter">Cross-filtering</h2>
|
|
3329
3405
|
<p class="lead-in">
|
|
3330
3406
|
A derived panel can filter the grid it summarises. Click a region in the summary and the
|
|
@@ -5982,6 +6058,162 @@ r.ok ? r.value : r.error; <span class="cmt">// 42</span></code></pre>
|
|
|
5982
6058
|
a missing <code>return</code> is a typo and deleting the whole menu is a harsh reading of
|
|
5983
6059
|
one.</p>
|
|
5984
6060
|
</div>
|
|
6061
|
+
<h3 id="per-column-menu">Declaring a menu on the column itself</h3>
|
|
6062
|
+
<p class="lead-in">
|
|
6063
|
+
A cell menu can also be declared <strong>on the column</strong>, with
|
|
6064
|
+
<code>contextMenu</code> on the column definition. It takes the same shapes the grid-level
|
|
6065
|
+
option takes, plus a bare array for the common “just these items here” case:
|
|
6066
|
+
<code>boolean | MenuItem[] | (params, defaults) => items</code>.
|
|
6067
|
+
</p>
|
|
6068
|
+
<div class="example">
|
|
6069
|
+
<p class="example__label">Each column's menu logic beside the column it is about</p>
|
|
6070
|
+
<pre><code>createGrid(el, {
|
|
6071
|
+
columns: [
|
|
6072
|
+
{ field: 'account' },
|
|
6073
|
+
<span class="cmt">// Just these items, here.</span>
|
|
6074
|
+
{ field: 'owner', contextMenu: [{ name: 'Reassign', action: reassign }] },
|
|
6075
|
+
<span class="cmt">// Or the built-ins plus one, the same form the grid-level option takes.</span>
|
|
6076
|
+
{
|
|
6077
|
+
field: 'amount',
|
|
6078
|
+
contextMenu: (params, defaults) => [
|
|
6079
|
+
...defaults,
|
|
6080
|
+
{ separator: <span class="kw">true</span> },
|
|
6081
|
+
{ name: `Reprice ${params.value}`, action: (ctx) => reprice(ctx.data) },
|
|
6082
|
+
],
|
|
6083
|
+
},
|
|
6084
|
+
<span class="cmt">// And nothing at all on a column nobody should act on from here.</span>
|
|
6085
|
+
{ field: 'nationalId', contextMenu: <span class="kw">false</span> },
|
|
6086
|
+
],
|
|
6087
|
+
});</code></pre>
|
|
6088
|
+
</div>
|
|
6089
|
+
<p class="lead-in">
|
|
6090
|
+
This adds no power the grid-level option did not have — <code>params.colId</code> and
|
|
6091
|
+
<code>params.column</code> always let one callback branch by column. What it adds is
|
|
6092
|
+
<strong>locality</strong>: the menu for a column is declared where the column is, instead of
|
|
6093
|
+
collecting into one growing <code>switch</code> a long way from the thing it is about.
|
|
6094
|
+
</p>
|
|
6095
|
+
|
|
6096
|
+
<h4 id="per-column-menu-chain">The three levels compose as a chain</h4>
|
|
6097
|
+
<p class="lead-in">
|
|
6098
|
+
The built-in items go in first, then the grid-level <code>contextMenu</code>, then the
|
|
6099
|
+
column's own — <strong>each handed the previous level's result as its
|
|
6100
|
+
<code>defaults</code></strong>. A column that wants one extra item writes one extra item; it
|
|
6101
|
+
never has to restate Paste, Clear and Fill down, nor whatever the grid-level builder just
|
|
6102
|
+
added.
|
|
6103
|
+
</p>
|
|
6104
|
+
<div class="example">
|
|
6105
|
+
<p class="example__label">Built-ins → grid → column, executed</p>
|
|
6106
|
+
<pre data-run="js" data-expect="grid|column" data-covers="config:contextMenu"><code><span class="kw">const</span> { createTestDom, flushFrames } = <span class="kw">await</span> import('../packages/dom/src/renderer/testdom.js');
|
|
6107
|
+
<span class="kw">const</span> { createGrid } = <span class="kw">await</span> import('../packages/dom/src/index.js');
|
|
6108
|
+
<span class="kw">const</span> { root } = createTestDom({ width: 600, height: 300 });
|
|
6109
|
+
|
|
6110
|
+
<span class="kw">let</span> handedToTheColumn = [];
|
|
6111
|
+
<span class="kw">const</span> grid = createGrid(root, {
|
|
6112
|
+
rowKey: 'id',
|
|
6113
|
+
rows: [{ id: 1, account: 'Acme', amount: 120 }],
|
|
6114
|
+
columns: [
|
|
6115
|
+
{ field: 'account' },
|
|
6116
|
+
{
|
|
6117
|
+
field: 'amount',
|
|
6118
|
+
contextMenu: (params, defaults) => {
|
|
6119
|
+
handedToTheColumn = defaults.map((d) => d.name);
|
|
6120
|
+
<span class="kw">return</span> [...defaults, { name: 'from the column', action() {} }];
|
|
6121
|
+
},
|
|
6122
|
+
},
|
|
6123
|
+
],
|
|
6124
|
+
contextMenu: (params, defaults) => [...defaults, { name: 'from the grid', action() {} }],
|
|
6125
|
+
});
|
|
6126
|
+
flushFrames();
|
|
6127
|
+
|
|
6128
|
+
<span class="kw">const</span> row = grid.rows.get(0);
|
|
6129
|
+
grid.emit('cell:contextmenu', {
|
|
6130
|
+
row, key: row.key, index: 0, colId: 'amount',
|
|
6131
|
+
column: grid.columns.get('amount'), value: 120,
|
|
6132
|
+
event: { clientX: 10, clientY: 10, preventDefault() {} },
|
|
6133
|
+
});
|
|
6134
|
+
flushFrames();
|
|
6135
|
+
|
|
6136
|
+
<span class="cmt">// The column builder was handed the grid builder's output, not the raw</span>
|
|
6137
|
+
<span class="cmt">// built-ins: 'from the grid' is already in its `defaults`.</span>
|
|
6138
|
+
<span class="kw">const</span> chained = handedToTheColumn.includes('from the grid');
|
|
6139
|
+
<span class="kw">const</span> shown = [...root.querySelectorAll('.lat-menu__item')]
|
|
6140
|
+
.map((i) => String(i.textContent).trim());
|
|
6141
|
+
grid.destroy();
|
|
6142
|
+
<span class="kw">return</span> chained && shown.includes('from the column') ? 'grid|column' : 'broken';</code></pre>
|
|
6143
|
+
</div>
|
|
6144
|
+
<p class="lead-in">
|
|
6145
|
+
<strong>Suppression follows the same order, and the more specific level wins.</strong>
|
|
6146
|
+
<code>contextMenu: false</code> on a column is a statement about <em>that column</em> and no
|
|
6147
|
+
other. Equally, a column may declare a menu on a grid whose <code>contextMenu</code> is
|
|
6148
|
+
<code>false</code> — which is how you say “no menu anywhere except here”.
|
|
6149
|
+
<code>contextMenu: true</code> on a column means “whatever came before”, so it
|
|
6150
|
+
restores the built-in menu on a grid that turned it off.
|
|
6151
|
+
</p>
|
|
6152
|
+
<table class="ref">
|
|
6153
|
+
<thead><tr><th>Grid level</th><th>Column level</th><th>What opens on that column</th></tr></thead>
|
|
6154
|
+
<tbody>
|
|
6155
|
+
<tr><td><em>not set</em></td><td><em>not set</em></td><td>the built-in menu</td></tr>
|
|
6156
|
+
<tr><td>a builder</td><td><em>not set</em></td><td>the builder's result</td></tr>
|
|
6157
|
+
<tr><td>a builder</td><td>a builder</td><td>the column's builder, handed the grid builder's result</td></tr>
|
|
6158
|
+
<tr><td>a builder</td><td>an array</td><td>the array — the grid level still ran, and was replaced</td></tr>
|
|
6159
|
+
<tr><td>a builder, or not set</td><td><code>false</code></td><td><strong>nothing</strong> — the column suppresses, and no other column is affected</td></tr>
|
|
6160
|
+
<tr><td><code>false</code></td><td><em>not set</em></td><td><strong>nothing</strong> — the grid-level off stands, as it always has</td></tr>
|
|
6161
|
+
<tr><td><code>false</code></td><td>an array</td><td><strong>the column's array.</strong> The column opts back in: grid-level <code>false</code> is a default, not a lock</td></tr>
|
|
6162
|
+
<tr><td><code>false</code></td><td>a builder</td><td><strong>the builder's result</strong>, handed the built-in items as its <code>defaults</code>. The column opts back in</td></tr>
|
|
6163
|
+
<tr><td><code>false</code></td><td><code>true</code></td><td><strong>the built-in menu.</strong> The column opts back in and asks for the defaults</td></tr>
|
|
6164
|
+
</tbody>
|
|
6165
|
+
</table>
|
|
6166
|
+
<div class="note">
|
|
6167
|
+
<p><strong><code>contextMenu: false</code> on the grid is a default, not a lock.</strong> If
|
|
6168
|
+
you set it as a safety property — a read-only grid, a screen where nobody should be
|
|
6169
|
+
able to copy or clear from a right-click — be aware that a column declaring its own
|
|
6170
|
+
<code>contextMenu</code> will still open one, because the more specific level wins in
|
|
6171
|
+
<em>both</em> directions. That is deliberate: a read-only grid with one actionable column is a
|
|
6172
|
+
real shape, and it is the only way to say “no menu anywhere except here”. But it
|
|
6173
|
+
does mean grid-level <code>false</code> does not guarantee that no cell menu can open
|
|
6174
|
+
anywhere — only that none opens unless a column asks for one. If you need the absolute
|
|
6175
|
+
guarantee, do not declare <code>contextMenu</code> on any column.</p>
|
|
6176
|
+
</div>
|
|
6177
|
+
<div class="why">
|
|
6178
|
+
<p><strong>A chain, not a replacement.</strong> If the column level replaced the grid level,
|
|
6179
|
+
every column that wanted one extra item would have to restate everything the grid-level
|
|
6180
|
+
builder does — and would then stop tracking it the first time it changed. This is the
|
|
6181
|
+
same rule <code>columnMenu</code> already follows for the header: you are handed what came
|
|
6182
|
+
before so you can add to it rather than reproduce it.</p>
|
|
6183
|
+
</div>
|
|
6184
|
+
|
|
6185
|
+
<h4 id="per-column-menu-edges">A range, and rows that belong to no column</h4>
|
|
6186
|
+
<p class="lead-in">
|
|
6187
|
+
<strong>On a multi-column selection, the column you right-clicked decides.</strong> Not the
|
|
6188
|
+
intersection of the selected columns' menus, which silently drops items; not their union,
|
|
6189
|
+
which offers actions that are wrong for most of the selection. The clicked column is the one
|
|
6190
|
+
the user pointed at, and it is the one that answers — the built-in range actions
|
|
6191
|
+
(Copy, Clear, Fill down) still act on the whole range as they always did.
|
|
6192
|
+
</p>
|
|
6193
|
+
<p class="lead-in">
|
|
6194
|
+
<strong>A row with no owning column falls back to the grid-level menu.</strong> Group rows,
|
|
6195
|
+
pivot group rows and full-width rows do not belong to one column, so there is no column-level
|
|
6196
|
+
declaration to consult; the chain simply has one fewer link and the grid-level menu stands.
|
|
6197
|
+
Nothing errors and nothing silently shows an empty menu.
|
|
6198
|
+
</p>
|
|
6199
|
+
<p class="lead-in">
|
|
6200
|
+
Every route honours the column: a right-click, and the keyboard's
|
|
6201
|
+
<kbd>Shift</kbd>+<kbd>F10</kbd> or <kbd>Context Menu</kbd> key on the focused cell.
|
|
6202
|
+
The menu is a <code>role="menu"</code> of <code>role="menuitem"</code>s that takes focus and
|
|
6203
|
+
closes on <kbd>Escape</kbd> wherever it was opened from.
|
|
6204
|
+
</p>
|
|
6205
|
+
<div class="why">
|
|
6206
|
+
<p><strong>Trust is unchanged.</strong> A <code>MenuItem</code> is the same object it always
|
|
6207
|
+
was, including <code>icon</code> markup being trusted at the same level as
|
|
6208
|
+
<code>action</code>. Declaring one on a column changes <em>where</em> it is written, not who
|
|
6209
|
+
is trusted to write it: a column definition is your code, exactly as a grid config is.</p>
|
|
6210
|
+
</div>
|
|
6211
|
+
<p class="lead-in">
|
|
6212
|
+
A column preset or <code>columnDefaults</code> may supply <code>contextMenu</code> too, and
|
|
6213
|
+
the column's own declaration outranks both — so a house rule like “no cell menu on
|
|
6214
|
+
anything tagged sensitive” is written once.
|
|
6215
|
+
</p>
|
|
6216
|
+
|
|
5985
6217
|
<p class="lead-in">
|
|
5986
6218
|
<code>columnMenu</code> takes the same function form, for both routes into a column's menu:
|
|
5987
6219
|
the header's 3-dot button and a right-click on the heading. Its <code>params</code> is
|
package/lattice-grid.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/*!
|
|
2
|
-
* Lattice Grid 1.
|
|
2
|
+
* Lattice Grid 1.51.0, type declarations
|
|
3
3
|
* Copyright (c) 2026 TOCLOCO Inc. All rights reserved.
|
|
4
4
|
* https://latticegrid.dev
|
|
5
5
|
*/
|
|
@@ -863,6 +863,24 @@ export interface Column {
|
|
|
863
863
|
layout?: ColumnLayoutSpec | number;
|
|
864
864
|
/** The header cell: its text, tooltip, menu and any header chart. */
|
|
865
865
|
header?: ColumnHeaderSpec | string;
|
|
866
|
+
/**
|
|
867
|
+
* The cell right-click menu for this column alone (BACKLOG-0001068), in the
|
|
868
|
+
* same shapes the grid-level `contextMenu` takes plus a bare array for the
|
|
869
|
+
* common "just these items here" case.
|
|
870
|
+
*
|
|
871
|
+
* Declared where the column is declared rather than as another branch inside
|
|
872
|
+
* one grid-level callback: the menu logic for a column belongs beside the
|
|
873
|
+
* column it belongs to. It does not replace the grid-level menu — the three
|
|
874
|
+
* levels compose as a chain, built-in defaults then grid-level then this one,
|
|
875
|
+
* each handed the previous result as its `defaults`, so a column adding one
|
|
876
|
+
* item does not have to restate Paste, Clear and Fill down.
|
|
877
|
+
*
|
|
878
|
+
* `false` suppresses the menu on this column and leaves every other column
|
|
879
|
+
* alone: what a sensitive or read-only column wants. The more specific level
|
|
880
|
+
* wins, so a column may also declare a menu on a grid whose `contextMenu` is
|
|
881
|
+
* `false`.
|
|
882
|
+
*/
|
|
883
|
+
contextMenu?: boolean | MenuItem[] | ((p: CellMenuParams, defaults: MenuItem[]) => MenuItem[] | void);
|
|
866
884
|
/**
|
|
867
885
|
* When this column's header controls — its sort arrow, filter funnel and menu
|
|
868
886
|
* button — are shown, overriding the grid-level `headerControls` default for
|
|
@@ -939,6 +957,12 @@ export interface ResolvedColumn {
|
|
|
939
957
|
grandTotal: TotalName | TotalFn | null;
|
|
940
958
|
layout: ColumnLayoutSpec;
|
|
941
959
|
header: ColumnHeaderSpec;
|
|
960
|
+
/**
|
|
961
|
+
* This column's own cell-menu declaration (BACKLOG-0001068), or null when it
|
|
962
|
+
* makes none and the grid-level menu stands alone. Carried onto the resolved
|
|
963
|
+
* column so a column preset or `columnDefaults` can supply one.
|
|
964
|
+
*/
|
|
965
|
+
contextMenu: boolean | MenuItem[] | ((p: CellMenuParams, defaults: MenuItem[]) => MenuItem[] | void) | null;
|
|
942
966
|
export: ColumnExportSpec;
|
|
943
967
|
lookup: LookupSpec | null;
|
|
944
968
|
allowGroup: boolean;
|
|
@@ -1220,9 +1244,57 @@ export interface DerivedSourceConfig {
|
|
|
1220
1244
|
* key when nothing is grouped. `config.rowKey` defaults to it, so it need not
|
|
1221
1245
|
* be set; an explicit `rowKey` still wins.
|
|
1222
1246
|
*/
|
|
1223
|
-
/**
|
|
1224
|
-
|
|
1225
|
-
|
|
1247
|
+
/**
|
|
1248
|
+
* The grid to read, or several to combine into one row set before the rest
|
|
1249
|
+
* of the pipeline runs (BACKLOG-0001045). A bare `Grid` is shorthand for a
|
|
1250
|
+
* `UnionSourceOptions` with no `label`/`follow`/`map` override, so an
|
|
1251
|
+
* existing `from: <grid>` keeps meaning exactly what it always has.
|
|
1252
|
+
*
|
|
1253
|
+
* Given an array, every source is read (each narrowed by its own `follow`,
|
|
1254
|
+
* defaulting to `'filtered'` as a lone `from` does today), concatenated in
|
|
1255
|
+
* **declaration order** — deterministic, not interleaved — and only then
|
|
1256
|
+
* does `unnest`/`join`/`where`/`bucket`/`groupBy`/`select`/`sort`/`limit`/
|
|
1257
|
+
* `limitPer`/`cumulative` run, over the combined set, so "the worst
|
|
1258
|
+
* performers across both" is one derivation rather than a hand-merge.
|
|
1259
|
+
*
|
|
1260
|
+
* The output carries the **union of the sources' fields**: a field present
|
|
1261
|
+
* on only one source is `undefined` on rows from the others. Sources are
|
|
1262
|
+
* **not** type-reconciled — if two disagree on what a field means or holds,
|
|
1263
|
+
* that is not resolved for you; give each source a `map` to project it into
|
|
1264
|
+
* a common shape first. Every row also carries `__source` (the entry's
|
|
1265
|
+
* `label`, or its declaration index when unlabelled), which is required —
|
|
1266
|
+
* not optional — because without it a combined list cannot be read, filtered
|
|
1267
|
+
* or grouped by where it came from; it is an ordinary field to `where`,
|
|
1268
|
+
* `groupBy` and `select`. And because the derived key (`__key`) would
|
|
1269
|
+
* otherwise collide across sources sharing the same identifiers, it is
|
|
1270
|
+
* namespaced by the same source tag when nothing is grouped (a grouped
|
|
1271
|
+
* union's `__key` is the group value, exactly as today, and rows from
|
|
1272
|
+
* different sources correctly land in the *same* group when their group
|
|
1273
|
+
* values agree — that merging is the point of grouping a union, not a
|
|
1274
|
+
* collision to guard against).
|
|
1275
|
+
*
|
|
1276
|
+
* This is **not** a join: there is no dedup or merge-on-key, and it draws no
|
|
1277
|
+
* UNION/UNION ALL distinction — overlapping rows from two sources simply
|
|
1278
|
+
* both appear. Reach for `join` when two sides share a key and you want them
|
|
1279
|
+
* matched rather than stacked.
|
|
1280
|
+
*
|
|
1281
|
+
* An empty source contributes nothing and the rest still combine; a source
|
|
1282
|
+
* that fails to read is named in a `warnOnce` and skipped for that pass
|
|
1283
|
+
* rather than silently dropped, because a silently missing source would
|
|
1284
|
+
* make "worst across both" quietly wrong. A source list that includes the
|
|
1285
|
+
* grid being derived, directly or through a chain, is refused when the
|
|
1286
|
+
* source is built (naming the offender) rather than recursed into.
|
|
1287
|
+
*
|
|
1288
|
+
* `crossFilter` has no single target once there is more than one parent, so
|
|
1289
|
+
* it is not supported alongside a union `from` (ignored, with a `warnOnce`,
|
|
1290
|
+
* rather than guessing which parent to push onto).
|
|
1291
|
+
*/
|
|
1292
|
+
from: Grid | UnionSourceOptions[];
|
|
1293
|
+
/**
|
|
1294
|
+
* Which of its rows to read. `filtered` by default. Ignored — with a
|
|
1295
|
+
* `warnOnce` — when `from` is a union array: each entry there carries its
|
|
1296
|
+
* own `follow` instead (BACKLOG-0001045).
|
|
1297
|
+
*/
|
|
1226
1298
|
follow?: 'filtered' | 'all' | 'selected' | 'grouped';
|
|
1227
1299
|
|
|
1228
1300
|
/** An array property to expand, one row per element, before anything else. */
|
|
@@ -1265,6 +1337,38 @@ export interface DerivedSourceConfig {
|
|
|
1265
1337
|
crossFilter?: boolean | string | { col?: string };
|
|
1266
1338
|
}
|
|
1267
1339
|
|
|
1340
|
+
/**
|
|
1341
|
+
* One member of a union `from` (BACKLOG-0001045): a grid to combine with the
|
|
1342
|
+
* others, plus how to read it and reshape it before it joins the rest. A bare
|
|
1343
|
+
* `Grid` in the `from` array is shorthand for `{ grid }` with every other
|
|
1344
|
+
* field defaulted.
|
|
1345
|
+
*/
|
|
1346
|
+
export interface UnionSourceOptions {
|
|
1347
|
+
/** The grid this source reads. */
|
|
1348
|
+
grid: Grid;
|
|
1349
|
+
/**
|
|
1350
|
+
* Identifies this source: it is what `__source` carries on every row this
|
|
1351
|
+
* source contributes, and what namespaces that row's `__key` so two sources
|
|
1352
|
+
* sharing the same identifiers do not collide. Defaults to the source's
|
|
1353
|
+
* position in the `from` array (`'0'`, `'1'`, …), as a string.
|
|
1354
|
+
*/
|
|
1355
|
+
label?: string;
|
|
1356
|
+
/**
|
|
1357
|
+
* Which of this source's rows to read. `filtered` by default, exactly as a
|
|
1358
|
+
* lone `from` follows its grid today — set independently per source, so
|
|
1359
|
+
* filtering one narrows only its own contribution.
|
|
1360
|
+
*/
|
|
1361
|
+
follow?: 'filtered' | 'all' | 'selected' | 'grouped';
|
|
1362
|
+
/**
|
|
1363
|
+
* Reshape this source's rows into the common shape before they join the
|
|
1364
|
+
* rest — typically a rename or a projection, for a field this source calls
|
|
1365
|
+
* something else. Not a type coercion: if a field means something different
|
|
1366
|
+
* on two sources, `map` is where you make them agree, because the union
|
|
1367
|
+
* itself does not guess.
|
|
1368
|
+
*/
|
|
1369
|
+
map?: (row: unknown) => unknown;
|
|
1370
|
+
}
|
|
1371
|
+
|
|
1268
1372
|
export interface DerivedJoin {
|
|
1269
1373
|
/** The grid holding the other side. */
|
|
1270
1374
|
with: Grid;
|
|
@@ -5952,6 +6056,18 @@ export function duckdbAdapter(options: {
|
|
|
5952
6056
|
from: string;
|
|
5953
6057
|
/** Columns to select. Everything by default. */
|
|
5954
6058
|
fields?: string[];
|
|
6059
|
+
/**
|
|
6060
|
+
* Whether to count the matching set at all. `true` by default: the total is a
|
|
6061
|
+
* separate `count(*)` statement carrying the same `WHERE`, dispatched in the
|
|
6062
|
+
* same tick as the page query rather than serialised behind it
|
|
6063
|
+
* (BACKLOG-0001065). `false` issues no count statement, declares
|
|
6064
|
+
* `capabilities.total: false`, and leaves the result's `total` **absent** — so
|
|
6065
|
+
* the grid scrolls open-ended instead of being told the page length is the
|
|
6066
|
+
* whole set. Turn it off for a grid that never shows a count: an unfiltered
|
|
6067
|
+
* count is answered from Parquet metadata and a filtered one still has to
|
|
6068
|
+
* evaluate the predicate, so it is cheap rather than free.
|
|
6069
|
+
*/
|
|
6070
|
+
count?: boolean;
|
|
5955
6071
|
/**
|
|
5956
6072
|
* The key column an update and a delete target in their `WHERE`, and that an
|
|
5957
6073
|
* add-row is rekeyed by. Write-back is refused unless this names a real column,
|
|
@@ -5974,7 +6090,15 @@ export function duckdbAdapter(options: {
|
|
|
5974
6090
|
* key to rekey the temp row.
|
|
5975
6091
|
*/
|
|
5976
6092
|
returning?: 'row' | 'none';
|
|
5977
|
-
}): PushdownAdapter & {
|
|
6093
|
+
}): PushdownAdapter & {
|
|
6094
|
+
sqlFor(query: RemoteRequest): { sql: string; params: unknown[] };
|
|
6095
|
+
/**
|
|
6096
|
+
* The separate `count(*)` statement that reports the matching set's size, with
|
|
6097
|
+
* the same `WHERE` as {@link sqlFor} and no `ORDER BY` or `LIMIT`
|
|
6098
|
+
* (BACKLOG-0001065). `null` when the adapter was built with `count: false`.
|
|
6099
|
+
*/
|
|
6100
|
+
countSqlFor(query: RemoteRequest): { sql: string; params: unknown[] } | null;
|
|
6101
|
+
};
|
|
5978
6102
|
|
|
5979
6103
|
/**
|
|
5980
6104
|
* An adapter for a DemandFlow entity, speaking `POST /v1/query`.
|
|
@@ -6054,6 +6178,15 @@ export function graphqlAdapter(options: {
|
|
|
6054
6178
|
pagination?: 'offset' | 'cursor';
|
|
6055
6179
|
/** The page size for the whole-result and forward-cursor walks. */
|
|
6056
6180
|
pageSize?: number;
|
|
6181
|
+
/**
|
|
6182
|
+
* Whether the default query asks for `totalCount`. `true` by default.
|
|
6183
|
+
* `false` drops it from the selection set and declares
|
|
6184
|
+
* `capabilities.total: false`, so a grid that never shows a count does not
|
|
6185
|
+
* make the server compute one (BACKLOG-0001065). Unlike the DuckDB adapter the
|
|
6186
|
+
* count is not split into a second operation — that would cost an extra HTTP
|
|
6187
|
+
* round trip rather than saving one — so suppression is the only lever here.
|
|
6188
|
+
*/
|
|
6189
|
+
count?: boolean;
|
|
6057
6190
|
/** Rename the pagination variables the adapter drives per page. */
|
|
6058
6191
|
vars?: Partial<Record<'offset' | 'limit' | 'first' | 'after', string>>;
|
|
6059
6192
|
capabilities?: PushdownCapabilities; operators?: string[];
|
|
@@ -8356,7 +8489,17 @@ declare module 'lattice-grid/modules/kpi' {
|
|
|
8356
8489
|
field?: string;
|
|
8357
8490
|
value: unknown;
|
|
8358
8491
|
formatted: string;
|
|
8359
|
-
|
|
8492
|
+
/**
|
|
8493
|
+
* The tile's semantic band, or `unknown` when the panel holds no rows at
|
|
8494
|
+
* all. `unknown` is decided from data presence before any threshold is
|
|
8495
|
+
* consulted: an aggregation over nothing returns the identity of its
|
|
8496
|
+
* operation (`sum` and `count` return 0), and 0 is a number a threshold
|
|
8497
|
+
* grades, so without it an empty panel would report as a healthy one. A
|
|
8498
|
+
* tile whose `filter` matches none of the rows the panel *does* hold has
|
|
8499
|
+
* measured a real zero and is banded normally. `null` means the tile has no
|
|
8500
|
+
* thresholds or bands configured.
|
|
8501
|
+
*/
|
|
8502
|
+
status: 'good' | 'warn' | 'critical' | 'unknown' | null;
|
|
8360
8503
|
target?: number;
|
|
8361
8504
|
baseline?: number;
|
|
8362
8505
|
delta: number | null;
|