@kanunilabs/pivotgrid-core 1.5.0 → 1.7.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.
package/dist/index.d.cts CHANGED
@@ -164,22 +164,6 @@ interface PivotDictionary {
164
164
  drillDown: string;
165
165
  /** `Label: {count}` — no plural engine, so no locale is ever ungrammatical. */
166
166
  drillDownRecords: string;
167
- /** Chart panel (#214); the eleven shared strings come from DataGrid. */
168
- chart: string;
169
- chartClose: string;
170
- chartFullscreen: string;
171
- chartExitFullscreen: string;
172
- chartEmpty: string;
173
- /** `Showing {shown} of {total}.` — used for categories AND for series. */
174
- chartWindowed: string;
175
- chartTypeColumn: string;
176
- chartTypeBar: string;
177
- chartTypeLine: string;
178
- chartTypeArea: string;
179
- chartTypePie: string;
180
- chartTypeScatter: string;
181
- chartTypePolar: string;
182
- chartTypeCombination: string;
183
167
  /** PDF export header (#215). */
184
168
  exportGeneratedOn: string;
185
169
  /** Designer level tiles and live formula verdict (#220). */
@@ -316,6 +300,310 @@ declare class PivotSelectionModel {
316
300
  private static getBounds;
317
301
  }
318
302
 
303
+ /** How a cartesian chart lays out several series: beside, on top, or as shares of 100. */
304
+ type ChartStacking = 'grouped' | 'stacked' | 'normalized';
305
+ interface StackedSegment {
306
+ /** Where the segment starts, per category. NaN where the series has no value. */
307
+ low: Float64Array;
308
+ /** Where it ends; a line is drawn through these. NaN where the series has no value. */
309
+ high: Float64Array;
310
+ /**
311
+ * The value's percentage of its category's absolute total (`normalized`
312
+ * only; NaN otherwise and where there is no value) — what a tooltip prints
313
+ * next to the raw number.
314
+ */
315
+ share: Float64Array;
316
+ }
317
+ /**
318
+ * Value-axis ticks for a 100% stack: every 25%, ending exactly at 100.
319
+ *
320
+ * A general "nice ticks" routine rounds the domain OUTWARD, and on a 100%
321
+ * stack that drew an axis running to 150% above bars that cannot pass 100
322
+ * (seen in the first visual check, 2026-10-02). `min` is the lowest segment
323
+ * edge — below zero only when some values are negative.
324
+ */
325
+ declare function percentAxisTicks(min: number): number[];
326
+ /**
327
+ * Lay series out on top of one another.
328
+ *
329
+ * `series[s][c]` is the value of series `s` in category `c`. The result is
330
+ * index-aligned with `series`; an excluded series (hidden from the legend)
331
+ * comes back as `null` and takes no room in the stack. Earlier series sit
332
+ * nearer the axis.
333
+ */
334
+ declare function stackSeries(series: ReadonlyArray<ArrayLike<number>>, mode: Exclude<ChartStacking, 'grouped'>, include?: (index: number) => boolean): Array<StackedSegment | null>;
335
+
336
+ /** Where the legend sits around the plot, or `'none'` to hide it. */
337
+ type ChartLegendPosition = 'top' | 'bottom' | 'left' | 'right' | 'none';
338
+ interface ChartFormat {
339
+ /** Shown above the chart, and written into an exported image. */
340
+ title?: string;
341
+ /** Caption under the category axis (the left one for horizontal bars). */
342
+ categoryAxisTitle?: string;
343
+ /** Caption beside the value axis. */
344
+ valueAxisTitle?: string;
345
+ /** Default `'top'`. */
346
+ legend?: ChartLegendPosition;
347
+ /** Print each value on its mark. Default `false`. */
348
+ dataLabels?: boolean;
349
+ /**
350
+ * The value axis: `'linear'` (default) or `'log'` — for values spanning
351
+ * several orders of magnitude, where a linear axis flattens all but the
352
+ * largest. A log axis cannot show zero or a negative value; those marks are
353
+ * left out. It does not apply to a stacked layout, whose segments add up.
354
+ */
355
+ valueScale?: 'linear' | 'log';
356
+ }
357
+ /**
358
+ * Value-axis ticks for a log scale: every power of ten from just below `min`
359
+ * to just above `max` (both > 0). With fewer than two decades the 2 and 5
360
+ * steps in between are added so the axis still has something to read.
361
+ */
362
+ declare function logAxisTicks(min: number, max: number): number[];
363
+ declare const CHART_LEGEND_POSITIONS: readonly ChartLegendPosition[];
364
+ /**
365
+ * A saved format read back defensively — from storage, a report or an older
366
+ * build — keeping only fields of the right kind. Key order is fixed, so two
367
+ * formats with the same content serialise the same.
368
+ */
369
+ declare function readChartFormat(value: unknown): ChartFormat;
370
+ /**
371
+ * What a chart API's image calls take — `getImageDataUrl` and `download` on
372
+ * the DataGrid chart API, `getChartImageDataUrl` and `downloadChart` on the
373
+ * PivotGrid one.
374
+ */
375
+ interface ChartImageRequest {
376
+ /** `'png'` (default), `'jpeg'` or `'svg'`. */
377
+ format?: 'png' | 'jpeg' | 'svg';
378
+ /** File name without extension (`download` only). Defaults to one naming what the chart shows. */
379
+ fileName?: string;
380
+ /**
381
+ * Image size in CSS pixels. One side keeps the chart's proportions; both fix
382
+ * the box and centre the chart in it. Ignored for SVG, which scales anyway.
383
+ */
384
+ width?: number;
385
+ height?: number;
386
+ /** JPEG quality, 0–1. Default `0.92`. */
387
+ quality?: number;
388
+ /** Raster pixel ratio. Default `2`. */
389
+ pixelRatio?: number;
390
+ /** Background colour; `null` keeps transparency (not for JPEG). Default white. */
391
+ background?: string | null;
392
+ }
393
+ /**
394
+ * `count` colours derived from `accent`, index-stable.
395
+ *
396
+ * Falls back to the packaged eight when the accent cannot be read — an unthemed
397
+ * grid, or a custom theme whose `--dg-primary` is something this cannot parse
398
+ * (a named colour, `color-mix()`, `oklch()`). Falling back is deliberate: a
399
+ * chart with the wrong-but-readable palette beats a chart of black shapes.
400
+ *
401
+ * A near-grey accent also falls back. Rotating the hue of something with almost
402
+ * no saturation produces eight indistinguishable greys, which is worse than an
403
+ * off-theme palette that can actually be told apart.
404
+ */
405
+ declare function chartPalette(accent: string | null | undefined, count: number): string[];
406
+
407
+ interface WaterfallStep {
408
+ /** Running total before and after this step. */
409
+ start: number;
410
+ end: number;
411
+ /** An increase, a decrease, or the closing total bar (which starts at zero). */
412
+ kind: 'up' | 'down' | 'total';
413
+ /** The step's own value (the total's value is the running total). */
414
+ value: number;
415
+ }
416
+ /**
417
+ * Each category moves the running total by its value; the bar floats from the
418
+ * old total to the new one. With `total`, a last bar shows where it ended,
419
+ * standing on zero. NaN ("nothing to total") moves nothing and draws a step
420
+ * of zero height, so the categories stay aligned.
421
+ */
422
+ declare function waterfallSteps(values: ArrayLike<number>, total?: boolean): WaterfallStep[];
423
+ /** Lowest and highest point a waterfall reaches, zero included. */
424
+ declare function waterfallDomain(steps: readonly WaterfallStep[]): {
425
+ min: number;
426
+ max: number;
427
+ };
428
+ interface FunnelStage {
429
+ /** Index into the input values. */
430
+ index: number;
431
+ value: number;
432
+ /** Bar width as a share of the widest stage, 0..1. */
433
+ ratio: number;
434
+ /** Share of the FIRST stage (the funnel's conversion so far), 0..1. */
435
+ ofFirst: number;
436
+ }
437
+ /**
438
+ * Stages in the order given — a funnel's order is the process, not the
439
+ * magnitude, so it is never re-sorted. Negative and NaN values are stages of
440
+ * zero width: a funnel stage cannot hold less than nothing.
441
+ */
442
+ declare function funnelStages(values: ArrayLike<number>): FunnelStage[];
443
+ interface TreemapCell {
444
+ /** Index into the input values. */
445
+ index: number;
446
+ value: number;
447
+ x: number;
448
+ y: number;
449
+ width: number;
450
+ height: number;
451
+ }
452
+ /**
453
+ * Squarified treemap (Bruls, Huizing, van Wijk): rectangles with areas
454
+ * proportional to the values, laid out to stay as close to square as the
455
+ * values allow — a long thin sliver is hard to compare and to label. Only
456
+ * positive values take area; the rest are left out (a negative area has no
457
+ * meaning). Largest first.
458
+ */
459
+ declare function squarify(values: ArrayLike<number>, x: number, y: number, width: number, height: number): TreemapCell[];
460
+ /**
461
+ * A heatmap cell's colour: `t` in 0..1 between `from` and `to` (hex colours),
462
+ * as an `rgb()` literal — a presentation attribute cannot hold `var()`.
463
+ */
464
+ declare function heatColor(t: number, from?: string, to?: string): string;
465
+ /** Text that stays readable on a heat colour: dark on light cells, white on dark. */
466
+ declare function heatTextColor(t: number): string;
467
+
468
+ type MarkTone = 'muted' | 'border' | 'text';
469
+ type ChartMark = {
470
+ kind: 'rect';
471
+ x: number;
472
+ y: number;
473
+ width: number;
474
+ height: number;
475
+ fill: string;
476
+ /** Category / series index, for tooltips and clicks (absent on chrome). */
477
+ ci?: number;
478
+ si?: number;
479
+ /** Native tooltip text. */
480
+ title?: string;
481
+ rx?: number;
482
+ opacity?: number;
483
+ } | {
484
+ kind: 'text';
485
+ x: number;
486
+ y: number;
487
+ text: string;
488
+ anchor: 'start' | 'middle' | 'end';
489
+ size?: number;
490
+ weight?: number;
491
+ /** A literal colour, or a chrome role. */
492
+ fill?: string;
493
+ tone?: MarkTone;
494
+ /** Degrees, about (x, y). */
495
+ rotate?: number;
496
+ } | {
497
+ kind: 'line';
498
+ x1: number;
499
+ y1: number;
500
+ x2: number;
501
+ y2: number;
502
+ dashed?: boolean;
503
+ tone: MarkTone;
504
+ } | {
505
+ /** An SVG path — the arcs of a sunburst. */
506
+ kind: 'path';
507
+ d: string;
508
+ fill: string;
509
+ ci?: number;
510
+ si?: number;
511
+ title?: string;
512
+ opacity?: number;
513
+ };
514
+ interface ShapeChartInput {
515
+ /** Category labels, blanks already captioned. */
516
+ categories: string[];
517
+ /** The series to draw (hidden ones already left out), each with its ORIGINAL index. */
518
+ series: Array<{
519
+ index: number;
520
+ label: string;
521
+ values: ArrayLike<number>;
522
+ color: string;
523
+ }>;
524
+ width: number;
525
+ height: number;
526
+ /** A value as the series' column or measure prints it. */
527
+ format: (seriesIndex: number, value: number) => string;
528
+ /** Compact axis captions ("5.5M"). */
529
+ compact: (value: number) => string;
530
+ /** Caption of the waterfall's closing bar. */
531
+ totalLabel?: string;
532
+ /** Category / value axis titles (waterfall). */
533
+ categoryAxisTitle?: string;
534
+ valueAxisTitle?: string;
535
+ /** Colours: waterfall up / down / total, heatmap ramp ends. Defaults are neutral. */
536
+ colors?: {
537
+ up?: string;
538
+ down?: string;
539
+ total?: string;
540
+ heatFrom?: string;
541
+ heatTo?: string;
542
+ };
543
+ rtl?: boolean;
544
+ }
545
+ /**
546
+ * The first series as a waterfall: each category moves the running total,
547
+ * increases and decreases coloured apart, a closing total bar on zero.
548
+ * Connector lines run from each bar's end to the next bar's start.
549
+ */
550
+ declare function waterfallMarks(input: ShapeChartInput): ChartMark[];
551
+ /**
552
+ * The first series as a funnel: one centred bar per category, in the given
553
+ * order (the order is the process), each labelled with its value and its
554
+ * share of the first stage.
555
+ */
556
+ declare function funnelMarks(input: ShapeChartInput, percentText: (share: number) => string): ChartMark[];
557
+ /**
558
+ * The first series as a treemap: a rectangle per category with area
559
+ * proportional to its value (positive values only), labelled where the
560
+ * label fits. Colours run through the given palette by category.
561
+ */
562
+ declare function treemapMarks(input: ShapeChartInput, palette: readonly string[]): ChartMark[];
563
+ /**
564
+ * Categories across, series down, each cell coloured by its value on one ramp
565
+ * over all the visible cells — a matrix read at a glance. Values print in the
566
+ * cells that have room; a cell with nothing to total stays blank.
567
+ */
568
+ declare function heatmapMarks(input: ShapeChartInput): ChartMark[];
569
+ /**
570
+ * The first two series as a RANGE per category: one floating bar from the
571
+ * lower of the two values to the higher — a day's low and high, a price band,
572
+ * a planned-versus-actual span. Which series is "low" does not matter: each
573
+ * category draws from its own smaller value to its larger one. A category
574
+ * missing either value draws nothing (a span needs both ends).
575
+ */
576
+ declare function rangeBarMarks(input: ShapeChartInput): ChartMark[];
577
+ interface SunburstInput {
578
+ /**
579
+ * The leaves, each with its path from the outermost level in (`['EMEA',
580
+ * 'Berlin']`), its value and the category index a click or tooltip names.
581
+ * Inner rings are the sums of the leaves under them.
582
+ */
583
+ leaves: Array<{
584
+ path: readonly string[];
585
+ value: number;
586
+ ci: number;
587
+ }>;
588
+ width: number;
589
+ height: number;
590
+ /** One colour per outermost-level node; deeper rings take their parent's, lighter. */
591
+ colors: readonly string[];
592
+ format: (value: number) => string;
593
+ /** The share of the whole, as the reader's locale prints a percentage. */
594
+ percentText: (share: number) => string;
595
+ }
596
+ /**
597
+ * A hierarchy as rings: the outermost level in the middle ring, each level
598
+ * further out, every arc's angle its share of the whole. The leaves sum up
599
+ * into their parents, so the picture answers "what is this made of, and of
600
+ * what is that made". Zero, negative and missing leaves take no angle.
601
+ */
602
+ declare function sunburstMarks(input: SunburstInput): ChartMark[];
603
+ /** The chart types laid out here. */
604
+ declare const SHAPE_CHART_TYPES: readonly ["waterfall", "funnel", "treemap", "heatmap", "rangeBar", "sunburst"];
605
+ type ShapeChartType = (typeof SHAPE_CHART_TYPES)[number];
606
+
319
607
  type DateFormatContext = 'grid' | 'chart' | 'export';
320
608
  type DateGranularity = 'year' | 'quarter' | 'month' | 'day' | 'dayOfWeek' | string | number;
321
609
  declare class DateFormatter {
@@ -382,6 +670,12 @@ interface WorkerFilterSpec {
382
670
  fieldId: string;
383
671
  filterType: 'include' | 'exclude';
384
672
  dictIds: number[];
673
+ /**
674
+ * Filter values the dictionary does not hold yet. No stored row can match
675
+ * them, but a row added by a later transaction can — a rebuild would include
676
+ * it, so the engine keeps them and resolves them as the dictionary grows.
677
+ */
678
+ valueStrings?: string[];
385
679
  }
386
680
  interface PrefilterConditionSpec {
387
681
  fieldId: string;
@@ -473,6 +767,23 @@ interface WorkerReconfigureRequest {
473
767
  /** Dictionary entries appended (in order) since the worker's dictionary was last synced. */
474
768
  dictionaryAppend?: string[];
475
769
  }
770
+ /**
771
+ * Live update (G-PIV-02): replace, remove and append rows of the stored
772
+ * columns. Positions are those BEFORE the transaction; `update` and `remove`
773
+ * never share a position. `columns` carries, for EVERY stored column, the
774
+ * values of the updated rows (in `update` order) followed by the added rows —
775
+ * encoded by the main thread with the same field definitions as the column.
776
+ */
777
+ interface WorkerTransactionRequest {
778
+ action: 'transaction';
779
+ id: string;
780
+ update: Int32Array;
781
+ /** Ascending. */
782
+ remove: Int32Array;
783
+ addCount: number;
784
+ columns: Record<string, ArrayBuffer>;
785
+ dictionaryAppend?: string[];
786
+ }
476
787
  /**
477
788
  * Flat cell matrix returned by the engine.
478
789
  * `keys[i]` = `rowId * MAX_C + colId` hash; `sums[i * numDF + df]` = finalized aggregated value.
@@ -485,11 +796,31 @@ interface FlatCellMatrix {
485
796
  /** Post-processed display values after summary display modes are applied. */
486
797
  displaySums?: Float64Array;
487
798
  }
799
+ /**
800
+ * What the main thread needs to run `summaryType: 'custom'` measures.
801
+ *
802
+ * A custom summary is the customer's own function (or a registry aggregator),
803
+ * and functions cannot be posted to a worker — so the worker groups and the
804
+ * main thread summarises. Each stored row is mapped to its leaf group, and
805
+ * each leaf group to the visible cells it rolls up into, so the main thread
806
+ * feeds every cell exactly the rows that cell covers, in source-row order.
807
+ */
808
+ interface CustomSummaryInputs {
809
+ /** Leaf group of each stored row; -1 for a row the filters removed. */
810
+ rowGroupOf: Int32Array;
811
+ /** Cells of leaf group g are `groupCells[groupCellOffsets[g] .. groupCellOffsets[g + 1])` (cell flat indexes). */
812
+ groupCellOffsets: Int32Array;
813
+ groupCells: Int32Array;
814
+ /** Per-row value of each custom data field, by field id, as encoded for the worker. */
815
+ values: Record<string, Float64Array>;
816
+ }
488
817
  interface WorkerResponse {
489
818
  id: string;
490
819
  rowTree: PivotNode[];
491
820
  colTree: PivotNode[];
492
821
  cellMatrix: FlatCellMatrix;
822
+ /** Present only when a data field has `summaryType: 'custom'`. */
823
+ customSummary?: CustomSummaryInputs;
493
824
  rowIndexMap: Record<string, number>;
494
825
  columnIndexMap: Record<string, number>;
495
826
  cardinalityMap: Record<string, number>;
@@ -505,13 +836,18 @@ interface WorkerResponse {
505
836
  isInitComplete?: boolean;
506
837
  isFilterComplete?: boolean;
507
838
  isReconfigureComplete?: boolean;
839
+ isTransactionComplete?: boolean;
840
+ /** Rows stored after a transaction — the main thread checks it against its own array. */
841
+ rowCount?: number;
508
842
  }
509
843
  /**
510
844
  * TypedArray-based pivot computation engine designed to run inside a Web Worker.
511
845
  *
512
846
  * Pipelines: monolithic `init()` → `applyFilter()` → `compute()`, or the
513
847
  * chunked `initStart()` → `initChunk()`× → `initEnd()` → `compute()` (filters
514
- * applied during the chunked build; result state is identical).
848
+ * applied during the chunked build; result state is identical). Either may be
849
+ * followed by any number of `applyTransaction()` (live updates) — the state
850
+ * after each is the one a fresh init over the resulting rows would reach.
515
851
  * Uses {@link FastTrie} for hierarchical grouping and pre-aggregates leaf groups
516
852
  * into flat TypedArrays supporting SUM, COUNT, AVG, MIN, MAX, and DISTINCT.
517
853
  */
@@ -525,8 +861,13 @@ declare class FlatPivotEngine {
525
861
  private lastMask;
526
862
  private isBuilt;
527
863
  private buildState;
528
- private incrementalFilters;
529
- private incrementalPrefilter;
864
+ private activeFilters;
865
+ private activePrefilter;
866
+ private tracking;
867
+ private groupRows;
868
+ private groupMap;
869
+ private rowLive;
870
+ private colLive;
530
871
  private rowTrie;
531
872
  private colTrie;
532
873
  private cardinalityMap;
@@ -543,6 +884,7 @@ declare class FlatPivotEngine {
543
884
  private leafGroupMins;
544
885
  private leafGroupMaxes;
545
886
  private leafGroupDistinctSets;
887
+ private rowGroupOf;
546
888
  init(req: WorkerInitRequest): void;
547
889
  /**
548
890
  * Swap field roles and regroup the already-stored columnar data under the
@@ -561,6 +903,10 @@ declare class FlatPivotEngine {
561
903
  initChunk(req: WorkerInitChunkRequest): void;
562
904
  /** Close the chunked init and commit the accumulated build. */
563
905
  initEnd(): void;
906
+ /** Append dictionary entries and resolve pending filter strings against them. */
907
+ private appendDictionary;
908
+ /** Put filters in force and size the mask for them (null when nothing filters). */
909
+ private setFilters;
564
910
  /**
565
911
  * Resolve pending filter value strings against dictionary entries from
566
912
  * `fromIndex` on. A string that is not yet in the dictionary cannot match
@@ -584,8 +930,42 @@ declare class FlatPivotEngine {
584
930
  private processRange;
585
931
  /** Commit the build state into the engine + rebuild per-build caches. */
586
932
  private finalizeBuild;
933
+ /** Path string per trie node; `known` (from an earlier pass) is extended, not redone. */
587
934
  private buildPathStrs;
588
935
  private buildIndexMap;
936
+ /**
937
+ * Live update (G-PIV-02): replace, remove and append rows, then bring the
938
+ * groups up to date — without re-reading the rows that did not change.
939
+ *
940
+ * What it costs, and why each part is there:
941
+ * - Each changed row is regrouped (two trie walks, one group lookup).
942
+ * - Every leaf group a changed row left or joined is RE-SUMMED from its
943
+ * rows, in row order, in one pass over the row→group column. Not patched
944
+ * by subtracting the old value: for floats that drifts (0.1 + 0.2 - 0.2 is
945
+ * 0.10000000000000003) and the error walks with every tick; min, max and
946
+ * distinct cannot be un-applied at all. A re-sum in row order produces the
947
+ * very value a rebuild produces.
948
+ * - Removing rows compacts the stored columns (a memmove per column), so
949
+ * rows stay in the order of the array the main thread holds.
950
+ * - Only when a group appears, empties or comes back is node visibility,
951
+ * the index maps and the cardinality redone — O(groups + nodes), never
952
+ * O(rows).
953
+ * The first transaction turns tracking on with one regroup of the stored
954
+ * columns (no re-encode, no transfer), and once empty groups outnumber live
955
+ * ones the engine regroups again rather than carry them through every
956
+ * compute. Returns the number of rows stored afterwards.
957
+ */
958
+ applyTransaction(req: WorkerTransactionRequest): number;
959
+ /** Reset the touched groups and fold their rows back in — the same arithmetic, in the same order, as processRange. */
960
+ private resumGroups;
961
+ /**
962
+ * One trie, after a transaction reshaped the groups: which nodes a live
963
+ * group still reaches, each node's live child count (what `hasChildren`
964
+ * reads), the path strings for nodes it created, the index map without the
965
+ * dead nodes, and the fields' cardinality counted over live nodes — what
966
+ * the build's seen-bitmaps count over rows.
967
+ */
968
+ private relive;
589
969
  compute(req: WorkerComputeRequest): WorkerResponse;
590
970
  }
591
971
 
@@ -596,6 +976,11 @@ interface PivotEngineResult {
596
976
  rowIndexMap: Record<string, number>;
597
977
  columnIndexMap: Record<string, number>;
598
978
  cardinalityMap: Record<string, number>;
979
+ /**
980
+ * Worker grouping for `summaryType: 'custom'` measures, consumed (and then
981
+ * removed) by `applyCustomSummaries` before any other post-phase.
982
+ */
983
+ customSummary?: CustomSummaryInputs;
599
984
  }
600
985
  interface FieldDistinctEntry {
601
986
  rawStr: string;
@@ -645,7 +1030,7 @@ declare function initPivotEngineAsync<TRow>(data: TRow[], rowFields: PivotField<
645
1030
  * full {@link initPivotEngineAsync}.
646
1031
  */
647
1032
  declare function reconfigurePivotEngineAsync<TRow>(data: TRow[], rowFields: PivotField<TRow>[], colFields: PivotField<TRow>[], dataFields: PivotField<TRow>[], registry?: PivotEngineRegistry,
648
- /** All fields + the active prefilter, so its columns are encoded too. */
1033
+ /** All fields + the active prefilter, so the filters' columns are encoded too. */
649
1034
  prefilterState?: {
650
1035
  fields: PivotField<TRow>[];
651
1036
  prefilter?: PivotPrefilter;
@@ -656,6 +1041,17 @@ prefilterState?: {
656
1041
  * set lookups on columnar data instead of string comparisons.
657
1042
  */
658
1043
  declare function filterPivotEngineAsync(fields: PivotField<any>[], prefilter?: PivotPrefilter): Promise<void>;
1044
+ /**
1045
+ * Live update (G-PIV-02): a resolved transaction, in positions of the array
1046
+ * the engine holds. See controller/rowTransaction.ts.
1047
+ */
1048
+ interface EngineRowChange<TRow> {
1049
+ data: TRow[];
1050
+ updateIndex: number[];
1051
+ updateRows: TRow[];
1052
+ removeIndex: number[];
1053
+ addRows: TRow[];
1054
+ }
659
1055
  declare function computePivotMatrixAsync(expandedPaths: string[], expandedColPaths: string[]): Promise<PivotEngineResult>;
660
1056
  /**
661
1057
  * Cached distinct values for a header filter.
@@ -674,6 +1070,19 @@ interface PivotReportState {
674
1070
  expandedPaths: string[];
675
1071
  expandedColPaths?: string[];
676
1072
  chartState?: Record<string, unknown>;
1073
+ /**
1074
+ * State kept by layers above the core, by name — the Enterprise chart panel
1075
+ * keeps its chart under `chart`. A report saved before a part existed has
1076
+ * none, and loading it leaves that part as it is.
1077
+ */
1078
+ parts?: Record<string, unknown>;
1079
+ }
1080
+ /** A piece of a saved report owned outside the core (see `registerReportStatePart`). */
1081
+ interface ReportStatePart {
1082
+ /** A JSON-safe snapshot of the part. */
1083
+ get(): unknown;
1084
+ /** Restore a snapshot `get` produced (or an older build's — validate it). */
1085
+ apply(value: unknown): void;
677
1086
  }
678
1087
  interface PivotReport {
679
1088
  id: string;
@@ -713,6 +1122,23 @@ interface PivotReport {
713
1122
  * insets from these numbers and React sizes its left pane from them, and two
714
1123
  * copies of the arithmetic would be two copies to keep in step.
715
1124
  */
1125
+ /** A row-header column's width when neither the app nor the user set one. */
1126
+ declare const DEFAULT_ROW_HEADER_WIDTH = 150;
1127
+ /** A data column's width when neither the app nor the user set one. */
1128
+ declare const DEFAULT_DATA_COLUMN_WIDTH = 95;
1129
+ /**
1130
+ * `defaultColumnWidths` with every hole filled: what the renderers paint a
1131
+ * column at when `columnWidths` has no entry for it.
1132
+ *
1133
+ * A value that is not a positive finite number falls back to the built-in
1134
+ * default rather than painting a zero-width or NaN column; the row-header
1135
+ * value is capped like a stored one, and the frozen-block budget below still
1136
+ * compresses it when the viewport is narrow.
1137
+ */
1138
+ declare function resolveColumnWidthDefaults(defaults?: PivotColumnWidthDefaults): {
1139
+ rowHeader: number;
1140
+ data: number;
1141
+ };
716
1142
  declare const ROW_HEADER_MIN_DATA_STRIP = 160;
717
1143
  declare const ROW_HEADER_MAX_PINNED_FRACTION = 0.6;
718
1144
  /** However tight it gets, a row-header column stays wide enough to read. */
@@ -1003,13 +1429,34 @@ declare function buildPivotAOA(engineResult: PivotEngineResult, rowFields: Pivot
1003
1429
  styles: (ExportCellStyle | null)[][] | null;
1004
1430
  } | null>;
1005
1431
 
1432
+ type PivotRowKey = string | number;
1433
+ /** A field name, or a function returning the key — the DataGrid `rowKey` contract. */
1434
+ type PivotRowKeyResolver<TRow> = keyof TRow | ((row: TRow) => PivotRowKey);
1435
+ interface PivotRowTransaction<TRow> {
1436
+ /** Rows appended after the existing ones. */
1437
+ add?: TRow[];
1438
+ /** Replace the row with the same key, in place. A key the grid does not have is ignored. */
1439
+ update?: TRow[];
1440
+ /** Keys, or rows (matched by their key), to take out. */
1441
+ remove?: (PivotRowKey | TRow)[];
1442
+ }
1443
+ interface PivotTransactionResult {
1444
+ added: number;
1445
+ updated: number;
1446
+ removed: number;
1447
+ }
1448
+
1006
1449
  /** Area where a {@link PivotField} can be placed. */
1007
1450
  type PivotArea = 'row' | 'column' | 'data' | 'filter' | 'chooser';
1008
1451
  /** Aggregation function applied to data fields in worker pre-aggregation. */
1009
1452
  type PivotSummaryType = 'sum' | 'count' | 'avg' | 'min' | 'max' | 'distinct' | 'custom';
1010
1453
  /** Post-aggregation display transformation applied after cell values are computed. */
1011
1454
  type PivotSummaryDisplayMode = 'absolute' | 'percentOfTotal' | 'percentOfColumnTotal' | 'percentOfRowTotal' | 'runningTotal' | 'difference' | 'percentDifference';
1012
- /** Temporal or numeric grouping interval for date/number fields. */
1455
+ /**
1456
+ * Date grouping interval. `'numeric'` and `'custom'` are accepted for
1457
+ * compatibility and have no effect (the grid reports them): numeric bins are
1458
+ * set with a number on {@link PivotField.groupInterval}.
1459
+ */
1013
1460
  type PivotGroupInterval = 'year' | 'quarter' | 'month' | 'day' | 'numeric' | 'custom';
1014
1461
  /** Supported UI locale identifiers. */
1015
1462
  type PivotLocale = 'tr' | 'en' | 'ar' | 'de' | 'fr' | 'es' | 'it' | 'pt' | 'ru' | 'zh-CN' | 'ja' | 'ko';
@@ -1091,10 +1538,27 @@ interface PivotField<TRow = any, TCellTemplate = PivotCellTemplate, TStyle = any
1091
1538
  areaIndex?: number;
1092
1539
  /** Whether the field is visible in the field chooser. */
1093
1540
  visible?: boolean;
1094
- /** Aggregation type — computed in the Web Worker for all types. */
1541
+ /**
1542
+ * Aggregation type. The built-ins are computed in the Web Worker; `'custom'`
1543
+ * runs {@link PivotField.calculateCustomSummary} on the main thread over
1544
+ * rows the worker grouped.
1545
+ */
1095
1546
  summaryType?: PivotSummaryType;
1096
1547
  /** Post-aggregation display transformation (e.g. percent of total). */
1097
1548
  summaryDisplayMode?: PivotSummaryDisplayMode;
1549
+ /**
1550
+ * The summary for `summaryType: 'custom'`, run once per cell (leaf,
1551
+ * subtotal and grand total alike) over that cell's own rows.
1552
+ *
1553
+ * - A function gets one options object per cell through three phases:
1554
+ * `'start'` (once), `'calculate'` (once per row, in source order, with the
1555
+ * row's value in `value`) and `'finalize'` (once). The cell shows
1556
+ * `totalValue` after finalize; other properties you set on the object
1557
+ * persist between phases.
1558
+ * - A string names an aggregator registered with
1559
+ * `registry.registerAggregator(id, fn)`; `fn` receives the cell's values.
1560
+ * An unregistered name is an engine error, not a silent sum.
1561
+ */
1098
1562
  calculateCustomSummary?: string | ((options: CustomSummaryOptions) => void);
1099
1563
  /** True for calculated (virtual) fields that derive from other fields. */
1100
1564
  isCalculated?: boolean;
@@ -1110,7 +1574,11 @@ interface PivotField<TRow = any, TCellTemplate = PivotCellTemplate, TStyle = any
1110
1574
  conditionalFormatting?: PivotConditionRule<TStyle>[];
1111
1575
  /** Custom cell render function. */
1112
1576
  cellTemplate?: TCellTemplate;
1113
- /** Date/numeric grouping interval. */
1577
+ /**
1578
+ * Grouping interval: `'year' | 'quarter' | 'month' | 'day'` for a date
1579
+ * field, or a bin width as a number for a number field (`100` → 0–99,
1580
+ * 100–199, …). `'numeric'` and `'custom'` have no effect.
1581
+ */
1114
1582
  groupInterval?: PivotGroupInterval | number;
1115
1583
  /** Active filter values for header filter (include/exclude mode). */
1116
1584
  filterValues?: Set<any>;
@@ -1138,12 +1606,9 @@ interface PivotLayoutState {
1138
1606
  /** The grand total ROW, at the end of the grid. */
1139
1607
  showRowGrandTotals?: boolean;
1140
1608
  /**
1141
- * @deprecated NOT IMPLEMENTED. Subtotals on the column axis do not exist:
1142
- * row subtotals are inserted while flattening the row tree, and the column
1143
- * axis has no equivalent pass in the renderer. Setting this to `true`
1144
- * explicitly reports through `onError`; it is left in the type because it
1145
- * has always had a default of `true`, so removing it would be a breaking
1146
- * change for a value that never did anything.
1609
+ * Per-level subtotals on the column axis: an expanded column group gets a
1610
+ * total column (before or after its children, per `totalsPosition`).
1611
+ * Default `true`.
1147
1612
  */
1148
1613
  showColumnTotals?: boolean;
1149
1614
  /** Per-level subtotals on the row axis. */
@@ -1156,9 +1621,8 @@ interface PivotLayoutState {
1156
1621
  */
1157
1622
  showGrandTotals?: 'rows' | 'columns' | 'both' | 'none';
1158
1623
  /**
1159
- * Coarse form of `showRowTotals`. Bridged for the ROW axis only — asking for
1160
- * `'columns'` or `'both'` reports through `onError`, because column subtotals
1161
- * are not implemented. See `showColumnTotals`.
1624
+ * Coarse form of `showRowTotals` + `showColumnTotals`. Bridged at the
1625
+ * controller boundary; an explicitly passed granular flag wins.
1162
1626
  */
1163
1627
  showSubTotals?: 'rows' | 'columns' | 'both' | 'none';
1164
1628
  /** Alias of `totalsPosition`, bridged at the controller boundary. */
@@ -1306,6 +1770,22 @@ interface PivotControllerState<TRow = any> {
1306
1770
  layout: PivotLayoutState;
1307
1771
  expandedPaths: Set<string>;
1308
1772
  expandedColPaths: Set<string>;
1773
+ /**
1774
+ * Column widths in pixels that the user dragged or `setColumnWidths` wrote.
1775
+ * Persisted with the layout (`stateStoring`). Keys:
1776
+ *
1777
+ * - `row-<i>` — row-header column `i`, counted from the outer row field
1778
+ * (`row-0`); in the tree layout `row-0` is the single row-header column.
1779
+ * With `dataFieldPosition: 'rows'` the column of measure names comes
1780
+ * last, at `row-<number of row-header columns>`.
1781
+ * - `leaf-<i>` — data column `i`, counted left to right across the whole
1782
+ * crosstab, totals included.
1783
+ *
1784
+ * A column with no entry is painted at `defaultColumnWidths` (150 for row
1785
+ * headers, 95 for data columns, when that is not set either). Row-header
1786
+ * widths may be painted narrower than stored when the frozen block would
1787
+ * leave the data less than its share of the viewport.
1788
+ */
1309
1789
  columnWidths: Record<string, number>;
1310
1790
  engineResult: PivotEngineResult | null;
1311
1791
  isBuilding: boolean;
@@ -1341,6 +1821,12 @@ interface PivotControllerOptions<TRow = any> extends PivotControllerCallbacks<TR
1341
1821
  controlledLayout?: PivotLayoutState;
1342
1822
  chartIntegrationOptions?: Record<string, any>;
1343
1823
  registry?: PivotEngineRegistry;
1824
+ /**
1825
+ * Server-side pivot (A4 / G-PIV-01, Enterprise): the server aggregates and
1826
+ * the grid lays the answer out. With it, `data` is not read; build one with
1827
+ * `pivotServerSource` from an Enterprise package.
1828
+ */
1829
+ serverSource?: PivotServerSource;
1344
1830
  locale?: string;
1345
1831
  workerUrl?: string | URL;
1346
1832
  /**
@@ -1366,6 +1852,33 @@ interface PivotControllerOptions<TRow = any> extends PivotControllerCallbacks<TR
1366
1852
  * with nothing to set.
1367
1853
  */
1368
1854
  enterpriseEdition?: boolean;
1855
+ /**
1856
+ * What identifies a row for `applyTransaction`: a field name, or a function
1857
+ * returning a string or number — the DataGrid's `rowKey`. Without it rows
1858
+ * are matched by object identity, so an update must pass the very object
1859
+ * that is in `data` (mutated in place) and a remove cannot name a bare key.
1860
+ */
1861
+ rowKey?: PivotRowKeyResolver<TRow>;
1862
+ }
1863
+ /**
1864
+ * Starting widths, in pixels, for every column of a kind — the renderer's
1865
+ * `defaultColumnWidths` option.
1866
+ *
1867
+ * Configuration, not state: it is not persisted, and a width in
1868
+ * `columnWidths` (a user's drag, or `setColumnWidths`) wins over it. That is
1869
+ * also why it is a separate option rather than seeded `columnWidths` entries:
1870
+ * the number of data columns is only known once the data is pivoted, and a
1871
+ * default written into the persisted map would outlive a later change of the
1872
+ * default in the app's code.
1873
+ */
1874
+ interface PivotColumnWidthDefaults {
1875
+ /**
1876
+ * Every row-header column: one per row field in the tabular layout, the
1877
+ * single column in the tree layout. Default 150.
1878
+ */
1879
+ rowHeader?: number;
1880
+ /** Every data column. Default 95. */
1881
+ data?: number;
1369
1882
  }
1370
1883
  /** Imperative API handle exposed via React ref or Angular adapter. */
1371
1884
  interface PivotGridRef {
@@ -1389,6 +1902,94 @@ interface PivotGridRef {
1389
1902
  getCell: (rowPath: string[], colPath: string[]) => PivotCell | null;
1390
1903
  }
1391
1904
  declare const defaultPivotLayout: PivotLayoutState;
1905
+ /** One field as a server-side pivot request names it. */
1906
+ interface PivotServerField {
1907
+ fieldId: string;
1908
+ /** The source column, when the field reads one (calculated fields do not). */
1909
+ dataField?: string;
1910
+ dataType: PivotField['dataType'];
1911
+ groupInterval?: PivotField['groupInterval'];
1912
+ }
1913
+ /**
1914
+ * What a server-side pivot asks for: the layout and the filters, and which
1915
+ * groups are open — so the server can answer only the levels on screen.
1916
+ */
1917
+ interface PivotServerRequest {
1918
+ /** Row fields, outermost first. */
1919
+ rows: PivotServerField[];
1920
+ /** Column fields, outermost first. */
1921
+ columns: PivotServerField[];
1922
+ /** Measures, in the order the cells carry them. */
1923
+ values: Array<PivotServerField & {
1924
+ summaryType: PivotSummaryType;
1925
+ }>;
1926
+ /** Value filters (header filter, filter area): keep or drop the listed values. */
1927
+ filters: Array<PivotServerField & {
1928
+ type: 'include' | 'exclude';
1929
+ values: unknown[];
1930
+ }>;
1931
+ prefilter: PivotPrefilter | null;
1932
+ /** Open row groups by path, or `'all'`. A closed group's children are not wanted. */
1933
+ expandedRows: string[][] | 'all';
1934
+ expandedColumns: string[][] | 'all';
1935
+ }
1936
+ /** What the grid hands its server source on each compute. */
1937
+ interface PivotServerInput<TRow = any> {
1938
+ gridId: string;
1939
+ fields: PivotField<TRow>[];
1940
+ prefilter: PivotPrefilter | null;
1941
+ /** Open row / column group keys (`'A|B'`); `'*'` for all. */
1942
+ expandedRows: Set<string>;
1943
+ expandedColumns: Set<string>;
1944
+ }
1945
+ /**
1946
+ * The grid side of a server-side pivot; `pivotServerSource` (Enterprise)
1947
+ * builds one. Kept to two calls so the Community bundle carries only the
1948
+ * scheduling — the request, the answer's translation and the field values
1949
+ * are the source's.
1950
+ */
1951
+ interface PivotServerSource {
1952
+ /** The pivot for the grid's current state, as the engine would produce it. */
1953
+ compute(input: PivotServerInput): Promise<PivotEngineResult>;
1954
+ /** Forget cached answers (an explicit refresh: the server's data changed). */
1955
+ reset?(): void;
1956
+ }
1957
+
1958
+ interface PivotChartState {
1959
+ /** Bumped when the shape changes, so an old snapshot is refused rather than misread. */
1960
+ version: 1;
1961
+ /** Whether the chart panel is showing. */
1962
+ open: boolean;
1963
+ /** Chart type id (`'column'`, `'line'`, `'heatmap'`…). */
1964
+ type: string;
1965
+ stacking: ChartStacking;
1966
+ /** Title, axis titles, legend position, data labels and value scale. */
1967
+ format: ChartFormat;
1968
+ /** Series switched off in the legend, by series key (a chart series' `dataKey`). */
1969
+ hiddenSeries: string[];
1970
+ }
1971
+ /** Which part of the state a `chartChanged` event moved. */
1972
+ type PivotChartChange = 'type' | 'stacking' | 'format' | 'hiddenSeries';
1973
+ interface PivotChartOpenedEvent {
1974
+ state: PivotChartState;
1975
+ }
1976
+ interface PivotChartChangedEvent {
1977
+ state: PivotChartState;
1978
+ changes: PivotChartChange[];
1979
+ }
1980
+ interface PivotChartClosedEvent {
1981
+ state: PivotChartState;
1982
+ }
1983
+ /**
1984
+ * Read a saved chart state defensively — it came from storage, a report or an
1985
+ * older build. Fields of the wrong kind fall back to their defaults; `null`
1986
+ * only for something that is not a chart state of this version at all. The
1987
+ * type is not checked against a list: which types exist is the renderer's
1988
+ * business, and an unknown one draws the panel's empty state, not an error.
1989
+ */
1990
+ declare function readPivotChartState(input: unknown): PivotChartState | null;
1991
+ /** Which parts differ between two states (the `changes` of `chartChanged`). */
1992
+ declare function diffPivotChartState(previous: PivotChartState, next: PivotChartState): PivotChartChange[];
1392
1993
 
1393
1994
  type PivotStateListener = () => void;
1394
1995
  /**
@@ -1450,6 +2051,13 @@ interface PivotControllerEvents<TRow> {
1450
2051
  activeReportId: string | null;
1451
2052
  };
1452
2053
  exporting: ExportingEvent;
2054
+ /**
2055
+ * The Enterprise chart panel opened, changed or closed — each with the
2056
+ * panel's whole `PivotChartState`. A Community grid never fires them.
2057
+ */
2058
+ chartOpened: PivotChartOpenedEvent;
2059
+ chartChanged: PivotChartChangedEvent;
2060
+ chartClosed: PivotChartClosedEvent;
1453
2061
  }
1454
2062
  interface PivotImportLabels {
1455
2063
  initializing: string;
@@ -1474,6 +2082,10 @@ interface PivotImportLabels {
1474
2082
  declare class PivotController<TRow> {
1475
2083
  private store;
1476
2084
  private events;
2085
+ /** Saved-report parts owned above the core, and loaded ones still waiting for their owner. */
2086
+ private readonly reportParts;
2087
+ private pendingReportParts;
2088
+ private readonly chartReporter;
1477
2089
  private options;
1478
2090
  private reportService;
1479
2091
  private engineService;
@@ -1482,13 +2094,54 @@ declare class PivotController<TRow> {
1482
2094
  /** Full row set of a parsed import awaiting preview commit (kept out of observable state). */
1483
2095
  private pendingImportRows;
1484
2096
  private disposed;
2097
+ /**
2098
+ * The array the application last handed in. After a transaction the
2099
+ * controller's rows are a NEW array; a binding that re-sends every option
2100
+ * (React's usePivotController does, on every render) would otherwise hand
2101
+ * back this one and undo every transaction. Same array: not a change.
2102
+ */
2103
+ private externalData;
1485
2104
  constructor(options: PivotControllerOptions<TRow>);
1486
2105
  subscribe: (listener: PivotStateListener) => (() => void);
1487
2106
  getSnapshot: () => PivotControllerState<TRow>;
1488
2107
  getState(): PivotControllerState<TRow>;
1489
2108
  on<K extends keyof PivotControllerEvents<TRow>>(eventName: K, handler: (payload: PivotControllerEvents<TRow>[K]) => void): () => void;
2109
+ /**
2110
+ * The chart panel's state, now. The controller turns it into `chartOpened` /
2111
+ * `chartChanged` / `chartClosed` — call it after every change, as often as
2112
+ * convenient: the same state twice is one event, and edits while the panel
2113
+ * is closed arrive with the next `chartOpened`.
2114
+ */
2115
+ reportChartState(state: PivotChartState): void;
2116
+ /**
2117
+ * Keep a piece of state owned outside the core inside saved reports — the
2118
+ * Enterprise chart panel keeps its chart this way. Order-independent: the
2119
+ * default report loads while the controller is constructed, BEFORE any
2120
+ * Enterprise layer has mounted, so a part loaded before its owner registers
2121
+ * is held and handed over on registration. Returns the unregister function.
2122
+ */
2123
+ registerReportStatePart(key: string, part: ReportStatePart): () => void;
2124
+ private collectReportParts;
1490
2125
  updateOptions(options: Partial<PivotControllerOptions<TRow>>): void;
1491
2126
  setFields(fields: PivotField<TRow>[]): void;
2127
+ /**
2128
+ * Replace every row — a full rebuild, transactions included. Unlike
2129
+ * `updateOptions({ data })`, passing the array the grid started from still
2130
+ * replaces: an explicit call means what it says.
2131
+ */
2132
+ setData(data: TRow[]): void;
2133
+ /**
2134
+ * Add, update and remove rows without rebuilding the pivot (live updates).
2135
+ *
2136
+ * Only the changed rows are encoded and sent to the worker, which regroups
2137
+ * them and re-sums the groups they touched; the result is the pivot a
2138
+ * rebuild from the new rows gives. Rows are matched by `rowKey` (by object
2139
+ * identity without one). An update for a key the grid does not have is
2140
+ * ignored; a remove wins over an update of the same row; adds go to the end
2141
+ * — the DataGrid's semantics. `getRawData()` returns the new rows at once;
2142
+ * the recomputed pivot follows (listen for `engineCompleted`).
2143
+ */
2144
+ applyTransaction(transaction: PivotRowTransaction<TRow>): PivotTransactionResult;
1492
2145
  updateField(updatedField: PivotField<TRow>): void;
1493
2146
  deleteField(fieldId: string): void;
1494
2147
  changeFieldArea(fieldId: string, area: PivotArea): void;
@@ -1660,20 +2313,67 @@ declare class PivotEngineService<TRow> {
1660
2313
  private disposed;
1661
2314
  private isInitComplete;
1662
2315
  private forceNextReencode;
2316
+ private engineLock;
2317
+ private busy;
2318
+ private pendingChanges;
2319
+ private drainQueued;
2320
+ private computing;
2321
+ private recomputeAfter;
1663
2322
  private hashes;
1664
2323
  constructor(config: PivotEngineServiceConfig<TRow>);
1665
2324
  refresh(): void;
2325
+ /**
2326
+ * A transaction changed the rows from `previous` to `change.data` (already
2327
+ * the controller's data). Applied incrementally when the engine holds
2328
+ * `previous`; otherwise the change is just new data and takes the normal path.
2329
+ */
2330
+ applyRowChange(change: EngineRowChange<TRow>, previous: TRow[]): void;
2331
+ /**
2332
+ * Run an engine phase alone. Inits, reconfigures and live updates all
2333
+ * encode against the rows the worker holds, and the first two are async
2334
+ * (encoding yields to the main thread): interleaved, one would hand the
2335
+ * worker columns of a length the other just changed, or a second init's
2336
+ * chunks would land inside the first one's build.
2337
+ */
2338
+ private exclusive;
2339
+ private scheduleDrain;
2340
+ /**
2341
+ * Recompute for a live update without ever pushing a picture back. A compute
2342
+ * already due includes the change (the worker takes messages in order), so
2343
+ * it is joined, not restarted; one in flight does not include it, so another
2344
+ * follows it — and the one in flight is still shown. Restarting the debounce
2345
+ * and bumping the version, as view changes do, would let a feed that ticks
2346
+ * faster than a compute keep the grid on its first picture indefinitely.
2347
+ */
2348
+ private computeAfterChange;
2349
+ /** Post one change; on refusal or failure, rebuild from the current data. */
2350
+ private pushRowChange;
2351
+ /** Send the queued changes, in order. False = one was refused and a rebuild is on its way. */
2352
+ private drainRowChanges;
1666
2353
  refreshData(): void;
1667
2354
  computeIfReady(): void;
1668
2355
  dispose(): void;
1669
2356
  private scheduleInit;
1670
2357
  private runInit;
2358
+ private initPhase;
1671
2359
  private scheduleReconfigure;
1672
2360
  private runReconfigure;
1673
2361
  private scheduleFilter;
1674
2362
  private runFilter;
1675
2363
  private scheduleComputeIfReady;
1676
2364
  private runCompute;
2365
+ private serverTimeout;
2366
+ /** What the last scheduled server compute was for; an identical one is skipped. */
2367
+ private serverKey;
2368
+ private serverInput;
2369
+ private scheduleServerCompute;
2370
+ /**
2371
+ * Everything after the aggregation, on the main thread: custom summaries,
2372
+ * calculated summary fields, sorting and top-N, display modes. One copy for
2373
+ * the three ways a result arrives (worker, fully-expanded snapshot, server).
2374
+ */
2375
+ private postPhases;
2376
+ private runServerCompute;
1677
2377
  /**
1678
2378
  * Produce a fully-expanded engine snapshot on demand, WITHOUT mutating the
1679
2379
  * grid's live state. Used by `expandAll` exports: the cached engineResult
@@ -1704,6 +2404,7 @@ declare class PivotEngineService<TRow> {
1704
2404
  private progressCapFor;
1705
2405
  private estimateProgressDuration;
1706
2406
  private handleEngineError;
2407
+ private unplacedFilteredFieldIds;
1707
2408
  private createStructuralFieldsHash;
1708
2409
  private createFieldsHash;
1709
2410
  private createFilterHash;
@@ -1755,6 +2456,8 @@ declare class PivotFieldService {
1755
2456
  static placeFields<TRow>(fields: PivotField<TRow>[], fieldIds: string[], pick: (field: PivotField<TRow>) => PivotArea): PivotField<TRow>[];
1756
2457
  static moveFieldByDirection<TRow>(fields: PivotField<TRow>[], fieldId: string, direction: 'start' | 'end' | 'left' | 'right'): PivotField<TRow>[];
1757
2458
  static moveFieldFromDrop<TRow>(fields: PivotField<TRow>[], command: FieldDropCommand): Promise<PivotField<TRow>[]>;
2459
+ /** The field list after the drop, or `fields` itself when the drop changes nothing. */
2460
+ private static applyDrop;
1758
2461
  static calculateDropIntent(pointerX: number, pointerY: number, itemRect: DropIntentRect, previousIntent: DropIntent, isLastItem: boolean, orientation?: 'horizontal' | 'vertical'): DropIntent;
1759
2462
  private static isArea;
1760
2463
  }
@@ -1786,6 +2489,28 @@ declare function cellHash(rId: number, cId: number): number;
1786
2489
  declare function cellHashRow(hash: number): number;
1787
2490
  declare function cellHashCol(hash: number): number;
1788
2491
 
2492
+ interface XYPairInput {
2493
+ name: string;
2494
+ fieldId?: string;
2495
+ seriesKey?: string;
2496
+ seriesLabel?: string;
2497
+ }
2498
+ interface XYPair {
2499
+ /** Legend caption: the column path, or the y measure when there is none. */
2500
+ label: string;
2501
+ /** Indexes into the input series. */
2502
+ x: number;
2503
+ y: number;
2504
+ size: number | null;
2505
+ }
2506
+ /**
2507
+ * Group series by column path (in first-seen order) and pair each group's
2508
+ * measures. A group with fewer measures than the type needs is left out —
2509
+ * there is nothing to plot it against. An empty result means the pivot does
2510
+ * not have enough measures at all; the caller says so.
2511
+ */
2512
+ declare function pairSeriesForXY(series: readonly XYPairInput[], sized: boolean): XYPair[];
2513
+
1789
2514
  declare class PivotCellAggregator<TRow> {
1790
2515
  private accumulators;
1791
2516
  private dataFields;
@@ -2242,4 +2967,186 @@ declare function coerceRecords(records: Record<string, unknown>[], onProgress?:
2242
2967
  */
2243
2968
  declare function parseCsvToRows(text: string, onProgress?: (p: CsvParseProgress) => void): Record<string, unknown>[];
2244
2969
 
2245
- export { type AdvancedExportRequest, type AdvancedExporter, type CellClickEvent, type CellPreparedEvent, type CellType, type ColHeaderCell, type ColumnSlot, type ColumnSlotOptions, type ConditionalVisual, type CsvParseProgress, CsvReader, type CustomSummaryOptions, DataNormalizer, type DateFormatContext, DateFormatter, type DateGranularity, type DimensionFieldInfo, type DropIntent, type ExportFormat, type ExportOptions, ExportStreamController, type ExportingEvent, FastTrie, type FieldDistinctEntry, type FieldMoveEvent, type FilterCondition, type FilterGroup, type FilterOperator, type FlatCellMatrix, FlatPivotEngine, type FlatRow, type IFileReader, type ImportFormat, ImportManager, type ImportManagerOptions, type ImportProgress, type ImportResult, type ImportSnapshot, ImportStreamController, type ImportWorkerKind, type ImportWorkerResult, MAX_C, type ParseFileOptions, type PivotArea, type PivotCell, PivotCellAggregator, type PivotCellTemplate, type PivotCellTextAccessor, type PivotCellValueAccessor, type PivotConditionRule, PivotController, type PivotControllerCallbacks, type PivotControllerEvents, type PivotControllerOptions, type PivotControllerState, type PivotDataSource, type PivotDictionary, type PivotDragIntentState, PivotEngineRegistry, type PivotEngineResult, PivotEngineService, type PivotEngineServiceConfig, PivotEventEmitter, type PivotEventHandler, type PivotEventMap, PivotExportService, type PivotField, PivotFieldService, type PivotGridRef, type PivotGroupInterval, type PivotImportField, type PivotImportLabels, type PivotImportPreviewState, type PivotImportStatus, type PivotLayoutProfile, type PivotLayoutState, type PivotLocale, type PivotNode, type PivotPlugin, type PivotPrefilter, type PivotReport, PivotReportManager, PivotReportService, type PivotReportState, type PivotSelectionMode, PivotSelectionModel, type PivotSelectionRange, type PivotSelectionState, type PivotSelectionStats, type PivotStateListener, PivotStateStore, type PivotStateStoringConfig, type PivotSummaryDisplayMode, type PivotSummaryType, type PivotTheme, type PivotToolbarConfig, type PivotValue, type PrefilterConditionSpec, type PrefilterGroupSpec, type ProcessFileStreamingOptions, type ProcessFromUrlOptions, ROW_HEADER_MAX_PINNED_FRACTION, ROW_HEADER_MIN_COL_WIDTH, ROW_HEADER_MIN_DATA_STRIP, type RowHeaderLayout, type ValidationResult, type ValueRange, type WorkerComputeRequest, type WorkerFilterRequest, type WorkerFilterSpec, type WorkerInitRequest, type WorkerResponse, XmlReader, ar, buildCellLookup, buildColPathsList, buildColumnHeaders, buildPivotAOA, buildRowLabelCells, cellHash, cellHashCol, cellHashRow, classifyValue, clearFormatCache, coerceRecords, coerceValue, compileSafeFormula, computeFieldRanges, computePivotMatrixAsync, computeRowHeaderWidths, conditionRuleToExcelStyle, cssColorToRgb, de, dedupeHeaders, defaultPivotLayout, destroyEngineWorker, detectDelimiter, en, es, escapeXmlText, executePostAggregationPhase, executeSummaryDisplayModesPhase, exportPivotData, extractGroupValue, fieldNeedsRange, filterEmptySummaryTree, filterPivotEngineAsync, filterRecords, filterRecordsAsync, flattenExportRows, flattenRowTree, flattenRowTreeTabular, flattenRowTreeTree, formatCellValue, formatCellValueCached, formatDimensionValue, formatHeaderValue, fr, getAdvancedExporter, getCellVal, getFieldDistinctValues, getVisibleColLeaves, getVisibleColSlots, getVisibleLeafCount, getVisibleMaxDepth, inferColumnType, initPivotEngineAsync, intlFormatToExcelNumFmt, invertRowHeaderWidth, it, ja, ko, parseBoolean, parseColor, parseCsvToRows, parseDelimited, parseFlexibleDate, parseLocaleNumber, pt, rangeRatio, reconfigurePivotEngineAsync, registerAdvancedExporter, resolveColPath, resolveConditionalVisual, ru, safeKey, sanitizeRows, scaleColor, setActiveEngineGrid, sortPivotTree, stripBom, testSafeFormula, tr, zhCN };
2970
+ /** The parts of the pivot a model may read and change. */
2971
+ type PivotAiFeature = 'fields' | 'filter' | 'sort' | 'totals';
2972
+ declare const PIVOT_AI_FEATURES: readonly PivotAiFeature[];
2973
+ interface PivotAiFieldOptions {
2974
+ /** Plain-language meaning: "Net revenue in EUR". */
2975
+ description?: string;
2976
+ /**
2977
+ * List the field's distinct values in the schema so "only Izmir" can be
2978
+ * matched to what is stored. Off by default: the values travel to whatever
2979
+ * model the application uses. An answer's filter values are checked against
2980
+ * the data either way, locally.
2981
+ */
2982
+ includeValues?: boolean;
2983
+ /** Leave the field out entirely; it keeps its place and the model never sees it. */
2984
+ exclude?: boolean;
2985
+ }
2986
+ interface PivotAiOptions {
2987
+ /** Parts the model may not change (and is not told about). */
2988
+ exclude?: readonly PivotAiFeature[];
2989
+ /** Per-field descriptions, value lists and exclusions, by field id. */
2990
+ fields?: Readonly<Record<string, PivotAiFieldOptions>>;
2991
+ /** Most values listed per field with `includeValues`. Default 100. */
2992
+ maxValues?: number;
2993
+ }
2994
+ interface PivotAiValue {
2995
+ fieldId: string;
2996
+ summaryType: PivotSummaryType;
2997
+ displayMode: PivotSummaryDisplayMode;
2998
+ }
2999
+ interface PivotAiFilter {
3000
+ fieldId: string;
3001
+ type: 'include' | 'exclude';
3002
+ values: Array<string | number | boolean>;
3003
+ }
3004
+ interface PivotAiSort {
3005
+ fieldId: string;
3006
+ direction: 'asc' | 'desc';
3007
+ /** Order by this measure's grand total instead of by the field's own values. */
3008
+ byValueField: string | null;
3009
+ /** Keep only the first N after sorting (needs `byValueField`). */
3010
+ top: number | null;
3011
+ }
3012
+ interface PivotAiTotals {
3013
+ grandTotalRow: boolean;
3014
+ grandTotalColumn: boolean;
3015
+ subtotalRows: boolean;
3016
+ subtotalColumns: boolean;
3017
+ rowHeaderLayout: 'tabular' | 'tree';
3018
+ }
3019
+ /** The pivot's layout in the schema's shape; a part left out is left as it is. */
3020
+ interface PivotAiState {
3021
+ /** Row dimensions, outermost first. */
3022
+ rows?: string[];
3023
+ /** Column dimensions, outermost first. */
3024
+ columns?: string[];
3025
+ /** Measures in the cells. */
3026
+ values?: PivotAiValue[];
3027
+ /** Every value filter; [] clears them. */
3028
+ filters?: PivotAiFilter[];
3029
+ /** Every dimension sort; a dimension not listed keeps its data order. */
3030
+ sort?: PivotAiSort[];
3031
+ totals?: PivotAiTotals;
3032
+ }
3033
+ interface PivotAiApplyResult {
3034
+ applied: PivotAiFeature[];
3035
+ dropped: string[];
3036
+ }
3037
+ /**
3038
+ * The JSON Schema of this pivot's layout, as narrow as its fields allow.
3039
+ * Send it to your own model as the structured-output format together with
3040
+ * `getPivotAiState()` and the user's request; pass the answer to
3041
+ * `applyPivotAiState`. No model ships with the grid and nothing is sent.
3042
+ */
3043
+ declare function getPivotAiSchema<TRow>(controller: PivotController<TRow>, options?: PivotAiOptions): Record<string, unknown>;
3044
+ /** The current layout in the schema's shape — what the model should start from. */
3045
+ declare function getPivotAiState<TRow>(controller: PivotController<TRow>, options?: PivotAiOptions): PivotAiState;
3046
+ /**
3047
+ * Apply a model's answer, object or JSON text. What is valid is applied in one
3048
+ * field commit (plus one layout update when totals changed); what is not is
3049
+ * listed in `dropped`. Never throws on a bad answer. Pass the options the
3050
+ * schema was built with.
3051
+ */
3052
+ declare function applyPivotAiState<TRow>(controller: PivotController<TRow>, answer: unknown, options?: PivotAiOptions): PivotAiApplyResult;
3053
+
3054
+ /**
3055
+ * The column paths a row's trend runs over: the columns ON SCREEN, left to
3056
+ * right, totals left out. A collapsed column group is one step (its subtotal
3057
+ * is what the row shows there); an expanded one is its children.
3058
+ */
3059
+ declare function trendColumnPaths(colTree: readonly PivotNode[]): string[][];
3060
+ /** One measure's values for a row across those columns; NaN where there is no cell. */
3061
+ declare function pivotRowTrend(getCell: (rowPath: string[], colPath: string[]) => PivotCell | null, colPaths: readonly string[][], rowPath: string[], fieldId: string): number[];
3062
+ interface TrendShape {
3063
+ /** `points` for an SVG polyline over the present values, in order. */
3064
+ line: string;
3065
+ /** One bar per value from the zero line (or the floor); NaN values have none. */
3066
+ bars: Array<{
3067
+ x: number;
3068
+ y: number;
3069
+ width: number;
3070
+ height: number;
3071
+ negative: boolean;
3072
+ }>;
3073
+ /** The last present value — the dot the eye goes to. */
3074
+ last: {
3075
+ x: number;
3076
+ y: number;
3077
+ } | null;
3078
+ }
3079
+ /**
3080
+ * Lay the values out in a `width` × `height` box. The scale is the row's own
3081
+ * (a sparkline shows SHAPE, not size — the total beside it gives the size);
3082
+ * a flat row sits in the middle. Fewer than two values draw no line.
3083
+ */
3084
+ declare function trendShape(values: readonly number[], width: number, height: number, pad?: number): TrendShape;
3085
+ /** Options for the row sparkline, shared by both Enterprise renderers. */
3086
+ interface PivotRowSparkline {
3087
+ /** The measure to draw, by field id. Default: the first measure. */
3088
+ field?: string;
3089
+ /** `'line'` (default) or `'bar'`. */
3090
+ type?: 'line' | 'bar';
3091
+ /** Drawing size in px. Default 60 × 16. */
3092
+ width?: number;
3093
+ height?: number;
3094
+ }
3095
+
3096
+ /** A header value as it travels: what the engine would have grouped by. */
3097
+ type PivotServerValue = string | number | boolean | null;
3098
+ /** One cell of the answer: a row path, a column path, the measures there. */
3099
+ interface PivotServerCell {
3100
+ /** Row values from the outermost field in; `[]` is the grand-total row. */
3101
+ row: PivotServerValue[];
3102
+ /** Column values from the outermost field in; `[]` is the grand-total column. */
3103
+ col: PivotServerValue[];
3104
+ /** Each measure's value by field id; null or missing for "no value". */
3105
+ values: Record<string, number | null>;
3106
+ }
3107
+ interface PivotServerResponse {
3108
+ /**
3109
+ * Every (row path, column path) pair the request's open groups make
3110
+ * visible — each level's subtotals and the grand totals included. Cells
3111
+ * deeper than the open groups are ignored, so a server may send more than
3112
+ * was asked.
3113
+ */
3114
+ cells: PivotServerCell[];
3115
+ }
3116
+ interface PivotServerSourceOptions {
3117
+ /** Answer a request — usually one call to your API. */
3118
+ load: (request: PivotServerRequest) => Promise<PivotServerResponse>;
3119
+ /** Every value of a field, for the header filter's checklist. Optional. */
3120
+ loadFieldValues?: (field: PivotServerField) => Promise<unknown[]>;
3121
+ /**
3122
+ * Answers kept per distinct request, so closing a group and opening it
3123
+ * again, or switching totals, does not ask again. Default 16; 0 turns the
3124
+ * cache off.
3125
+ */
3126
+ cacheSize?: number;
3127
+ }
3128
+ /** The request for the grid's state: the layout, the filters, the open groups. */
3129
+ declare function serverRequestFor(input: PivotServerInput): PivotServerRequest;
3130
+ /** The grid side of a server-side pivot: pass it as `serverSource`. */
3131
+ declare function pivotServerSource(options: PivotServerSourceOptions): PivotServerSource & {
3132
+ clearCache(): void;
3133
+ };
3134
+ /**
3135
+ * The cells as a PivotEngineResult: the row and column trees (open groups
3136
+ * only), the index maps, and the flat cell matrix the renderers read.
3137
+ */
3138
+ declare function buildEngineResult(request: PivotServerRequest, response: PivotServerResponse): PivotEngineResult;
3139
+ /**
3140
+ * Answer a request from rows held in memory — the specification a real server
3141
+ * implements, and what tests and demos run against. For each open row group
3142
+ * and open column group (the grand totals included), one cell per pair, the
3143
+ * filters and the prefilter applied first.
3144
+ *
3145
+ * `fields` resolves the fields a prefilter names that the request does not
3146
+ * carry (it only names the fields it lays out and filters by).
3147
+ */
3148
+ declare function rollupInMemory<TRow>(rows: readonly TRow[], request: PivotServerRequest, fields?: readonly PivotField<TRow>[]): PivotServerResponse;
3149
+ /** Every value of a field in rows held in memory — the reference `loadFieldValues`. */
3150
+ declare function fieldValuesInMemory<TRow>(rows: readonly TRow[], field: PivotServerField): unknown[];
3151
+
3152
+ export { type AdvancedExportRequest, type AdvancedExporter, CHART_LEGEND_POSITIONS, type CellClickEvent, type CellPreparedEvent, type CellType, type ChartFormat, type ChartImageRequest, type ChartLegendPosition, type ChartMark, type ChartStacking, type ColHeaderCell, type ColumnSlot, type ColumnSlotOptions, type ConditionalVisual, type CsvParseProgress, CsvReader, type CustomSummaryOptions, DEFAULT_DATA_COLUMN_WIDTH, DEFAULT_ROW_HEADER_WIDTH, DataNormalizer, type DateFormatContext, DateFormatter, type DateGranularity, type DimensionFieldInfo, type DropIntent, type ExportFormat, type ExportOptions, ExportStreamController, type ExportingEvent, FastTrie, type FieldDistinctEntry, type FieldMoveEvent, type FilterCondition, type FilterGroup, type FilterOperator, type FlatCellMatrix, FlatPivotEngine, type FlatRow, type FunnelStage, type IFileReader, type ImportFormat, ImportManager, type ImportManagerOptions, type ImportProgress, type ImportResult, type ImportSnapshot, ImportStreamController, type ImportWorkerKind, type ImportWorkerResult, MAX_C, type MarkTone, PIVOT_AI_FEATURES, type ParseFileOptions, type PivotAiApplyResult, type PivotAiFeature, type PivotAiFieldOptions, type PivotAiFilter, type PivotAiOptions, type PivotAiSort, type PivotAiState, type PivotAiTotals, type PivotAiValue, type PivotArea, type PivotCell, PivotCellAggregator, type PivotCellTemplate, type PivotCellTextAccessor, type PivotCellValueAccessor, type PivotChartChange, type PivotChartChangedEvent, type PivotChartClosedEvent, type PivotChartOpenedEvent, type PivotChartState, type PivotColumnWidthDefaults, type PivotConditionRule, PivotController, type PivotControllerCallbacks, type PivotControllerEvents, type PivotControllerOptions, type PivotControllerState, type PivotDataSource, type PivotDictionary, type PivotDragIntentState, PivotEngineRegistry, type PivotEngineResult, PivotEngineService, type PivotEngineServiceConfig, PivotEventEmitter, type PivotEventHandler, type PivotEventMap, PivotExportService, type PivotField, PivotFieldService, type PivotGridRef, type PivotGroupInterval, type PivotImportField, type PivotImportLabels, type PivotImportPreviewState, type PivotImportStatus, type PivotLayoutProfile, type PivotLayoutState, type PivotLocale, type PivotNode, type PivotPlugin, type PivotPrefilter, type PivotReport, PivotReportManager, PivotReportService, type PivotReportState, type PivotRowKey, type PivotRowKeyResolver, type PivotRowSparkline, type PivotRowTransaction, type PivotSelectionMode, PivotSelectionModel, type PivotSelectionRange, type PivotSelectionState, type PivotSelectionStats, type PivotServerCell, type PivotServerField, type PivotServerInput, type PivotServerRequest, type PivotServerResponse, type PivotServerSource, type PivotServerSourceOptions, type PivotServerValue, type PivotStateListener, PivotStateStore, type PivotStateStoringConfig, type PivotSummaryDisplayMode, type PivotSummaryType, type PivotTheme, type PivotToolbarConfig, type PivotTransactionResult, type PivotValue, type PrefilterConditionSpec, type PrefilterGroupSpec, type ProcessFileStreamingOptions, type ProcessFromUrlOptions, ROW_HEADER_MAX_PINNED_FRACTION, ROW_HEADER_MIN_COL_WIDTH, ROW_HEADER_MIN_DATA_STRIP, type ReportStatePart, type RowHeaderLayout, SHAPE_CHART_TYPES, type ShapeChartInput, type ShapeChartType, type StackedSegment, type TreemapCell, type TrendShape, type ValidationResult, type ValueRange, type WaterfallStep, type WorkerComputeRequest, type WorkerFilterRequest, type WorkerFilterSpec, type WorkerInitRequest, type WorkerResponse, type WorkerTransactionRequest, type XYPair, type XYPairInput, XmlReader, applyPivotAiState, ar, buildCellLookup, buildColPathsList, buildColumnHeaders, buildEngineResult, buildPivotAOA, buildRowLabelCells, cellHash, cellHashCol, cellHashRow, chartPalette, classifyValue, clearFormatCache, coerceRecords, coerceValue, compileSafeFormula, computeFieldRanges, computePivotMatrixAsync, computeRowHeaderWidths, conditionRuleToExcelStyle, cssColorToRgb, de, dedupeHeaders, defaultPivotLayout, destroyEngineWorker, detectDelimiter, diffPivotChartState, en, es, escapeXmlText, executePostAggregationPhase, executeSummaryDisplayModesPhase, exportPivotData, extractGroupValue, fieldNeedsRange, fieldValuesInMemory, filterEmptySummaryTree, filterPivotEngineAsync, filterRecords, filterRecordsAsync, flattenExportRows, flattenRowTree, flattenRowTreeTabular, flattenRowTreeTree, formatCellValue, formatCellValueCached, formatDimensionValue, formatHeaderValue, fr, funnelMarks, funnelStages, getAdvancedExporter, getCellVal, getFieldDistinctValues, getPivotAiSchema, getPivotAiState, getVisibleColLeaves, getVisibleColSlots, getVisibleLeafCount, getVisibleMaxDepth, heatColor, heatTextColor, heatmapMarks, inferColumnType, initPivotEngineAsync, intlFormatToExcelNumFmt, invertRowHeaderWidth, it, ja, ko, logAxisTicks, pairSeriesForXY, parseBoolean, parseColor, parseCsvToRows, parseDelimited, parseFlexibleDate, parseLocaleNumber, percentAxisTicks, pivotRowTrend, pivotServerSource, pt, rangeBarMarks, rangeRatio, readChartFormat, readPivotChartState, reconfigurePivotEngineAsync, registerAdvancedExporter, resolveColPath, resolveColumnWidthDefaults, resolveConditionalVisual, rollupInMemory, ru, safeKey, sanitizeRows, scaleColor, serverRequestFor, setActiveEngineGrid, sortPivotTree, squarify, stackSeries, stripBom, sunburstMarks, testSafeFormula, tr, treemapMarks, trendColumnPaths, trendShape, waterfallDomain, waterfallMarks, waterfallSteps, zhCN };