@kanunilabs/pivotgrid-core 1.1.0 → 1.3.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
@@ -43,6 +43,29 @@ interface PivotDictionary {
43
43
  rawDataFormats: string;
44
44
  customExports: string;
45
45
  customPlugin: string;
46
+ saveReport: string;
47
+ saveReportDescription: string;
48
+ reportName: string;
49
+ reportNamePlaceholder: string;
50
+ setAsDefault: string;
51
+ save: string;
52
+ enterFullscreen: string;
53
+ exitFullscreen: string;
54
+ print: string;
55
+ sendToPrinter: string;
56
+ copyToClipboard: string;
57
+ tabSeparatedValues: string;
58
+ enterpriseBadge: string;
59
+ enterpriseUpgradeHint: string;
60
+ dataImportUnsupported: string;
61
+ columnFilter: string;
62
+ percentOfColumnTotal: string;
63
+ percentOfRowTotal: string;
64
+ percentDifference: string;
65
+ filterField: string;
66
+ removeFromArea: string;
67
+ removeField: string;
68
+ deletePermanently: string;
46
69
  importSuccess: string;
47
70
  importWarning: string;
48
71
  importError: string;
@@ -105,6 +128,67 @@ interface PivotDictionary {
105
128
  moveToEnd: string;
106
129
  moveToLeft: string;
107
130
  moveToRight: string;
131
+ /** Field chip a11y: caption, area, 1-based index, count. */
132
+ fieldChipLabel: string;
133
+ /** Field chip a11y: the key bindings, read as a description. */
134
+ fieldChipKeys: string;
135
+ /** Context-menu submenu that moves a field to another area. */
136
+ moveToArea: string;
137
+ /** Prefilter operator labels (#210) — shared vocabulary with DataGrid. */
138
+ opEquals: string;
139
+ opNotEquals: string;
140
+ opGreater: string;
141
+ opGreaterOrEqual: string;
142
+ opLess: string;
143
+ opLessOrEqual: string;
144
+ opContains: string;
145
+ opNotContains: string;
146
+ opStartsWith: string;
147
+ opEndsWith: string;
148
+ opIsBlank: string;
149
+ opIsNotBlank: string;
150
+ /** Prefilter builder chrome (#210) — shared with DataGrid. */
151
+ builderCombine: string;
152
+ builderAddCondition: string;
153
+ builderAddGroup: string;
154
+ builderRemoveGroup: string;
155
+ builderRemoveCondition: string;
156
+ builderEmpty: string;
157
+ builderValuePlaceholder: string;
158
+ /** Drill-down modal (#212); the pager/CSV five are shared with DataGrid. */
159
+ exportCsv: string;
160
+ pagerPage: string;
161
+ pagerOf: string;
162
+ pagerPrevious: string;
163
+ pagerNext: string;
164
+ drillDown: string;
165
+ /** `Label: {count}` — no plural engine, so no locale is ever ungrammatical. */
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
+ /** PDF export header (#215). */
184
+ exportGeneratedOn: string;
185
+ /** Designer level tiles and live formula verdict (#220). */
186
+ calcRowLevelHint: string;
187
+ calcSummaryLevelHint: string;
188
+ formulaValid: string;
189
+ calcFieldsHint: string;
190
+ /** `Uses: {fields}` — the captions the formula references. */
191
+ formulaUses: string;
108
192
  showFieldList: string;
109
193
  editCalculation: string;
110
194
  [key: string]: string;
@@ -547,7 +631,12 @@ declare function initPivotEngineAsync<TRow>(data: TRow[], rowFields: PivotField<
547
631
  * worker context, or the dataset changed) — the caller must fall back to a
548
632
  * full {@link initPivotEngineAsync}.
549
633
  */
550
- declare function reconfigurePivotEngineAsync<TRow>(data: TRow[], rowFields: PivotField<TRow>[], colFields: PivotField<TRow>[], dataFields: PivotField<TRow>[], registry?: PivotEngineRegistry): Promise<boolean>;
634
+ declare function reconfigurePivotEngineAsync<TRow>(data: TRow[], rowFields: PivotField<TRow>[], colFields: PivotField<TRow>[], dataFields: PivotField<TRow>[], registry?: PivotEngineRegistry,
635
+ /** All fields + the active prefilter, so its columns are encoded too. */
636
+ prefilterState?: {
637
+ fields: PivotField<TRow>[];
638
+ prefilter?: PivotPrefilter;
639
+ }): Promise<boolean>;
551
640
  /**
552
641
  * Send filter specs to the worker. Converts field.filterValues to
553
642
  * dictionary-encoded integer IDs so the worker can do fast integer
@@ -555,7 +644,16 @@ declare function reconfigurePivotEngineAsync<TRow>(data: TRow[], rowFields: Pivo
555
644
  */
556
645
  declare function filterPivotEngineAsync(fields: PivotField<any>[], prefilter?: PivotPrefilter): Promise<void>;
557
646
  declare function computePivotMatrixAsync(expandedPaths: string[], expandedColPaths: string[]): Promise<PivotEngineResult>;
558
- declare function getFieldDistinctValues(fieldId: string): FieldDistinctEntry[] | null;
647
+ /**
648
+ * Cached distinct values for a header filter.
649
+ *
650
+ * `gridId` is optional for backwards compatibility, but callers that know
651
+ * their own id MUST pass it: without it this resolves through the
652
+ * module-global `activeGridId` — whichever grid last ran an engine phase — so
653
+ * on a page with two grids sharing a field id, one grid's filter popup listed
654
+ * the OTHER grid's values (sweep #193).
655
+ */
656
+ declare function getFieldDistinctValues(fieldId: string, gridId?: string): FieldDistinctEntry[] | null;
559
657
 
560
658
  interface PivotReportState {
561
659
  fields: PivotField<any>[];
@@ -585,10 +683,111 @@ interface PivotReport {
585
683
  * intersection (colId). Nodes missing from the index maps are kept as-is
586
684
  * (they cannot be safely evaluated).
587
685
  */
686
+ /**
687
+ * How wide the frozen row-header block may get before it starves the data.
688
+ *
689
+ * Pinning every row-header column is fine until the block approaches the
690
+ * viewport, and then it collapses completely: the sticky headers cover the
691
+ * whole visible area and the crosstab is painted UNDERNEATH them, unreachable
692
+ * by scrolling because sticky boxes do not move (journal #201).
693
+ *
694
+ * The first attempt un-pinned the deep columns. That traded one broken rule
695
+ * for another — the row headers scrolled away with the data — and was
696
+ * rejected. The lever is WIDTH: every column stays frozen, and the block is
697
+ * compressed until the crosstab has somewhere to live.
698
+ *
699
+ * Both renderers call this. They must: the vanilla painter derives its sticky
700
+ * insets from these numbers and React sizes its left pane from them, and two
701
+ * copies of the arithmetic would be two copies to keep in step.
702
+ */
703
+ declare const ROW_HEADER_MIN_DATA_STRIP = 160;
704
+ declare const ROW_HEADER_MAX_PINNED_FRACTION = 0.6;
705
+ /** However tight it gets, a row-header column stays wide enough to read. */
706
+ declare const ROW_HEADER_MIN_COL_WIDTH = 56;
707
+ interface RowHeaderLayout {
708
+ /** Widths as PAINTED, one per row-header column. */
709
+ widths: number[];
710
+ /** Their sum — the width of the whole frozen block. */
711
+ total: number;
712
+ }
713
+ /**
714
+ * Row-header widths as painted: the configured widths, scaled down when the
715
+ * block would not leave room for the data.
716
+ *
717
+ * `viewport <= 0` means "not measured yet" — before the first layout, and in
718
+ * a hidden pane — and compresses nothing. Compressing on a guess would shrink
719
+ * every grid that has not been measured.
720
+ */
721
+ declare function computeRowHeaderWidths(configured: number[], viewport: number): RowHeaderLayout;
722
+ /**
723
+ * The configured width that, once the budget has compressed the block, paints
724
+ * column `index` at `targetPainted` — "what you drag is what you get".
725
+ *
726
+ * A resize grip lives on the PAINTED edge. Committing painted+delta as the
727
+ * CONFIGURED width is wrong under compression, and wrong in the worst way:
728
+ * eight columns configured 150 (painted 90) in a 1198px viewport, drag column
729
+ * 0 to 100, commit 100 → the configured total shrinks, the scale falls, and
730
+ * column 0 is painted 63. The user asked for wider and got narrower.
731
+ *
732
+ * The dragged column's painted width is c·budget/(c + rest), rounded, then
733
+ * floored — the scale comes from the CONFIGURED total, and the floor is
734
+ * applied per column afterwards, so the OTHER columns' floors never touch
735
+ * this one. That makes the inverse a closed form, c = t·rest/(budget − t),
736
+ * with a one-pixel correction for the rounding.
737
+ *
738
+ * What is stored matters beyond this viewport: the map persists through
739
+ * stateStoring and is painted again in wider containers. So the target is
740
+ * clamped to what a column can actually be painted at here — never below
741
+ * the floor, never past the point where the block no longer fits — and the
742
+ * returned width is the moderate one that paints there. (A first version
743
+ * bisected and returned the SMALLEST width in the floor band, i.e. 2px, and
744
+ * a ten-figure number for an unreachable target; both painted fine today
745
+ * and absurdly tomorrow.)
746
+ *
747
+ * Without compression this is the identity, so callers can use it always.
748
+ */
749
+ declare function invertRowHeaderWidth(configured: number[], index: number, targetPainted: number, viewport: number): number;
588
750
  declare function filterEmptySummaryTree(engineResult: PivotEngineResult): [PivotNode[], PivotNode[]];
589
- declare function getVisibleLeafCount(node: PivotNode, expandedColPaths: Set<string>, allExpanded?: boolean): number;
751
+ /**
752
+ * Column-axis subtotals: one extra leaf per EXPANDED node with children,
753
+ * holding that node's own aggregate.
754
+ *
755
+ * No second aggregation pass is needed and none is done. The engine already
756
+ * materialises a cell for every VISIBLE node, parents included — its
757
+ * ancestor walk keeps the parent in the chain when the parent is expanded
758
+ * (FlatPivotEngine.collectActiveAncestors), so `Electro` carries 420 while
759
+ * `Electro|Phone` carries 400 and `Electro|TV` carries 20. The column
760
+ * subtotal is therefore a LAYOUT concern: emit a leaf slot addressing the
761
+ * parent's own hash, in the right place.
762
+ *
763
+ * The three column functions below must agree cell for cell — the header
764
+ * builder, the leaf list the renderers index cells with, and the span count.
765
+ * A disagreement shifts every data cell under the wrong header, so they share
766
+ * this one options shape and a test asserts they produce the same count.
767
+ */
768
+ interface ColumnSlotOptions {
769
+ /** `layout.showColumnTotals` — per-level subtotals on the column axis. */
770
+ subTotals?: boolean;
771
+ /** Before or after the children, matching `layout.totalsPosition`. */
772
+ totalsPosition?: 'top' | 'bottom';
773
+ }
774
+ declare function getVisibleLeafCount(node: PivotNode, expandedColPaths: Set<string>, allExpanded?: boolean, opts?: ColumnSlotOptions): number;
590
775
  declare function getVisibleMaxDepth(nodes: PivotNode[], expandedColPaths: Set<string>, allExpanded?: boolean): number;
591
- declare function getVisibleColLeaves(nodes: PivotNode[], expandedColPaths: Set<string>, allExpanded?: boolean): PivotNode[];
776
+ /** One painted data column: the node whose hash addresses it, and why. */
777
+ interface ColumnSlot {
778
+ node: PivotNode;
779
+ /** True for a subtotal slot — the node's OWN aggregate beside its children. */
780
+ isSubtotal: boolean;
781
+ }
782
+ /**
783
+ * The visible data columns, in paint order.
784
+ *
785
+ * This is the list renderers index cells with, and it must line up one-for-one
786
+ * with the leaf cells `buildColumnHeaders` emits.
787
+ */
788
+ declare function getVisibleColSlots(nodes: PivotNode[], expandedColPaths: Set<string>, allExpanded?: boolean, opts?: ColumnSlotOptions): ColumnSlot[];
789
+ /** The visible data columns as bare nodes — `getVisibleColSlots` without the flags. */
790
+ declare function getVisibleColLeaves(nodes: PivotNode[], expandedColPaths: Set<string>, allExpanded?: boolean, opts?: ColumnSlotOptions): PivotNode[];
592
791
  interface ColHeaderCell {
593
792
  node: PivotNode | null;
594
793
  caption: string;
@@ -599,8 +798,26 @@ interface ColHeaderCell {
599
798
  colPath?: string;
600
799
  hasChildren?: boolean;
601
800
  isExpanded?: boolean;
801
+ /** A column-axis subtotal cell: the node's OWN aggregate beside its children. */
802
+ isSubtotal?: boolean;
803
+ /**
804
+ * Index of the first data column this cell sits above, in the FULL visible
805
+ * leaf order (already multiplied by the data-field count when measures are
806
+ * on the column axis).
807
+ *
808
+ * Renderers used to derive this by accumulating `colSpan` per header row,
809
+ * which is only correct when every row starts at leaf 0. A collapsed node
810
+ * at depth 0 appears in row 0 alone (it carries a rowSpan instead), so the
811
+ * next row holds only the expanded siblings' children — and their
812
+ * accumulator restarted at 0, addressing the collapsed sibling's column.
813
+ * The producer knows the true order, so it now states it (#194).
814
+ */
815
+ leafStart: number;
602
816
  }
603
- declare function buildColumnHeaders(colTree: PivotNode[], dataFields: PivotField[], layout: PivotLayoutState, expandedColPaths: Set<string>, allExpanded?: boolean): ColHeaderCell[][];
817
+ declare function buildColumnHeaders(colTree: PivotNode[], dataFields: PivotField[], layout: PivotLayoutState, expandedColPaths: Set<string>, allExpanded?: boolean, labels?: {
818
+ grandTotal?: string;
819
+ subTotal?: string;
820
+ }): ColHeaderCell[][];
604
821
  /**
605
822
  * A flattened row for rendering. Each row represents either:
606
823
  * - A leaf data row (values at each depth level column)
@@ -649,6 +866,52 @@ declare function flattenRowTreeTabular(nodes: PivotNode[], expandedPaths: Set<st
649
866
  */
650
867
  declare function flattenRowTreeTree(nodes: PivotNode[], expandedPaths: Set<string>, allExpanded: boolean, treeDepth: number, showRowTotals: boolean, totalsPosition: 'top' | 'bottom'): FlatRow[];
651
868
 
869
+ interface ValueRange {
870
+ min: number;
871
+ max: number;
872
+ }
873
+ /** What a cell should look like once its rules have been applied. */
874
+ interface ConditionalVisual {
875
+ /** Style properties to merge into the cell — the historical `style` object. */
876
+ style: Record<string, string | number>;
877
+ /**
878
+ * A data bar: how far to fill (0..1), in what colour, and which way. The
879
+ * renderer draws it; core will not pick a gradient syntax for everyone.
880
+ */
881
+ dataBar?: {
882
+ ratio: number;
883
+ color: string;
884
+ negative: boolean;
885
+ };
886
+ /** The icon a renderer should draw before the value, by name. */
887
+ icon?: string;
888
+ }
889
+ declare function fieldNeedsRange(field: PivotField): boolean;
890
+ /**
891
+ * The min and max of each measure across the CURRENT matrix.
892
+ *
893
+ * TOTALS ARE EXCLUDED. A grand total is an order of magnitude above the cells
894
+ * beside it, and letting it set the top of the scale flattens every real value
895
+ * into the first colour stop — the scale would technically be correct and
896
+ * visually useless. A cell is a total when either index is 0 (the root row or
897
+ * root column), which is how the matrix already addresses them.
898
+ */
899
+ declare function computeFieldRanges(engineResult: PivotEngineResult, dataFields: PivotField[]): Record<string, ValueRange>;
900
+ /** #rgb / #rrggbb / rgb(...) → [r,g,b], or null when it cannot be parsed. */
901
+ declare function parseColor(color: string | undefined): [number, number, number] | null;
902
+ /** Position of `value` inside the range, 0..1. A flat range is all zero. */
903
+ declare function rangeRatio(value: number, range: ValueRange | undefined): number;
904
+ /** Blend across two or three stops, the way a spreadsheet colour scale does. */
905
+ declare function scaleColor(ratio: number, stops: string[]): string | null;
906
+ /**
907
+ * Apply every rule on a field to one cell.
908
+ *
909
+ * `expression` keeps its exact historical behaviour, evaluation errors and
910
+ * all: a throwing formula is skipped rather than breaking the paint, which is
911
+ * what both renderers already did.
912
+ */
913
+ declare function resolveConditionalVisual(value: number | undefined, field: PivotField, cell: PivotCell | null, range?: ValueRange): ConditionalVisual;
914
+
652
915
  type ExportFormat = 'excel-list' | 'excel-pivot' | 'pdf' | 'json' | 'json-pivot' | 'json-snapshot' | 'xml' | 'csv' | 'print' | 'clipboard';
653
916
  /** Options passed to the export pipeline — all fields optional for backward compat. */
654
917
  interface ExportOptions {
@@ -704,7 +967,9 @@ declare function flattenExportRows(engineResult: PivotEngineResult, rowFields: P
704
967
  * single indented label (subtotals keep a "Total" marker so downstream
705
968
  * heuristics still recognize them); tabular mode returns one cell per depth.
706
969
  */
707
- declare function buildRowLabelCells(flatRow: FlatRow, rowFields: PivotField[], treeMode: boolean, rowLabelCols: number, locale: string): any[];
970
+ declare function buildRowLabelCells(flatRow: FlatRow, rowFields: PivotField[], treeMode: boolean, rowLabelCols: number, locale: string, labels?: {
971
+ subTotal?: string;
972
+ }): any[];
708
973
  /** Find the data field for a given column path entry. */
709
974
  declare function resolveColPath(cPathInfo: string[], dataFields: PivotField[]): {
710
975
  cHash: string;
@@ -717,7 +982,10 @@ declare function resolveColPath(cPathInfo: string[], dataFields: PivotField[]):
717
982
  * event loop every {@link AOA_CHUNK} rows, honors an AbortSignal, and reports
718
983
  * incremental progress. Returns `null` when the export was aborted mid-build.
719
984
  */
720
- declare function buildPivotAOA(engineResult: PivotEngineResult, rowFields: PivotField[], dataFields: PivotField[], layout: PivotLayoutState, expandedPaths: Set<string>, expandedColPaths: Set<string>, allExpanded?: boolean, locale?: string, applyFormatting?: boolean, colFields?: PivotField[], signal?: AbortSignal, onProgress?: (percent: number) => void): Promise<{
985
+ declare function buildPivotAOA(engineResult: PivotEngineResult, rowFields: PivotField[], dataFields: PivotField[], layout: PivotLayoutState, expandedPaths: Set<string>, expandedColPaths: Set<string>, allExpanded?: boolean, locale?: string, applyFormatting?: boolean, colFields?: PivotField[], signal?: AbortSignal, onProgress?: (percent: number) => void, labels?: {
986
+ grandTotal?: string;
987
+ subTotal?: string;
988
+ }): Promise<{
721
989
  aoa: any[][];
722
990
  styles: (ExportCellStyle | null)[][] | null;
723
991
  } | null>;
@@ -852,14 +1120,35 @@ interface PivotField<TRow = any, TCellTemplate = PivotCellTemplate, TStyle = any
852
1120
  }
853
1121
  /** Controls pivot grid layout, totals visibility, and structural options. */
854
1122
  interface PivotLayoutState {
1123
+ /** The grand total COLUMN, at the end of every row. */
855
1124
  showColumnGrandTotals?: boolean;
1125
+ /** The grand total ROW, at the end of the grid. */
856
1126
  showRowGrandTotals?: boolean;
1127
+ /**
1128
+ * @deprecated NOT IMPLEMENTED. Subtotals on the column axis do not exist:
1129
+ * row subtotals are inserted while flattening the row tree, and the column
1130
+ * axis has no equivalent pass in the renderer. Setting this to `true`
1131
+ * explicitly reports through `onError`; it is left in the type because it
1132
+ * has always had a default of `true`, so removing it would be a breaking
1133
+ * change for a value that never did anything.
1134
+ */
857
1135
  showColumnTotals?: boolean;
1136
+ /** Per-level subtotals on the row axis. */
858
1137
  showRowTotals?: boolean;
859
1138
  /** Whether subtotals appear above or below their groups. */
860
1139
  totalsPosition?: 'top' | 'bottom';
1140
+ /**
1141
+ * Coarse form of `showRowGrandTotals` + `showColumnGrandTotals`. Bridged to
1142
+ * them at the controller boundary; an explicitly passed granular flag wins.
1143
+ */
861
1144
  showGrandTotals?: 'rows' | 'columns' | 'both' | 'none';
1145
+ /**
1146
+ * Coarse form of `showRowTotals`. Bridged for the ROW axis only — asking for
1147
+ * `'columns'` or `'both'` reports through `onError`, because column subtotals
1148
+ * are not implemented. See `showColumnTotals`.
1149
+ */
862
1150
  showSubTotals?: 'rows' | 'columns' | 'both' | 'none';
1151
+ /** Alias of `totalsPosition`, bridged at the controller boundary. */
863
1152
  totalsDisplayMode?: 'top' | 'bottom';
864
1153
  /** Row header style: 'tabular' gives one column per row field, 'tree' nests all into one column. */
865
1154
  rowHeaderLayout?: 'tabular' | 'tree';
@@ -1046,10 +1335,37 @@ interface PivotControllerOptions<TRow = any> extends PivotControllerCallbacks<TR
1046
1335
  * step instead of applying immediately. Defaults to false (apply directly).
1047
1336
  */
1048
1337
  previewImport?: boolean;
1338
+ /**
1339
+ * Where the grid's layout is persisted between sessions.
1340
+ *
1341
+ * Omitted, it is localStorage keyed by `gridId` — the historical behaviour,
1342
+ * kept so an upgrade does not lose anyone's saved layout.
1343
+ */
1344
+ stateStoring?: PivotStateStoringConfig;
1345
+ /**
1346
+ * Turns on the Enterprise edition for this controller.
1347
+ *
1348
+ * `BasePivotGrid` sets it from the `advancedExport` prop, which is what
1349
+ * `EnterprisePivotGrid` passes — so a normal React app never touches this.
1350
+ * It exists because `usePivotController`, `PivotGridBody` and `PivotFooter`
1351
+ * are all public: building your own shell is supported, and without a
1352
+ * documented way in, that path would go from "exports a file" to "throws"
1353
+ * with nothing to set.
1354
+ */
1355
+ enterpriseEdition?: boolean;
1049
1356
  }
1050
1357
  /** Imperative API handle exposed via React ref or Angular adapter. */
1051
1358
  interface PivotGridRef {
1052
- exportData: (format: ExportFormat | string, options?: ExportOptions) => void;
1359
+ /**
1360
+ * Returns a promise so a caller can handle a refusal.
1361
+ *
1362
+ * It used to be `void`. That was harmless while an unavailable format was a
1363
+ * `console.warn` and a resolved promise; the controller now REJECTS for the
1364
+ * three Enterprise formats, and a `void` return meant the caller could not
1365
+ * catch it even if they wanted to. Widening `void` to `Promise<void>` is
1366
+ * source-compatible — existing callers ignore the return either way.
1367
+ */
1368
+ exportData: (format: ExportFormat | string, options?: ExportOptions) => Promise<void>;
1053
1369
  expandAll: () => void;
1054
1370
  collapseAll: () => void;
1055
1371
  getState: () => PivotControllerState;
@@ -1062,13 +1378,37 @@ interface PivotGridRef {
1062
1378
  declare const defaultPivotLayout: PivotLayoutState;
1063
1379
 
1064
1380
  type PivotStateListener = () => void;
1065
- /** Immutable-snapshot state store with debounced localStorage persistence. */
1381
+ /**
1382
+ * Where the store reads and writes.
1383
+ *
1384
+ * It used to be `localStorage`, unconditionally. `PivotStateStoringConfig`
1385
+ * declared `enabled`, `type`, `onSave` and `onLoad`, and NONE of them were
1386
+ * read — only `storageKey`, and only as a `gridId` fallback. So
1387
+ * `enabled: false` did not disable anything, `type: 'sessionStorage'` still
1388
+ * wrote to localStorage, and the `type: 'custom'` + `onSave`/`onLoad` pair
1389
+ * documented on the features page as "persist to your own backend" was a hook
1390
+ * that nothing ever called.
1391
+ */
1392
+ /** What actually goes in and out of storage. */
1393
+ interface PersistedPivotState {
1394
+ fields?: unknown[];
1395
+ layout?: Partial<PivotLayoutState>;
1396
+ columnWidths?: Record<string, number>;
1397
+ expandedPaths?: string[];
1398
+ expandedColPaths?: string[];
1399
+ }
1400
+ interface PivotStatePersistence {
1401
+ load(): PersistedPivotState | null;
1402
+ save(state: PersistedPivotState): void;
1403
+ }
1404
+ /** Immutable-snapshot state store with debounced persistence. */
1066
1405
  declare class PivotStateStore<TRow> {
1067
1406
  private snapshot;
1068
1407
  private listeners;
1069
1408
  private storageKey;
1070
1409
  private persistScheduled;
1071
- constructor(gridId: string, initialFields: PivotField<TRow>[]);
1410
+ private persistence;
1411
+ constructor(gridId: string, initialFields: PivotField<TRow>[], persistence?: PivotStatePersistence);
1072
1412
  getSnapshot: () => PivotControllerState<TRow>;
1073
1413
  subscribe: (listener: PivotStateListener) => (() => void);
1074
1414
  setState(updates: Partial<PivotControllerState<TRow>>): void;
@@ -1139,6 +1479,14 @@ declare class PivotController<TRow> {
1139
1479
  updateField(updatedField: PivotField<TRow>): void;
1140
1480
  deleteField(fieldId: string): void;
1141
1481
  changeFieldArea(fieldId: string, area: PivotArea): void;
1482
+ /**
1483
+ * Take several fields out of the chooser in ONE commit.
1484
+ *
1485
+ * The routing rule is the field chooser's checkbox rule — a number is a
1486
+ * measure, anything else describes a row — and it lives here so the vanilla
1487
+ * chooser, the React chooser and the Angular one cannot drift on it.
1488
+ */
1489
+ showFields(fieldIds: string[]): void;
1142
1490
  removeField(fieldId: string): void;
1143
1491
  moveField(fieldId: string, direction: 'start' | 'end' | 'left' | 'right'): void;
1144
1492
  sortField(fieldId: string, direction: 'asc' | 'desc' | undefined): void;
@@ -1147,6 +1495,14 @@ declare class PivotController<TRow> {
1147
1495
  applyFilter(fieldId: string, selectedValues: Set<any>, filterType: 'include' | 'exclude'): void;
1148
1496
  clearFilter(fieldId: string): void;
1149
1497
  setLayout(updates: Partial<PivotLayoutState>): void;
1498
+ private readonly unsupportedReporter;
1499
+ /**
1500
+ * Translate a whole layout arriving from outside — the `layout` prop, or a
1501
+ * controlled update — and report what could not be honoured.
1502
+ *
1503
+ * Returns `undefined` for `undefined` so callers can keep their `??` chain.
1504
+ */
1505
+ private normalizeIncomingLayout;
1150
1506
  toggleExpanded(path: string): void;
1151
1507
  expandPaths(paths: string[]): void;
1152
1508
  collapsePaths(paths: string[]): void;
@@ -1207,6 +1563,43 @@ declare class PivotController<TRow> {
1207
1563
  deleteReport(id: string): void;
1208
1564
  setDefaultReport(id: string): void;
1209
1565
  refreshEngine(): void;
1566
+ /**
1567
+ * Which edition is driving this controller. Set by the React adapter from the
1568
+ * flag `EnterprisePivotGrid` passes; Community never turns it on.
1569
+ *
1570
+ * The engine keeps the capability either way — the pipeline is one piece and
1571
+ * an edition boundary that tried to cut through it would be a fork, not a
1572
+ * gate. What the flag decides is what the controller AGREES to do.
1573
+ *
1574
+ * This mirrors `DataGridController.setEnterpriseEdition` deliberately: the two
1575
+ * products should not have two different answers to the same question.
1576
+ */
1577
+ setEnterpriseEdition(on: boolean): void;
1578
+ isEnterpriseEdition(): boolean;
1579
+ private enterpriseEdition;
1580
+ /** Seeded from the options so a headless caller has a documented way in. */
1581
+ private applyEditionOption;
1582
+ /**
1583
+ * Refuse an Enterprise-only call in a clear voice.
1584
+ *
1585
+ * Throws rather than resolving empty: an export that silently returns nothing
1586
+ * looks like a bug in the caller's data, and they would go looking there
1587
+ * first. That is not hypothetical here — until this existed, a Community
1588
+ * grid asking for `excel-pivot` got a `console.warn` and a resolved promise,
1589
+ * AFTER `onExporting` had already fired, so an app showing a spinner on that
1590
+ * event span forever.
1591
+ */
1592
+ private requireEnterprise;
1593
+ /**
1594
+ * The formats that need the Enterprise package.
1595
+ *
1596
+ * The exporter itself is registered globally by a side-effect import, so
1597
+ * before this gate existed, one Enterprise import anywhere in a bundle
1598
+ * enabled styled export for EVERY grid in it — including a Community
1599
+ * `BasePivotGrid`. The registry is a module-level `let` and cannot tell who
1600
+ * is asking; the controller can.
1601
+ */
1602
+ private static readonly ENTERPRISE_FORMATS;
1210
1603
  exportData(format: ExportFormat | string, options?: ExportOptions): Promise<void>;
1211
1604
  getRawData(): TRow[];
1212
1605
  getEngineResult(): PivotEngineResult | null;
@@ -1280,6 +1673,15 @@ declare class PivotEngineService<TRow> {
1280
1673
  * user expanded all nodes manually.
1281
1674
  */
1282
1675
  computeFullyExpandedSnapshot(): Promise<PivotEngineResult>;
1676
+ /**
1677
+ * Surface options the sort accepted but could not honour.
1678
+ *
1679
+ * Routed to `onError` rather than thrown: the pivot it just built is correct
1680
+ * and useful, so failing the whole compute would be worse than the problem.
1681
+ * See utils/unsupported.ts for why it is not a console warning.
1682
+ */
1683
+ private readonly unsupportedReporter;
1684
+ private reportUnsupported;
1283
1685
  private startBuildProgress;
1284
1686
  private tickBuildProgress;
1285
1687
  private advanceBuildProgress;
@@ -1320,6 +1722,24 @@ declare class PivotFieldService {
1320
1722
  static removeField<TRow>(fields: PivotField<TRow>[], fieldId: string): PivotField<TRow>[];
1321
1723
  static deleteField<TRow>(fields: PivotField<TRow>[], fieldId: string): PivotField<TRow>[];
1322
1724
  static changeFieldArea<TRow>(fields: PivotField<TRow>[], fieldId: string, area: PivotArea): PivotField<TRow>[];
1725
+ /**
1726
+ * Move several fields into areas in ONE commit.
1727
+ *
1728
+ * `changeFieldArea` in a loop is the obvious way to write "show all these
1729
+ * fields", and it is the wrong one: every call commits, which re-runs the
1730
+ * engine. On the million-row demo that is one full rebuild per field.
1731
+ *
1732
+ * The areaIndex bookkeeping is the reason this cannot just be a `map`.
1733
+ * `changeFieldArea` derives its index from CURRENT occupancy
1734
+ * (`fields.filter(c => c.area === area).length`); doing that per field
1735
+ * inside one pass would hand every arrival the same number, and
1736
+ * `normalizeAreaIndexes` sorts on that key — so an all-equal key would drop
1737
+ * the batch into whatever order `sort` happened to produce. Instead each
1738
+ * area's counter is seeded from occupancy once and then advanced, so the
1739
+ * newcomers land after the fields already there, in the order the CALLER
1740
+ * listed them (which is the order the user was looking at).
1741
+ */
1742
+ static placeFields<TRow>(fields: PivotField<TRow>[], fieldIds: string[], pick: (field: PivotField<TRow>) => PivotArea): PivotField<TRow>[];
1323
1743
  static moveFieldByDirection<TRow>(fields: PivotField<TRow>[], fieldId: string, direction: 'start' | 'end' | 'left' | 'right'): PivotField<TRow>[];
1324
1744
  static moveFieldFromDrop<TRow>(fields: PivotField<TRow>[], command: FieldDropCommand): Promise<PivotField<TRow>[]>;
1325
1745
  static calculateDropIntent(pointerX: number, pointerY: number, itemRect: DropIntentRect, previousIntent: DropIntent, isLastItem: boolean, orientation?: 'horizontal' | 'vertical'): DropIntent;
@@ -1394,7 +1814,23 @@ declare function testSafeFormula(formulaString: string, fieldMap: Map<string, st
1394
1814
  declare function filterRecords<TRow>(records: TRow[], fields: PivotField<TRow>[], prefilter?: PivotPrefilter): TRow[];
1395
1815
  declare function filterRecordsAsync<TRow>(records: TRow[], fields: PivotField<TRow>[], prefilter?: PivotPrefilter): Promise<TRow[]>;
1396
1816
 
1397
- declare function sortPivotTree(nodes: PivotNode[], fields: PivotField<any>[], engineResult: PivotEngineResult, dataFields: PivotField<any>[], isRowTree: boolean, locale?: string, cellMapCache?: Map<number, number>): void;
1817
+ /**
1818
+ * Anything the sort accepted but could not honour, so the caller can surface it
1819
+ * instead of the option being swallowed.
1820
+ *
1821
+ * `topNShowOthers` is the reason this exists. It used to report that the whole
1822
+ * feature was missing; since #200 the Others node is built, and what is
1823
+ * reported is narrower and exact: `avg` and `distinct` cannot be combined from
1824
+ * already-aggregated cells (one needs the row counts behind each value, the
1825
+ * other the values themselves), so those measures are left empty rather than
1826
+ * filled with a plausible wrong number. `sum`, `count`, `min` and `max` are
1827
+ * exact — the cut nodes are still fully aggregated in the matrix, so no second
1828
+ * pass over the source data is involved.
1829
+ */
1830
+ interface SortOutcome {
1831
+ unsupported: string[];
1832
+ }
1833
+ declare function sortPivotTree(nodes: PivotNode[], fields: PivotField<any>[], engineResult: PivotEngineResult, dataFields: PivotField<any>[], isRowTree: boolean, locale?: string, cellMapCache?: Map<number, number>, outcome?: SortOutcome): void;
1398
1834
 
1399
1835
  declare function executePostAggregationPhase<TRow>(engineResult: PivotEngineResult, dataFields: PivotField<TRow>[], registry?: PivotEngineRegistry): void;
1400
1836
  declare function executeSummaryDisplayModesPhase<TRow>(engineResult: PivotEngineResult, dataFields: PivotField<TRow>[]): void;
@@ -1787,4 +2223,4 @@ declare function coerceRecords(records: Record<string, unknown>[], onProgress?:
1787
2223
  */
1788
2224
  declare function parseCsvToRows(text: string, onProgress?: (p: CsvParseProgress) => void): Record<string, unknown>[];
1789
2225
 
1790
- export { type AdvancedExportRequest, type AdvancedExporter, type CellClickEvent, type CellPreparedEvent, type CellType, type CsvParseProgress, CsvReader, type CustomSummaryOptions, DataNormalizer, type DateFormatContext, DateFormatter, type DateGranularity, 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, type ValidationResult, 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, computePivotMatrixAsync, conditionRuleToExcelStyle, cssColorToRgb, de, dedupeHeaders, defaultPivotLayout, destroyEngineWorker, detectDelimiter, en, es, escapeXmlText, executePostAggregationPhase, executeSummaryDisplayModesPhase, exportPivotData, extractGroupValue, filterEmptySummaryTree, filterPivotEngineAsync, filterRecords, filterRecordsAsync, flattenExportRows, flattenRowTree, flattenRowTreeTabular, flattenRowTreeTree, formatCellValue, formatCellValueCached, formatHeaderValue, fr, getAdvancedExporter, getCellVal, getFieldDistinctValues, getVisibleColLeaves, getVisibleLeafCount, getVisibleMaxDepth, inferColumnType, initPivotEngineAsync, intlFormatToExcelNumFmt, it, ja, ko, parseBoolean, parseCsvToRows, parseDelimited, parseFlexibleDate, parseLocaleNumber, pt, reconfigurePivotEngineAsync, registerAdvancedExporter, resolveColPath, ru, safeKey, sanitizeRows, setActiveEngineGrid, sortPivotTree, stripBom, testSafeFormula, tr, zhCN };
2226
+ 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 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, 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 };