@canvas-components/pivot-table 0.2.1 → 0.2.3

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.cjs CHANGED
@@ -330,8 +330,6 @@ function indexPivotNodes(root) {
330
330
  visit(root);
331
331
  return nodeMap;
332
332
  }
333
-
334
- // src/domain/aggregation/built-in-aggregators.ts
335
333
  function createBuiltInPivotAggregators() {
336
334
  return {
337
335
  sum: createNumericAggregator("sum"),
@@ -362,14 +360,14 @@ function createNumericAggregator(type) {
362
360
  return;
363
361
  }
364
362
  state.numericCount += 1;
365
- state.sum += numericValue;
363
+ state.sum = utils.addPreciseNumbers(state.sum, numericValue) ?? state.sum;
366
364
  state.min = state.min == null ? numericValue : Math.min(state.min, numericValue);
367
365
  state.max = state.max == null ? numericValue : Math.max(state.max, numericValue);
368
366
  },
369
367
  merge(state, source) {
370
368
  state.count += source.count;
371
369
  state.numericCount += source.numericCount;
372
- state.sum += source.sum;
370
+ state.sum = utils.addPreciseNumbers(state.sum, source.sum) ?? state.sum;
373
371
  state.min = mergeMinimum(state.min, source.min);
374
372
  state.max = mergeMaximum(state.max, source.max);
375
373
  source.distinctValues.forEach((value) => state.distinctValues.add(value));
@@ -377,9 +375,11 @@ function createNumericAggregator(type) {
377
375
  finalize(state) {
378
376
  if (type === "count") return state.count;
379
377
  if (type === "distinctCount") return state.distinctValues.size;
380
- if (type === "sum") return state.sum;
378
+ if (type === "sum") return Number(state.sum);
381
379
  if (type === "avg") {
382
- return state.numericCount === 0 ? null : state.sum / state.numericCount;
380
+ if (state.numericCount === 0) return null;
381
+ const average = utils.dividePreciseNumber(state.sum, state.numericCount);
382
+ return average == null ? null : Number(average);
383
383
  }
384
384
  if (type === "min") return state.min;
385
385
  return state.max;
@@ -390,7 +390,7 @@ function createAccumulator() {
390
390
  return {
391
391
  count: 0,
392
392
  numericCount: 0,
393
- sum: 0,
393
+ sum: "0",
394
394
  min: null,
395
395
  max: null,
396
396
  distinctValues: /* @__PURE__ */ new Set()
@@ -432,6 +432,101 @@ function resolvePivotIndicatorAggregator(indicator, registry) {
432
432
  }
433
433
  return aggregator;
434
434
  }
435
+ var DATE_PATTERNS = {
436
+ dateSlash: "YYYY/MM/DD",
437
+ dateDash: "YYYY-MM-DD",
438
+ dateFullCN: "YYYY\u5E74M\u6708D\u65E5",
439
+ time: "HH:mm:ss",
440
+ timeShort: "HH:mm",
441
+ datetime: "YYYY/MM/DD HH:mm:ss",
442
+ datetime12: "YYYY/MM/DD Ah:mm"
443
+ };
444
+ function formatPivotIndicatorValue(value, dataFormat) {
445
+ if (typeof dataFormat === "object") {
446
+ if (dataFormat.type === "custom" && dataFormat.pattern) {
447
+ return utils.formatCustomNumberPattern(value, dataFormat.pattern);
448
+ }
449
+ return formatPivotIndicatorValueWithOptions(value, dataFormat);
450
+ }
451
+ if (dataFormat === "default" || dataFormat === "text") {
452
+ return String(value ?? "");
453
+ }
454
+ if (dataFormat === "number") {
455
+ return utils.formatNumberValue(value, {
456
+ decimalPlaces: "auto",
457
+ thousandsSeparator: false
458
+ });
459
+ }
460
+ if (dataFormat === "thousands") {
461
+ return utils.formatNumberValue(value, {
462
+ decimalPlaces: "auto",
463
+ thousandsSeparator: true
464
+ });
465
+ }
466
+ if (dataFormat === "decimal") {
467
+ return utils.formatNumberValue(value, {
468
+ decimalPlaces: 2,
469
+ thousandsSeparator: true
470
+ });
471
+ }
472
+ if (dataFormat === "percent" || dataFormat === "percentDecimal") {
473
+ return utils.formatPercentValue(value, dataFormat === "percentDecimal" ? 2 : 0);
474
+ }
475
+ if (dataFormat === "scientific") return utils.formatScientificValue(value);
476
+ if (dataFormat === "numberToChineseUpper") {
477
+ return utils.formatChineseNumber(value, "financial") ?? String(value ?? "");
478
+ }
479
+ if (dataFormat === "chineseUpperToNumber") {
480
+ const parsed = utils.parseChineseNumber(value);
481
+ return parsed == null ? String(value ?? "") : String(parsed);
482
+ }
483
+ if (dataFormat === "cny" || dataFormat === "cnyDecimal") {
484
+ return utils.formatNumberValue(value, {
485
+ decimalPlaces: dataFormat === "cnyDecimal" ? 2 : 0,
486
+ thousandsSeparator: true,
487
+ symbol: "\xA5"
488
+ });
489
+ }
490
+ if (dataFormat === "usd" || dataFormat === "usdDecimal") {
491
+ return utils.formatNumberValue(value, {
492
+ decimalPlaces: dataFormat === "usdDecimal" ? 2 : 0,
493
+ thousandsSeparator: true,
494
+ symbol: "$"
495
+ });
496
+ }
497
+ const pattern = DATE_PATTERNS[dataFormat];
498
+ return pattern ? utils.formatDateTimeValue(value, pattern) ?? String(value ?? "") : String(value ?? "");
499
+ }
500
+ function formatPivotIndicatorValueWithOptions(value, dataFormat) {
501
+ if (dataFormat.pattern && dataFormat.type !== "custom") {
502
+ const formatted = utils.formatDateTimeValue(value, dataFormat.pattern);
503
+ if (formatted != null) return formatted;
504
+ }
505
+ const decimalPlaces = Math.max(
506
+ 0,
507
+ Math.min(20, dataFormat.decimalPlaces ?? 2)
508
+ );
509
+ if (dataFormat.type === "percent" || dataFormat.type === "percentDecimal") {
510
+ return utils.formatPercentValue(value, decimalPlaces);
511
+ }
512
+ if (dataFormat.type === "scientific") {
513
+ return utils.formatScientificValue(value, decimalPlaces);
514
+ }
515
+ if (dataFormat.type === "cny" || dataFormat.type === "cnyDecimal" || dataFormat.type === "usd" || dataFormat.type === "usdDecimal") {
516
+ return utils.formatNumberValue(value, {
517
+ decimalPlaces,
518
+ thousandsSeparator: dataFormat.useGrouping ?? true,
519
+ symbol: dataFormat.symbol ?? (dataFormat.type.startsWith("usd") ? "$" : "\xA5")
520
+ });
521
+ }
522
+ if (dataFormat.type === "number" || dataFormat.type === "thousands" || dataFormat.type === "decimal") {
523
+ return utils.formatNumberValue(value, {
524
+ decimalPlaces,
525
+ thousandsSeparator: dataFormat.useGrouping ?? dataFormat.type !== "number"
526
+ });
527
+ }
528
+ return formatPivotIndicatorValue(value, dataFormat.type);
529
+ }
435
530
 
436
531
  // src/domain/aggregation/aggregate-records.ts
437
532
  function aggregatePivotRecords(params) {
@@ -496,7 +591,7 @@ function aggregatePivotRecords(params) {
496
591
  recordCount: item.recordIndexes.length,
497
592
  rowPathKey: item.rowPathKey,
498
593
  columnPathKey: item.columnPathKey
499
- }) ?? formatAggregateValue(value, item.indicator.precision),
594
+ }) ?? (item.indicator.dataFormat ? formatPivotIndicatorValue(value, item.indicator.dataFormat) : formatAggregateValue(value, item.indicator.precision)),
500
595
  recordCount: item.recordIndexes.length,
501
596
  recordIndexes: item.recordIndexes
502
597
  });
@@ -1046,7 +1141,6 @@ function resolveThumbOffset(scroll, maxScroll, track, thumb) {
1046
1141
  // src/domain/layout/pivot-layout.ts
1047
1142
  var DEFAULT_ROW_HEIGHT = 40;
1048
1143
  var DEFAULT_COLUMN_WIDTH = 120;
1049
- var DEFAULT_HEADER_SIZE = 40;
1050
1144
  var DEFAULT_ROW_DIMENSION_WIDTH2 = 140;
1051
1145
  var SCROLLBAR_THICKNESS2 = 10;
1052
1146
  function buildPivotLayoutSnapshot(params) {
@@ -1078,7 +1172,7 @@ function buildPivotLayoutSnapshot(params) {
1078
1172
  options.columns.length + (indicatorLayout === "columns" && hasIndicators ? 1 : 0),
1079
1173
  1
1080
1174
  );
1081
- const columnHeaderHeight = columnHeaderLevels * DEFAULT_HEADER_SIZE;
1175
+ const columnHeaderHeight = columnHeaderLevels * rowHeight;
1082
1176
  const reportFilterHeight = (options.filterDimensions?.length ?? 0) * rowHeight;
1083
1177
  const tableHeaderHeight = reportFilterHeight + columnHeaderHeight;
1084
1178
  const rawBodyWidth = Math.max(width - rowHeaderWidth, 0);
@@ -1212,6 +1306,7 @@ function buildPivotLayoutSnapshot(params) {
1212
1306
  rowHeaderWidth,
1213
1307
  scrollLeft: scroll.left,
1214
1308
  levels: columnHeaderLevels,
1309
+ headerSize: rowHeight,
1215
1310
  headerTop: reportFilterHeight,
1216
1311
  expandedNodeIds: params.expandedColumnNodeIds
1217
1312
  });
@@ -1612,7 +1707,7 @@ function buildColumnHeaderCells(params) {
1612
1707
  x,
1613
1708
  y: params.headerTop,
1614
1709
  width: columnWidth,
1615
- height: DEFAULT_HEADER_SIZE * (params.levels - (slot.indicatorKey ? 1 : 0))
1710
+ height: params.headerSize * (params.levels - (slot.indicatorKey ? 1 : 0))
1616
1711
  }
1617
1712
  });
1618
1713
  } else {
@@ -1652,9 +1747,9 @@ function buildColumnHeaderCells(params) {
1652
1747
  ),
1653
1748
  rect: {
1654
1749
  x,
1655
- y: params.headerTop + level * DEFAULT_HEADER_SIZE,
1750
+ y: params.headerTop + level * params.headerSize,
1656
1751
  width: columnWidth,
1657
- height: DEFAULT_HEADER_SIZE
1752
+ height: params.headerSize
1658
1753
  }
1659
1754
  });
1660
1755
  });
@@ -1681,9 +1776,9 @@ function buildColumnHeaderCells(params) {
1681
1776
  sortDirection: null,
1682
1777
  rect: {
1683
1778
  x,
1684
- y: params.headerTop + (params.levels - 1) * DEFAULT_HEADER_SIZE,
1779
+ y: params.headerTop + (params.levels - 1) * params.headerSize,
1685
1780
  width: columnWidth,
1686
- height: DEFAULT_HEADER_SIZE
1781
+ height: params.headerSize
1687
1782
  }
1688
1783
  });
1689
1784
  }
@@ -2720,6 +2815,15 @@ function resolvePivotSelectionRect(layout, selection) {
2720
2815
  const visibleStartY = Math.max(startY, clipRect.y);
2721
2816
  const visibleEndY = Math.min(endY, clipRect.y + clipRect.height);
2722
2817
  if (visibleStartX >= visibleEndX || visibleStartY >= visibleEndY) return null;
2818
+ const singleCell = kind === "cell" && indexes.startRowIndex === indexes.endRowIndex && indexes.startColumnIndex === indexes.endColumnIndex;
2819
+ if (singleCell) {
2820
+ return {
2821
+ x: startX,
2822
+ y: startY,
2823
+ width: endX - startX,
2824
+ height: endY - startY
2825
+ };
2826
+ }
2723
2827
  return {
2724
2828
  x: visibleStartX,
2725
2829
  y: visibleStartY,
@@ -2769,18 +2873,50 @@ function resolvePivotSelectionKind(selection) {
2769
2873
  }
2770
2874
  return selection?.activeNode?.axis ?? "cell";
2771
2875
  }
2772
- function drawPivotSelectionBorder(context, rect, color) {
2876
+ function drawPivotSelectionBorder(context, rect, color, options) {
2773
2877
  context.save();
2774
2878
  context.strokeStyle = color;
2775
2879
  context.lineWidth = 1;
2776
- context.strokeRect(
2777
- rect.x + 0.5,
2778
- rect.y + 0.5,
2779
- Math.max(rect.width - 1, 0),
2780
- Math.max(rect.height - 1, 0)
2781
- );
2880
+ const left = rect.x + 0.5;
2881
+ const top = rect.y + 0.5;
2882
+ const width = Math.max(rect.width - 1, 0);
2883
+ const height = Math.max(rect.height - 1, 0);
2884
+ const right = left + width;
2885
+ const bottom = top + height;
2886
+ const viewport = options?.viewportRect;
2887
+ const radius = Math.min(options?.radius ?? 8, width / 2, height / 2);
2888
+ const topLeft = viewport && isSameCoordinate(rect.x, viewport.x) && isSameCoordinate(rect.y, viewport.y) ? radius : 0;
2889
+ const topRight = viewport && isSameCoordinate(rect.x + rect.width, viewport.x + viewport.width) && isSameCoordinate(rect.y, viewport.y) ? radius : 0;
2890
+ const bottomRight = viewport && isSameCoordinate(rect.x + rect.width, viewport.x + viewport.width) && isSameCoordinate(rect.y + rect.height, viewport.y + viewport.height) ? radius : 0;
2891
+ const bottomLeft = viewport && isSameCoordinate(rect.x, viewport.x) && isSameCoordinate(rect.y + rect.height, viewport.y + viewport.height) ? radius : 0;
2892
+ if (!topLeft && !topRight && !bottomRight && !bottomLeft) {
2893
+ context.strokeRect(left, top, width, height);
2894
+ context.restore();
2895
+ return;
2896
+ }
2897
+ context.beginPath();
2898
+ context.moveTo(left + topLeft, top);
2899
+ context.lineTo(right - topRight, top);
2900
+ if (topRight) context.arcTo(right, top, right, top + topRight, topRight);
2901
+ else context.lineTo(right, top);
2902
+ context.lineTo(right, bottom - bottomRight);
2903
+ if (bottomRight) {
2904
+ context.arcTo(right, bottom, right - bottomRight, bottom, bottomRight);
2905
+ } else context.lineTo(right, bottom);
2906
+ context.lineTo(left + bottomLeft, bottom);
2907
+ if (bottomLeft) {
2908
+ context.arcTo(left, bottom, left, bottom - bottomLeft, bottomLeft);
2909
+ } else context.lineTo(left, bottom);
2910
+ context.lineTo(left, top + topLeft);
2911
+ if (topLeft) context.arcTo(left, top, left + topLeft, top, topLeft);
2912
+ else context.lineTo(left, top);
2913
+ context.closePath();
2914
+ context.stroke();
2782
2915
  context.restore();
2783
2916
  }
2917
+ function isSameCoordinate(first, second) {
2918
+ return Math.abs(first - second) < 0.01;
2919
+ }
2784
2920
  function resolveSelectionIndexes(layout, selection) {
2785
2921
  const range = selection?.ranges[0];
2786
2922
  if (range) {
@@ -3077,6 +3213,17 @@ function drawPivotGridRect(context, rect, color, options) {
3077
3213
  context.stroke();
3078
3214
  context.restore();
3079
3215
  }
3216
+ function drawPivotViewportRightBorder(context, rect, color) {
3217
+ const x = rect.x + rect.width - 0.5;
3218
+ context.save();
3219
+ context.strokeStyle = color;
3220
+ context.lineWidth = 1;
3221
+ context.beginPath();
3222
+ context.moveTo(x, rect.y);
3223
+ context.lineTo(x, rect.y + rect.height);
3224
+ context.stroke();
3225
+ context.restore();
3226
+ }
3080
3227
  function clipPivotViewport(context, rect, radius = 8) {
3081
3228
  appendRoundedRectPath(context, rect, radius);
3082
3229
  context.clip();
@@ -3162,6 +3309,12 @@ function renderPivotTable(params) {
3162
3309
  );
3163
3310
  const areaSelection = isPivotAreaSelection(layout, params.selection);
3164
3311
  const viewportWidth = resolvePivotViewportWidth(layout);
3312
+ const tableViewportRect = {
3313
+ x: 0,
3314
+ y: 0,
3315
+ width: layout.tableWidth,
3316
+ height: layout.height
3317
+ };
3165
3318
  context.clearRect(0, 0, layout.width, layout.height);
3166
3319
  context.save();
3167
3320
  clipPivotViewport(context, {
@@ -3202,7 +3355,7 @@ function renderPivotTable(params) {
3202
3355
  (cell) => drawPivotGridRect(context, cell.rect, theme.borderColor, {
3203
3356
  skipTop: true,
3204
3357
  skipLeft: cell.rect.x > 0,
3205
- skipRight: isSameCoordinate(
3358
+ skipRight: isSameCoordinate2(
3206
3359
  cell.rect.x + cell.rect.width,
3207
3360
  layout.rowHeaderRect.x + layout.rowHeaderRect.width
3208
3361
  )
@@ -3223,13 +3376,21 @@ function renderPivotTable(params) {
3223
3376
  );
3224
3377
  layout.columnHeaderCells.forEach(
3225
3378
  (cell) => drawPivotGridRect(context, cell.rect, theme.borderColor, {
3226
- skipTop: cell.rect.y > 0,
3227
- skipLeft: true,
3228
- skipRight: isSameCoordinate(
3379
+ skipTop: hasColumnHeaderCellAbove(
3380
+ layout.columnHeaderCells,
3381
+ cell,
3382
+ layout.columnHeaderRect.y
3383
+ ),
3384
+ skipLeft: hasColumnHeaderCellLeft(
3385
+ layout.columnHeaderCells,
3386
+ cell,
3387
+ layout.columnHeaderRect.x
3388
+ ),
3389
+ skipRight: isSameCoordinate2(
3229
3390
  cell.rect.x + cell.rect.width,
3230
3391
  layout.tableWidth
3231
3392
  ),
3232
- skipBottom: isSameCoordinate(
3393
+ skipBottom: isSameCoordinate2(
3233
3394
  cell.rect.y + cell.rect.height,
3234
3395
  layout.columnHeaderRect.y + layout.columnHeaderRect.height
3235
3396
  )
@@ -3261,7 +3422,7 @@ function renderPivotTable(params) {
3261
3422
  (cell) => drawPivotGridRect(context, cell.rect, theme.borderColor, {
3262
3423
  skipTop: true,
3263
3424
  skipLeft: true,
3264
- skipRight: isSameCoordinate(
3425
+ skipRight: isSameCoordinate2(
3265
3426
  cell.rect.x + cell.rect.width,
3266
3427
  layout.tableWidth
3267
3428
  )
@@ -3290,34 +3451,72 @@ function renderPivotTable(params) {
3290
3451
  headerBackgroundColor: theme.columnHeaderBackgroundColor,
3291
3452
  bodyBackgroundColor: theme.backgroundColor
3292
3453
  });
3454
+ if (params.resizeGuideX != null) {
3455
+ drawPivotResizeGuide(context, params.resizeGuideX, contentHeight);
3456
+ }
3457
+ context.restore();
3458
+ if (!layout.scrollbar.vertical) {
3459
+ context.save();
3460
+ clipPivotViewport(context, tableViewportRect);
3461
+ drawPivotViewportRightBorder(context, tableViewportRect, theme.borderColor);
3462
+ context.restore();
3463
+ }
3464
+ drawPivotViewportBorder(
3465
+ context,
3466
+ { x: 0, y: 0, width: viewportWidth, height: contentHeight },
3467
+ theme.borderColor
3468
+ );
3293
3469
  const selectionRect = resolvePivotSelectionRect(layout, params.selection);
3294
3470
  if (selectionRect) {
3295
3471
  const selectionClipRect = resolvePivotSelectionClipRect(
3296
3472
  layout,
3297
3473
  params.selection
3298
3474
  );
3475
+ context.save();
3476
+ clipPivotViewport(context, tableViewportRect);
3299
3477
  withClip(context, selectionClipRect, () => {
3300
3478
  drawPivotSelectionBorder(
3301
3479
  context,
3302
3480
  selectionRect,
3303
- theme.selectionBorderColor
3481
+ theme.selectionBorderColor,
3482
+ { viewportRect: tableViewportRect }
3304
3483
  );
3305
3484
  });
3485
+ context.restore();
3306
3486
  }
3307
- if (params.resizeGuideX != null) {
3308
- drawPivotResizeGuide(context, params.resizeGuideX, contentHeight);
3309
- }
3310
- context.restore();
3311
- drawPivotViewportBorder(
3312
- context,
3313
- { x: 0, y: 0, width: viewportWidth, height: contentHeight },
3314
- theme.borderColor
3315
- );
3316
3487
  return {
3317
3488
  headerCellCount: layout.reportFilterCells.length + layout.rowFieldHeaderCells.length + layout.rowHeaderCells.length + layout.columnHeaderCells.length,
3318
3489
  bodyCellCount: layout.bodyCells.length
3319
3490
  };
3320
3491
  }
3492
+ function hasColumnHeaderCellAbove(cells, cell, headerTop) {
3493
+ if (isSameCoordinate2(cell.rect.y, headerTop)) return headerTop > 0;
3494
+ const left = cell.rect.x;
3495
+ const right = left + cell.rect.width;
3496
+ return cells.some((candidate) => {
3497
+ if (candidate === cell) return false;
3498
+ const candidateBottom = candidate.rect.y + candidate.rect.height;
3499
+ if (!isSameCoordinate2(candidateBottom, cell.rect.y)) return false;
3500
+ const candidateLeft = candidate.rect.x;
3501
+ const candidateRight = candidateLeft + candidate.rect.width;
3502
+ return candidateLeft < right && candidateRight > left;
3503
+ });
3504
+ }
3505
+ function hasColumnHeaderCellLeft(cells, cell, headerLeft) {
3506
+ if (cell.rect.x <= headerLeft + 0.01) {
3507
+ return true;
3508
+ }
3509
+ const top = cell.rect.y;
3510
+ const bottom = top + cell.rect.height;
3511
+ return cells.some((candidate) => {
3512
+ if (candidate === cell) return false;
3513
+ const candidateRight = candidate.rect.x + candidate.rect.width;
3514
+ if (!isSameCoordinate2(candidateRight, cell.rect.x)) return false;
3515
+ const candidateTop = candidate.rect.y;
3516
+ const candidateBottom = candidateTop + candidate.rect.height;
3517
+ return candidateTop < bottom && candidateBottom > top;
3518
+ });
3519
+ }
3321
3520
  function resolvePivotViewportWidth(layout) {
3322
3521
  const horizontalRight = layout.scrollbar.horizontal ? layout.scrollbar.horizontal.increaseButton.x + layout.scrollbar.horizontal.increaseButton.width : 0;
3323
3522
  const verticalRight = layout.scrollbar.vertical ? layout.scrollbar.vertical.decreaseButton.x + layout.scrollbar.vertical.decreaseButton.width : 0;
@@ -3357,7 +3556,7 @@ function drawDebugOverlay(context, layout, theme) {
3357
3556
  layout.bodyRect
3358
3557
  ].forEach((rect) => strokeRect(context, rect, "#f04438"));
3359
3558
  context.setLineDash([]);
3360
- context.font = `12px ${theme.fontFamily}`;
3559
+ context.font = `${theme.fontSize}px ${theme.fontFamily}`;
3361
3560
  context.textAlign = "left";
3362
3561
  context.textBaseline = "top";
3363
3562
  context.fillStyle = "#f04438";
@@ -3616,11 +3815,11 @@ function resolveSlotPathKey(slots, nodeId, indicatorKey) {
3616
3815
  (slot) => (slot.nodeId ?? "") === nodeId && (!slot.indicatorKey || slot.indicatorKey === indicatorKey)
3617
3816
  )?.pathKey ?? null;
3618
3817
  }
3619
- function isSameCoordinate(first, second) {
3818
+ function isSameCoordinate2(first, second) {
3620
3819
  return Math.abs(first - second) < 0.01;
3621
3820
  }
3622
3821
  function resolveBodyDisplayText(cell, indicator) {
3623
- if (!indicator || indicator.formatter || typeof cell.value !== "number") {
3822
+ if (!indicator || indicator.formatter || indicator.dataFormat || typeof cell.value !== "number") {
3624
3823
  return cell.formattedValue;
3625
3824
  }
3626
3825
  if (indicator.cellType === "percent") {
@@ -3757,10 +3956,18 @@ function drawSortIcon(context, rect, direction, rightPadding) {
3757
3956
  function drawText(context, text, rect, color, padding, align) {
3758
3957
  const availableWidth = Math.max(rect.width - padding * 2, 0);
3759
3958
  const value = truncateText(context, text, availableWidth);
3959
+ context.textBaseline = "alphabetic";
3960
+ const metrics = context.measureText(value);
3961
+ const ascent = metrics.actualBoundingBoxAscent ?? 0;
3962
+ const descent = metrics.actualBoundingBoxDescent ?? 0;
3963
+ const hasGlyphBounds = ascent > 0 || descent > 0;
3964
+ const centerY = rect.y + rect.height / 2;
3760
3965
  context.fillStyle = color;
3761
3966
  context.textAlign = align;
3967
+ context.textBaseline = hasGlyphBounds ? "alphabetic" : "middle";
3762
3968
  const x = align === "right" ? rect.x + rect.width - padding : align === "center" ? rect.x + rect.width / 2 : rect.x + padding;
3763
- context.fillText(value, x, rect.y + rect.height / 2, availableWidth);
3969
+ const y = hasGlyphBounds ? centerY + (ascent - descent) / 2 : centerY;
3970
+ context.fillText(value, x, y, availableWidth);
3764
3971
  }
3765
3972
  function truncateText(context, text, maxWidth) {
3766
3973
  if (context.measureText(text).width <= maxWidth) return text;
@@ -3808,7 +4015,7 @@ function hitTestPivotLayout(layout, x, y) {
3808
4015
  onSortIcon: false
3809
4016
  };
3810
4017
  }
3811
- const bodyCell = findLastContaining(layout.bodyCells, x, y);
4018
+ const bodyCell = contains(layout.bodyRect, x, y) ? findLastContaining(layout.bodyCells, x, y) : void 0;
3812
4019
  if (bodyCell) {
3813
4020
  return {
3814
4021
  region: "body",
@@ -3818,7 +4025,7 @@ function hitTestPivotLayout(layout, x, y) {
3818
4025
  onSortIcon: false
3819
4026
  };
3820
4027
  }
3821
- const rowHeaderCell = findLastContaining(layout.rowHeaderCells, x, y);
4028
+ const rowHeaderCell = contains(layout.rowHeaderRect, x, y) ? findLastContaining(layout.rowHeaderCells, x, y) : void 0;
3822
4029
  if (rowHeaderCell) {
3823
4030
  return {
3824
4031
  region: "rowHeader",
@@ -3828,9 +4035,7 @@ function hitTestPivotLayout(layout, x, y) {
3828
4035
  onSortIcon: isSortIconHit(rowHeaderCell, x)
3829
4036
  };
3830
4037
  }
3831
- const columnHeaderCell = layout.columnHeaderCells.find(
3832
- (cell) => contains(cell.rect, x, y)
3833
- );
4038
+ const columnHeaderCell = contains(layout.columnHeaderRect, x, y) ? layout.columnHeaderCells.find((cell) => contains(cell.rect, x, y)) : void 0;
3834
4039
  if (columnHeaderCell) {
3835
4040
  return {
3836
4041
  region: "columnHeader",
@@ -4346,7 +4551,7 @@ var PivotTable = class {
4346
4551
  }
4347
4552
  if (hit.reportFilterCell) return;
4348
4553
  if (hit.bodyCell) {
4349
- this.selectCell(toCellAddress(hit.bodyCell), event.shiftKey);
4554
+ this.selectPointerCell(toCellAddress(hit.bodyCell), event.shiftKey);
4350
4555
  return;
4351
4556
  }
4352
4557
  const header = hit.headerCell;
@@ -4448,7 +4653,76 @@ var PivotTable = class {
4448
4653
  });
4449
4654
  __publicField(this, "handleContextMenu", (event) => {
4450
4655
  const hit = this.resolveHit(event);
4451
- if (!hit?.bodyCell || !this.core || !this.layout) return;
4656
+ if (!hit || !this.core || !this.layout) return;
4657
+ if (hit.reportFilterCell) {
4658
+ const cell = hit.reportFilterCell;
4659
+ event.preventDefault();
4660
+ this.options.ui?.onContextMenuRequest?.({
4661
+ x: event.clientX,
4662
+ y: event.clientY,
4663
+ target: "header",
4664
+ title: cell.label,
4665
+ axis: "filter",
4666
+ onCopy: () => this.copyPivotText(`${cell.label} ${cell.valueLabel}`),
4667
+ onOpenFilter: () => this.openReportFilterPanel(cell),
4668
+ ...this.createContextMenuSettings(),
4669
+ onExportCsv: () => downloadBlob(
4670
+ new Blob([this.exportCsv()], { type: "text/csv;charset=utf-8" }),
4671
+ "pivot-table.csv"
4672
+ ),
4673
+ onExportXlsx: () => downloadBlob(
4674
+ new Blob([toArrayBuffer(this.exportXlsx())], {
4675
+ type: "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"
4676
+ }),
4677
+ "pivot-table.xlsx"
4678
+ ),
4679
+ onClose: () => this.options.ui?.onContextMenuRequest?.(null)
4680
+ });
4681
+ return;
4682
+ }
4683
+ if (hit.headerCell) {
4684
+ const header = hit.headerCell;
4685
+ const model2 = this.core.getPivotModel();
4686
+ const node = header.nodeId ? (header.axis === "row" ? model2?.rowNodes : model2?.columnNodes)?.get(
4687
+ header.nodeId
4688
+ ) : void 0;
4689
+ const dimensionKey = header.dimensionKey ?? node?.dimensionKey;
4690
+ event.preventDefault();
4691
+ this.options.ui?.onContextMenuRequest?.({
4692
+ x: event.clientX,
4693
+ y: event.clientY,
4694
+ target: "header",
4695
+ title: header.label,
4696
+ axis: header.axis,
4697
+ nodeKind: node?.kind ?? header.kind,
4698
+ indicatorKey: header.indicatorKey ?? void 0,
4699
+ onCopy: () => this.copyPivotText(header.label),
4700
+ sortDirection: header.sortDirection,
4701
+ onSortAsc: header.sortable && dimensionKey ? () => this.setDimensionSort(header.axis, dimensionKey, "asc") : void 0,
4702
+ onSortDesc: header.sortable && dimensionKey ? () => this.setDimensionSort(header.axis, dimensionKey, "desc") : void 0,
4703
+ onClearSort: header.sortable && dimensionKey ? () => this.setDimensionSort(header.axis, dimensionKey, null) : void 0,
4704
+ onOpenFilter: header.filterable ? () => this.openFilterPanel(hit) : void 0,
4705
+ expanded: header.expandable ? header.expanded : void 0,
4706
+ onExpand: header.expandable && header.nodeId ? () => header.axis === "row" ? this.expandRowNode(header.nodeId) : this.expandColumnNode(header.nodeId) : void 0,
4707
+ onCollapse: header.expandable && header.nodeId ? () => header.axis === "row" ? this.collapseRowNode(header.nodeId) : this.collapseColumnNode(header.nodeId) : void 0,
4708
+ onExpandAll: () => this.expandAll(header.axis),
4709
+ onCollapseAll: () => this.collapseAll(header.axis),
4710
+ ...this.createContextMenuSettings(header.indicatorKey ?? void 0),
4711
+ onExportCsv: () => downloadBlob(
4712
+ new Blob([this.exportCsv()], { type: "text/csv;charset=utf-8" }),
4713
+ "pivot-table.csv"
4714
+ ),
4715
+ onExportXlsx: () => downloadBlob(
4716
+ new Blob([toArrayBuffer(this.exportXlsx())], {
4717
+ type: "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"
4718
+ }),
4719
+ "pivot-table.xlsx"
4720
+ ),
4721
+ onClose: () => this.options.ui?.onContextMenuRequest?.(null)
4722
+ });
4723
+ return;
4724
+ }
4725
+ if (!hit.bodyCell) return;
4452
4726
  event.preventDefault();
4453
4727
  const address = toCellAddress(hit.bodyCell);
4454
4728
  const currentRange = resolvePivotSelectionRange(
@@ -4458,7 +4732,7 @@ var PivotTable = class {
4458
4732
  const insideCurrentRange = Boolean(
4459
4733
  currentRange && hit.bodyCell.rowIndex >= currentRange.startRowIndex && hit.bodyCell.rowIndex <= currentRange.endRowIndex && hit.bodyCell.columnIndex >= currentRange.startColumnIndex && hit.bodyCell.columnIndex <= currentRange.endColumnIndex
4460
4734
  );
4461
- if (!insideCurrentRange) this.selectCell(address);
4735
+ if (!insideCurrentRange) this.selectPointerCell(address);
4462
4736
  const selectionRange = resolvePivotSelectionRange(
4463
4737
  this.layout,
4464
4738
  this.core.getSelection()
@@ -4468,15 +4742,28 @@ var PivotTable = class {
4468
4742
  columnPathKey: hit.bodyCell.columnPathKey,
4469
4743
  indicatorKey: hit.bodyCell.indicatorKey
4470
4744
  });
4745
+ const model = this.core.getPivotModel();
4746
+ const rowNode = hit.bodyCell.rowNodeId ? model?.rowNodes.get(hit.bodyCell.rowNodeId) : void 0;
4747
+ const summary = rowNode?.kind === "subtotal" || rowNode?.kind === "grandTotal";
4748
+ const indicator = this.options.indicators.find(
4749
+ (item) => item.key === hit.bodyCell?.indicatorKey
4750
+ );
4751
+ const summaryAggregator = typeof indicator?.aggregator === "string" && isBuiltInPivotAggregator(indicator.aggregator) ? indicator.aggregator : "sum";
4471
4752
  this.options.ui?.onContextMenuRequest?.({
4472
4753
  x: event.clientX,
4473
4754
  y: event.clientY,
4755
+ target: summary ? "summary" : "body",
4756
+ title: summary ? rowNode.label : void 0,
4757
+ nodeKind: rowNode?.kind,
4758
+ indicatorKey: hit.bodyCell.indicatorKey,
4474
4759
  recordCount: records.length,
4760
+ summaryAggregator: summary ? summaryAggregator : void 0,
4761
+ summaryPrecision: summary ? indicator?.precision : void 0,
4475
4762
  onCopy: () => {
4476
- const model = this.core?.getPivotModel();
4477
- const text = model ? createPivotRangeSelectionText({
4763
+ const model2 = this.core?.getPivotModel();
4764
+ const text = model2 ? createPivotRangeSelectionText({
4478
4765
  layout: this.layout,
4479
- model,
4766
+ model: model2,
4480
4767
  startRowIndex: hit.bodyCell.rowIndex,
4481
4768
  endRowIndex: hit.bodyCell.rowIndex,
4482
4769
  startColumnIndex: hit.bodyCell.columnIndex,
@@ -4501,11 +4788,11 @@ var PivotTable = class {
4501
4788
  columnCount: this.layout.columnSlots.length
4502
4789
  },
4503
4790
  onCopyCustomSelection: (range) => {
4504
- const model = this.core?.getPivotModel();
4505
- if (!model || !this.layout) return;
4791
+ const model2 = this.core?.getPivotModel();
4792
+ if (!model2 || !this.layout) return;
4506
4793
  const text = createPivotRangeSelectionText({
4507
4794
  layout: this.layout,
4508
- model,
4795
+ model: model2,
4509
4796
  startRowIndex: range.startRow - 1,
4510
4797
  endRowIndex: range.endRow - 1,
4511
4798
  startColumnIndex: range.startColumn - 1,
@@ -4515,6 +4802,7 @@ var PivotTable = class {
4515
4802
  this.copyPivotText(text);
4516
4803
  }
4517
4804
  },
4805
+ ...this.createContextMenuSettings(hit.bodyCell.indicatorKey),
4518
4806
  onExportCsv: () => downloadBlob(
4519
4807
  new Blob([this.exportCsv()], { type: "text/csv;charset=utf-8" }),
4520
4808
  "pivot-table.csv"
@@ -4530,9 +4818,9 @@ var PivotTable = class {
4530
4818
  });
4531
4819
  __publicField(this, "handlePointerDown", (event) => {
4532
4820
  if (!this.layout || !this.host) return;
4533
- this.stopWheelScrollSmoothing();
4534
4821
  const scrollbarHit = this.resolveScrollbarHit(event);
4535
4822
  if (scrollbarHit && event.button === 0) {
4823
+ this.stopWheelScrollSmoothing();
4536
4824
  this.beginScrollbarInteraction(event, scrollbarHit);
4537
4825
  return;
4538
4826
  }
@@ -4543,7 +4831,7 @@ var PivotTable = class {
4543
4831
  if (cellHit?.bodyCell) {
4544
4832
  const address = toCellAddress(cellHit.bodyCell);
4545
4833
  this.host.root.focus({ preventScroll: true });
4546
- const selection = this.selectCell(address, event.shiftKey);
4834
+ const selection = this.selectPointerCell(address, event.shiftKey);
4547
4835
  const text = this.resolveBodyText(cellHit.bodyCell);
4548
4836
  const textOriginX = this.resolveBodyTextOriginX(cellHit.bodyCell, text);
4549
4837
  const anchorOffset = text ? resolvePivotTextOffset({
@@ -4629,8 +4917,12 @@ var PivotTable = class {
4629
4917
  if (selection) this.setSelection(selection);
4630
4918
  this.host.root.setPointerCapture(event.pointerId);
4631
4919
  }
4920
+ if (cellHit?.headerCell || cellHit?.reportFilterCell) {
4921
+ event.preventDefault();
4922
+ }
4632
4923
  return;
4633
4924
  }
4925
+ this.stopWheelScrollSmoothing();
4634
4926
  event.preventDefault();
4635
4927
  const startWidth = hit.kind === "rowDimension" ? this.layout.rowDimensionWidths[hit.index] ?? 140 : this.layout.columnWidths[hit.index] ?? this.layout.columnWidth;
4636
4928
  this.resizeSession = {
@@ -4820,9 +5112,16 @@ var PivotTable = class {
4820
5112
  }
4821
5113
  /** 选中指定值单元格。 */
4822
5114
  selectCell(address, extend = false) {
5115
+ return this.commitCellSelection(address, extend, true);
5116
+ }
5117
+ /** 鼠标命中的单元格已在视口内,选中时不再校正滚动位置。 */
5118
+ selectPointerCell(address, extend = false) {
5119
+ return this.commitCellSelection(address, extend, false);
5120
+ }
5121
+ commitCellSelection(address, extend, reveal) {
4823
5122
  const selection = this.core?.selectCell(address, extend) ?? null;
4824
5123
  if (selection) {
4825
- this.scrollToCell(address);
5124
+ if (reveal) this.scrollToCell(address);
4826
5125
  this.scheduleRender();
4827
5126
  }
4828
5127
  return selection;
@@ -4980,6 +5279,27 @@ var PivotTable = class {
4980
5279
  this.advanceWheelScrollSmoothing(nextTime);
4981
5280
  });
4982
5281
  }
5282
+ createContextMenuSettings(indicatorKey) {
5283
+ const indicator = indicatorKey ? this.options.indicators.find((item) => item.key === indicatorKey) : void 0;
5284
+ return {
5285
+ density: resolvePivotTableDensity(this.options.rowHeight),
5286
+ onChangeDensity: (density) => {
5287
+ this.updateOptions({
5288
+ ...this.options,
5289
+ rowHeight: resolvePivotTableDensityRowHeight(density)
5290
+ });
5291
+ },
5292
+ dataFormat: indicator?.dataFormat ?? "default",
5293
+ onChangeDataFormat: indicator ? (dataFormat) => {
5294
+ this.updateOptions({
5295
+ ...this.options,
5296
+ indicators: this.options.indicators.map(
5297
+ (item) => item.key === indicator.key ? { ...item, dataFormat } : item
5298
+ )
5299
+ });
5300
+ } : void 0
5301
+ };
5302
+ }
4983
5303
  updateTooltip(hit, event) {
4984
5304
  if (!this.host) return;
4985
5305
  const cell = hit?.bodyCell ?? hit?.headerCell;
@@ -5185,7 +5505,7 @@ var PivotTable = class {
5185
5505
  const indicator = this.options.indicators.find(
5186
5506
  (item) => item.key === cell.indicatorKey
5187
5507
  );
5188
- if (!indicator || indicator.formatter || typeof cell.value !== "number")
5508
+ if (!indicator || indicator.formatter || indicator.dataFormat || typeof cell.value !== "number")
5189
5509
  return cell.formattedValue;
5190
5510
  if (indicator.cellType === "percent")
5191
5511
  return `${(cell.value * 100).toFixed(indicator.precision ?? 2)}%`;
@@ -5223,7 +5543,7 @@ var PivotTable = class {
5223
5543
  measurePivotText(text) {
5224
5544
  const context = this.host?.canvas.getContext("2d");
5225
5545
  if (!context) return text.length * 8;
5226
- context.font = `${this.options.theme?.fontSize ?? 13}px ${this.options.theme?.fontFamily ?? "sans-serif"}`;
5546
+ context.font = `${this.options.theme?.fontSize ?? 14}px ${this.options.theme?.fontFamily ?? "sans-serif"}`;
5227
5547
  return context.measureText(text).width;
5228
5548
  }
5229
5549
  updateRowHeaderSelectionDrag(event) {
@@ -5547,6 +5867,9 @@ function toCellAddress(cell) {
5547
5867
  indicatorKey: cell.indicatorKey
5548
5868
  };
5549
5869
  }
5870
+ function isBuiltInPivotAggregator(value) {
5871
+ return ["sum", "avg", "count", "distinctCount", "min", "max"].includes(value);
5872
+ }
5550
5873
  function isSameAddress(left, right) {
5551
5874
  return Boolean(
5552
5875
  left === right || left && right && left.rowNodeId === right.rowNodeId && left.columnNodeId === right.columnNodeId && left.indicatorKey === right.indicatorKey
@@ -5559,6 +5882,20 @@ function resolveNavigationDirection(key) {
5559
5882
  if (key === "ArrowRight") return { row: 0, column: 1 };
5560
5883
  return null;
5561
5884
  }
5885
+ function resolvePivotTableDensity(rowHeight) {
5886
+ if (rowHeight == null) return "auto";
5887
+ if (rowHeight <= 26) return "ultra-compact";
5888
+ if (rowHeight <= 32) return "compact";
5889
+ if (rowHeight >= 56) return "comfortable";
5890
+ return "middle";
5891
+ }
5892
+ function resolvePivotTableDensityRowHeight(density) {
5893
+ if (density === "comfortable") return 56;
5894
+ if (density === "middle") return 48;
5895
+ if (density === "compact") return 32;
5896
+ if (density === "ultra-compact") return 26;
5897
+ return void 0;
5898
+ }
5562
5899
  function resolveAdjacentAddress(layout, address, direction) {
5563
5900
  const indexes = resolveAddressIndexes3(layout, address);
5564
5901
  if (!indexes) return null;
@@ -5609,6 +5946,90 @@ function toArrayBuffer(value) {
5609
5946
  return buffer;
5610
5947
  }
5611
5948
 
5949
+ // src/adapters/storage/indexeddb-field-layout-storage.ts
5950
+ var DATABASE_NAME = "canvas_components_pivot_table_config";
5951
+ var STORE_NAME = "field_layout";
5952
+ var DB_VERSION = 1;
5953
+ var dbPromise = null;
5954
+ async function savePivotFieldLayout(key, layout) {
5955
+ try {
5956
+ const db = await openDatabase();
5957
+ await requestToPromise(
5958
+ db.transaction(STORE_NAME, "readwrite").objectStore(STORE_NAME).put(cloneLayout(layout), key)
5959
+ );
5960
+ } catch (error) {
5961
+ console.error("[PivotTable] \u5B57\u6BB5\u5E03\u5C40\u4FDD\u5B58\u5931\u8D25\uFF1A", error);
5962
+ }
5963
+ }
5964
+ async function loadPivotFieldLayout(key) {
5965
+ try {
5966
+ const db = await openDatabase();
5967
+ const value = await requestToPromise(
5968
+ db.transaction(STORE_NAME, "readonly").objectStore(STORE_NAME).get(key)
5969
+ );
5970
+ return isPivotFieldLayout(value) ? cloneLayout(value) : void 0;
5971
+ } catch (error) {
5972
+ console.error("[PivotTable] \u5B57\u6BB5\u5E03\u5C40\u8BFB\u53D6\u5931\u8D25\uFF1A", error);
5973
+ return void 0;
5974
+ }
5975
+ }
5976
+ async function clearPivotFieldLayout(key) {
5977
+ try {
5978
+ const db = await openDatabase();
5979
+ await requestToPromise(
5980
+ db.transaction(STORE_NAME, "readwrite").objectStore(STORE_NAME).delete(key)
5981
+ );
5982
+ } catch (error) {
5983
+ console.error("[PivotTable] \u5B57\u6BB5\u5E03\u5C40\u6E05\u9664\u5931\u8D25\uFF1A", error);
5984
+ }
5985
+ }
5986
+ function openDatabase() {
5987
+ if (dbPromise) return dbPromise;
5988
+ dbPromise = new Promise((resolve, reject) => {
5989
+ if (typeof indexedDB === "undefined") {
5990
+ reject(new Error("\u5F53\u524D\u73AF\u5883\u4E0D\u652F\u6301 IndexedDB"));
5991
+ return;
5992
+ }
5993
+ const request = indexedDB.open(DATABASE_NAME, DB_VERSION);
5994
+ request.onupgradeneeded = () => {
5995
+ const db = request.result;
5996
+ if (!db.objectStoreNames.contains(STORE_NAME)) {
5997
+ db.createObjectStore(STORE_NAME);
5998
+ }
5999
+ };
6000
+ request.onsuccess = () => resolve(request.result);
6001
+ request.onerror = () => reject(request.error);
6002
+ });
6003
+ dbPromise.catch(() => {
6004
+ dbPromise = null;
6005
+ });
6006
+ return dbPromise;
6007
+ }
6008
+ function requestToPromise(request) {
6009
+ return new Promise((resolve, reject) => {
6010
+ request.onsuccess = () => resolve(request.result);
6011
+ request.onerror = () => reject(request.error);
6012
+ });
6013
+ }
6014
+ function isPivotFieldLayout(value) {
6015
+ if (!value || typeof value !== "object") return false;
6016
+ const layout = value;
6017
+ return ["filters", "columns", "rows", "values"].every(
6018
+ (key) => isStringArray(layout[key])
6019
+ );
6020
+ }
6021
+ function isStringArray(value) {
6022
+ return Array.isArray(value) && value.every((item) => typeof item === "string");
6023
+ }
6024
+ function cloneLayout(layout) {
6025
+ return {
6026
+ filters: [...layout.filters],
6027
+ columns: [...layout.columns],
6028
+ rows: [...layout.rows],
6029
+ values: [...layout.values]
6030
+ };
6031
+ }
6032
+
5612
6033
  // src/domain/field/pivot-field-layout.ts
5613
6034
  function createEmptyPivotFieldLayout() {
5614
6035
  return { filters: [], columns: [], rows: [], values: [] };
@@ -5772,6 +6193,7 @@ exports.PivotTable = PivotTable;
5772
6193
  exports.aggregatePivotRecords = aggregatePivotRecords;
5773
6194
  exports.buildPivotDimensionTree = buildPivotDimensionTree;
5774
6195
  exports.buildPivotLayoutSnapshot = buildPivotLayoutSnapshot;
6196
+ exports.clearPivotFieldLayout = clearPivotFieldLayout;
5775
6197
  exports.computePivotScrollbarLayout = computePivotScrollbarLayout;
5776
6198
  exports.createBuiltInPivotAggregators = createBuiltInPivotAggregators;
5777
6199
  exports.createEmptyPivotFieldLayout = createEmptyPivotFieldLayout;
@@ -5795,6 +6217,7 @@ exports.getPivotDimensionFilterOptions = getPivotDimensionFilterOptions;
5795
6217
  exports.hitTestPivotLayout = hitTestPivotLayout;
5796
6218
  exports.hitTestPivotResize = hitTestPivotResize;
5797
6219
  exports.hitTestPivotScrollbar = hitTestPivotScrollbar;
6220
+ exports.loadPivotFieldLayout = loadPivotFieldLayout;
5798
6221
  exports.movePivotField = movePivotField;
5799
6222
  exports.removePivotField = removePivotField;
5800
6223
  exports.renderPivotTable = renderPivotTable;
@@ -5806,5 +6229,6 @@ exports.resolvePivotDimensionWidth = resolvePivotDimensionWidth;
5806
6229
  exports.resolvePivotFieldLayoutOptions = resolvePivotFieldLayoutOptions;
5807
6230
  exports.resolvePivotIndicatorAggregator = resolvePivotIndicatorAggregator;
5808
6231
  exports.resolvePivotValueFieldTitle = resolvePivotValueFieldTitle;
6232
+ exports.savePivotFieldLayout = savePivotFieldLayout;
5809
6233
  //# sourceMappingURL=index.cjs.map
5810
6234
  //# sourceMappingURL=index.cjs.map