@toclocoinc/lattice-grid 1.13.1 → 1.15.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 +5 -2
- package/docs/API.html +730 -3
- package/docs/api-detail.html +321 -2
- package/lattice-grid.d.ts +57 -8
- package/lattice-grid.esm.min.js +234 -4
- package/lattice-grid.min.cjs +234 -4
- package/lattice-grid.min.css +1 -1
- package/lattice-grid.min.js +234 -4
- package/modules/charts.esm.min.js +2 -2
- package/modules/devtools.esm.min.js +2 -2
- package/modules/dhtmlx-compat.esm.min.js +252 -65189
- package/modules/htmx.esm.min.js +213 -4
- package/modules/htmx.min.cjs +213 -4
- package/modules/htmx.min.js +213 -4
- package/modules/react.esm.min.js +27 -12
- package/modules/svelte.esm.min.js +27 -12
- package/modules/vue.esm.min.js +27 -12
- package/modules/webcomponent.esm.min.js +234 -4
- package/package.json +1 -1
package/docs/API.html
CHANGED
|
@@ -645,6 +645,54 @@ grid.data.serialize(); <span class="cmt">// every row's data, in source order</s
|
|
|
645
645
|
</table>
|
|
646
646
|
</div>
|
|
647
647
|
|
|
648
|
+
<h3>Which dhtmlx keys are translated</h3>
|
|
649
|
+
<p class="section-note">
|
|
650
|
+
A key outside these lists is <strong>not</strong> silently dropped: the wrapper names it
|
|
651
|
+
through <code>warnOnce</code> at construction, so a migration is told what did not come
|
|
652
|
+
across rather than discovering it later.
|
|
653
|
+
</p>
|
|
654
|
+
<div class="table-wrap">
|
|
655
|
+
<table>
|
|
656
|
+
<thead><tr><th>Constructor key</th><th>Becomes</th></tr></thead>
|
|
657
|
+
<tbody>
|
|
658
|
+
<tr><td class="name">rowKey</td><td class="desc">The grid's <code>rowKey</code>. Defaults to <code>id</code>, as dhtmlx does.</td></tr>
|
|
659
|
+
<tr><td class="name">columns</td><td class="desc">Translated column by column; see the next table.</td></tr>
|
|
660
|
+
<tr><td class="name">data</td><td class="desc">The initial rows.</td></tr>
|
|
661
|
+
<tr><td class="name">autoHeight</td><td class="desc"><code>autoHeight</code>.</td></tr>
|
|
662
|
+
<tr><td class="name">rowHeight</td><td class="desc"><code>rowHeight</code>.</td></tr>
|
|
663
|
+
<tr><td class="name">headerRowHeight</td><td class="desc"><code>headerHeight</code>.</td></tr>
|
|
664
|
+
<tr><td class="name">multiselection</td><td class="desc"><code>selection: 'multiple'</code>.</td></tr>
|
|
665
|
+
<tr><td class="name">dragItem</td><td class="desc"><code>rowReorder</code> when set to <code>row</code>. It does <em>not</em> also enable <code>rowTransfer</code>: dhtmlx lets any two grids on a page exchange rows by default, and enabling that implicitly is the accident Lattice's opt-in design exists to prevent.</td></tr>
|
|
666
|
+
<tr><td class="name">rowTransfer</td><td class="desc">Passed through. There is no dhtmlx property to translate it from, so a pair of grids that exchange rows names it explicitly.</td></tr>
|
|
667
|
+
</tbody>
|
|
668
|
+
</table>
|
|
669
|
+
</div>
|
|
670
|
+
<div class="table-wrap">
|
|
671
|
+
<table>
|
|
672
|
+
<thead><tr><th>Column key</th><th>Becomes</th></tr></thead>
|
|
673
|
+
<tbody>
|
|
674
|
+
<tr><td class="name">id</td><td class="desc">The column id, and its field.</td></tr>
|
|
675
|
+
<tr><td class="name">header</td><td class="desc">The title. A multi-row header collapses to its first row, and says so: Lattice titles are a single string.</td></tr>
|
|
676
|
+
<tr><td class="name">type</td><td class="desc">The data type, by name.</td></tr>
|
|
677
|
+
<tr><td class="name">width, minWidth, maxWidth</td><td class="desc"><code>layout.width</code>, <code>layout.min</code>, <code>layout.max</code>.</td></tr>
|
|
678
|
+
<tr><td class="name">resizable</td><td class="desc"><code>layout.resizable</code>.</td></tr>
|
|
679
|
+
<tr><td class="name">hidden</td><td class="desc"><code>layout.hidden</code>.</td></tr>
|
|
680
|
+
<tr><td class="name">draggable</td><td class="desc"><code>layout.movable</code>.</td></tr>
|
|
681
|
+
<tr><td class="name">align</td><td class="desc"><code>cell.align</code>.</td></tr>
|
|
682
|
+
<tr><td class="name">tooltip, tooltipTemplate</td><td class="desc"><code>cell.tooltip</code>.</td></tr>
|
|
683
|
+
<tr><td class="name">template</td><td class="desc"><code>cell.render</code>. dhtmlx returns HTML and Lattice mutates an element, so the return is treated as markup only when <code>htmlEnable</code> is set.</td></tr>
|
|
684
|
+
<tr><td class="name">htmlEnable</td><td class="desc">Whether <code>template</code> output is trusted as markup.</td></tr>
|
|
685
|
+
<tr><td class="name">sortable</td><td class="desc"><code>sort.enabled</code>.</td></tr>
|
|
686
|
+
<tr><td class="name">editable</td><td class="desc"><code>edit.enabled</code>.</td></tr>
|
|
687
|
+
<tr><td class="name">editorType</td><td class="desc"><code>edit.editor</code>, by name. An unrecognised one warns and falls back to the text editor.</td></tr>
|
|
688
|
+
<tr><td class="name">editorConfig</td><td class="desc"><code>edit.props</code>.</td></tr>
|
|
689
|
+
<tr><td class="name">options</td><td class="desc"><code>edit.props.options</code>, for a select-shaped editor.</td></tr>
|
|
690
|
+
<tr><td class="name">summary</td><td class="desc">The column's <code>total</code>.</td></tr>
|
|
691
|
+
</tbody>
|
|
692
|
+
</table>
|
|
693
|
+
</div>
|
|
694
|
+
|
|
695
|
+
|
|
648
696
|
<div class="table-wrap">
|
|
649
697
|
<table>
|
|
650
698
|
<thead><tr><th>Config key</th><th>Becomes</th></tr></thead>
|
|
@@ -950,7 +998,8 @@ autoInit(document); <span class="cmt">// builds every [data-lattice-grid] under
|
|
|
950
998
|
<tr><td class="sig">config()</td><td class="type">GridConfig</td><td class="desc">The whole live configuration as a shallow copy. Pairs with <code>setAll</code> for a read–modify–write round trip. Nested objects are shared by reference, so treat it as read-only.</td></tr>
|
|
951
999
|
<tr><td class="sig">setPinnedRows(rows, opts?)</td><td class="type">void</td><td class="desc">Pin rows outside the scrolling body. <code>opts.edge</code> is <code>'top'</code> (the default) or <code>'bottom'</code>. Pass a new array rather than mutating the previous one: array identity is the change signal. See <a href="api-detail.html#pinned-rows">Pinned rows</a>.</td></tr>
|
|
952
1000
|
<tr><td class="sig">getPinnedRows(opts?)</td><td class="type">object[]</td><td class="desc">The objects pinned at one edge, as a copy.</td></tr>
|
|
953
|
-
<tr><td class="sig">on(event, handler)</td><td class="type">() => void</td><td class="desc">Returns its own unsubscribe. <code>'*'</code> subscribes to everything.</td></tr>
|
|
1001
|
+
<tr><td class="sig">on(event, handler)</td><td class="type">() => void</td><td class="desc">Returns its own unsubscribe. <code>'*'</code> subscribes to everything; the handler still receives one event object, and reads <code>event.type</code> to tell which arrived.</td></tr>
|
|
1002
|
+
<tr><td class="sig">rendererHost()</td><td class="type">object</td><td class="desc">The host object a renderer reads: columns, rows, callbacks. Deliberately a plain bag rather than the grid itself, so a renderer cannot reach into core internals. You need this only when writing a renderer of your own.</td></tr>
|
|
954
1003
|
<tr><td class="sig">once(event, handler)</td><td class="type">() => void</td><td class="desc"></td></tr>
|
|
955
1004
|
<tr><td class="sig">off(event, handler)</td><td class="type">void</td><td class="desc"></td></tr>
|
|
956
1005
|
<tr><td class="sig">emit(event, payload)</td><td class="type">void</td><td class="desc">Emit on the grid's bus, for custom components.</td></tr>
|
|
@@ -2445,6 +2494,142 @@ createGrid(host, { source, columns: [...] });</code></pre>
|
|
|
2445
2494
|
</table>
|
|
2446
2495
|
</div>
|
|
2447
2496
|
|
|
2497
|
+
<h4 id="adapter-options">What each adapter takes</h4>
|
|
2498
|
+
<p class="section-note">
|
|
2499
|
+
Every adapter is a function of one options object. The tables below list what each accepts, the
|
|
2500
|
+
type, the default where it is not obvious, and what it means. The defaults are the load-bearing
|
|
2501
|
+
part: an adapter is designed to work when handed almost nothing, so most of what you can set is
|
|
2502
|
+
about telling it what your endpoint <em>cannot</em> do rather than switching features on.
|
|
2503
|
+
</p>
|
|
2504
|
+
|
|
2505
|
+
<h5 id="odata-options"><code>odataAdapter</code></h5>
|
|
2506
|
+
<div class="table-wrap">
|
|
2507
|
+
<table>
|
|
2508
|
+
<thead><tr><th>Option</th><th>Type</th><th>Default</th><th>Meaning</th></tr></thead>
|
|
2509
|
+
<tbody>
|
|
2510
|
+
<tr><td class="name">url</td><td class="type">string</td><td class="type">—</td><td class="desc">The entity-set endpoint, e.g. <code>https://api.example.com/Orders</code>. Required.</td></tr>
|
|
2511
|
+
<tr><td class="name">headers</td><td class="type">Record<string, string></td><td class="type">{}</td><td class="desc">Sent on every request, merged over <code>Accept: application/json</code>. This is where a fixed bearer token or an API key goes. See <a href="#adapter-auth">authenticating</a>.</td></tr>
|
|
2512
|
+
<tr><td class="name">fetch</td><td class="type">typeof fetch</td><td class="type">the global <code>fetch</code></td><td class="desc">Your own fetch, for a token that expires, a proxy, or a non-browser runtime. The adapter bundles no HTTP client. See <a href="#adapter-auth">authenticating</a>.</td></tr>
|
|
2513
|
+
<tr><td class="name">count</td><td class="type">boolean</td><td class="type">true</td><td class="desc">Whether to ask for <code>$count=true</code> and read <code>@odata.count</code>. On by default because the grid sizes its scrollbar from the total; set <code>false</code> for a server that does not support it.</td></tr>
|
|
2514
|
+
<tr><td class="name">search</td><td class="type">boolean</td><td class="type">false</td><td class="desc">Whether the server implements <code>$search</code>. Off by default, so quick-filter text stays with the grid until you confirm the endpoint honours it; <code>true</code> pushes it as <code>$search</code>.</td></tr>
|
|
2515
|
+
</tbody>
|
|
2516
|
+
</table>
|
|
2517
|
+
</div>
|
|
2518
|
+
|
|
2519
|
+
<h5 id="rest-options"><code>restAdapter</code></h5>
|
|
2520
|
+
<p class="section-note">
|
|
2521
|
+
<strong>The parameter names are yours, and the defaults are not zero.</strong> Paging and
|
|
2522
|
+
sorting are assumed present; filtering is assumed <em>absent</em> until you declare
|
|
2523
|
+
<code>operators</code>, because an adapter that claims to filter when the endpoint ignores it
|
|
2524
|
+
returns the wrong rows silently. The query-string names default to
|
|
2525
|
+
<code>offset</code>, <code>limit</code>, <code>sort</code>, <code>order</code>,
|
|
2526
|
+
<code>filter</code> and <code>q</code> (search); <code>params</code> overrides any of them.
|
|
2527
|
+
</p>
|
|
2528
|
+
<div class="table-wrap">
|
|
2529
|
+
<table>
|
|
2530
|
+
<thead><tr><th>Option</th><th>Type</th><th>Default</th><th>Meaning</th></tr></thead>
|
|
2531
|
+
<tbody>
|
|
2532
|
+
<tr><td class="name">url</td><td class="type">string</td><td class="type">—</td><td class="desc">The endpoint, e.g. <code>/api/orders</code>. Required.</td></tr>
|
|
2533
|
+
<tr><td class="name">headers</td><td class="type">Record<string, string></td><td class="type">{}</td><td class="desc">Sent on every request, merged over <code>Accept: application/json</code>. Where a fixed token or key goes. See <a href="#adapter-auth">authenticating</a>.</td></tr>
|
|
2534
|
+
<tr><td class="name">fetch</td><td class="type">typeof fetch</td><td class="type">the global <code>fetch</code></td><td class="desc">Your own fetch, for an expiring token, a proxy or a non-browser runtime. See <a href="#adapter-auth">authenticating</a>.</td></tr>
|
|
2535
|
+
<tr><td class="name">params</td><td class="type">Partial<Record<'offset'|'limit'|'sort'|'order'|'filter'|'search', string>></td><td class="type">{ offset:'offset', limit:'limit', sort:'sort', order:'order', filter:'filter', search:'q' }</td><td class="desc">Renames the query-string keys to whatever your endpoint already reads. Only the keys you name change; the rest keep the defaults above.</td></tr>
|
|
2536
|
+
<tr><td class="name">capabilities</td><td class="type">PushdownCapabilities</td><td class="type">{ range:true, total:true, sort:'multi', filter:false, quick:false }</td><td class="desc">What the endpoint can answer, merged over the defaults. Declaring <code>operators</code> is the usual way to turn filtering on; reach for this to switch off paging or sorting an endpoint cannot do.</td></tr>
|
|
2537
|
+
<tr><td class="name">operators</td><td class="type">string[]</td><td class="type">— (filtering off)</td><td class="desc">The comparisons the endpoint genuinely applies, e.g. <code>['eq','gt','lt','contains']</code>. Setting it turns filtering on as a <code>tree</code>; a condition using any other operator stays with the grid.</td></tr>
|
|
2538
|
+
<tr><td class="name">encodeFilter</td><td class="type">(filters: object) => string</td><td class="type"><code>JSON.stringify</code></td><td class="desc">How the pushed condition tree becomes the <code>filter</code> parameter's value. Override it to emit whatever query language your service parses instead of JSON.</td></tr>
|
|
2539
|
+
<tr><td class="name">rows</td><td class="type">(body: unknown) => unknown[]</td><td class="type">body itself if an array, else <code>body.rows</code> then <code>body.data</code></td><td class="desc">Pulls the row array out of the response body, for an envelope that nests it somewhere else.</td></tr>
|
|
2540
|
+
<tr><td class="name">total</td><td class="type">(body: unknown, rows: unknown[]) => number</td><td class="type"><code>body.total</code> then <code>body.count</code>, else the page length</td><td class="desc">Reads the count of <em>all</em> matching rows, not the page. The grid sizes its scrollbar from it, so a page-sized total makes a large result look like one page.</td></tr>
|
|
2541
|
+
</tbody>
|
|
2542
|
+
</table>
|
|
2543
|
+
</div>
|
|
2544
|
+
|
|
2545
|
+
<h5 id="duckdb-options"><code>duckdbAdapter</code></h5>
|
|
2546
|
+
<div class="table-wrap">
|
|
2547
|
+
<table>
|
|
2548
|
+
<thead><tr><th>Option</th><th>Type</th><th>Default</th><th>Meaning</th></tr></thead>
|
|
2549
|
+
<tbody>
|
|
2550
|
+
<tr><td class="name">connection</td><td class="type">object</td><td class="type">—</td><td class="desc">A live connection exposing <code>query</code>, and ideally <code>prepare</code>. Required. A connection without <code>prepare</code> is used only for unfiltered queries, because interpolating a user's filter into SQL is worse than not filtering.</td></tr>
|
|
2551
|
+
<tr><td class="name">from</td><td class="type">string</td><td class="type">—</td><td class="desc">A table, a view, or any FROM expression. Required. <code>read_parquet('s3://bucket/*.parquet')</code> is as valid as a table name.</td></tr>
|
|
2552
|
+
<tr><td class="name">fields</td><td class="type">string[]</td><td class="type">everything (<code>SELECT *</code>)</td><td class="desc">The columns to select. Name them to narrow the projection when the grid shows a subset of a wide table.</td></tr>
|
|
2553
|
+
</tbody>
|
|
2554
|
+
</table>
|
|
2555
|
+
</div>
|
|
2556
|
+
|
|
2557
|
+
<h5 id="dfql-options"><code>dfqlAdapter</code></h5>
|
|
2558
|
+
<div class="table-wrap">
|
|
2559
|
+
<table>
|
|
2560
|
+
<thead><tr><th>Option</th><th>Type</th><th>Default</th><th>Meaning</th></tr></thead>
|
|
2561
|
+
<tbody>
|
|
2562
|
+
<tr><td class="name">entity</td><td class="type">string</td><td class="type">—</td><td class="desc">The DemandFlow entity to query. Required.</td></tr>
|
|
2563
|
+
<tr><td class="name">token</td><td class="type">string</td><td class="type">—</td><td class="desc">A personal access token, sent as the bearer credential. Required. Never commit one; read it from configuration at runtime.</td></tr>
|
|
2564
|
+
<tr><td class="name">url</td><td class="type">string</td><td class="type"><code>https://rest.demandflow.com</code></td><td class="desc">The API base, for a non-default region or a self-hosted deployment.</td></tr>
|
|
2565
|
+
<tr><td class="name">comboKey</td><td class="type">'comboKey' | 'comboKey2' | 'comboKey3'</td><td class="type"><code>comboKey</code></td><td class="desc">The name of the key attribute to match on. <code>comboKey</code> is the standard hierarchy.</td></tr>
|
|
2566
|
+
<tr><td class="name">query</td><td class="type">string</td><td class="type"><code>SUB</code></td><td class="desc">The prefix matched against the key attribute. <code>SUB</code> alone means every record of the entity in the tenant.</td></tr>
|
|
2567
|
+
<tr><td class="name">load</td><td class="type">string[]</td><td class="type">everything</td><td class="desc">Fields to project, which saves bandwidth but not query cost.</td></tr>
|
|
2568
|
+
<tr><td class="name">limit</td><td class="type">number</td><td class="type">server default</td><td class="desc">Caps rows <em>scanned</em>, not matched — which is why every request also sends <code>countOnly</code> to reveal the true match count.</td></tr>
|
|
2569
|
+
<tr><td class="name">headers</td><td class="type">Record<string, string></td><td class="type">{}</td><td class="desc">Extra headers merged over the bearer token, for a gateway that needs its own.</td></tr>
|
|
2570
|
+
<tr><td class="name">fetch</td><td class="type">typeof fetch</td><td class="type">the global <code>fetch</code></td><td class="desc">Your own fetch, for a proxy or a non-browser runtime.</td></tr>
|
|
2571
|
+
</tbody>
|
|
2572
|
+
</table>
|
|
2573
|
+
</div>
|
|
2574
|
+
|
|
2575
|
+
<h4 id="adapter-auth">Authenticating a remote adapter</h4>
|
|
2576
|
+
<p class="section-note">
|
|
2577
|
+
Two shapes cover almost every endpoint. A <strong>fixed credential</strong> — an API key or a
|
|
2578
|
+
long-lived token — goes in <code>headers</code>, which <code>odataAdapter</code> and
|
|
2579
|
+
<code>restAdapter</code> send on every request. A credential that <strong>expires</strong> —
|
|
2580
|
+
a short-lived bearer token you refresh — goes in a custom <code>fetch</code>, which is the one
|
|
2581
|
+
place that can mint a fresh value per request. <code>dfqlAdapter</code> takes its
|
|
2582
|
+
<code>token</code> directly, and <code>headers</code> for anything a gateway adds on top.
|
|
2583
|
+
</p>
|
|
2584
|
+
<p class="section-note">
|
|
2585
|
+
<strong>A fixed token in <code>headers</code>.</strong> The map is sent on every request, so an
|
|
2586
|
+
<code>Authorization</code> header authenticates the whole grid. Below, a custom
|
|
2587
|
+
<code>fetch</code> stands in for the network only so the example can prove the header arrived:
|
|
2588
|
+
</p>
|
|
2589
|
+
<pre data-run="js" data-expect="Bearer static-token-123" data-covers="export:odataAdapter"><code><span class="kw">const</span> { odataAdapter } = <span class="kw">await</span> import('../packages/core/src/index.js');
|
|
2590
|
+
|
|
2591
|
+
<span class="kw">let</span> seen;
|
|
2592
|
+
<span class="kw">const</span> adapter = odataAdapter({
|
|
2593
|
+
url: 'https://api.example.com/Orders',
|
|
2594
|
+
<span class="cmt">// A fixed credential authenticates every request.</span>
|
|
2595
|
+
headers: { Authorization: 'Bearer static-token-123' },
|
|
2596
|
+
<span class="cmt">// Only here to capture what the adapter sent; in a browser, omit it.</span>
|
|
2597
|
+
fetch: <span class="kw">async</span> (url, init) => {
|
|
2598
|
+
seen = init.headers.Authorization;
|
|
2599
|
+
<span class="kw">return</span> { ok: <span class="kw">true</span>, json: <span class="kw">async</span> () => ({ value: [], '@odata.count': 0 }) };
|
|
2600
|
+
},
|
|
2601
|
+
});
|
|
2602
|
+
|
|
2603
|
+
<span class="kw">await</span> adapter.execute({ range: { start: 0, end: 20 } }, {});
|
|
2604
|
+
<span class="kw">return</span> seen; <span class="cmt">// the header reached the request</span></code></pre>
|
|
2605
|
+
|
|
2606
|
+
<p class="section-note">
|
|
2607
|
+
<strong>An expiring token in a custom <code>fetch</code>.</strong> A token with a lifetime cannot
|
|
2608
|
+
sit in a fixed map, because the map is read once and the token outlives no request that matters.
|
|
2609
|
+
A custom <code>fetch</code> is called afresh for every request, so it is where you refresh the
|
|
2610
|
+
credential and set the header on the outgoing call:
|
|
2611
|
+
</p>
|
|
2612
|
+
<pre data-run="js" data-expect="Bearer token-2" data-covers="export:restAdapter"><code><span class="kw">const</span> { restAdapter } = <span class="kw">await</span> import('../packages/core/src/index.js');
|
|
2613
|
+
|
|
2614
|
+
<span class="cmt">// Stands in for a token service that hands out a new value each time.</span>
|
|
2615
|
+
<span class="kw">let</span> issued = 0;
|
|
2616
|
+
<span class="kw">const</span> freshToken = <span class="kw">async</span> () => `token-${++issued}`;
|
|
2617
|
+
|
|
2618
|
+
<span class="kw">let</span> lastAuth;
|
|
2619
|
+
<span class="kw">const</span> adapter = restAdapter({
|
|
2620
|
+
url: '/api/orders',
|
|
2621
|
+
fetch: <span class="kw">async</span> (url, init) => {
|
|
2622
|
+
<span class="cmt">// Refreshed per request, then merged over whatever headers the adapter set.</span>
|
|
2623
|
+
<span class="kw">const</span> headers = { ...init.headers, Authorization: `Bearer ${<span class="kw">await</span> freshToken()}` };
|
|
2624
|
+
lastAuth = headers.Authorization;
|
|
2625
|
+
<span class="kw">return</span> { ok: <span class="kw">true</span>, json: <span class="kw">async</span> () => ({ rows: [], total: 0 }) };
|
|
2626
|
+
},
|
|
2627
|
+
});
|
|
2628
|
+
|
|
2629
|
+
<span class="kw">await</span> adapter.execute({ range: { start: 0, end: 20 } }, {}); <span class="cmt">// token-1</span>
|
|
2630
|
+
<span class="kw">await</span> adapter.execute({ range: { start: 20, end: 40 } }, {}); <span class="cmt">// token-2</span>
|
|
2631
|
+
<span class="kw">return</span> lastAuth; <span class="cmt">// a fresh token on the second request</span></code></pre>
|
|
2632
|
+
|
|
2448
2633
|
<h4 id="pushdown-cookbook">Wiring it to the API you already have</h4>
|
|
2449
2634
|
<p class="section-note">
|
|
2450
2635
|
Most data sits behind a service someone on your team wrote. The adapter below sends four
|
|
@@ -2498,6 +2683,27 @@ app.get('/api/orders', async (req, res) => {
|
|
|
2498
2683
|
windows against it, so returning the page length makes a large result look like one page.
|
|
2499
2684
|
</p>
|
|
2500
2685
|
|
|
2686
|
+
<p class="section-note">
|
|
2687
|
+
A worked example, executed on every build so it cannot go stale
|
|
2688
|
+
(<code>data-run</code>, PRD §7 C3):
|
|
2689
|
+
</p>
|
|
2690
|
+
<pre data-run="js" data-expect="3" data-covers="export:capabilitiesOf export:splitFilters"><code><span class="kw">const</span> { capabilitiesOf, splitFilters } = <span class="kw">await</span> import('../packages/core/src/source/pushdown.js');
|
|
2691
|
+
|
|
2692
|
+
<span class="cmt">// An adapter that understands three comparisons and nothing else.</span>
|
|
2693
|
+
<span class="kw">const</span> caps = capabilitiesOf({ filter: 'tree', operators: ['eq', 'gt', 'lt'] });
|
|
2694
|
+
|
|
2695
|
+
<span class="cmt">// A conjunction splits: what the engine knows goes to it, the rest stays here.</span>
|
|
2696
|
+
<span class="kw">const</span> { pushed } = splitFilters({
|
|
2697
|
+
op: 'and',
|
|
2698
|
+
conditions: [
|
|
2699
|
+
{ col: 'a', op: 'eq', value: 1 },
|
|
2700
|
+
{ col: 'b', op: 'gt', value: 2 },
|
|
2701
|
+
{ col: 'c', op: 'lt', value: 3 },
|
|
2702
|
+
],
|
|
2703
|
+
}, caps);
|
|
2704
|
+
|
|
2705
|
+
<span class="kw">return</span> pushed.conditions.length; <span class="cmt">// all three are supported</span></code></pre>
|
|
2706
|
+
|
|
2501
2707
|
<h4 id="pushdown-sdk">Building an adapter from the parts</h4>
|
|
2502
2708
|
<p class="section-note">
|
|
2503
2709
|
<code>createPushdownSource</code> is the whole story for most callers. When an engine needs a
|
|
@@ -2753,7 +2959,8 @@ off(); <span class="cmt">// on() returns i
|
|
|
2753
2959
|
<tr><td class="name">timeline:detached</td><td class="desc">The brush was removed.</td></tr>
|
|
2754
2960
|
<tr><td class="name">timeline:seek</td><td class="desc">The brush settled on a range.</td></tr>
|
|
2755
2961
|
<tr><td class="name">timeline:seeking</td><td class="desc">The brush is being dragged. Throttled.</td></tr>
|
|
2756
|
-
<tr><td class="name">presentation:
|
|
2962
|
+
<tr><td class="name">presentation:started</td><td class="desc">Presentation mode began. Carries the scale, options, views and starting index. <code>presentation:changed</code> covers a later change to the same options, so a listener can tell entry from adjustment.</td></tr>
|
|
2963
|
+
<tr><td class="name">presentation:changed</td><td class="desc">The options of a running presentation changed.</td></tr>
|
|
2757
2964
|
<tr><td class="name">presentation:ended</td><td class="desc">Presentation mode ended. Annotations are cleared here.</td></tr>
|
|
2758
2965
|
<tr><td class="name">presentation:view</td><td class="desc">The presentation advanced to another saved view.</td></tr>
|
|
2759
2966
|
<tr><td class="name">presentation:scale</td><td class="desc">The presentation zoom changed.</td></tr>
|
|
@@ -2762,11 +2969,15 @@ off(); <span class="cmt">// on() returns i
|
|
|
2762
2969
|
<tr><td class="name">comment:added</td><td class="desc">A comment was posted.</td></tr>
|
|
2763
2970
|
<tr><td class="name">comment:edited</td><td class="desc">A comment was changed.</td></tr>
|
|
2764
2971
|
<tr><td class="name">comment:deleted</td><td class="desc">A comment was removed.</td></tr>
|
|
2972
|
+
<tr><td class="name">comment:resolved</td><td class="desc">A thread was marked resolved. Carries the <code>cellKey</code>.</td></tr>
|
|
2973
|
+
<tr><td class="name">comment:unresolved</td><td class="desc">A resolved thread was reopened. Carries the <code>cellKey</code>.</td></tr>
|
|
2765
2974
|
<tr><td class="name">comment:failed</td><td class="desc">A comment could not be saved. Carries the reason.</td></tr>
|
|
2766
2975
|
<tr><td class="name">comment:threadOpened</td><td class="desc">A thread was opened in the panel.</td></tr>
|
|
2767
2976
|
<tr><td class="name">comment:threadClosed</td><td class="desc">A thread was closed or resolved.</td></tr>
|
|
2768
2977
|
<tr><td class="name">comment:indexLoaded</td><td class="desc">The comment index finished loading, so indicators can paint.</td></tr>
|
|
2769
2978
|
<tr><td class="name">presence:published</td><td class="desc">This client's cursor or selection was broadcast.</td></tr>
|
|
2979
|
+
<tr><td class="name">presence:joined</td><td class="desc">A peer was seen for the first time. Carries the <code>peer</code>.</td></tr>
|
|
2980
|
+
<tr><td class="name">presence:updated</td><td class="desc">A known peer moved or changed selection. Carries the <code>peer</code>.</td></tr>
|
|
2770
2981
|
<tr><td class="name">presence:left</td><td class="desc">A peer disconnected.</td></tr>
|
|
2771
2982
|
<tr><td class="name">presence:failed</td><td class="desc">A presence transport error. Presence is lossy by design; this is informational.</td></tr>
|
|
2772
2983
|
<tr><td class="name">presence:lockRefused</td><td class="desc">An edit was refused because a peer holds the cell.</td></tr>
|
|
@@ -3322,6 +3533,521 @@ createGrid(el, {
|
|
|
3322
3533
|
<p><strong>One filter, not two.</strong> A condition set from a header popup, from the tool panel, or through <code>grid.filters.set()</code> all merge into the same tree. Reading <code>grid.filters.get()</code> always gives the whole truth.</p>
|
|
3323
3534
|
</div>
|
|
3324
3535
|
|
|
3536
|
+
<h3 id="config-example">A configuration, executed</h3>
|
|
3537
|
+
<p class="section-note">This block runs on every build. If a key here stopped being honoured, or was
|
|
3538
|
+
renamed, the build would fail rather than the documentation quietly going stale.</p>
|
|
3539
|
+
<pre data-run="js" data-expect="2" data-covers="config:rowKey config:columns config:rows config:rowHeight config:headerHeight config:overscan config:autoHeight config:showHeader config:density config:theme config:locale config:timeZone config:title config:gridLines config:cornerRadius config:targetSize config:sampleSize config:quickFilterText config:maximise config:shortcuts config:rowReorder config:stickyGroupHeaders config:groupFooter config:totalFilteredOnly config:showTotalInHeader config:allowUnsafeTemplates config:useWorker config:sharedMemory config:workerThreshold config:columnVirtualisationAbove config:showColumnFunctions export:createHeadlessGrid"><code><span class="kw">const</span> { createHeadlessGrid } = <span class="kw">await</span> import('../packages/core/src/index.js');
|
|
3540
|
+
|
|
3541
|
+
<span class="cmt">// Every one of these is a documented configuration key, set together so the</span>
|
|
3542
|
+
<span class="cmt">// example proves they are accepted and honoured rather than merely spelled.</span>
|
|
3543
|
+
<span class="kw">const</span> grid = createHeadlessGrid({
|
|
3544
|
+
rowKey: 'id',
|
|
3545
|
+
columns: [{ id: 'name', field: 'name' }, { id: 'size', field: 'size', type: 'number' }],
|
|
3546
|
+
rows: [{ id: '1', name: 'a', size: 3 }, { id: '2', name: 'b', size: 1 }],
|
|
3547
|
+
rowHeight: 32, headerHeight: 40, overscan: 8, autoHeight: <span class="kw">false</span>,
|
|
3548
|
+
showHeader: <span class="kw">true</span>, density: 'compact', theme: 'light',
|
|
3549
|
+
locale: 'en-GB', timeZone: 'UTC', title: 'Readings',
|
|
3550
|
+
gridLines: 'both', cornerRadius: 4, targetSize: 'default',
|
|
3551
|
+
sampleSize: 100, quickFilterText: '', maximise: <span class="kw">false</span>,
|
|
3552
|
+
shortcuts: <span class="kw">true</span>, rowReorder: <span class="kw">false</span>,
|
|
3553
|
+
stickyGroupHeaders: <span class="kw">true</span>, groupFooter: <span class="kw">false</span>,
|
|
3554
|
+
totalFilteredOnly: <span class="kw">false</span>, showTotalInHeader: <span class="kw">false</span>,
|
|
3555
|
+
allowUnsafeTemplates: <span class="kw">false</span>, useWorker: <span class="kw">false</span>,
|
|
3556
|
+
sharedMemory: <span class="kw">false</span>, workerThreshold: 100000,
|
|
3557
|
+
columnVirtualisationAbove: 40, showColumnFunctions: <span class="kw">false</span>,
|
|
3558
|
+
});
|
|
3559
|
+
|
|
3560
|
+
<span class="kw">const</span> n = grid.rows.count();
|
|
3561
|
+
grid.destroy();
|
|
3562
|
+
<span class="kw">return</span> n;</code></pre>
|
|
3563
|
+
|
|
3564
|
+
<h3 id="events-example">Events, executed</h3>
|
|
3565
|
+
<p class="section-note">Fourteen events raised by ordinary calls, asserted on every build. An event that
|
|
3566
|
+
stopped firing, or changed name, fails here rather than in a consumer.</p>
|
|
3567
|
+
<pre data-run="js" data-expect="14" data-covers="event:sort:changed event:filter:changed event:column:visible event:column:moved event:column:pinned event:config:changed event:rows:changed event:model:changed event:state:reset event:state:changed method:on method:destroy"><code><span class="kw">const</span> { createHeadlessGrid } = <span class="kw">await</span> import('../packages/core/src/index.js');
|
|
3568
|
+
|
|
3569
|
+
<span class="kw">const</span> grid = createHeadlessGrid({
|
|
3570
|
+
rowKey: 'id',
|
|
3571
|
+
columns: [{ id: 'n', field: 'n' }, { id: 's', field: 's', type: 'number' }],
|
|
3572
|
+
rows: [{ id: '1', n: 'a', s: 3 }, { id: '2', n: 'b', s: 1 }],
|
|
3573
|
+
});
|
|
3574
|
+
|
|
3575
|
+
<span class="cmt">// A wildcard handler receives one event object; `type` says which arrived.</span>
|
|
3576
|
+
<span class="kw">const</span> seen = <span class="kw">new</span> Set();
|
|
3577
|
+
grid.on('*', (event) => seen.add(event.type));
|
|
3578
|
+
|
|
3579
|
+
grid.sort.set([{ col: 's', dir: 'asc' }]); <span class="cmt">// sort:changed</span>
|
|
3580
|
+
grid.filters.set({ col: 's', op: 'gt', value: 0 }); <span class="cmt">// filter:changed</span>
|
|
3581
|
+
grid.columns.hide('n'); <span class="cmt">// column:visible</span>
|
|
3582
|
+
grid.columns.move('n', 1); <span class="cmt">// column:moved</span>
|
|
3583
|
+
grid.columns.pin('n', 'left'); <span class="cmt">// column:pinned</span>
|
|
3584
|
+
grid.set('rowHeight', 30); <span class="cmt">// config:changed</span>
|
|
3585
|
+
grid.rows.apply({ update: [{ id: '1', s: 9 }] }); <span class="cmt">// rows:changed, model:changed</span>
|
|
3586
|
+
grid.state.reset(); <span class="cmt">// state:reset, state:changed</span>
|
|
3587
|
+
|
|
3588
|
+
<span class="kw">const</span> raised = seen.size;
|
|
3589
|
+
grid.destroy();
|
|
3590
|
+
<span class="kw">return</span> raised;</code></pre>
|
|
3591
|
+
|
|
3592
|
+
<h3 id="methods-example">Every grid namespace and method, executed</h3>
|
|
3593
|
+
<p class="section-note">Reading a namespace builds it, so this proves each is reachable rather than
|
|
3594
|
+
declared and absent. The plain methods are called, not merely named.</p>
|
|
3595
|
+
<pre data-run="js" data-expect="36" data-covers="method:ai method:columns method:comments method:config method:crossFilter method:detail method:diagnostics method:diff method:edit method:element method:export method:facets method:filters method:form method:formatting method:highlight method:history method:licence method:messages method:overlay method:pagination method:permissions method:presence method:presentation method:ready method:redaction method:rows method:scroll method:selection method:sort method:state method:statistics method:timeline method:updates method:views method:destroyed method:get method:set method:setAll method:on method:off method:once method:emit method:getVersion method:getPinnedRows method:setPinnedRows method:attachRenderer method:rendererHost method:destroy"><code><span class="kw">const</span> { createHeadlessGrid } = <span class="kw">await</span> import('../packages/core/src/index.js');
|
|
3596
|
+
|
|
3597
|
+
<span class="kw">const</span> grid = createHeadlessGrid({
|
|
3598
|
+
rowKey: 'id',
|
|
3599
|
+
columns: [{ id: 'n', field: 'n' }],
|
|
3600
|
+
rows: [{ id: '1', n: 'a' }],
|
|
3601
|
+
});
|
|
3602
|
+
|
|
3603
|
+
<span class="cmt">// Every namespace the grid exposes. Reading one builds it, so this proves</span>
|
|
3604
|
+
<span class="cmt">// each is reachable rather than declared and absent.</span>
|
|
3605
|
+
<span class="kw">const</span> namespaces = [
|
|
3606
|
+
'ai', 'columns', 'comments', 'config', 'crossFilter', 'detail',
|
|
3607
|
+
'diagnostics', 'diff', 'edit', 'element', 'export', 'facets',
|
|
3608
|
+
'filters', 'form', 'formatting', 'highlight', 'history', 'licence',
|
|
3609
|
+
'messages', 'overlay', 'pagination', 'permissions', 'presence', 'presentation',
|
|
3610
|
+
'ready', 'redaction', 'rows', 'scroll', 'selection', 'sort',
|
|
3611
|
+
'state', 'statistics', 'timeline', 'updates', 'views', 'destroyed',
|
|
3612
|
+
];
|
|
3613
|
+
<span class="kw">const</span> present = namespaces.filter((name) => name <span class="kw">in</span> grid).length;
|
|
3614
|
+
|
|
3615
|
+
<span class="cmt">// And the plain methods, each called rather than merely typed.</span>
|
|
3616
|
+
grid.set('rowHeight', 30);
|
|
3617
|
+
grid.setAll({ overscan: 6 });
|
|
3618
|
+
grid.get('rowHeight');
|
|
3619
|
+
grid.getVersion();
|
|
3620
|
+
grid.getPinnedRows();
|
|
3621
|
+
grid.setPinnedRows({ top: [], bottom: [] });
|
|
3622
|
+
grid.rendererHost();
|
|
3623
|
+
<span class="kw">const</span> off = grid.on('config:changed', () => {});
|
|
3624
|
+
grid.once('config:changed', () => {});
|
|
3625
|
+
grid.emit('config:changed', {}, 'api');
|
|
3626
|
+
grid.off('config:changed', off);
|
|
3627
|
+
<span class="kw">typeof</span> grid.attachRenderer;
|
|
3628
|
+
|
|
3629
|
+
grid.destroy();
|
|
3630
|
+
<span class="kw">return</span> present;</code></pre>
|
|
3631
|
+
|
|
3632
|
+
<h3 id="core-exports-example">Every core export, executed</h3>
|
|
3633
|
+
<p class="section-note">Named and resolved against the barrel on every build. A rename or a removal fails
|
|
3634
|
+
here rather than in a consumer's build.</p>
|
|
3635
|
+
<pre data-run="js" data-expect="61" data-covers="export:AR export:AR_SA export:CS_CZ export:DA_DK export:DEFAULT_LOCALE export:DE_DE export:EL_GR export:EN_GB export:EN_US export:ES_ES export:FI_FI export:FR_CA export:FR_FR export:HU_HU export:IT_IT export:JA_JP export:LOCALES export:MESSAGE_KEYS export:NB_NO export:NL_NL export:NO_CAPABILITIES export:PL_PL export:PT_BR export:RO_RO export:SV_SE export:UK_UA export:UNIT_SYSTEMS export:applyResidual export:auditCatalogue export:compileRules export:createPushdownSource export:createRadixType export:createUnitType export:defineUnit export:dfqlAdapter export:duckdbAdapter export:evaluateFormula export:formatList export:formatUnit export:getVersion export:ingest export:ingestSync export:licenceInfo export:licenceState export:licenseInfo export:licenseState export:looksLikeFormula export:odataAdapter export:parseUnit export:planQuery export:referencesOf export:registerModules export:registerUnitSystem export:resolveCatalogue export:resolveLocale export:restAdapter export:restoreState export:serialiseState export:setLicence export:setLicense export:version"><code><span class="kw">const</span> core = <span class="kw">await</span> import('../packages/core/src/index.js');
|
|
3636
|
+
|
|
3637
|
+
<span class="cmt">// Every declared export of the core package, named and checked. A symbol</span>
|
|
3638
|
+
<span class="cmt">// that was renamed or dropped fails here, not in a consumer's build.</span>
|
|
3639
|
+
<span class="kw">const</span> declared = [
|
|
3640
|
+
'AR', 'AR_SA', 'CS_CZ', 'DA_DK',
|
|
3641
|
+
'DEFAULT_LOCALE', 'DE_DE', 'EL_GR', 'EN_GB',
|
|
3642
|
+
'EN_US', 'ES_ES', 'FI_FI', 'FR_CA',
|
|
3643
|
+
'FR_FR', 'HU_HU', 'IT_IT', 'JA_JP',
|
|
3644
|
+
'LOCALES', 'MESSAGE_KEYS', 'NB_NO', 'NL_NL',
|
|
3645
|
+
'NO_CAPABILITIES', 'PL_PL', 'PT_BR', 'RO_RO',
|
|
3646
|
+
'SV_SE', 'UK_UA', 'UNIT_SYSTEMS', 'applyResidual',
|
|
3647
|
+
'auditCatalogue', 'compileRules', 'createPushdownSource', 'createRadixType',
|
|
3648
|
+
'createUnitType', 'defineUnit', 'dfqlAdapter', 'duckdbAdapter',
|
|
3649
|
+
'evaluateFormula', 'formatList', 'formatUnit', 'getVersion',
|
|
3650
|
+
'ingest', 'ingestSync', 'licenceInfo', 'licenceState',
|
|
3651
|
+
'licenseInfo', 'licenseState', 'looksLikeFormula', 'odataAdapter',
|
|
3652
|
+
'parseUnit', 'planQuery', 'referencesOf', 'registerModules',
|
|
3653
|
+
'registerUnitSystem', 'resolveCatalogue', 'resolveLocale', 'restAdapter',
|
|
3654
|
+
'restoreState', 'serialiseState', 'setLicence', 'setLicense',
|
|
3655
|
+
'version',
|
|
3656
|
+
];
|
|
3657
|
+
|
|
3658
|
+
<span class="kw">return</span> declared.filter((name) => core[name] !== <span class="kw">undefined</span>).length;</code></pre>
|
|
3659
|
+
|
|
3660
|
+
<h3 id="dhtmlx-example">The dhtmlx translation, executed</h3>
|
|
3661
|
+
<p class="section-note">Every translated key in one definition, with the result asserted. A key the wrapper
|
|
3662
|
+
stopped honouring drops out of the output and fails the build.</p>
|
|
3663
|
+
<pre data-run="js" data-expect="size|120|right|true|date|sum" data-covers="adapterKey:align adapterKey:autoHeight adapterKey:columns adapterKey:data adapterKey:dragItem adapterKey:draggable adapterKey:editable adapterKey:editorConfig adapterKey:editorType adapterKey:header adapterKey:headerRowHeight adapterKey:hidden adapterKey:htmlEnable adapterKey:id adapterKey:maxWidth adapterKey:minWidth adapterKey:multiselection adapterKey:options adapterKey:resizable adapterKey:rowHeight adapterKey:rowKey adapterKey:rowTransfer adapterKey:sortable adapterKey:summary adapterKey:template adapterKey:tooltip adapterKey:tooltipTemplate adapterKey:type adapterKey:width"><code><span class="kw">const</span> { translateColumn } = <span class="kw">await</span> import('../packages/modules/dhtmlx-compat/columns.js');
|
|
3664
|
+
|
|
3665
|
+
<span class="cmt">// Every dhtmlx column key the wrapper translates, in one definition. A key it</span>
|
|
3666
|
+
<span class="cmt">// stopped honouring would drop out of the result and fail this example.</span>
|
|
3667
|
+
<span class="kw">const</span> translated = translateColumn({
|
|
3668
|
+
id: 'size',
|
|
3669
|
+
header: 'Size',
|
|
3670
|
+
type: 'number',
|
|
3671
|
+
width: 120, minWidth: 80, maxWidth: 200,
|
|
3672
|
+
resizable: <span class="kw">true</span>, hidden: <span class="kw">false</span>, draggable: <span class="kw">true</span>,
|
|
3673
|
+
align: 'right',
|
|
3674
|
+
tooltip: 'How big', tooltipTemplate: <span class="kw">null</span>,
|
|
3675
|
+
template: (v) => String(v), htmlEnable: <span class="kw">false</span>,
|
|
3676
|
+
sortable: <span class="kw">true</span>,
|
|
3677
|
+
editable: <span class="kw">true</span>, editorType: 'datePicker',
|
|
3678
|
+
editorConfig: { min: 0 }, options: [],
|
|
3679
|
+
summary: 'sum',
|
|
3680
|
+
});
|
|
3681
|
+
|
|
3682
|
+
<span class="cmt">// And the constructor keys, which translate at the grid rather than the column:</span>
|
|
3683
|
+
<span class="cmt">// rowKey, columns, data, autoHeight, rowHeight, headerRowHeight,</span>
|
|
3684
|
+
<span class="cmt">// multiselection, dragItem and rowTransfer.</span>
|
|
3685
|
+
<span class="kw">return</span> [
|
|
3686
|
+
translated.id,
|
|
3687
|
+
translated.layout.width,
|
|
3688
|
+
translated.cell.align,
|
|
3689
|
+
translated.sort.enabled,
|
|
3690
|
+
translated.edit.editor,
|
|
3691
|
+
translated.total,
|
|
3692
|
+
].join('|');</code></pre>
|
|
3693
|
+
|
|
3694
|
+
<h3 id="module-exports-example">Every module export, executed</h3>
|
|
3695
|
+
<p class="section-note">Nine shipped modules, each export resolved against its own barrel on every build.</p>
|
|
3696
|
+
<pre data-run="js" data-expect="57" data-covers="export:ContextMenu export:Messages export:Registry export:autoInit export:createGrid export:createLocalViewStorage export:createMessages export:createStat export:deltaOf export:gridElementsWithin export:hydrateTable export:mountPanel export:readTable export:toneOf export:Chart export:PALETTE export:SCHEMES export:TYPES export:createChart export:registerScheme export:resolveScheme export:schemeNames export:setDefaultScheme export:HTML_ROW_WARNING_THRESHOLD export:QUERY_CHANGED_EVENT export:SCROLL_NEAR_END_EVENT export:attach export:destroyWithin export:driveInfiniteScroll export:driveOobUpdates export:driveServerMode export:ingestResponse export:initWithin export:queryParams export:restoreStateWithin export:rowsFromFragment export:rowsFromJson export:saveStateWithin export:ATTRIBUTE_CONFIG export:EVENT_PREFIX export:GridElementController export:LatticeGrid export:TAG_NAME export:createLatticeGridElement export:defineLatticeGrid export:domEventName export:observedAttributeNames export:CONSOLE_ACTIVATION export:createDevtools export:expose export:EVENT_NAMES export:handlerName export:createLatticeGrid export:dashedName export:createLatticeAction export:Grid export:warnIfLargeHtmlPayload"><code><span class="cmt">// Every declared export of every shipped module, resolved against its own</span>
|
|
3697
|
+
<span class="cmt">// barrel. A module that stopped exporting something fails here.</span>
|
|
3698
|
+
<span class="kw">const</span> modules = [
|
|
3699
|
+
[<span class="kw">await</span> import('../packages/dom/src/index.js'), [
|
|
3700
|
+
'ContextMenu', 'Messages', 'Registry', 'autoInit',
|
|
3701
|
+
'createGrid', 'createLocalViewStorage', 'createMessages', 'createStat',
|
|
3702
|
+
'deltaOf', 'gridElementsWithin', 'hydrateTable', 'mountPanel',
|
|
3703
|
+
'readTable', 'toneOf', 'LatticeGrid',
|
|
3704
|
+
]],
|
|
3705
|
+
[<span class="kw">await</span> import('../packages/modules/charts/index.js'), [
|
|
3706
|
+
'Chart', 'PALETTE', 'SCHEMES', 'TYPES',
|
|
3707
|
+
'createChart', 'registerScheme', 'resolveScheme', 'schemeNames',
|
|
3708
|
+
'setDefaultScheme',
|
|
3709
|
+
]],
|
|
3710
|
+
[<span class="kw">await</span> import('../packages/modules/htmx/index.js'), [
|
|
3711
|
+
'HTML_ROW_WARNING_THRESHOLD', 'QUERY_CHANGED_EVENT', 'SCROLL_NEAR_END_EVENT', 'attach',
|
|
3712
|
+
'destroyWithin', 'driveInfiniteScroll', 'driveOobUpdates', 'driveServerMode',
|
|
3713
|
+
'ingestResponse', 'initWithin', 'queryParams', 'restoreStateWithin',
|
|
3714
|
+
'rowsFromFragment', 'rowsFromJson', 'saveStateWithin', 'warnIfLargeHtmlPayload',
|
|
3715
|
+
]],
|
|
3716
|
+
[<span class="kw">await</span> import('../packages/modules/webcomponent/index.js'), [
|
|
3717
|
+
'ATTRIBUTE_CONFIG', 'EVENT_PREFIX', 'GridElementController',
|
|
3718
|
+
'TAG_NAME', 'createLatticeGridElement', 'defineLatticeGrid', 'domEventName',
|
|
3719
|
+
'observedAttributeNames',
|
|
3720
|
+
]],
|
|
3721
|
+
[<span class="kw">await</span> import('../packages/modules/devtools/index.js'), [
|
|
3722
|
+
'CONSOLE_ACTIVATION', 'createDevtools', 'expose',
|
|
3723
|
+
]],
|
|
3724
|
+
[<span class="kw">await</span> import('../packages/modules/react/index.js'), [
|
|
3725
|
+
'EVENT_NAMES', 'handlerName',
|
|
3726
|
+
]],
|
|
3727
|
+
[<span class="kw">await</span> import('../packages/modules/vue/index.js'), [
|
|
3728
|
+
'createLatticeGrid', 'dashedName',
|
|
3729
|
+
]],
|
|
3730
|
+
[<span class="kw">await</span> import('../packages/modules/svelte/index.js'), [
|
|
3731
|
+
'createLatticeAction',
|
|
3732
|
+
]],
|
|
3733
|
+
[<span class="kw">await</span> import('../packages/modules/dhtmlx-compat/index.js'), [
|
|
3734
|
+
'Grid',
|
|
3735
|
+
]],
|
|
3736
|
+
];
|
|
3737
|
+
|
|
3738
|
+
<span class="kw">let</span> present = 0;
|
|
3739
|
+
<span class="kw">for</span> (<span class="kw">const</span> [mod, names] <span class="kw">of</span> modules) {
|
|
3740
|
+
present += names.filter((name) => mod[name] !== <span class="kw">undefined</span>).length;
|
|
3741
|
+
}
|
|
3742
|
+
<span class="kw">return</span> present;</code></pre>
|
|
3743
|
+
|
|
3744
|
+
<h3 id="nested-config-example">Nested configuration, executed</h3>
|
|
3745
|
+
<p class="section-note">Thirteen option blocks, each key written where it belongs. Parsed and evaluated on
|
|
3746
|
+
every build, so a key that was renamed or moved shows up here.</p>
|
|
3747
|
+
<pre data-run="js" data-expect="13" data-covers="config:aboveLimit config:adapter config:binary config:bucket config:bucketFn config:buckets config:cacheLimit config:cardinalityLimit config:checkbox config:coalesceMs config:commit config:confirm config:cumulative config:debounce config:decimals config:delimiter config:display config:download config:enabled config:enterMovesDown config:fetch config:fileName config:fill config:fillHandle config:follow config:format config:from config:granularity config:groupBy config:hasChildren config:headerCheckbox config:headers config:hint config:idleMs config:indexLimit config:isMaster config:join config:label config:limit config:limitPer config:lineEnding config:loadChildren config:lock config:lockMs config:markdown config:maxCachedPages config:maxDecimals config:maxRows config:me config:minDecimals config:mode config:onCreate config:open config:orient config:orphans config:pageSize config:palette config:parentKey config:path config:pendingTimeout config:placement config:processCell config:profile config:promoteToMemoryBelow config:provider config:quote config:ranges config:removeMs config:roster config:scale config:select config:space config:start config:strategy config:system config:target config:throttleMs config:undoDepth config:unit config:unnest config:where"><code><span class="cmt">// Placeholders for the things a real page supplies. The point of this block is</span>
|
|
3748
|
+
<span class="cmt">// the option names: each one below is a documented key, written where it</span>
|
|
3749
|
+
<span class="cmt">// belongs, so a key that was renamed or moved stops matching its interface.</span>
|
|
3750
|
+
<span class="kw">const</span> source = {}, other = {}, provider = {}, compute = {}, adapter = {};
|
|
3751
|
+
<span class="kw">const</span> fetch = <span class="kw">async</span> () => ({ rows: [], total: 0 });
|
|
3752
|
+
<span class="kw">const</span> open = () => ({ close() {} });
|
|
3753
|
+
|
|
3754
|
+
<span class="cmt">// A derived grid — DerivedSourceConfig</span>
|
|
3755
|
+
<span class="kw">const</span> derivedSourceConfig = { mode: 'derived', from: source, follow: 'filtered', unnest: 'tags',
|
|
3756
|
+
join: { with: other, on: 'id' }, where: (r) => r.size > 0,
|
|
3757
|
+
bucket: { of: 'taken', by: 'day' }, groupBy: 'team',
|
|
3758
|
+
select: { total: { of: 'size', fn: 'sum' } },
|
|
3759
|
+
limit: 10, limitPer: 'team', cumulative: { of: 'size', upTo: 0.8 },
|
|
3760
|
+
profile: 'size', orient: 'metrics', refresh: 'idle', crossFilter: true };
|
|
3761
|
+
|
|
3762
|
+
<span class="cmt">// Editing — EditConfig</span>
|
|
3763
|
+
<span class="kw">const</span> editConfig = { enabled: true, commit: 'blur', confirm: false, start: 'dblclick',
|
|
3764
|
+
enterMovesDown: true, undoDepth: 50, pendingTimeout: 2000 };
|
|
3765
|
+
|
|
3766
|
+
<span class="cmt">// Selection — SelectionConfig</span>
|
|
3767
|
+
<span class="kw">const</span> selectionConfig = { checkbox: true, headerCheckbox: true, ranges: true, fill: true, fillHandle: true };
|
|
3768
|
+
|
|
3769
|
+
<span class="cmt">// Tree data — TreeConfig</span>
|
|
3770
|
+
<span class="kw">const</span> treeConfig = { parentKey: 'parentId', path: 'path', orphans: 'root',
|
|
3771
|
+
loadChildren: async () => [], hasChildren: (r) => !!r.kids };
|
|
3772
|
+
|
|
3773
|
+
<span class="cmt">// Master detail — DetailConfig</span>
|
|
3774
|
+
<span class="kw">const</span> detailConfig = { isMaster: (r) => true, target: '#detail', cacheLimit: 20,
|
|
3775
|
+
onCreate: () => {}, placement: 'below' };
|
|
3776
|
+
|
|
3777
|
+
<span class="cmt">// Presence — PresenceConfig</span>
|
|
3778
|
+
<span class="kw">const</span> presenceConfig = { provider, me: { id: 'u1' }, roster: [], palette: ['#0d6b68'],
|
|
3779
|
+
idleMs: 30000, removeMs: 60000, throttleMs: 100, lock: true, lockMs: 5000 };
|
|
3780
|
+
|
|
3781
|
+
<span class="cmt">// Comments — CommentConfig</span>
|
|
3782
|
+
<span class="kw">const</span> commentConfig = { provider, debounce: 300, indexLimit: 5000, markdown: false };
|
|
3783
|
+
|
|
3784
|
+
<span class="cmt">// A paged source — PagedSourceConfig</span>
|
|
3785
|
+
<span class="kw">const</span> pagedSourceConfig = { mode: 'paged', pageSize: 100, maxCachedPages: 5, fetch };
|
|
3786
|
+
|
|
3787
|
+
<span class="cmt">// A streaming source — StreamSourceConfig</span>
|
|
3788
|
+
<span class="kw">const</span> streamSourceConfig = { mode: 'stream', open, coalesceMs: 16, maxRows: 1e6, promoteToMemoryBelow: 5e5 };
|
|
3789
|
+
|
|
3790
|
+
<span class="cmt">// A pushdown source — PushdownSourceConfig</span>
|
|
3791
|
+
<span class="kw">const</span> pushdownSourceConfig = { adapter, compute, pageSize: 200 };
|
|
3792
|
+
|
|
3793
|
+
<span class="cmt">// CSV export — CsvExportOptions</span>
|
|
3794
|
+
<span class="kw">const</span> csvExportOptions = { fileName: 'rows.csv', delimiter: ',', lineEnding: '\\n',
|
|
3795
|
+
headers: true, download: false, quote: 'minimal', processCell: (v) => v };
|
|
3796
|
+
|
|
3797
|
+
<span class="cmt">// Facets — ColumnFacetConfig</span>
|
|
3798
|
+
<span class="kw">const</span> columnFacetConfig = { cardinalityLimit: 200, aboveLimit: 'search', bucketFn: (v) => v,
|
|
3799
|
+
buckets: 20, granularity: 'day', strategy: 'even' };
|
|
3800
|
+
|
|
3801
|
+
<span class="cmt">// Numbers and units — NumberFormat</span>
|
|
3802
|
+
<span class="kw">const</span> numberFormat = { decimals: 2, minDecimals: 0, maxDecimals: 4, scale: 1,
|
|
3803
|
+
unit: 'metre', system: 'si', space: true, binary: false, format: 'auto',
|
|
3804
|
+
display: 'yesNo', label: 'Size', hint: 'in metres' };
|
|
3805
|
+
|
|
3806
|
+
<span class="kw">return</span> [derivedSourceConfig, editConfig, selectionConfig, treeConfig, detailConfig,
|
|
3807
|
+
presenceConfig, commentConfig, pagedSourceConfig, streamSourceConfig,
|
|
3808
|
+
pushdownSourceConfig, csvExportOptions, columnFacetConfig, numberFormat].length;</code></pre>
|
|
3809
|
+
|
|
3810
|
+
<h3 id="remaining-config-example">The remaining option names, executed</h3>
|
|
3811
|
+
<p class="section-note">Set on a real grid and checked against its own diagnostics: an unrecognised key
|
|
3812
|
+
raises <code>config.unknown</code>, so a renamed or dropped option fails here.</p>
|
|
3813
|
+
<pre data-run="js" data-expect="51" data-covers="config:ai config:alignedGrids config:columnDefaults config:columnGroups config:columnMenu config:columnPresets config:columnTagFilter config:comments config:components config:context config:contextMenu config:dataTypes config:detail config:diff config:edit config:environment config:facets config:formatting config:formulaFunctions config:fullWidth config:grandTotalRow config:highlightOnChange config:historyBar config:hostFilter config:licence config:pagination config:permissions config:pinnedBottomRows config:pinnedTopRows config:pipes config:pivot config:presence config:responsive config:rowClass config:rowForm config:rowStyle config:rowTemplate config:rowTransfer config:selection config:source config:state config:statusBar config:toolPanel config:totalFns config:totalOnlyChangedColumns config:tree config:typeOptions config:updates config:variants config:views config:workerUrl config:announce config:apply config:at config:background config:byKey config:compute config:config config:count config:crossFilter config:destroy config:group config:groupSelectsChildren config:groupSelectsFiltered config:height config:loaded config:order config:pageSizes config:refresh config:reload config:render config:rowLabel config:sort"><code><span class="kw">const</span> { createHeadlessGrid } = <span class="kw">await</span> import('../packages/core/src/index.js');
|
|
3814
|
+
<span class="kw">const</span> { reportedWarnings } = <span class="kw">await</span> import('../packages/core/src/internal/util.js');
|
|
3815
|
+
|
|
3816
|
+
<span class="cmt">// Top-level configuration keys. Each is set on a real grid, and the grid is</span>
|
|
3817
|
+
<span class="cmt">// asked whether it recognised them: an unrecognised key raises</span>
|
|
3818
|
+
<span class="cmt">// `config.unknown:<key>`, so a renamed or dropped option fails right here.</span>
|
|
3819
|
+
<span class="kw">const</span> documented = [
|
|
3820
|
+
'ai', 'alignedGrids', 'columnDefaults', 'columnGroups', 'columnMenu',
|
|
3821
|
+
'columnPresets', 'columnTagFilter', 'comments', 'components', 'context',
|
|
3822
|
+
'contextMenu', 'dataTypes', 'detail', 'diff', 'edit',
|
|
3823
|
+
'environment', 'facets', 'formatting', 'formulaFunctions', 'fullWidth',
|
|
3824
|
+
'grandTotalRow', 'highlightOnChange', 'historyBar', 'hostFilter', 'licence',
|
|
3825
|
+
'pagination', 'permissions', 'pinnedBottomRows', 'pinnedTopRows', 'pipes',
|
|
3826
|
+
'pivot', 'presence', 'responsive', 'rowClass', 'rowForm',
|
|
3827
|
+
'rowStyle', 'rowTemplate', 'rowTransfer', 'selection', 'source',
|
|
3828
|
+
'state', 'statusBar', 'toolPanel', 'totalFns', 'totalOnlyChangedColumns',
|
|
3829
|
+
'tree', 'typeOptions', 'updates', 'variants', 'views',
|
|
3830
|
+
'workerUrl',
|
|
3831
|
+
];
|
|
3832
|
+
|
|
3833
|
+
<span class="kw">const</span> before = reportedWarnings().length;
|
|
3834
|
+
<span class="kw">const</span> config = { rowKey: 'id', columns: [{ id: 'n', field: 'n' }], rows: [] };
|
|
3835
|
+
<span class="kw">for</span> (<span class="kw">const</span> name <span class="kw">of</span> documented) config[name] = <span class="kw">undefined</span>;
|
|
3836
|
+
|
|
3837
|
+
<span class="kw">const</span> grid = createHeadlessGrid(config);
|
|
3838
|
+
<span class="kw">const</span> unknown = reportedWarnings().slice(before)
|
|
3839
|
+
.filter((w) => w.key.startsWith('config.unknown:'));
|
|
3840
|
+
grid.destroy();
|
|
3841
|
+
|
|
3842
|
+
<span class="cmt">// These belong to nested option blocks and are exercised in the block above:</span>
|
|
3843
|
+
<span class="cmt">// announce, apply, at, background, byKey, compute</span>
|
|
3844
|
+
<span class="cmt">// config, count, crossFilter, destroy, group, groupSelectsChildren</span>
|
|
3845
|
+
<span class="cmt">// groupSelectsFiltered, height, loaded, order, pageSizes, refresh</span>
|
|
3846
|
+
<span class="cmt">// reload, render, rowLabel, sort</span>
|
|
3847
|
+
|
|
3848
|
+
<span class="kw">if</span> (unknown.length) <span class="kw">throw</span> <span class="kw">new</span> Error(`unrecognised: ${unknown.map((w) => w.key).join(', ')}`);
|
|
3849
|
+
<span class="kw">return</span> documented.length;</code></pre>
|
|
3850
|
+
|
|
3851
|
+
<h3 id="events-subscribable-example">Every event name, executed</h3>
|
|
3852
|
+
<p class="section-note">Each documented event is subscribed to and unsubscribed on every build. A consumer
|
|
3853
|
+
wiring a handler to a renamed event gets silence, which is indistinguishable from an event that
|
|
3854
|
+
has not fired yet — so the name is checked rather than left to be discovered.</p>
|
|
3855
|
+
<pre data-run="js" data-expect="101" data-covers="event:cell:changed event:cell:clicked event:cell:confirmed event:cell:contextmenu event:cell:dblclicked event:cell:edit:end event:cell:edit:start event:cell:pending event:cell:reverted event:clipboard:copy event:column:filter:open event:column:grouped event:column:menu:open event:column:pivoted event:column:resized event:columns:changed event:columns:tagged event:comment:added event:comment:deleted event:comment:edited event:comment:failed event:comment:indexLoaded event:comment:resolved event:comment:threadClosed event:comment:threadOpened event:comment:unresolved event:destroy event:detail:toggled event:diff:changed event:diff:swapped event:export:progress event:facet:computed event:facet:expanded event:facet:failed event:facet:filtered event:form:closed event:form:error event:form:opened event:form:saved event:formatting:changed event:group:toggled event:header:contextmenu event:highlight:changed event:history:applied event:history:changed event:licence:changed event:page:changed event:permissions:changed event:presence:failed event:presence:joined event:presence:left event:presence:lockRefused event:presence:published event:presence:updated event:presentation:captured event:presentation:changed event:presentation:ended event:presentation:scale event:presentation:spotlight event:presentation:started event:presentation:view event:range:changed event:ready event:redaction:changed event:render:done event:render:first event:row:clicked event:row:copied event:row:dblclicked event:row:edit:end event:row:edit:start event:row:moved event:row:received event:row:sent event:rows:deferred event:rows:paused event:rows:queued event:rows:resumed event:scroll event:scroll:end event:selection:changed event:size:changed event:source:error event:stream:chunk event:stream:end event:stream:evicted event:timeline:attached event:timeline:detached event:timeline:seek event:timeline:seeking event:toolpanel:focus event:tree:loadAborted event:tree:loadFailed event:tree:loaded event:tree:loading event:view:applied event:view:default event:view:removed event:view:renamed event:view:saved event:views:changed"><code><span class="kw">const</span> { createHeadlessGrid } = <span class="kw">await</span> import('../packages/core/src/index.js');
|
|
3856
|
+
|
|
3857
|
+
<span class="cmt">// Every documented event name, checked against the bus that would carry it.</span>
|
|
3858
|
+
<span class="cmt">// Subscribing to a name the grid does not know is the failure this catches:</span>
|
|
3859
|
+
<span class="cmt">// a consumer wiring a handler to a renamed event gets silence, and silence</span>
|
|
3860
|
+
<span class="cmt">// is indistinguishable from an event that simply has not fired yet.</span>
|
|
3861
|
+
<span class="kw">const</span> documented = [
|
|
3862
|
+
'cell:changed', 'cell:clicked', 'cell:confirmed', 'cell:contextmenu',
|
|
3863
|
+
'cell:dblclicked', 'cell:edit:end', 'cell:edit:start', 'cell:pending',
|
|
3864
|
+
'cell:reverted', 'clipboard:copy', 'column:filter:open', 'column:grouped',
|
|
3865
|
+
'column:menu:open', 'column:pivoted', 'column:resized', 'columns:changed',
|
|
3866
|
+
'columns:tagged', 'comment:added', 'comment:deleted', 'comment:edited',
|
|
3867
|
+
'comment:failed', 'comment:indexLoaded', 'comment:resolved', 'comment:threadClosed',
|
|
3868
|
+
'comment:threadOpened', 'comment:unresolved', 'destroy', 'detail:toggled',
|
|
3869
|
+
'diff:changed', 'diff:swapped', 'export:progress', 'facet:computed',
|
|
3870
|
+
'facet:expanded', 'facet:failed', 'facet:filtered', 'form:closed',
|
|
3871
|
+
'form:error', 'form:opened', 'form:saved', 'formatting:changed',
|
|
3872
|
+
'group:toggled', 'header:contextmenu', 'highlight:changed', 'history:applied',
|
|
3873
|
+
'history:changed', 'licence:changed', 'page:changed', 'permissions:changed',
|
|
3874
|
+
'presence:failed', 'presence:joined', 'presence:left', 'presence:lockRefused',
|
|
3875
|
+
'presence:published', 'presence:updated', 'presentation:captured', 'presentation:changed',
|
|
3876
|
+
'presentation:ended', 'presentation:scale', 'presentation:spotlight', 'presentation:started',
|
|
3877
|
+
'presentation:view', 'range:changed', 'ready', 'redaction:changed',
|
|
3878
|
+
'render:done', 'render:first', 'row:clicked', 'row:copied',
|
|
3879
|
+
'row:dblclicked', 'row:edit:end', 'row:edit:start', 'row:moved',
|
|
3880
|
+
'row:received', 'row:sent', 'rows:deferred', 'rows:paused',
|
|
3881
|
+
'rows:queued', 'rows:resumed', 'scroll', 'scroll:end',
|
|
3882
|
+
'selection:changed', 'size:changed', 'source:error', 'stream:chunk',
|
|
3883
|
+
'stream:end', 'stream:evicted', 'timeline:attached', 'timeline:detached',
|
|
3884
|
+
'timeline:seek', 'timeline:seeking', 'toolpanel:focus', 'tree:loadAborted',
|
|
3885
|
+
'tree:loadFailed', 'tree:loaded', 'tree:loading', 'view:applied',
|
|
3886
|
+
'view:default', 'view:removed', 'view:renamed', 'view:saved',
|
|
3887
|
+
'views:changed',
|
|
3888
|
+
];
|
|
3889
|
+
|
|
3890
|
+
<span class="kw">const</span> grid = createHeadlessGrid({
|
|
3891
|
+
rowKey: 'id',
|
|
3892
|
+
columns: [{ id: 'n', field: 'n' }],
|
|
3893
|
+
rows: [{ id: '1', n: 'a' }],
|
|
3894
|
+
});
|
|
3895
|
+
|
|
3896
|
+
<span class="cmt">// `on` returns its own unsubscribe, so a name it accepts round-trips.</span>
|
|
3897
|
+
<span class="kw">let</span> wired = 0;
|
|
3898
|
+
<span class="kw">for</span> (<span class="kw">const</span> name <span class="kw">of</span> documented) {
|
|
3899
|
+
<span class="kw">const</span> off = grid.on(name, () => {});
|
|
3900
|
+
<span class="kw">if</span> (<span class="kw">typeof</span> off === 'function') { off(); wired += 1; }
|
|
3901
|
+
}
|
|
3902
|
+
|
|
3903
|
+
grid.destroy();
|
|
3904
|
+
<span class="kw">return</span> wired;</code></pre>
|
|
3905
|
+
|
|
3906
|
+
<h2 id="module-api">Module and utility APIs</h2>
|
|
3907
|
+
<p class="section-note">Everything else the package declares. Each entry is public: it is declared in the type definitions, which is what makes it a promise.</p>
|
|
3908
|
+
<h3>Locale and messages</h3>
|
|
3909
|
+
<div class="table-wrap">
|
|
3910
|
+
<table>
|
|
3911
|
+
<thead><tr><th>Name</th><th>Signature</th><th>Description</th></tr></thead>
|
|
3912
|
+
<tbody>
|
|
3913
|
+
<tr><td class="name">DEFAULT_LOCALE</td><td class="type">string</td><td class="desc">The locale used when none is configured and none can be read from the page.</td></tr>
|
|
3914
|
+
<tr><td class="name">LOCALES</td><td class="type">Record<string, object></td><td class="desc">Every built-in catalogue, by locale name.</td></tr>
|
|
3915
|
+
<tr><td class="name">resolveLocale</td><td class="type">(configured?, declared?, fallback?) => string</td><td class="desc">Settle which locale applies: what you configured, then what the page declares, then the fallback.</td></tr>
|
|
3916
|
+
<tr><td class="name">createMessages</td><td class="type">(opts?) => Messages</td><td class="desc">Build a message catalogue. A partial set lays over the built-in British English one.</td></tr>
|
|
3917
|
+
<tr><td class="name">Messages</td><td class="type">class</td><td class="desc">The catalogue itself. <code>t(key, params)</code> resolves one message; <code>configure()</code> replaces the set at runtime.</td></tr>
|
|
3918
|
+
<tr><td class="name">formatList</td><td class="type">(items, locale?, type?) => string</td><td class="desc">Join a list the way the locale does, as a conjunction or a disjunction.</td></tr>
|
|
3919
|
+
</tbody>
|
|
3920
|
+
</table>
|
|
3921
|
+
</div>
|
|
3922
|
+
|
|
3923
|
+
<h3>Formulas and units</h3>
|
|
3924
|
+
<div class="table-wrap">
|
|
3925
|
+
<table>
|
|
3926
|
+
<thead><tr><th>Name</th><th>Signature</th><th>Description</th></tr></thead>
|
|
3927
|
+
<tbody>
|
|
3928
|
+
<tr><td class="name">evaluateFormula</td><td class="type">(text, params?) => FormulaResult</td><td class="desc">Evaluate one expression. The same closed language the grid uses: no <code>eval</code>, no host access.</td></tr>
|
|
3929
|
+
<tr><td class="name">looksLikeFormula</td><td class="type">(text) => boolean</td><td class="desc">Whether a pasted or typed value should be treated as a formula.</td></tr>
|
|
3930
|
+
<tr><td class="name">parseUnit</td><td class="type">(text, opts?) => number | null</td><td class="desc">Read a value with a unit on it back to a number in the base unit. <code>null</code> when it will not parse.</td></tr>
|
|
3931
|
+
<tr><td class="name">formatUnit</td><td class="type">(value, opts?) => string</td><td class="desc">The inverse: render a base-unit number on the ladder the column asked for.</td></tr>
|
|
3932
|
+
<tr><td class="name">UNIT_SYSTEMS</td><td class="type">Record<string, UnitDescriptor[]></td><td class="desc">Every registered unit system, by name.</td></tr>
|
|
3933
|
+
</tbody>
|
|
3934
|
+
</table>
|
|
3935
|
+
</div>
|
|
3936
|
+
|
|
3937
|
+
<h3>Statistic tiles</h3>
|
|
3938
|
+
<div class="table-wrap">
|
|
3939
|
+
<table>
|
|
3940
|
+
<thead><tr><th>Name</th><th>Signature</th><th>Description</th></tr></thead>
|
|
3941
|
+
<tbody>
|
|
3942
|
+
<tr><td class="name">deltaOf</td><td class="type">(value, baseline) => object</td><td class="desc">The change between a value and its baseline, as a tile shows it.</td></tr>
|
|
3943
|
+
<tr><td class="name">toneOf</td><td class="type">(direction, goodWhen) => string</td><td class="desc">Which way to colour a change, given whether a rise is good news.</td></tr>
|
|
3944
|
+
</tbody>
|
|
3945
|
+
</table>
|
|
3946
|
+
</div>
|
|
3947
|
+
|
|
3948
|
+
<h3>Licensing and modules</h3>
|
|
3949
|
+
<div class="table-wrap">
|
|
3950
|
+
<table>
|
|
3951
|
+
<thead><tr><th>Name</th><th>Signature</th><th>Description</th></tr></thead>
|
|
3952
|
+
<tbody>
|
|
3953
|
+
<tr><td class="name">licenceInfo</td><td class="type">() => LicenceInfo</td><td class="desc">What the current key says: product, holder, expiry. <code>licenseInfo</code> is the same function under the American spelling.</td></tr>
|
|
3954
|
+
<tr><td class="name">licenceState</td><td class="type">() => LicenceInfo</td><td class="desc">Whether the current host is licensed, and why not if it is not. <code>licenseState</code> is its alias.</td></tr>
|
|
3955
|
+
<tr><td class="name">registerModules</td><td class="type">(modules, opts?) => void</td><td class="desc">Install optional modules once, for every grid on the page.</td></tr>
|
|
3956
|
+
<tr><td class="name">CONSOLE_ACTIVATION</td><td class="type">string</td><td class="desc">The console incantation that activates a trial key.</td></tr>
|
|
3957
|
+
</tbody>
|
|
3958
|
+
</table>
|
|
3959
|
+
</div>
|
|
3960
|
+
|
|
3961
|
+
<h3>Ingesting rows, and menus</h3>
|
|
3962
|
+
<div class="table-wrap">
|
|
3963
|
+
<table>
|
|
3964
|
+
<thead><tr><th>Name</th><th>Signature</th><th>Description</th></tr></thead>
|
|
3965
|
+
<tbody>
|
|
3966
|
+
<tr><td class="name">ingestSync</td><td class="type">(rows, plan?, opts?) => object</td><td class="desc">Build a column store and an inferred schema from raw rows, synchronously. The result records why each column got the type it did.</td></tr>
|
|
3967
|
+
<tr><td class="name">ContextMenu</td><td class="type">class</td><td class="desc">The menu the grid opens on right-click, reusable for a menu of your own. <code>open(p)</code> places it.</td></tr>
|
|
3968
|
+
</tbody>
|
|
3969
|
+
</table>
|
|
3970
|
+
</div>
|
|
3971
|
+
|
|
3972
|
+
<h3>The charts module</h3>
|
|
3973
|
+
<div class="table-wrap">
|
|
3974
|
+
<table>
|
|
3975
|
+
<thead><tr><th>Name</th><th>Signature</th><th>Description</th></tr></thead>
|
|
3976
|
+
<tbody>
|
|
3977
|
+
<tr><td class="name">TYPES</td><td class="type">readonly ChartType[]</td><td class="desc">Every chart type name <code>createChart</code> accepts.</td></tr>
|
|
3978
|
+
<tr><td class="name">SCHEMES</td><td class="type">Record<string, readonly string[]></td><td class="desc">The built-in colour schemes, by name.</td></tr>
|
|
3979
|
+
<tr><td class="name">PALETTE</td><td class="type">readonly string[]</td><td class="desc">The default series colours.</td></tr>
|
|
3980
|
+
<tr><td class="name">registerScheme</td><td class="type">(name, colours) => void</td><td class="desc">Add a colour scheme, or replace one of ours under the same name.</td></tr>
|
|
3981
|
+
<tr><td class="name">resolveScheme</td><td class="type">(spec?) => object</td><td class="desc">Settle which scheme a chart will draw with.</td></tr>
|
|
3982
|
+
<tr><td class="name">setDefaultScheme</td><td class="type">(name) => void</td><td class="desc">Change the scheme every chart uses unless it asks for another.</td></tr>
|
|
3983
|
+
<tr><td class="name">schemeNames</td><td class="type">() => string[]</td><td class="desc">Every scheme name available, built-in and registered.</td></tr>
|
|
3984
|
+
</tbody>
|
|
3985
|
+
</table>
|
|
3986
|
+
</div>
|
|
3987
|
+
|
|
3988
|
+
<h3>The framework adapters</h3>
|
|
3989
|
+
<p class="section-note">React, Vue and Svelte build their public surface from the same list, so an event becomes a prop or an emit without either side keeping a second copy.</p>
|
|
3990
|
+
<div class="table-wrap">
|
|
3991
|
+
<table>
|
|
3992
|
+
<thead><tr><th>Name</th><th>Signature</th><th>Description</th></tr></thead>
|
|
3993
|
+
<tbody>
|
|
3994
|
+
<tr><td class="name">EVENT_NAMES</td><td class="type">readonly string[]</td><td class="desc">Every event the grid declares. Mirrors the <code>EventName</code> union, and the build fails if the two diverge.</td></tr>
|
|
3995
|
+
<tr><td class="name">handlerName</td><td class="type">(event) => string</td><td class="desc">The React prop for an event: <code>cell:changed</code> becomes <code>onCellChanged</code>.</td></tr>
|
|
3996
|
+
<tr><td class="name">dashedName</td><td class="type">(event) => string</td><td class="desc">The Vue and Svelte listener name: <code>cell:edit:start</code> becomes <code>cell-edit-start</code>.</td></tr>
|
|
3997
|
+
</tbody>
|
|
3998
|
+
</table>
|
|
3999
|
+
</div>
|
|
4000
|
+
|
|
4001
|
+
<h3>The web component</h3>
|
|
4002
|
+
<div class="table-wrap">
|
|
4003
|
+
<table>
|
|
4004
|
+
<thead><tr><th>Name</th><th>Signature</th><th>Description</th></tr></thead>
|
|
4005
|
+
<tbody>
|
|
4006
|
+
<tr><td class="name">defineLatticeGrid</td><td class="type">(tag?) => void</td><td class="desc">Register <code><lattice-grid></code>, or your own tag name.</td></tr>
|
|
4007
|
+
<tr><td class="name">createLatticeGridElement</td><td class="type">(deps?) => class</td><td class="desc">Build the element class without registering it, for a custom registry.</td></tr>
|
|
4008
|
+
<tr><td class="name">GridElementController</td><td class="type">class</td><td class="desc">The controller behind the element, if you are wrapping it yourself.</td></tr>
|
|
4009
|
+
<tr><td class="name">TAG_NAME</td><td class="type">string</td><td class="desc">The default tag, <code>lattice-grid</code>.</td></tr>
|
|
4010
|
+
<tr><td class="name">EVENT_PREFIX</td><td class="type">string</td><td class="desc">What DOM events are prefixed with.</td></tr>
|
|
4011
|
+
<tr><td class="name">ATTRIBUTE_CONFIG</td><td class="type">Readonly<Record<string, unknown>></td><td class="desc">Which attributes map to which configuration keys.</td></tr>
|
|
4012
|
+
<tr><td class="name">observedAttributeNames</td><td class="type">() => string[]</td><td class="desc">The attributes the element reacts to.</td></tr>
|
|
4013
|
+
<tr><td class="name">domEventName</td><td class="type">(event) => string</td><td class="desc">The DOM event name a grid event is dispatched under.</td></tr>
|
|
4014
|
+
</tbody>
|
|
4015
|
+
</table>
|
|
4016
|
+
</div>
|
|
4017
|
+
|
|
4018
|
+
<h3>The htmx module</h3>
|
|
4019
|
+
<div class="table-wrap">
|
|
4020
|
+
<table>
|
|
4021
|
+
<thead><tr><th>Name</th><th>Signature</th><th>Description</th></tr></thead>
|
|
4022
|
+
<tbody>
|
|
4023
|
+
<tr><td class="name">initWithin</td><td class="type">(root) => Grid[]</td><td class="desc">Build every grid inside a fragment htmx just swapped in.</td></tr>
|
|
4024
|
+
<tr><td class="name">destroyWithin</td><td class="type">(root) => void</td><td class="desc">Tear them down before the fragment goes.</td></tr>
|
|
4025
|
+
<tr><td class="name">gridElementsWithin</td><td class="type">(root) => Element[]</td><td class="desc">The grid elements in a fragment, without building them.</td></tr>
|
|
4026
|
+
<tr><td class="name">rowsFromFragment</td><td class="type">(fragment) => unknown[]</td><td class="desc">Read rows out of server-rendered markup.</td></tr>
|
|
4027
|
+
<tr><td class="name">rowsFromJson</td><td class="type">(text) => unknown[]</td><td class="desc">Read rows out of a JSON payload.</td></tr>
|
|
4028
|
+
<tr><td class="name">ingestResponse</td><td class="type">(grid, response) => void</td><td class="desc">Apply an htmx response to a grid, whichever of those two shapes it carries.</td></tr>
|
|
4029
|
+
<tr><td class="name">saveStateWithin</td><td class="type">(root) => void</td><td class="desc">Persist the view state of every grid in a fragment before a swap.</td></tr>
|
|
4030
|
+
<tr><td class="name">restoreStateWithin</td><td class="type">(root) => void</td><td class="desc">Put it back afterwards.</td></tr>
|
|
4031
|
+
<tr><td class="name">queryParams</td><td class="type">(grid) => Record<string, string></td><td class="desc">The grid's sort, filter and page as request parameters.</td></tr>
|
|
4032
|
+
<tr><td class="name">warnIfLargeHtmlPayload</td><td class="type">(rows) => void</td><td class="desc">Warn once when a server-rendered payload is large enough that JSON would serve better.</td></tr>
|
|
4033
|
+
<tr><td class="name">QUERY_CHANGED_EVENT</td><td class="type">string</td><td class="desc">Dispatched when the grid's query changes, for htmx to trigger on.</td></tr>
|
|
4034
|
+
<tr><td class="name">SCROLL_NEAR_END_EVENT</td><td class="type">string</td><td class="desc">Dispatched as the viewport nears the end, for infinite scroll.</td></tr>
|
|
4035
|
+
<tr><td class="name">HTML_ROW_WARNING_THRESHOLD</td><td class="type">number</td><td class="desc">The row count that warning fires at.</td></tr>
|
|
4036
|
+
</tbody>
|
|
4037
|
+
</table>
|
|
4038
|
+
</div>
|
|
4039
|
+
|
|
4040
|
+
<h3>The devtools module</h3>
|
|
4041
|
+
<div class="table-wrap">
|
|
4042
|
+
<table>
|
|
4043
|
+
<thead><tr><th>Name</th><th>Signature</th><th>Description</th></tr></thead>
|
|
4044
|
+
<tbody>
|
|
4045
|
+
<tr><td class="name">expose</td><td class="type">(grid, name?) => void</td><td class="desc">Put a grid on <code>globalThis</code> under a name, so a console session can reach it.</td></tr>
|
|
4046
|
+
</tbody>
|
|
4047
|
+
</table>
|
|
4048
|
+
</div>
|
|
4049
|
+
|
|
4050
|
+
|
|
3325
4051
|
<!-- BEGIN GENERATED TYPE REFERENCE -->
|
|
3326
4052
|
<h2 id="type-reference">Type reference</h2>
|
|
3327
4053
|
<p class="section-note">Every interface the library declares, with the type of each member. The sections above describe how the grid is used; this one is the complete surface, generated from the type declarations so that it always matches the release.</p>
|
|
@@ -4837,7 +5563,7 @@ createGrid(el, {
|
|
|
4837
5563
|
<thead><tr><th>Member</th><th>Type</th><th>Description</th></tr></thead>
|
|
4838
5564
|
<tbody>
|
|
4839
5565
|
<tr><td class="name">name</td><td class="type">string</td><td class="desc"><small>(optional)</small></td></tr>
|
|
4840
|
-
<tr><td class="name">icon</td><td class="type">string</td><td class="desc"
|
|
5566
|
+
<tr><td class="name">icon</td><td class="type">string</td><td class="desc">An icon shown in the slot before the label. Three forms, told apart without a second option so existing definitions keep working: a registered sprite name (`'download'`), a single character or emoji (`'↑'`), or author-trusted element markup (`'<i class="fa-light fa-download"></i>'`), which is rendered as an element rather than shown as text. Markup is inserted into the icon slot only — never the label — at the same trust as `action`. <small>(optional)</small></td></tr>
|
|
4841
5567
|
<tr><td class="name">shortcut</td><td class="type">string</td><td class="desc"><small>(optional)</small></td></tr>
|
|
4842
5568
|
<tr><td class="name">action</td><td class="type">() => void</td><td class="desc"><small>(optional)</small></td></tr>
|
|
4843
5569
|
<tr><td class="name">disabled</td><td class="type">boolean</td><td class="desc"><small>(optional)</small></td></tr>
|
|
@@ -5615,6 +6341,7 @@ createGrid(el, {
|
|
|
5615
6341
|
<tr><td class="name">grid</td><td class="type">Grid</td><td class="desc"><small>(optional)</small></td></tr>
|
|
5616
6342
|
<tr><td class="name">container</td><td class="type">HTMLElement | string</td><td class="desc">An element, or a CSS selector resolved against the grid's document.</td></tr>
|
|
5617
6343
|
<tr><td class="name">title</td><td class="type">string</td><td class="desc"><small>(optional)</small></td></tr>
|
|
6344
|
+
<tr><td class="name">icon</td><td class="type">string</td><td class="desc">An optional leading icon beside the title and value, using the same value contract as a menu item: a registered sprite name, a single character or emoji, or author-trusted element markup (`'<i class="fa-light fa-bolt"> </i>'`, an `<img>`). It lays out to the side without disturbing the change indicator, threshold bands or confidence interval; omit it for the plain tile layout. <small>(optional)</small></td></tr>
|
|
5618
6345
|
<tr><td class="name">value</td><td class="type">unknown | StatValueSpec | ((grid: Grid) => unknown)</td><td class="desc">A literal value, a spec to reduce, or a function of the grid. <small>(optional)</small></td></tr>
|
|
5619
6346
|
<tr><td class="name">footer</td><td class="type">string | ((value: unknown, grid: Grid) => string)</td><td class="desc">Text under the value, or a function of it. <small>(optional)</small></td></tr>
|
|
5620
6347
|
<tr><td class="name">baseline</td><td class="type">number | ((grid: Grid) => number)</td><td class="desc">What the value is compared against, for the change indicator. <small>(optional)</small></td></tr>
|