@toclocoinc/lattice-grid 1.7.1 → 1.8.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 +93 -1
- package/docs/AI-SKILL.md +461 -0
- package/docs/API.html +88 -1
- package/docs/api-detail.html +9 -7
- package/lattice-grid.d.ts +96 -4
- package/lattice-grid.esm.min.js +5156 -637
- package/lattice-grid.min.cjs +5156 -637
- package/lattice-grid.min.css +1 -1
- package/lattice-grid.min.js +5156 -637
- package/modules/charts.esm.min.js +5858 -0
- package/modules/devtools.esm.min.js +2 -2
- package/modules/dhtmlx-compat.esm.min.js +5156 -637
- package/modules/htmx.esm.min.js +5156 -637
- package/modules/htmx.min.cjs +5156 -637
- package/modules/htmx.min.js +5156 -637
- package/modules/react.esm.min.js +2 -2
- package/modules/svelte.esm.min.js +2 -2
- package/modules/vue.esm.min.js +2 -2
- package/modules/webcomponent.esm.min.js +5156 -637
- package/package.json +1 -1
package/docs/API.html
CHANGED
|
@@ -397,6 +397,7 @@
|
|
|
397
397
|
<a href="#presentation">grid.presentation</a>
|
|
398
398
|
<a href="#redaction">grid.redaction</a>
|
|
399
399
|
<a href="#formatting">grid.formatting</a>
|
|
400
|
+
<a href="#statistics">grid.statistics</a>
|
|
400
401
|
<a href="#highlight">grid.highlight</a>
|
|
401
402
|
<a href="#views">grid.views</a>
|
|
402
403
|
<a href="#diff">grid.diff</a>
|
|
@@ -793,7 +794,7 @@ autoInit(document); <span class="cmt">// builds every [data-lattice-grid] under
|
|
|
793
794
|
<tbody>
|
|
794
795
|
<tr><td class="name">statusBar</td><td class="type">boolean | { panels }</td><td class="desc">Composable panels along the bottom. Default set: <code>rowCount</code>, <code>selectedCount</code>, <code>aggregation</code>, <code>comments</code>, <code>updates</code>, <code>progress</code>. Each is silent when it has nothing to report.</td></tr>
|
|
795
796
|
<tr><td class="name">maximise</td><td class="type">boolean</td><td class="dflt">true</td><td class="desc"><code>false</code> removes the rail button and <code>grid.maximise</code>, for an application with its own full-screen mode.</td></tr>
|
|
796
|
-
<tr><td class="name">toolPanel</td><td class="type">boolean | object</td><td class="desc">Side dock. <code>panels</code>: <code>columns</code>, <code>filters</code>, <code>views</code>, <code>quick</code>, <code>formatting</code>. <code>side: 'left'</code> makes it the icon rail, which also turns on <code>actions</code> (<code>undo</code>, <code>redo</code>, <code>pause</code>, <code>restore</code>, <code>maximise</code>, then the export group: <code>export</code>, <code>excel</code>, <code>clipboard</code>, <code>print</code> — nine in all, and an array takes these names rather than the button labels) and <code>icons</code>. An explicit array <em>replaces</em> that list rather than extending it; a bare <code>'-'</code> in it renders a divider between groups. <code>exportName</code> names the CSV.</td></tr>
|
|
797
|
+
<tr><td class="name">toolPanel</td><td class="type">boolean | object</td><td class="desc">Side dock. <code>panels</code>: <code>columns</code>, <code>filters</code>, <code>views</code>, <code>quick</code>, <code>formatting</code>, <code>statistics</code>. <code>side: 'left'</code> makes it the icon rail, which also turns on <code>actions</code> (<code>undo</code>, <code>redo</code>, <code>pause</code>, <code>restore</code>, <code>maximise</code>, then the export group: <code>export</code>, <code>excel</code>, <code>clipboard</code>, <code>print</code> — nine in all, and an array takes these names rather than the button labels) and <code>icons</code>. An explicit array <em>replaces</em> that list rather than extending it; a bare <code>'-'</code> in it renders a divider between groups. <code>exportName</code> names the CSV.</td></tr>
|
|
797
798
|
<tr><td class="name">contextMenu</td><td class="type">boolean | (p) => MenuItem[]</td><td class="desc">Right-click menu. The function form is <code>(params, defaults) => items</code> — see <a href="#custom-menu">custom items</a>. <code>false</code> suppresses it — what a read-only grid wants, since the default menu offers Paste, Clear and Fill down.</td></tr>
|
|
798
799
|
<tr><td class="name">columnMenu</td><td class="type">boolean | (p) => MenuItem[]</td><td class="desc">The header's 3-dot menu, and a right-click on a column heading. The function form is <code>(params, defaults) => items</code>, with <code>params</code> carrying <code>colId</code>, <code>column</code> and <code>grid</code> — see <a href="#custom-menu">custom items</a>. <code>false</code> suppresses it.</td></tr>
|
|
799
800
|
<tr><td class="name">shortcuts</td><td class="type">boolean</td><td class="dflt">true</td><td class="desc">The <kbd>?</kbd> keyboard shortcut overlay. <code>false</code> suppresses it, for a host that wants <kbd>?</kbd> for itself. See <a href="api-detail.html#keyboard">Keyboard</a>.</td></tr>
|
|
@@ -1563,6 +1564,89 @@ grid.formatting.clear('margin'); // or clear() for everything</
|
|
|
1563
1564
|
</div>
|
|
1564
1565
|
<div class="note"><p>A rule held here must be JSON: <code>style</code> may not be a function, because the rules are serialised into views and undo slices. Config-time <code>cell.style</code> still accepts one. Group rows are not formatted, matching the way decoration is dropped for them.</p></div>
|
|
1565
1566
|
|
|
1567
|
+
<h3>Rules that describe the data, not a threshold</h3>
|
|
1568
|
+
<p><code>gt: 100</code> needs somebody to know that 100 is the interesting number. Often nobody does — the interesting cells are <em>the top decile</em>, or <em>the outliers</em>, and where those fall is a property of the data rather than of the rule. These operators say that directly, and the grid works out the threshold from the column itself, over the filtered rows.</p>
|
|
1569
|
+
<pre><code>grid.formatting.add('margin', { when: { op: 'outlier' }, style: { background: '#fbeceb' } });
|
|
1570
|
+
grid.formatting.add('qty', { when: { op: 'topPercent', value: 10 }, style: { bold: true } });
|
|
1571
|
+
grid.formatting.add('score', { scale: { from: 'quantile', colours: ['#f8f9fa', '#1a6bc7'] } });
|
|
1572
|
+
|
|
1573
|
+
grid.formatting.distribution('margin'); // { n, min, max, mean, stddev, median, q1, q3, iqr }
|
|
1574
|
+
grid.formatting.restat(); // re-derive every threshold from the data as it stands</code></pre>
|
|
1575
|
+
<div class="table-wrap">
|
|
1576
|
+
<table>
|
|
1577
|
+
<thead><tr><th>Operator</th><th><code>value</code></th><th>Marks</th></tr></thead>
|
|
1578
|
+
<tbody>
|
|
1579
|
+
<tr><td class="sig">topPercent</td><td class="type">10 or 0.1</td><td class="desc">The top tenth of the column. Written either way; both mean the same thing.</td></tr>
|
|
1580
|
+
<tr><td class="sig">bottomPercent</td><td class="type">10 or 0.1</td><td class="desc">The bottom tenth.</td></tr>
|
|
1581
|
+
<tr><td class="sig">topN</td><td class="type">5</td><td class="desc">The five largest, ties included — three rows sharing second place in a top three all take the colour.</td></tr>
|
|
1582
|
+
<tr><td class="sig">bottomN</td><td class="type">5</td><td class="desc">The five smallest.</td></tr>
|
|
1583
|
+
<tr><td class="sig">aboveMean / belowMean</td><td class="type">—</td><td class="desc">Either side of the mean.</td></tr>
|
|
1584
|
+
<tr><td class="sig">aboveMedian / belowMedian</td><td class="type">—</td><td class="desc">Either side of the median, which is the one to reach for on a skewed column.</td></tr>
|
|
1585
|
+
<tr><td class="sig">zAbove / zBelow</td><td class="type">2</td><td class="desc">That many standard deviations from the mean. A column with no spread marks nothing rather than everything.</td></tr>
|
|
1586
|
+
<tr><td class="sig">outlier</td><td class="type">1.5</td><td class="desc">Outside Tukey's fences at that many IQRs — the same definition a box plot draws, so the marked cells are the ones its whiskers exclude.</td></tr>
|
|
1587
|
+
</tbody>
|
|
1588
|
+
</table>
|
|
1589
|
+
</div>
|
|
1590
|
+
<p>A colour scale can take its bounds the same way, with <code>from</code> in place of <code>min</code> and <code>max</code>: <code>'minmax'</code> spans the data, <code>'quantile'</code> spans <code>low</code> to <code>high</code> (5th to 95th percentile by default), <code>'stddev'</code> spans <code>deviations</code> either side of the mean. The quantile form is the better default on real data — one mistyped order of magnitude otherwise compresses every real value into the first swatch.</p>
|
|
1591
|
+
<div class="note"><p>Thresholds are <strong>pinned</strong> when the rules compile and do not move on their own. That is deliberate: a boundary that re-derived itself as rows were filtered would repaint cells whose values had not changed, and nobody comparing two screenshots could tell which of the two things had moved. <code>grid.formatting.restat()</code> is how you move it, and a "recalculate" control is the natural place to put it.</p></div>
|
|
1592
|
+
|
|
1593
|
+
<h2 id="statistics">grid.statistics</h2>
|
|
1594
|
+
<p>What the grid knows about its own numbers, and about how they have changed since the page loaded. Every figure is computed over the <em>filtered</em> rows, through the same column handles the totals row uses — so a median here and a median in the footer are the same number, by the same definition (R type 7).</p>
|
|
1595
|
+
<pre><code>grid.statistics.profile('margin');
|
|
1596
|
+
// { column, rows, present, missing, distinct, min, max, mean, median,
|
|
1597
|
+
// q1, q3, iqr, stddev, outliers, histogram: [{ from, to, count }, …] }
|
|
1598
|
+
|
|
1599
|
+
grid.statistics.reduce('margin', 'p95'); // any registered kernel
|
|
1600
|
+
grid.statistics.correlation('spend', 'revenue'); // Pearson's r, clamped to [-1, 1]
|
|
1601
|
+
grid.statistics.weightedAverage('price', 'qty');
|
|
1602
|
+
|
|
1603
|
+
grid.statistics.shadow('price', 'delta', 'R42'); // one row's shadow value
|
|
1604
|
+
grid.statistics.rebase('price'); // "mark all" — today's values become the baseline
|
|
1605
|
+
grid.statistics.tracking(); // { columns, rows, forgotten }</code></pre>
|
|
1606
|
+
<p>The <code>statistics</code> tool panel is the end-user half of <code>profile()</code>: a column picker, the twelve figures and a histogram of the column's shape, all following the filters. Add it with <code>toolPanel: { panels: ['columns', 'statistics'] }</code>.</p>
|
|
1607
|
+
|
|
1608
|
+
<h3>Shadow columns</h3>
|
|
1609
|
+
<p>A shadow column is declared against another column and maintained by the grid. It has no field in the data and it is not a pure computed column either, because its value depends on what happened <em>before</em>. It is a real column throughout: sortable, filterable, totalled, grouped, exported, saved into a view — which is what makes "show me every circuit repriced more than twice this session, most-changed first" one gesture rather than a report.</p>
|
|
1610
|
+
<pre><code>columns: [
|
|
1611
|
+
{ field: 'price', type: 'number' },
|
|
1612
|
+
{ id: 'moved', title: 'Change', shadow: { of: 'price', kind: 'delta' } },
|
|
1613
|
+
{ id: 'churn', title: 'Updates', shadow: { of: 'price', kind: 'updates' } },
|
|
1614
|
+
{ id: 'run', title: 'Streak', shadow: 'streak' }, // shorthand: shadows the column beside it
|
|
1615
|
+
]</code></pre>
|
|
1616
|
+
<div class="table-wrap">
|
|
1617
|
+
<table>
|
|
1618
|
+
<thead><tr><th>Kind</th><th>Value</th></tr></thead>
|
|
1619
|
+
<tbody>
|
|
1620
|
+
<tr><td class="sig">updates</td><td class="desc">How many times the row's value has changed. Arrival is not a change, so a freshly loaded grid reads zero rather than one.</td></tr>
|
|
1621
|
+
<tr><td class="sig">updatedAt</td><td class="desc">When it last changed, as a <code>Date</code>.</td></tr>
|
|
1622
|
+
<tr><td class="sig">sinceUpdate</td><td class="desc">Milliseconds since it last changed.</td></tr>
|
|
1623
|
+
<tr><td class="sig">delta</td><td class="desc">Current value minus the baseline.</td></tr>
|
|
1624
|
+
<tr><td class="sig">deltaPercent</td><td class="desc">The same as a percentage. A change from nothing has no percentage and reads null rather than infinity.</td></tr>
|
|
1625
|
+
<tr><td class="sig">rate</td><td class="desc">Change per second, from the last two readings.</td></tr>
|
|
1626
|
+
<tr><td class="sig">history</td><td class="desc">The recent readings, oldest first. <code>depth</code> sets how many; the default is 20.</td></tr>
|
|
1627
|
+
<tr><td class="sig">firstValue</td><td class="desc">The baseline itself.</td></tr>
|
|
1628
|
+
<tr><td class="sig">streak</td><td class="desc">Consecutive moves in one direction, signed. It resets on a turn, because "seven rises" means something and "seven changes" does not.</td></tr>
|
|
1629
|
+
</tbody>
|
|
1630
|
+
</table>
|
|
1631
|
+
</div>
|
|
1632
|
+
<p>A second family answers where the row sits among the others rather than what it did before. They share the same declaration and the same state — the tracker already holds every row's current value <em>and</em> its baseline, which is exactly what a rank and a rank <em>change</em> need.</p>
|
|
1633
|
+
<div class="table-wrap">
|
|
1634
|
+
<table>
|
|
1635
|
+
<thead><tr><th>Kind</th><th>Value</th></tr></thead>
|
|
1636
|
+
<tbody>
|
|
1637
|
+
<tr><td class="sig">rank</td><td class="desc">Competition rank, largest first: ties share the better rank and the next value skips, so two firsts are followed by a third.</td></tr>
|
|
1638
|
+
<tr><td class="sig">rankAsc</td><td class="desc">The same ranking read from the other end.</td></tr>
|
|
1639
|
+
<tr><td class="sig">rankChange</td><td class="desc">Places climbed since the baseline. Positive means climbed, even though the rank number itself falls — this is the "top movers" column.</td></tr>
|
|
1640
|
+
<tr><td class="sig">percentile</td><td class="desc">The share of rows at or below this one, 0 to 100.</td></tr>
|
|
1641
|
+
<tr><td class="sig">quartile</td><td class="desc">1 to 4, agreeing with <code>percentile</code>: the 60th percentile is in the third quartile.</td></tr>
|
|
1642
|
+
<tr><td class="sig">zScore</td><td class="desc">Deviations from the mean. A column with no spread reads null rather than zero.</td></tr>
|
|
1643
|
+
<tr><td class="sig">shareOfTotal</td><td class="desc">The value over the column's total, as a percentage. A total of zero — a column of offsetting positions — reads null rather than a division by it.</td></tr>
|
|
1644
|
+
</tbody>
|
|
1645
|
+
</table>
|
|
1646
|
+
</div>
|
|
1647
|
+
<div class="note"><p>Positional kinds rank over every <em>tracked</em> row, not over the filtered set: a rank that changed as you filtered would make "the top ten movers" depend on what happened to be on screen, and the column would disagree with itself between two views of the same data. For the filtered answer, use <code>grid.statistics.profile()</code> or a distribution formatting rule.</p></div>
|
|
1648
|
+
<div class="note"><p>Shadow state is keyed by row key, never by index — after any sort an index-keyed history would report one row's past against another row's present, and the wrong number would be <em>sortable</em>. Memory is capped at 200,000 tracked rows per column; past that the oldest are dropped and <code>tracking().forgotten</code> says how many, rather than a smaller number being reported as though it were the truth.</p></div>
|
|
1649
|
+
|
|
1566
1650
|
<h2 id="highlight">grid.highlight</h2>
|
|
1567
1651
|
<p>One mechanism for two jobs: the flash a changed cell makes, and a marker you paint deliberately. A target is a cell (<code>{key, colId}</code>), a row (<code>{key}</code>, or a bare row key) or a column (<code>{colId}</code>). Cell beats row beats column, so a specific highlight is never hidden by a broad one laid over it.</p>
|
|
1568
1652
|
<pre><code>createGrid(el, {
|
|
@@ -2110,10 +2194,13 @@ grid.filters.quickState(); <span class="cmt">// { text: 'acme london', mode: '
|
|
|
2110
2194
|
<tr><td class="sig">Rounding</td><td><code>ROUND</code>, <code>ROUNDUP</code>, <code>ROUNDDOWN</code>, <code>FLOOR</code>, <code>CEILING</code></td></tr>
|
|
2111
2195
|
<tr><td class="sig">Logic</td><td><code>IF</code>, <code>AND</code>, <code>OR</code>, <code>NOT</code>, <code>COALESCE</code></td></tr>
|
|
2112
2196
|
<tr><td class="sig">Text</td><td><code>CONCAT</code>, <code>LEN</code>, <code>UPPER</code>, <code>LOWER</code>, <code>TRIM</code>, <code>LEFT</code>, <code>RIGHT</code></td></tr>
|
|
2197
|
+
<tr><td class="sig">Statistics</td><td><code>MEDIAN</code>, <code>PERCENTILE</code>, <code>QUARTILE1</code>, <code>QUARTILE3</code>, <code>IQR</code>, <code>STDEV</code>, <code>STDEVP</code>, <code>VAR</code>, <code>VARP</code>, <code>COUNTDISTINCT</code></td></tr>
|
|
2113
2198
|
</tbody>
|
|
2114
2199
|
</table>
|
|
2115
2200
|
</div>
|
|
2116
2201
|
|
|
2202
|
+
<div class="note"><p>The statistical functions use R type 7 quantiles — the same definition as the totals row, <code>grid.statistics</code> and the distribution formatting rules, so the four never disagree about what a median is. <code>PERCENTILE</code> reads <code>90</code> and <code>0.9</code> as the same request. Over an empty set they return a number rather than null, because a formula is arithmetic and has to keep composing.</p></div>
|
|
2203
|
+
|
|
2117
2204
|
<p>Operators <code>+ - * / ^</code> with parentheses, comparison for <code>IF</code>, and postfix
|
|
2118
2205
|
<code>%</code>. <code>^</code> is right-associative and unary minus binds tighter than it, so
|
|
2119
2206
|
<code>-2^2</code> is 4 — Excel's answer rather than mathematics'.</p>
|
package/docs/api-detail.html
CHANGED
|
@@ -1652,9 +1652,10 @@ grid.set('theme', <span class="kw">null</span>); <span class="cmt">// back to
|
|
|
1652
1652
|
<div class="why">
|
|
1653
1653
|
<p><strong>Why this is needed at all.</strong> A grid is mounted inside somebody else's
|
|
1654
1654
|
stylesheet. A rule as ordinary as <code>section { padding: 5.5rem 0 }</code> — a marketing
|
|
1655
|
-
page, a CMS theme, a Tailwind preflight —
|
|
1656
|
-
|
|
1657
|
-
elements
|
|
1655
|
+
page, a CMS theme, a Tailwind preflight — matches by tag name, and the grid builds parts of
|
|
1656
|
+
its own interface from those tags: the tool panel's filter rows are
|
|
1657
|
+
<code><section></code> elements. Without the reset, 5.5rem of somebody else's padding
|
|
1658
|
+
lands on every one of them.</p>
|
|
1658
1659
|
<p>The reset uses no <code>!important</code>. It is specificity (0,1,1) and every rule that
|
|
1659
1660
|
dresses a grid element is (0,2,0) or higher, so the grid's own styling always wins and the
|
|
1660
1661
|
reset only fills a gap. Yours wins too, on the same terms: a rule aimed at a Lattice class —
|
|
@@ -4440,10 +4441,11 @@ r.ok ? r.value : r.error; <span class="cmt">// 42</span></code></pre>
|
|
|
4440
4441
|
</p>
|
|
4441
4442
|
<div class="why">
|
|
4442
4443
|
<p><strong>Bare arithmetic is deliberately not a formula.</strong> <code>2-1</code> is a
|
|
4443
|
-
plausible product code and <code>1/2</code> a plausible date.
|
|
4444
|
-
|
|
4445
|
-
<code>2*3</code> as <strong>23</strong
|
|
4446
|
-
refused outright, so the cell keeps what it had rather than
|
|
4444
|
+
plausible product code and <code>1/2</code> a plausible date. A reader that evaluated either on
|
|
4445
|
+
a guess would have to guess wrong sometimes, and the wrong answer is not a visible error but a
|
|
4446
|
+
plausible number — <code>2*3</code> stored as <strong>23</strong> looks like data. Arithmetic
|
|
4447
|
+
without a leading <code>=</code> is refused outright, so the cell keeps what it had rather than
|
|
4448
|
+
taking a number nobody typed.</p>
|
|
4447
4449
|
</div>
|
|
4448
4450
|
|
|
4449
4451
|
<h2 id="custom-menu">Your own menu items and buttons</h2>
|
package/lattice-grid.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/*!
|
|
2
|
-
* Lattice Grid 1.
|
|
2
|
+
* Lattice Grid 1.8.0 — type declarations
|
|
3
3
|
* Copyright (c) 2026 TOCLOCO Inc. All rights reserved.
|
|
4
4
|
* https://latticegrid.dev
|
|
5
5
|
*/
|
|
@@ -1389,15 +1389,31 @@ export interface StateApplyReport {
|
|
|
1389
1389
|
// ---------------------------------------------------------------------------
|
|
1390
1390
|
|
|
1391
1391
|
export interface FormattingCondition {
|
|
1392
|
-
|
|
1392
|
+
/**
|
|
1393
|
+
* A filter operator compared against `value`, or a distribution operator
|
|
1394
|
+
* whose threshold comes from the column itself — `{op: 'topPercent', value: 10}`,
|
|
1395
|
+
* `{op: 'outlier'}`. Distribution thresholds are pinned when the rules
|
|
1396
|
+
* compile; `grid.formatting.restat()` moves them.
|
|
1397
|
+
*/
|
|
1398
|
+
op: Operator | DistributionOp;
|
|
1393
1399
|
value?: unknown;
|
|
1394
1400
|
value2?: unknown;
|
|
1395
1401
|
}
|
|
1396
1402
|
|
|
1397
1403
|
export interface FormattingScale {
|
|
1398
|
-
|
|
1399
|
-
|
|
1404
|
+
/**
|
|
1405
|
+
* Where the bounds come from when `min` and `max` are not given.
|
|
1406
|
+
* `'minmax'` spans the data, `'quantile'` spans `low` to `high`
|
|
1407
|
+
* (5th to 95th percentile by default), `'stddev'` spans `deviations`
|
|
1408
|
+
* either side of the mean.
|
|
1409
|
+
*/
|
|
1410
|
+
from?: 'minmax' | 'quantile' | 'stddev';
|
|
1411
|
+
min?: number;
|
|
1412
|
+
max?: number;
|
|
1400
1413
|
mid?: number;
|
|
1414
|
+
low?: number;
|
|
1415
|
+
high?: number;
|
|
1416
|
+
deviations?: number;
|
|
1401
1417
|
colours?: string[];
|
|
1402
1418
|
}
|
|
1403
1419
|
|
|
@@ -1421,6 +1437,58 @@ export interface FormattingRule {
|
|
|
1421
1437
|
/** A column id, or `'*'` for every column. */
|
|
1422
1438
|
export type FormattingScope = string;
|
|
1423
1439
|
|
|
1440
|
+
export interface StatisticsApi {
|
|
1441
|
+
/** One shadow value for one row, by the column it shadows and the kind. */
|
|
1442
|
+
shadow(colId: string, kind: ShadowKind, rowKey: string): unknown;
|
|
1443
|
+
/** Make the current values the new baseline — "mark all". */
|
|
1444
|
+
rebase(colId?: string): void;
|
|
1445
|
+
/** What the shadow histories are costing. */
|
|
1446
|
+
tracking(): { columns: string[]; rows: number; forgotten: number };
|
|
1447
|
+
/** Reduce a column by a named kernel over the filtered rows. */
|
|
1448
|
+
reduce(colId: string, fn: string): unknown;
|
|
1449
|
+
/** Everything worth knowing about one column, in one pass each. */
|
|
1450
|
+
profile(colId: string): ColumnProfile | null;
|
|
1451
|
+
/** Pearson's correlation between two columns. */
|
|
1452
|
+
correlation(a: string, b: string): number | null;
|
|
1453
|
+
/** A weighted average of one column by another. */
|
|
1454
|
+
weightedAverage(colId: string, weightId: string): number | null;
|
|
1455
|
+
/** The key a row's data resolves to. */
|
|
1456
|
+
keyOf(data: unknown): string | null;
|
|
1457
|
+
/** Which reductions can be maintained against a change, and which rescan. */
|
|
1458
|
+
readonly maintenance: Readonly<Record<string, 'maintained' | 'rescan'>>;
|
|
1459
|
+
}
|
|
1460
|
+
|
|
1461
|
+
export type ShadowKind =
|
|
1462
|
+
| 'updates' | 'updatedAt' | 'sinceUpdate' | 'delta' | 'deltaPercent'
|
|
1463
|
+
| 'rate' | 'history' | 'firstValue' | 'streak'
|
|
1464
|
+
/** Where the row sits among the others, over every tracked row. */
|
|
1465
|
+
| 'rank' | 'rankAsc' | 'rankChange' | 'percentile' | 'quartile'
|
|
1466
|
+
| 'zScore' | 'shareOfTotal';
|
|
1467
|
+
|
|
1468
|
+
export interface ColumnProfile {
|
|
1469
|
+
column: string;
|
|
1470
|
+
rows: number;
|
|
1471
|
+
present: number;
|
|
1472
|
+
missing: number;
|
|
1473
|
+
distinct: number;
|
|
1474
|
+
min: number | null;
|
|
1475
|
+
max: number | null;
|
|
1476
|
+
mean: number | null;
|
|
1477
|
+
median: number | null;
|
|
1478
|
+
q1: number | null;
|
|
1479
|
+
q3: number | null;
|
|
1480
|
+
iqr: number | null;
|
|
1481
|
+
stddev: number | null;
|
|
1482
|
+
outliers: number;
|
|
1483
|
+
histogram: HistogramBin[];
|
|
1484
|
+
}
|
|
1485
|
+
|
|
1486
|
+
export interface HistogramBin {
|
|
1487
|
+
from: number;
|
|
1488
|
+
to: number;
|
|
1489
|
+
count: number;
|
|
1490
|
+
}
|
|
1491
|
+
|
|
1424
1492
|
export interface FormattingApi {
|
|
1425
1493
|
list(scope?: FormattingScope): FormattingRule[];
|
|
1426
1494
|
all(): Record<FormattingScope, FormattingRule[]>;
|
|
@@ -1433,6 +1501,29 @@ export interface FormattingApi {
|
|
|
1433
1501
|
replaceAll(rules: Record<FormattingScope, FormattingRule[]>): void;
|
|
1434
1502
|
clear(scope?: FormattingScope): void;
|
|
1435
1503
|
styleFor(colId: string, value: unknown): CellStyle | null;
|
|
1504
|
+
/** Re-derive the thresholds of distribution rules from the data as it stands. */
|
|
1505
|
+
restat(): void;
|
|
1506
|
+
/** The five numbers a distribution rule resolves against for one column. */
|
|
1507
|
+
distribution(colId: string): ColumnDistribution | null;
|
|
1508
|
+
}
|
|
1509
|
+
|
|
1510
|
+
/** Operators that resolve against the column's own distribution (spec 8.12). */
|
|
1511
|
+
export type DistributionOp =
|
|
1512
|
+
| 'topPercent' | 'bottomPercent' | 'topN' | 'bottomN'
|
|
1513
|
+
| 'aboveMean' | 'belowMean' | 'aboveMedian' | 'belowMedian'
|
|
1514
|
+
| 'zAbove' | 'zBelow' | 'outlier';
|
|
1515
|
+
|
|
1516
|
+
export interface ColumnDistribution {
|
|
1517
|
+
n: number;
|
|
1518
|
+
min: number;
|
|
1519
|
+
max: number;
|
|
1520
|
+
mean: number;
|
|
1521
|
+
stddev: number;
|
|
1522
|
+
median: number;
|
|
1523
|
+
q1: number;
|
|
1524
|
+
q3: number;
|
|
1525
|
+
iqr: number;
|
|
1526
|
+
sorted: number[];
|
|
1436
1527
|
}
|
|
1437
1528
|
|
|
1438
1529
|
// ---------------------------------------------------------------------------
|
|
@@ -2430,6 +2521,7 @@ export interface Grid {
|
|
|
2430
2521
|
readonly comments: CommentsApi;
|
|
2431
2522
|
readonly presence: PresenceApi;
|
|
2432
2523
|
readonly diagnostics: DiagnosticsApi;
|
|
2524
|
+
readonly statistics: StatisticsApi;
|
|
2433
2525
|
readonly formatting: FormattingApi;
|
|
2434
2526
|
readonly maximise?: MaximiseApi;
|
|
2435
2527
|
/**
|