@toclocoinc/lattice-grid 1.59.0 → 1.60.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 (112) hide show
  1. package/README.md +3 -3
  2. package/docs/API.html +1620 -90
  3. package/docs/api-detail.html +270 -5
  4. package/lattice-grid.d.ts +100 -2880
  5. package/lattice-grid.esm.min.js +288 -57
  6. package/lattice-grid.min.cjs +288 -57
  7. package/lattice-grid.min.js +288 -57
  8. package/modules/ai.d.ts +401 -0
  9. package/modules/ai.esm.min.js +25 -6
  10. package/modules/ai.min.cjs +25 -6
  11. package/modules/ai.min.js +25 -6
  12. package/modules/angular.d.ts +31 -0
  13. package/modules/angular.esm.min.js +3 -3
  14. package/modules/angular.min.cjs +3 -3
  15. package/modules/angular.min.js +3 -3
  16. package/modules/chart-alluvial.d.ts +18 -0
  17. package/modules/chart-alluvial.esm.min.js +1 -1
  18. package/modules/chart-arc.d.ts +18 -0
  19. package/modules/chart-arc.esm.min.js +1 -1
  20. package/modules/chart-bubblemap.d.ts +18 -0
  21. package/modules/chart-bubblemap.esm.min.js +1 -1
  22. package/modules/chart-bump.d.ts +12 -0
  23. package/modules/chart-bump.esm.min.js +1 -1
  24. package/modules/chart-calendar.d.ts +12 -0
  25. package/modules/chart-calendar.esm.min.js +1 -1
  26. package/modules/chart-decomposition.d.ts +20 -0
  27. package/modules/chart-decomposition.esm.min.js +1 -1
  28. package/modules/chart-diverging.d.ts +12 -0
  29. package/modules/chart-diverging.esm.min.js +1 -1
  30. package/modules/chart-dumbbell.d.ts +18 -0
  31. package/modules/chart-dumbbell.esm.min.js +1 -1
  32. package/modules/chart-fan.d.ts +18 -0
  33. package/modules/chart-fan.esm.min.js +1 -1
  34. package/modules/chart-hexbin.d.ts +18 -0
  35. package/modules/chart-hexbin.esm.min.js +1 -1
  36. package/modules/chart-hexmap.d.ts +18 -0
  37. package/modules/chart-hexmap.esm.min.js +1 -1
  38. package/modules/chart-icicle.d.ts +12 -0
  39. package/modules/chart-icicle.esm.min.js +1 -1
  40. package/modules/chart-parallel.d.ts +19 -0
  41. package/modules/chart-parallel.esm.min.js +1 -1
  42. package/modules/chart-ridgeline.d.ts +14 -0
  43. package/modules/chart-ridgeline.esm.min.js +1 -1
  44. package/modules/chart-roc.d.ts +20 -0
  45. package/modules/chart-roc.esm.min.js +1 -1
  46. package/modules/chart-slope.d.ts +12 -0
  47. package/modules/chart-slope.esm.min.js +1 -1
  48. package/modules/chart-splom.d.ts +19 -0
  49. package/modules/chart-splom.esm.min.js +1 -1
  50. package/modules/chart-waffle.d.ts +12 -0
  51. package/modules/chart-waffle.esm.min.js +1 -1
  52. package/modules/charts.d.ts +122 -0
  53. package/modules/charts.esm.min.js +4 -4
  54. package/modules/charts.min.cjs +4 -4
  55. package/modules/charts.min.js +4 -4
  56. package/modules/data-router.d.ts +91 -0
  57. package/modules/data-router.esm.min.js +109 -17
  58. package/modules/data-router.min.cjs +109 -17
  59. package/modules/data-router.min.js +109 -17
  60. package/modules/devtools.d.ts +28 -0
  61. package/modules/devtools.esm.min.js +2 -2
  62. package/modules/devtools.min.cjs +2 -2
  63. package/modules/devtools.min.js +2 -2
  64. package/modules/dhtmlx-compat.d.ts +19 -0
  65. package/modules/dhtmlx-compat.esm.min.js +4 -4
  66. package/modules/dhtmlx-compat.min.cjs +4 -4
  67. package/modules/dhtmlx-compat.min.js +4 -4
  68. package/modules/gantt.d.ts +515 -0
  69. package/modules/gantt.esm.min.js +4 -4
  70. package/modules/gantt.min.cjs +4 -4
  71. package/modules/gantt.min.js +4 -4
  72. package/modules/htmx.d.ts +176 -0
  73. package/modules/htmx.esm.min.js +288 -57
  74. package/modules/htmx.min.cjs +288 -57
  75. package/modules/htmx.min.js +288 -57
  76. package/modules/kanban.d.ts +492 -0
  77. package/modules/kanban.esm.min.js +4 -4
  78. package/modules/kanban.min.cjs +4 -4
  79. package/modules/kanban.min.js +4 -4
  80. package/modules/kpi.d.ts +255 -0
  81. package/modules/kpi.esm.min.js +40 -7
  82. package/modules/kpi.min.cjs +40 -7
  83. package/modules/kpi.min.js +40 -7
  84. package/modules/layout.d.ts +332 -0
  85. package/modules/layout.esm.min.js +4 -4
  86. package/modules/layout.min.cjs +4 -4
  87. package/modules/layout.min.js +4 -4
  88. package/modules/mock-socket.d.ts +114 -0
  89. package/modules/mock-socket.esm.min.js +2 -2
  90. package/modules/mock-socket.min.cjs +2 -2
  91. package/modules/mock-socket.min.js +2 -2
  92. package/modules/react.d.ts +25 -0
  93. package/modules/react.esm.min.js +3 -3
  94. package/modules/react.min.cjs +3 -3
  95. package/modules/react.min.js +3 -3
  96. package/modules/svelte.d.ts +26 -0
  97. package/modules/svelte.esm.min.js +3 -3
  98. package/modules/svelte.min.cjs +3 -3
  99. package/modules/svelte.min.js +3 -3
  100. package/modules/tabs.d.ts +133 -0
  101. package/modules/tabs.esm.min.js +4 -4
  102. package/modules/tabs.min.cjs +4 -4
  103. package/modules/tabs.min.js +4 -4
  104. package/modules/vue.d.ts +24 -0
  105. package/modules/vue.esm.min.js +3 -3
  106. package/modules/vue.min.cjs +3 -3
  107. package/modules/vue.min.js +3 -3
  108. package/modules/webcomponent.d.ts +47 -0
  109. package/modules/webcomponent.esm.min.js +288 -57
  110. package/modules/webcomponent.min.cjs +288 -57
  111. package/modules/webcomponent.min.js +288 -57
  112. package/package.json +2 -2
package/lattice-grid.d.ts CHANGED
@@ -1,5 +1,5 @@
1
1
  /*!
2
- * Lattice Grid 1.59.0, type declarations
2
+ * Lattice Grid 1.60.0, type declarations
3
3
  * Copyright (c) 2026 TOCLOCO Inc. All rights reserved.
4
4
  * https://latticegrid.dev
5
5
  */
@@ -1080,6 +1080,16 @@ export interface Column {
1080
1080
  * the way `align` is. Omitted, the column follows the grid default.
1081
1081
  */
1082
1082
  verticalAlign?: VAlign;
1083
+ /**
1084
+ * When this leaf column is shown, the same union `ColumnGroup` declares
1085
+ * (BACKLOG-0001279). A leaf reads its own `showWhen` exactly as a group
1086
+ * reads its own — `open`/`closed` tie the leaf to an ancestor group's
1087
+ * collapsed state, `always` (the default) shows it regardless — so tying a
1088
+ * leaf's visibility to a group's open/closed state does not require
1089
+ * wrapping it in a `ColumnGroup` of its own just to hold this setting; a
1090
+ * wrapper is for grouping columns, not for this.
1091
+ */
1092
+ showWhen?: 'open' | 'closed' | 'always';
1083
1093
  /** How the column leaves the grid, where that differs from how it is shown. */
1084
1094
  export?: ColumnExportSpec;
1085
1095
  /** Whether the user may group by this column from the interface. */
@@ -1329,6 +1339,38 @@ export interface RemoteRequest {
1329
1339
  sort: SortEntry[];
1330
1340
  context: unknown;
1331
1341
  signal: AbortSignal;
1342
+ /**
1343
+ * The `where` predicates in force, as a runtime the source can evaluate but
1344
+ * not mutate (BACKLOG-0001268). Present **only when at least one predicate is
1345
+ * registered**, so a grid that does not use `where` sends the request it
1346
+ * always sent, field for field.
1347
+ *
1348
+ * A host `fetch` may ignore it, and every existing one does: it is a host
1349
+ * function, so there is nothing to serialise and no engine can evaluate it —
1350
+ * `passes` is dropped by `JSON.stringify` the way `signal` already is. It is
1351
+ * carried for the one reader that can act on it, `createPushdownSource`,
1352
+ * which runs it as the residual over the matching set when that set is under
1353
+ * `whereRowLimit`. The `{ condition }` twin remains the route that narrows
1354
+ * the fetch itself, at any size.
1355
+ */
1356
+ where?: WhereRuntime;
1357
+ }
1358
+
1359
+ /**
1360
+ * The `where` predicates in force, as a source sees them (BACKLOG-0001268).
1361
+ *
1362
+ * A snapshot rather than the model, so a source can evaluate the predicates but
1363
+ * cannot register or remove one through it.
1364
+ */
1365
+ export interface WhereRuntime {
1366
+ /** Whether any predicate is registered at all. */
1367
+ active: boolean;
1368
+ /** The registered names, in registration order — for diagnostics. */
1369
+ names: string[];
1370
+ /** Bumped on every registration or removal, so a cache key can track it. */
1371
+ version: number;
1372
+ /** Does this row survive every registered predicate? */
1373
+ passes(row: unknown, key?: string): boolean;
1332
1374
  }
1333
1375
 
1334
1376
  export interface RemoteResult {
@@ -3175,11 +3217,31 @@ export interface PushdownAdapter {
3175
3217
  export interface PushdownPlan {
3176
3218
  /** The query the adapter was given. */
3177
3219
  pushed: RemoteRequest;
3178
- /** What the grid applied afterwards. */
3179
- residual: { filters: object | null; sort: SortEntry[] | null; quick: string };
3220
+ /**
3221
+ * What the grid applied afterwards. `where` is the host predicate runtime
3222
+ * when one survived the `whereRowLimit` gate, and `null` when none was
3223
+ * registered or the gate refused it (BACKLOG-0001268).
3224
+ */
3225
+ residual: {
3226
+ filters: object | null;
3227
+ sort: SortEntry[] | null;
3228
+ quick: string;
3229
+ where: WhereRuntime | null;
3230
+ /**
3231
+ * Whether the rows the residual runs over are the whole matching set rather
3232
+ * than a fetched fraction (BACKLOG-0001268). Set by the source when it hands
3233
+ * the residual to `applyResidual`; absent on the plan `lastPlan()` reports,
3234
+ * because it is a property of one fetch's result, not of the plan.
3235
+ *
3236
+ * When true the counts the residual produces are whole-dataset counts, so
3237
+ * the page-relative `where` warning is suppressed. Absent counts as not
3238
+ * whole: silence has to be earned.
3239
+ */
3240
+ whole?: boolean;
3241
+ };
3180
3242
  /** Whether the whole result had to be fetched rather than a window. */
3181
3243
  needsAll: boolean;
3182
- /** Which parts could not be pushed: `filter`, `sort`, `quick`. */
3244
+ /** Which parts could not be pushed: `filter`, `sort`, `quick`, `where`. */
3183
3245
  unpushed: string[];
3184
3246
  /**
3185
3247
  * Whether the whole result was fetched because `fullDataset` is on, rather
@@ -3310,6 +3372,26 @@ export interface PushdownSourceConfig {
3310
3372
  * no-residual short-return warning.
3311
3373
  */
3312
3374
  allowPartialResults?: boolean;
3375
+ /**
3376
+ * The most rows the source will fetch and hold in order to run a twinless
3377
+ * `where` predicate as the residual (BACKLOG-0001268). Defaults to `50_000`,
3378
+ * the same anchor as the grid's `workerThreshold` — the size at which this
3379
+ * codebase already judges a dataset big enough to need different handling.
3380
+ *
3381
+ * A `where` predicate is a host function no engine can evaluate, so the only
3382
+ * way to honour one is to fetch every matching row and filter here. That
3383
+ * silently turns a windowed grid into a whole-dataset download, which is the
3384
+ * thing a pushdown source exists to avoid. So it is a gate, not a free
3385
+ * upgrade: at or past this many matching rows the predicate is **refused and
3386
+ * warned about** — the rows it would exclude stay on screen — rather than the
3387
+ * download being taken on the host's behalf. An adapter that reports no row
3388
+ * total counts as over the limit, because guessing the other way is guessing
3389
+ * your way into the download.
3390
+ *
3391
+ * Raise it when you want that download; the `{ condition }` twin is the route
3392
+ * that narrows the fetch itself and works at any size.
3393
+ */
3394
+ whereRowLimit?: number;
3313
3395
  }
3314
3396
 
3315
3397
  export interface StatisticsApi {
@@ -4398,6 +4480,13 @@ export type EventName =
4398
4480
  * whichever row occupies it next. Nothing in the grid is gated on hover, so
4399
4481
  * a keyboard user reaches everything a pointer does. */
4400
4482
  | 'cell:mouseover' | 'cell:mouseout'
4483
+ /* A pointer press and release on a cell (BACKLOG-0001272), the same
4484
+ * convention as the hover pair above: announcements only, carrying what
4485
+ * `cell:clicked` carries plus the cell element as `target`. A host cannot
4486
+ * wire these itself for the same reason it cannot wire the hover pair —
4487
+ * rows and cells are pooled and re-used as the grid scrolls, so a listener
4488
+ * bound to a cell node fires for whichever row occupies it next. */
4489
+ | 'cell:mousedown' | 'cell:mouseup'
4401
4490
  | 'cell:edit:start' | 'cell:edit:end' | 'row:edit:start' | 'row:edit:end'
4402
4491
  | 'row:clicked' | 'row:dblclicked'
4403
4492
  | 'row:pending' | 'row:confirmed' | 'row:reverted' | 'row:conflict'
@@ -5019,6 +5108,13 @@ export interface WhereOptions {
5019
5108
  * of filtering a page client-side. It must be implied by the predicate: the
5020
5109
  * grid ANDs both, so a twin wider than the function costs only time, while one
5021
5110
  * narrower than it hides rows the function would have kept.
5111
+ *
5112
+ * **The twin is what works at any size.** Without one, a pushdown source can
5113
+ * still run the function — but only as the residual over the whole matching
5114
+ * set, so it does so only while that set is under `whereRowLimit` (default
5115
+ * `50_000`) and refuses loudly past it (BACKLOG-0001268). A paged or remote
5116
+ * source cannot run it at all and warns at registration. The twin is pushed
5117
+ * to the engine, so it narrows the fetch itself and none of that applies.
5022
5118
  */
5023
5119
  condition?: FilterSet;
5024
5120
  }
@@ -6040,8 +6136,6 @@ export interface DiffApi {
6040
6136
  report(): Record<string, unknown>;
6041
6137
  }
6042
6138
 
6043
- export type PermissionLevel = 'hidden' | 'read' | 'write' | 'writeOnly';
6044
-
6045
6139
  export interface PermissionsApi {
6046
6140
  levelOf(column: string | ResolvedColumn): PermissionLevel;
6047
6141
  isHidden(column: string | ResolvedColumn): boolean;
@@ -7603,2877 +7697,3 @@ export interface Chart {
7603
7697
  toCSV(): string;
7604
7698
  destroy(): void;
7605
7699
  }
7606
-
7607
- declare module 'lattice-grid/modules/charts' {
7608
- /** Every type name `createChart` accepts. */
7609
- export const TYPES: readonly ChartType[];
7610
- /** The built-in colour schemes, by name. */
7611
- export const SCHEMES: Readonly<Record<string, readonly string[]>>;
7612
- export const PALETTE: readonly string[];
7613
- export function createChart(spec: ChartSpec): Chart;
7614
- /**
7615
- * Chart a selected cell range. Derives the chart from the range's shape — a
7616
- * leading text column becomes the categories, the numeric columns become the
7617
- * measures — and returns the live chart, or null when the range has nothing
7618
- * to measure. Respects hidden and unreadable columns. The type is a sensible
7619
- * default the caller can change with `chart.update({ type })`.
7620
- */
7621
- export function chartRange(
7622
- grid: Grid,
7623
- opts: {
7624
- container: Element | string;
7625
- range?: CellRange;
7626
- type?: ChartType;
7627
- } & Partial<ChartSpec>,
7628
- ): Chart | null;
7629
- /** Would {@link chartRange} draw something for the grid's current selection? */
7630
- export function canChartRange(grid: Grid, opts?: { range?: CellRange }): boolean;
7631
- /**
7632
- * Decide what a chart of a range should be, without drawing it: the type, the
7633
- * category column, the measure columns, and a `spec` ready for `createChart`
7634
- * — or a `reason` naming why the range cannot be charted.
7635
- */
7636
- export function deriveRangeSpec(
7637
- grid: Grid,
7638
- opts?: { range?: CellRange; type?: ChartType },
7639
- ): {
7640
- spec: ChartSpec | null;
7641
- type: ChartType | null;
7642
- x: string | null;
7643
- measures: string[];
7644
- columns: string[];
7645
- reason: string | null;
7646
- };
7647
- /**
7648
- * Turn a fitted regression model into diagnostic chart specs ready for
7649
- * `createChart` (BACKLOG-0000812). Pass a precomputed `model`, or a `spec` to
7650
- * fit one over the grid, and the `fitted` and `residual` fit-shadow column ids
7651
- * the residual and QQ plots draw over.
7652
- *
7653
- * The presets that map onto grid columns come back as drawable specs: `fit`
7654
- * (the fit line with its confidence band), `residualsFitted`, `qq`, and
7655
- * `multicollinearity` (a correlogram over the predictors, with the model's
7656
- * `vif` alongside). The three that need a per-row or per-coefficient quantity
7657
- * the grid has no column for — `scaleLocation`, `residualsLeverage`,
7658
- * `coefficientForest` — come back with a null `spec` and a stable `reason`,
7659
- * rather than silently dropped.
7660
- */
7661
- export function regressionPlots(
7662
- grid: Grid,
7663
- opts?: {
7664
- model?: RegressionModel;
7665
- spec?: RegressionSpec;
7666
- fitted?: string;
7667
- residual?: string;
7668
- rows?: object[] | ((grid: Grid) => object[]);
7669
- confidence?: number;
7670
- },
7671
- ): {
7672
- model: RegressionModel | null;
7673
- plots: Record<
7674
- 'fit' | 'residualsFitted' | 'qq' | 'multicollinearity'
7675
- | 'scaleLocation' | 'residualsLeverage' | 'coefficientForest',
7676
- {
7677
- spec: ChartSpec | null;
7678
- reason: string | null;
7679
- vif?: number[] | null;
7680
- coefficients?: RegressionCoefficient[] | null;
7681
- }
7682
- >;
7683
- };
7684
- export function registerScheme(name: string, colours: readonly string[]): void;
7685
- export function resolveScheme(spec?: object): object;
7686
- export function schemeNames(): string[];
7687
- export function setDefaultScheme(name: string): void;
7688
- /**
7689
- * The definition an extension chart type registers (BACKLOG-0000886). `draw`
7690
- * receives the base drawing context — `plot`, `bound`, `groups`, `scheme`,
7691
- * `typography`, `fontSize`, `labels`, `grid`, `spec`, `doc` — plus
7692
- * `ctx.helpers`, the base's own toolkit of primitives (element factory, scales,
7693
- * axes, mark pool, distribution kernels), and appends its marks to the layer
7694
- * groups. `bind` optionally supplies the bound data (default: the by-series
7695
- * binder); `freeform` lays the chart out without axis gutters; `labelled`
7696
- * declares that `labels` applies.
7697
- */
7698
- interface ChartTypeDefinition {
7699
- draw: (ctx: object) => object;
7700
- bind?: (grid: Grid, spec: ChartSpec) => object;
7701
- freeform?: boolean;
7702
- labelled?: boolean;
7703
- }
7704
- /**
7705
- * Register an extension chart type so `createChart({ type })` can draw it
7706
- * (BACKLOG-0000886). Extension types ship as their own opt-in modules, so the
7707
- * base charts bundle does not grow for a type a caller never imports — you pay
7708
- * only for the charts you use.
7709
- */
7710
- export function registerChartType(name: string, def: ChartTypeDefinition): void;
7711
- /** Every registered extension chart-type name, in registration order. */
7712
- export function registeredChartTypes(): string[];
7713
- export { Chart };
7714
- }
7715
-
7716
- declare module 'lattice-grid/modules/chart-ridgeline' {
7717
- /**
7718
- * The ridgeline (joy plot) extension chart type (BACKLOG-0000886). Importing
7719
- * this module registers `ridgeline` with the base charts module; the base
7720
- * bundle does not include it unless a caller imports it. Draws one
7721
- * kernel-density ridge per category (`x`), stacked and overlapping, over the
7722
- * distribution of a measure (`y`); `spec.overlap` sets the vertical overlap.
7723
- */
7724
- export function drawRidgeline(ctx: object): object;
7725
- export default drawRidgeline;
7726
- }
7727
-
7728
- declare module 'lattice-grid/modules/chart-calendar' {
7729
- /**
7730
- * The calendar-heatmap extension chart type (BACKLOG-0000886). Importing this
7731
- * module registers `calendar`. Draws value-by-day as a GitHub-style grid: `x`
7732
- * is a date column, `y` the measure summed per day.
7733
- */
7734
- export function drawCalendar(ctx: object): object;
7735
- export default drawCalendar;
7736
- }
7737
-
7738
- declare module 'lattice-grid/modules/chart-splom' {
7739
- /**
7740
- * The scatter-plot-matrix (SPLOM) extension chart type (BACKLOG-0000886).
7741
- * Importing this module registers `splom`. Crosses every pair of the numeric
7742
- * `columns` (2–6) as a matrix of scatters, naming each variable on the
7743
- * diagonal.
7744
- */
7745
- export function drawSplom(ctx: object): object;
7746
- /** The SPLOM binding: reads the numeric `columns` off the grid's visible rows. */
7747
- export function bindSplom(grid: Grid, spec: object): object;
7748
- export default drawSplom;
7749
- }
7750
-
7751
- declare module 'lattice-grid/modules/chart-hexbin' {
7752
- /**
7753
- * The hexbin / 2D-density extension chart type (BACKLOG-0000886). Importing
7754
- * this module registers `hexbin`. Bins `x`/`y` points into hexagons shaded by
7755
- * count, so a large scatter reads as a density field rather than overplotting.
7756
- */
7757
- export function drawHexbin(ctx: object): object;
7758
- /** The hexbin binding: reads the numeric `x` and `y` columns off the grid's rows. */
7759
- export function bindHexbin(grid: Grid, spec: object): object;
7760
- export default drawHexbin;
7761
- }
7762
-
7763
- declare module 'lattice-grid/modules/chart-roc' {
7764
- /**
7765
- * The ROC / PR / calibration extension chart type (BACKLOG-0000886). Importing
7766
- * this module registers `roc`. `spec.curve` chooses `'roc'` (default, with the
7767
- * chance diagonal and AUC), `'pr'`, or `'calibration'`; `label` is the outcome
7768
- * column (positive when truthy or equal to `spec.positive`), `score` the model
7769
- * score.
7770
- */
7771
- export function drawRoc(ctx: object): object;
7772
- /** The ROC binding: reads the outcome and score off the grid's rows. */
7773
- export function bindRoc(grid: Grid, spec: object): object;
7774
- export default drawRoc;
7775
- }
7776
-
7777
- declare module 'lattice-grid/modules/chart-fan' {
7778
- /**
7779
- * The fan / forecast extension chart type (BACKLOG-0000886). Importing this
7780
- * module registers `fan`. Draws `y` (history) as a solid line, `forecast` as a
7781
- * dashed continuation, and the `lower`/`upper` interval as a widening band.
7782
- */
7783
- export function drawFan(ctx: object): object;
7784
- /** The fan binding: reads the history, forecast and interval columns in row order. */
7785
- export function bindFan(grid: Grid, spec: object): object;
7786
- export default drawFan;
7787
- }
7788
-
7789
- declare module 'lattice-grid/modules/chart-decomposition' {
7790
- /**
7791
- * The seasonal-decomposition panel extension chart type (BACKLOG-0000886),
7792
- * companion to the `tsTrend`/`tsSeasonal`/`tsResidual` shadow columns.
7793
- * Importing this module registers `decomposition`. Draws a stacked panel per
7794
- * named component column (`observed`/`trend`/`seasonal`/`residual`) sharing one
7795
- * x axis.
7796
- */
7797
- export function drawDecomposition(ctx: object): object;
7798
- /** The decomposition binding: reads the named component columns in row order. */
7799
- export function bindDecomposition(grid: Grid, spec: object): object;
7800
- export default drawDecomposition;
7801
- }
7802
-
7803
- declare module 'lattice-grid/modules/chart-slope' {
7804
- /**
7805
- * The slope-chart extension type (BACKLOG-0000886). Importing this module
7806
- * registers `slope`. One line per `series` connecting its `y` across the `x`
7807
- * periods — before/after comparison read from the slopes.
7808
- */
7809
- export function drawSlope(ctx: object): object;
7810
- export default drawSlope;
7811
- }
7812
-
7813
- declare module 'lattice-grid/modules/chart-dumbbell' {
7814
- /**
7815
- * The dumbbell / connected-dot extension type (BACKLOG-0000886). Importing
7816
- * this module registers `dumbbell`. Two dots (`start`, `end`) joined by a bar
7817
- * per `x` category — the gap is the bar's length.
7818
- */
7819
- export function drawDumbbell(ctx: object): object;
7820
- /** The dumbbell binding: reads the category and its two numeric columns. */
7821
- export function bindDumbbell(grid: Grid, spec: object): object;
7822
- export default drawDumbbell;
7823
- }
7824
-
7825
- declare module 'lattice-grid/modules/chart-bump' {
7826
- /**
7827
- * The bump-chart extension type (BACKLOG-0000886). Importing this module
7828
- * registers `bump`. One line per `series` plotted by its rank of `y` within
7829
- * each `x` period — rank-over-time, where crossings are the story.
7830
- */
7831
- export function drawBump(ctx: object): object;
7832
- export default drawBump;
7833
- }
7834
-
7835
- declare module 'lattice-grid/modules/chart-diverging' {
7836
- /**
7837
- * The diverging-bar extension type (BACKLOG-0000886). Importing this module
7838
- * registers `diverging`. Horizontal bars growing left/right from a central
7839
- * zero over a signed `y`, on a symmetric scale.
7840
- */
7841
- export function drawDiverging(ctx: object): object;
7842
- export default drawDiverging;
7843
- }
7844
-
7845
- declare module 'lattice-grid/modules/chart-parallel' {
7846
- /**
7847
- * The parallel-coordinates extension type (BACKLOG-0000886). Importing this
7848
- * module registers `parallel`. One polyline per row across the numeric
7849
- * `columns`, each a vertical axis with its own scale; `spec.colourBy` colours
7850
- * by a category.
7851
- */
7852
- export function drawParallel(ctx: object): object;
7853
- /** The parallel-coordinates binding: reads the dimension columns off the rows. */
7854
- export function bindParallel(grid: Grid, spec: object): object;
7855
- export default drawParallel;
7856
- }
7857
-
7858
- declare module 'lattice-grid/modules/chart-icicle' {
7859
- /**
7860
- * The icicle extension type (BACKLOG-0000886). Importing this module registers
7861
- * `icicle`. A hierarchy (the grid's group tree) as nested rectangles in rows,
7862
- * sized by `y`; drills like the built-in hierarchical types.
7863
- */
7864
- export function drawIcicle(ctx: object): object;
7865
- export default drawIcicle;
7866
- }
7867
-
7868
- declare module 'lattice-grid/modules/chart-waffle' {
7869
- /**
7870
- * The waffle / dot-matrix extension type (BACKLOG-0000886). Importing this
7871
- * module registers `waffle`. Proportion as counted squares (default 100), one
7872
- * colour per `x` category sized by `y`.
7873
- */
7874
- export function drawWaffle(ctx: object): object;
7875
- export default drawWaffle;
7876
- }
7877
-
7878
- declare module 'lattice-grid/modules/chart-alluvial' {
7879
- /**
7880
- * The alluvial extension type (BACKLOG-0000886). Importing this module
7881
- * registers `alluvial`. Ribbons from `source` categories to `target`
7882
- * categories sized by `value` — categorical flow between two dimensions.
7883
- */
7884
- export function drawAlluvial(ctx: object): object;
7885
- /** The alluvial binding: aggregates source→target flows off the grid's rows. */
7886
- export function bindAlluvial(grid: Grid, spec: object): object;
7887
- export default drawAlluvial;
7888
- }
7889
-
7890
- declare module 'lattice-grid/modules/chart-arc' {
7891
- /**
7892
- * The arc-diagram extension type (BACKLOG-0000886). Importing this module
7893
- * registers `arc`. Nodes on a baseline with `source`→`target` relationships as
7894
- * semicircular arcs, thickness by `value`.
7895
- */
7896
- export function drawArc(ctx: object): object;
7897
- /** The arc-diagram binding: collects nodes and edges off the grid's rows. */
7898
- export function bindArc(grid: Grid, spec: object): object;
7899
- export default drawArc;
7900
- }
7901
-
7902
- declare module 'lattice-grid/modules/chart-bubblemap' {
7903
- /**
7904
- * The symbol / bubble-map extension type (BACKLOG-0000886). Importing this
7905
- * module registers `bubblemap`. Points placed by `lon`/`lat`, each a bubble
7906
- * with a square-root radius from `size`; needs no outlines and fetches nothing.
7907
- */
7908
- export function drawBubbleMap(ctx: object): object;
7909
- /** The bubble-map binding: reads the coordinate and size columns off the rows. */
7910
- export function bindBubbleMap(grid: Grid, spec: object): object;
7911
- export default drawBubbleMap;
7912
- }
7913
-
7914
- declare module 'lattice-grid/modules/chart-hexmap' {
7915
- /**
7916
- * The hexbin-map extension type (BACKLOG-0000886). Importing this module
7917
- * registers `hexmap`. `lon`/`lat` points binned into hexagons shaded by count,
7918
- * so a geographic density reads without overplotting or outlines.
7919
- */
7920
- export function drawHexMap(ctx: object): object;
7921
- /** The hexbin-map binding: reads the coordinate columns off the grid's rows. */
7922
- export function bindHexMap(grid: Grid, spec: object): object;
7923
- export default drawHexMap;
7924
- }
7925
-
7926
- declare module 'lattice-grid/modules/react' {
7927
- /**
7928
- * Build the React component.
7929
- *
7930
- * A factory rather than a component, because the adapter imports neither
7931
- * React nor the grid: you pass both in. That is what keeps the package's
7932
- * promise of no runtime dependencies, and what stops an adapter disagreeing
7933
- * with the grid version already loaded.
7934
- *
7935
- * The live grid is reached through a forwarded ref: `ref.current.grid` is the
7936
- * same `Grid` the vanilla `createGrid` returns, or null before mount.
7937
- */
7938
- export function createLatticeGrid(deps: { React: unknown; createGrid: unknown }): unknown;
7939
- /** Every grid event, as the prop name a React caller writes. */
7940
- export const EVENT_NAMES: readonly string[];
7941
- export function handlerName(event: string): string;
7942
- export default createLatticeGrid;
7943
- }
7944
-
7945
- declare module 'lattice-grid/modules/vue' {
7946
- /**
7947
- * Build the Vue 3 component.
7948
- *
7949
- * The Vue runtime and `createGrid` are passed in, for the same reason as the
7950
- * React adapter: the package ships no dependencies and cannot import either.
7951
- * The dependency key is lowercase `vue` — `createLatticeGrid({ vue, createGrid })`.
7952
- *
7953
- * The live grid is reached through the component's exposed `grid()` method:
7954
- * with `ref="grid"` on the element, `this.$refs.grid.grid()` returns the same
7955
- * `Grid` the vanilla `createGrid` returns, or null before mount.
7956
- */
7957
- export function createLatticeGrid(deps: { vue: unknown; createGrid: unknown }): unknown;
7958
- export const EVENT_NAMES: readonly string[];
7959
- export function dashedName(event: string): string;
7960
- export default createLatticeGrid;
7961
- }
7962
-
7963
- declare module 'lattice-grid/modules/svelte' {
7964
- /**
7965
- * A Svelte action: `use:lattice={config}`.
7966
- *
7967
- * The action owns nothing but the node the caller already has, so the grid is
7968
- * reached one of two ways. Pass an `onGrid` callback in the action params
7969
- * (BACKLOG-0000785): `use:lattice={{ ...config, onGrid: (g) => (grid = g) }}`
7970
- * calls it once with the live `Grid` the moment it is built — synchronously,
7971
- * before `ready` fires — and again if you hand the action a different
7972
- * `onGrid`. Or read it off an event: every grid event carries the grid on its
7973
- * `detail`, so `on:ready={(e) => e.detail.grid}` hands you the same `Grid` a
7974
- * turn after construction. Use `onGrid` when you need the instance during the
7975
- * first render.
7976
- */
7977
- export function createLatticeAction(deps: { createGrid: unknown }): unknown;
7978
- export const EVENT_NAMES: readonly string[];
7979
- export function dashedName(event: string): string;
7980
- export default createLatticeAction;
7981
- }
7982
-
7983
- declare module 'lattice-grid/modules/angular' {
7984
- /**
7985
- * Build the Angular standalone component and directive from one shared
7986
- * controller (BACKLOG-0000805).
7987
- *
7988
- * The Angular core namespace and `createGrid` are passed in, for the same
7989
- * reason as every other adapter: the package ships no dependencies and cannot
7990
- * import `@angular/core` or the grid. Pass `@angular/common`'s
7991
- * `isPlatformBrowser` too for an explicit SSR guard; without it the adapter
7992
- * guards on the presence of a `document`.
7993
- *
7994
- * The returned `LatticeGridComponent` (`<lattice-grid [config]="…">`) and
7995
- * `LatticeGridDirective` (`<div [latticeGrid]="…">`) each expose the live grid
7996
- * through a `grid` getter — the same `Grid` the vanilla `createGrid` returns,
7997
- * or null before build — at parity with React's `ref.current.grid`. Grid
7998
- * events are `@Output`s aliased to their dashed names (`(cell-changed)`).
7999
- */
8000
- export function createLatticeGrid(
8001
- deps: { ng: unknown; createGrid: unknown; isPlatformBrowser?: (id: unknown) => boolean },
8002
- ): { LatticeGridComponent: unknown; LatticeGridDirective: unknown };
8003
- export const EVENT_NAMES: readonly string[];
8004
- export function dashedName(event: string): string;
8005
- export default createLatticeGrid;
8006
- }
8007
-
8008
- declare module 'lattice-grid/modules/data-router' {
8009
- /**
8010
- * A record routed through a data router: any object. Its partition comes from
8011
- * the router's `key` and its identity within a grid from `rowKey`.
8012
- */
8013
- type RouterRecord = Record<string, unknown>;
8014
-
8015
- /** A per-route diff summary returned by `load`. */
8016
- interface RouteDiff { added: number; updated: number; removed: number }
8017
-
8018
- /** A predicate: a property value (`row[key] === value`) or a `fn(row)`. */
8019
- type RoutePredicate = unknown | ((row: RouterRecord) => boolean);
8020
-
8021
- /**
8022
- * Per-route reshaping options (v3, BACKLOG-0000887): `transform` maps/renames/
8023
- * derives each row before the grid sees it; `filter` gives the grid only the
8024
- * rows it admits; `sort` (a comparator or `{ key, dir }`) orders what the grid
8025
- * receives. `rowKey` overrides the router default. All optional.
8026
- */
8027
- interface RouteOptions {
8028
- rowKey?: (string | ((row: RouterRecord) => unknown));
8029
- transform?: (row: RouterRecord) => RouterRecord;
8030
- filter?: (row: RouterRecord) => boolean;
8031
- sort?: (((a: RouterRecord, b: RouterRecord) => number) | { key: string; dir?: 'asc' | 'desc' });
8032
- }
8033
-
8034
- /**
8035
- * A cross-grid selection relation (v2, BACKLOG-0000880): a key map (target
8036
- * rows whose `to` value is among the selected source rows' `from` values — an
8037
- * IN set), or a function handed the selected source rows that returns a
8038
- * target-row predicate.
8039
- */
8040
- type SelectionRelation =
8041
- | { from: string; to: string }
8042
- | ((selected: RouterRecord[]) => ((row: RouterRecord) => boolean));
8043
-
8044
- /**
8045
- * A data router: one arriving stream, partitioned by a property (or composite
8046
- * predicate), fanned out to a grid per partition (BACKLOG-0000879). Each grid
8047
- * sees only its slice, updated by keyed diff through the public
8048
- * `grid.rows.apply` path — no grid-core change, no cross-references between
8049
- * grids. Snapshots apply keyed diffs (unchanged rows never repaint); deltas add,
8050
- * update or remove in place by `rowKey`, preserving selection and scroll.
8051
- */
8052
- interface DataRouter {
8053
- /** Attach a grid behind a predicate; `opts` may reshape/filter/sort the route (v3). */
8054
- attach(grid: unknown, predicate: RoutePredicate, opts?: RouteOptions): DataRouter;
8055
- /** Attach the "rest" sink for records no explicit route matched. */
8056
- attachDefault(grid: unknown, opts?: RouteOptions): DataRouter;
8057
- /** Detach a grid; the host still owns and destroys it. */
8058
- detach(grid: unknown): DataRouter;
8059
- /** Apply a full snapshot as a keyed diff per grid; returns per-route counts. */
8060
- load(snapshot: RouterRecord[]): RouteDiff[];
8061
- /** Apply incremental deltas, routed and applied in place by `rowKey`. */
8062
- apply(deltas: { op: 'upsert' | 'delete'; row: RouterRecord }[]): void;
8063
- /**
8064
- * Link a source grid's selection to what a target grid receives (v2,
8065
- * BACKLOG-0000880): the target shows the subset of its partition the
8066
- * `relation` admits, re-pushed through the keyed-diff path. No selection
8067
- * shows the full partition; changes are debounced.
8068
- */
8069
- link(source: unknown, target: unknown, relation: SelectionRelation): DataRouter;
8070
- /** Apply any debounced selection refilter synchronously (for tests/determinism). */
8071
- flush(): DataRouter;
8072
- /** How many records matched no route. */
8073
- readonly unrouted: number;
8074
- /** Detach every grid and drop every link (the host destroys the grids themselves). */
8075
- destroy(): void;
8076
- }
8077
-
8078
- /**
8079
- * Create a data router that partitions one stream to many grids.
8080
- *
8081
- * `key` is the partition property or `fn(row)`; `rowKey` is the identity within
8082
- * a grid; `overlap` fans a record to every matching route (default: first match
8083
- * wins); `onUnrouted` receives records that match none; `selectionDebounce` is
8084
- * the debounce in ms for cross-grid selection refilters (default 16; `0` is
8085
- * synchronous).
8086
- */
8087
- export function createDataRouter(opts: {
8088
- key: (string | ((row: RouterRecord) => unknown));
8089
- rowKey?: (string | ((row: RouterRecord) => unknown));
8090
- overlap?: boolean;
8091
- onUnrouted?: (item: unknown) => void;
8092
- selectionDebounce?: number;
8093
- }): DataRouter;
8094
- export default createDataRouter;
8095
- }
8096
-
8097
- declare module 'lattice-grid/modules/gantt' {
8098
- /** One of the four dependency link types (finish-to-start, start-to-start, finish-to-finish, start-to-finish). */
8099
- export type GanttLinkType = 'FS' | 'SS' | 'FF' | 'SF';
8100
-
8101
- /** A scheduling constraint: pin the start, pin the finish, or schedule as late as possible. */
8102
- export type GanttConstraintType =
8103
- | 'must-start-on' | 'must-finish-on' | 'as-late-as-possible' | 'MSO' | 'MFO' | 'ALAP';
8104
-
8105
- /** A working-time calendar: a Monday–Friday preset, or explicit working weekdays and holidays. */
8106
- export type GanttCalendar =
8107
- | 'weekends'
8108
- | { workdays?: number[]; holidays?: Array<string | number | Date> };
8109
-
8110
- /**
8111
- * A task in a Gantt plan. Give a `duration` or a `start`+`end` (a day-number,
8112
- * ISO date string or `Date`; one is derived from the other). `milestone: true`
8113
- * (or `duration: 0`) is a zero-duration point. `parent` nests a task under a
8114
- * summary, whose window and progress are DERIVED from its children.
8115
- * `baselineStart`/`baselineEnd` (host-stored) drive planned-vs-actual variance;
8116
- * `constraint` pins or pulls the task; `assignee` and `height` feed the split
8117
- * view's grid panel.
8118
- */
8119
- export interface GanttTask {
8120
- id: string | number;
8121
- name?: string;
8122
- start?: number | string | Date;
8123
- end?: number | string | Date;
8124
- duration?: number;
8125
- percentComplete?: number;
8126
- milestone?: boolean;
8127
- parent?: string | number;
8128
- baselineStart?: number | string | Date;
8129
- baselineEnd?: number | string | Date;
8130
- baseline?: { start?: number | string | Date; end?: number | string | Date };
8131
- constraint?: GanttConstraintType;
8132
- constraintDate?: number | string | Date;
8133
- assignee?: string | string[];
8134
- assignees?: string[];
8135
- owner?: string;
8136
- /**
8137
- * Explicit resource assignments with fractional units (BACKLOG-0000948):
8138
- * `units` is a multiplier where 1 is a full-time booking. Use this when a
8139
- * task books a resource at less (or more) than 100%; a bare `assignee` is
8140
- * `units: 1`.
8141
- */
8142
- assignments?: Array<{ resource?: string; name?: string; id?: string; units?: number }>;
8143
- /** Leveling priority: a higher value is delayed last (default 0). */
8144
- priority?: number;
8145
- /** An explicit row height (px) for the split view; applied to both panels. */
8146
- height?: number;
8147
- /**
8148
- * The budgeted cost (BAC) for earned-value analysis (BACKLOG-0000958). When
8149
- * omitted the task's duration is used as the budget, giving schedule-only EVM.
8150
- */
8151
- cost?: number;
8152
- /**
8153
- * The actual cost incurred (ACWP) for earned-value analysis
8154
- * (BACKLOG-0000958). Left out, the task's cost variance/CPI are `null`.
8155
- */
8156
- actualCost?: number;
8157
- }
8158
-
8159
- /**
8160
- * Resource capacities for over-allocation detection and leveling
8161
- * (BACKLOG-0000948): either a list of resources with a capacity (max
8162
- * concurrent units, default 1) or a name→capacity map.
8163
- */
8164
- export type GanttResourceSpec =
8165
- | Array<{ id?: string; name?: string; resource?: string; capacity?: number; maxUnits?: number; max?: number; units?: number }>
8166
- | Record<string, number>;
8167
-
8168
- /**
8169
- * A typed dependency between two tasks (by id), with optional lag/lead. `type`
8170
- * defaults to `'FS'`; either endpoint may be a leaf or a summary.
8171
- *
8172
- * `type` also accepts the MS Project string shorthand — `'FS+2'`, `'SS-1'`
8173
- * (BACKLOG-0001072). It is normalised to the structured form on the way in, so
8174
- * `gantt.dependencies` always reads back `{ type, lag }` and there is no second
8175
- * internal representation. Giving both a shorthand lag and a conflicting `lag`
8176
- * field warns; the explicit field wins.
8177
- */
8178
- export interface GanttDependency {
8179
- from: string | number;
8180
- to: string | number;
8181
- type?: GanttLinkType | `${GanttLinkType}${'+' | '-'}${number}`;
8182
- lag?: number;
8183
- }
8184
-
8185
- /** The computed CPM values for one task (a leaf is scheduled, a summary derived). */
8186
- interface GanttScheduledTask {
8187
- id: string;
8188
- name: string;
8189
- duration: number;
8190
- es: number;
8191
- ef: number;
8192
- ls: number;
8193
- lf: number;
8194
- totalFloat: number;
8195
- critical: boolean;
8196
- percentComplete: number | null;
8197
- parent: string | null;
8198
- isSummary: boolean;
8199
- isMilestone: boolean;
8200
- children: string[];
8201
- /** The planned (baseline) window, present only when the task carries a baseline. */
8202
- baselineStart?: number | null;
8203
- baselineEnd?: number | null;
8204
- /** Variance vs the baseline (actual − planned, day-numbers); a positive value is a slip. */
8205
- startVariance?: number | null;
8206
- finishVariance?: number | null;
8207
- durationVariance?: number | null;
8208
- }
8209
-
8210
- /** An unhonourable scheduling constraint, reported rather than obeyed. */
8211
- interface GanttConflict {
8212
- id: string;
8213
- type: string;
8214
- at: number | null;
8215
- earliestFeasible: number;
8216
- }
8217
-
8218
- /** A CPM schedule result: per-task dates/float and the critical path, or an error. */
8219
- interface GanttSchedule {
8220
- ok: boolean;
8221
- error?: { code: string; message: string; cycle?: string[] };
8222
- tasks?: Map<string, GanttScheduledTask>;
8223
- order?: string[];
8224
- critical?: string[];
8225
- criticalPaths?: string[][];
8226
- projectStart?: number;
8227
- projectFinish?: number;
8228
- projectDuration?: number;
8229
- /** Constraints a predecessor made infeasible (empty when all are satisfied). */
8230
- conflicts?: GanttConflict[];
8231
- /** Whether a working-time calendar was applied. */
8232
- calendar?: boolean;
8233
- /** The resource over-allocations for this schedule (BACKLOG-0000948). */
8234
- overAllocations?: GanttOverAllocation[];
8235
- /** The full resource-load report for this schedule (BACKLOG-0000948). */
8236
- resourceLoad?: GanttResourceLoad;
8237
- }
8238
-
8239
- /** One contiguous load segment for a resource: how many units are booked over a span. */
8240
- interface GanttResourceSegment {
8241
- start: number;
8242
- end: number;
8243
- load: number;
8244
- taskIds: string[];
8245
- }
8246
-
8247
- /** A resource booked beyond its capacity across concurrent tasks (BACKLOG-0000948). */
8248
- interface GanttOverAllocation {
8249
- resource: string;
8250
- capacity: number;
8251
- start: number;
8252
- end: number;
8253
- load: number;
8254
- taskIds: string[];
8255
- }
8256
-
8257
- /** The per-resource load and the over-allocations across a schedule (BACKLOG-0000948). */
8258
- interface GanttResourceLoad {
8259
- ok: boolean;
8260
- resources: Array<{ resource: string; capacity: number; peak: number; segments: GanttResourceSegment[] }>;
8261
- overAllocations: GanttOverAllocation[];
8262
- byResource: Map<string, { capacity: number; peak: number; segments: GanttResourceSegment[] }>;
8263
- }
8264
-
8265
- /** The result of resource leveling: the shifted tasks and what moved (BACKLOG-0000948). */
8266
- interface GanttLevelResult {
8267
- ok: boolean;
8268
- resolved?: boolean;
8269
- tasks?: GanttTask[];
8270
- schedule?: GanttSchedule;
8271
- moves?: Array<{ id: string; from: number; to: number; delay: number }>;
8272
- remaining?: GanttOverAllocation[];
8273
- error?: { code: string; message: string };
8274
- }
8275
-
8276
- /** A placement violation flagged by `findViolations`. */
8277
- interface GanttViolation {
8278
- id: string;
8279
- placedStart: number;
8280
- earliestStart: number;
8281
- by: number;
8282
- }
8283
-
8284
- /** The four link types, in documented order. */
8285
- export const LINK_TYPES: readonly GanttLinkType[];
8286
-
8287
- /** Error codes the scheduler reports (rather than throwing) on bad input. */
8288
- export const SCHEDULE_ERROR: Record<string, string>;
8289
-
8290
- /**
8291
- * Compute the CPM schedule for a set of tasks and dependencies: forward and
8292
- * backward passes over the leaf tasks honouring FS/SS/FF/SF + lag, slack/float
8293
- * and the zero-float critical path, with summaries derived from their children,
8294
- * milestones scheduled as points, and dependency cycles refused (never looped).
8295
- */
8296
- export function computeSchedule(tasks: GanttTask[], deps?: GanttDependency[], options?: { projectStart?: number | string | Date; deadline?: number | string | Date; calendar?: GanttCalendar | null }): GanttSchedule;
8297
-
8298
- /** The tasks placed earlier than their earliest feasible start (manual validation). */
8299
- export function findViolations(tasks: GanttTask[], schedule: GanttSchedule): GanttViolation[];
8300
-
8301
- /** Format an engine day-number as an ISO calendar date (`YYYY-MM-DD`, UTC). */
8302
- export function toISODate(day: number): string | null;
8303
-
8304
- /** Earned-value metrics for one task or the whole project (BACKLOG-0000958). */
8305
- interface GanttEarnedValueRow {
8306
- id: string;
8307
- name: string;
8308
- isSummary: boolean;
8309
- isMilestone: boolean;
8310
- percentComplete: number | null;
8311
- /** Whether a baseline (not the fallback scheduled window) drove PV. */
8312
- hasBaseline: boolean;
8313
- /** Whether any actual cost fed AC (else AC/CV/CPI are null). */
8314
- hasActualCost: boolean;
8315
- /** Budget at completion (the task's cost, or its duration when no cost). */
8316
- bac: number;
8317
- /** Planned Value (BCWS): budgeted cost of the work scheduled by the status date. */
8318
- pv: number;
8319
- /** Earned Value (BCWP): budgeted cost of the work performed (BAC × %complete). */
8320
- ev: number;
8321
- /** Actual Cost (ACWP): what the work performed actually cost, or null. */
8322
- ac: number | null;
8323
- /** Schedule Variance (EV − PV); positive is ahead of schedule. */
8324
- sv: number;
8325
- /** Cost Variance (EV − AC); positive is under budget; null without AC. */
8326
- cv: number | null;
8327
- /** Schedule Performance Index (EV / PV); null when PV is zero. */
8328
- spi: number | null;
8329
- /** Cost Performance Index (EV / AC); null without AC or when AC is zero. */
8330
- cpi: number | null;
8331
- }
8332
-
8333
- /** The earned-value result at a status date (BACKLOG-0000958). */
8334
- interface GanttEarnedValue {
8335
- ok: boolean;
8336
- error?: { code: string; message: string };
8337
- /** The status date the metrics were evaluated at (day-number). */
8338
- statusDate?: number;
8339
- /** Every task keyed by id (leaf, summary and derived). */
8340
- byTask?: Map<string, GanttEarnedValueRow>;
8341
- /** The same rows in schedule order. */
8342
- rows?: GanttEarnedValueRow[];
8343
- /** The project total, rolled up as money sums of the leaves. */
8344
- project?: GanttEarnedValueRow;
8345
- }
8346
-
8347
- /**
8348
- * Compute earned-value management (EVM) metrics for a scheduled plan at a
8349
- * status date (BACKLOG-0000958): PV/BCWS from the baseline, EV/BCWP from
8350
- * %complete, AC/ACWP from the per-task `actualCost`, and the derived SV/CV and
8351
- * SPI/CPI — per leaf, rolled up to summaries and the project. The math is
8352
- * implemented locally in the module (no core-compute dependency).
8353
- */
8354
- export function computeEarnedValue(
8355
- tasks: GanttTask[],
8356
- schedule: GanttSchedule,
8357
- options?: { statusDate?: number | string | Date; costField?: string; actualCostField?: string },
8358
- ): GanttEarnedValue;
8359
-
8360
- /** A headless Gantt controller: holds the model, recomputes on edits, emits changes. */
8361
- interface Gantt {
8362
- readonly tasks: GanttTask[];
8363
- readonly dependencies: GanttDependency[];
8364
- readonly schedule: GanttSchedule | null;
8365
- readonly critical: string[];
8366
- /** Constraints the latest schedule could not honour (empty when all are satisfied). */
8367
- readonly conflicts: GanttConflict[];
8368
- readonly autoSchedule: boolean;
8369
- readonly grid: unknown;
8370
- /** The over-allocations from the latest schedule (BACKLOG-0000948). */
8371
- readonly overAllocations: GanttOverAllocation[];
8372
- /** The latest resource-load report, or null before a successful schedule (BACKLOG-0000948). */
8373
- readonly resourceLoad: GanttResourceLoad | null;
8374
- setTasks(tasks: GanttTask[]): GanttSchedule;
8375
- setDependencies(deps: GanttDependency[]): GanttSchedule;
8376
- applyEdit(patch: { id: string | number; start?: number; end?: number; duration?: number }, editOpts?: { writeBack?: boolean }): GanttSchedule;
8377
- compute(): GanttSchedule;
8378
- findViolations(): GanttViolation[];
8379
- /**
8380
- * Compute the resource load and over-allocations on demand (BACKLOG-0000948),
8381
- * optionally overriding the capacities for this call.
8382
- */
8383
- resources(loadOpts?: { resources?: GanttResourceSpec; defaultCapacity?: number }): GanttResourceLoad;
8384
- /**
8385
- * Resolve resource over-allocation by shifting tasks later — resource
8386
- * leveling (BACKLOG-0000948). Honours the CPM dependencies and the
8387
- * working-time calendar. Mutates the model unless `{ dryRun: true }`; with
8388
- * `{ writeBack: true }` and a bound grid the moved tasks are pushed through
8389
- * the grid's edit surface.
8390
- */
8391
- level(levelOpts?: {
8392
- dryRun?: boolean;
8393
- writeBack?: boolean;
8394
- priorityField?: string;
8395
- maxIterations?: number;
8396
- resources?: GanttResourceSpec;
8397
- defaultCapacity?: number;
8398
- }): GanttLevelResult;
8399
- /** Export the scheduled tasks as CSV; `{ dates: true }` writes ISO dates. */
8400
- toCSV(csvOpts?: { dates?: boolean }): string;
8401
- /**
8402
- * Export the current plan as Microsoft Project (MSPDI) XML (BACKLOG-0000950):
8403
- * tasks, dependencies, constraints, baseline, resources and assignments, plus
8404
- * the working-time calendar, serialised with the computed schedule.
8405
- */
8406
- toMSPDI(xmlOpts?: { hoursPerDay?: number; projectName?: string }): string;
8407
- /**
8408
- * The live consumer surface, mirroring `grid.rows.apply`, so a Data Router
8409
- * can drive the Gantt like any other view. Keyed by the controller's rowKey.
8410
- */
8411
- readonly rows: {
8412
- apply(change: { add?: GanttTask[]; update?: GanttTask[]; remove?: Array<string | GanttTask> }): {
8413
- added: GanttTask[]; updated: GanttTask[]; removed: string[];
8414
- };
8415
- };
8416
- on(event: 'schedule' | 'error', fn: (payload: unknown) => void): () => void;
8417
- off(event: 'schedule' | 'error', fn: (payload: unknown) => void): void;
8418
- /**
8419
- * Render the plan into a container as an SVG timeline (bars, dependency
8420
- * arrows, critical-path highlight, today line, non-working shading,
8421
- * milestones, progress). The view redraws when the schedule recomputes.
8422
- */
8423
- mount(container: unknown, options?: {
8424
- /**
8425
- * The plot width. `'container'` (the default) measures the element it was
8426
- * mounted into and keeps following it, so a plan in a tab, drawer,
8427
- * accordion or split pane fits without the host writing a
8428
- * `ResizeObserver` (BACKLOG-0001079); a container with no box yet holds a
8429
- * 720px fallback rather than drawing at zero. A number is honoured
8430
- * exactly and installs no observer. Ignored under `zoom`, which warns.
8431
- */
8432
- width?: number | 'container';
8433
- rowHeight?: number;
8434
- labelWidth?: number;
8435
- rowLabels?: boolean;
8436
- showArrows?: boolean;
8437
- showCritical?: boolean;
8438
- showProgress?: boolean;
8439
- dateAxis?: boolean;
8440
- /**
8441
- * The today line, as a plan day-number or a calendar date. A date is
8442
- * converted into plan space through `projectEpoch` (BACKLOG-0001079), so
8443
- * "put the line on the real today" is expressible for a relative plan.
8444
- */
8445
- today?: number | string | Date;
8446
- /**
8447
- * The calendar date plan day 0 stands for (BACKLOG-0001079).
8448
- *
8449
- * Display-only: axis ticks, bar labels, tooltips, screen-reader text and
8450
- * the built-in `'weekends'` shading move with it; the schedule, `getState`
8451
- * and the CSV/MSPDI exports do not. Without it, the engine's contract makes
8452
- * day 0 the Unix epoch, which is why a plan written as day offsets renders
8453
- * as January 1970. A host-supplied `nonWorking` function still receives raw
8454
- * plan days.
8455
- */
8456
- projectEpoch?: number | string | Date | null;
8457
- nonWorking?: 'weekends' | ((day: number) => boolean);
8458
- label?: 'name' | 'percent' | 'dates' | 'none' | ((task: GanttScheduledTask) => string);
8459
- /** Whether bars can be dragged to move/resize (default true). */
8460
- editable?: boolean;
8461
- /** Pixels from a bar's right edge that begin a resize rather than a move. */
8462
- resizeZone?: number;
8463
- /** Time-scale zoom: a level, or raw pixels-per-day. Omit to fit the width. */
8464
- zoom?: 'day' | 'week' | 'month' | 'quarter' | number;
8465
- /** Scroll so the today line is in view after drawing. */
8466
- scrollToToday?: boolean;
8467
- /** Show a hover tooltip (dates/duration/%/slack); default true. */
8468
- tooltip?: boolean;
8469
- /** Group tasks into swimlanes by a task property name or `fn(task)`. */
8470
- groupBy?: string | ((task: GanttTask) => unknown);
8471
- /** Keyboard editing + focusable bars + ARIA announcements (default true). */
8472
- keyboard?: boolean;
8473
- /** Days a keyboard arrow moves/resizes a task (default 1). */
8474
- moveStep?: number;
8475
- }): unknown;
8476
- /**
8477
- * Mount the JOINED split view (BACKLOG-0000938): one continuous, row-aligned
8478
- * surface with a left task-grid panel (Task Name tree with expand/collapse,
8479
- * assignee avatars, a circular % ring, plus any host columns) and the right
8480
- * timeline, sharing a single vertical scroll so every grid row lines up
8481
- * exactly with its bar row. The timeline scrolls horizontally on its own.
8482
- * Composes the controller's schedule; makes no change to grid core.
8483
- */
8484
- mountSplit(container: unknown, options?: {
8485
- height?: number;
8486
- rowHeight?: number;
8487
- headerHeight?: number;
8488
- gridWidth?: number;
8489
- indent?: number;
8490
- zoom?: 'day' | 'week' | 'month' | 'quarter' | number;
8491
- today?: number;
8492
- nonWorking?: 'weekends' | ((day: number) => boolean);
8493
- calendar?: GanttCalendar | null;
8494
- showArrows?: boolean;
8495
- showProgress?: boolean;
8496
- showBaseline?: boolean;
8497
- barLabel?: 'name' | 'percent' | 'dates' | 'none' | ((task: GanttScheduledTask) => string);
8498
- /**
8499
- * Surface earned-value metrics in `kind: 'evm'` columns (BACKLOG-0000958).
8500
- * `true` computes EVM at the today line (or the project finish); an object
8501
- * overrides the status date and the cost field names.
8502
- */
8503
- evm?: boolean | { statusDate?: number | string | Date; costField?: string; actualCostField?: string };
8504
- columns?: Array<{ key: string; title?: string; width?: number; kind?: 'name' | 'assignee' | 'progress' | 'evm'; metric?: 'bac' | 'pv' | 'ev' | 'ac' | 'sv' | 'cv' | 'spi' | 'cpi'; digits?: number; render?: (task: GanttScheduledTask, ctx: { rawTask: GanttTask; depth: number }) => unknown }>;
8505
- }): unknown;
8506
- /**
8507
- * Capture a baseline (planned) snapshot of the current schedule as HOST data
8508
- * (this does not mutate the tasks). Store it and feed it back as
8509
- * `baselineStart`/`baselineEnd` task fields to get variance and ghost bars.
8510
- */
8511
- captureBaseline(): Array<{ id: string; baselineStart: number; baselineEnd: number; baselineDuration: number }>;
8512
- /**
8513
- * Compute earned-value (EVM) metrics for the current plan at a status date
8514
- * (BACKLOG-0000958): PV/EV/AC and the derived SV/CV/SPI/CPI per task, rolled
8515
- * up to summaries and the project. Budget (BAC) is the task's `cost`, or its
8516
- * duration when no cost is given; AC comes from `actualCost`.
8517
- */
8518
- earnedValue(evmOpts?: { statusDate?: number | string | Date; costField?: string; actualCostField?: string }): GanttEarnedValue;
8519
- /** Detach the mounted view, if any. The host still owns the container. */
8520
- unmount(): void;
8521
- /** The mounted view, or null. */
8522
- readonly view: unknown;
8523
- destroy(): void;
8524
- }
8525
-
8526
- /**
8527
- * Create a Gantt controller over a task list and a dependency list. Computes
8528
- * the CPM schedule immediately and again on every `setTasks`/`setDependencies`/
8529
- * `applyEdit`, emitting `schedule` on success and `error` on a cycle or bad
8530
- * input. `grid` is stored for the write-back binding; `autoSchedule` requests
8531
- * dependent cascading.
8532
- */
8533
- export function createGantt(opts?: {
8534
- tasks?: GanttTask[];
8535
- dependencies?: GanttDependency[];
8536
- /** The schedule anchor: a day-number, ISO date string or Date. It only sets the floor a task with no predecessor starts on; it does not change how the schedule is computed. */
8537
- projectStart?: number | string | Date;
8538
- /** A project deadline (a day-number, ISO string or Date); tasks that cannot meet it get negative float. */
8539
- deadline?: number | string | Date;
8540
- /** A working-time calendar: skip weekends/holidays, durations in working days. */
8541
- calendar?: GanttCalendar | null;
8542
- /** Resource capacities for over-allocation detection and leveling (BACKLOG-0000948). */
8543
- resources?: GanttResourceSpec;
8544
- /** The capacity for a resource with none stated (default 1 = one full-time booking). */
8545
- defaultCapacity?: number;
8546
- autoSchedule?: boolean;
8547
- grid?: unknown;
8548
- /** Map task fields to grid column ids to enable drag write-back. */
8549
- columns?: { start?: string; end?: string; duration?: string };
8550
- /** Task identity for the live `rows.apply` surface (a field or fn); default 'id'. */
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)>;
8572
- /** Auto-mount into this element at construction. */
8573
- element?: unknown;
8574
- }): Gantt;
8575
- export default createGantt;
8576
-
8577
- /** The model {@link importMSPDI} returns and {@link exportMSPDI} takes. */
8578
- interface GanttMSPDIModel {
8579
- tasks: GanttTask[];
8580
- dependencies?: GanttDependency[];
8581
- resources?: GanttResourceSpec;
8582
- projectStart?: number | string | Date;
8583
- calendar?: GanttCalendar | null;
8584
- schedule?: GanttSchedule;
8585
- }
8586
-
8587
- /**
8588
- * Import a Microsoft Project (MSPDI) XML document (BACKLOG-0000950) into the
8589
- * module's model: the task tree, typed dependencies with lag, constraints,
8590
- * baseline, %complete, resources with capacity, the resource assignments, and
8591
- * the working-time calendar. The result is ready to pass to {@link createGantt}.
8592
- */
8593
- export function importMSPDI(xml: string, opts?: { hoursPerDay?: number }): {
8594
- ok: boolean;
8595
- error?: string;
8596
- tasks: GanttTask[];
8597
- dependencies: GanttDependency[];
8598
- resources: Array<{ id: string; name: string; capacity: number }>;
8599
- projectStart?: number;
8600
- calendar?: null | { workdays: number[]; holidays: number[] };
8601
- };
8602
-
8603
- /**
8604
- * Export a Gantt model to Microsoft Project (MSPDI) XML (BACKLOG-0000950). A
8605
- * scheduled model may be passed so start/finish dates are the computed ones.
8606
- */
8607
- export function exportMSPDI(model: GanttMSPDIModel, opts?: { hoursPerDay?: number; projectName?: string }): string;
8608
- }
8609
-
8610
- declare module 'lattice-grid/modules/webcomponent' {
8611
- /**
8612
- * Register `<lattice-grid>`.
8613
- *
8614
- * This module carries the grid inside it. Use it *or* `createGrid` in one
8615
- * page, never both: two copies keep separate registries, and a renderer
8616
- * registered through one will not appear in the other.
8617
- *
8618
- * The live grid is reached through the element's `grid` getter: `el.grid` is
8619
- * the same `Grid` the vanilla `createGrid` returns, or null while the element
8620
- * is disconnected.
8621
- */
8622
- export function defineLatticeGrid(tag?: string): void;
8623
- /**
8624
- * Build the `<lattice-grid>` element class. The one argument is the grid
8625
- * factory the element creates its grid with — `createGrid`-shaped, and
8626
- * defaulting to it — injectable for tests. Returns the class, or `null`
8627
- * where `HTMLElement` is undefined (a Node import, a server-side pass).
8628
- */
8629
- export function createLatticeGridElement(
8630
- factory?: (element: Element, config: GridConfig) => Grid,
8631
- ): typeof HTMLElement | null;
8632
- export const TAG_NAME: string;
8633
- export const EVENT_PREFIX: string;
8634
- export const ATTRIBUTE_CONFIG: Readonly<Record<string, unknown>>;
8635
- export function observedAttributeNames(): string[];
8636
- export function domEventName(event: string): string;
8637
- export class GridElementController {}
8638
- // Core factories re-exported from this module so they bind to the one engine
8639
- // the element already carries: a type built with these here shares the
8640
- // element's registry rather than a second copy's (BACKLOG-0000787). Typed by
8641
- // reference to the base package.
8642
- export { createCurrencyType, createUnitType, registerUnitSystem, createStat } from 'lattice-grid';
8643
- export default defineLatticeGrid;
8644
- }
8645
-
8646
- declare module 'lattice-grid/modules/htmx' {
8647
- /**
8648
- * The htmx integration, which re-exports the base API alongside its own,
8649
- * a page using it imports this and never the base package as well.
8650
- */
8651
- export function createGrid(element: Element, config: GridConfig): Grid;
8652
- export function autoInit(root?: ParentNode): Grid[];
8653
- /**
8654
- * Wire the htmx lifecycle events on a document: grids are built in each
8655
- * swapped-in fragment, released before htmx detaches one, and their view
8656
- * state carried across history navigation. Called once on import against
8657
- * the global `document`; call it again only for another document. Returns
8658
- * the function that removes every listener it installed.
8659
- */
8660
- export function attach(doc?: Document): () => void;
8661
- export function initWithin(root: ParentNode): Grid[];
8662
- export function destroyWithin(root: ParentNode): void;
8663
- export function gridElementsWithin(root: ParentNode): Element[];
8664
- export function hydrateTable(table: Element, config?: GridConfig): Grid;
8665
- export function readTable(table: Element): { columns: Column[]; rows: unknown[] };
8666
- export function rowsFromFragment(fragment: ParentNode): unknown[];
8667
- export function rowsFromJson(text: string): unknown[];
8668
- /**
8669
- * Parse a response into rows by its content type: JSON through
8670
- * `rowsFromJson`, anything else through `rowsFromFragment` against the
8671
- * columns given. The fragment arrives already parsed; this never touches
8672
- * `DOMParser` or `innerHTML`. Returns the rows and, when the body carried
8673
- * one, the total.
8674
- */
8675
- export function ingestResponse(
8676
- response: { contentType: string; text?: string; fragment?: ParentNode },
8677
- columns: { field: string }[],
8678
- ): { rows: unknown[]; total: number | undefined };
8679
- /**
8680
- * Drive server-side sort and filter through htmx. `trigger` is the element
8681
- * carrying the htmx request attributes (`hx-get`, `hx-target`,
8682
- * `hx-trigger="lattice:query-changed"`); the grid's query parameters are
8683
- * merged into that element's request and its response ingested. Returns the
8684
- * function that detaches everything this attached.
8685
- */
8686
- export function driveServerMode(
8687
- grid: Grid,
8688
- trigger: Element,
8689
- opts?: { columns?: { field: string }[] },
8690
- ): () => void;
8691
- /**
8692
- * Load rows in chunks as the user nears the end of what is loaded.
8693
- * `sentinelEl` is the element carrying `hx-get` and
8694
- * `hx-trigger="revealed, lattice:scroll-near-end"`; `threshold` is how many
8695
- * rows from the end counts as near (default 20). Returns the function that
8696
- * detaches everything this attached.
8697
- */
8698
- export function driveInfiniteScroll(
8699
- grid: Grid,
8700
- sentinelEl: Element,
8701
- opts?: { columns?: { field: string }[]; threshold?: number },
8702
- ): () => void;
8703
- export function driveOobUpdates(grid: Grid, opts?: object): () => void;
8704
- export function serialiseState(grid: Grid): string;
8705
- export function restoreState(grid: Grid, state: string): void;
8706
- export function saveStateWithin(root: ParentNode): void;
8707
- export function restoreStateWithin(root: ParentNode): void;
8708
- export function queryParams(grid: Grid): Record<string, string>;
8709
- export function warnIfLargeHtmlPayload(rows: number): void;
8710
- export const QUERY_CHANGED_EVENT: string;
8711
- export const SCROLL_NEAR_END_EVENT: string;
8712
- export const HTML_ROW_WARNING_THRESHOLD: number;
8713
- // The core factory surface this module re-exports, so an htmx page builds its
8714
- // configured columns (a currency type, a unit type, a stat) from the one
8715
- // engine it already carries rather than a second copy (BACKLOG-0000786).
8716
- // Typed by reference to the base package; names the base package leaves
8717
- // untyped stay untyped here too.
8718
- export {
8719
- createHeadlessGrid, version, getVersion, Grid, Registry, registerModules,
8720
- createRadixType, createUnitType, registerUnitSystem, defineUnit, UNIT_SYSTEMS, parseUnit, formatUnit,
8721
- createCurrencyType, parseMoney, formatMoney, convertMoney, rateFunction, MISSING_RATE,
8722
- Messages, createMessages, auditCatalogue,
8723
- EN_GB, MESSAGE_KEYS, DEFAULT_LOCALE, formatList, resolveLocale, LOCALES, resolveCatalogue,
8724
- EN_US, FR_FR, FR_CA, IT_IT, ES_ES, PT_BR, DE_DE, NL_NL, SV_SE, DA_DK, NB_NO, FI_FI,
8725
- PL_PL, CS_CZ, HU_HU, RO_RO, UK_UA, EL_GR, JA_JP, AR, AR_SA,
8726
- Window, openWindow, WINDOW_KINDS,
8727
- evaluateFormula, referencesOf, looksLikeFormula, compileRules, ingest, ingestSync,
8728
- createPushdownSource, planQuery, splitFilters, applyResidual, capabilitiesOf, resolveMutate, NO_CAPABILITIES,
8729
- odataAdapter, restAdapter, dfqlAdapter, duckdbAdapter,
8730
- createStat, deltaOf, toneOf,
8731
- } from 'lattice-grid';
8732
- // American licence aliases mirror the base package (dom/index.js).
8733
- export { setLicence as setLicense, licenceInfo as licenseInfo, licenceState as licenseState } from 'lattice-grid';
8734
- }
8735
-
8736
- declare module 'lattice-grid/modules/dhtmlx-compat' {
8737
- /**
8738
- * A dhtmlx Grid-shaped API over Lattice, for migrating a piece at a time.
8739
- *
8740
- * The module shares the page's one core rather than bundling its own: the
8741
- * grid it builds comes from the `lattice-grid` package the app already loads
8742
- * (or the `LatticeGrid` global a script tag publishes), so a licence set on
8743
- * that core applies to these grids too. Load the core alongside this module —
8744
- * a bundler wires the peer import for you; a `<script src>` page loads the
8745
- * global build first.
8746
- */
8747
- export class Grid {
8748
- constructor(container: Element | string, config?: object);
8749
- }
8750
- export default Grid;
8751
- }
8752
-
8753
- declare module 'lattice-grid/modules/devtools' {
8754
- /**
8755
- * The devtools panel, including the accessibility checks.
8756
- *
8757
- * The grid is handed in rather than imported: a module may depend on nothing
8758
- * in core, or the bundler inlines the whole grid into it.
8759
- */
8760
- export function createDevtools(opts: { grid: Grid; container?: Element }): {
8761
- element: Element;
8762
- refresh(): void;
8763
- destroy(): void;
8764
- };
8765
- export function expose(grid: Grid, name?: string): void;
8766
- /**
8767
- * Whether the console entry point is compiled in. A build that replaces the
8768
- * activation token with `false` removes the global entirely; in every other
8769
- * build this is `true`.
8770
- */
8771
- export const CONSOLE_ACTIVATION: boolean;
8772
- export default createDevtools;
8773
- }
8774
-
8775
- declare module 'lattice-grid/modules/mock-socket' {
8776
- /** One record on a feed: any object. Its partition comes from a property and its identity from `rowKey`. */
8777
- type FeedRow = Record<string, unknown>;
8778
-
8779
- /** One change in a delta batch, in the shape the data router applies. */
8780
- interface FeedChange { op: 'upsert' | 'delete'; row: FeedRow }
8781
-
8782
- /**
8783
- * A message on the wire. A snapshot carries the full opening set; a delta
8784
- * carries the changes since. The reader parses `event.data` and switches on
8785
- * `kind`, exactly as against a real feed that framed its messages the same way.
8786
- */
8787
- interface FeedMessage {
8788
- kind: 'snapshot' | 'delta';
8789
- /** Present on a snapshot: the full opening set of rows. */
8790
- rows?: FeedRow[];
8791
- /** Present on a delta: the changes to apply. */
8792
- changes?: FeedChange[];
8793
- }
8794
-
8795
- /** A feed: any iterator that yields a snapshot first, then deltas forever. */
8796
- type Feed = Iterator<FeedMessage>;
8797
-
8798
- /**
8799
- * A serverless stand-in for a live `WebSocket`. It presents the same surface
8800
- * as the browser's `WebSocket` — `readyState` and the state constants,
8801
- * `onopen`/`onmessage`/`onclose`/`onerror`, `addEventListener`, `send` and
8802
- * `close` — so the code that reads it does not change when it is swapped for a
8803
- * real socket. It opens after a short delay, emits the feed's first value as a
8804
- * snapshot, then pumps one value per tick as a delta.
8805
- */
8806
- export class MockWebSocket {
8807
- static readonly CONNECTING: 0;
8808
- static readonly OPEN: 1;
8809
- static readonly CLOSING: 2;
8810
- static readonly CLOSED: 3;
8811
- readonly CONNECTING: 0;
8812
- readonly OPEN: 1;
8813
- readonly CLOSING: 2;
8814
- readonly CLOSED: 3;
8815
- readyState: number;
8816
- url: string;
8817
- onopen: ((event: { type: string }) => void) | null;
8818
- onmessage: ((event: { type: string; data: string }) => void) | null;
8819
- onclose: ((event: { type: string; code: number; reason: string; wasClean: boolean }) => void) | null;
8820
- onerror: ((event: { type: string; error: unknown }) => void) | null;
8821
- /**
8822
- * @param init the feed and its timing: `feed` (snapshot first, then deltas);
8823
- * `rate` ms between deltas (default 1000); `jitter` random plus-or-minus ms
8824
- * per gap (default 0); `seed` for that jitter (default 1); `snapshotDelay`
8825
- * ms before opening (default 60); `pauseWhenHidden` stops while the tab is
8826
- * hidden (default true); `url` a cosmetic address.
8827
- */
8828
- constructor(init: {
8829
- feed: Feed;
8830
- rate?: number;
8831
- jitter?: number;
8832
- seed?: number;
8833
- snapshotDelay?: number;
8834
- pauseWhenHidden?: boolean;
8835
- url?: string;
8836
- });
8837
- addEventListener(type: string, fn: (event: unknown) => void): void;
8838
- removeEventListener(type: string, fn: (event: unknown) => void): void;
8839
- /** A real socket sends upstream; here it is accepted and ignored. */
8840
- send(data?: unknown): void;
8841
- /** Stop the feed until `resume()`; the socket stays open (a demo/test affordance). */
8842
- pause(): void;
8843
- /** Resume a paused feed. */
8844
- resume(): void;
8845
- /** Close the socket, stop the feed and emit a clean `close`. */
8846
- close(): void;
8847
- }
8848
-
8849
- /**
8850
- * mulberry32: a small seeded pseudo-random generator, so a custom feed can be
8851
- * seeded the same way the shipped ones are. The same seed yields the same
8852
- * sequence of values in `[0, 1)`.
8853
- */
8854
- export function rng(seed: number): () => number;
8855
-
8856
- /**
8857
- * A mixed operations feed — orders, shipments and incidents across three
8858
- * regions plus a throughput rollup — the Data Router tutorial partitions
8859
- * across several grids and a chart from one source. Yields a snapshot, then
8860
- * deltas forever. Seedable for a repeatable stream.
8861
- */
8862
- export function opsFeed(options?: {
8863
- seed?: number;
8864
- orders?: number;
8865
- shipments?: number;
8866
- incidents?: number;
8867
- batch?: number;
8868
- }): Generator<FeedMessage>;
8869
-
8870
- /**
8871
- * A market-data feed: instruments whose prices random-walk each tick, each
8872
- * record carrying `type: 'price'`, `symbol`, `last`, `chg` and a bid/ask. The
8873
- * price/random-walk feed behind the trading-terminal tutorial. Yields a
8874
- * snapshot, then deltas forever. Seedable for a repeatable stream.
8875
- */
8876
- export function priceFeed(options?: {
8877
- seed?: number;
8878
- symbols?: { symbol: string; last: number }[];
8879
- move?: number;
8880
- batch?: number;
8881
- spread?: number;
8882
- }): Generator<FeedMessage>;
8883
-
8884
- export default MockWebSocket;
8885
- }
8886
-
8887
- declare module 'lattice-grid/modules/kanban' {
8888
- /** A row backing a card: any object. Its column comes from `columnProperty` and its identity from `rowKey`. */
8889
- type KanbanRow = Record<string, unknown>;
8890
-
8891
- /**
8892
- * A card model — one row as it appears on the board. `fields` holds the
8893
- * resolved display text for each mapped card field; `columnId` is the column
8894
- * the card sits in; `points` is the numeric points value (0 when absent).
8895
- * `swimlane`/`sprint`/`epic`/`order` are read from their configured properties
8896
- * and carried for the later cycles that render them.
8897
- */
8898
- interface KanbanCard {
8899
- key: unknown;
8900
- row: KanbanRow;
8901
- columnId: string | null;
8902
- points: number;
8903
- hasPoints: boolean;
8904
- order?: unknown;
8905
- swimlane?: unknown;
8906
- sprint?: unknown;
8907
- epic?: unknown;
8908
- fields: Record<string, string>;
8909
- }
8910
-
8911
- /** A column with its cards and aggregates. `over` is true when `count` exceeds `wipLimit`. */
8912
- interface KanbanColumn {
8913
- id: string;
8914
- title: string;
8915
- color: string | null;
8916
- wipLimit: number | null;
8917
- collapsed: boolean;
8918
- cards: KanbanCard[];
8919
- count: number;
8920
- points: number;
8921
- over: boolean;
8922
- }
8923
-
8924
- /** A column definition: an id string, or an object configuring one column. */
8925
- type KanbanColumnDef = string | {
8926
- id: string;
8927
- title?: string;
8928
- color?: string;
8929
- wipLimit?: number;
8930
- collapsed?: boolean;
8931
- /**
8932
- * A per-column SLA override (BACKLOG-0000960): a lone threshold read as the
8933
- * breach level, or a `{ warn, breach }` pair. Overrides the global `sla`
8934
- * thresholds for cards in this column (precedence: lane → column → global).
8935
- */
8936
- sla?: KanbanSlaThreshold | { warn?: KanbanSlaThreshold; breach?: KanbanSlaThreshold };
8937
- /** A per-column warn threshold — the shorthand for `sla: { warn }`. */
8938
- slaWarn?: KanbanSlaThreshold;
8939
- /** A per-column breach threshold — the shorthand for `sla: { breach }`. */
8940
- slaBreach?: KanbanSlaThreshold;
8941
- };
8942
-
8943
- /** A card field editor handle returned by a host editor factory. */
8944
- interface KanbanEditor {
8945
- el: HTMLElement;
8946
- focus?: () => void;
8947
- destroy?: () => void;
8948
- }
8949
-
8950
- /** A card field mapping: a property path, a function, or an object opting into inline edit. */
8951
- type KanbanFieldMap = string | ((row: KanbanRow) => unknown) | {
8952
- field: string;
8953
- edit?: boolean;
8954
- editor?: (ctx: { card: KanbanCard; field: string; value: string; commit: (value: unknown) => void; cancel: () => void }) => KanbanEditor;
8955
- };
8956
-
8957
- /** The field-to-property mapping that drives the card template. */
8958
- interface KanbanCardMap {
8959
- title?: KanbanFieldMap;
8960
- subtitle?: KanbanFieldMap;
8961
- labels?: KanbanFieldMap;
8962
- assignee?: KanbanFieldMap;
8963
- due?: KanbanFieldMap;
8964
- cover?: KanbanFieldMap;
8965
- progress?: KanbanFieldMap;
8966
- badges?: KanbanFieldMap;
8967
- accent?: KanbanFieldMap;
8968
- [field: string]: KanbanFieldMap | undefined;
8969
- }
8970
-
8971
- /** Granular readonly: the whole board, or selectively by column id and card key. */
8972
- type KanbanReadonly = boolean | {
8973
- board?: boolean;
8974
- columns?: Record<string, boolean>;
8975
- cards?: Record<string, boolean>;
8976
- };
8977
-
8978
- /** The payload every board event carries. */
8979
- interface KanbanEvent {
8980
- card: KanbanCard;
8981
- column: string | null;
8982
- el?: unknown;
8983
- originalEvent?: unknown;
8984
- }
8985
-
8986
- /**
8987
- * A card-aging / SLA threshold (BACKLOG-0000960): a raw millisecond count, or
8988
- * a `{ weeks, days, hours, minutes, seconds, ms }` spec whose fields are summed
8989
- * (`{ days: 3, hours: 12 }` → 3.5 days). A negative or non-finite value means
8990
- * "no threshold at this level".
8991
- */
8992
- type KanbanSlaThreshold = number | {
8993
- weeks?: number; week?: number; w?: number;
8994
- days?: number; day?: number; d?: number;
8995
- hours?: number; hour?: number; h?: number;
8996
- minutes?: number; minute?: number; m?: number; min?: number;
8997
- seconds?: number; second?: number; s?: number; sec?: number;
8998
- ms?: number; milliseconds?: number;
8999
- };
9000
-
9001
- /**
9002
- * Card-aging / SLA configuration (BACKLOG-0000960). A card is measured against a
9003
- * `warn` and a `breach` threshold; the view puts an age chip on aged cards and a
9004
- * highlight on breached ones, and a rising crossing fires the `card:sla` event
9005
- * and the matching `onWarn`/`onBreach` callback (signature `(level, rows)`, the
9006
- * Data Router alert handler's). Thresholds resolve most-specific-first:
9007
- * lane → column → global. Reached at runtime as {@link Kanban#sla}.
9008
- */
9009
- interface KanbanSlaConfig {
9010
- /** The global warn threshold. */
9011
- warn?: KanbanSlaThreshold;
9012
- /** The global breach threshold. */
9013
- breach?: KanbanSlaThreshold;
9014
- /** Per-column overrides by column id (each a threshold or a `{ warn, breach }` pair). */
9015
- columns?: Record<string, KanbanSlaThreshold | { warn?: KanbanSlaThreshold; breach?: KanbanSlaThreshold }>;
9016
- /** Per-swimlane overrides by lane id (each a threshold or a `{ warn, breach }` pair). */
9017
- lanes?: Record<string, KanbanSlaThreshold | { warn?: KanbanSlaThreshold; breach?: KanbanSlaThreshold }>;
9018
- /**
9019
- * Where the ageing clock starts: `'column'` (default) measures time in the
9020
- * card's current column; `'board'` measures age since the card arrived/was
9021
- * created.
9022
- */
9023
- basis?: 'column' | 'board';
9024
- /** A row property holding the wall-clock time the card entered its column. */
9025
- enteredProperty?: string;
9026
- /** A row property holding the wall-clock time the card was created. */
9027
- createdProperty?: string;
9028
- /** Whether cards in a done column are exempt from ageing (default true). */
9029
- ignoreDone?: boolean;
9030
- /** Whether the flow transition log drives the ageing basis when present (default true). */
9031
- useTransitionLog?: boolean;
9032
- /** Show the age chip on every aged card (`'always'`), or only on warn/breach (`'threshold'`, default). */
9033
- showAge?: 'always' | 'threshold';
9034
- /** A wall-clock epoch clock, injectable for deterministic tests (default `Date.now`). */
9035
- now?: () => number;
9036
- /** A re-check interval in ms so a card breaching by sitting still still lights up (0 = off). */
9037
- tick?: number;
9038
- /** Called on a rising crossing to warn level, `(level, rows)` — the router alert handler's shape. */
9039
- onWarn?: (level: 'warn' | 'breach', rows: KanbanRow[]) => void;
9040
- /** Called on a rising crossing to breach level, `(level, rows)` — the router alert handler's shape. */
9041
- onBreach?: (level: 'warn' | 'breach', rows: KanbanRow[]) => void;
9042
- }
9043
-
9044
- /** The computed SLA state of one card (BACKLOG-0000960). */
9045
- interface KanbanSlaState {
9046
- key: unknown;
9047
- columnId: string | null;
9048
- lane?: unknown;
9049
- /** The ageing-clock start epoch (ms), or null when no time source could be resolved. */
9050
- start: number | null;
9051
- /** The card's age in ms, or null when unknown. */
9052
- ageMs: number | null;
9053
- /** A short human age label (`2d`, `5h`, …), '' when unknown. */
9054
- ageText: string;
9055
- /** The resolved warn threshold in ms, or null. */
9056
- warnMs: number | null;
9057
- /** The resolved breach threshold in ms, or null. */
9058
- breachMs: number | null;
9059
- /** The classified level, or null when the card cannot be aged. */
9060
- level: 'ok' | 'warn' | 'breach' | null;
9061
- /** True when `level` is `'breach'`. */
9062
- breached: boolean;
9063
- }
9064
-
9065
- /**
9066
- * The card-aging / SLA monitor (BACKLOG-0000960), reached as {@link Kanban#sla}
9067
- * when a `sla` config is supplied. Pure and DOM-free: it computes each card's
9068
- * ageing state from the board's card model and the flow transition log, and the
9069
- * view paints it.
9070
- */
9071
- interface KanbanSla {
9072
- /** The normalised SLA config (read-only). */
9073
- readonly config: object;
9074
- /** Recompute every card's SLA state without emitting anything. */
9075
- sync(): KanbanSla;
9076
- /** Recompute and fire `card:sla`/`onWarn`/`onBreach` on each rising crossing. */
9077
- evaluate(opts?: { emit?: boolean }): KanbanSlaState[];
9078
- /** Establish the baseline, notify on the current state, and start the optional tick. */
9079
- start(): KanbanSla;
9080
- /** The SLA state of one card (by card model or key), or null when unknown. */
9081
- stateFor(cardOrKey: KanbanCard | unknown): KanbanSlaState | null;
9082
- /** Every card's current SLA state. */
9083
- states(): KanbanSlaState[];
9084
- /** The cards currently at breach level. */
9085
- breaches(): KanbanSlaState[];
9086
- /** The cards currently at warn level (not yet breached). */
9087
- warnings(): KanbanSlaState[];
9088
- /** Stop the tick and drop the board subscriptions. */
9089
- destroy(): void;
9090
- }
9091
-
9092
- /**
9093
- * Kanban configuration. Every structural property is named here so the same
9094
- * board maps DemandFlow (a status field, `points`, `sprint`, `epic`, a
9095
- * swimlane property) and any customer schema without code change.
9096
- */
9097
- interface KanbanConfig {
9098
- rows?: KanbanRow[];
9099
- grid?: unknown;
9100
- rowKey?: string | ((row: KanbanRow) => unknown);
9101
- columnProperty?: string;
9102
- columns?: KanbanColumnDef[];
9103
- columnOrder?: string[];
9104
- pointsProperty?: string;
9105
- showPoints?: boolean;
9106
- orderProperty?: string;
9107
- swimlaneProperty?: string;
9108
- /** Render the 2D swimlane layout using `swimlaneProperty` (default false). */
9109
- swimlanes?: boolean;
9110
- /** Explicit lane definitions; otherwise lanes come from the distinct swimlane values. */
9111
- lanes?: (string | { id: string; title?: string })[];
9112
- /** An explicit lane order by id (also set by a lane-header-drag reorder). */
9113
- laneOrder?: string[];
9114
- /** Enforce `wipLimit` as a hard gate: a move that would exceed it is refused (default false). */
9115
- enforceWip?: boolean;
9116
- /** A custom card template: return an HTML string or a DOM node to own the whole card body. */
9117
- cardRenderer?: (card: KanbanCard, ctx: { column: KanbanColumn; readonly: boolean; el: HTMLElement; doc: Document }) => string | Node | void;
9118
- sprintProperty?: string;
9119
- epicProperty?: string;
9120
- /** A configurable sprint dataset: the canonical sprint list (order + titles), shown even when empty. */
9121
- sprints?: (string | { id: unknown; title?: string })[];
9122
- /** The initially selected sprint id, `Kanban.BACKLOG`, or undefined for all. */
9123
- sprint?: unknown;
9124
- /** The initially selected epic id, or undefined for all. */
9125
- epic?: unknown;
9126
- /** Column ids that count as "done" for a rollup's progress (also a column def's `done: true`). */
9127
- doneColumns?: string[];
9128
- /** Card pop-out: a nested child grid or board (master-detail by composition). */
9129
- children?: KanbanChildren;
9130
- /** Card virtualization for tall columns: true, or `{ rowHeight, overscan, threshold, viewport }`. */
9131
- virtualize?: boolean | { rowHeight?: number; overscan?: number; threshold?: number; viewport?: number };
9132
- /**
9133
- * Card aging / SLA highlighting (BACKLOG-0000960): warn/breach thresholds
9134
- * (globally, per column and/or per lane) that age each card and fire
9135
- * `card:sla` on a rising crossing. Opt-in; reached at runtime as
9136
- * {@link Kanban#sla}. See {@link KanbanSlaConfig}.
9137
- */
9138
- sla?: KanbanSlaConfig;
9139
- /** A saved board state (from `getState`) to restore on construction. */
9140
- state?: object;
9141
- /** Show a per-column add-card affordance. */
9142
- addCard?: boolean;
9143
- /** Persist a standalone inline edit; return false or a rejected promise to revert. */
9144
- onCardEdit?: (event: { card: KanbanCard; key: unknown; field: string; fieldPath: string; value: unknown }) => boolean | void | Promise<boolean | void>;
9145
- /**
9146
- * Create a card for a column on add-card; return the row to create (with
9147
- * its key), a Promise of that row, or nothing to auto-generate. A rejected
9148
- * Promise creates no card and leaves the board unchanged (BACKLOG-0001230).
9149
- */
9150
- onAddCard?: (columnId: string) => KanbanRow | Promise<KanbanRow> | void;
9151
- /** A predicate filter over cards; only matching cards are shown. */
9152
- filter?: (row: KanbanRow, card: KanbanCard) => boolean;
9153
- /** Quick-filter text matched case-insensitively across card fields. */
9154
- quickFilter?: string;
9155
- card?: KanbanCardMap;
9156
- readonly?: KanbanReadonly;
9157
- ariaLabel?: string;
9158
- emptyText?: string;
9159
- /** Whether card selection is enabled (default true). */
9160
- selectable?: boolean;
9161
- /** Host-localised words for the move announcements (grabbed/moved/dropped/reverted/cancelled). */
9162
- labels?: Record<string, string>;
9163
- /**
9164
- * Veto/confirm a move before any write. Return `false` (or a promise of it)
9165
- * to refuse; `from`/`to` are column ids, `index` the target position.
9166
- */
9167
- onBeforeMove?: (card: KanbanCard, from: string | null, to: string, index: number | null) => boolean | Promise<boolean>;
9168
- /**
9169
- * Persist a move on a standalone (non-grid) board. Return `false` or a
9170
- * rejected promise to revert the optimistic move. On a grid-bound board the
9171
- * grid's write-back pipeline persists instead and this is not called.
9172
- */
9173
- onCardMove?: (event: KanbanMoveEvent) => boolean | void | Promise<boolean | void>;
9174
- /** A per-card context menu: items, or `fn(card, selectedCards)` returning items. Suppresses `card:contextmenu`. */
9175
- contextMenu?: KanbanMenuItem[] | ((card: KanbanCard, selected: KanbanCard[]) => KanbanMenuItem[]);
9176
- onCardClick?: (event: KanbanEvent) => void;
9177
- onCardDblClick?: (event: KanbanEvent) => void;
9178
- onCardContextMenu?: (event: KanbanEvent) => void;
9179
- }
9180
-
9181
- /**
9182
- * Card pop-out configuration. The child view is a full composed grid (via
9183
- * `factory`, a `createGrid`), a nested board (`asBoard`), or a custom `render`.
9184
- * The child set is the rows whose `property` equals the card key, or the
9185
- * `load(card)` result. Recursion falls out: a nested board can pop its own
9186
- * children.
9187
- */
9188
- interface KanbanChildren {
9189
- /** Parent-id property linking child rows to a card within the same dataset. */
9190
- property?: string;
9191
- /** Per-card child rows, sync or async — an alternative (or addition) to `property`. */
9192
- load?: (card: KanbanCard) => KanbanRow[] | Promise<KanbanRow[]>;
9193
- /** Whether a card can be expanded, overriding the property/load inference. */
9194
- hasChildren?: (card: KanbanCard) => boolean;
9195
- /** Where the pop-out appears (default `drawer`). */
9196
- present?: 'drawer' | 'modal' | 'inline';
9197
- /** The grid factory (a `createGrid`) that builds the child grid. */
9198
- factory?: (container: HTMLElement, options: object) => { destroy?: () => void };
9199
- /** Make the child a nested board (recursive) instead of a grid. */
9200
- asBoard?: boolean;
9201
- /** Options for the child grid/board — an object or `fn(card)`. */
9202
- gridOptions?: object | ((card: KanbanCard) => object);
9203
- /** Fully custom child render; returns a cleanup function. */
9204
- render?: (container: HTMLElement, ctx: { card: KanbanCard; rows: KanbanRow[]; board: Kanban; depth: number }) => (void | (() => void));
9205
- /** The pop-out title (default the card title). */
9206
- title?: (card: KanbanCard) => string;
9207
- }
9208
-
9209
- /** One context-menu item. `action` receives the card, the selected cards, and the board. */
9210
- interface KanbanMenuItem {
9211
- label: string;
9212
- action?: (ctx: { card: KanbanCard; cards: KanbanCard[]; board: Kanban }) => void;
9213
- disabled?: boolean;
9214
- }
9215
-
9216
- /** The payload of a `card:move` (and `card:reverted`) event. */
9217
- interface KanbanMoveEvent {
9218
- keys: unknown[];
9219
- cards: KanbanCard[];
9220
- from: (string | null)[];
9221
- to: string;
9222
- index: number | null;
9223
- orders: number[] | null;
9224
- }
9225
-
9226
- /** The keyed-diff consumer surface a board shares with a grid, so a Data Router routes to it directly. */
9227
- interface KanbanRows {
9228
- apply(change: { add?: KanbanRow[]; update?: KanbanRow[]; remove?: unknown[] }): void;
9229
- forEach(fn: (row: KanbanRow, key: unknown) => void): void;
9230
- readonly count: number;
9231
- }
9232
-
9233
- /**
9234
- * Named card predicates, composed with AND (BACKLOG-0001229), following the
9235
- * grid's `filters.where` convention (BACKLOG-0001202). Several may be
9236
- * registered under different names at once; each can be replaced or removed
9237
- * without touching the others. `setFilter(fn)` is unchanged sugar for
9238
- * `where(DEFAULT, fn)` / `where(DEFAULT, null)`.
9239
- */
9240
- interface KanbanFilters {
9241
- /** The reserved name `board.setFilter` registers/removes under. */
9242
- readonly DEFAULT: string;
9243
- /** The registered names, in registration order. */
9244
- where(): string[];
9245
- /** Register or replace the predicate under `name`. */
9246
- where(name: string, predicate: (row: KanbanRow, card: KanbanCard) => boolean): Kanban;
9247
- /** Remove whatever is registered under `name`; a no-op if nothing was. */
9248
- where(name: string, predicate: null): Kanban;
9249
- /** Re-run every named predicate (or one, by name) and re-render. */
9250
- reapply(name?: string): boolean;
9251
- }
9252
-
9253
- /**
9254
- * A board instance: a kanban view of grid rows as cards grouped into columns.
9255
- * It consumes data through the same keyed-diff `rows.apply` contract a grid
9256
- * exposes, so `dataRouter.attach(value, board)` drives it like any other
9257
- * viewer.
9258
- */
9259
- interface Kanban {
9260
- readonly el: unknown | null;
9261
- readonly rowKey: string | ((row: KanbanRow) => unknown);
9262
- rows: KanbanRows;
9263
- /** The card-aging / SLA monitor, present only when a `sla` config was supplied (BACKLOG-0000960). */
9264
- sla?: KanbanSla;
9265
- columns(): KanbanColumn[];
9266
- column(id: string): KanbanColumn | undefined;
9267
- count(id: string): number;
9268
- points(id: string): number;
9269
- cards(): KanbanCard[];
9270
- card(key: unknown): KanbanCard | undefined;
9271
- on(name: string, fn: (event: KanbanEvent) => void): () => void;
9272
- off(name: string, fn: (event: KanbanEvent) => void): void;
9273
- readonly(scope?: { column?: string; card?: unknown }): boolean;
9274
- /**
9275
- * Move one or more cards to a column (and, with an order property, to a
9276
- * position within it), through the `onBeforeMove` veto and the grid's
9277
- * shipped write-back path. The single entry point behind drag-and-drop and
9278
- * keyboard move.
9279
- */
9280
- move(keys: unknown | unknown[], toColumn: string, toIndex?: number | null, toLane?: string): Promise<{ moved: unknown[]; reverted: boolean }>;
9281
- /** The selected card keys. */
9282
- selection(): unknown[];
9283
- /** Whether a card is selected. */
9284
- isSelected(key: unknown): boolean;
9285
- /** Change the selection: `set` (replace), `add`, `toggle` or `remove`. */
9286
- select(keys: unknown | unknown[], mode?: 'set' | 'add' | 'toggle' | 'remove'): Kanban;
9287
- /** Clear the selection. */
9288
- clearSelection(): Kanban;
9289
- /** Collapse, expand or toggle a column (emits `column:collapse`). */
9290
- collapseColumn(id: string, collapsed?: boolean): Kanban;
9291
- /** Collapse, expand or toggle a swimlane (emits `swimlane:collapse`). */
9292
- collapseLane(id: string, collapsed?: boolean): Kanban;
9293
- /** Reorder the columns to the given id order (emits `column:reorder`). */
9294
- reorderColumns(order: string[]): Kanban;
9295
- /** Move one column before another (or to the end); emits `column:reorder`. */
9296
- moveColumn(id: string, beforeId: string | null): Kanban;
9297
- /** Reorder the swimlanes to the given id order (emits `swimlane:reorder`). */
9298
- reorderLanes(order: string[]): Kanban;
9299
- /** Move one swimlane before another (or to the end); emits `swimlane:reorder`. */
9300
- moveLane(id: string, beforeId: string | null): Kanban;
9301
- /** Named card predicates, composed with AND (BACKLOG-0001229). See {@link KanbanFilters}. */
9302
- filters: KanbanFilters;
9303
- /** Set a predicate filter over cards, or clear it with null. Sugar for `filters.where(filters.DEFAULT, fn)`. */
9304
- setFilter(fn: ((row: KanbanRow, card: KanbanCard) => boolean) | null): Kanban;
9305
- /** Set the quick-filter text matched across card fields. Independent of every `filters.where` predicate. */
9306
- setQuickFilter(text: string): Kanban;
9307
- /** Distinct values of a property with card counts — the raw material for a facet control. */
9308
- facets(property: string): { value: unknown; count: number }[];
9309
- /** The sentinel `setSprint` value that selects the backlog (cards with no sprint). */
9310
- readonly BACKLOG: unknown;
9311
- /** Select the shown sprint (`BACKLOG` for the backlog, undefined for all); emits `sprint:changed`. */
9312
- setSprint(sprint: unknown): Kanban;
9313
- /** Show only the backlog (cards with no sprint). */
9314
- showBacklog(): Kanban;
9315
- /** Select the shown epic (undefined for all); emits `epic:changed`. */
9316
- setEpic(epic: unknown): Kanban;
9317
- /** The distinct sprint values (the switcher's options); a configured `sprints` dataset pins the order. */
9318
- sprints(): unknown[];
9319
- /** The sprint dataset as `{ id, title }` descriptors — the configured list plus any data-only sprint. */
9320
- sprintDefs(): { id: unknown; title: string }[];
9321
- /** The distinct epic values. */
9322
- epics(): unknown[];
9323
- /** Roll rows up by a property: per-bucket count, points, done and progress. */
9324
- rollup(property: string): { value: unknown; count: number; points: number; doneCount: number; donePoints: number; progress: number }[];
9325
- /** The epic rollup (empty when no epic property is configured). */
9326
- epicRollup(): { value: unknown; count: number; points: number; doneCount: number; donePoints: number; progress: number }[];
9327
- /** Whether a card can be expanded to a child pop-out. */
9328
- canExpand(card: KanbanCard): boolean;
9329
- /** Open a card's children in a pop-out (drawer/modal/inline); emits `card:expand`/`card:drill`. */
9330
- expand(key: unknown): Promise<object | null>;
9331
- /** Close any open card pop-out. */
9332
- closeDetail(): Kanban;
9333
- /** Whether a mapped card field is opted into inline edit and writable. */
9334
- isFieldEditable(name: string): boolean;
9335
- /** Start inline editing a card's field (the grid's own field editor when bound); no-op headless. */
9336
- editCard(key: unknown, name?: string): object | null;
9337
- /** Commit an inline edit through the write-back path (grid.edit.setCells when bound); emits `card:edit`. */
9338
- applyEdit(key: unknown, name: string, value: unknown): Promise<boolean>;
9339
- /**
9340
- * Add a card to a column and open it in inline edit; emits `card:add`.
9341
- * Returns the new key directly, or a Promise of it when `onAddCard`
9342
- * returns a Promise or a `beforeAdd` handler defers (BACKLOG-0001230); a
9343
- * rejected `onAddCard` Promise resolves this to `null` with no card added.
9344
- */
9345
- addCard(columnId: string, seed?: KanbanRow): unknown | Promise<unknown>;
9346
- /** Serialise the restorable state: collapsed columns/lanes, order, filter, sprint/epic, selection. */
9347
- getState(): object;
9348
- /** Restore a state snapshot from {@link Kanban#getState}. */
9349
- setState(snapshot: object): Kanban;
9350
- /** Mark the board loading (renders a host-localised loading state). */
9351
- setLoading(loading: boolean): Kanban;
9352
- /** Set (or clear with null) an error state, rendered as a host-supplied message. */
9353
- setError(message: string | null): Kanban;
9354
- setRows(rows: KanbanRow[]): Kanban;
9355
- /**
9356
- * Replace the board's configured column set (BACKLOG-0001228). Keeps card
9357
- * placement and interaction state (collapsed columns, column order, quick
9358
- * filter, selection) for every column id that survives; a dropped id is
9359
- * not specially handled — a card whose value has nowhere configured to go
9360
- * re-derives an ad hoc column rather than becoming `unplaced` (the same
9361
- * "never silently drop a card" rule an unconfigured value already gets).
9362
- */
9363
- setColumns(defs: KanbanColumnDef[]): Kanban;
9364
- refresh(): Kanban;
9365
- destroy(): void;
9366
- }
9367
-
9368
- /**
9369
- * Create a board (kanban) view of rows, grouped into columns by a configurable
9370
- * property. Pass a DOM element to render into, or `null` for a headless board
9371
- * that computes the same column/card model without a DOM.
9372
- */
9373
- export function createKanban(el: HTMLElement | null, config?: KanbanConfig): Kanban;
9374
- export default createKanban;
9375
- }
9376
-
9377
- declare module 'lattice-grid/modules/kpi' {
9378
- /** A row backing a KPI aggregate: any object. Its identity comes from `rowKey`. */
9379
- type KPIRow = Record<string, unknown>;
9380
-
9381
- /** The aggregation kinds a tile can compute. `custom` is a host reducer over the rows. */
9382
- type KPIAggregation = 'sum' | 'avg' | 'min' | 'max' | 'count' | 'countDistinct' | 'custom';
9383
-
9384
- /** Number formatting for a tile value. `percent` treats the value as a ratio (0.42 → 42%). */
9385
- type KPIFormat =
9386
- | 'number' | 'currency' | 'percent' | 'compact'
9387
- | { type?: 'number' | 'currency' | 'percent' | 'compact'; decimals?: number; currency?: string; locale?: string };
9388
-
9389
- /**
9390
- * A semantic threshold: two cut points and a direction. `higherIsBetter` (the
9391
- * default) makes a value at/above `warn` good, at/above `critical` a warning,
9392
- * below it critical; `lowerIsBetter` mirrors it. Colour is a host concern.
9393
- */
9394
- interface KPIThresholds {
9395
- warn: number;
9396
- critical: number;
9397
- direction?: 'higherIsBetter' | 'lowerIsBetter';
9398
- }
9399
-
9400
- /** An explicit band: the `status` of the first band whose half-open `[min, max)` contains the value. */
9401
- interface KPIBand {
9402
- min?: number;
9403
- max?: number;
9404
- status: 'good' | 'warn' | 'critical';
9405
- }
9406
-
9407
- /** An optional sparkline series: the `y` field plotted in order of the `x` field (or insertion). */
9408
- interface KPISparkline {
9409
- x?: string;
9410
- y: string | ((row: KPIRow) => unknown);
9411
- }
9412
-
9413
- /** One tile: an aggregate over the routed rows, with optional filter, format, threshold and trend. */
9414
- interface KPITile {
9415
- /** A stable identity for the tile (defaults to the label, then the index). */
9416
- id?: string;
9417
- /** The tile's accessible label. */
9418
- label?: string;
9419
- /** The aggregation kind, or a reducer `(rows, tile) => value` for a custom tile. */
9420
- aggregation?: KPIAggregation | ((rows: KPIRow[], tile: object) => unknown);
9421
- /** The reducer for a `custom` aggregation, when `aggregation` is the string `'custom'`. */
9422
- compute?: (rows: KPIRow[], tile: object) => unknown;
9423
- /** The field the aggregation reads (a path or accessor). Ignored by `count`. */
9424
- field?: string | ((row: KPIRow) => unknown);
9425
- /** A predicate limiting the rows this tile aggregates. */
9426
- filter?: (row: KPIRow) => boolean;
9427
- /** Value formatting. */
9428
- format?: KPIFormat;
9429
- /** A comparison target rendered alongside the value. */
9430
- target?: number;
9431
- /** A baseline the tile's delta is measured against. */
9432
- baseline?: number;
9433
- /** Threshold bands, either two cut points or an explicit band list. */
9434
- thresholds?: KPIThresholds;
9435
- /** Explicit status bands (an alternative to `thresholds`). */
9436
- bands?: KPIBand[];
9437
- /** A trend sparkline series. */
9438
- sparkline?: KPISparkline | string;
9439
- }
9440
-
9441
- /**
9442
- * The hierarchy a KPI panel arranges its tiles into (BACKLOG-0001059): a rail
9443
- * of top-level items that expand to the indicators beneath them, each parent
9444
- * highlighted with the worst status below it.
9445
- *
9446
- * The shape is declared with `path` or `parentKey` — the same two shapes the
9447
- * grid's tree data and the tree-select editor take — over the **tile specs**,
9448
- * not the rows. With neither declared, one is derived by splitting the tile
9449
- * ids on `separator`, so `system.compute.cpu` files itself under Compute
9450
- * under System. A panel whose ids carry no separator stays flat, and `false`
9451
- * keeps it flat whatever they look like.
9452
- *
9453
- * A tile's `field` is never a source: a dot there already means a nested
9454
- * object property.
9455
- */
9456
- interface KPITreeConfig {
9457
- /** The tile's own place in the hierarchy, its own segment last. */
9458
- path?: (tile: KPITile) => (string | number)[];
9459
- /** The id of the tile this one sits under, or a reader for it. */
9460
- parentKey?: string | ((tile: KPITile) => unknown);
9461
- /** The heading tiles whose parent is not in the panel are gathered under. */
9462
- orphans?: 'root' | string;
9463
- /** The separator a derived hierarchy splits a tile id on. Defaults to `.`. */
9464
- separator?: string;
9465
- /** Which branches start open: every one (`true`), or these node keys. */
9466
- expanded?: true | string[];
9467
- }
9468
-
9469
- /**
9470
- * One node of the rail.
9471
- *
9472
- * **No value rolls up.** `value` and `formatted` are the node's own tile's
9473
- * reading, and are `null` on a level the hierarchy synthesised, because the
9474
- * running accumulators cannot be composed without a rescan.
9475
- *
9476
- * **Severity does.** `rollup` is the worst status at or below the node, which
9477
- * is what a collapsed branch reports. `unknown` is excluded from it on
9478
- * purpose — ranking "nothing was measured" as the worst would hide a real
9479
- * warning underneath it — and is surfaced as `unknown`, a count of the
9480
- * descendants that measured nothing, so neither can pass unnoticed.
9481
- */
9482
- interface KPINodeModel {
9483
- /** The node's stable identity: the tile id, or the path of a synthesised level. */
9484
- key: string;
9485
- /** The tile id, or null on a synthesised level. */
9486
- id: string | null;
9487
- label: string;
9488
- /** Depth, 0 at the top level. */
9489
- level: number;
9490
- /** Its place among its siblings, from 1, and how many there are. */
9491
- posinset: number;
9492
- setsize: number;
9493
- hasChildren: boolean;
9494
- expanded: boolean;
9495
- children: KPINodeModel[];
9496
- /** The node's own tile, or null on a synthesised level. */
9497
- tile: KPITileModel | null;
9498
- value: unknown;
9499
- formatted: string | null;
9500
- /** The node's own status. */
9501
- status: 'good' | 'warn' | 'critical' | 'unknown' | null;
9502
- /** The worst status at or below the node. Never `unknown`. */
9503
- rollup: 'good' | 'warn' | 'critical' | null;
9504
- /** How many tiles at or below the node measured nothing. */
9505
- unknown: number;
9506
- /** How many tiles are at or below the node. */
9507
- items: number;
9508
- }
9509
-
9510
- /** A computed tile, as it appears in the model. */
9511
- interface KPITileModel {
9512
- id: string;
9513
- label: string;
9514
- aggregation: string;
9515
- field?: string;
9516
- value: unknown;
9517
- formatted: string;
9518
- /**
9519
- * The tile's semantic band, or `unknown` when the tile measured nothing.
9520
- * `unknown` is decided from data presence before any threshold is
9521
- * consulted: an aggregation over nothing returns the identity of its
9522
- * operation (`sum` and `count` return 0), and 0 is a number a threshold
9523
- * grades, so without it an empty panel would report as a healthy one.
9524
- *
9525
- * Two things make a tile `unknown`: the panel holds no rows at all, or the
9526
- * tile's `field` names no column on the bound grid, so it never read a cell
9527
- * to reduce over. A tile whose `filter` matches none of the rows the panel
9528
- * *does* hold is neither — it has measured a real zero and is banded
9529
- * normally. `null` means the tile has no thresholds or bands configured.
9530
- */
9531
- status: 'good' | 'warn' | 'critical' | 'unknown' | null;
9532
- target?: number;
9533
- baseline?: number;
9534
- delta: number | null;
9535
- deltaPercent: number | null;
9536
- deltaFormatted?: string;
9537
- count: number;
9538
- sparkline: number[] | null;
9539
- }
9540
-
9541
- /** The payload every tile event carries. */
9542
- interface KPIEvent {
9543
- tile: KPITileModel;
9544
- id: string;
9545
- originalEvent?: unknown;
9546
- }
9547
-
9548
- /** KPI panel configuration. */
9549
- interface KPIConfig {
9550
- rows?: KPIRow[];
9551
- grid?: unknown;
9552
- rowKey?: string | ((row: KPIRow) => unknown);
9553
- /**
9554
- * Extra columns of the bound `grid` to project onto the rows a tile `filter`
9555
- * sees, beyond the fields the tiles themselves declare. A grid-bound panel
9556
- * hands a filter a projection, not a whole grid row, so a filter over a
9557
- * column no tile names would otherwise read `undefined` and report a
9558
- * confident zero. Ignored on a panel over a plain `rows` array.
9559
- */
9560
- fields?: string[];
9561
- tiles?: KPITile[];
9562
- columns?: number;
9563
- ariaLabel?: string;
9564
- nullText?: string;
9565
- /** Arrange the tiles as a hierarchy; `false` keeps the panel flat. */
9566
- tree?: KPITreeConfig | false;
9567
- /**
9568
- * The catalogue the panel's own text is read from. A panel routinely has no
9569
- * grid to borrow one off — two of its three input modes have none — so this
9570
- * is the first-class way to translate it. A grid's own `messages` satisfies
9571
- * the shape; a key it does not carry falls back to English.
9572
- */
9573
- messages?: { t(key: string, params?: Record<string, unknown>): string };
9574
- onTileClick?: (event: KPIEvent) => void;
9575
- onTileDblClick?: (event: KPIEvent) => void;
9576
- onTileContextMenu?: (event: KPIEvent) => void;
9577
- onNodeToggle?: (event: { key: string; expanded: boolean; node?: KPINodeModel }) => void;
9578
- onChange?: (event: { model: { tiles: KPITileModel[]; nodes?: KPINodeModel[] } }) => void;
9579
- }
9580
-
9581
- /** The keyed-diff consumer surface a KPI panel shares with a grid, so a Data Router routes to it directly. */
9582
- interface KPIRows {
9583
- apply(change: { add?: KPIRow[]; update?: KPIRow[]; remove?: unknown[] }): void;
9584
- forEach(fn: (row: KPIRow, key: unknown) => void): void;
9585
- readonly count: number;
9586
- }
9587
-
9588
- /**
9589
- * A KPI / stat-tile panel: a grid of aggregate tiles over a dataset. It
9590
- * consumes data through the same keyed-diff `rows.apply` contract a grid
9591
- * exposes, so `dataRouter.attach(value, kpi)` drives it like any other viewer,
9592
- * updating each tile incrementally from the routed delta.
9593
- */
9594
- interface KPI {
9595
- readonly el: unknown | null;
9596
- readonly rowKey: string | ((row: KPIRow) => unknown);
9597
- /** Whether the panel renders as a hierarchy rather than a flat tile grid. */
9598
- readonly tree: boolean;
9599
- rows: KPIRows;
9600
- tiles(): KPITileModel[];
9601
- tile(id: string): KPITileModel | undefined;
9602
- value(id: string): unknown;
9603
- /** The top-level nodes of the hierarchy. Empty on a flat panel. */
9604
- nodes(): KPINodeModel[];
9605
- /** One node by its key, at any depth. */
9606
- node(key: string): KPINodeModel | undefined;
9607
- /** The nodes on screen: the roots, plus the children of every open branch. */
9608
- visibleNodes(): KPINodeModel[];
9609
- expand(key: string): KPI;
9610
- collapse(key: string): KPI;
9611
- toggle(key: string): KPI;
9612
- setRows(rows: KPIRow[]): KPI;
9613
- refresh(): KPI;
9614
- getState(): object;
9615
- setState(snapshot: object): KPI;
9616
- on(name: string, fn: (event: KPIEvent) => void): () => void;
9617
- off(name: string, fn: (event: KPIEvent) => void): void;
9618
- destroy(): void;
9619
- }
9620
-
9621
- /**
9622
- * Create a KPI / stat-tile panel over rows or a bound grid. Pass a DOM element
9623
- * to render into, or `null` for a headless panel that computes the same tile
9624
- * model without a DOM.
9625
- */
9626
- export function createKPI(el: HTMLElement | null, config?: KPIConfig): KPI;
9627
- export default createKPI;
9628
- }
9629
-
9630
- declare module 'lattice-grid/modules/ai' {
9631
- /**
9632
- * The provider-agnostic model callback the host supplies (BACKLOG-0000965).
9633
- * The module never imports a provider SDK, reads a key, or makes a network
9634
- * call — it builds this payload and awaits the host's reply. A host may wrap a
9635
- * chat provider (`{ text }`), a completion (a bare string), a tool-calling turn
9636
- * (`{ toolCalls }`), or a structured provider (`{ structured }`).
9637
- */
9638
- type AIAsk = (payload: {
9639
- /** The narrate-only system instruction. */
9640
- system: string;
9641
- /** The single user message: the facts block and the ask. */
9642
- message: string;
9643
- /** System and message joined, for a completion-shaped provider. */
9644
- prompt: string;
9645
- /** The running chat, including any tool results, for a chat-shaped provider. */
9646
- messages: Array<{ role: string; content: string; [k: string]: unknown }>;
9647
- /** The read-only tool definitions, present only on the tool-use path. */
9648
- tools?: object[];
9649
- /** The grid's generated schema (no row values). */
9650
- schema?: unknown;
9651
- /** An abort signal the host should honour. */
9652
- signal?: AbortSignal;
9653
- }) => Promise<
9654
- | string
9655
- | { text?: string; content?: string; toolCalls?: object[]; structured?: unknown }
9656
- >;
9657
-
9658
- /** A single computed figure a narrative is grounded on. */
9659
- interface AIFact {
9660
- id: string;
9661
- label: string;
9662
- /** The raw numeric value, or null for a context-only fact. */
9663
- value: number | null;
9664
- /** The pre-formatted display string the model is told to use verbatim. */
9665
- display: string;
9666
- kind: string;
9667
- colId?: string;
9668
- }
9669
-
9670
- /**
9671
- * A narrative target. `view` narrates the current filtered view; `column`
9672
- * narrates one column's profile; `forecast` adds its projection; `kpi`/`chart`
9673
- * narrate figures the caller passes through in `facts`; `risk` assembles a
9674
- * project RISK SUMMARY from the separate Gantt / Kanban modules' public outputs
9675
- * (BACKLOG-0000979).
9676
- */
9677
- interface AITarget {
9678
- kind?: 'view' | 'column' | 'forecast' | 'kpi' | 'chart' | 'risk';
9679
- colId?: string;
9680
- /** Forecast options, for `kind: 'forecast'`. */
9681
- options?: object;
9682
- /** Caller-supplied figures for a KPI/chart Explain, grounded like the rest. */
9683
- facts?: Array<{ id?: string; label: string; value: unknown; display?: string; kind?: string; colId?: string }>;
9684
- /**
9685
- * For `kind: 'risk'`: a Gantt instance (from `createGantt`). Read duck-typed
9686
- * for `earnedValue()` (SPI/CPI/variances) and `schedule` (critical path,
9687
- * float). The AI bundle never imports the Gantt module.
9688
- */
9689
- gantt?: unknown;
9690
- /**
9691
- * For `kind: 'risk'`: a Kanban board (from `createKanban`). Read for its
9692
- * `board.sla` monitor (breach / warning counts). The AI bundle never imports
9693
- * the Kanban module.
9694
- */
9695
- board?: unknown;
9696
- /** For `kind: 'risk'`: an SLA monitor, if not reached through `board`. */
9697
- sla?: unknown;
9698
- /** For `kind: 'risk'`: a precomputed `gantt.earnedValue()` result. */
9699
- earnedValue?: object;
9700
- /** For `kind: 'risk'`: a precomputed `gantt.schedule` result. */
9701
- schedule?: object;
9702
- /** For `kind: 'risk'`: precomputed SLA breach states. */
9703
- breaches?: object[];
9704
- /** For `kind: 'risk'`: precomputed SLA warning states. */
9705
- warnings?: object[];
9706
- /** For `kind: 'risk'`: options passed to `gantt.earnedValue()`. */
9707
- evmOptions?: object;
9708
- /**
9709
- * For `kind: 'risk'`: expose the at-risk task NAMES (off by default — a risk
9710
- * summary carries aggregates only unless the host opts in).
9711
- */
9712
- includeTaskNames?: boolean;
9713
- /**
9714
- * For `kind: 'risk'`: expose the money figures BAC/PV/EV/AC (off by default).
9715
- */
9716
- includeCost?: boolean;
9717
- /** For `kind: 'risk'`: cap on named at-risk tasks (default 10). */
9718
- maxTasks?: number;
9719
- }
9720
-
9721
- /** The facts packet a narrative grounds on. */
9722
- interface AIFactsPacket {
9723
- target: AITarget;
9724
- facts: AIFact[];
9725
- /** The numeric values seeding the reconciliation registry. */
9726
- groundedValues: number[];
9727
- meta: {
9728
- kind: string; filtered: boolean; factCount: number; redacted?: boolean; colId?: string;
9729
- /** For `kind: 'risk'`: which module sources resolved. */
9730
- sources?: { schedule: boolean; earnedValue: boolean; sla: boolean };
9731
- /** For `kind: 'risk'`: which opt-in exposures were honoured. */
9732
- exposed?: { taskNames: boolean; cost: boolean };
9733
- };
9734
- }
9735
-
9736
- /**
9737
- * The risk facts a board / Gantt risk summary grounds on (BACKLOG-0000979),
9738
- * from {@link buildRiskFacts}: the facts plus which module sources resolved and
9739
- * which opt-in exposures (task names, cost) were honoured.
9740
- */
9741
- interface AIRiskFacts {
9742
- facts: AIFact[];
9743
- meta: {
9744
- kind: 'risk';
9745
- sources: { schedule: boolean; earnedValue: boolean; sla: boolean };
9746
- exposed: { taskNames: boolean; cost: boolean };
9747
- };
9748
- }
9749
-
9750
- /** The result of a narrative: reconciled prose plus what grounded and what did not. */
9751
- interface AINarrative {
9752
- /** The narrative, with every ungrounded figure stripped (or flagged). */
9753
- text: string;
9754
- facts: AIFact[];
9755
- /** The figures that reconciled against a computed value. */
9756
- grounded: string[];
9757
- /** The figures removed as ungrounded. */
9758
- flagged: string[];
9759
- packet: AIFactsPacket;
9760
- /** How many ask() rounds ran (>1 only on the tool-use path). */
9761
- rounds: number;
9762
- mode: 'tools' | 'packet';
9763
- }
9764
-
9765
- /** AI module configuration. */
9766
- interface AIConfig {
9767
- /** The host's model callback. Falls back to the grid's `ai.ask` when omitted. */
9768
- ask?: AIAsk;
9769
- /** Opt into specific features: `'narrative'`, `'insights'`, `'query'`/`'ask'`. All on when omitted. */
9770
- enable?: string[];
9771
- /**
9772
- * Ask-your-data: apply a safe (read-only) query result without a confirm
9773
- * step. Off by default — the resolved query is shown and waits for Apply.
9774
- */
9775
- autoApply?: boolean;
9776
- /**
9777
- * A Data Router instance; on applying a query the answer rows are fanned to
9778
- * its attached viewers (grid + chart + KPI together) via `load()`.
9779
- */
9780
- router?: unknown;
9781
- /** Budgets passed to the schema builder for ask-your-data. */
9782
- schemaOptions?: object;
9783
- /** Extra context passed through to `ask()`. */
9784
- context?: unknown;
9785
- /** Called with each ask-your-data result. */
9786
- onQuery?: (result: AIQueryResult) => void;
9787
- /** Called with each governed-actor proposal (Play C), before any approval. */
9788
- onProposal?: (result: AIProposal) => void;
9789
- /**
9790
- * A Kanban board (from `createKanban`) the governed actor writes moves
9791
- * through: an NL card move applies via the board's own `beforeMove` gate
9792
- * (BACKLOG-0000967), never a kanban-specific write bypass.
9793
- */
9794
- board?: unknown;
9795
- /** Cap on rows any tool result carries to `ask()`. */
9796
- maxRows?: number;
9797
- /** Columns whose values must never leave the browser. */
9798
- redact?: string | string[] | ((colId: string) => boolean);
9799
- /** Force tool-use on or off; auto-detected from how `ask` was supplied otherwise. */
9800
- tools?: boolean;
9801
- /** Locale for figure formatting. */
9802
- locale?: string;
9803
- /** Column cap for a view summary. */
9804
- maxColumns?: number;
9805
- /** What to do with an ungrounded figure: `'strip'` (default) or `'flag'`. */
9806
- reconcile?: 'strip' | 'flag';
9807
- /** An element to mount the insights panel into. */
9808
- element?: HTMLElement;
9809
- /** Called when a narrative is produced. */
9810
- onNarrative?: (result: AINarrative) => void;
9811
- /** Called when `ask()` errors; the grid stays usable. */
9812
- onError?: (error: { error: unknown; target: AITarget }) => void;
9813
- }
9814
-
9815
- /** The report from applying an ask-your-data query. */
9816
- interface AIApplyReport {
9817
- ok: boolean;
9818
- /** The action types that were applied. */
9819
- applied: string[];
9820
- /** Actions that threw while applying. */
9821
- failed: Array<{ type: string; reason: string }>;
9822
- /** Actions refused by the read-only gate — a mutation is never applied. */
9823
- refused: Array<{ type: string; reason: string }>;
9824
- /** How many answer rows were fanned to a router's viewers. */
9825
- fannedOut: number;
9826
- }
9827
-
9828
- /**
9829
- * The result of an ask-your-data question (BACKLOG-0000966): a validated,
9830
- * READ-ONLY query spec — never rows — that the host reviews before applying.
9831
- */
9832
- interface AIQueryResult {
9833
- /** True when the spec is safe to apply: at least one read, nothing unsafe. */
9834
- ok: boolean;
9835
- /** The user's question. */
9836
- question: string;
9837
- /** The core plan (from `grid.ai.plan`). */
9838
- plan: Record<string, unknown>;
9839
- /** The read-only actions that will run — the validated query spec. */
9840
- actions: object[];
9841
- /** Actions refused as not read-only (a mutation the model asked for). */
9842
- unsafe: Array<{ type: string; reason: string }>;
9843
- /** Parts the core validator dropped (unknown column, bad operator, …). */
9844
- rejected: Array<{ at: string; what: string; reason: string }>;
9845
- /** The model's own one-line summary, if any. */
9846
- explain: string;
9847
- /** The validated query spec as data. */
9848
- spec: { actions: object[] };
9849
- /** The apply report once applied, or null. */
9850
- applied: AIApplyReport | null;
9851
- /** The resolved query in one human sentence, from the validated spec. */
9852
- describe(): string;
9853
- /** Apply the query (re-gated), fanning the answer to a router if configured. */
9854
- apply(opts?: { router?: unknown; onResult?: (rows: object[]) => void }): AIApplyReport;
9855
- }
9856
-
9857
- /** One before/after change in a governed-actor proposal (BACKLOG-0000967). */
9858
- interface AIDiffEntry {
9859
- /** The target row key. */
9860
- key: string;
9861
- /** A human label identifying the row (a name-like column, else the key). */
9862
- rowLabel: string;
9863
- /** The target column id. */
9864
- colId: string;
9865
- /** The column's title, for the diff header. */
9866
- colTitle: string;
9867
- /** The current stored value. */
9868
- oldValue: unknown;
9869
- /** The current value as shown (a lookup id mapped to its label). */
9870
- oldDisplay: string;
9871
- /** The proposed stored value (a label resolved to its option id). */
9872
- newValue: unknown;
9873
- /** The proposed value as shown. */
9874
- newDisplay: string;
9875
- }
9876
-
9877
- /**
9878
- * A governed-actor proposal (Play C, BACKLOG-0000967): the model's structured
9879
- * edits, VALIDATED and resolved against the current view — never written until
9880
- * a human approves. `apply()` writes ONLY through the grid's own gate.
9881
- */
9882
- interface AIProposal {
9883
- /** True when there is at least one applicable change and nothing needs a pick first. */
9884
- ok: boolean;
9885
- /** The user's instruction. */
9886
- instruction: string;
9887
- /** `'view'` (the filtered set, the default) or `'all'` (an opted-in widen). */
9888
- scope: 'view' | 'all';
9889
- /** How many rows the scope covers. */
9890
- scopeCount: number;
9891
- /** The scope in words, always stated in the confirm/diff. */
9892
- scopeText: string;
9893
- /** Whether any proposal was a bulk (`scope:'view'`) edit. */
9894
- bulk: boolean;
9895
- /** The before/after diff — exactly what would change. Nothing is written yet. */
9896
- diff: AIDiffEntry[];
9897
- /** Proposals refused before apply (unknown column, unknown label, bad type/range, no match). */
9898
- rejected: Array<{ reason: string; [k: string]: unknown }>;
9899
- /** Matches needing a human pick (>1 row for one phrase), with candidates. */
9900
- ambiguous: Array<{ reason: string; candidates: Array<{ key: string; label: string }>; [k: string]: unknown }>;
9901
- /** Named targets found only outside the view, offered for an opt-in widen. */
9902
- outOfView: Array<{ reason: string; candidates: Array<{ key: string; label: string }>; [k: string]: unknown }>;
9903
- /** Matches whose value already equals the ask (nothing to change). */
9904
- noops: Array<{ reason: string; [k: string]: unknown }>;
9905
- /** The apply report once applied, or null. */
9906
- applied: AIProposalReport | null;
9907
- /** The proposal in one human sentence, always stating the scope. */
9908
- describe(): string;
9909
- /** Apply the approved diff through the gate (`beforeEdit`, or `beforeMove` for a board). */
9910
- apply(opts?: { board?: unknown }): Promise<AIProposalReport>;
9911
- }
9912
-
9913
- /** The report from applying a governed-actor proposal. */
9914
- interface AIProposalReport {
9915
- /** True when at least one edit landed. */
9916
- ok: boolean;
9917
- /** How many edits landed through the gate. */
9918
- applied: number;
9919
- /** How many edits were attempted. */
9920
- requested: number;
9921
- /** How many were stopped by a before-handler veto. */
9922
- vetoed: number;
9923
- /** Which gated path applied them: `'setCells'`, `'board.move'`, or `'none'`. */
9924
- via: string;
9925
- }
9926
-
9927
- /**
9928
- * An AI controller over a live grid. It explains the grid's computed figures
9929
- * (Play A), answers questions with validated read-only query specs (Play B),
9930
- * and PROPOSES governed edits a human approves and the grid's own gate applies
9931
- * (Play C). `grid.ai` (in core) is the complementary intent/plan skill layer
9932
- * this consumes.
9933
- */
9934
- interface AI {
9935
- /** The mounted insights panel element, or null. */
9936
- readonly el: HTMLElement | null;
9937
- /** Whether a usable `ask()` is configured. */
9938
- readonly ready: boolean;
9939
- /** Produce a grounded, reconciled narrative for a target. */
9940
- explain(target?: AITarget, opts?: object): Promise<AINarrative>;
9941
- /** An alias for {@link AI.explain}. */
9942
- narrate(target?: AITarget, opts?: object): Promise<AINarrative>;
9943
- /**
9944
- * Produce a grounded, reconciled board / Gantt RISK SUMMARY
9945
- * (BACKLOG-0000979): a plain-language reading like "3 tasks at risk on the
9946
- * critical path, SPI 0.67, 2 SLA breaches". A convenience over
9947
- * `explain({ kind: 'risk', ... })`; the module sources go in `sources`
9948
- * (`gantt`, `board`/`sla`, or precomputed outputs). Every figure runs through
9949
- * the same reconciliation guard as {@link AI.explain}.
9950
- */
9951
- riskSummary(sources?: {
9952
- gantt?: unknown; board?: unknown; sla?: unknown;
9953
- earnedValue?: object; schedule?: object; breaches?: object[]; warnings?: object[];
9954
- includeTaskNames?: boolean; includeCost?: boolean; maxTasks?: number; evmOptions?: object;
9955
- }, opts?: object): Promise<AINarrative>;
9956
- /** Mount (or re-target) the insights panel into an element. */
9957
- insights(el?: HTMLElement, opts?: object): AI;
9958
- /** Build an "Explain" button bound to a target. */
9959
- attachExplain(target: AITarget, opts?: object): HTMLElement | null;
9960
- /** Build the facts packet for a target without calling `ask()`. */
9961
- facts(target?: AITarget, opts?: object): AIFactsPacket;
9962
- /**
9963
- * Ask-your-data: turn a question into a validated, read-only query spec, run
9964
- * it in the engine, and (on apply) fan the answer to router-attached viewers.
9965
- * Returns a result the host reviews; `autoApply` applies a safe read for you.
9966
- */
9967
- query(question: string, opts?: {
9968
- autoApply?: boolean; router?: unknown; schemaOptions?: object;
9969
- context?: unknown; tools?: boolean; signal?: AbortSignal;
9970
- onResult?: (rows: object[]) => void;
9971
- }): Promise<AIQueryResult>;
9972
- /** Apply a reviewed query result (the confirm path); re-gated at the seam. */
9973
- applyQuery(result: AIQueryResult, opts?: { router?: unknown; onResult?: (rows: object[]) => void }): AIApplyReport;
9974
- /** Mount the ask-your-data bar (input, Ask, auto-apply toggle, preview, Apply/Discard). */
9975
- askBar(el?: HTMLElement, opts?: object): AI;
9976
- /**
9977
- * Governed actor (Play C): ask the model for structured edit PROPOSALS over
9978
- * the current view, validate and resolve them (label -> stored value, locate
9979
- * a named row, reject unknown columns/labels/out-of-range), and return a
9980
- * reviewable {@link AIProposal} with a before/after diff. NOTHING is written
9981
- * — the model proposes; a human approves.
9982
- */
9983
- propose(instruction: string, opts?: {
9984
- widen?: boolean; board?: unknown; schemaOptions?: object; maxRows?: number;
9985
- context?: unknown; redact?: string | string[] | ((colId: string) => boolean);
9986
- signal?: AbortSignal;
9987
- }): Promise<AIProposal>;
9988
- /**
9989
- * Apply an approved proposal — the human-approval step. Writes ONLY through
9990
- * the gate: a grid cell edit via `grid.edit.setCells({ origin: 'ai' })` (the
9991
- * `beforeEdit` veto), a kanban move via `board.move({ origin: 'ai' })` (the
9992
- * `beforeMove` veto). A vetoing host handler stops the write.
9993
- */
9994
- applyProposal(result: AIProposal, opts?: { board?: unknown }): Promise<AIProposalReport>;
9995
- /**
9996
- * Mount the governed-actor bar: an instruction input, Propose, a before/after
9997
- * diff preview stating the scope, and Approve/Discard. Approve applies
9998
- * through the gate.
9999
- */
10000
- actorBar(el?: HTMLElement, opts?: object): AI;
10001
- on(name: 'narrative' | 'query' | 'proposal' | 'error' | string, fn: (payload: object) => void): () => void;
10002
- off(name: string, fn: (payload: object) => void): void;
10003
- destroy(): void;
10004
- }
10005
-
10006
- /**
10007
- * Create an AI narrative / insights controller over a live grid. The grid may
10008
- * be headless or rendered; the module grounds every figure on the grid's
10009
- * engine and calls only the host's `ask()`.
10010
- */
10011
- export function createAI(grid: unknown, config?: AIConfig): AI;
10012
-
10013
- /**
10014
- * Build the RISK-SUMMARY facts packet (BACKLOG-0000979) from the separate
10015
- * Gantt / Kanban modules' public outputs — SPI/CPI and variances from
10016
- * `gantt.earnedValue()`, tasks at risk / on the critical path from
10017
- * `gantt.schedule`, and SLA breaches from `board.sla`. Reads the module
10018
- * instances (or their precomputed outputs) duck-typed off `target`; the AI
10019
- * bundle imports neither module. This is the exact grounded set
10020
- * `explain({ kind: 'risk' })` would use, exposed for preview and testing.
10021
- */
10022
- export function buildRiskFacts(target: AITarget, opts?: {
10023
- locale?: string; fmt?: (value: number) => string;
10024
- }): AIRiskFacts;
10025
-
10026
- export default createAI;
10027
- }
10028
-
10029
- declare module 'lattice-grid/modules/tabs' {
10030
- /**
10031
- * One tab: an id, a display label, a grid config, and — for a derived tab —
10032
- * the parent tab id plus the narrowing forwarded onto the derived source
10033
- * built for it (`source: { mode: 'derived', from: <parent's grid>, ... }`).
10034
- * The derivation keys are the ones `packages/core/src/source/derive.js`
10035
- * already understands; this module invents none of its own.
10036
- */
10037
- interface TabDescriptor {
10038
- /** A stable, unique id. Required. */
10039
- id: string;
10040
- /** The tab button's text. Defaults to `id`. */
10041
- label?: string;
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. */
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;
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). */
10058
- from?: string;
10059
- /** Row predicate forwarded to the derived source. */
10060
- where?: (row: unknown) => boolean;
10061
- /** Group-by forwarded to the derived source. */
10062
- group?: unknown;
10063
- groupBy?: unknown;
10064
- /** Time-bucketing forwarded to the derived source. */
10065
- bucket?: unknown;
10066
- /** Join spec forwarded to the derived source. */
10067
- join?: unknown;
10068
- /** Array-field unnesting forwarded to the derived source. */
10069
- unnest?: unknown;
10070
- /** `'live' | 'idle' | 'manual' | number` forwarded to the derived source. */
10071
- refresh?: 'live' | 'idle' | 'manual' | number;
10072
- /** Cross-filter wiring forwarded to the derived source. */
10073
- crossFilter?: unknown;
10074
- /** Which slice of the parent's rows to derive from: `'filtered' | 'all' | 'selected' | 'grouped'`. */
10075
- follow?: 'filtered' | 'all' | 'selected' | 'grouped';
10076
- /** Row limit forwarded to the derived source. */
10077
- limit?: number;
10078
- /** Sort forwarded to the derived source. */
10079
- sort?: unknown;
10080
- /** Statistical-profile derivation, forwarded to the derived source. */
10081
- profile?: unknown;
10082
- /** This tab's panel's own `aria-label`, when the label alone is not enough context. */
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);
10090
- }
10091
-
10092
- /** The payload every tab-change event carries. */
10093
- interface TabChangeEvent {
10094
- id: string;
10095
- previousId: string | null;
10096
- origin?: 'api' | 'user' | 'init';
10097
- reason?: string | null;
10098
- /** Cancel the switch (only meaningful on `beforeTabChange`). */
10099
- preventDefault?: (reason?: string) => void;
10100
- defaultPrevented?: boolean;
10101
- }
10102
-
10103
- /** Tabbed-grid configuration. */
10104
- interface TabsConfig {
10105
- /** The grid factory to mount each tab with, e.g. `import { createGrid } from 'lattice-grid'`. Required. */
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;
10109
- /** The tabs, in display order. Required, at least one. */
10110
- tabs: TabDescriptor[];
10111
- /** The initially active tab id. Defaults to the first tab. */
10112
- active?: string;
10113
- /** The tablist landmark's accessible name. */
10114
- ariaLabel?: string;
10115
- /** An explicit message-catalogue override; otherwise a mounted tab's own `grid.messages` is used. */
10116
- messages?: { t(key: string, params?: Record<string, unknown>): string };
10117
- onTabChange?: (event: TabChangeEvent) => void;
10118
- onBeforeTabChange?: (event: TabChangeEvent) => boolean | void | Promise<boolean>;
10119
- onTabChangeCancelled?: (event: TabChangeEvent) => void;
10120
- }
10121
-
10122
- /**
10123
- * A tabbed grid: a `role="tablist"` strip above a stack of `role="tabpanel"`
10124
- * regions, each hosting its own, independently-configured grid instance
10125
- * (BACKLOG-0001039). A tab's grid mounts on first activation and is kept
10126
- * alive, hidden, until `destroy()`.
10127
- */
10128
- interface Tabs {
10129
- readonly el: HTMLElement;
10130
- /** The currently active tab id. */
10131
- readonly activeId: string;
10132
- /** The configured tab ids, in order. */
10133
- tabs(): string[];
10134
- /** The live grid instance for a tab, or `null` before it has been materialised. */
10135
- tab(id: string): unknown | null;
10136
- /** Whether a tab's grid has been created yet. */
10137
- isMounted(id: string): boolean;
10138
- /** Switch the active tab, gated by `beforeTabChange`. */
10139
- activate(id: string, opts?: { origin?: 'api' | 'user' }): boolean | Promise<boolean>;
10140
- on(name: 'beforeTabChange' | 'tab:changed' | 'tabChange:cancelled' | string, fn: (event: TabChangeEvent) => void): () => void;
10141
- off(name: string, fn: (event: TabChangeEvent) => void): void;
10142
- /** Tear the whole strip down; destroys every mounted tab's grid. */
10143
- destroy(): void;
10144
- }
10145
-
10146
- /**
10147
- * Create a tabbed grid over a host element. Each tab is a full,
10148
- * independently-configured grid instance; a tab may derive from another via
10149
- * `from`, reusing the shipped `source: { mode: 'derived' }` mechanism.
10150
- */
10151
- export function createTabs(el: HTMLElement, config: TabsConfig): Tabs;
10152
- export default createTabs;
10153
- }
10154
-
10155
- declare module 'lattice-grid/modules/layout' {
10156
- /**
10157
- * One window on the cell grid.
10158
- *
10159
- * Deliberately **not** named `WindowSpec`: that name is already taken by the
10160
- * rolling-statistics window (`{ kind: 'count'|'time'|'session', span, size }`)
10161
- * and reusing it would put `kind: 'session'` next to a dashboard pane.
10162
- */
10163
- interface LayoutWindow {
10164
- /** A stable, unique id. Required. */
10165
- id: string;
10166
- /** The 1-based column the window starts in. Auto-placed when omitted. */
10167
- xPos?: number;
10168
- /** The 1-based row the window starts in. Auto-placed when omitted. */
10169
- yPos?: number;
10170
- /** How many columns it spans (default 1). */
10171
- xSize?: number;
10172
- /** How many rows it spans (default 1). */
10173
- ySize?: number;
10174
- /** The title shown in the chrome bar, and the name every control takes. */
10175
- title?: string;
10176
- /** Whether to draw the title bar (default `true`). */
10177
- chrome?: boolean;
10178
- /** Whether to offer a close button (default `false`). */
10179
- closable?: boolean;
10180
- /** Whether the window can be moved by drag or keyboard (default `false`). */
10181
- movable?: boolean;
10182
- /** Whether the window can be resized by drag or keyboard (default `false`). */
10183
- resizable?: boolean;
10184
- /**
10185
- * Whether to offer a maximise control in the chrome (default `false`).
10186
- *
10187
- * Maximising fills the **layout host**, not the browser window, and hides
10188
- * every other window for the duration. Escape restores it, unless a payload
10189
- * has already claimed the key.
10190
- */
10191
- maximisable?: boolean;
10192
- /**
10193
- * Whether to offer a minimise control in the chrome (default `false`).
10194
- *
10195
- * A window with `chrome: false` cannot be minimised whatever this says:
10196
- * there would be nothing left on screen to restore it with.
10197
- */
10198
- minimisable?: boolean;
10199
- /** Padding inside the window; the layout's `padding` (default `'5px'`) otherwise. */
10200
- padding?: number | string;
10201
- /** The `id` given to the payload container (default `` `${id}-body` ``). */
10202
- payloadId?: string;
10203
- /** The window's accessible name, when the title alone is not enough context. */
10204
- ariaLabel?: string;
10205
- }
10206
-
10207
- /**
10208
- * The three capabilities a layout-level default and `setInteractive()` cover.
10209
- *
10210
- * These are the layout **defaults**, not the per-window resolution: a window
10211
- * that declared `movable: false` stays pinned whatever these say.
10212
- *
10213
- * Three values, not two. `undefined` means no layout-level default is in force
10214
- * and each window's own flag decides; `true` unlocks everything that did not
10215
- * opt out; `false` is an active lock. Reporting `undefined` as `false` would
10216
- * read correctly and round-trip wrongly, so it is reported as it is.
10217
- */
10218
- interface LayoutInteractive {
10219
- movable: boolean | undefined;
10220
- resizable: boolean | undefined;
10221
- closable: boolean | undefined;
10222
- }
10223
-
10224
- /** The plain, JSON-safe arrangement `getLayout()` returns and `setLayout()` takes. */
10225
- interface LayoutSnapshot {
10226
- columns: number;
10227
- rows: number;
10228
- windows: { id: string; xPos: number; yPos: number; xSize: number; ySize: number }[];
10229
- }
10230
-
10231
- /** A cell placement, as carried on the move and resize events. */
10232
- interface LayoutPlacement {
10233
- xPos: number;
10234
- yPos: number;
10235
- xSize: number;
10236
- ySize: number;
10237
- }
10238
-
10239
- /** The payload of `window:moved`, `beforeWindowMove`, `beforeWindowResize`. */
10240
- interface LayoutMoveEvent {
10241
- id: string;
10242
- from: LayoutPlacement;
10243
- /** Where the window was asked to go. */
10244
- to: LayoutPlacement;
10245
- /** Where it actually ended up, which under `compact: 'vertical'` may differ. */
10246
- landed?: LayoutPlacement;
10247
- origin?: 'api' | 'user' | 'init';
10248
- reason?: string | null;
10249
- /** Cancel the action (only meaningful on a `before*` event). */
10250
- preventDefault?: (reason?: string) => void;
10251
- defaultPrevented?: boolean;
10252
- }
10253
-
10254
- /**
10255
- * The payload of `window:resized` — the measured **content box** of the
10256
- * payload container, not a cell count. Emitted when the container genuinely
10257
- * changes size, including on the opening frame; never with a zero box.
10258
- */
10259
- interface LayoutResizeEvent {
10260
- id: string;
10261
- payloadId: string;
10262
- /** The payload container itself, so a host can act on it directly. */
10263
- payload: HTMLElement;
10264
- width: number;
10265
- height: number;
10266
- xPos: number;
10267
- yPos: number;
10268
- xSize: number;
10269
- ySize: number;
10270
- }
10271
-
10272
- /** The payload of `window:closed` and `beforeWindowClose`. */
10273
- interface LayoutCloseEvent {
10274
- id: string;
10275
- payloadId: string;
10276
- /** The payload container, handed back so the host can destroy what it mounted. */
10277
- payload?: HTMLElement;
10278
- origin?: 'api' | 'user';
10279
- reason?: string | null;
10280
- preventDefault?: (reason?: string) => void;
10281
- defaultPrevented?: boolean;
10282
- }
10283
-
10284
- /** The payload of `layout:changed`: the whole arrangement, plus what moved it. */
10285
- interface LayoutChangedEvent extends LayoutSnapshot {
10286
- cause: string;
10287
- }
10288
-
10289
- /** Dashboard layout configuration. */
10290
- interface LayoutConfig {
10291
- /** Cell columns across the mounted element (default 12). */
10292
- columns?: number;
10293
- /** Cell rows down the mounted element (default 6). */
10294
- rows?: number;
10295
- /** Horizontal overflow (default `'static'`). */
10296
- overflowX?: 'static' | 'scroll';
10297
- /** Vertical overflow (default `'static'`). */
10298
- overflowY?: 'static' | 'scroll';
10299
- /** Fixed column track size, used only when `overflowX` is `'scroll'` (default `'240px'`). */
10300
- columnWidth?: number | string;
10301
- /** Fixed row track size, used only when `overflowY` is `'scroll'` (default `'160px'`). */
10302
- rowHeight?: number | string;
10303
- /** The gap between cells (default `'8px'`). */
10304
- gap?: number | string;
10305
- /** The default padding inside a window (default `'5px'`). */
10306
- padding?: number | string;
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';
10316
- /**
10317
- * The default `movable` for every window that does not declare its own
10318
- * (default `false`). This states a default, so `false` takes nothing away
10319
- * from a window that declared `movable: true`; `setInteractive(false)` is
10320
- * the active lock that does.
10321
- */
10322
- movable?: boolean;
10323
- /** The default `resizable` for windows that declare none (default `false`); see `movable`. */
10324
- resizable?: boolean;
10325
- /** The default `closable` for windows that declare none (default `false`); see `movable`. */
10326
- closable?: boolean;
10327
- /**
10328
- * The default `maximisable` for windows that declare none (default `false`).
10329
- *
10330
- * Not touched by `setInteractive()`: a display mode neither moves nor resizes
10331
- * a window in the arrangement, so a locked dashboard can still be blown up
10332
- * to read.
10333
- */
10334
- maximisable?: boolean;
10335
- /** The default `minimisable` for windows that declare none (default `false`); see `maximisable`. */
10336
- minimisable?: boolean;
10337
- /** The windows, in mount order. */
10338
- windows?: LayoutWindow[];
10339
- /** An arrangement to apply at mount, as produced by `getLayout()`. */
10340
- layout?: LayoutSnapshot;
10341
- /** The layout region's accessible name. */
10342
- ariaLabel?: string;
10343
- /** A message catalogue, e.g. `grid.messages`; built-in English seeds otherwise. */
10344
- messages?: { t(key: string, params?: Record<string, unknown>): string };
10345
- onWindowMoved?: (event: LayoutMoveEvent) => void;
10346
- onWindowResized?: (event: LayoutResizeEvent) => void;
10347
- onWindowClosed?: (event: LayoutCloseEvent) => void;
10348
- onLayoutChanged?: (event: LayoutChangedEvent) => void;
10349
- onBeforeWindowMove?: (event: LayoutMoveEvent) => boolean | void | Promise<boolean>;
10350
- onBeforeWindowResize?: (event: LayoutMoveEvent) => boolean | void | Promise<boolean>;
10351
- onBeforeWindowClose?: (event: LayoutCloseEvent) => boolean | void | Promise<boolean>;
10352
- onWindowMoveCancelled?: (event: LayoutMoveEvent) => void;
10353
- onWindowResizeCancelled?: (event: LayoutMoveEvent) => void;
10354
- onWindowCloseCancelled?: (event: LayoutCloseEvent) => void;
10355
- }
10356
-
10357
- /**
10358
- * A reconfigurable dashboard: a cell grid inside an element, and a set of
10359
- * windows on it that a user can move, resize and close by pointer or by
10360
- * keyboard (BACKLOG-0001108).
10361
- *
10362
- * The module is **payload-agnostic**: a window body is a container with an id,
10363
- * which this module creates and sizes and never reads. It tells a payload it
10364
- * was resized by emitting `window:resized`; it never calls into one, because it
10365
- * cannot know what one is.
10366
- */
10367
- interface Layout {
10368
- readonly el: HTMLElement;
10369
- /** The window ids, in mount order. */
10370
- windows(): string[];
10371
- /** The payload container for a window, or `null`. */
10372
- payload(id: string): HTMLElement | null;
10373
- /** A copy of one window's current descriptor, or `null`. */
10374
- window(id: string): LayoutWindow | null;
10375
- /** Add a window after mount; returns its payload container. */
10376
- add(spec: LayoutWindow): HTMLElement;
10377
- /** Move or resize a window, through the same before-events the drag uses. */
10378
- move(id: string, to: Partial<LayoutPlacement>): boolean | Promise<boolean>;
10379
- /** Close a window through `beforeWindowClose`; the payload is not destroyed. */
10380
- close(id: string): boolean | Promise<boolean>;
10381
- /**
10382
- * Blow one window up to fill the layout host, hiding the rest.
10383
- *
10384
- * It fills the **host element**, not the browser window, so there is no
10385
- * `position: fixed` (whose containing block is the nearest ancestor carrying
10386
- * a `transform` or a `contain`, which is why the same rule fills the screen
10387
- * on one page and lands in a 300px box on the next), no reparenting and
10388
- * nothing that can disturb the page around the dashboard.
10389
- *
10390
- * **Nothing moves**: no compaction runs, no placement changes, and the
10391
- * payload container is the same DOM node throughout. **Escape restores it**,
10392
- * from anywhere inside the layout — a focused grid body cell or column
10393
- * heading included — unless a payload has already claimed the key: an open
10394
- * cell editor, filter menu or column menu closes first, and the next Escape
10395
- * restores the window. Afterwards focus lands on the window's maximise
10396
- * control. A minimised window is expanded first, and maximising a second
10397
- * window restores the first.
10398
- */
10399
- maximise(id: string): boolean;
10400
- /**
10401
- * Collapse one window to a single row: its payload is hidden and its chrome
10402
- * stays, carrying the control that brings it back.
10403
- *
10404
- * On screen it becomes one row and the windows below pull up into the space
10405
- * under `compact: 'vertical'`. In the arrangement nothing moves at all — the
10406
- * collapse is a projection of it — so `restore()` gives back exactly the
10407
- * arrangement that was there, in **any** order and with any number of other
10408
- * windows still collapsed.
10409
- *
10410
- * A window with `chrome: false` is refused, with a warning naming it.
10411
- */
10412
- minimise(id: string): boolean;
10413
- /** Leave whichever display mode a window is in; `false` when it was in none. */
10414
- restore(id: string): boolean;
10415
- /** The id of the window filling the host, or `null`. At most one. */
10416
- maximised(): string | null;
10417
- /** The ids of every currently minimised window, in mount order. */
10418
- minimised(): string[];
10419
- /**
10420
- * The full current arrangement.
10421
- *
10422
- * **A mode is not an arrangement**: this reports the *underlying* placement
10423
- * of a maximised or minimised window — where it will be when restored — never
10424
- * the geometry it is drawn at.
10425
- */
10426
- getLayout(): LayoutSnapshot;
10427
- /** Restore an arrangement; never throws on garbage. */
10428
- setLayout(incoming: LayoutSnapshot | LayoutWindow[]): number;
10429
- /** A versioned snapshot, following core's and gantt's shape. */
10430
- getState(): { version: number; layout: LayoutSnapshot };
10431
- /** Restore a `getState()` snapshot; never throws on garbage. */
10432
- setState(snapshot: unknown): number;
10433
- /**
10434
- * Lock or unlock the dashboard at runtime — the "Edit layout" button. A
10435
- * boolean sets all three capabilities; an object sets only the keys it
10436
- * carries. Nothing is destroyed, so every payload survives the toggle.
10437
- *
10438
- * The asymmetry is deliberate: **you can always take a capability away; you
10439
- * can never grant one where the developer said no.** `setInteractive(false)`
10440
- * locks every window, including one whose own spec says `movable: true`;
10441
- * `setInteractive(true)` unlocks only the windows that never opted out.
10442
- *
10443
- * `config.movable: false` and `setInteractive(false)` are deliberately not
10444
- * the same thing: the config states the *default* for windows that declare
10445
- * nothing (and `false` is already that default, so it takes nothing away from
10446
- * a window that opted in), while this is an *active lock*.
10447
- *
10448
- * A key carrying `undefined` is treated as absent, so
10449
- * `setInteractive(getInteractive())` is a no-op in every state.
10450
- *
10451
- * A locked layout is not a read-only dashboard: this module never reads or
10452
- * writes a payload, so a grid inside a window is made read-only with the
10453
- * grid's own settings.
10454
- */
10455
- setInteractive(value: boolean | Partial<LayoutInteractive>): LayoutInteractive;
10456
- /**
10457
- * The layout-level interactivity now in force, as a copy — `undefined` where
10458
- * no layout-level default is set, so the result round-trips through
10459
- * `setInteractive`.
10460
- */
10461
- getInteractive(): LayoutInteractive;
10462
- /** Re-measure every window and emit `window:resized` for those that changed. */
10463
- refresh(): number;
10464
- on(
10465
- name: 'window:moved' | 'window:resized' | 'window:closed' | 'layout:changed'
10466
- | 'beforeWindowMove' | 'beforeWindowResize' | 'beforeWindowClose'
10467
- | 'windowMove:cancelled' | 'windowResize:cancelled' | 'windowClose:cancelled'
10468
- | '*' | string,
10469
- fn: (event: any) => unknown,
10470
- ): () => void;
10471
- off(name: string, fn: (event: any) => unknown): void;
10472
- /** Tear the layout down; whatever the host mounted in a payload is the host's to destroy. */
10473
- destroy(): void;
10474
- }
10475
-
10476
- /** Create a reconfigurable dashboard layout over a host element. */
10477
- export function createLayout(el: HTMLElement, config?: LayoutConfig): Layout;
10478
- export default createLayout;
10479
- }