@toclocoinc/lattice-grid 1.64.0 → 1.66.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.
Files changed (177) hide show
  1. package/README.md +6 -3
  2. package/angular/LICENSE +111 -0
  3. package/angular/README.md +78 -0
  4. package/angular/fesm2022/toclocoinc-lattice-grid-angular.mjs +2843 -0
  5. package/angular/package.json +16 -0
  6. package/angular/types/toclocoinc-lattice-grid-angular.d.ts +1366 -0
  7. package/docs/API.html +791 -8
  8. package/docs/api-detail.html +172 -4
  9. package/lattice-grid.d.ts +241 -205
  10. package/lattice-grid.esm.min.js +19 -7
  11. package/lattice-grid.min.cjs +19 -7
  12. package/lattice-grid.min.css +1 -1
  13. package/lattice-grid.min.js +19 -7
  14. package/modules/ai.d.ts +10 -13
  15. package/modules/ai.esm.min.js +3 -3
  16. package/modules/ai.min.cjs +3 -3
  17. package/modules/ai.min.js +3 -3
  18. package/modules/angular.d.ts +2 -2
  19. package/modules/angular.esm.min.js +433 -7
  20. package/modules/angular.min.cjs +433 -7
  21. package/modules/angular.min.js +433 -7
  22. package/modules/chart-alluvial.d.ts +2 -2
  23. package/modules/chart-alluvial.esm.min.js +1 -1
  24. package/modules/chart-alluvial.min.cjs +1 -1
  25. package/modules/chart-alluvial.min.js +1 -1
  26. package/modules/chart-arc.d.ts +2 -2
  27. package/modules/chart-arc.esm.min.js +1 -1
  28. package/modules/chart-arc.min.cjs +1 -1
  29. package/modules/chart-arc.min.js +1 -1
  30. package/modules/chart-bubblemap.d.ts +2 -2
  31. package/modules/chart-bubblemap.esm.min.js +1 -1
  32. package/modules/chart-bubblemap.min.cjs +1 -1
  33. package/modules/chart-bubblemap.min.js +1 -1
  34. package/modules/chart-bump.d.ts +2 -2
  35. package/modules/chart-bump.esm.min.js +1 -1
  36. package/modules/chart-bump.min.cjs +1 -1
  37. package/modules/chart-bump.min.js +1 -1
  38. package/modules/chart-calendar.d.ts +2 -2
  39. package/modules/chart-calendar.esm.min.js +1 -1
  40. package/modules/chart-calendar.min.cjs +1 -1
  41. package/modules/chart-calendar.min.js +1 -1
  42. package/modules/chart-decomposition.d.ts +2 -2
  43. package/modules/chart-decomposition.esm.min.js +1 -1
  44. package/modules/chart-decomposition.min.cjs +1 -1
  45. package/modules/chart-decomposition.min.js +1 -1
  46. package/modules/chart-diverging.d.ts +2 -2
  47. package/modules/chart-diverging.esm.min.js +1 -1
  48. package/modules/chart-diverging.min.cjs +1 -1
  49. package/modules/chart-diverging.min.js +1 -1
  50. package/modules/chart-dumbbell.d.ts +2 -2
  51. package/modules/chart-dumbbell.esm.min.js +1 -1
  52. package/modules/chart-dumbbell.min.cjs +1 -1
  53. package/modules/chart-dumbbell.min.js +1 -1
  54. package/modules/chart-fan.d.ts +2 -2
  55. package/modules/chart-fan.esm.min.js +1 -1
  56. package/modules/chart-fan.min.cjs +1 -1
  57. package/modules/chart-fan.min.js +1 -1
  58. package/modules/chart-hexbin.d.ts +2 -2
  59. package/modules/chart-hexbin.esm.min.js +1 -1
  60. package/modules/chart-hexbin.min.cjs +1 -1
  61. package/modules/chart-hexbin.min.js +1 -1
  62. package/modules/chart-hexmap.d.ts +2 -2
  63. package/modules/chart-hexmap.esm.min.js +1 -1
  64. package/modules/chart-hexmap.min.cjs +1 -1
  65. package/modules/chart-hexmap.min.js +1 -1
  66. package/modules/chart-icicle.d.ts +2 -2
  67. package/modules/chart-icicle.esm.min.js +1 -1
  68. package/modules/chart-icicle.min.cjs +1 -1
  69. package/modules/chart-icicle.min.js +1 -1
  70. package/modules/chart-markermap.d.ts +1 -1
  71. package/modules/chart-markermap.esm.min.js +1 -1
  72. package/modules/chart-markermap.min.cjs +1 -1
  73. package/modules/chart-markermap.min.js +1 -1
  74. package/modules/chart-parallel.d.ts +2 -2
  75. package/modules/chart-parallel.esm.min.js +1 -1
  76. package/modules/chart-parallel.min.cjs +1 -1
  77. package/modules/chart-parallel.min.js +1 -1
  78. package/modules/chart-ridgeline.d.ts +2 -2
  79. package/modules/chart-ridgeline.esm.min.js +1 -1
  80. package/modules/chart-ridgeline.min.cjs +1 -1
  81. package/modules/chart-ridgeline.min.js +1 -1
  82. package/modules/chart-roc.d.ts +2 -2
  83. package/modules/chart-roc.esm.min.js +1 -1
  84. package/modules/chart-roc.min.cjs +1 -1
  85. package/modules/chart-roc.min.js +1 -1
  86. package/modules/chart-slope.d.ts +2 -2
  87. package/modules/chart-slope.esm.min.js +1 -1
  88. package/modules/chart-slope.min.cjs +1 -1
  89. package/modules/chart-slope.min.js +1 -1
  90. package/modules/chart-splom.d.ts +2 -2
  91. package/modules/chart-splom.esm.min.js +1 -1
  92. package/modules/chart-splom.min.cjs +1 -1
  93. package/modules/chart-splom.min.js +1 -1
  94. package/modules/chart-waffle.d.ts +2 -2
  95. package/modules/chart-waffle.esm.min.js +1 -1
  96. package/modules/chart-waffle.min.cjs +1 -1
  97. package/modules/chart-waffle.min.js +1 -1
  98. package/modules/charts.d.ts +4 -5
  99. package/modules/charts.esm.min.js +261 -48
  100. package/modules/charts.min.cjs +261 -48
  101. package/modules/charts.min.js +261 -48
  102. package/modules/data-router.d.ts +5 -6
  103. package/modules/data-router.esm.min.js +3 -3
  104. package/modules/data-router.min.cjs +3 -3
  105. package/modules/data-router.min.js +3 -3
  106. package/modules/devtools.d.ts +1 -1
  107. package/modules/devtools.esm.min.js +1 -1
  108. package/modules/devtools.min.cjs +1 -1
  109. package/modules/devtools.min.js +1 -1
  110. package/modules/dhtmlx-compat.d.ts +1 -1
  111. package/modules/dhtmlx-compat.esm.min.js +3 -3
  112. package/modules/dhtmlx-compat.min.cjs +3 -3
  113. package/modules/dhtmlx-compat.min.js +3 -3
  114. package/modules/gantt.d.ts +37 -43
  115. package/modules/gantt.esm.min.js +3 -3
  116. package/modules/gantt.min.cjs +3 -3
  117. package/modules/gantt.min.js +3 -3
  118. package/modules/geo-europe-nuts.d.ts +2 -2
  119. package/modules/geo-europe-nuts.esm.min.js +1 -1
  120. package/modules/geo-uk.d.ts +2 -2
  121. package/modules/geo-uk.esm.min.js +1 -1
  122. package/modules/geo-us-states.d.ts +2 -2
  123. package/modules/geo-us-states.esm.min.js +1 -1
  124. package/modules/geo-world-110m.d.ts +2 -2
  125. package/modules/geo-world-110m.esm.min.js +1 -1
  126. package/modules/geo-world-50m.d.ts +2 -2
  127. package/modules/geo-world-50m.esm.min.js +1 -1
  128. package/modules/htmx.d.ts +2 -2
  129. package/modules/htmx.esm.min.js +19 -7
  130. package/modules/htmx.min.cjs +19 -7
  131. package/modules/htmx.min.js +19 -7
  132. package/modules/kanban.d.ts +14 -14
  133. package/modules/kanban.esm.min.js +3 -3
  134. package/modules/kanban.min.cjs +3 -3
  135. package/modules/kanban.min.js +3 -3
  136. package/modules/kpi.d.ts +5 -5
  137. package/modules/kpi.esm.min.js +3 -3
  138. package/modules/kpi.min.cjs +3 -3
  139. package/modules/kpi.min.js +3 -3
  140. package/modules/layout.d.ts +2 -2
  141. package/modules/layout.esm.min.js +3 -3
  142. package/modules/layout.min.cjs +3 -3
  143. package/modules/layout.min.js +3 -3
  144. package/modules/mock-socket.d.ts +1 -1
  145. package/modules/mock-socket.esm.min.js +1 -1
  146. package/modules/mock-socket.min.cjs +1 -1
  147. package/modules/mock-socket.min.js +1 -1
  148. package/modules/react.d.ts +4 -5
  149. package/modules/react.esm.min.js +3 -3
  150. package/modules/react.min.cjs +3 -3
  151. package/modules/react.min.js +3 -3
  152. package/modules/svelte.d.ts +252 -4
  153. package/modules/svelte.esm.min.js +960 -6
  154. package/modules/svelte.min.cjs +942 -5
  155. package/modules/svelte.min.js +942 -5
  156. package/modules/tabs.d.ts +2 -3
  157. package/modules/tabs.esm.min.js +3 -3
  158. package/modules/tabs.min.cjs +3 -3
  159. package/modules/tabs.min.js +3 -3
  160. package/modules/vue.d.ts +398 -9
  161. package/modules/vue.esm.min.js +1163 -31
  162. package/modules/vue.min.cjs +1147 -30
  163. package/modules/vue.min.js +1147 -30
  164. package/modules/webcomponent.d.ts +234 -2
  165. package/modules/webcomponent.esm.min.js +1267 -38
  166. package/modules/webcomponent.min.cjs +1249 -38
  167. package/modules/webcomponent.min.js +1249 -38
  168. package/package.json +27 -2
  169. package/svelte/Chart.svelte +34 -0
  170. package/svelte/Gantt.svelte +34 -0
  171. package/svelte/Grid.svelte +30 -0
  172. package/svelte/GridProvider.svelte +13 -0
  173. package/svelte/KPI.svelte +34 -0
  174. package/svelte/Kanban.svelte +34 -0
  175. package/svelte/Layout.svelte +34 -0
  176. package/svelte/Router.svelte +16 -0
  177. package/svelte/Tabs.svelte +53 -0
package/docs/API.html CHANGED
@@ -360,7 +360,7 @@
360
360
  <div class="shell">
361
361
  <aside class="rail">
362
362
  <p class="rail__brand">Lattice Grid</p>
363
- <p class="rail__sub">API reference · v1.64.0</p>
363
+ <p class="rail__sub">API reference · v1.66.0</p>
364
364
  <nav>
365
365
  <div class="rail__group">
366
366
  <span class="rail__label">Start</span>
@@ -413,6 +413,15 @@
413
413
  <a href="#sources">Sources</a>
414
414
  <a href="#events">Event reference</a>
415
415
  </div>
416
+ <div class="rail__group">
417
+ <span class="rail__label">Frameworks</span>
418
+ <a href="#adapters">Framework adapters</a>
419
+ <a href="#react-v2">React</a>
420
+ <a href="#vue-v2">Vue</a>
421
+ <a href="#svelte-v2">Svelte</a>
422
+ <a href="#angular-v2">Angular</a>
423
+ <a href="#webcomponent-v2">Web Components</a>
424
+ </div>
416
425
  <div class="rail__group">
417
426
  <span class="rail__label">Registries</span>
418
427
  <a href="#names">Built-in names</a>
@@ -444,7 +453,7 @@
444
453
  </header>
445
454
 
446
455
  <p class="chips">
447
- <span class="chip">Version 1.64.0</span>
456
+ <span class="chip">Version 1.66.0</span>
448
457
  <span class="chip">Zero dependencies</span>
449
458
  <span class="chip"><a href="api-detail.html">Developer guide &rarr;</a></span>
450
459
  </p>
@@ -520,8 +529,9 @@
520
529
  <thead><tr><th>Entry point</th><th>Factory</th><th>Needs</th></tr></thead>
521
530
  <tbody>
522
531
  <tr><td class="name">modules/react</td><td class="sig">createLatticeGrid({ React, createGrid })</td><td class="desc">Returns a component. Forwards a ref exposing <code>.grid</code>. Since 1.63 the same entry point also builds a component for <a href="#react-v2">every other viewer</a> and the data router.</td></tr>
523
- <tr><td class="name">modules/vue</td><td class="sig">createLatticeGrid({ vue, createGrid })</td><td class="desc">Returns a Vue 3 component definition.</td></tr>
524
- <tr><td class="name">modules/svelte</td><td class="sig">createLatticeAction({ createGrid })</td><td class="desc">Returns a <code>use:</code> action.</td></tr>
532
+ <tr><td class="name">@toclocoinc/<wbr>lattice-grid/angular</td><td class="sig">&lt;lattice-grid [config]="…"&gt;</td><td class="desc">A compiled entry point of this package, not a bundle: standalone components compiled ahead of time, one per viewer, plus the data router as a service. No second install. See <a href="#angular-v2">Angular</a>. <code>modules/angular</code>, which needs the JIT compiler, is deprecated.</td></tr>
533
+ <tr><td class="name">modules/vue</td><td class="sig">createLatticeGrid({ vue, createGrid })</td><td class="desc">Returns a Vue 3 component definition. Since 1.66 the same entry point also builds a component for <a href="#vue-v2">every other viewer</a> and the data router.</td></tr>
534
+ <tr><td class="name">modules/svelte</td><td class="sig">createLatticeAction({ createGrid })</td><td class="desc">Returns a <code>use:</code> action. Since 1.66 the package also ships <a href="#svelte-v2">a Svelte 5 component per viewer</a> as <code>.svelte</code> source at <code>@toclocoinc/lattice-grid/svelte/*.svelte</code>, and this entry point carries the helpers they are built from.</td></tr>
525
535
  </tbody>
526
536
  </table>
527
537
  </div>
@@ -706,6 +716,473 @@ controller.destroy();
706
716
  a line that could never run. The build now deletes that branch from every emitted artefact.
707
717
  </p>
708
718
 
719
+ <h3 id="vue-v2">Vue 3: every viewer, not only the grid</h3>
720
+ <p>
721
+ Until 1.66 the Vue adapter wrapped <code>createGrid</code> and nothing else. Every other
722
+ shipped viewer &mdash; the KPI panel, a chart, the board, the Gantt, the layout, the tab
723
+ strip &mdash; and the data router had no Vue surface, so a Vue application wrote its own
724
+ <code>onMounted</code>/<code>onBeforeUnmount</code> pair per viewer. It does not have to any
725
+ more: there is one component per viewer, and each keeps the contract the grid component
726
+ already kept.
727
+ </p>
728
+ <p>
729
+ Every component is built with <code>defineComponent</code> and a render function, so the
730
+ adapter needs <strong>no template compiler</strong> in your build and runs on
731
+ <code>vue.runtime.*</code> &mdash; the compiler-free build a Vite application ships. The
732
+ floor is <strong>Vue 3.3</strong>.
733
+ </p>
734
+ <pre><code><span class="cmt">// lattice.js — built once, at module scope.</span>
735
+ <span class="kw">import</span> * <span class="kw">as</span> vue <span class="kw">from</span> 'vue';
736
+ <span class="kw">import</span> { createGrid } <span class="kw">from</span> '@toclocoinc/lattice-grid';
737
+ <span class="kw">import</span> { createKPI } <span class="kw">from</span> '@toclocoinc/lattice-grid/modules/kpi';
738
+ <span class="kw">import</span> { createChart } <span class="kw">from</span> '@toclocoinc/lattice-grid/modules/charts';
739
+ <span class="kw">import</span> { createDataRouter } <span class="kw">from</span> '@toclocoinc/lattice-grid/modules/data-router';
740
+ <span class="kw">import</span> { createLatticeVue } <span class="kw">from</span> '@toclocoinc/lattice-grid/modules/vue';
741
+
742
+ <span class="kw">export const</span> L = createLatticeVue({ vue, createGrid, createKPI, createChart, createDataRouter });</code></pre>
743
+ <pre><code><span class="cmt">// Dashboard.vue</span>
744
+ &lt;script setup&gt;
745
+ <span class="kw">import</span> { L } <span class="kw">from</span> './lattice.js';
746
+
747
+ L.provideLatticeGrids();
748
+ L.provideLatticeRouter(ROUTER_CONFIG);
749
+
750
+ <span class="kw">const</span> rows = shallowRef([]);
751
+ &lt;/script&gt;
752
+
753
+ &lt;template&gt;
754
+ &lt;component :is="L.LatticeGrid" name="quakes" route="all" v-bind="GRID_CONFIG" :rows="rows" /&gt;
755
+ &lt;component :is="L.LatticeKPI" grid-name="quakes" :tiles="TILES" :columns="5" /&gt;
756
+ &lt;component :is="L.LatticeChart" grid-name="quakes" type="bar" x="region" y="count" /&gt;
757
+ &lt;/template&gt;</code></pre>
758
+ <div class="table-wrap">
759
+ <table>
760
+ <thead><tr><th>Export</th><th>Signature</th><th>What it is</th></tr></thead>
761
+ <tbody>
762
+ <tr><td class="name">createLatticeGrid</td><td class="sig">({ vue, createGrid })</td><td class="desc">The grid component. Extended in 1.66 with <code>rowUpdates</code>, <code>predicates</code>, <code>name</code>, <code>route</code> and the <code>grid-ready</code> / <code>grid-destroyed</code> emits. Unchanged for anyone already on it.</td></tr>
763
+ <tr><td class="name">createLatticeKPI</td><td class="sig">({ vue, createKPI })</td><td class="desc">The KPI panel. Grid-bound through the provided registry by default; bind <code>:rows</code> instead for a panel with no grid.</td></tr>
764
+ <tr><td class="name">createLatticeChart</td><td class="sig">({ vue, createChart })</td><td class="desc">A chart. Requires a grid, so nothing is mounted until one exists; a changed spec key goes to <code>chart.update()</code> and the chart redraws rather than being rebuilt.</td></tr>
765
+ <tr><td class="name">createLatticeKanban</td><td class="sig">({ vue, createKanban })</td><td class="desc">The board. <code>rows</code>, <code>quickFilter</code>, <code>sprint</code>, <code>epic</code>, <code>loading</code> and <code>error</code> are live props.</td></tr>
766
+ <tr><td class="name">createLatticeGantt</td><td class="sig">({ vue, createGantt })</td><td class="desc">The Gantt. <code>tasks</code> and <code>dependencies</code> are live props.</td></tr>
767
+ <tr><td class="name">createLatticeLayout</td><td class="sig">({ vue, createLayout })</td><td class="desc">The layout. Windows are driven through the exposed instance; its events arrive as <code>@layout-changed</code>, <code>@window-moved</code> and the rest.</td></tr>
768
+ <tr><td class="name">createLatticeTabs</td><td class="sig">({ vue, createTabs, createGrid? })</td><td class="desc">The tab strip, with <strong>Vue-rendered tab content</strong>: a tab's content is a <strong>named slot</strong> &mdash; <code>&lt;template #overview&gt;</code> for a tab with <code>id: 'overview'</code>, or <code>&lt;template #tab-overview&gt;</code> &mdash; rendered into the strip's own panel through a <code>&lt;Teleport&gt;</code>, so a tab's grid is a real <code>LatticeGrid</code> with props, a ref and the surrounding <code>provide</code>s. A tab with no slot is left to the module, so the two kinds mix on one strip.</td></tr>
769
+ <tr><td class="name">createLatticeGridContext</td><td class="sig">({ vue })</td><td class="desc">Returns <code>{ LatticeGridProvider, provideLatticeGrids, useLatticeGrid }</code>. A grid-bound viewer needs the grid <em>instance</em>, which appears after the first render; a template ref cannot help, because assigning to one re-renders nobody. Call <code>provideLatticeGrids()</code> in a parent's <code>setup</code>, or wrap the subtree in <code>&lt;LatticeGridProvider&gt;</code>. Several grids may publish under one registry &mdash; <code>name</code> on the grid, <code>grid-name</code> on the viewer.</td></tr>
770
+ <tr><td class="name">createLatticeRouter</td><td class="sig">({ vue, createDataRouter })</td><td class="desc">Returns <code>{ provideLatticeRouter, useLatticeRouter, LatticeRouterProvider }</code>. <code>provideLatticeRouter(config)</code> is called in a component's <code>setup</code>: the router is built by the first grid under it that names a <code>route</code>, and destroyed when that component's scope is. The config is read once, because rebuilding would drop every attached grid and every row held.</td></tr>
771
+ <tr><td class="name">createLatticeViewer</td><td class="sig">({ vue, viewer, mount, … })</td><td class="desc">The generic behind all of the above, and the escape hatch for a viewer with no named factory yet.</td></tr>
772
+ <tr><td class="name">createLatticeVue</td><td class="sig">({ vue, …factories })</td><td class="desc">Every binding from one call: pass the factories the application uses and it builds those components, leaving the rest undefined. Nothing is imported, so an application that never uses the board never loads the board.</td></tr>
773
+ <tr><td class="name">createViewerController</td><td class="sig">({ viewer, mount, element, props })</td><td class="desc">The framework-free viewer lifecycle &mdash; mount once, push changed props into the live instance, destroy &mdash; shared by every adapter.</td></tr>
774
+ <tr><td class="name">VIEWER_EVENTS</td><td class="type">Record&lt;string, readonly string[]&gt;</td><td class="desc">Every event each non-grid viewer emits, keyed by viewer name.</td></tr>
775
+ <tr><td class="name">VIEWER_APPLY</td><td class="type">Record&lt;string, Record&lt;string, Function&gt;&gt;</td><td class="desc">Which props each viewer can take <em>live</em>, and the instance call each becomes. Anything not listed is mount-time configuration.</td></tr>
776
+ <tr><td class="name">viewerHandlerName</td><td class="type">(event) =&gt; string</td><td class="desc">A viewer event as the handler key the shared controller dispatches on: <code>card:move</code> becomes <code>onCardMove</code>. A Vue template binds the dashed form instead &mdash; <code>@card-move</code>.</td></tr>
777
+ <tr><td class="name">DEFAULT_GRID_NAME</td><td class="type">string</td><td class="desc">The name a grid publishes itself under when you do not choose one: <code>default</code>.</td></tr>
778
+ </tbody>
779
+ </table>
780
+ </div>
781
+
782
+ <pre data-run="js" data-expect="7 components, cell-changed, card-move, default, rows 2" data-covers="export:createLatticeVue export:createLatticeGrid export:createLatticeKPI export:createLatticeChart export:createLatticeKanban export:createLatticeGantt export:createLatticeLayout export:createLatticeTabs export:createLatticeViewer export:createLatticeGridContext export:createLatticeRouter export:createViewerController export:viewerHandlerName export:dashedName export:VIEWER_EVENTS export:VIEWER_APPLY export:DEFAULT_GRID_NAME export:EVENT_NAMES"><code><span class="kw">const</span> A = <span class="kw">await</span> import('../packages/modules/vue/index.js');
783
+
784
+ <span class="cmt">// A stand-in for Vue. The adapter never imports it, so a factory only needs</span>
785
+ <span class="cmt">// the handful of names it destructures — which is exactly why this example</span>
786
+ <span class="cmt">// runs in Node with no framework installed.</span>
787
+ <span class="kw">const</span> vue = {
788
+ defineComponent: (d) =&gt; d, h: () =&gt; ({}), ref: (v) =&gt; ({ value: v }),
789
+ shallowRef: (v) =&gt; ({ value: v }), computed: (f) =&gt; ({ get value() { <span class="kw">return</span> f(); } }),
790
+ watch: () =&gt; () =&gt; {}, onMounted: () =&gt; {}, onBeforeUnmount: () =&gt; {},
791
+ onScopeDispose: () =&gt; {}, provide: () =&gt; {}, inject: () =&gt; <span class="kw">null</span>,
792
+ Teleport: Symbol('Teleport'), Fragment: Symbol('Fragment'),
793
+ };
794
+ <span class="kw">const</span> stub = () =&gt; ({ on: () =&gt; () =&gt; {}, setRows: () =&gt; {}, destroy() {} });
795
+
796
+ <span class="cmt">// One call builds whichever components the factories you pass support.</span>
797
+ <span class="kw">const</span> L = A.createLatticeVue({
798
+ vue, createGrid: stub, createKPI: stub, createChart: stub, createDataRouter: stub,
799
+ });
800
+
801
+ <span class="cmt">// …or one factory at a time. Each returns a Vue component definition.</span>
802
+ <span class="kw">const</span> components = [
803
+ A.createLatticeGrid({ vue, createGrid: stub }),
804
+ A.createLatticeKPI({ vue, createKPI: stub }),
805
+ A.createLatticeChart({ vue, createChart: stub }),
806
+ A.createLatticeKanban({ vue, createKanban: stub }),
807
+ A.createLatticeGantt({ vue, createGantt: stub }),
808
+ A.createLatticeLayout({ vue, createLayout: stub }),
809
+ A.createLatticeTabs({ vue, createTabs: stub }),
810
+ ].filter((c) =&gt; <span class="kw">typeof</span> c.setup === 'function');
811
+
812
+ <span class="cmt">// The generic behind them, the registry pair and the router composables.</span>
813
+ A.createLatticeViewer({ vue, viewer: 'kpi', mount: stub });
814
+ <span class="kw">const</span> { LatticeGridProvider, useLatticeGrid } = A.createLatticeGridContext({ vue });
815
+ <span class="kw">const</span> { provideLatticeRouter } = A.createLatticeRouter({ vue, createDataRouter: stub });
816
+
817
+ <span class="cmt">// Events are bound under dashed names, because a colon is directive syntax.</span>
818
+ <span class="kw">const</span> gridEvent = A.dashedName(A.EVENT_NAMES.find((n) =&gt; n === 'cell:changed'));
819
+ <span class="kw">const</span> viewerEvent = A.dashedName(A.VIEWER_EVENTS.kanban.find((n) =&gt; n === 'card:move'));
820
+ A.viewerHandlerName('card:move');
821
+
822
+ <span class="cmt">// And the lifecycle underneath, driven with no framework at all: mount once,</span>
823
+ <span class="cmt">// push a changed live prop into the instance that already exists, destroy.</span>
824
+ <span class="kw">const</span> seen = [];
825
+ <span class="kw">const</span> controller = A.createViewerController({
826
+ viewer: 'kpi',
827
+ element: {},
828
+ props: { rows: [{ id: 1 }] },
829
+ mount: () =&gt; ({ setRows: (r) =&gt; seen.push(r.length), on: () =&gt; () =&gt; {}, destroy() {} }),
830
+ });
831
+ controller.update({ rows: [{ id: 1 }, { id: 2 }] });
832
+ controller.destroy();
833
+ <span class="kw">void</span> [L.LatticeGrid, LatticeGridProvider, useLatticeGrid, provideLatticeRouter, A.VIEWER_APPLY];
834
+
835
+ <span class="kw">return</span> `${components.length} components, ${gridEvent}, ${viewerEvent}, `
836
+ + `${A.DEFAULT_GRID_NAME}, rows ${seen.join('/')}`;</code></pre>
837
+
838
+ <p class="section-note">
839
+ <strong>The grid paints outside Vue's reactivity, and that is deliberate.</strong> The
840
+ adapter creates the grid in <code>onMounted</code> and pushes changed props into it; the grid
841
+ then renders on its own schedule, not Vue's. So <code>nextTick()</code> tells you Vue has
842
+ patched the DOM, <em>not</em> that the grid has painted. Measure, screenshot or scroll in a
843
+ grid event &mdash; <code>@render-done</code>, <code>@rows-changed</code>,
844
+ <code>@size-changed</code> &mdash; rather than after <code>nextTick</code> or in
845
+ <code>onMounted</code>.
846
+ </p>
847
+ <p class="section-note">
848
+ <strong>Hold big row arrays in a <code>shallowRef</code>, never a <code>ref</code>.</strong>
849
+ <code>ref([...100k rows])</code> makes every row object a reactive proxy: Vue walks the array
850
+ on assignment and every property read on every row goes through a proxy trap from then on.
851
+ The grid neither needs nor wants that &mdash; it re-reads the array itself when the reference
852
+ changes. Use <code>shallowRef(rows)</code> and assign a <em>new</em> array when the data
853
+ changes (<code>rows.value = next</code>), or <code>markRaw</code> the objects. The same goes
854
+ for the grid instance you keep off a template ref: a <code>ref</code> would proxy the whole
855
+ <code>Grid</code>, and the proxy is not <code>===</code> the object
856
+ <code>createGrid</code> returned.
857
+ </p>
858
+ <p class="section-note">
859
+ <strong>Props are diffed with <code>Object.is</code>, and that is a promise about identity.</strong>
860
+ An inline <code>:columns="[{ field: 'a' }]"</code> is a new array on every render, so it
861
+ counts as changed every render and reconfigures the grid every render. Hoist it to module
862
+ scope or put it behind a <code>computed</code>. This is not a defect to work around: a deep
863
+ compare of a million-row array on every render would cost more than the reload it prevents.
864
+ For the same reason nothing here uses a <code>{ deep: true }</code> watcher &mdash; one over a
865
+ config carrying <code>rows</code> would walk every row object on every tick.
866
+ </p>
867
+ <p class="section-note">
868
+ <strong>Live props versus mount-time props.</strong> The grid takes any changed configuration
869
+ key through one call (<code>grid.setAll</code>); no other viewer does. So each viewer declares
870
+ which props it can take while it is running (<code>VIEWER_APPLY</code>), and everything else
871
+ is mount-time configuration. A mount-time prop that changes is <em>not</em> silently ignored
872
+ and <em>not</em> silently remounted (that would throw away scroll position, selection and
873
+ expansion): it is named once in a warning that tells you to give the component a
874
+ <code>:key</code> that changes when the rebuild is wanted, or to drive the instance through
875
+ the ref. The tab strip's whole <code>tabs</code> array is mount-time for the same reason
876
+ &mdash; the module has no way to repaint an existing tab's label or badge.
877
+ </p>
878
+ <p class="section-note">
879
+ <strong>Events are dashed; the lifecycle pair is <code>grid-ready</code> and
880
+ <code>grid-destroyed</code>.</strong> <code>cell:changed</code> is emitted as
881
+ <code>@cell-changed</code>, because a colon in a Vue binding is directive syntax. Every grid
882
+ event is declared in <code>emits</code>, so none of them falls through onto the host element.
883
+ The two lifecycle emits are named <code>grid-*</code> because <code>ready</code> and
884
+ <code>destroy</code> are grid event names already re-emitted here, and the React and Angular
885
+ adapters name their pair the same.
886
+ </p>
887
+ <p class="section-note">
888
+ <strong>Reaching the instance, and <code>class</code>/<code>style</code>/<code>id</code>.</strong>
889
+ A template ref on the grid exposes <code>grid()</code>
890
+ (<code>gridRef.value.grid()</code>); every other component exposes
891
+ <code>instance()</code>. Both are <code>null</code> before mount. <code>class</code>,
892
+ <code>style</code> and <code>id</code> are applied to the component's own host element rather
893
+ than passed on as configuration; every other binding is the viewer's configuration, with a
894
+ known kebab-cased key mapped to its camelCase form (<code>:row-key</code> reaches
895
+ <code>rowKey</code>), as the web component does.
896
+ </p>
897
+ <p class="section-note">
898
+ <strong>Cleanup, and no SSR.</strong> Every component destroys its instance, stops its
899
+ watchers and unsubscribes its listeners in <code>onBeforeUnmount</code>, so a
900
+ <code>v-if</code> toggled twice leaves exactly one instance alive and an unmounted page
901
+ leaves nothing on a timer or an event bus. The router is destroyed with the component whose
902
+ <code>setup</code> provided it. Every component owns a real DOM element and builds nothing
903
+ until <code>onMounted</code>, so a server render emits the empty host and nothing else: there
904
+ is no SSR or Nuxt server-side support, and Vue 2 is not supported.
905
+ </p>
906
+
907
+ <h3 id="svelte-v2">Svelte 5: components you compile, not a bundle you load</h3>
908
+ <p>
909
+ Until 1.66 the Svelte adapter was a single action, <code>use:lattice</code>, over a grid.
910
+ An action cannot do what the other adapters do &mdash; it takes a node and a value, so it has
911
+ no children, no snippets and no context of its own, and a tab strip whose panels hold
912
+ Svelte-rendered content is simply out of its reach. So 1.66 adds <strong>components</strong>,
913
+ one per viewer, and they ship the way the Svelte ecosystem ships components: as
914
+ <code>.svelte</code> <strong>source</strong>, compiled by your own toolchain.
915
+ <code>svelte</code> is an <em>optional</em> peer dependency; the package still installs
916
+ nothing. <strong>Svelte 5 only</strong> &mdash; the components are written in runes and
917
+ snippets, neither of which exists in Svelte 4. The action is unchanged and still supported.
918
+ </p>
919
+ <pre><code><span class="cmt">&lt;!-- Dashboard.svelte --&gt;</span>
920
+ &lt;script&gt;
921
+ <span class="kw">import</span> Provider <span class="kw">from</span> '@toclocoinc/lattice-grid/svelte/GridProvider.svelte';
922
+ <span class="kw">import</span> Grid <span class="kw">from</span> '@toclocoinc/lattice-grid/svelte/Grid.svelte';
923
+ <span class="kw">import</span> KPI <span class="kw">from</span> '@toclocoinc/lattice-grid/svelte/KPI.svelte';
924
+ <span class="kw">import</span> Chart <span class="kw">from</span> '@toclocoinc/lattice-grid/svelte/Chart.svelte';
925
+ <span class="kw">import</span> Tabs <span class="kw">from</span> '@toclocoinc/lattice-grid/svelte/Tabs.svelte';
926
+
927
+ <span class="kw">let</span> rows = $state.raw([]);
928
+ <span class="kw">let</span> board;
929
+ &lt;/script&gt;
930
+
931
+ &lt;Provider&gt;
932
+ &lt;Grid bind:this={board} name="quakes" class="grid" {...GRID_CONFIG} {rows}
933
+ onCellChanged={(e) =&gt; save(e)} /&gt;
934
+ &lt;KPI gridName="quakes" tiles={TILES} columns={5} /&gt;
935
+ &lt;Chart gridName="quakes" type="bar" x="region" y="count" /&gt;
936
+
937
+ &lt;Tabs tabs={TABS} active={open} onTabChanged={(e) =&gt; (open = e.id)}&gt;
938
+ {#snippet all()}&lt;Grid name="all" {...GRID_CONFIG} {rows} /&gt;{/snippet}
939
+ &lt;/Tabs&gt;
940
+ &lt;/Provider&gt;</code></pre>
941
+ <div class="table-wrap">
942
+ <table>
943
+ <thead><tr><th>Component</th><th>Import</th><th>What it is</th></tr></thead>
944
+ <tbody>
945
+ <tr><td class="name">Grid.svelte</td><td class="sig">@toclocoinc/<wbr>lattice-grid/<wbr>svelte/<wbr>Grid.svelte</td><td class="desc">One grid for the component's lifetime. Every configuration key is a prop, diffed with <code>Object.is</code> and pushed through <code>setAll</code>; <code>rowUpdates</code> is a keyed diff straight to <code>rows.apply()</code>; <code>predicates</code> are named filters registered through <code>filters.where</code>, so they compose with the reader's own. <code>bind:this</code> plus <code>grid()</code> hands you the live <code>Grid</code>.</td></tr>
946
+ <tr><td class="name">GridProvider.svelte</td><td class="sig">…/<wbr>svelte/<wbr>GridProvider.svelte</td><td class="desc">Puts a grid registry in context. A grid publishes itself under its <code>name</code>; a viewer finds it by <code>gridName</code>. Also available as <code>createGridRegistry()</code> for a host that would rather call <code>setContext</code> itself.</td></tr>
947
+ <tr><td class="name">KPI, Chart, Kanban, Gantt, Layout</td><td class="sig">…/<wbr>svelte/<wbr>KPI.svelte, …</td><td class="desc">One viewer each, built once and updated in place. A grid-bound viewer waits for its grid rather than mounting empty, and a <em>different</em> grid rebuilds it. <code>bind:this</code> plus <code>instance()</code> hands you the live viewer.</td></tr>
948
+ <tr><td class="name">Tabs.svelte</td><td class="sig">…/<wbr>svelte/<wbr>Tabs.svelte</td><td class="desc">The tab strip, with Svelte-rendered panels: a tab's content is a <code>{#snippet}</code> named after the tab's id, so a tab's grid is a real component with props, context and a reference. A tab with no snippet is left to the module, so configuration-driven tabs still work and the two kinds mix.</td></tr>
949
+ <tr><td class="name">Router.svelte</td><td class="sig">…/<wbr>svelte/<wbr>Router.svelte</td><td class="desc">A Data Router in context, built by the first grid that names a <code>route</code> and destroyed with the component.</td></tr>
950
+ </tbody>
951
+ </table>
952
+ </div>
953
+ <p>
954
+ The components are deliberately <strong>thin</strong> — a template, the <code>$props()</code>
955
+ plumbing, a <code>bind:this</code> getter and the context wiring — because they ship as source
956
+ you read. Every rule they follow lives in <code>modules/svelte</code>, which ships minified,
957
+ and each is exported so you can build a component of your own on exactly the same footing:
958
+ </p>
959
+ <div class="table-wrap">
960
+ <table>
961
+ <thead><tr><th>Export</th><th>Signature</th><th>What it does</th></tr></thead>
962
+ <tbody>
963
+ <tr><td class="name">bindGrid</td><td class="sig">({ createGrid, element, props, registry, router })</td><td class="desc">Owns one grid: builds it once, diffs props into it through <code>setAll</code>, registers <code>predicates</code> before it publishes, applies each new <code>rowUpdates</code> object through <code>rows.apply()</code>, attaches it to the router, and on <code>destroy</code> detaches and withdraws before destroying.</td></tr>
964
+ <tr><td class="name">bindViewer</td><td class="sig">({ viewer, factory, element, registry })</td><td class="desc">Owns one KPI panel, chart, board, plan or layout. <code>sync(props)</code> builds it once everything it needs has arrived, pushes changed props into the live instance, and rebuilds it only when it is bound to a <em>different</em> grid.</td></tr>
965
+ <tr><td class="name">bindTabs</td><td class="sig">({ createTabs, element, createGrid, onPanels })</td><td class="desc">Owns one tab strip and claims a panel for each tab the host rendered content for. <code>place(id, holder)</code> moves that tab's <code>display: contents</code> holder into the panel — Svelte has no portal, and moving one container is what keeps the subtree a genuine child of the component that declared it.</td></tr>
966
+ <tr><td class="name">createGridRegistry</td><td class="sig">()</td><td class="desc">The registry <code>GridProvider.svelte</code> puts in context: <code>publish</code>, <code>get</code>, <code>names</code> and a <code>subscribe</code> a component turns into its own reactivity.</td></tr>
967
+ <tr><td class="name">createRouterHandle</td><td class="sig">({ createDataRouter, config })</td><td class="desc">A Data Router built on the first grid that asks to be routed and destroyed with the component that provided it. <code>config</code> may be a function, read at build time.</td></tr>
968
+ <tr><td class="name">adaptProps</td><td class="sig">(kind, props)</td><td class="desc">Splits a component's props into the instance's configuration and the adapter's own, dropping <code>class</code>/<code>style</code>/<code>id</code>, resolving <code>row-key</code>-style aliases, and normalising a callback prop written either way.</td></tr>
969
+ <tr><td class="name">pickCallback</td><td class="sig">(props, name)</td><td class="desc">A callback prop under either spelling: <code>onGridReady</code> or <code>ongridready</code>.</td></tr>
970
+ <tr><td class="name">viewerMount</td><td class="sig">(viewer, factory)</td><td class="desc">The call each module's factory actually takes — three want <code>(element, config)</code>, the chart wants a <code>container</code> in its options and the Gantt an <code>element</code>.</td></tr>
971
+ <tr><td class="name">snippetTabIds</td><td class="sig">(tabs, props)</td><td class="desc">Which tabs the host declared a snippet for, in declaration order.</td></tr>
972
+ <tr><td class="name">VIEWER_BINDING</td><td class="sig">Record&lt;string, {…}&gt;</td><td class="desc">How each viewer is wired: its label, the config keys without which nothing is built, and the key its bound grid is passed under.</td></tr>
973
+ <tr><td class="name">GRID_REGISTRY_KEY<br>ROUTER_KEY</td><td class="sig">symbol</td><td class="desc">The context keys the registry and the router handle are published under. Registered symbols (<code>Symbol.for</code>), so two components imported from different files meet the same registry.</td></tr>
974
+ </tbody>
975
+ </table>
976
+ </div>
977
+ <pre data-run="js" data-expect="config rowKey,onCellChanged; own main; registry main; grid dark; tabs one; viewers kpi,chart,kanban,gantt,layout; router 1" data-covers="export:bindGrid export:bindViewer export:bindTabs export:createGridRegistry export:createRouterHandle export:adaptProps export:pickCallback export:viewerMount export:snippetTabIds export:VIEWER_BINDING export:GRID_REGISTRY_KEY export:ROUTER_KEY"><code><span class="kw">const</span> S = <span class="kw">await</span> import('../packages/modules/svelte/index.js');
978
+
979
+ <span class="cmt">// A component hands its props straight to the helpers. They keep the</span>
980
+ <span class="cmt">// adapter's own props out of the instance and accept a callback under</span>
981
+ <span class="cmt">// either Svelte 5 spelling.</span>
982
+ <span class="kw">const</span> split = S.adaptProps('grid', {
983
+ rowKey: 'id', 'class': 'grid', name: 'main', oncellchanged: () =&gt; {},
984
+ });
985
+ <span class="kw">const</span> ready = S.pickCallback({ ongridready: () =&gt; 'called' }, 'onGridReady');
986
+
987
+ <span class="cmt">// The registry a &lt;GridProvider&gt; puts in context, and the grid binding a</span>
988
+ <span class="cmt">// &lt;Grid&gt; drives. Both are plain functions: no Svelte here at all.</span>
989
+ <span class="kw">const</span> registry = S.createGridRegistry();
990
+ <span class="kw">const</span> applied = [];
991
+ <span class="kw">const</span> fakeGrid = () =&gt; ({
992
+ setAll: (c) =&gt; applied.push(c.theme), destroy() {},
993
+ on: () =&gt; () =&gt; {}, rows: { apply: () =&gt; {} },
994
+ filters: { where: () =&gt; {} },
995
+ });
996
+ <span class="kw">const</span> bound = S.bindGrid({
997
+ createGrid: fakeGrid, element: {}, registry, props: { name: 'main', theme: 'light' },
998
+ });
999
+ bound.update({ name: 'main', theme: 'dark' });
1000
+
1001
+ <span class="cmt">// A viewer waits for its grid, then is built once. VIEWER_BINDING says</span>
1002
+ <span class="cmt">// which key its grid arrives under and what it cannot be built without.</span>
1003
+ <span class="kw">const</span> built = [];
1004
+ <span class="kw">const</span> stub = () =&gt; ({ on: () =&gt; () =&gt; {}, setRows: () =&gt; {}, update: () =&gt; {}, destroy() {} });
1005
+ <span class="kw">for</span> (<span class="kw">const</span> viewer <span class="kw">of</span> Object.keys(S.VIEWER_BINDING)) {
1006
+ <span class="kw">const</span> panel = S.bindViewer({
1007
+ viewer, element: {}, registry,
1008
+ factory: (...args) =&gt; { built.push(viewer); <span class="kw">return</span> stub(...args); },
1009
+ });
1010
+ panel.sync({ gridName: 'main' });
1011
+ panel.destroy();
1012
+ }
1013
+
1014
+ <span class="cmt">// A tab strip claims a panel for each tab the host wrote a snippet for.</span>
1015
+ <span class="kw">const</span> tabs = [{ id: 'one' }, { id: 'two' }];
1016
+ <span class="kw">const</span> props = { one: () =&gt; {} };
1017
+ <span class="kw">const</span> strip = S.bindTabs({
1018
+ element: {},
1019
+ createTabs: (el, config) =&gt; ({ config, on: () =&gt; () =&gt; {}, destroy() {} }),
1020
+ });
1021
+ strip.sync({ tabs, contentIds: S.snippetTabIds(tabs, props) });
1022
+ <span class="kw">const</span> claimed = strip.instance.config.tabs.filter((t) =&gt; t.view).map((t) =&gt; t.id);
1023
+
1024
+ <span class="cmt">// One router, built by the first grid that asks and destroyed with the</span>
1025
+ <span class="cmt">// component that provided it.</span>
1026
+ <span class="kw">let</span> routers = 0;
1027
+ <span class="kw">const</span> handle = S.createRouterHandle({
1028
+ createDataRouter: () =&gt; { routers += 1; <span class="kw">return</span> { attach: () =&gt; {}, detach: () =&gt; {}, destroy: () =&gt; {} }; },
1029
+ });
1030
+ handle.attach({}, 'all'); handle.attach({}, 'all'); handle.destroy();
1031
+
1032
+ <span class="cmt">// And the call each module's factory actually takes.</span>
1033
+ S.viewerMount('chart', (opts) =&gt; opts)({}, { type: 'bar' });
1034
+ <span class="kw">void</span> [S.GRID_REGISTRY_KEY, S.ROUTER_KEY, ready()];
1035
+
1036
+ <span class="kw">return</span> `config ${Object.keys(split.config).join(',')}; own ${split.own.name}; `
1037
+ + `registry ${registry.names().join(',')}; grid ${applied.join(',')}; `
1038
+ + `tabs ${claimed.join(',')}; viewers ${built.join(',')}; router ${routers}`;</code></pre>
1039
+
1040
+ <p class="section-note">
1041
+ <strong>Events are callback props.</strong> Svelte 5 removed component events, so
1042
+ <code>cell:changed</code> arrives as <code>onCellChanged</code> &mdash; the same name React
1043
+ uses. The all-lowercase <code>oncellchanged</code> is accepted too, for anyone who prefers
1044
+ Svelte's DOM attribute style. The grid's own dashed names (<code>cell-changed</code>) still
1045
+ apply to the <code>use:lattice</code> action, which dispatches <code>CustomEvent</code>s on
1046
+ the node.
1047
+ </p>
1048
+ <p class="section-note">
1049
+ <strong>Reactivity, and rows.</strong> The grid paints outside Svelte's scheduler: it owns
1050
+ its element and its own render loop, and nothing inside it is reactive state. Hold large row
1051
+ arrays in <code>$state.raw</code> (or in an ordinary <code>let</code>), never in deep
1052
+ <code>$state</code> &mdash; a proxy over a million rows costs far more than the reload it
1053
+ would save, and the components diff by identity, so a new array is what signals a change.
1054
+ An inline literal prop (<code>columns={[…]}</code>) is a new array on every render and
1055
+ reconfigures the viewer every time; hoist it.
1056
+ </p>
1057
+ <p class="section-note">
1058
+ <strong>Cleanup, and no SSR.</strong> Every component builds its instance in
1059
+ <code>onMount</code> and destroys it in <code>onDestroy</code>, unsubscribing its listeners,
1060
+ withdrawing its grid from the registry and detaching from the router before the grid goes.
1061
+ Nothing is built during server rendering &mdash; SvelteKit SSR emits the empty host element
1062
+ and nothing else &mdash; so these components are browser-only by construction.
1063
+ </p>
1064
+
1065
+ <h3 id="angular-v2">Angular: a compiled entry point, <code>@toclocoinc/lattice-grid/angular</code></h3>
1066
+ <p>
1067
+ Angular's components are not objects a library can assemble at run time in a production
1068
+ build. <code>modules/angular</code> did assemble them that way, which needs Angular's JIT
1069
+ compiler in the page &mdash; present on a development server, absent from every AOT build.
1070
+ So Angular gets an entry point of its own: TypeScript components compiled by
1071
+ <code>@angular/compiler-cli</code> into a partial-Ivy library, which your build's Angular
1072
+ Linker turns into definitions exactly as it does for any other Angular library you install.
1073
+ <strong>No compiler in your bundle, and one standalone component per viewer.</strong>
1074
+ </p>
1075
+ <p>
1076
+ It ships <em>inside</em> the grid package, at the subpath
1077
+ <code>@toclocoinc/lattice-grid/angular</code>: one install, one version number, one
1078
+ licence. <code>@angular/core</code> and <code>@angular/common</code> are optional peer
1079
+ dependencies of the package, so a project that is not an Angular one installs nothing extra
1080
+ and sees no warning.
1081
+ </p>
1082
+ <pre><code>npm install @toclocoinc/lattice-grid</code></pre>
1083
+ <pre><code><span class="kw">import</span> { bootstrapApplication } <span class="kw">from</span> '@angular/platform-browser';
1084
+ <span class="kw">import</span> { createGrid } <span class="kw">from</span> '@toclocoinc/lattice-grid';
1085
+ <span class="kw">import</span> { createKPI } <span class="kw">from</span> '@toclocoinc/lattice-grid/modules/kpi';
1086
+ <span class="kw">import</span> { createChart } <span class="kw">from</span> '@toclocoinc/lattice-grid/modules/charts';
1087
+ <span class="kw">import</span> {
1088
+ LatticeGridComponent, LatticeKpiComponent, LatticeChartComponent, provideLattice,
1089
+ } <span class="kw">from</span> '@toclocoinc/lattice-grid/angular';
1090
+
1091
+ @Component({
1092
+ selector: 'app-dashboard',
1093
+ imports: [LatticeGridComponent, LatticeKpiComponent, LatticeChartComponent],
1094
+ template: `
1095
+ &lt;lattice-grid #grid name="quakes" [config]="config" [quickFilter]="search()"
1096
+ (cell-changed)="save($event)" /&gt;
1097
+ &lt;lattice-kpi gridName="quakes" [config]="{ tiles }" /&gt;
1098
+ &lt;lattice-chart gridName="quakes" [config]="{ type: 'bar', x: 'region', y: 'count' }" /&gt;
1099
+ `,
1100
+ })
1101
+ <span class="kw">export class</span> Dashboard {
1102
+ <span class="cmt">// The live grid, the same object createGrid returns.</span>
1103
+ grid = viewChild&lt;LatticeGridComponent&gt;('grid');
1104
+ }
1105
+
1106
+ <span class="cmt">// Each factory is injected, not imported by the library: an application that</span>
1107
+ <span class="cmt">// shows a grid downloads the grid, and never the board or the eighteen charts.</span>
1108
+ bootstrapApplication(Dashboard, {
1109
+ providers: [provideLattice({ createGrid, createKPI, createChart })],
1110
+ });</code></pre>
1111
+
1112
+ <div class="table-wrap">
1113
+ <table>
1114
+ <thead><tr><th>Export</th><th>Element</th><th>What it is</th></tr></thead>
1115
+ <tbody>
1116
+ <tr><td class="name">LatticeGridComponent</td><td class="sig">&lt;lattice-grid&gt;</td><td class="desc">The grid. <code>[config]</code> is every configuration key; <code>[sort]</code>, <code>[filters]</code>, <code>[quickFilter]</code> and <code>[selectedKeys]</code> are applied through the matching API; <code>[rowUpdates]</code> and <code>[predicates]</code> drive a live feed. Every grid event is an output under its kebab-case name, and <code>(grid-ready)</code> hands you the instance. Give the element a height.</td></tr>
1117
+ <tr><td class="name">LatticeGridDirective</td><td class="sig">[latticeGrid]</td><td class="desc">The same component on an element your template already owns: <code>&lt;div [latticeGrid]="config" class="tall"&gt;&lt;/div&gt;</code>. Same inputs, outputs and <code>grid</code> reference.</td></tr>
1118
+ <tr><td class="name">LatticeKpiComponent</td><td class="sig">&lt;lattice-kpi&gt;</td><td class="desc">The KPI panel. Grid-bound by default &mdash; <code>[gridName]</code> picks which published grid &mdash; or give it <code>[rows]</code> for a panel with no grid.</td></tr>
1119
+ <tr><td class="name">LatticeChartComponent</td><td class="sig">&lt;lattice-chart&gt;</td><td class="desc">A chart. Requires a grid, so nothing is built until one exists; a changed spec key goes to <code>chart.update()</code> and the chart redraws rather than being rebuilt. <code>(click)</code>, <code>(hover)</code> and <code>(leave)</code> on this element are the chart's events, carrying the datum under the pointer.</td></tr>
1120
+ <tr><td class="name">LatticeKanbanComponent</td><td class="sig">&lt;lattice-kanban&gt;</td><td class="desc">The board. <code>[rows]</code>, <code>[quickFilter]</code>, <code>[sprint]</code>, <code>[epic]</code>, <code>[loading]</code> and <code>[error]</code> are live inputs.</td></tr>
1121
+ <tr><td class="name">LatticeGanttComponent</td><td class="sig">&lt;lattice-gantt&gt;</td><td class="desc">The plan. <code>[tasks]</code> and <code>[dependencies]</code> are live inputs. It takes an explicit <code>[grid]</code> but never adopts a published one.</td></tr>
1122
+ <tr><td class="name">LatticeLayoutComponent</td><td class="sig">&lt;lattice-layout&gt;</td><td class="desc">The dashboard layout. Windows are driven through the instance; its events arrive as <code>(layout-changed)</code>, <code>(window-moved)</code> and the rest.</td></tr>
1123
+ <tr><td class="name">LatticeTabsComponent<br>LatticeTabDirective</td><td class="sig">&lt;lattice-tabs&gt;<br>ng-template[latticeTab]</td><td class="desc">The tab strip, with <strong>Angular-rendered tab content</strong>: a tab's content is an <code>&lt;ng-template latticeTab="id"&gt;</code> in your own template, rendered into the strip's panel through this component's <code>ViewContainerRef</code> &mdash; so a tab's grid is a real <code>&lt;lattice-grid&gt;</code> with inputs, a reference and your injectors above it. A tab with no template is left to the module, so configuration-driven grid tabs still work and the two kinds mix on one strip.</td></tr>
1124
+ <tr><td class="name">LatticeGridRegistry</td><td class="sig">inject(LatticeGridRegistry)</td><td class="desc">Where <code>&lt;lattice-grid name="…"&gt;</code> publishes itself and grid-bound viewers find it, as a signal per name &mdash; a panel declared before its grid exists mounts itself the moment the grid arrives. <code>providedIn: 'root'</code>; put it in a component's own <code>providers</code> to scope a registry to that subtree.</td></tr>
1125
+ <tr><td class="name">provideLattice</td><td class="sig">provideLattice({ …factories })</td><td class="desc">The factories the components build through. Give it the ones your application uses; a component whose factory is missing names the import and the provider call that fixes it.</td></tr>
1126
+ <tr><td class="name">provideLatticeRouter<br>LatticeRouter</td><td class="sig">providers: [provideLatticeRouter(cfg)]</td><td class="desc">The data router as a service. Put it in a component's <code>providers</code> and it is created when the first <code>&lt;lattice-grid route="…"&gt;</code> under it attaches, and destroyed with that component &mdash; its configuration read once, because rebuilding would drop every attached grid and every row it holds. Each grid detaches before it is destroyed, so the router never holds a dead grid.</td></tr>
1127
+ </tbody>
1128
+ </table>
1129
+ </div>
1130
+
1131
+ <p class="section-note">
1132
+ <strong>Change detection: zone or zoneless, unconfigured.</strong> The grid is created inside
1133
+ <code>NgZone.runOutsideAngular</code>, because it installs its own scroll, wheel and pointer
1134
+ listeners and running change detection on every scroll frame of a million-row grid is the
1135
+ difference between smooth and unusable. An event that reaches an output you have
1136
+ <em>bound</em> re-enters the zone, so <code>(cell-changed)="count = count + 1"</code> repaints
1137
+ exactly as you expect; an output nobody bound costs nothing. Under zoneless change detection
1138
+ that machinery is Angular's own no-op and a signal you set in a handler repaints the view.
1139
+ The one thing to know when reading the grid's DOM from Angular: <strong>the grid paints on its
1140
+ own schedule, not Angular's</strong>, so measure a cell in a grid event, not in
1141
+ <code>ngAfterViewInit</code>.
1142
+ </p>
1143
+ <p class="section-note">
1144
+ <strong><code>OnPush</code> is safe everywhere.</strong> None of these components asks its
1145
+ parent to re-render: each owns one element, builds one instance in
1146
+ <code>afterNextRender</code> and pushes changed inputs into it from <code>ngOnChanges</code>.
1147
+ A host on <code>OnPush</code> that never re-renders still gets a fully live grid, because the
1148
+ grid is not rendered by Angular.
1149
+ </p>
1150
+ <p class="section-note">
1151
+ <strong>Inputs are diffed by identity, and never rebuild the instance.</strong> A changed
1152
+ input reaches the viewer that is already on screen &mdash; scroll position, selection,
1153
+ expansion and any open editor intact. Two things rebuild rather than update: the grid a
1154
+ viewer is bound to, and an input it cannot exist without. An input a viewer has no live
1155
+ setter for is named once in a warning rather than silently dropped. And because the
1156
+ comparison is <code>Object.is</code>, an inline <code>[config]="{ rows: rows }"</code> is a
1157
+ new object on every pass: hold it in a field or a signal.
1158
+ </p>
1159
+ <p class="section-note">
1160
+ <strong>Cleanup is the component's.</strong> <code>ngOnDestroy</code> detaches from the
1161
+ router, withdraws the grid from the registry and destroys the instance &mdash; in that order.
1162
+ An <code>&#64;if</code> that closes and opens again leaves exactly one instance alive, and
1163
+ destroying the application leaves no instance, interval or listener behind. That is asserted
1164
+ with counters in a real browser, against the linked package, in
1165
+ <code>test/angular-package-browser.test.js</code>.
1166
+ </p>
1167
+ <p class="section-note">
1168
+ <strong>Angular 17 and up, browser only.</strong> The library is compiled partially, so your
1169
+ own Angular version compiles it: its declarations need a linker no newer than 14, and its peer
1170
+ range is <code>&gt;=17</code>. Nothing is created on the server &mdash;
1171
+ <code>isPlatformBrowser</code> guards every build and <code>afterNextRender</code> does not
1172
+ run there &mdash; so a server-rendered page emits the empty host element and the grid is built
1173
+ on hydration. Angular Universal is not otherwise supported.
1174
+ </p>
1175
+ <p class="section-note">
1176
+ <strong><code>modules/angular</code> is deprecated.</strong> The old bundle still works where
1177
+ it always worked &mdash; a page with <code>@angular/compiler</code> loaded &mdash; and it now
1178
+ says so once, and fails with a <code>[lattice]</code> message naming this package when it
1179
+ finds a real Angular with no JIT compiler, instead of leaving you with Angular's own. In
1180
+ 1.65 it also gained the fix that <code>&lt;div [latticeGrid]="config"&gt;</code> binds the
1181
+ configuration through the directive's selector, as its documentation always said it did. It
1182
+ will be removed in a later release; move to <code>@toclocoinc/lattice-grid/angular</code>,
1183
+ which covers every viewer rather than the grid alone and needs no second install.
1184
+ </p>
1185
+
709
1186
  <div class="note">
710
1187
  <p><strong>Published as <code>@toclocoinc/lattice-grid</code></strong>: <code>npm install
711
1188
  @toclocoinc/lattice-grid</code>, then <code>import { createLatticeGrid } from
@@ -880,6 +1357,93 @@ defaults(<span class="kw">null</span>); <span class="cmt">// clea
880
1357
 
881
1358
  <p class="section-note">Events are re-dispatched as <code>CustomEvent</code>s named <code>lattice-</code> plus the grid name with colons hyphenated: <code>cell:changed</code> becomes <code>lattice-cell-changed</code>. The payload is on <code>event.detail</code>. The prefix avoids colliding with platform events, the grid emits one called <code>scroll</code>.</p>
882
1359
 
1360
+ <h3 id="webcomponent-v2">Web Components: every viewer, not only the grid</h3>
1361
+ <p class="section-note">Since 1.66 the same bundle registers one element per viewer, so a page
1362
+ without a bundler gets the KPI panel, a chart, the board, the Gantt, the dashboard layout, the
1363
+ tab strip and the Data Router as tags. The grid is already registered on import;
1364
+ <code>defineLatticeElements</code> registers the rest from the module factories the page has
1365
+ imported, because nothing is imported by this module — a page that draws no charts never loads
1366
+ the charts bundle.</p>
1367
+
1368
+ <pre data-run="js" data-expect="8" data-covers="export:ELEMENT_TAGS"><code><span class="cmt">// Every tag this package can register, under the default prefix.</span>
1369
+ <span class="kw">const</span> { ELEMENT_TAGS } = <span class="kw">await</span> import('../packages/modules/webcomponent/elements.js');
1370
+ <span class="kw">return</span> Object.keys(ELEMENT_TAGS).length;</code></pre>
1371
+
1372
+ <pre><code>&lt;script type="module"&gt;
1373
+ import { createKPI } from './dist/modules/kpi.esm.js';
1374
+ import { createChart } from './dist/modules/charts.esm.js';
1375
+ import { defineLatticeElements } from './dist/modules/webcomponent.esm.js';
1376
+ defineLatticeElements({ createKPI, createChart });
1377
+ &lt;/script&gt;
1378
+
1379
+ &lt;lattice-grid name="quakes" row-key="id"&gt;&lt;/lattice-grid&gt;
1380
+ &lt;lattice-kpi grid-name="quakes"&gt;&lt;/lattice-kpi&gt;
1381
+ &lt;lattice-chart grid-name="quakes" type="bar" x="place" y="mag"&gt;&lt;/lattice-chart&gt;</code></pre>
1382
+
1383
+ <div class="table-wrap">
1384
+ <table>
1385
+ <thead><tr><th>Tag</th><th>Viewer</th><th>Notes</th></tr></thead>
1386
+ <tbody>
1387
+ <tr><td class="sig">&lt;lattice-grid&gt;</td><td class="type">createGrid</td><td class="desc">Registered on import. Gains <code>name</code>, <code>route</code>, <code>route-options</code>, the <code>predicates</code> and <code>rowUpdates</code> properties, and <code>grid-ready</code> / <code>grid-destroyed</code>.</td></tr>
1388
+ <tr><td class="sig">&lt;lattice-kpi&gt;</td><td class="type">createKPI</td><td class="desc">Grid-bound by default; set <code>rows</code> for a standalone panel. <code>tiles</code> is a property. Like every grid-bound viewer it <strong>waits</strong> for its grid rather than mounting empty, so a panel written above its grid is built once, when the grid arrives.</td></tr>
1389
+ <tr><td class="sig">&lt;lattice-chart&gt;</td><td class="type">createChart</td><td class="desc">Always drawn from a grid, so it waits for one. Every other attribute is the chart spec and a change goes into <code>chart.update()</code>.</td></tr>
1390
+ <tr><td class="sig">&lt;lattice-kanban&gt;</td><td class="type">createKanban</td><td class="desc">Board. <code>rows</code>, <code>columns</code>, <code>card</code>, <code>facets</code> are properties.</td></tr>
1391
+ <tr><td class="sig">&lt;lattice-gantt&gt;</td><td class="type">createGantt</td><td class="desc"><code>tasks</code> and <code>dependencies</code> are properties and both are live.</td></tr>
1392
+ <tr><td class="sig">&lt;lattice-layout&gt;</td><td class="type">createLayout</td><td class="desc"><code>windows</code> is a property.</td></tr>
1393
+ <tr><td class="sig">&lt;lattice-tabs&gt;</td><td class="type">createTabs</td><td class="desc">A tab's content is a <code>&lt;template data-tab="&#8249;id&#8250;"&gt;</code> child, cloned into the panel the module creates. With no <code>tabs</code> property the descriptors are read from the templates. A tab with no template is left to the module; the two kinds mix.</td></tr>
1394
+ <tr><td class="sig">&lt;lattice-router&gt;</td><td class="type">createDataRouter</td><td class="desc">A grouping element. Owns one Data Router, built by the first <code>&lt;lattice-grid route="&#8230;"&gt;</code> beneath it and destroyed on disconnect, and the name registry its descendants resolve <code>grid-name</code> through.</td></tr>
1395
+ </tbody>
1396
+ </table>
1397
+ </div>
1398
+
1399
+ <p class="section-note"><strong>Attributes for scalars, properties for structures.</strong> An
1400
+ attribute is a string, so <code>columns</code>, <code>rows</code>, <code>tiles</code>,
1401
+ <code>tasks</code>, <code>windows</code> and a geometry pack are properties. Scalars work as
1402
+ either: an empty attribute is <code>true</code>, <code>true</code>/<code>false</code> are
1403
+ booleans, a finite number is a number, a value starting <code>[</code> or <code>{</code> is
1404
+ JSON, and anything else is the string — so use the property when the type matters. On the viewer
1405
+ elements a hyphenated attribute reaches the camelCase key (<code>column-property</code> &rarr;
1406
+ <code>columnProperty</code>). Every configuration key has a real property, including one no
1407
+ table here names: assign it and the element adopts it. <code>&lt;lattice-grid&gt;</code> keeps
1408
+ its own attribute table above &mdash; each of those is a property too
1409
+ (<code>el.theme</code>, <code>el.rowKey</code>) &mdash; and takes everything outside it through
1410
+ <code>config</code>.</p>
1411
+
1412
+ <p class="section-note"><strong>Upgrade order.</strong> A property assigned before the element
1413
+ upgrades is honoured. <strong>Set your configuration before the element connects</strong> —
1414
+ <code>document.createElement</code>, assign, then append — so the viewer is built once with
1415
+ everything it needs rather than built empty and reconfigured; in markup, use the JSON attribute
1416
+ forms. <strong>Cleanup</strong> is on disconnect, deferred by a microtask so moving an element in
1417
+ the DOM keeps its instance rather than rebuilding it. <strong>SSR</strong>: every element owns a
1418
+ real DOM node, so nothing is built until it is connected in a browser; a server render emits the
1419
+ empty tag and no more.</p>
1420
+
1421
+ <p class="section-note"><strong>Events.</strong> Instance events are <code>CustomEvent</code>s
1422
+ named <code>lattice-</code> plus the event name with colons hyphenated
1423
+ (<code>card:move</code> &rarr; <code>lattice-card-move</code>); the prefix is load-bearing,
1424
+ because the grid emits <code>scroll</code> and a chart emits <code>click</code>. The lifecycle
1425
+ events are this adapter's own and are unprefixed: <code>grid-ready</code> /
1426
+ <code>grid-destroyed</code> on the grid and <code>ready</code> / <code>destroyed</code> on every
1427
+ viewer, each carrying the instance in <code>detail</code>. The instance is also on
1428
+ <code>el.grid</code> / <code>el.instance</code>.</p>
1429
+
1430
+ <h4>Inside a shadow root</h4>
1431
+ <p class="section-note">The elements are light DOM, deliberately. A <em>host</em> that puts them
1432
+ inside a shadow root of its own is a different matter: the theme, each module's injected
1433
+ stylesheet and the grid's generated one all live in <code>document.head</code> and none of them
1434
+ crosses a shadow boundary, so a grid in there used to render structurally perfect and completely
1435
+ unstyled. Every element now calls <code>adoptLatticeStyles</code> on its own root when that root
1436
+ is a shadow root, mirroring each sheet as a constructable <code>CSSStyleSheet</code> and keeping
1437
+ it level as the grid compiles new classes and as a module injects its own. A host that manages
1438
+ its own <code>adoptedStyleSheets</code> can take the sheets directly with
1439
+ <code>latticeStyleSheets()</code>; <code>lattice-styles="off"</code> on an element opts out. A
1440
+ stylesheet that cannot be read as text — a cross-origin <code>&lt;link&gt;</code>, or one
1441
+ carrying <code>@import</code> — is cloned into the root as a <code>&lt;link&gt;</code> instead.</p>
1442
+
1443
+ <pre><code><span class="kw">const</span> root = host.attachShadow({ mode: 'open' });
1444
+ adoptLatticeStyles(root);
1445
+ root.append(document.createElement('lattice-grid'));</code></pre>
1446
+
883
1447
  <h2 id="dhtmlx-compat">dhtmlx Grid compatibility wrapper</h2>
884
1448
  <p class="section-note">A <code>Grid</code> class shaped like dhtmlx's own <code>dhx.Grid</code> (Suite 5+), backed by a real Lattice grid. Covers column definitions, <code>.data</code>, <code>.selection</code>, <code>.history</code>, <code>.export.csv</code>/<code>.xlsx</code>, and a name-mapped subset of <code>.events</code>: see <a href="api-detail.html#dhtmlx-guide">the guide</a> for exactly what is and is not covered. Not the classic pre-Suite-5 <code>dhtmlXGridObject</code>.</p>
885
1449
 
@@ -1393,7 +1957,7 @@ grid.destroy();
1393
1957
  <table>
1394
1958
  <thead><tr><th>Member</th><th>Returns</th><th>Description</th></tr></thead>
1395
1959
  <tbody>
1396
- <tr><td class="sig">getVersion()</td><td class="type">string</td><td class="desc">The version this grid came from, e.g. <code>'1.64.0'</code>. Also on the module as <code>getVersion()</code>, for when you have no grid to hand.</td></tr>
1960
+ <tr><td class="sig">getVersion()</td><td class="type">string</td><td class="desc">The version this grid came from, e.g. <code>'1.66.0'</code>. Also on the module as <code>getVersion()</code>, for when you have no grid to hand.</td></tr>
1397
1961
  <tr><td class="sig">get(key)</td><td class="type">unknown</td><td class="desc">Read any configuration key.</td></tr>
1398
1962
  <tr><td class="sig">set(key, value)</td><td class="type">void</td><td class="desc">Write one key. Every key is live; nothing needs a rebuild.</td></tr>
1399
1963
  <tr><td class="sig">setAll(values)</td><td class="type">void</td><td class="desc">Write several in one pass. Emits one <code>config:changed</code> for the batch, not one per key.</td></tr>
@@ -8074,7 +8638,7 @@ spans.addSpan('R0', 'name', 3, 2); <span class="cmt">// rowspan 3, colspan 2</sp
8074
8638
 
8075
8639
  <h3 id="module-exports-example">Every module export, executed</h3>
8076
8640
  <p class="section-note">Every shipped module’s exports, resolved against its own barrel on every build.</p>
8077
- <pre data-run="js" data-expect="68" 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:chartRange export:canChartRange export:deriveRangeSpec export:regressionPlots 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 export:createDataRouter export:MockWebSocket export:rng export:opsFeed export:priceFeed export:createKanban export:pack"><code><span class="cmt">// Every declared export of every shipped module, resolved against its own</span>
8641
+ <pre data-run="js" data-expect="80" 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:chartRange export:canChartRange export:deriveRangeSpec export:regressionPlots 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:createLatticeElements export:defineLatticeElements export:ELEMENT_TAGS export:createLatticeKPIElement export:createLatticeChartElement export:createLatticeKanbanElement export:createLatticeGanttElement export:createLatticeLayoutElement export:createLatticeTabsElement export:createLatticeRouterElement export:adoptLatticeStyles export:latticeStyleSheets export:CONSOLE_ACTIVATION export:createDevtools export:expose export:EVENT_NAMES export:handlerName export:createLatticeGrid export:dashedName export:createLatticeAction export:Grid export:warnIfLargeHtmlPayload export:createDataRouter export:MockWebSocket export:rng export:opsFeed export:priceFeed export:createKanban export:pack"><code><span class="cmt">// Every declared export of every shipped module, resolved against its own</span>
8078
8642
  <span class="cmt">// barrel. A module that stopped exporting something fails here.</span>
8079
8643
  <span class="kw">const</span> modules = [
8080
8644
  [<span class="kw">await</span> import('../packages/dom/src/index.js'), [
@@ -8099,6 +8663,10 @@ spans.addSpan('R0', 'name', 3, 2); <span class="cmt">// rowspan 3, colspan 2</sp
8099
8663
  'ATTRIBUTE_CONFIG', 'EVENT_PREFIX', 'GridElementController',
8100
8664
  'TAG_NAME', 'createLatticeGridElement', 'defineLatticeGrid', 'domEventName',
8101
8665
  'observedAttributeNames',
8666
+ 'createLatticeElements', 'defineLatticeElements', 'ELEMENT_TAGS',
8667
+ 'createLatticeKPIElement', 'createLatticeChartElement', 'createLatticeKanbanElement',
8668
+ 'createLatticeGanttElement', 'createLatticeLayoutElement', 'createLatticeTabsElement',
8669
+ 'createLatticeRouterElement', 'adoptLatticeStyles', 'latticeStyleSheets',
8102
8670
  ]],
8103
8671
  [<span class="kw">await</span> import('../packages/modules/devtools/index.js'), [
8104
8672
  'CONSOLE_ACTIVATION', 'createDevtools', 'expose',
@@ -8415,6 +8983,18 @@ grid.destroy();
8415
8983
  <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>
8416
8984
  <tr><td class="name">observedAttributeNames</td><td class="type">() =&gt; string[]</td><td class="desc">The attributes the element reacts to.</td></tr>
8417
8985
  <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>
8986
+ <tr><td class="name">defineLatticeElements</td><td class="type">(opts?) =&gt; Record&lt;string, class&gt;</td><td class="desc">Register one element per viewer from the factories you pass, under <code>prefix</code> (<code>lattice-</code> by default). Safe to call twice. See <a href="#webcomponent-v2">Web Components</a>.</td></tr>
8987
+ <tr><td class="name">createLatticeElements</td><td class="type">(deps?) =&gt; Record&lt;string, class&gt;</td><td class="desc">The classes, without registering them.</td></tr>
8988
+ <tr><td class="name">ELEMENT_TAGS</td><td class="type">Readonly&lt;Record&lt;string, string&gt;&gt;</td><td class="desc">The tag each viewer takes, before the prefix.</td></tr>
8989
+ <tr><td class="name">createLatticeKPIElement</td><td class="type">({ createKPI }) =&gt; class</td><td class="desc">The <code>&lt;lattice-kpi&gt;</code> class on its own.</td></tr>
8990
+ <tr><td class="name">createLatticeChartElement</td><td class="type">({ createChart }) =&gt; class</td><td class="desc">The <code>&lt;lattice-chart&gt;</code> class on its own.</td></tr>
8991
+ <tr><td class="name">createLatticeKanbanElement</td><td class="type">({ createKanban }) =&gt; class</td><td class="desc">The <code>&lt;lattice-kanban&gt;</code> class on its own.</td></tr>
8992
+ <tr><td class="name">createLatticeGanttElement</td><td class="type">({ createGantt }) =&gt; class</td><td class="desc">The <code>&lt;lattice-gantt&gt;</code> class on its own.</td></tr>
8993
+ <tr><td class="name">createLatticeLayoutElement</td><td class="type">({ createLayout }) =&gt; class</td><td class="desc">The <code>&lt;lattice-layout&gt;</code> class on its own.</td></tr>
8994
+ <tr><td class="name">createLatticeTabsElement</td><td class="type">({ createTabs, createGrid? }) =&gt; class</td><td class="desc">The <code>&lt;lattice-tabs&gt;</code> class on its own.</td></tr>
8995
+ <tr><td class="name">createLatticeRouterElement</td><td class="type">({ createDataRouter }) =&gt; class</td><td class="desc">The <code>&lt;lattice-router&gt;</code> class on its own.</td></tr>
8996
+ <tr><td class="name">adoptLatticeStyles</td><td class="type">(root, opts?) =&gt; () =&gt; void</td><td class="desc">Make a shadow root see Lattice's styles, and keep it seeing them as more are generated. Returns the undo.</td></tr>
8997
+ <tr><td class="name">latticeStyleSheets</td><td class="type">(opts?) =&gt; CSSStyleSheet[]</td><td class="desc">Those stylesheets as constructable sheets, for a host managing its own <code>adoptedStyleSheets</code>. Shared and live; do not mutate them.</td></tr>
8418
8998
  </tbody>
8419
8999
  </table>
8420
9000
  </div>
@@ -11083,6 +11663,7 @@ return `next ${p.mean.toFixed(1)}; r2 ${f.r2.toFixed(1)}; band ${p.upper - p.low
11083
11663
  <tr><td class="name">pipes</td><td class="type">Record&lt;string, (value: unknown, ...args: string[]) =&gt; string&gt;</td><td class="desc">Named text transforms usable from a format mask or a template. <small>(optional)</small></td></tr>
11084
11664
  <tr><td class="name">totalFns</td><td class="type">Record&lt;string, TotalFn&gt;</td><td class="desc">Your own reductions, alongside the built-in ones. <small>(optional)</small></td></tr>
11085
11665
  <tr><td class="name">variants</td><td class="type">Record&lt;string, VariantDefinition&gt;</td><td class="desc">Named appearance variants a row or cell can be switched into by a rule. <small>(optional)</small></td></tr>
11666
+ <tr><td class="name">icons</td><td class="type">Record&lt;string, IconDefinition&gt;</td><td class="desc">Your own SVG glyphs, registered by name before the first paint. The same registry `registerIcon` writes to and every cell, header control, rail button and chart glyph is painted from, so a name given here is usable anywhere a glyph name is: a column's `icon` decoration, a rail action's `icon`, a network node's `icon`. Registering a built-in name overrides it. Read the result back through {@link Grid.icons}. <small>(optional)</small></td></tr>
11086
11667
  <tr><td class="name">tree</td><td class="type">TreeConfig</td><td class="desc">Hierarchical rows: where the parent link or the path lives. <small>(optional)</small></td></tr>
11087
11668
  <tr><td class="name">detail</td><td class="type">DetailConfig</td><td class="desc">The expandable panel beneath a row. <small>(optional)</small></td></tr>
11088
11669
  <tr><td class="name">selection</td><td class="type">SelectionConfig | 'single' | 'multiple' | 'none'</td><td class="desc">What the user may select, and how selection behaves across groups. The `'none'` shorthand is `{ mode: 'none' }` and behaves identically: no row selection, and no cell ranges or fill handle either. <small>(optional)</small></td></tr>
@@ -11162,7 +11743,7 @@ return `next ${p.mean.toFixed(1)}; r2 ${f.r2.toFixed(1)}; band ${p.upper - p.low
11162
11743
  <tr><td class="name">rowClass</td><td class="type">string | string[] | ((p: RowStyleParams) =&gt; string | string[])</td><td class="desc">A class, or classes, for every row. Re-evaluated on each repaint. <small>(optional)</small></td></tr>
11163
11744
  <tr><td class="name">rowStyle</td><td class="type">CellStyle | ((p: RowStyleParams) =&gt; CellStyle)</td><td class="desc">Inline styles for every row. Camel-case or hyphenated property names. <small>(optional)</small></td></tr>
11164
11745
  <tr><td class="name">toolPanel</td><td class="type">boolean | {</td><td class="desc"><small>(optional)</small></td></tr>
11165
- <tr><td class="name">groupPanel</td><td class="type">boolean | {</td><td class="desc">A drag-and-drop group-by strip above the column header — the pattern AG Grid calls the row-group panel. Drag a column heading into it to group by that column; the active groups show as removable, reorderable chips, and reordering the chips changes the nesting order. It is keyboard-operable (arrows navigate, Shift+arrow reorders, Delete ungroups, and an add control groups any column), and every change is announced through the live region, which is why it also addresses the drag-only complaint of BACKLOG-0000429. Off by default and non-breaking, matching `toolPanel`. It drives the same grouping model as `grid.columns.group()`; it reimplements nothing. <small>(optional)</small></td></tr>
11746
+ <tr><td class="name">groupPanel</td><td class="type">boolean | {</td><td class="desc">A drag-and-drop group-by strip above the column header — the pattern AG Grid calls the row-group panel. Drag a column heading into it to group by that column; the active groups show as removable, reorderable chips, and reordering the chips changes the nesting order. It is keyboard-operable (arrows navigate, Shift+arrow reorders, Delete ungroups, and an add control groups any column), and every change is announced through the live region, which is why it also addresses the drag-only complaint (BACKLOG-0000429). Off by default and non-breaking, matching `toolPanel`. It drives the same grouping model as `grid.columns.group()`; it reimplements nothing. <small>(optional)</small></td></tr>
11166
11747
  <tr><td class="name">kpis</td><td class="type">Array&lt;Omit&lt;StatConfig, 'grid' | 'container'&gt;&gt;</td><td class="desc">A built-in KPI/stat strip: a labelled band of {@link createStat} tiles the grid places for you, above the column header. Each entry is a stat spec — the same fields {@link StatConfig} takes, minus `grid` and `container`, which the grid supplies — so a strip tile and a hand-placed one are the same object. The tiles follow the grid's filters, recomputing on every change exactly as a stand-alone stat does. Off by default and non-breaking, matching `groupPanel`: no `kpis` means no band and no cost. It reuses `createStat` and reimplements no compute. <small>(optional)</small></td></tr>
11167
11748
  <tr><td class="name">quickFilterText</td><td class="type">string</td><td class="desc">The quick filter's initial text. <small>(optional)</small></td></tr>
11168
11749
  <tr><td class="name">permissions</td><td class="type">PermissionPolicy</td><td class="desc">Per-column read/write/hidden policy. A usability control, not a security boundary: hidden data is still resident in the store. Enforce the same policy server-side with `permittedColumns` / `permittedExport`. <small>(optional)</small></td></tr>
@@ -12048,6 +12629,71 @@ return `next ${p.mean.toFixed(1)}; r2 ${f.r2.toFixed(1)}; band ${p.upper - p.low
12048
12629
  </tbody>
12049
12630
  </table>
12050
12631
  </div>
12632
+ <h3 id="type-LatticeChartElement">LatticeChartElement</h3>
12633
+ <p class="section-note">`&lt;lattice-chart&gt;`. Always drawn from a grid, so it waits for one.</p>
12634
+ <div class="table-wrap">
12635
+ <table>
12636
+ <thead><tr><th>Member</th><th>Type</th><th>Description</th></tr></thead>
12637
+ <tbody>
12638
+ <tr><td class="name">shapes</td><td class="type">unknown</td><td class="desc"></td></tr>
12639
+ <tr><td class="name">series</td><td class="type">unknown</td><td class="desc"></td></tr>
12640
+ <tr><td class="name">data</td><td class="type">unknown</td><td class="desc"></td></tr>
12641
+ <tr><td class="name">formatting</td><td class="type">unknown</td><td class="desc"></td></tr>
12642
+ <tr><td class="name">type</td><td class="type">string | undefined</td><td class="desc"></td></tr>
12643
+ <tr><td class="name">x</td><td class="type">string | undefined</td><td class="desc"></td></tr>
12644
+ <tr><td class="name">y</td><td class="type">string | string[] | undefined</td><td class="desc"></td></tr>
12645
+ <tr><td class="name">value</td><td class="type">string | undefined</td><td class="desc"></td></tr>
12646
+ <tr><td class="name">label</td><td class="type">string | undefined</td><td class="desc"></td></tr>
12647
+ <tr><td class="name">lon</td><td class="type">string | undefined</td><td class="desc"></td></tr>
12648
+ <tr><td class="name">lat</td><td class="type">string | undefined</td><td class="desc"></td></tr>
12649
+ <tr><td class="name">labels</td><td class="type">boolean | undefined</td><td class="desc"></td></tr>
12650
+ <tr><td class="name">stacked</td><td class="type">boolean | undefined</td><td class="desc"></td></tr>
12651
+ <tr><td class="name">horizontal</td><td class="type">boolean | undefined</td><td class="desc"></td></tr>
12652
+ <tr><td class="name">scheme</td><td class="type">string | undefined</td><td class="desc"></td></tr>
12653
+ </tbody>
12654
+ </table>
12655
+ </div>
12656
+ <h3 id="type-LatticeGanttElement">LatticeGanttElement</h3>
12657
+ <p class="section-note">`&lt;lattice-gantt&gt;`.</p>
12658
+ <div class="table-wrap">
12659
+ <table>
12660
+ <thead><tr><th>Member</th><th>Type</th><th>Description</th></tr></thead>
12661
+ <tbody>
12662
+ <tr><td class="name">tasks</td><td class="type">unknown[]</td><td class="desc"></td></tr>
12663
+ <tr><td class="name">dependencies</td><td class="type">unknown[]</td><td class="desc"></td></tr>
12664
+ <tr><td class="name">resources</td><td class="type">unknown</td><td class="desc"></td></tr>
12665
+ <tr><td class="name">calendar</td><td class="type">unknown</td><td class="desc"></td></tr>
12666
+ <tr><td class="name">columns</td><td class="type">unknown</td><td class="desc"></td></tr>
12667
+ <tr><td class="name">rowKey</td><td class="type">string | undefined</td><td class="desc"></td></tr>
12668
+ <tr><td class="name">scale</td><td class="type">string | undefined</td><td class="desc"></td></tr>
12669
+ <tr><td class="name">autoSchedule</td><td class="type">boolean | undefined</td><td class="desc"></td></tr>
12670
+ </tbody>
12671
+ </table>
12672
+ </div>
12673
+ <h3 id="type-LatticeGridElement">LatticeGridElement</h3>
12674
+ <p class="section-note">`&lt;lattice-grid&gt;`. Structures are properties, scalars are the attributes in the table above, and `config` merges anything without an attribute of its own. Instance events arrive as `CustomEvent`s named `lattice-` plus the event name with colons hyphenated; `grid-ready` and `grid-destroyed` are this adapter's own and carry {@link LatticeGridEventDetail}.</p>
12675
+ <div class="table-wrap">
12676
+ <table>
12677
+ <thead><tr><th>Member</th><th>Type</th><th>Description</th></tr></thead>
12678
+ <tbody>
12679
+ <tr><td class="name">theme</td><td class="type">string | undefined</td><td class="desc">Every attribute in the table above is also a property of the same name.</td></tr>
12680
+ <tr><td class="name">density</td><td class="type">string | undefined</td><td class="desc"></td></tr>
12681
+ <tr><td class="name">locale</td><td class="type">string | undefined</td><td class="desc"></td></tr>
12682
+ <tr><td class="name">rowKey</td><td class="type">string | undefined</td><td class="desc"></td></tr>
12683
+ <tr><td class="name">rowHeight</td><td class="type">number | undefined</td><td class="desc"></td></tr>
12684
+ <tr><td class="name">headerHeight</td><td class="type">number | undefined</td><td class="desc"></td></tr>
12685
+ <tr><td class="name">autoHeight</td><td class="type">boolean | undefined</td><td class="desc"></td></tr>
12686
+ <tr><td class="name">selection</td><td class="type">string | undefined</td><td class="desc"></td></tr>
12687
+ <tr><td class="name">grid</td><td class="type">Grid | null</td><td class="desc">The live grid, or null while the element is disconnected. <small>(read-only)</small></td></tr>
12688
+ <tr><td class="name">instance</td><td class="type">Grid | null</td><td class="desc">The live grid, under the name every viewer element uses. <small>(read-only)</small></td></tr>
12689
+ <tr><td class="name">rows</td><td class="type">Row[]</td><td class="desc">The row data.</td></tr>
12690
+ <tr><td class="name">columns</td><td class="type">Column[]</td><td class="desc">The column definitions.</td></tr>
12691
+ <tr><td class="name">config</td><td class="type">GridConfig</td><td class="desc">Any configuration without an attribute of its own; merged, not replaced.</td></tr>
12692
+ <tr><td class="name">predicates</td><td class="type">Record&lt;string, ((row: Row) =&gt; boolean) | null&gt; | undefined</td><td class="desc">Named row predicates, registered through `grid.filters.where` — they compose with whatever filter the reader has set, unlike `filters`.</td></tr>
12693
+ <tr><td class="name">rowUpdates</td><td class="type">{ add?: Row[]; update?: Row[]; remove?: unknown[] } | undefined</td><td class="desc">A keyed diff applied straight to `grid.rows.apply()`.</td></tr>
12694
+ </tbody>
12695
+ </table>
12696
+ </div>
12051
12697
  <h3 id="type-LatticeGridHandle">LatticeGridHandle</h3>
12052
12698
  <p class="section-note">The live instance a `&lt;LatticeGrid&gt;` ref exposes; `null` before mount.</p>
12053
12699
  <div class="table-wrap">
@@ -12058,6 +12704,75 @@ return `next ${p.mean.toFixed(1)}; r2 ${f.r2.toFixed(1)}; band ${p.upper - p.low
12058
12704
  </tbody>
12059
12705
  </table>
12060
12706
  </div>
12707
+ <h3 id="type-LatticeKanbanElement">LatticeKanbanElement</h3>
12708
+ <p class="section-note">`&lt;lattice-kanban&gt;`. Grid-bound like the KPI panel; give it `rows` to stand alone.</p>
12709
+ <div class="table-wrap">
12710
+ <table>
12711
+ <thead><tr><th>Member</th><th>Type</th><th>Description</th></tr></thead>
12712
+ <tbody>
12713
+ <tr><td class="name">rows</td><td class="type">Row[]</td><td class="desc"></td></tr>
12714
+ <tr><td class="name">columns</td><td class="type">unknown[]</td><td class="desc"></td></tr>
12715
+ <tr><td class="name">card</td><td class="type">unknown</td><td class="desc"></td></tr>
12716
+ <tr><td class="name">facets</td><td class="type">unknown</td><td class="desc"></td></tr>
12717
+ <tr><td class="name">rowKey</td><td class="type">string | undefined</td><td class="desc"></td></tr>
12718
+ <tr><td class="name">columnProperty</td><td class="type">string | undefined</td><td class="desc"></td></tr>
12719
+ <tr><td class="name">titleProperty</td><td class="type">string | undefined</td><td class="desc"></td></tr>
12720
+ <tr><td class="name">swimlanes</td><td class="type">boolean | undefined</td><td class="desc"></td></tr>
12721
+ </tbody>
12722
+ </table>
12723
+ </div>
12724
+ <h3 id="type-LatticeKPIElement">LatticeKPIElement</h3>
12725
+ <p class="section-note">`&lt;lattice-kpi&gt;`. Grid-bound by default, and it **waits** for its grid rather than mounting empty; give it `rows` for a standalone panel.</p>
12726
+ <div class="table-wrap">
12727
+ <table>
12728
+ <thead><tr><th>Member</th><th>Type</th><th>Description</th></tr></thead>
12729
+ <tbody>
12730
+ <tr><td class="name">tiles</td><td class="type">unknown[]</td><td class="desc"></td></tr>
12731
+ <tr><td class="name">rows</td><td class="type">Row[]</td><td class="desc"></td></tr>
12732
+ <tr><td class="name">columns</td><td class="type">number | undefined</td><td class="desc"></td></tr>
12733
+ <tr><td class="name">rowKey</td><td class="type">string | undefined</td><td class="desc"></td></tr>
12734
+ <tr><td class="name">locale</td><td class="type">string | undefined</td><td class="desc"></td></tr>
12735
+ <tr><td class="name">theme</td><td class="type">string | undefined</td><td class="desc"></td></tr>
12736
+ <tr><td class="name">density</td><td class="type">string | undefined</td><td class="desc"></td></tr>
12737
+ <tr><td class="name">format</td><td class="type">unknown</td><td class="desc"></td></tr>
12738
+ </tbody>
12739
+ </table>
12740
+ </div>
12741
+ <h3 id="type-LatticeLayoutElement">LatticeLayoutElement</h3>
12742
+ <p class="section-note">`&lt;lattice-layout&gt;`.</p>
12743
+ <div class="table-wrap">
12744
+ <table>
12745
+ <thead><tr><th>Member</th><th>Type</th><th>Description</th></tr></thead>
12746
+ <tbody>
12747
+ <tr><td class="name">windows</td><td class="type">unknown[]</td><td class="desc"></td></tr>
12748
+ <tr><td class="name">columns</td><td class="type">number | undefined</td><td class="desc"></td></tr>
12749
+ <tr><td class="name">rows</td><td class="type">number | undefined</td><td class="desc"></td></tr>
12750
+ <tr><td class="name">compact</td><td class="type">string | undefined</td><td class="desc"></td></tr>
12751
+ </tbody>
12752
+ </table>
12753
+ </div>
12754
+ <h3 id="type-LatticeRouterElement">LatticeRouterElement</h3>
12755
+ <p class="section-note">`&lt;lattice-router&gt;`. Owns a Data Router for the life of the element, built by the first `&lt;lattice-grid route="…"&gt;` beneath it.</p>
12756
+ <div class="table-wrap">
12757
+ <table>
12758
+ <thead><tr><th>Member</th><th>Type</th><th>Description</th></tr></thead>
12759
+ <tbody>
12760
+ <tr><td class="name">router</td><td class="type">unknown</td><td class="desc">The live router, or null until something routes. <small>(read-only)</small></td></tr>
12761
+ <tr><td class="name">instance</td><td class="type">unknown</td><td class="desc">The live router, under the name every viewer element uses. <small>(read-only)</small></td></tr>
12762
+ <tr><td class="name">config</td><td class="type">Record&lt;string, unknown&gt;</td><td class="desc">The router configuration; read once, when the router is built.</td></tr>
12763
+ </tbody>
12764
+ </table>
12765
+ </div>
12766
+ <h3 id="type-LatticeTabsElement">LatticeTabsElement</h3>
12767
+ <p class="section-note">`&lt;lattice-tabs&gt;`. A tab's content is a `&lt;template data-tab="‹id›"&gt;` child, cloned into the panel the module creates for it; a tab with no template is left to the module.</p>
12768
+ <div class="table-wrap">
12769
+ <table>
12770
+ <thead><tr><th>Member</th><th>Type</th><th>Description</th></tr></thead>
12771
+ <tbody>
12772
+ <tr><td class="name">tabs</td><td class="type">unknown[]</td><td class="desc"></td></tr>
12773
+ </tbody>
12774
+ </table>
12775
+ </div>
12061
12776
  <h3 id="type-LatticeTabSpec">LatticeTabSpec</h3>
12062
12777
  <p class="section-note">One tab of a `&lt;LatticeTabs&gt;`; `content` makes it React's rather than the module's.</p>
12063
12778
  <div class="table-wrap">
@@ -12086,6 +12801,19 @@ return `next ${p.mean.toFixed(1)}; r2 ${f.r2.toFixed(1)}; band ${p.upper - p.low
12086
12801
  </tbody>
12087
12802
  </table>
12088
12803
  </div>
12804
+ <h3 id="type-LatticeViewerElement">LatticeViewerElement</h3>
12805
+ <p class="section-note">What every viewer element carries.</p>
12806
+ <div class="table-wrap">
12807
+ <table>
12808
+ <thead><tr><th>Member</th><th>Type</th><th>Description</th></tr></thead>
12809
+ <tbody>
12810
+ <tr><td class="name">instance</td><td class="type">Instance | null</td><td class="desc">The live viewer, or null before it is built. Every configuration key below is also a property, and an attribute of the same name is the same key. A key none of these interfaces declares works as a property too — the element adopts it — but is written through `config` where the declarations are the contract. <small>(read-only)</small></td></tr>
12811
+ <tr><td class="name">grid</td><td class="type">unknown</td><td class="desc">The grid this viewer is built against, when it is given one directly.</td></tr>
12812
+ <tr><td class="name">gridName</td><td class="type">string | undefined</td><td class="desc">Which published grid to bind to; the `grid-name` attribute in markup.</td></tr>
12813
+ <tr><td class="name">config</td><td class="type">Record&lt;string, unknown&gt;</td><td class="desc">Any configuration without a property of its own; merged, not replaced.</td></tr>
12814
+ </tbody>
12815
+ </table>
12816
+ </div>
12089
12817
  <h3 id="type-LatticeViewerHandle">LatticeViewerHandle</h3>
12090
12818
  <p class="section-note">The live instance a viewer component's ref exposes; `null` before mount.</p>
12091
12819
  <div class="table-wrap">
@@ -12096,6 +12824,61 @@ return `next ${p.mean.toFixed(1)}; r2 ${f.r2.toFixed(1)}; band ${p.upper - p.low
12096
12824
  </tbody>
12097
12825
  </table>
12098
12826
  </div>
12827
+ <h3 id="type-LatticeVueGridExposed">LatticeVueGridExposed</h3>
12828
+ <p class="section-note">What a `&lt;LatticeGrid&gt;` template ref exposes; `grid()` is null before mount.</p>
12829
+ <div class="table-wrap">
12830
+ <table>
12831
+ <thead><tr><th>Member</th><th>Type</th><th>Description</th></tr></thead>
12832
+ <tbody>
12833
+ <tr><td class="name">grid</td><td class="type">(): Grid | null</td><td class="desc"></td></tr>
12834
+ </tbody>
12835
+ </table>
12836
+ </div>
12837
+ <h3 id="type-LatticeVueGridRegistry">LatticeVueGridRegistry</h3>
12838
+ <p class="section-note">Where grids publish themselves so the viewers around them can find one.</p>
12839
+ <div class="table-wrap">
12840
+ <table>
12841
+ <thead><tr><th>Member</th><th>Type</th><th>Description</th></tr></thead>
12842
+ <tbody>
12843
+ <tr><td class="name">grids</td><td class="type">{ value: Readonly&lt;Record&lt;string, Grid&gt;&gt; }</td><td class="desc">A shallow ref of name → grid; replaced, never mutated, on each change.</td></tr>
12844
+ <tr><td class="name">publish</td><td class="type">(name: string, grid: Grid | null): void</td><td class="desc">Publish a grid under a name, or withdraw it with `null`.</td></tr>
12845
+ </tbody>
12846
+ </table>
12847
+ </div>
12848
+ <h3 id="type-LatticeVueRouterHandle">LatticeVueRouterHandle</h3>
12849
+ <p class="section-note">The Data Router handle a `provideLatticeRouter` puts in scope.</p>
12850
+ <div class="table-wrap">
12851
+ <table>
12852
+ <thead><tr><th>Member</th><th>Type</th><th>Description</th></tr></thead>
12853
+ <tbody>
12854
+ <tr><td class="name">router</td><td class="type">unknown</td><td class="desc">The live router, built the first time anything asks for it. <small>(read-only)</small></td></tr>
12855
+ <tr><td class="name">attach</td><td class="type">(grid: Grid, route: unknown, opts?: Record&lt;string, unknown&gt;): void</td><td class="desc">Attach a grid to a route.</td></tr>
12856
+ <tr><td class="name">detach</td><td class="type">(grid: Grid): void</td><td class="desc">Detach a grid, before it is destroyed.</td></tr>
12857
+ <tr><td class="name">destroy</td><td class="type">(): void</td><td class="desc">Destroy the router; called for you when the providing scope is disposed.</td></tr>
12858
+ </tbody>
12859
+ </table>
12860
+ </div>
12861
+ <h3 id="type-LatticeVueTabSpec">LatticeVueTabSpec</h3>
12862
+ <p class="section-note">One tab of a `&lt;LatticeTabs&gt;`; a slot named for its `id` makes it Vue's.</p>
12863
+ <div class="table-wrap">
12864
+ <table>
12865
+ <thead><tr><th>Member</th><th>Type</th><th>Description</th></tr></thead>
12866
+ <tbody>
12867
+ <tr><td class="name">id</td><td class="type">string</td><td class="desc"></td></tr>
12868
+ <tr><td class="name">label</td><td class="type">string</td><td class="desc"><small>(optional)</small></td></tr>
12869
+ </tbody>
12870
+ </table>
12871
+ </div>
12872
+ <h3 id="type-LatticeVueViewerExposed">LatticeVueViewerExposed</h3>
12873
+ <p class="section-note">What a viewer's template ref exposes; `instance()` is null before mount.</p>
12874
+ <div class="table-wrap">
12875
+ <table>
12876
+ <thead><tr><th>Member</th><th>Type</th><th>Description</th></tr></thead>
12877
+ <tbody>
12878
+ <tr><td class="name">instance</td><td class="type">(): Instance | null</td><td class="desc"></td></tr>
12879
+ </tbody>
12880
+ </table>
12881
+ </div>
12099
12882
  <h3 id="type-Layout">Layout</h3>
12100
12883
  <p class="section-note">A reconfigurable dashboard: a cell grid inside an element, and a set of windows on it that a user can move, resize and close by pointer or by keyboard (BACKLOG-0001108). The module is **payload-agnostic**: a window body is a container with an id, which this module creates and sizes and never reads. It tells a payload it was resized by emitting `window:resized`; it never calls into one, because it cannot know what one is.</p>
12101
12884
  <div class="table-wrap">
@@ -14065,7 +14848,7 @@ return `next ${p.mean.toFixed(1)}; r2 ${f.r2.toFixed(1)}; band ${p.upper - p.low
14065
14848
  <!-- END GENERATED TYPE REFERENCE -->
14066
14849
 
14067
14850
  <footer>
14068
- Lattice Grid 1.64.0 · Copyright © 2026 TOCLOCO Inc. All rights reserved.
14851
+ Lattice Grid 1.66.0 · Copyright © 2026 TOCLOCO Inc. All rights reserved.
14069
14852
  This document describes the behaviour of the shipped library. Where this guide and the code
14070
14853
  disagree, the code wins: please <a href="https://www.latticegrid.dev">tell us</a>.
14071
14854
  </footer>