@toclocoinc/lattice-grid 1.65.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 -4
  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 +675 -14
  8. package/docs/api-detail.html +137 -4
  9. package/lattice-grid.d.ts +1 -1
  10. package/lattice-grid.esm.min.js +18 -6
  11. package/lattice-grid.min.cjs +18 -6
  12. package/lattice-grid.min.css +1 -1
  13. package/lattice-grid.min.js +18 -6
  14. package/modules/ai.d.ts +1 -1
  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 +1 -1
  19. package/modules/angular.esm.min.js +7 -7
  20. package/modules/angular.min.cjs +7 -7
  21. package/modules/angular.min.js +7 -7
  22. package/modules/chart-alluvial.d.ts +1 -1
  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 +1 -1
  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 +1 -1
  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 +1 -1
  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 +1 -1
  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 +1 -1
  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 +1 -1
  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 +1 -1
  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 +1 -1
  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 +1 -1
  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 +1 -1
  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 +1 -1
  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 +1 -1
  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 +1 -1
  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 +1 -1
  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 +1 -1
  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 +1 -1
  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 +1 -1
  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 +1 -1
  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 +1 -1
  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 +1 -1
  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 +1 -1
  119. package/modules/geo-europe-nuts.esm.min.js +1 -1
  120. package/modules/geo-uk.d.ts +1 -1
  121. package/modules/geo-uk.esm.min.js +1 -1
  122. package/modules/geo-us-states.d.ts +1 -1
  123. package/modules/geo-us-states.esm.min.js +1 -1
  124. package/modules/geo-world-110m.d.ts +1 -1
  125. package/modules/geo-world-110m.esm.min.js +1 -1
  126. package/modules/geo-world-50m.d.ts +1 -1
  127. package/modules/geo-world-50m.esm.min.js +1 -1
  128. package/modules/htmx.d.ts +1 -1
  129. package/modules/htmx.esm.min.js +18 -6
  130. package/modules/htmx.min.cjs +18 -6
  131. package/modules/htmx.min.js +18 -6
  132. package/modules/kanban.d.ts +1 -1
  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 +1 -1
  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 +1 -1
  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 +1 -1
  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 +251 -2
  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 +1 -1
  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 +233 -1
  165. package/modules/webcomponent.esm.min.js +1266 -37
  166. package/modules/webcomponent.min.cjs +1248 -37
  167. package/modules/webcomponent.min.js +1248 -37
  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.65.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>
@@ -417,7 +417,10 @@
417
417
  <span class="rail__label">Frameworks</span>
418
418
  <a href="#adapters">Framework adapters</a>
419
419
  <a href="#react-v2">React</a>
420
+ <a href="#vue-v2">Vue</a>
421
+ <a href="#svelte-v2">Svelte</a>
420
422
  <a href="#angular-v2">Angular</a>
423
+ <a href="#webcomponent-v2">Web Components</a>
421
424
  </div>
422
425
  <div class="rail__group">
423
426
  <span class="rail__label">Registries</span>
@@ -450,7 +453,7 @@
450
453
  </header>
451
454
 
452
455
  <p class="chips">
453
- <span class="chip">Version 1.65.0</span>
456
+ <span class="chip">Version 1.66.0</span>
454
457
  <span class="chip">Zero dependencies</span>
455
458
  <span class="chip"><a href="api-detail.html">Developer guide &rarr;</a></span>
456
459
  </p>
@@ -526,9 +529,9 @@
526
529
  <thead><tr><th>Entry point</th><th>Factory</th><th>Needs</th></tr></thead>
527
530
  <tbody>
528
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>
529
- <tr><td class="name">@toclocoinc/<wbr>lattice-grid-angular</td><td class="sig">&lt;lattice-grid [config]="…"&gt;</td><td class="desc">A package of its own, not a bundle: standalone components compiled ahead of time, one per viewer, plus the data router as a service. See <a href="#angular-v2">Angular</a>. <code>modules/angular</code>, which needs the JIT compiler, is deprecated.</td></tr>
530
- <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>
531
- <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>
532
535
  </tbody>
533
536
  </table>
534
537
  </div>
@@ -713,24 +716,377 @@ controller.destroy();
713
716
  a line that could never run. The build now deletes that branch from every emitted artefact.
714
717
  </p>
715
718
 
716
- <h3 id="angular-v2">Angular: a compiled package, <code>@toclocoinc/lattice-grid-angular</code></h3>
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>
717
1066
  <p>
718
1067
  Angular's components are not objects a library can assemble at run time in a production
719
1068
  build. <code>modules/angular</code> did assemble them that way, which needs Angular's JIT
720
1069
  compiler in the page &mdash; present on a development server, absent from every AOT build.
721
- So Angular gets a package of its own: TypeScript components compiled by
1070
+ So Angular gets an entry point of its own: TypeScript components compiled by
722
1071
  <code>@angular/compiler-cli</code> into a partial-Ivy library, which your build's Angular
723
1072
  Linker turns into definitions exactly as it does for any other Angular library you install.
724
1073
  <strong>No compiler in your bundle, and one standalone component per viewer.</strong>
725
1074
  </p>
726
- <pre><code>npm install @toclocoinc/lattice-grid @toclocoinc/lattice-grid-angular</code></pre>
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>
727
1083
  <pre><code><span class="kw">import</span> { bootstrapApplication } <span class="kw">from</span> '@angular/platform-browser';
728
1084
  <span class="kw">import</span> { createGrid } <span class="kw">from</span> '@toclocoinc/lattice-grid';
729
1085
  <span class="kw">import</span> { createKPI } <span class="kw">from</span> '@toclocoinc/lattice-grid/modules/kpi';
730
1086
  <span class="kw">import</span> { createChart } <span class="kw">from</span> '@toclocoinc/lattice-grid/modules/charts';
731
1087
  <span class="kw">import</span> {
732
1088
  LatticeGridComponent, LatticeKpiComponent, LatticeChartComponent, provideLattice,
733
- } <span class="kw">from</span> '@toclocoinc/lattice-grid-angular';
1089
+ } <span class="kw">from</span> '@toclocoinc/lattice-grid/angular';
734
1090
 
735
1091
  @Component({
736
1092
  selector: 'app-dashboard',
@@ -823,8 +1179,8 @@ bootstrapApplication(Dashboard, {
823
1179
  finds a real Angular with no JIT compiler, instead of leaving you with Angular's own. In
824
1180
  1.65 it also gained the fix that <code>&lt;div [latticeGrid]="config"&gt;</code> binds the
825
1181
  configuration through the directive's selector, as its documentation always said it did. It
826
- will be removed in a later release; move to <code>@toclocoinc/lattice-grid-angular</code>,
827
- which covers every viewer rather than the grid alone.
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.
828
1184
  </p>
829
1185
 
830
1186
  <div class="note">
@@ -1001,6 +1357,93 @@ defaults(<span class="kw">null</span>); <span class="cmt">// clea
1001
1357
 
1002
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>
1003
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
+
1004
1447
  <h2 id="dhtmlx-compat">dhtmlx Grid compatibility wrapper</h2>
1005
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>
1006
1449
 
@@ -1514,7 +1957,7 @@ grid.destroy();
1514
1957
  <table>
1515
1958
  <thead><tr><th>Member</th><th>Returns</th><th>Description</th></tr></thead>
1516
1959
  <tbody>
1517
- <tr><td class="sig">getVersion()</td><td class="type">string</td><td class="desc">The version this grid came from, e.g. <code>'1.65.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>
1518
1961
  <tr><td class="sig">get(key)</td><td class="type">unknown</td><td class="desc">Read any configuration key.</td></tr>
1519
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>
1520
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>
@@ -8195,7 +8638,7 @@ spans.addSpan('R0', 'name', 3, 2); <span class="cmt">// rowspan 3, colspan 2</sp
8195
8638
 
8196
8639
  <h3 id="module-exports-example">Every module export, executed</h3>
8197
8640
  <p class="section-note">Every shipped module’s exports, resolved against its own barrel on every build.</p>
8198
- <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>
8199
8642
  <span class="cmt">// barrel. A module that stopped exporting something fails here.</span>
8200
8643
  <span class="kw">const</span> modules = [
8201
8644
  [<span class="kw">await</span> import('../packages/dom/src/index.js'), [
@@ -8220,6 +8663,10 @@ spans.addSpan('R0', 'name', 3, 2); <span class="cmt">// rowspan 3, colspan 2</sp
8220
8663
  'ATTRIBUTE_CONFIG', 'EVENT_PREFIX', 'GridElementController',
8221
8664
  'TAG_NAME', 'createLatticeGridElement', 'defineLatticeGrid', 'domEventName',
8222
8665
  'observedAttributeNames',
8666
+ 'createLatticeElements', 'defineLatticeElements', 'ELEMENT_TAGS',
8667
+ 'createLatticeKPIElement', 'createLatticeChartElement', 'createLatticeKanbanElement',
8668
+ 'createLatticeGanttElement', 'createLatticeLayoutElement', 'createLatticeTabsElement',
8669
+ 'createLatticeRouterElement', 'adoptLatticeStyles', 'latticeStyleSheets',
8223
8670
  ]],
8224
8671
  [<span class="kw">await</span> import('../packages/modules/devtools/index.js'), [
8225
8672
  'CONSOLE_ACTIVATION', 'createDevtools', 'expose',
@@ -8536,6 +8983,18 @@ grid.destroy();
8536
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>
8537
8984
  <tr><td class="name">observedAttributeNames</td><td class="type">() =&gt; string[]</td><td class="desc">The attributes the element reacts to.</td></tr>
8538
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>
8539
8998
  </tbody>
8540
8999
  </table>
8541
9000
  </div>
@@ -12170,6 +12629,71 @@ return `next ${p.mean.toFixed(1)}; r2 ${f.r2.toFixed(1)}; band ${p.upper - p.low
12170
12629
  </tbody>
12171
12630
  </table>
12172
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>
12173
12697
  <h3 id="type-LatticeGridHandle">LatticeGridHandle</h3>
12174
12698
  <p class="section-note">The live instance a `&lt;LatticeGrid&gt;` ref exposes; `null` before mount.</p>
12175
12699
  <div class="table-wrap">
@@ -12180,6 +12704,75 @@ return `next ${p.mean.toFixed(1)}; r2 ${f.r2.toFixed(1)}; band ${p.upper - p.low
12180
12704
  </tbody>
12181
12705
  </table>
12182
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>
12183
12776
  <h3 id="type-LatticeTabSpec">LatticeTabSpec</h3>
12184
12777
  <p class="section-note">One tab of a `&lt;LatticeTabs&gt;`; `content` makes it React's rather than the module's.</p>
12185
12778
  <div class="table-wrap">
@@ -12208,6 +12801,19 @@ return `next ${p.mean.toFixed(1)}; r2 ${f.r2.toFixed(1)}; band ${p.upper - p.low
12208
12801
  </tbody>
12209
12802
  </table>
12210
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>
12211
12817
  <h3 id="type-LatticeViewerHandle">LatticeViewerHandle</h3>
12212
12818
  <p class="section-note">The live instance a viewer component's ref exposes; `null` before mount.</p>
12213
12819
  <div class="table-wrap">
@@ -12218,6 +12824,61 @@ return `next ${p.mean.toFixed(1)}; r2 ${f.r2.toFixed(1)}; band ${p.upper - p.low
12218
12824
  </tbody>
12219
12825
  </table>
12220
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>
12221
12882
  <h3 id="type-Layout">Layout</h3>
12222
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>
12223
12884
  <div class="table-wrap">
@@ -14187,7 +14848,7 @@ return `next ${p.mean.toFixed(1)}; r2 ${f.r2.toFixed(1)}; band ${p.upper - p.low
14187
14848
  <!-- END GENERATED TYPE REFERENCE -->
14188
14849
 
14189
14850
  <footer>
14190
- Lattice Grid 1.65.0 · Copyright © 2026 TOCLOCO Inc. All rights reserved.
14851
+ Lattice Grid 1.66.0 · Copyright © 2026 TOCLOCO Inc. All rights reserved.
14191
14852
  This document describes the behaviour of the shipped library. Where this guide and the code
14192
14853
  disagree, the code wins: please <a href="https://www.latticegrid.dev">tell us</a>.
14193
14854
  </footer>