@toclocoinc/lattice-grid 1.13.0 → 1.14.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/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">() =&gt; 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">() =&gt; 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">() =&gt; 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>
@@ -2498,6 +2547,27 @@ app.get('/api/orders', async (req, res) =&gt; {
2498
2547
  windows against it, so returning the page length makes a large result look like one page.
2499
2548
  </p>
2500
2549
 
2550
+ <p class="section-note">
2551
+ A worked example, executed on every build so it cannot go stale
2552
+ (<code>data-run</code>, PRD&nbsp;§7&nbsp;C3):
2553
+ </p>
2554
+ <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');
2555
+
2556
+ <span class="cmt">// An adapter that understands three comparisons and nothing else.</span>
2557
+ <span class="kw">const</span> caps = capabilitiesOf({ filter: 'tree', operators: ['eq', 'gt', 'lt'] });
2558
+
2559
+ <span class="cmt">// A conjunction splits: what the engine knows goes to it, the rest stays here.</span>
2560
+ <span class="kw">const</span> { pushed } = splitFilters({
2561
+ op: 'and',
2562
+ conditions: [
2563
+ { col: 'a', op: 'eq', value: 1 },
2564
+ { col: 'b', op: 'gt', value: 2 },
2565
+ { col: 'c', op: 'lt', value: 3 },
2566
+ ],
2567
+ }, caps);
2568
+
2569
+ <span class="kw">return</span> pushed.conditions.length; <span class="cmt">// all three are supported</span></code></pre>
2570
+
2501
2571
  <h4 id="pushdown-sdk">Building an adapter from the parts</h4>
2502
2572
  <p class="section-note">
2503
2573
  <code>createPushdownSource</code> is the whole story for most callers. When an engine needs a
@@ -2753,7 +2823,8 @@ off(); <span class="cmt">// on() returns i
2753
2823
  <tr><td class="name">timeline:detached</td><td class="desc">The brush was removed.</td></tr>
2754
2824
  <tr><td class="name">timeline:seek</td><td class="desc">The brush settled on a range.</td></tr>
2755
2825
  <tr><td class="name">timeline:seeking</td><td class="desc">The brush is being dragged. Throttled.</td></tr>
2756
- <tr><td class="name">presentation:changed</td><td class="desc">Presentation mode started, or its options changed.</td></tr>
2826
+ <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>
2827
+ <tr><td class="name">presentation:changed</td><td class="desc">The options of a running presentation changed.</td></tr>
2757
2828
  <tr><td class="name">presentation:ended</td><td class="desc">Presentation mode ended. Annotations are cleared here.</td></tr>
2758
2829
  <tr><td class="name">presentation:view</td><td class="desc">The presentation advanced to another saved view.</td></tr>
2759
2830
  <tr><td class="name">presentation:scale</td><td class="desc">The presentation zoom changed.</td></tr>
@@ -2762,11 +2833,15 @@ off(); <span class="cmt">// on() returns i
2762
2833
  <tr><td class="name">comment:added</td><td class="desc">A comment was posted.</td></tr>
2763
2834
  <tr><td class="name">comment:edited</td><td class="desc">A comment was changed.</td></tr>
2764
2835
  <tr><td class="name">comment:deleted</td><td class="desc">A comment was removed.</td></tr>
2836
+ <tr><td class="name">comment:resolved</td><td class="desc">A thread was marked resolved. Carries the <code>cellKey</code>.</td></tr>
2837
+ <tr><td class="name">comment:unresolved</td><td class="desc">A resolved thread was reopened. Carries the <code>cellKey</code>.</td></tr>
2765
2838
  <tr><td class="name">comment:failed</td><td class="desc">A comment could not be saved. Carries the reason.</td></tr>
2766
2839
  <tr><td class="name">comment:threadOpened</td><td class="desc">A thread was opened in the panel.</td></tr>
2767
2840
  <tr><td class="name">comment:threadClosed</td><td class="desc">A thread was closed or resolved.</td></tr>
2768
2841
  <tr><td class="name">comment:indexLoaded</td><td class="desc">The comment index finished loading, so indicators can paint.</td></tr>
2769
2842
  <tr><td class="name">presence:published</td><td class="desc">This client's cursor or selection was broadcast.</td></tr>
2843
+ <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>
2844
+ <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
2845
  <tr><td class="name">presence:left</td><td class="desc">A peer disconnected.</td></tr>
2771
2846
  <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
2847
  <tr><td class="name">presence:lockRefused</td><td class="desc">An edit was refused because a peer holds the cell.</td></tr>
@@ -3322,6 +3397,521 @@ createGrid(el, {
3322
3397
  <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
3398
  </div>
3324
3399
 
3400
+ <h3 id="config-example">A configuration, executed</h3>
3401
+ <p class="section-note">This block runs on every build. If a key here stopped being honoured, or was
3402
+ renamed, the build would fail rather than the documentation quietly going stale.</p>
3403
+ <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');
3404
+
3405
+ <span class="cmt">// Every one of these is a documented configuration key, set together so the</span>
3406
+ <span class="cmt">// example proves they are accepted and honoured rather than merely spelled.</span>
3407
+ <span class="kw">const</span> grid = createHeadlessGrid({
3408
+ rowKey: 'id',
3409
+ columns: [{ id: 'name', field: 'name' }, { id: 'size', field: 'size', type: 'number' }],
3410
+ rows: [{ id: '1', name: 'a', size: 3 }, { id: '2', name: 'b', size: 1 }],
3411
+ rowHeight: 32, headerHeight: 40, overscan: 8, autoHeight: <span class="kw">false</span>,
3412
+ showHeader: <span class="kw">true</span>, density: 'compact', theme: 'light',
3413
+ locale: 'en-GB', timeZone: 'UTC', title: 'Readings',
3414
+ gridLines: 'both', cornerRadius: 4, targetSize: 'default',
3415
+ sampleSize: 100, quickFilterText: '', maximise: <span class="kw">false</span>,
3416
+ shortcuts: <span class="kw">true</span>, rowReorder: <span class="kw">false</span>,
3417
+ stickyGroupHeaders: <span class="kw">true</span>, groupFooter: <span class="kw">false</span>,
3418
+ totalFilteredOnly: <span class="kw">false</span>, showTotalInHeader: <span class="kw">false</span>,
3419
+ allowUnsafeTemplates: <span class="kw">false</span>, useWorker: <span class="kw">false</span>,
3420
+ sharedMemory: <span class="kw">false</span>, workerThreshold: 100000,
3421
+ columnVirtualisationAbove: 40, showColumnFunctions: <span class="kw">false</span>,
3422
+ });
3423
+
3424
+ <span class="kw">const</span> n = grid.rows.count();
3425
+ grid.destroy();
3426
+ <span class="kw">return</span> n;</code></pre>
3427
+
3428
+ <h3 id="events-example">Events, executed</h3>
3429
+ <p class="section-note">Fourteen events raised by ordinary calls, asserted on every build. An event that
3430
+ stopped firing, or changed name, fails here rather than in a consumer.</p>
3431
+ <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');
3432
+
3433
+ <span class="kw">const</span> grid = createHeadlessGrid({
3434
+ rowKey: 'id',
3435
+ columns: [{ id: 'n', field: 'n' }, { id: 's', field: 's', type: 'number' }],
3436
+ rows: [{ id: '1', n: 'a', s: 3 }, { id: '2', n: 'b', s: 1 }],
3437
+ });
3438
+
3439
+ <span class="cmt">// A wildcard handler receives one event object; `type` says which arrived.</span>
3440
+ <span class="kw">const</span> seen = <span class="kw">new</span> Set();
3441
+ grid.on('*', (event) =&gt; seen.add(event.type));
3442
+
3443
+ grid.sort.set([{ col: 's', dir: 'asc' }]); <span class="cmt">// sort:changed</span>
3444
+ grid.filters.set({ col: 's', op: 'gt', value: 0 }); <span class="cmt">// filter:changed</span>
3445
+ grid.columns.hide('n'); <span class="cmt">// column:visible</span>
3446
+ grid.columns.move('n', 1); <span class="cmt">// column:moved</span>
3447
+ grid.columns.pin('n', 'left'); <span class="cmt">// column:pinned</span>
3448
+ grid.set('rowHeight', 30); <span class="cmt">// config:changed</span>
3449
+ grid.rows.apply({ update: [{ id: '1', s: 9 }] }); <span class="cmt">// rows:changed, model:changed</span>
3450
+ grid.state.reset(); <span class="cmt">// state:reset, state:changed</span>
3451
+
3452
+ <span class="kw">const</span> raised = seen.size;
3453
+ grid.destroy();
3454
+ <span class="kw">return</span> raised;</code></pre>
3455
+
3456
+ <h3 id="methods-example">Every grid namespace and method, executed</h3>
3457
+ <p class="section-note">Reading a namespace builds it, so this proves each is reachable rather than
3458
+ declared and absent. The plain methods are called, not merely named.</p>
3459
+ <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');
3460
+
3461
+ <span class="kw">const</span> grid = createHeadlessGrid({
3462
+ rowKey: 'id',
3463
+ columns: [{ id: 'n', field: 'n' }],
3464
+ rows: [{ id: '1', n: 'a' }],
3465
+ });
3466
+
3467
+ <span class="cmt">// Every namespace the grid exposes. Reading one builds it, so this proves</span>
3468
+ <span class="cmt">// each is reachable rather than declared and absent.</span>
3469
+ <span class="kw">const</span> namespaces = [
3470
+ 'ai', 'columns', 'comments', 'config', 'crossFilter', 'detail',
3471
+ 'diagnostics', 'diff', 'edit', 'element', 'export', 'facets',
3472
+ 'filters', 'form', 'formatting', 'highlight', 'history', 'licence',
3473
+ 'messages', 'overlay', 'pagination', 'permissions', 'presence', 'presentation',
3474
+ 'ready', 'redaction', 'rows', 'scroll', 'selection', 'sort',
3475
+ 'state', 'statistics', 'timeline', 'updates', 'views', 'destroyed',
3476
+ ];
3477
+ <span class="kw">const</span> present = namespaces.filter((name) =&gt; name <span class="kw">in</span> grid).length;
3478
+
3479
+ <span class="cmt">// And the plain methods, each called rather than merely typed.</span>
3480
+ grid.set('rowHeight', 30);
3481
+ grid.setAll({ overscan: 6 });
3482
+ grid.get('rowHeight');
3483
+ grid.getVersion();
3484
+ grid.getPinnedRows();
3485
+ grid.setPinnedRows({ top: [], bottom: [] });
3486
+ grid.rendererHost();
3487
+ <span class="kw">const</span> off = grid.on('config:changed', () =&gt; {});
3488
+ grid.once('config:changed', () =&gt; {});
3489
+ grid.emit('config:changed', {}, 'api');
3490
+ grid.off('config:changed', off);
3491
+ <span class="kw">typeof</span> grid.attachRenderer;
3492
+
3493
+ grid.destroy();
3494
+ <span class="kw">return</span> present;</code></pre>
3495
+
3496
+ <h3 id="core-exports-example">Every core export, executed</h3>
3497
+ <p class="section-note">Named and resolved against the barrel on every build. A rename or a removal fails
3498
+ here rather than in a consumer&#39;s build.</p>
3499
+ <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');
3500
+
3501
+ <span class="cmt">// Every declared export of the core package, named and checked. A symbol</span>
3502
+ <span class="cmt">// that was renamed or dropped fails here, not in a consumer's build.</span>
3503
+ <span class="kw">const</span> declared = [
3504
+ 'AR', 'AR_SA', 'CS_CZ', 'DA_DK',
3505
+ 'DEFAULT_LOCALE', 'DE_DE', 'EL_GR', 'EN_GB',
3506
+ 'EN_US', 'ES_ES', 'FI_FI', 'FR_CA',
3507
+ 'FR_FR', 'HU_HU', 'IT_IT', 'JA_JP',
3508
+ 'LOCALES', 'MESSAGE_KEYS', 'NB_NO', 'NL_NL',
3509
+ 'NO_CAPABILITIES', 'PL_PL', 'PT_BR', 'RO_RO',
3510
+ 'SV_SE', 'UK_UA', 'UNIT_SYSTEMS', 'applyResidual',
3511
+ 'auditCatalogue', 'compileRules', 'createPushdownSource', 'createRadixType',
3512
+ 'createUnitType', 'defineUnit', 'dfqlAdapter', 'duckdbAdapter',
3513
+ 'evaluateFormula', 'formatList', 'formatUnit', 'getVersion',
3514
+ 'ingest', 'ingestSync', 'licenceInfo', 'licenceState',
3515
+ 'licenseInfo', 'licenseState', 'looksLikeFormula', 'odataAdapter',
3516
+ 'parseUnit', 'planQuery', 'referencesOf', 'registerModules',
3517
+ 'registerUnitSystem', 'resolveCatalogue', 'resolveLocale', 'restAdapter',
3518
+ 'restoreState', 'serialiseState', 'setLicence', 'setLicense',
3519
+ 'version',
3520
+ ];
3521
+
3522
+ <span class="kw">return</span> declared.filter((name) =&gt; core[name] !== <span class="kw">undefined</span>).length;</code></pre>
3523
+
3524
+ <h3 id="dhtmlx-example">The dhtmlx translation, executed</h3>
3525
+ <p class="section-note">Every translated key in one definition, with the result asserted. A key the wrapper
3526
+ stopped honouring drops out of the output and fails the build.</p>
3527
+ <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');
3528
+
3529
+ <span class="cmt">// Every dhtmlx column key the wrapper translates, in one definition. A key it</span>
3530
+ <span class="cmt">// stopped honouring would drop out of the result and fail this example.</span>
3531
+ <span class="kw">const</span> translated = translateColumn({
3532
+ id: 'size',
3533
+ header: 'Size',
3534
+ type: 'number',
3535
+ width: 120, minWidth: 80, maxWidth: 200,
3536
+ resizable: <span class="kw">true</span>, hidden: <span class="kw">false</span>, draggable: <span class="kw">true</span>,
3537
+ align: 'right',
3538
+ tooltip: 'How big', tooltipTemplate: <span class="kw">null</span>,
3539
+ template: (v) =&gt; String(v), htmlEnable: <span class="kw">false</span>,
3540
+ sortable: <span class="kw">true</span>,
3541
+ editable: <span class="kw">true</span>, editorType: 'datePicker',
3542
+ editorConfig: { min: 0 }, options: [],
3543
+ summary: 'sum',
3544
+ });
3545
+
3546
+ <span class="cmt">// And the constructor keys, which translate at the grid rather than the column:</span>
3547
+ <span class="cmt">// rowKey, columns, data, autoHeight, rowHeight, headerRowHeight,</span>
3548
+ <span class="cmt">// multiselection, dragItem and rowTransfer.</span>
3549
+ <span class="kw">return</span> [
3550
+ translated.id,
3551
+ translated.layout.width,
3552
+ translated.cell.align,
3553
+ translated.sort.enabled,
3554
+ translated.edit.editor,
3555
+ translated.total,
3556
+ ].join('|');</code></pre>
3557
+
3558
+ <h3 id="module-exports-example">Every module export, executed</h3>
3559
+ <p class="section-note">Nine shipped modules, each export resolved against its own barrel on every build.</p>
3560
+ <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>
3561
+ <span class="cmt">// barrel. A module that stopped exporting something fails here.</span>
3562
+ <span class="kw">const</span> modules = [
3563
+ [<span class="kw">await</span> import('../packages/dom/src/index.js'), [
3564
+ 'ContextMenu', 'Messages', 'Registry', 'autoInit',
3565
+ 'createGrid', 'createLocalViewStorage', 'createMessages', 'createStat',
3566
+ 'deltaOf', 'gridElementsWithin', 'hydrateTable', 'mountPanel',
3567
+ 'readTable', 'toneOf', 'LatticeGrid',
3568
+ ]],
3569
+ [<span class="kw">await</span> import('../packages/modules/charts/index.js'), [
3570
+ 'Chart', 'PALETTE', 'SCHEMES', 'TYPES',
3571
+ 'createChart', 'registerScheme', 'resolveScheme', 'schemeNames',
3572
+ 'setDefaultScheme',
3573
+ ]],
3574
+ [<span class="kw">await</span> import('../packages/modules/htmx/index.js'), [
3575
+ 'HTML_ROW_WARNING_THRESHOLD', 'QUERY_CHANGED_EVENT', 'SCROLL_NEAR_END_EVENT', 'attach',
3576
+ 'destroyWithin', 'driveInfiniteScroll', 'driveOobUpdates', 'driveServerMode',
3577
+ 'ingestResponse', 'initWithin', 'queryParams', 'restoreStateWithin',
3578
+ 'rowsFromFragment', 'rowsFromJson', 'saveStateWithin', 'warnIfLargeHtmlPayload',
3579
+ ]],
3580
+ [<span class="kw">await</span> import('../packages/modules/webcomponent/index.js'), [
3581
+ 'ATTRIBUTE_CONFIG', 'EVENT_PREFIX', 'GridElementController',
3582
+ 'TAG_NAME', 'createLatticeGridElement', 'defineLatticeGrid', 'domEventName',
3583
+ 'observedAttributeNames',
3584
+ ]],
3585
+ [<span class="kw">await</span> import('../packages/modules/devtools/index.js'), [
3586
+ 'CONSOLE_ACTIVATION', 'createDevtools', 'expose',
3587
+ ]],
3588
+ [<span class="kw">await</span> import('../packages/modules/react/index.js'), [
3589
+ 'EVENT_NAMES', 'handlerName',
3590
+ ]],
3591
+ [<span class="kw">await</span> import('../packages/modules/vue/index.js'), [
3592
+ 'createLatticeGrid', 'dashedName',
3593
+ ]],
3594
+ [<span class="kw">await</span> import('../packages/modules/svelte/index.js'), [
3595
+ 'createLatticeAction',
3596
+ ]],
3597
+ [<span class="kw">await</span> import('../packages/modules/dhtmlx-compat/index.js'), [
3598
+ 'Grid',
3599
+ ]],
3600
+ ];
3601
+
3602
+ <span class="kw">let</span> present = 0;
3603
+ <span class="kw">for</span> (<span class="kw">const</span> [mod, names] <span class="kw">of</span> modules) {
3604
+ present += names.filter((name) =&gt; mod[name] !== <span class="kw">undefined</span>).length;
3605
+ }
3606
+ <span class="kw">return</span> present;</code></pre>
3607
+
3608
+ <h3 id="nested-config-example">Nested configuration, executed</h3>
3609
+ <p class="section-note">Thirteen option blocks, each key written where it belongs. Parsed and evaluated on
3610
+ every build, so a key that was renamed or moved shows up here.</p>
3611
+ <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>
3612
+ <span class="cmt">// the option names: each one below is a documented key, written where it</span>
3613
+ <span class="cmt">// belongs, so a key that was renamed or moved stops matching its interface.</span>
3614
+ <span class="kw">const</span> source = {}, other = {}, provider = {}, compute = {}, adapter = {};
3615
+ <span class="kw">const</span> fetch = <span class="kw">async</span> () =&gt; ({ rows: [], total: 0 });
3616
+ <span class="kw">const</span> open = () =&gt; ({ close() {} });
3617
+
3618
+ <span class="cmt">// A derived grid — DerivedSourceConfig</span>
3619
+ <span class="kw">const</span> derivedSourceConfig = { mode: 'derived', from: source, follow: 'filtered', unnest: 'tags',
3620
+ join: { with: other, on: 'id' }, where: (r) =&gt; r.size &gt; 0,
3621
+ bucket: { of: 'taken', by: 'day' }, groupBy: 'team',
3622
+ select: { total: { of: 'size', fn: 'sum' } },
3623
+ limit: 10, limitPer: 'team', cumulative: { of: 'size', upTo: 0.8 },
3624
+ profile: 'size', orient: 'metrics', refresh: 'idle', crossFilter: true };
3625
+
3626
+ <span class="cmt">// Editing — EditConfig</span>
3627
+ <span class="kw">const</span> editConfig = { enabled: true, commit: 'blur', confirm: false, start: 'dblclick',
3628
+ enterMovesDown: true, undoDepth: 50, pendingTimeout: 2000 };
3629
+
3630
+ <span class="cmt">// Selection — SelectionConfig</span>
3631
+ <span class="kw">const</span> selectionConfig = { checkbox: true, headerCheckbox: true, ranges: true, fill: true, fillHandle: true };
3632
+
3633
+ <span class="cmt">// Tree data — TreeConfig</span>
3634
+ <span class="kw">const</span> treeConfig = { parentKey: 'parentId', path: 'path', orphans: 'root',
3635
+ loadChildren: async () =&gt; [], hasChildren: (r) =&gt; !!r.kids };
3636
+
3637
+ <span class="cmt">// Master detail — DetailConfig</span>
3638
+ <span class="kw">const</span> detailConfig = { isMaster: (r) =&gt; true, target: '#detail', cacheLimit: 20,
3639
+ onCreate: () =&gt; {}, placement: 'below' };
3640
+
3641
+ <span class="cmt">// Presence — PresenceConfig</span>
3642
+ <span class="kw">const</span> presenceConfig = { provider, me: { id: 'u1' }, roster: [], palette: ['#0d6b68'],
3643
+ idleMs: 30000, removeMs: 60000, throttleMs: 100, lock: true, lockMs: 5000 };
3644
+
3645
+ <span class="cmt">// Comments — CommentConfig</span>
3646
+ <span class="kw">const</span> commentConfig = { provider, debounce: 300, indexLimit: 5000, markdown: false };
3647
+
3648
+ <span class="cmt">// A paged source — PagedSourceConfig</span>
3649
+ <span class="kw">const</span> pagedSourceConfig = { mode: 'paged', pageSize: 100, maxCachedPages: 5, fetch };
3650
+
3651
+ <span class="cmt">// A streaming source — StreamSourceConfig</span>
3652
+ <span class="kw">const</span> streamSourceConfig = { mode: 'stream', open, coalesceMs: 16, maxRows: 1e6, promoteToMemoryBelow: 5e5 };
3653
+
3654
+ <span class="cmt">// A pushdown source — PushdownSourceConfig</span>
3655
+ <span class="kw">const</span> pushdownSourceConfig = { adapter, compute, pageSize: 200 };
3656
+
3657
+ <span class="cmt">// CSV export — CsvExportOptions</span>
3658
+ <span class="kw">const</span> csvExportOptions = { fileName: 'rows.csv', delimiter: ',', lineEnding: '\\n',
3659
+ headers: true, download: false, quote: 'minimal', processCell: (v) =&gt; v };
3660
+
3661
+ <span class="cmt">// Facets — ColumnFacetConfig</span>
3662
+ <span class="kw">const</span> columnFacetConfig = { cardinalityLimit: 200, aboveLimit: 'search', bucketFn: (v) =&gt; v,
3663
+ buckets: 20, granularity: 'day', strategy: 'even' };
3664
+
3665
+ <span class="cmt">// Numbers and units — NumberFormat</span>
3666
+ <span class="kw">const</span> numberFormat = { decimals: 2, minDecimals: 0, maxDecimals: 4, scale: 1,
3667
+ unit: 'metre', system: 'si', space: true, binary: false, format: 'auto',
3668
+ display: 'yesNo', label: 'Size', hint: 'in metres' };
3669
+
3670
+ <span class="kw">return</span> [derivedSourceConfig, editConfig, selectionConfig, treeConfig, detailConfig,
3671
+ presenceConfig, commentConfig, pagedSourceConfig, streamSourceConfig,
3672
+ pushdownSourceConfig, csvExportOptions, columnFacetConfig, numberFormat].length;</code></pre>
3673
+
3674
+ <h3 id="remaining-config-example">The remaining option names, executed</h3>
3675
+ <p class="section-note">Set on a real grid and checked against its own diagnostics: an unrecognised key
3676
+ raises <code>config.unknown</code>, so a renamed or dropped option fails here.</p>
3677
+ <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');
3678
+ <span class="kw">const</span> { reportedWarnings } = <span class="kw">await</span> import('../packages/core/src/internal/util.js');
3679
+
3680
+ <span class="cmt">// Top-level configuration keys. Each is set on a real grid, and the grid is</span>
3681
+ <span class="cmt">// asked whether it recognised them: an unrecognised key raises</span>
3682
+ <span class="cmt">// `config.unknown:&lt;key&gt;`, so a renamed or dropped option fails right here.</span>
3683
+ <span class="kw">const</span> documented = [
3684
+ 'ai', 'alignedGrids', 'columnDefaults', 'columnGroups', 'columnMenu',
3685
+ 'columnPresets', 'columnTagFilter', 'comments', 'components', 'context',
3686
+ 'contextMenu', 'dataTypes', 'detail', 'diff', 'edit',
3687
+ 'environment', 'facets', 'formatting', 'formulaFunctions', 'fullWidth',
3688
+ 'grandTotalRow', 'highlightOnChange', 'historyBar', 'hostFilter', 'licence',
3689
+ 'pagination', 'permissions', 'pinnedBottomRows', 'pinnedTopRows', 'pipes',
3690
+ 'pivot', 'presence', 'responsive', 'rowClass', 'rowForm',
3691
+ 'rowStyle', 'rowTemplate', 'rowTransfer', 'selection', 'source',
3692
+ 'state', 'statusBar', 'toolPanel', 'totalFns', 'totalOnlyChangedColumns',
3693
+ 'tree', 'typeOptions', 'updates', 'variants', 'views',
3694
+ 'workerUrl',
3695
+ ];
3696
+
3697
+ <span class="kw">const</span> before = reportedWarnings().length;
3698
+ <span class="kw">const</span> config = { rowKey: 'id', columns: [{ id: 'n', field: 'n' }], rows: [] };
3699
+ <span class="kw">for</span> (<span class="kw">const</span> name <span class="kw">of</span> documented) config[name] = <span class="kw">undefined</span>;
3700
+
3701
+ <span class="kw">const</span> grid = createHeadlessGrid(config);
3702
+ <span class="kw">const</span> unknown = reportedWarnings().slice(before)
3703
+ .filter((w) =&gt; w.key.startsWith('config.unknown:'));
3704
+ grid.destroy();
3705
+
3706
+ <span class="cmt">// These belong to nested option blocks and are exercised in the block above:</span>
3707
+ <span class="cmt">// announce, apply, at, background, byKey, compute</span>
3708
+ <span class="cmt">// config, count, crossFilter, destroy, group, groupSelectsChildren</span>
3709
+ <span class="cmt">// groupSelectsFiltered, height, loaded, order, pageSizes, refresh</span>
3710
+ <span class="cmt">// reload, render, rowLabel, sort</span>
3711
+
3712
+ <span class="kw">if</span> (unknown.length) <span class="kw">throw</span> <span class="kw">new</span> Error(`unrecognised: ${unknown.map((w) =&gt; w.key).join(', ')}`);
3713
+ <span class="kw">return</span> documented.length;</code></pre>
3714
+
3715
+ <h3 id="events-subscribable-example">Every event name, executed</h3>
3716
+ <p class="section-note">Each documented event is subscribed to and unsubscribed on every build. A consumer
3717
+ wiring a handler to a renamed event gets silence, which is indistinguishable from an event that
3718
+ has not fired yet — so the name is checked rather than left to be discovered.</p>
3719
+ <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');
3720
+
3721
+ <span class="cmt">// Every documented event name, checked against the bus that would carry it.</span>
3722
+ <span class="cmt">// Subscribing to a name the grid does not know is the failure this catches:</span>
3723
+ <span class="cmt">// a consumer wiring a handler to a renamed event gets silence, and silence</span>
3724
+ <span class="cmt">// is indistinguishable from an event that simply has not fired yet.</span>
3725
+ <span class="kw">const</span> documented = [
3726
+ 'cell:changed', 'cell:clicked', 'cell:confirmed', 'cell:contextmenu',
3727
+ 'cell:dblclicked', 'cell:edit:end', 'cell:edit:start', 'cell:pending',
3728
+ 'cell:reverted', 'clipboard:copy', 'column:filter:open', 'column:grouped',
3729
+ 'column:menu:open', 'column:pivoted', 'column:resized', 'columns:changed',
3730
+ 'columns:tagged', 'comment:added', 'comment:deleted', 'comment:edited',
3731
+ 'comment:failed', 'comment:indexLoaded', 'comment:resolved', 'comment:threadClosed',
3732
+ 'comment:threadOpened', 'comment:unresolved', 'destroy', 'detail:toggled',
3733
+ 'diff:changed', 'diff:swapped', 'export:progress', 'facet:computed',
3734
+ 'facet:expanded', 'facet:failed', 'facet:filtered', 'form:closed',
3735
+ 'form:error', 'form:opened', 'form:saved', 'formatting:changed',
3736
+ 'group:toggled', 'header:contextmenu', 'highlight:changed', 'history:applied',
3737
+ 'history:changed', 'licence:changed', 'page:changed', 'permissions:changed',
3738
+ 'presence:failed', 'presence:joined', 'presence:left', 'presence:lockRefused',
3739
+ 'presence:published', 'presence:updated', 'presentation:captured', 'presentation:changed',
3740
+ 'presentation:ended', 'presentation:scale', 'presentation:spotlight', 'presentation:started',
3741
+ 'presentation:view', 'range:changed', 'ready', 'redaction:changed',
3742
+ 'render:done', 'render:first', 'row:clicked', 'row:copied',
3743
+ 'row:dblclicked', 'row:edit:end', 'row:edit:start', 'row:moved',
3744
+ 'row:received', 'row:sent', 'rows:deferred', 'rows:paused',
3745
+ 'rows:queued', 'rows:resumed', 'scroll', 'scroll:end',
3746
+ 'selection:changed', 'size:changed', 'source:error', 'stream:chunk',
3747
+ 'stream:end', 'stream:evicted', 'timeline:attached', 'timeline:detached',
3748
+ 'timeline:seek', 'timeline:seeking', 'toolpanel:focus', 'tree:loadAborted',
3749
+ 'tree:loadFailed', 'tree:loaded', 'tree:loading', 'view:applied',
3750
+ 'view:default', 'view:removed', 'view:renamed', 'view:saved',
3751
+ 'views:changed',
3752
+ ];
3753
+
3754
+ <span class="kw">const</span> grid = createHeadlessGrid({
3755
+ rowKey: 'id',
3756
+ columns: [{ id: 'n', field: 'n' }],
3757
+ rows: [{ id: '1', n: 'a' }],
3758
+ });
3759
+
3760
+ <span class="cmt">// `on` returns its own unsubscribe, so a name it accepts round-trips.</span>
3761
+ <span class="kw">let</span> wired = 0;
3762
+ <span class="kw">for</span> (<span class="kw">const</span> name <span class="kw">of</span> documented) {
3763
+ <span class="kw">const</span> off = grid.on(name, () =&gt; {});
3764
+ <span class="kw">if</span> (<span class="kw">typeof</span> off === 'function') { off(); wired += 1; }
3765
+ }
3766
+
3767
+ grid.destroy();
3768
+ <span class="kw">return</span> wired;</code></pre>
3769
+
3770
+ <h2 id="module-api">Module and utility APIs</h2>
3771
+ <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>
3772
+ <h3>Locale and messages</h3>
3773
+ <div class="table-wrap">
3774
+ <table>
3775
+ <thead><tr><th>Name</th><th>Signature</th><th>Description</th></tr></thead>
3776
+ <tbody>
3777
+ <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>
3778
+ <tr><td class="name">LOCALES</td><td class="type">Record&lt;string, object&gt;</td><td class="desc">Every built-in catalogue, by locale name.</td></tr>
3779
+ <tr><td class="name">resolveLocale</td><td class="type">(configured?, declared?, fallback?) =&gt; string</td><td class="desc">Settle which locale applies: what you configured, then what the page declares, then the fallback.</td></tr>
3780
+ <tr><td class="name">createMessages</td><td class="type">(opts?) =&gt; Messages</td><td class="desc">Build a message catalogue. A partial set lays over the built-in British English one.</td></tr>
3781
+ <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>
3782
+ <tr><td class="name">formatList</td><td class="type">(items, locale?, type?) =&gt; string</td><td class="desc">Join a list the way the locale does, as a conjunction or a disjunction.</td></tr>
3783
+ </tbody>
3784
+ </table>
3785
+ </div>
3786
+
3787
+ <h3>Formulas and units</h3>
3788
+ <div class="table-wrap">
3789
+ <table>
3790
+ <thead><tr><th>Name</th><th>Signature</th><th>Description</th></tr></thead>
3791
+ <tbody>
3792
+ <tr><td class="name">evaluateFormula</td><td class="type">(text, params?) =&gt; FormulaResult</td><td class="desc">Evaluate one expression. The same closed language the grid uses: no <code>eval</code>, no host access.</td></tr>
3793
+ <tr><td class="name">looksLikeFormula</td><td class="type">(text) =&gt; boolean</td><td class="desc">Whether a pasted or typed value should be treated as a formula.</td></tr>
3794
+ <tr><td class="name">parseUnit</td><td class="type">(text, opts?) =&gt; 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>
3795
+ <tr><td class="name">formatUnit</td><td class="type">(value, opts?) =&gt; string</td><td class="desc">The inverse: render a base-unit number on the ladder the column asked for.</td></tr>
3796
+ <tr><td class="name">UNIT_SYSTEMS</td><td class="type">Record&lt;string, UnitDescriptor[]&gt;</td><td class="desc">Every registered unit system, by name.</td></tr>
3797
+ </tbody>
3798
+ </table>
3799
+ </div>
3800
+
3801
+ <h3>Statistic tiles</h3>
3802
+ <div class="table-wrap">
3803
+ <table>
3804
+ <thead><tr><th>Name</th><th>Signature</th><th>Description</th></tr></thead>
3805
+ <tbody>
3806
+ <tr><td class="name">deltaOf</td><td class="type">(value, baseline) =&gt; object</td><td class="desc">The change between a value and its baseline, as a tile shows it.</td></tr>
3807
+ <tr><td class="name">toneOf</td><td class="type">(direction, goodWhen) =&gt; string</td><td class="desc">Which way to colour a change, given whether a rise is good news.</td></tr>
3808
+ </tbody>
3809
+ </table>
3810
+ </div>
3811
+
3812
+ <h3>Licensing and modules</h3>
3813
+ <div class="table-wrap">
3814
+ <table>
3815
+ <thead><tr><th>Name</th><th>Signature</th><th>Description</th></tr></thead>
3816
+ <tbody>
3817
+ <tr><td class="name">licenceInfo</td><td class="type">() =&gt; 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>
3818
+ <tr><td class="name">licenceState</td><td class="type">() =&gt; 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>
3819
+ <tr><td class="name">registerModules</td><td class="type">(modules, opts?) =&gt; void</td><td class="desc">Install optional modules once, for every grid on the page.</td></tr>
3820
+ <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>
3821
+ </tbody>
3822
+ </table>
3823
+ </div>
3824
+
3825
+ <h3>Ingesting rows, and menus</h3>
3826
+ <div class="table-wrap">
3827
+ <table>
3828
+ <thead><tr><th>Name</th><th>Signature</th><th>Description</th></tr></thead>
3829
+ <tbody>
3830
+ <tr><td class="name">ingestSync</td><td class="type">(rows, plan?, opts?) =&gt; 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>
3831
+ <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>
3832
+ </tbody>
3833
+ </table>
3834
+ </div>
3835
+
3836
+ <h3>The charts module</h3>
3837
+ <div class="table-wrap">
3838
+ <table>
3839
+ <thead><tr><th>Name</th><th>Signature</th><th>Description</th></tr></thead>
3840
+ <tbody>
3841
+ <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>
3842
+ <tr><td class="name">SCHEMES</td><td class="type">Record&lt;string, readonly string[]&gt;</td><td class="desc">The built-in colour schemes, by name.</td></tr>
3843
+ <tr><td class="name">PALETTE</td><td class="type">readonly string[]</td><td class="desc">The default series colours.</td></tr>
3844
+ <tr><td class="name">registerScheme</td><td class="type">(name, colours) =&gt; void</td><td class="desc">Add a colour scheme, or replace one of ours under the same name.</td></tr>
3845
+ <tr><td class="name">resolveScheme</td><td class="type">(spec?) =&gt; object</td><td class="desc">Settle which scheme a chart will draw with.</td></tr>
3846
+ <tr><td class="name">setDefaultScheme</td><td class="type">(name) =&gt; void</td><td class="desc">Change the scheme every chart uses unless it asks for another.</td></tr>
3847
+ <tr><td class="name">schemeNames</td><td class="type">() =&gt; string[]</td><td class="desc">Every scheme name available, built-in and registered.</td></tr>
3848
+ </tbody>
3849
+ </table>
3850
+ </div>
3851
+
3852
+ <h3>The framework adapters</h3>
3853
+ <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>
3854
+ <div class="table-wrap">
3855
+ <table>
3856
+ <thead><tr><th>Name</th><th>Signature</th><th>Description</th></tr></thead>
3857
+ <tbody>
3858
+ <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>
3859
+ <tr><td class="name">handlerName</td><td class="type">(event) =&gt; string</td><td class="desc">The React prop for an event: <code>cell:changed</code> becomes <code>onCellChanged</code>.</td></tr>
3860
+ <tr><td class="name">dashedName</td><td class="type">(event) =&gt; string</td><td class="desc">The Vue and Svelte listener name: <code>cell:edit:start</code> becomes <code>cell-edit-start</code>.</td></tr>
3861
+ </tbody>
3862
+ </table>
3863
+ </div>
3864
+
3865
+ <h3>The web component</h3>
3866
+ <div class="table-wrap">
3867
+ <table>
3868
+ <thead><tr><th>Name</th><th>Signature</th><th>Description</th></tr></thead>
3869
+ <tbody>
3870
+ <tr><td class="name">defineLatticeGrid</td><td class="type">(tag?) =&gt; void</td><td class="desc">Register <code>&lt;lattice-grid&gt;</code>, or your own tag name.</td></tr>
3871
+ <tr><td class="name">createLatticeGridElement</td><td class="type">(deps?) =&gt; class</td><td class="desc">Build the element class without registering it, for a custom registry.</td></tr>
3872
+ <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>
3873
+ <tr><td class="name">TAG_NAME</td><td class="type">string</td><td class="desc">The default tag, <code>lattice-grid</code>.</td></tr>
3874
+ <tr><td class="name">EVENT_PREFIX</td><td class="type">string</td><td class="desc">What DOM events are prefixed with.</td></tr>
3875
+ <tr><td class="name">ATTRIBUTE_CONFIG</td><td class="type">Readonly&lt;Record&lt;string, unknown&gt;&gt;</td><td class="desc">Which attributes map to which configuration keys.</td></tr>
3876
+ <tr><td class="name">observedAttributeNames</td><td class="type">() =&gt; string[]</td><td class="desc">The attributes the element reacts to.</td></tr>
3877
+ <tr><td class="name">domEventName</td><td class="type">(event) =&gt; string</td><td class="desc">The DOM event name a grid event is dispatched under.</td></tr>
3878
+ </tbody>
3879
+ </table>
3880
+ </div>
3881
+
3882
+ <h3>The htmx module</h3>
3883
+ <div class="table-wrap">
3884
+ <table>
3885
+ <thead><tr><th>Name</th><th>Signature</th><th>Description</th></tr></thead>
3886
+ <tbody>
3887
+ <tr><td class="name">initWithin</td><td class="type">(root) =&gt; Grid[]</td><td class="desc">Build every grid inside a fragment htmx just swapped in.</td></tr>
3888
+ <tr><td class="name">destroyWithin</td><td class="type">(root) =&gt; void</td><td class="desc">Tear them down before the fragment goes.</td></tr>
3889
+ <tr><td class="name">gridElementsWithin</td><td class="type">(root) =&gt; Element[]</td><td class="desc">The grid elements in a fragment, without building them.</td></tr>
3890
+ <tr><td class="name">rowsFromFragment</td><td class="type">(fragment) =&gt; unknown[]</td><td class="desc">Read rows out of server-rendered markup.</td></tr>
3891
+ <tr><td class="name">rowsFromJson</td><td class="type">(text) =&gt; unknown[]</td><td class="desc">Read rows out of a JSON payload.</td></tr>
3892
+ <tr><td class="name">ingestResponse</td><td class="type">(grid, response) =&gt; void</td><td class="desc">Apply an htmx response to a grid, whichever of those two shapes it carries.</td></tr>
3893
+ <tr><td class="name">saveStateWithin</td><td class="type">(root) =&gt; void</td><td class="desc">Persist the view state of every grid in a fragment before a swap.</td></tr>
3894
+ <tr><td class="name">restoreStateWithin</td><td class="type">(root) =&gt; void</td><td class="desc">Put it back afterwards.</td></tr>
3895
+ <tr><td class="name">queryParams</td><td class="type">(grid) =&gt; Record&lt;string, string&gt;</td><td class="desc">The grid&#39;s sort, filter and page as request parameters.</td></tr>
3896
+ <tr><td class="name">warnIfLargeHtmlPayload</td><td class="type">(rows) =&gt; void</td><td class="desc">Warn once when a server-rendered payload is large enough that JSON would serve better.</td></tr>
3897
+ <tr><td class="name">QUERY_CHANGED_EVENT</td><td class="type">string</td><td class="desc">Dispatched when the grid&#39;s query changes, for htmx to trigger on.</td></tr>
3898
+ <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>
3899
+ <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>
3900
+ </tbody>
3901
+ </table>
3902
+ </div>
3903
+
3904
+ <h3>The devtools module</h3>
3905
+ <div class="table-wrap">
3906
+ <table>
3907
+ <thead><tr><th>Name</th><th>Signature</th><th>Description</th></tr></thead>
3908
+ <tbody>
3909
+ <tr><td class="name">expose</td><td class="type">(grid, name?) =&gt; void</td><td class="desc">Put a grid on <code>globalThis</code> under a name, so a console session can reach it.</td></tr>
3910
+ </tbody>
3911
+ </table>
3912
+ </div>
3913
+
3914
+
3325
3915
  <!-- BEGIN GENERATED TYPE REFERENCE -->
3326
3916
  <h2 id="type-reference">Type reference</h2>
3327
3917
  <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>