@toclocoinc/lattice-grid 1.57.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 +227 -17
  3. package/docs/api-detail.html +180 -4
  4. package/lattice-grid.d.ts +316 -13
  5. package/lattice-grid.esm.min.js +809 -128
  6. package/lattice-grid.min.cjs +809 -128
  7. package/lattice-grid.min.css +1 -1
  8. package/lattice-grid.min.js +809 -128
  9. package/modules/ai.esm.min.js +5 -4
  10. package/modules/ai.min.cjs +5 -4
  11. package/modules/ai.min.js +5 -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 +809 -128
  49. package/modules/htmx.min.cjs +809 -128
  50. package/modules/htmx.min.js +809 -128
  51. package/modules/kanban.esm.min.js +5 -6
  52. package/modules/kanban.min.cjs +5 -6
  53. package/modules/kanban.min.js +5 -6
  54. package/modules/kpi.esm.min.js +41 -8
  55. package/modules/kpi.min.cjs +41 -8
  56. package/modules/kpi.min.js +41 -8
  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 +809 -128
  76. package/modules/webcomponent.min.cjs +809 -128
  77. package/modules/webcomponent.min.js +809 -128
  78. package/package.json +1 -1
package/lattice-grid.d.ts CHANGED
@@ -1,5 +1,5 @@
1
1
  /*!
2
- * Lattice Grid 1.57.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
  *
@@ -2313,6 +2475,36 @@ export interface GridConfig {
2313
2475
  sharedMemory?: boolean;
2314
2476
  /** A totals line at the foot of each group as well as the grid. */
2315
2477
  groupFooter?: boolean;
2478
+ /**
2479
+ * Draw the group row yourself.
2480
+ *
2481
+ * The grid's own group row is an expander, a label and a count. A host that
2482
+ * needs more — a section header with a points rollup, a done/total count and
2483
+ * a progress bar — supplies this instead, and owns the whole row: it is drawn
2484
+ * as one band across every column, and no ordinary cells are mounted for it.
2485
+ *
2486
+ * Return an HTML string, or a node, or write into `params.element` and return
2487
+ * nothing. Unlike `fullWidth.render`, a string here **is** inserted as markup,
2488
+ * on the same footing as the board's `cardRenderer`: this is your own template
2489
+ * for a row the grid synthesised, not a value out of your data.
2490
+ *
2491
+ * The chevron is yours to draw and yours to wire: give any element in your
2492
+ * markup `data-lat-group-toggle` and a click on it expands or collapses the
2493
+ * group, or call `params.toggle()` from a node you built yourself.
2494
+ */
2495
+ groupRenderer?: (params: GroupRowParams) => string | Node | void;
2496
+ /**
2497
+ * Which groups start expanded, before anyone has opened or closed one.
2498
+ *
2499
+ * `true` (the default) opens every group, `false` closes every group, a
2500
+ * number opens the first N levels (`0` closes everything, a negative opens
2501
+ * every level), and a predicate answers per group — the current sprint's
2502
+ * section open while the rest start closed.
2503
+ *
2504
+ * Only ever consulted for a group nobody has touched: once the user or your
2505
+ * code expands or collapses one, that decision stands.
2506
+ */
2507
+ groupDefaultExpanded?: boolean | number | ((group: GroupInfo) => boolean);
2316
2508
  /**
2317
2509
  * Where the grand total goes.
2318
2510
  *
@@ -2346,13 +2538,13 @@ export interface GridConfig {
2346
2538
  * should *not* be part of the data, use `pinnedTopRows`.
2347
2539
  */
2348
2540
  fullWidth?: {
2349
- when(row: Row): boolean;
2541
+ when: (row: Row) => boolean;
2350
2542
  /**
2351
2543
  * Return a string for text, or a node for content. Return nothing and
2352
2544
  * write into `params.element` yourself. An HTML string is deliberately not
2353
2545
  * accepted: see `allowUnsafeTemplates` for that decision elsewhere.
2354
2546
  */
2355
- render(params: FullWidthParams): string | Node | void;
2547
+ render: (params: FullWidthParams) => string | Node | void;
2356
2548
  };
2357
2549
  /** Total what the filters left rather than the whole set. */
2358
2550
  totalFilteredOnly?: boolean;
@@ -2544,10 +2736,12 @@ export interface GridConfig {
2544
2736
  exportName?: string;
2545
2737
  /**
2546
2738
  * Put the native annotation tools — pen, arrow, rectangle, highlighter — on
2547
- * the rail. Off by default; each is a real toggle button that shows pressed
2548
- * while it is the tool in use and turns off when pressed again. The tools
2549
- * also appear automatically for the duration of a presentation, so this is
2550
- * 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.
2551
2745
  */
2552
2746
  annotate?: boolean;
2553
2747
  };
@@ -2618,7 +2812,7 @@ export interface GridConfig {
2618
2812
  * host's, and owns the model, the key and the privacy decision.
2619
2813
  */
2620
2814
  ai?: {
2621
- ask(p: {
2815
+ ask: (p: {
2622
2816
  /** The full text to send: the schema description and the question together. */
2623
2817
  prompt: string;
2624
2818
  /** The grid's schema as data: columns, types and operators. No row values. */
@@ -2628,7 +2822,7 @@ export interface GridConfig {
2628
2822
  /** What the user typed. */
2629
2823
  message: string;
2630
2824
  context?: unknown;
2631
- }): Promise<unknown>;
2825
+ }) => Promise<unknown>;
2632
2826
  schemaOptions?: object;
2633
2827
  context?: unknown;
2634
2828
  element?: HTMLElement;
@@ -4197,6 +4391,13 @@ export type EventName =
4197
4391
  /* Cells and editing */
4198
4392
  | 'cell:changed' | 'cell:pending' | 'cell:confirmed' | 'cell:reverted' | 'cell:conflict'
4199
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'
4200
4401
  | 'cell:edit:start' | 'cell:edit:end' | 'row:edit:start' | 'row:edit:end'
4201
4402
  | 'row:clicked' | 'row:dblclicked'
4202
4403
  | 'row:pending' | 'row:confirmed' | 'row:reverted' | 'row:conflict'
@@ -4634,6 +4835,13 @@ export interface RowsApi {
4634
4835
  * grid is not grouped.
4635
4836
  */
4636
4837
  groupHeadings(index: number): Row[];
4838
+ /**
4839
+ * The leaf rows beneath a group heading: the members it counts in
4840
+ * `leafCount`, as rows, so you can roll up a field the grid was never told
4841
+ * to total. Filtered members in display order. Computed per call, so call it
4842
+ * when you draw a group row rather than in a loop over every row.
4843
+ */
4844
+ leavesOf(key: string): Row[];
4637
4845
  expand(key: string, deep?: boolean): void;
4638
4846
  collapse(key: string): void;
4639
4847
  expandAll(): void;
@@ -5931,6 +6139,53 @@ export interface CellMenuParams {
5931
6139
  grid: Grid;
5932
6140
  }
5933
6141
 
6142
+ /** Which group `groupDefaultExpanded` is being asked about. */
6143
+ export interface GroupInfo {
6144
+ /** The group's key, the same string `Row.key` carries and `rows.expand` takes. */
6145
+ key: string;
6146
+ /** The id of the column this level groups on. */
6147
+ column?: string;
6148
+ /** The value this group stands for. */
6149
+ value?: unknown;
6150
+ /** Depth of the group. Zero is the outermost level. */
6151
+ level?: number;
6152
+ /** The group path from the root down to this group. */
6153
+ path?: string[];
6154
+ }
6155
+
6156
+ /** What `groupRenderer` is handed. */
6157
+ export interface GroupRowParams {
6158
+ /** The group row itself. */
6159
+ row: Row;
6160
+ /** The group's key, as `rows.expand`/`rows.collapse` take it. */
6161
+ key: string;
6162
+ /** The id of the column this level groups on. */
6163
+ column?: string;
6164
+ /** The value this group stands for. */
6165
+ value: unknown;
6166
+ /** Depth of the group. Zero is the outermost level. */
6167
+ level: number;
6168
+ /** Whether the group is currently open. */
6169
+ expanded: boolean;
6170
+ /** How many records sit beneath it, at any depth. */
6171
+ leafCount: number;
6172
+ /** The group's own reductions, by column id — whatever `total` asked for. */
6173
+ totals?: Record<string, unknown>;
6174
+ /**
6175
+ * The rows beneath this group, computed when you call it.
6176
+ *
6177
+ * A function rather than an array because a group is unbounded and this runs
6178
+ * per paint: a host that only needs the count should read `leafCount` and
6179
+ * never call this.
6180
+ */
6181
+ leaves(): Row[];
6182
+ /** Expand the group if it is closed, collapse it if it is open. */
6183
+ toggle(): void;
6184
+ grid: Grid;
6185
+ /** The element to fill. Write into it directly, or return content instead. */
6186
+ element: HTMLElement;
6187
+ }
6188
+
5934
6189
  /** What `fullWidth.render` is handed. */
5935
6190
  export interface FullWidthParams {
5936
6191
  row: Row;
@@ -8294,6 +8549,26 @@ declare module 'lattice-grid/modules/gantt' {
8294
8549
  columns?: { start?: string; end?: string; duration?: string };
8295
8550
  /** Task identity for the live `rows.apply` surface (a field or fn); default 'id'. */
8296
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)>;
8297
8572
  /** Auto-mount into this element at construction. */
8298
8573
  element?: unknown;
8299
8574
  }): Gantt;
@@ -9764,8 +10039,21 @@ declare module 'lattice-grid/modules/tabs' {
9764
10039
  id: string;
9765
10040
  /** The tab button's text. Defaults to `id`. */
9766
10041
  label?: string;
9767
- /** 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. */
9768
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;
9769
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). */
9770
10058
  from?: string;
9771
10059
  /** Row predicate forwarded to the derived source. */
@@ -9793,6 +10081,12 @@ declare module 'lattice-grid/modules/tabs' {
9793
10081
  profile?: unknown;
9794
10082
  /** This tab's panel's own `aria-label`, when the label alone is not enough context. */
9795
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);
9796
10090
  }
9797
10091
 
9798
10092
  /** The payload every tab-change event carries. */
@@ -9810,6 +10104,8 @@ declare module 'lattice-grid/modules/tabs' {
9810
10104
  interface TabsConfig {
9811
10105
  /** The grid factory to mount each tab with, e.g. `import { createGrid } from 'lattice-grid'`. Required. */
9812
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;
9813
10109
  /** The tabs, in display order. Required, at least one. */
9814
10110
  tabs: TabDescriptor[];
9815
10111
  /** The initially active tab id. Defaults to the first tab. */
@@ -10008,8 +10304,15 @@ declare module 'lattice-grid/modules/layout' {
10008
10304
  gap?: number | string;
10009
10305
  /** The default padding inside a window (default `'5px'`). */
10010
10306
  padding?: number | string;
10011
- /** Rearrangement (default `'vertical'`): push displaced windows down, then pull up. */
10012
- 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';
10013
10316
  /**
10014
10317
  * The default `movable` for every window that does not declare its own
10015
10318
  * (default `false`). This states a default, so `false` takes nothing away