@canvas-components/pivot-table 0.2.2 → 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 +314 -34
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +46 -3
- package/dist/index.d.ts +46 -3
- package/dist/index.js +315 -35
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.cjs
CHANGED
|
@@ -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 *
|
|
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:
|
|
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 *
|
|
1750
|
+
y: params.headerTop + level * params.headerSize,
|
|
1656
1751
|
width: columnWidth,
|
|
1657
|
-
height:
|
|
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) *
|
|
1779
|
+
y: params.headerTop + (params.levels - 1) * params.headerSize,
|
|
1685
1780
|
width: columnWidth,
|
|
1686
|
-
height:
|
|
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,
|
|
@@ -3272,8 +3376,16 @@ function renderPivotTable(params) {
|
|
|
3272
3376
|
);
|
|
3273
3377
|
layout.columnHeaderCells.forEach(
|
|
3274
3378
|
(cell) => drawPivotGridRect(context, cell.rect, theme.borderColor, {
|
|
3275
|
-
skipTop:
|
|
3276
|
-
|
|
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
|
+
),
|
|
3277
3389
|
skipRight: isSameCoordinate2(
|
|
3278
3390
|
cell.rect.x + cell.rect.width,
|
|
3279
3391
|
layout.tableWidth
|
|
@@ -3343,10 +3455,12 @@ function renderPivotTable(params) {
|
|
|
3343
3455
|
drawPivotResizeGuide(context, params.resizeGuideX, contentHeight);
|
|
3344
3456
|
}
|
|
3345
3457
|
context.restore();
|
|
3346
|
-
|
|
3347
|
-
|
|
3348
|
-
|
|
3349
|
-
|
|
3458
|
+
if (!layout.scrollbar.vertical) {
|
|
3459
|
+
context.save();
|
|
3460
|
+
clipPivotViewport(context, tableViewportRect);
|
|
3461
|
+
drawPivotViewportRightBorder(context, tableViewportRect, theme.borderColor);
|
|
3462
|
+
context.restore();
|
|
3463
|
+
}
|
|
3350
3464
|
drawPivotViewportBorder(
|
|
3351
3465
|
context,
|
|
3352
3466
|
{ x: 0, y: 0, width: viewportWidth, height: contentHeight },
|
|
@@ -3375,6 +3489,34 @@ function renderPivotTable(params) {
|
|
|
3375
3489
|
bodyCellCount: layout.bodyCells.length
|
|
3376
3490
|
};
|
|
3377
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
|
+
}
|
|
3378
3520
|
function resolvePivotViewportWidth(layout) {
|
|
3379
3521
|
const horizontalRight = layout.scrollbar.horizontal ? layout.scrollbar.horizontal.increaseButton.x + layout.scrollbar.horizontal.increaseButton.width : 0;
|
|
3380
3522
|
const verticalRight = layout.scrollbar.vertical ? layout.scrollbar.vertical.decreaseButton.x + layout.scrollbar.vertical.decreaseButton.width : 0;
|
|
@@ -3677,7 +3819,7 @@ function isSameCoordinate2(first, second) {
|
|
|
3677
3819
|
return Math.abs(first - second) < 0.01;
|
|
3678
3820
|
}
|
|
3679
3821
|
function resolveBodyDisplayText(cell, indicator) {
|
|
3680
|
-
if (!indicator || indicator.formatter || typeof cell.value !== "number") {
|
|
3822
|
+
if (!indicator || indicator.formatter || indicator.dataFormat || typeof cell.value !== "number") {
|
|
3681
3823
|
return cell.formattedValue;
|
|
3682
3824
|
}
|
|
3683
3825
|
if (indicator.cellType === "percent") {
|
|
@@ -3814,10 +3956,18 @@ function drawSortIcon(context, rect, direction, rightPadding) {
|
|
|
3814
3956
|
function drawText(context, text, rect, color, padding, align) {
|
|
3815
3957
|
const availableWidth = Math.max(rect.width - padding * 2, 0);
|
|
3816
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;
|
|
3817
3965
|
context.fillStyle = color;
|
|
3818
3966
|
context.textAlign = align;
|
|
3967
|
+
context.textBaseline = hasGlyphBounds ? "alphabetic" : "middle";
|
|
3819
3968
|
const x = align === "right" ? rect.x + rect.width - padding : align === "center" ? rect.x + rect.width / 2 : rect.x + padding;
|
|
3820
|
-
|
|
3969
|
+
const y = hasGlyphBounds ? centerY + (ascent - descent) / 2 : centerY;
|
|
3970
|
+
context.fillText(value, x, y, availableWidth);
|
|
3821
3971
|
}
|
|
3822
3972
|
function truncateText(context, text, maxWidth) {
|
|
3823
3973
|
if (context.measureText(text).width <= maxWidth) return text;
|
|
@@ -3865,7 +4015,7 @@ function hitTestPivotLayout(layout, x, y) {
|
|
|
3865
4015
|
onSortIcon: false
|
|
3866
4016
|
};
|
|
3867
4017
|
}
|
|
3868
|
-
const bodyCell = findLastContaining(layout.bodyCells, x, y);
|
|
4018
|
+
const bodyCell = contains(layout.bodyRect, x, y) ? findLastContaining(layout.bodyCells, x, y) : void 0;
|
|
3869
4019
|
if (bodyCell) {
|
|
3870
4020
|
return {
|
|
3871
4021
|
region: "body",
|
|
@@ -3875,7 +4025,7 @@ function hitTestPivotLayout(layout, x, y) {
|
|
|
3875
4025
|
onSortIcon: false
|
|
3876
4026
|
};
|
|
3877
4027
|
}
|
|
3878
|
-
const rowHeaderCell = findLastContaining(layout.rowHeaderCells, x, y);
|
|
4028
|
+
const rowHeaderCell = contains(layout.rowHeaderRect, x, y) ? findLastContaining(layout.rowHeaderCells, x, y) : void 0;
|
|
3879
4029
|
if (rowHeaderCell) {
|
|
3880
4030
|
return {
|
|
3881
4031
|
region: "rowHeader",
|
|
@@ -3885,9 +4035,7 @@ function hitTestPivotLayout(layout, x, y) {
|
|
|
3885
4035
|
onSortIcon: isSortIconHit(rowHeaderCell, x)
|
|
3886
4036
|
};
|
|
3887
4037
|
}
|
|
3888
|
-
const columnHeaderCell = layout.columnHeaderCells.find(
|
|
3889
|
-
(cell) => contains(cell.rect, x, y)
|
|
3890
|
-
);
|
|
4038
|
+
const columnHeaderCell = contains(layout.columnHeaderRect, x, y) ? layout.columnHeaderCells.find((cell) => contains(cell.rect, x, y)) : void 0;
|
|
3891
4039
|
if (columnHeaderCell) {
|
|
3892
4040
|
return {
|
|
3893
4041
|
region: "columnHeader",
|
|
@@ -4403,7 +4551,7 @@ var PivotTable = class {
|
|
|
4403
4551
|
}
|
|
4404
4552
|
if (hit.reportFilterCell) return;
|
|
4405
4553
|
if (hit.bodyCell) {
|
|
4406
|
-
this.
|
|
4554
|
+
this.selectPointerCell(toCellAddress(hit.bodyCell), event.shiftKey);
|
|
4407
4555
|
return;
|
|
4408
4556
|
}
|
|
4409
4557
|
const header = hit.headerCell;
|
|
@@ -4505,7 +4653,76 @@ var PivotTable = class {
|
|
|
4505
4653
|
});
|
|
4506
4654
|
__publicField(this, "handleContextMenu", (event) => {
|
|
4507
4655
|
const hit = this.resolveHit(event);
|
|
4508
|
-
if (!hit
|
|
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;
|
|
4509
4726
|
event.preventDefault();
|
|
4510
4727
|
const address = toCellAddress(hit.bodyCell);
|
|
4511
4728
|
const currentRange = resolvePivotSelectionRange(
|
|
@@ -4515,7 +4732,7 @@ var PivotTable = class {
|
|
|
4515
4732
|
const insideCurrentRange = Boolean(
|
|
4516
4733
|
currentRange && hit.bodyCell.rowIndex >= currentRange.startRowIndex && hit.bodyCell.rowIndex <= currentRange.endRowIndex && hit.bodyCell.columnIndex >= currentRange.startColumnIndex && hit.bodyCell.columnIndex <= currentRange.endColumnIndex
|
|
4517
4734
|
);
|
|
4518
|
-
if (!insideCurrentRange) this.
|
|
4735
|
+
if (!insideCurrentRange) this.selectPointerCell(address);
|
|
4519
4736
|
const selectionRange = resolvePivotSelectionRange(
|
|
4520
4737
|
this.layout,
|
|
4521
4738
|
this.core.getSelection()
|
|
@@ -4525,15 +4742,28 @@ var PivotTable = class {
|
|
|
4525
4742
|
columnPathKey: hit.bodyCell.columnPathKey,
|
|
4526
4743
|
indicatorKey: hit.bodyCell.indicatorKey
|
|
4527
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";
|
|
4528
4752
|
this.options.ui?.onContextMenuRequest?.({
|
|
4529
4753
|
x: event.clientX,
|
|
4530
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,
|
|
4531
4759
|
recordCount: records.length,
|
|
4760
|
+
summaryAggregator: summary ? summaryAggregator : void 0,
|
|
4761
|
+
summaryPrecision: summary ? indicator?.precision : void 0,
|
|
4532
4762
|
onCopy: () => {
|
|
4533
|
-
const
|
|
4534
|
-
const text =
|
|
4763
|
+
const model2 = this.core?.getPivotModel();
|
|
4764
|
+
const text = model2 ? createPivotRangeSelectionText({
|
|
4535
4765
|
layout: this.layout,
|
|
4536
|
-
model,
|
|
4766
|
+
model: model2,
|
|
4537
4767
|
startRowIndex: hit.bodyCell.rowIndex,
|
|
4538
4768
|
endRowIndex: hit.bodyCell.rowIndex,
|
|
4539
4769
|
startColumnIndex: hit.bodyCell.columnIndex,
|
|
@@ -4558,11 +4788,11 @@ var PivotTable = class {
|
|
|
4558
4788
|
columnCount: this.layout.columnSlots.length
|
|
4559
4789
|
},
|
|
4560
4790
|
onCopyCustomSelection: (range) => {
|
|
4561
|
-
const
|
|
4562
|
-
if (!
|
|
4791
|
+
const model2 = this.core?.getPivotModel();
|
|
4792
|
+
if (!model2 || !this.layout) return;
|
|
4563
4793
|
const text = createPivotRangeSelectionText({
|
|
4564
4794
|
layout: this.layout,
|
|
4565
|
-
model,
|
|
4795
|
+
model: model2,
|
|
4566
4796
|
startRowIndex: range.startRow - 1,
|
|
4567
4797
|
endRowIndex: range.endRow - 1,
|
|
4568
4798
|
startColumnIndex: range.startColumn - 1,
|
|
@@ -4572,6 +4802,7 @@ var PivotTable = class {
|
|
|
4572
4802
|
this.copyPivotText(text);
|
|
4573
4803
|
}
|
|
4574
4804
|
},
|
|
4805
|
+
...this.createContextMenuSettings(hit.bodyCell.indicatorKey),
|
|
4575
4806
|
onExportCsv: () => downloadBlob(
|
|
4576
4807
|
new Blob([this.exportCsv()], { type: "text/csv;charset=utf-8" }),
|
|
4577
4808
|
"pivot-table.csv"
|
|
@@ -4587,9 +4818,9 @@ var PivotTable = class {
|
|
|
4587
4818
|
});
|
|
4588
4819
|
__publicField(this, "handlePointerDown", (event) => {
|
|
4589
4820
|
if (!this.layout || !this.host) return;
|
|
4590
|
-
this.stopWheelScrollSmoothing();
|
|
4591
4821
|
const scrollbarHit = this.resolveScrollbarHit(event);
|
|
4592
4822
|
if (scrollbarHit && event.button === 0) {
|
|
4823
|
+
this.stopWheelScrollSmoothing();
|
|
4593
4824
|
this.beginScrollbarInteraction(event, scrollbarHit);
|
|
4594
4825
|
return;
|
|
4595
4826
|
}
|
|
@@ -4600,7 +4831,7 @@ var PivotTable = class {
|
|
|
4600
4831
|
if (cellHit?.bodyCell) {
|
|
4601
4832
|
const address = toCellAddress(cellHit.bodyCell);
|
|
4602
4833
|
this.host.root.focus({ preventScroll: true });
|
|
4603
|
-
const selection = this.
|
|
4834
|
+
const selection = this.selectPointerCell(address, event.shiftKey);
|
|
4604
4835
|
const text = this.resolveBodyText(cellHit.bodyCell);
|
|
4605
4836
|
const textOriginX = this.resolveBodyTextOriginX(cellHit.bodyCell, text);
|
|
4606
4837
|
const anchorOffset = text ? resolvePivotTextOffset({
|
|
@@ -4686,8 +4917,12 @@ var PivotTable = class {
|
|
|
4686
4917
|
if (selection) this.setSelection(selection);
|
|
4687
4918
|
this.host.root.setPointerCapture(event.pointerId);
|
|
4688
4919
|
}
|
|
4920
|
+
if (cellHit?.headerCell || cellHit?.reportFilterCell) {
|
|
4921
|
+
event.preventDefault();
|
|
4922
|
+
}
|
|
4689
4923
|
return;
|
|
4690
4924
|
}
|
|
4925
|
+
this.stopWheelScrollSmoothing();
|
|
4691
4926
|
event.preventDefault();
|
|
4692
4927
|
const startWidth = hit.kind === "rowDimension" ? this.layout.rowDimensionWidths[hit.index] ?? 140 : this.layout.columnWidths[hit.index] ?? this.layout.columnWidth;
|
|
4693
4928
|
this.resizeSession = {
|
|
@@ -4877,9 +5112,16 @@ var PivotTable = class {
|
|
|
4877
5112
|
}
|
|
4878
5113
|
/** 选中指定值单元格。 */
|
|
4879
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) {
|
|
4880
5122
|
const selection = this.core?.selectCell(address, extend) ?? null;
|
|
4881
5123
|
if (selection) {
|
|
4882
|
-
this.scrollToCell(address);
|
|
5124
|
+
if (reveal) this.scrollToCell(address);
|
|
4883
5125
|
this.scheduleRender();
|
|
4884
5126
|
}
|
|
4885
5127
|
return selection;
|
|
@@ -5037,6 +5279,27 @@ var PivotTable = class {
|
|
|
5037
5279
|
this.advanceWheelScrollSmoothing(nextTime);
|
|
5038
5280
|
});
|
|
5039
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
|
+
}
|
|
5040
5303
|
updateTooltip(hit, event) {
|
|
5041
5304
|
if (!this.host) return;
|
|
5042
5305
|
const cell = hit?.bodyCell ?? hit?.headerCell;
|
|
@@ -5242,7 +5505,7 @@ var PivotTable = class {
|
|
|
5242
5505
|
const indicator = this.options.indicators.find(
|
|
5243
5506
|
(item) => item.key === cell.indicatorKey
|
|
5244
5507
|
);
|
|
5245
|
-
if (!indicator || indicator.formatter || typeof cell.value !== "number")
|
|
5508
|
+
if (!indicator || indicator.formatter || indicator.dataFormat || typeof cell.value !== "number")
|
|
5246
5509
|
return cell.formattedValue;
|
|
5247
5510
|
if (indicator.cellType === "percent")
|
|
5248
5511
|
return `${(cell.value * 100).toFixed(indicator.precision ?? 2)}%`;
|
|
@@ -5604,6 +5867,9 @@ function toCellAddress(cell) {
|
|
|
5604
5867
|
indicatorKey: cell.indicatorKey
|
|
5605
5868
|
};
|
|
5606
5869
|
}
|
|
5870
|
+
function isBuiltInPivotAggregator(value) {
|
|
5871
|
+
return ["sum", "avg", "count", "distinctCount", "min", "max"].includes(value);
|
|
5872
|
+
}
|
|
5607
5873
|
function isSameAddress(left, right) {
|
|
5608
5874
|
return Boolean(
|
|
5609
5875
|
left === right || left && right && left.rowNodeId === right.rowNodeId && left.columnNodeId === right.columnNodeId && left.indicatorKey === right.indicatorKey
|
|
@@ -5616,6 +5882,20 @@ function resolveNavigationDirection(key) {
|
|
|
5616
5882
|
if (key === "ArrowRight") return { row: 0, column: 1 };
|
|
5617
5883
|
return null;
|
|
5618
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
|
+
}
|
|
5619
5899
|
function resolveAdjacentAddress(layout, address, direction) {
|
|
5620
5900
|
const indexes = resolveAddressIndexes3(layout, address);
|
|
5621
5901
|
if (!indexes) return null;
|