@toclocoinc/lattice-grid 1.58.0 → 1.59.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 (78) hide show
  1. package/README.md +1 -1
  2. package/docs/API.html +188 -18
  3. package/docs/api-detail.html +55 -4
  4. package/lattice-grid.d.ts +233 -14
  5. package/lattice-grid.esm.min.js +595 -95
  6. package/lattice-grid.min.cjs +595 -95
  7. package/lattice-grid.min.css +1 -1
  8. package/lattice-grid.min.js +595 -95
  9. package/modules/ai.esm.min.js +4 -4
  10. package/modules/ai.min.cjs +4 -4
  11. package/modules/ai.min.js +4 -4
  12. package/modules/angular.esm.min.js +3 -2
  13. package/modules/angular.min.cjs +3 -2
  14. package/modules/angular.min.js +3 -2
  15. package/modules/chart-alluvial.esm.min.js +1 -1
  16. package/modules/chart-arc.esm.min.js +1 -1
  17. package/modules/chart-bubblemap.esm.min.js +1 -1
  18. package/modules/chart-bump.esm.min.js +1 -1
  19. package/modules/chart-calendar.esm.min.js +1 -1
  20. package/modules/chart-decomposition.esm.min.js +1 -1
  21. package/modules/chart-diverging.esm.min.js +1 -1
  22. package/modules/chart-dumbbell.esm.min.js +1 -1
  23. package/modules/chart-fan.esm.min.js +1 -1
  24. package/modules/chart-hexbin.esm.min.js +1 -1
  25. package/modules/chart-hexmap.esm.min.js +1 -1
  26. package/modules/chart-icicle.esm.min.js +1 -1
  27. package/modules/chart-parallel.esm.min.js +1 -1
  28. package/modules/chart-ridgeline.esm.min.js +1 -1
  29. package/modules/chart-roc.esm.min.js +1 -1
  30. package/modules/chart-slope.esm.min.js +1 -1
  31. package/modules/chart-splom.esm.min.js +1 -1
  32. package/modules/chart-waffle.esm.min.js +1 -1
  33. package/modules/charts.esm.min.js +4 -4
  34. package/modules/charts.min.cjs +4 -4
  35. package/modules/charts.min.js +4 -4
  36. package/modules/data-router.esm.min.js +4 -4
  37. package/modules/data-router.min.cjs +4 -4
  38. package/modules/data-router.min.js +4 -4
  39. package/modules/devtools.esm.min.js +2 -2
  40. package/modules/devtools.min.cjs +2 -2
  41. package/modules/devtools.min.js +2 -2
  42. package/modules/dhtmlx-compat.esm.min.js +4 -4
  43. package/modules/dhtmlx-compat.min.cjs +4 -4
  44. package/modules/dhtmlx-compat.min.js +4 -4
  45. package/modules/gantt.esm.min.js +109 -33
  46. package/modules/gantt.min.cjs +109 -33
  47. package/modules/gantt.min.js +109 -33
  48. package/modules/htmx.esm.min.js +595 -95
  49. package/modules/htmx.min.cjs +595 -95
  50. package/modules/htmx.min.js +595 -95
  51. package/modules/kanban.esm.min.js +4 -4
  52. package/modules/kanban.min.cjs +4 -4
  53. package/modules/kanban.min.js +4 -4
  54. package/modules/kpi.esm.min.js +4 -4
  55. package/modules/kpi.min.cjs +4 -4
  56. package/modules/kpi.min.js +4 -4
  57. package/modules/layout.esm.min.js +59 -6
  58. package/modules/layout.min.cjs +59 -6
  59. package/modules/layout.min.js +59 -6
  60. package/modules/mock-socket.esm.min.js +2 -2
  61. package/modules/mock-socket.min.cjs +2 -2
  62. package/modules/mock-socket.min.js +2 -2
  63. package/modules/react.esm.min.js +3 -2
  64. package/modules/react.min.cjs +3 -2
  65. package/modules/react.min.js +3 -2
  66. package/modules/svelte.esm.min.js +3 -2
  67. package/modules/svelte.min.cjs +3 -2
  68. package/modules/svelte.min.js +3 -2
  69. package/modules/tabs.esm.min.js +411 -9
  70. package/modules/tabs.min.cjs +411 -9
  71. package/modules/tabs.min.js +411 -9
  72. package/modules/vue.esm.min.js +3 -2
  73. package/modules/vue.min.cjs +3 -2
  74. package/modules/vue.min.js +3 -2
  75. package/modules/webcomponent.esm.min.js +595 -95
  76. package/modules/webcomponent.min.cjs +595 -95
  77. package/modules/webcomponent.min.js +595 -95
  78. package/package.json +1 -1
@@ -437,7 +437,7 @@
437
437
  <div class="shell">
438
438
  <aside class="rail">
439
439
  <p class="rail__brand">Lattice Grid</p>
440
- <p class="rail__sub">Developer guide · v1.58.0</p>
440
+ <p class="rail__sub">Developer guide · v1.59.0</p>
441
441
  <nav>
442
442
  <div class="rail__group">
443
443
  <span class="rail__label">Start here</span>
@@ -553,7 +553,7 @@
553
553
  <a href="API.html">reference tables</a> are the shorter version for when you already know.
554
554
  </p>
555
555
  <p class="chips">
556
- <span class="chip">Version 1.58.0</span>
556
+ <span class="chip">Version 1.59.0</span>
557
557
  <span class="chip">Zero dependencies</span>
558
558
  <span class="chip">No build step</span>
559
559
  </p>
@@ -1278,7 +1278,7 @@ off(); <span class="cmt">// every subscrip
1278
1278
  </p>
1279
1279
  <div class="example">
1280
1280
  <p class="example__label">Which version am I running?</p>
1281
- <pre><code>grid.getVersion(); <span class="cmt">// '1.58.0'</span>
1281
+ <pre><code>grid.getVersion(); <span class="cmt">// '1.59.0'</span>
1282
1282
  LatticeGrid.getVersion(); <span class="cmt">// the same, when you have no grid to hand</span></code></pre>
1283
1283
  </div>
1284
1284
  <p class="lead-in">
@@ -4219,6 +4219,55 @@ grid.destroy();
4219
4219
  <span class="kw">return</span> `${a}, ${b}`;</code></pre>
4220
4220
  </div>
4221
4221
 
4222
+ <h2 id="cell-tooltips">Rich cell tooltips</h2>
4223
+ <p class="lead-in">
4224
+ A <code>cell.tooltip</code> string becomes the browser's own <code>title</code>. That is one
4225
+ line of plain text, shown on the browser's schedule, styled by the browser, and unreachable
4226
+ with a keyboard &mdash; it cannot show a related record, a small chart, a list of validation
4227
+ errors or an edit history. The object form of <code>cell.tooltip</code> declares a tooltip the
4228
+ grid draws itself instead (BACKLOG-0001204): <code>{ render, mount, unmount }</code>. The
4229
+ plain-text form is unchanged and still produces a <code>title</code>.
4230
+ </p>
4231
+ <p class="lead-in">
4232
+ <code>render(params)</code> may return an element, a <code>{ title, rows, note }</code> spec the
4233
+ grid renders as text, a <code>{ html }</code> wrapper, or a string. <strong>A bare string is
4234
+ always text, never markup.</strong> That is deliberate and is not a style choice: the most
4235
+ natural tooltip anyone writes is <code>render: (p) =&gt; p.value</code>, and a value is row data
4236
+ &mdash; so if a bare string were markup, a field holding an <code>onerror</code> attribute would
4237
+ execute while the code that rendered it looked harmless. Markup has to be asked for explicitly,
4238
+ in the source, where review can see it; and what goes through <code>{ html }</code> is scrubbed
4239
+ of script the same way <code>allowUnsafeTemplates</code> output is.
4240
+ </p>
4241
+ <p class="lead-in">
4242
+ <code>mount(el, params)</code> and <code>unmount(el)</code> hold live content. The grid core
4243
+ never imports a module, so a sparkline or a KPI tile is mounted by <em>you</em>, inside
4244
+ <code>mount</code>, from a module bundle your page loaded. <code>unmount</code> runs on every
4245
+ close, so nothing is left running behind a hidden tooltip.
4246
+ </p>
4247
+ <p class="lead-in">
4248
+ <code>tooltip: { delay, maxWidth }</code> on the grid carries the defaults. <code>delay</code>
4249
+ is the rest before anything is built &mdash; 400ms by default, which is what stops a pointer
4250
+ sweeping across the grid from mounting a chart per cell &mdash; and <code>maxWidth</code> caps
4251
+ the width (a number is pixels, a string is used as written). Neither switches tooltips on: a
4252
+ column with no <code>cell.tooltip</code> has none.
4253
+ </p>
4254
+ <p class="lead-in">
4255
+ <strong>Keyboard and assistive technology.</strong> Focusing a cell shows the same tooltip after
4256
+ the same delay, and the cell carries <code>aria-describedby</code> pointing at it, so the
4257
+ content is announced rather than merely drawn. Any <code>aria-describedby</code> the cell
4258
+ already had &mdash; a validation message, for instance &mdash; is preserved and restored, not
4259
+ replaced. The tooltip is hoverable and stays open while the pointer rests on it, and Escape
4260
+ dismisses it without moving the pointer (WCAG 2.2 AA, 1.4.13). Escape is consumed only while a
4261
+ tooltip is open, so an editor, a menu or a maximised grid still sees it otherwise.
4262
+ </p>
4263
+ <p class="lead-in">
4264
+ <strong>Pooled rows.</strong> The tooltip closes on scroll, and its content is resolved from the
4265
+ DOM at the moment it opens rather than when the pointer arrived. Both follow from the same
4266
+ fact: rows and cells are recycled as the grid scrolls, so a bubble left open would be anchored
4267
+ to a node that has since been handed to a different row. It can therefore never show one row's
4268
+ content over another's.
4269
+ </p>
4270
+
4222
4271
  <h2 id="scrollbars">Always-visible scrollbars</h2>
4223
4272
  <p class="lead-in">
4224
4273
  Native scrollbars are overlay bars on most platforms now: they fade away when the pointer is
@@ -7707,6 +7756,8 @@ el.grid.sort.set([{ col: 'charge', dir: 'desc' }]);</code></pre>
7707
7756
  <tr><td class="name">cell:conflict</td><td class="desc">The write was accepted but the server row had moved underneath it. Last-write-wins with the divergence surfaced: your value stands and serverRow carries the server's truth.</td></tr>
7708
7757
  <tr><td class="name">cell:contextmenu</td><td class="desc">Right-click on a cell.</td></tr>
7709
7758
  <tr><td class="name">cell:dblclicked</td><td class="desc">A cell was double-clicked. Carries the row, column, value and text.</td></tr>
7759
+ <tr><td class="name">cell:mouseover</td><td class="desc">The pointer entered a cell. Fires once per cell, carries what cell:clicked carries plus the cell element as target, and is delegated on the viewport so it stays correct over pooled rows.</td></tr>
7760
+ <tr><td class="name">cell:mouseout</td><td class="desc">The pointer left a cell. Fires once per cell, including when the pointer left the grid; moving to the next cell fires this first, then cell:mouseover.</td></tr>
7710
7761
  <tr><td class="name">cell:edit:end</td><td class="desc">It closed: committed or cancelled.</td></tr>
7711
7762
  <tr><td class="name">cell:edit:start</td><td class="desc">An edit session opened.</td></tr>
7712
7763
  <tr><td class="name">cell:pending</td><td class="desc">Applied optimistically, not yet durable. Only with edit.commit.</td></tr>
@@ -8026,7 +8077,7 @@ grid.licence.state(); <span class="cmt">// 'licensed' | 'localhost' | 'trial'<
8026
8077
 
8027
8078
  <footer>
8028
8079
  <p>
8029
- Lattice Grid 1.58.0 · Copyright © 2026 TOCLOCO Inc. All rights reserved.
8080
+ Lattice Grid 1.59.0 · Copyright © 2026 TOCLOCO Inc. All rights reserved.
8030
8081
  Written against the shipped source. Where this guide and the code disagree, the code wins,
8031
8082
  please <a href="https://www.latticegrid.dev">tell us</a>.
8032
8083
  </p>
package/lattice-grid.d.ts CHANGED
@@ -1,5 +1,5 @@
1
1
  /*!
2
- * Lattice Grid 1.58.0, type declarations
2
+ * Lattice Grid 1.59.0, type declarations
3
3
  * Copyright (c) 2026 TOCLOCO Inc. All rights reserved.
4
4
  * https://latticegrid.dev
5
5
  */
@@ -541,6 +541,125 @@ export interface ColumnValueSpec {
541
541
  quickFilterText?: (p: ValueParams) => string;
542
542
  }
543
543
 
544
+ /**
545
+ * One label/value line in a {@link TooltipSpec}.
546
+ *
547
+ * Both halves are written as text by the grid, whatever they contain.
548
+ */
549
+ export interface TooltipRow {
550
+ /** The line's label, drawn on the leading edge. */
551
+ label?: unknown;
552
+ /** The line's value, drawn on the trailing edge. */
553
+ value?: unknown;
554
+ }
555
+
556
+ /**
557
+ * Structured tooltip content the grid renders for you (BACKLOG-0001204): a
558
+ * heading, a list of label/value lines, and a closing note.
559
+ *
560
+ * Every field is written as **text**, never as markup, so a spec built out of
561
+ * row values needs no escaping and cannot become HTML by accident. Return
562
+ * `{ html }` from `render` when markup is genuinely wanted.
563
+ */
564
+ export interface TooltipSpec {
565
+ /** A heading for the tooltip. */
566
+ title?: unknown;
567
+ /** Label/value lines, in order. */
568
+ rows?: TooltipRow[];
569
+ /** A closing note under the lines, drawn quieter than them. */
570
+ note?: unknown;
571
+ }
572
+
573
+ /**
574
+ * What a tooltip's `render` and `mount` are given: the same identification
575
+ * `cell:clicked` carries, plus the cell element itself and the grid.
576
+ *
577
+ * Resolved from the DOM at the moment the tooltip opens rather than when the
578
+ * pointer arrived, so a pooled row re-used in between names the row it is
579
+ * showing now.
580
+ */
581
+ export interface TooltipParams {
582
+ /** The row under the pointer or the keyboard cursor. */
583
+ row: Row;
584
+ /** That row's key. */
585
+ key: string;
586
+ /** Its display index. */
587
+ index: number;
588
+ /** The column the cell belongs to. */
589
+ colId: string;
590
+ /** The resolved column. */
591
+ column: Column;
592
+ /** The cell's value. */
593
+ value: unknown;
594
+ /** The cell's formatted text. */
595
+ text: string;
596
+ /** The cell element the tooltip is anchored to. */
597
+ cell: HTMLElement;
598
+ /** The grid. */
599
+ grid: Grid;
600
+ }
601
+
602
+ /**
603
+ * A rich, keyboard-accessible tooltip for a column's cells (BACKLOG-0001204) —
604
+ * the object form of `cell.tooltip`, drawn by the grid rather than handed to
605
+ * the browser as a native `title`.
606
+ *
607
+ * Shown after a delay (`tooltip.delay`, 400ms by default) on hover *and* on
608
+ * keyboard focus; the cell points at it with `aria-describedby`; it can be
609
+ * hovered without closing and Escape dismisses it (WCAG 2.2 AA, 1.4.13). It
610
+ * closes on scroll, because rows are pooled and a bubble left open would be
611
+ * anchored to a node that is now showing a different row.
612
+ */
613
+ export interface ColumnTooltipSpec {
614
+ /**
615
+ * Produce the content. Four shapes, and the difference between the last two
616
+ * is a security property rather than a style choice:
617
+ *
618
+ * - an **element** — your own DOM, attached as it is;
619
+ * - a **{@link TooltipSpec}** — `{ title, rows, note }`, rendered as text;
620
+ * - **`{ html }`** — the only wrapper that inserts markup, scrubbed of script
621
+ * the same way `allowUnsafeTemplates` output is;
622
+ * - a **string** — *always* text, never markup.
623
+ *
624
+ * The last rule is what makes `render: (p) => p.value` safe: a value comes
625
+ * from row data, and data must not be able to promote itself to HTML.
626
+ */
627
+ render?: (params: TooltipParams) => HTMLElement | TooltipSpec | { html: string } | string | null | undefined;
628
+ /**
629
+ * Put live content in the tooltip — a sparkline, a KPI tile — by calling into
630
+ * a module bundle your application loaded. The grid core never imports a
631
+ * module, so anything live is mounted here by you.
632
+ */
633
+ mount?: (el: HTMLElement, params: TooltipParams) => void;
634
+ /**
635
+ * Tear down whatever `mount` built. Called every time the tooltip closes, so
636
+ * nothing keeps running behind a hidden box.
637
+ */
638
+ unmount?: (el: HTMLElement) => void;
639
+ }
640
+
641
+ /**
642
+ * Grid-level defaults for the rich cell tooltip (BACKLOG-0001204), set once for
643
+ * every column rather than repeated on each.
644
+ *
645
+ * Defaults only: it switches nothing on. A tooltip exists because a column
646
+ * declares `cell.tooltip`, and a grid whose columns declare none has no
647
+ * tooltips whatever is set here.
648
+ */
649
+ export interface TooltipConfig {
650
+ /**
651
+ * How long the pointer or the keyboard cursor must rest on a cell before the
652
+ * tooltip is built, in milliseconds. 400 by default.
653
+ *
654
+ * The delay is why a pointer sweeping across the grid mounts nothing: a
655
+ * tooltip that built a chart on every cell it crossed would be unusable, and
656
+ * `0` asks for exactly that.
657
+ */
658
+ delay?: number;
659
+ /** How wide the tooltip may grow. A number is pixels; a string is used as written. */
660
+ maxWidth?: number | string;
661
+ }
662
+
544
663
  export interface ColumnCellSpec {
545
664
  decoration?: DecorationName | DecorationSpec;
546
665
  variant?: VariantSpec;
@@ -551,7 +670,15 @@ export interface ColumnCellSpec {
551
670
  class?: string | string[] | ((p: CellParams) => string | string[]);
552
671
  classWhen?: Record<string, string | ((p: CellParams) => boolean)>;
553
672
  style?: CellStyle | ((p: CellParams) => CellStyle);
554
- tooltip?: string | ((p: CellParams) => string);
673
+ /**
674
+ * A tooltip for this column's cells.
675
+ *
676
+ * A string or a function is the plain-text case and becomes the browser's own
677
+ * `title`. An object is a {@link ColumnTooltipSpec}: a tooltip the grid draws,
678
+ * which can carry structure, markup or live content and which a keyboard user
679
+ * can reach (BACKLOG-0001204).
680
+ */
681
+ tooltip?: string | ((p: CellParams) => string) | ColumnTooltipSpec;
555
682
  align?: Align;
556
683
  /**
557
684
  * Vertical alignment of this column's cell content, overriding the grid-level
@@ -635,6 +762,31 @@ export interface ColumnLayoutSpec {
635
762
  * the grid only when nothing else is fixed.
636
763
  */
637
764
  width?: number | string;
765
+ /**
766
+ * `'content'` sizes the column to what it is actually showing, the way
767
+ * `columns.autoSize()` does, and keeps doing it: on the first paint, and
768
+ * again whenever the rows change, the columns are shown, hidden, reordered
769
+ * or pinned, or the grid is resized. It is the declarative form of the
770
+ * imperative call, so a host no longer has to re-issue `autoSize()` after
771
+ * every data change.
772
+ *
773
+ * Sized to the *visible* content, not to the widest value in the dataset:
774
+ * the measurement reads the rows the renderer has mounted, because measuring
775
+ * a million rows is not a plan. It measures the heading too, so a column
776
+ * whose title is longer than its values widens to show the title.
777
+ *
778
+ * **Anything the caller states outranks it.** A declared `width` wins, and
779
+ * so does a width the user drags to — a resize is recorded as a `width`, so
780
+ * from that moment the column is that wide and the fit no longer touches it.
781
+ * `min` and `max` clamp the fitted width as they clamp any other. `flex` is
782
+ * resolved before this and wins, the two being contradictory instructions:
783
+ * `flex` fits the column to the *grid*, this fits it to the *content*.
784
+ *
785
+ * Not re-measured on scroll, deliberately: different rows mount as the grid
786
+ * scrolls, and re-fitting against them would make the columns jitter under
787
+ * the reader.
788
+ */
789
+ fit?: 'content';
638
790
  min?: number;
639
791
  max?: number;
640
792
  flex?: number;
@@ -1874,6 +2026,16 @@ export interface GridConfig {
1874
2026
  */
1875
2027
  verticalAlign?: VAlign;
1876
2028
 
2029
+ /**
2030
+ * Defaults for the rich cell tooltip (BACKLOG-0001204).
2031
+ *
2032
+ * The tooltip itself is declared per column, on `cell.tooltip`; this only
2033
+ * carries the settings that are a house style rather than a per-column
2034
+ * decision. It switches nothing on: a column with no `cell.tooltip` has no
2035
+ * tooltip whatever is set here.
2036
+ */
2037
+ tooltip?: TooltipConfig;
2038
+
1877
2039
  /**
1878
2040
  * Keep the scroll viewport's scrollbars visible (BACKLOG-0000990).
1879
2041
  *
@@ -2330,7 +2492,7 @@ export interface GridConfig {
2330
2492
  * markup `data-lat-group-toggle` and a click on it expands or collapses the
2331
2493
  * group, or call `params.toggle()` from a node you built yourself.
2332
2494
  */
2333
- groupRenderer?(params: GroupRowParams): string | Node | void;
2495
+ groupRenderer?: (params: GroupRowParams) => string | Node | void;
2334
2496
  /**
2335
2497
  * Which groups start expanded, before anyone has opened or closed one.
2336
2498
  *
@@ -2376,13 +2538,13 @@ export interface GridConfig {
2376
2538
  * should *not* be part of the data, use `pinnedTopRows`.
2377
2539
  */
2378
2540
  fullWidth?: {
2379
- when(row: Row): boolean;
2541
+ when: (row: Row) => boolean;
2380
2542
  /**
2381
2543
  * Return a string for text, or a node for content. Return nothing and
2382
2544
  * write into `params.element` yourself. An HTML string is deliberately not
2383
2545
  * accepted: see `allowUnsafeTemplates` for that decision elsewhere.
2384
2546
  */
2385
- render(params: FullWidthParams): string | Node | void;
2547
+ render: (params: FullWidthParams) => string | Node | void;
2386
2548
  };
2387
2549
  /** Total what the filters left rather than the whole set. */
2388
2550
  totalFilteredOnly?: boolean;
@@ -2574,10 +2736,12 @@ export interface GridConfig {
2574
2736
  exportName?: string;
2575
2737
  /**
2576
2738
  * Put the native annotation tools — pen, arrow, rectangle, highlighter — on
2577
- * the rail. Off by default; each is a real toggle button that shows pressed
2578
- * while it is the tool in use and turns off when pressed again. The tools
2579
- * also appear automatically for the duration of a presentation, so this is
2580
- * only needed to keep them available outside one.
2739
+ * the rail. Each is a real toggle button that shows pressed while it is the
2740
+ * tool in use and turns off when pressed again. Three states, not two:
2741
+ * `true` opts in and keeps the tools on the rail always, presentation or
2742
+ * not; `false` opts OUT and the tools are never added, not even for a
2743
+ * presentation; omitted keeps the default, where the tools are off until a
2744
+ * presentation starts, appear for its duration, and leave when it ends.
2581
2745
  */
2582
2746
  annotate?: boolean;
2583
2747
  };
@@ -2648,7 +2812,7 @@ export interface GridConfig {
2648
2812
  * host's, and owns the model, the key and the privacy decision.
2649
2813
  */
2650
2814
  ai?: {
2651
- ask(p: {
2815
+ ask: (p: {
2652
2816
  /** The full text to send: the schema description and the question together. */
2653
2817
  prompt: string;
2654
2818
  /** The grid's schema as data: columns, types and operators. No row values. */
@@ -2658,7 +2822,7 @@ export interface GridConfig {
2658
2822
  /** What the user typed. */
2659
2823
  message: string;
2660
2824
  context?: unknown;
2661
- }): Promise<unknown>;
2825
+ }) => Promise<unknown>;
2662
2826
  schemaOptions?: object;
2663
2827
  context?: unknown;
2664
2828
  element?: HTMLElement;
@@ -4227,6 +4391,13 @@ export type EventName =
4227
4391
  /* Cells and editing */
4228
4392
  | 'cell:changed' | 'cell:pending' | 'cell:confirmed' | 'cell:reverted' | 'cell:conflict'
4229
4393
  | 'cell:clicked' | 'cell:dblclicked' | 'cell:contextmenu'
4394
+ /* The pointer entering and leaving a cell (BACKLOG-0001203). Announcements
4395
+ * only, carrying what `cell:clicked` carries plus the cell element as
4396
+ * `target`. A host cannot wire these itself: rows and cells are pooled and
4397
+ * re-used as the grid scrolls, so a listener bound to a cell node fires for
4398
+ * whichever row occupies it next. Nothing in the grid is gated on hover, so
4399
+ * a keyboard user reaches everything a pointer does. */
4400
+ | 'cell:mouseover' | 'cell:mouseout'
4230
4401
  | 'cell:edit:start' | 'cell:edit:end' | 'row:edit:start' | 'row:edit:end'
4231
4402
  | 'row:clicked' | 'row:dblclicked'
4232
4403
  | 'row:pending' | 'row:confirmed' | 'row:reverted' | 'row:conflict'
@@ -8378,6 +8549,26 @@ declare module 'lattice-grid/modules/gantt' {
8378
8549
  columns?: { start?: string; end?: string; duration?: string };
8379
8550
  /** Task identity for the live `rows.apply` surface (a field or fn); default 'id'. */
8380
8551
  rowKey?: string | ((row: GanttTask) => unknown);
8552
+ /**
8553
+ * The host's own names for the task properties the scheduler reads, so a
8554
+ * plan can be fed as it already exists rather than renamed for the Gantt:
8555
+ * `{ id: 'taskId', start: 'startDate', name: 'jobName' }`. Each value is a
8556
+ * field name or a reader `(row) => value`; anything unmapped reads its
8557
+ * canonical name. The vocabulary is `id`, `name`, `start`, `end`,
8558
+ * `duration`, `milestone`, `percentComplete`, `parent`, `baselineStart`,
8559
+ * `baselineEnd`, `constraint`, `constraintDate`.
8560
+ *
8561
+ * `rowKey` also reaches the scheduler now: a task with no `id` of its own
8562
+ * is identified by whatever `rowKey` names, which it previously was not —
8563
+ * such a plan was keyed correctly by `rows.apply` and then refused to
8564
+ * schedule.
8565
+ *
8566
+ * This is a READ mapping. `applyEdit` and `level()` write the canonical
8567
+ * property, so each says so rather than writing where nothing reads;
8568
+ * `assignee`, `cost` and `actualCost` belong to the resource and
8569
+ * earned-value layers and are not mapped.
8570
+ */
8571
+ fields?: Record<string, string | ((row: GanttTask) => unknown)>;
8381
8572
  /** Auto-mount into this element at construction. */
8382
8573
  element?: unknown;
8383
8574
  }): Gantt;
@@ -9848,8 +10039,21 @@ declare module 'lattice-grid/modules/tabs' {
9848
10039
  id: string;
9849
10040
  /** The tab button's text. Defaults to `id`. */
9850
10041
  label?: string;
9851
- /** The grid config passed to `createGrid` for this tab (merged with the derived `source`, when `from` is set). */
10042
+ /** The config for this tab's body: the grid config passed to `createGrid` (merged with the derived `source`, when `from` is set), or — with `view` — that viewer's own config. */
9852
10043
  config?: object;
10044
+ /**
10045
+ * Mount something other than a grid in this tab: the factory that builds
10046
+ * it, called as `(el, config) => instance`. `createKanban` and `createKPI`
10047
+ * have that signature already; a Gantt is adapted in a line
10048
+ * (`(el, config) => createGantt({ ...config, element: el })`). The factory
10049
+ * is injected rather than imported, exactly as `createGrid` is.
10050
+ *
10051
+ * A `view` tab derives from `from` exactly as a grid tab does: a headless
10052
+ * grid carries the derived source and its rows are piped into the viewer
10053
+ * through `rows.apply`, so deriving into one needs `createHeadlessGrid`
10054
+ * injected too.
10055
+ */
10056
+ view?: (el: HTMLElement, config: object) => unknown;
9853
10057
  /** The parent tab id to derive from. When set, `config.source` is built for you and any of your own is replaced (with a warning). */
9854
10058
  from?: string;
9855
10059
  /** Row predicate forwarded to the derived source. */
@@ -9877,6 +10081,12 @@ declare module 'lattice-grid/modules/tabs' {
9877
10081
  profile?: unknown;
9878
10082
  /** This tab's panel's own `aria-label`, when the label alone is not enough context. */
9879
10083
  ariaLabel?: string;
10084
+ /** A leading icon: a single character or emoji, or an element you built. Never a markup string — nothing here parses HTML. Decorative, so it is hidden from assistive technology. */
10085
+ icon?: string | HTMLElement;
10086
+ /** A count badge. `true` shows this tab's own live row count and follows it; a number or string is static; a function is given the live count and returns what to show (`null` hides it). Off when absent. */
10087
+ badge?: true | number | string | ((count: number | null, tab: { id: string; label: string; from: string | null }) => unknown);
10088
+ /** The badge's tone, declared by the host rather than derived from a threshold: `'good' | 'warn' | 'bad' | 'unknown'`, or a function of the live count returning one. */
10089
+ badgeTone?: 'good' | 'warn' | 'bad' | 'unknown' | ((count: number | null, tab: { id: string; label: string; from: string | null }) => 'good' | 'warn' | 'bad' | 'unknown' | null);
9880
10090
  }
9881
10091
 
9882
10092
  /** The payload every tab-change event carries. */
@@ -9894,6 +10104,8 @@ declare module 'lattice-grid/modules/tabs' {
9894
10104
  interface TabsConfig {
9895
10105
  /** The grid factory to mount each tab with, e.g. `import { createGrid } from 'lattice-grid'`. Required. */
9896
10106
  createGrid: (el: HTMLElement, config: object) => unknown;
10107
+ /** The headless grid factory, injected the same way and for the same reason. Optional, and only needed for badges: with it, a tab that has never been activated still carries a live count, computed with no DOM. Without it, such a tab shows no badge until its first activation. */
10108
+ createHeadlessGrid?: (config: object) => unknown;
9897
10109
  /** The tabs, in display order. Required, at least one. */
9898
10110
  tabs: TabDescriptor[];
9899
10111
  /** The initially active tab id. Defaults to the first tab. */
@@ -10092,8 +10304,15 @@ declare module 'lattice-grid/modules/layout' {
10092
10304
  gap?: number | string;
10093
10305
  /** The default padding inside a window (default `'5px'`). */
10094
10306
  padding?: number | string;
10095
- /** Rearrangement (default `'vertical'`): push displaced windows down, then pull up. */
10096
- compact?: 'vertical' | 'none';
10307
+ /**
10308
+ * Rearrangement (default `'vertical'`). One gravity direction, never two:
10309
+ * `'vertical'` pushes displaced windows down and then floats everything up,
10310
+ * `'horizontal'` pushes them right and then floats everything left — so
10311
+ * dragging a window out of a row closes the hole sideways — and `'none'`
10312
+ * leaves every placement exactly where it was put. An unrecognised value
10313
+ * warns once, naming what it got, and falls back to `'vertical'`.
10314
+ */
10315
+ compact?: 'vertical' | 'horizontal' | 'none';
10097
10316
  /**
10098
10317
  * The default `movable` for every window that does not declare its own
10099
10318
  * (default `false`). This states a default, so `false` takes nothing away