@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.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { stringifyDataIndex, getValueByDataIndex, dividePreciseNumber, addPreciseNumbers, clamp, serializeCsvRows, isPointInRect, copyTextSync, copyText } from '@canvas-components/utils';
|
|
1
|
+
import { stringifyDataIndex, getValueByDataIndex, dividePreciseNumber, addPreciseNumbers, clamp, serializeCsvRows, isPointInRect, copyTextSync, copyText, formatCustomNumberPattern, formatNumberValue, formatPercentValue, formatScientificValue, formatChineseNumber, parseChineseNumber, formatDateTimeValue } from '@canvas-components/utils';
|
|
2
2
|
export { getValueByDataIndex, isSameDataIndex, stringifyDataIndex } from '@canvas-components/utils';
|
|
3
3
|
import * as XLSX from 'xlsx';
|
|
4
4
|
|
|
@@ -411,6 +411,101 @@ function resolvePivotIndicatorAggregator(indicator, registry) {
|
|
|
411
411
|
}
|
|
412
412
|
return aggregator;
|
|
413
413
|
}
|
|
414
|
+
var DATE_PATTERNS = {
|
|
415
|
+
dateSlash: "YYYY/MM/DD",
|
|
416
|
+
dateDash: "YYYY-MM-DD",
|
|
417
|
+
dateFullCN: "YYYY\u5E74M\u6708D\u65E5",
|
|
418
|
+
time: "HH:mm:ss",
|
|
419
|
+
timeShort: "HH:mm",
|
|
420
|
+
datetime: "YYYY/MM/DD HH:mm:ss",
|
|
421
|
+
datetime12: "YYYY/MM/DD Ah:mm"
|
|
422
|
+
};
|
|
423
|
+
function formatPivotIndicatorValue(value, dataFormat) {
|
|
424
|
+
if (typeof dataFormat === "object") {
|
|
425
|
+
if (dataFormat.type === "custom" && dataFormat.pattern) {
|
|
426
|
+
return formatCustomNumberPattern(value, dataFormat.pattern);
|
|
427
|
+
}
|
|
428
|
+
return formatPivotIndicatorValueWithOptions(value, dataFormat);
|
|
429
|
+
}
|
|
430
|
+
if (dataFormat === "default" || dataFormat === "text") {
|
|
431
|
+
return String(value ?? "");
|
|
432
|
+
}
|
|
433
|
+
if (dataFormat === "number") {
|
|
434
|
+
return formatNumberValue(value, {
|
|
435
|
+
decimalPlaces: "auto",
|
|
436
|
+
thousandsSeparator: false
|
|
437
|
+
});
|
|
438
|
+
}
|
|
439
|
+
if (dataFormat === "thousands") {
|
|
440
|
+
return formatNumberValue(value, {
|
|
441
|
+
decimalPlaces: "auto",
|
|
442
|
+
thousandsSeparator: true
|
|
443
|
+
});
|
|
444
|
+
}
|
|
445
|
+
if (dataFormat === "decimal") {
|
|
446
|
+
return formatNumberValue(value, {
|
|
447
|
+
decimalPlaces: 2,
|
|
448
|
+
thousandsSeparator: true
|
|
449
|
+
});
|
|
450
|
+
}
|
|
451
|
+
if (dataFormat === "percent" || dataFormat === "percentDecimal") {
|
|
452
|
+
return formatPercentValue(value, dataFormat === "percentDecimal" ? 2 : 0);
|
|
453
|
+
}
|
|
454
|
+
if (dataFormat === "scientific") return formatScientificValue(value);
|
|
455
|
+
if (dataFormat === "numberToChineseUpper") {
|
|
456
|
+
return formatChineseNumber(value, "financial") ?? String(value ?? "");
|
|
457
|
+
}
|
|
458
|
+
if (dataFormat === "chineseUpperToNumber") {
|
|
459
|
+
const parsed = parseChineseNumber(value);
|
|
460
|
+
return parsed == null ? String(value ?? "") : String(parsed);
|
|
461
|
+
}
|
|
462
|
+
if (dataFormat === "cny" || dataFormat === "cnyDecimal") {
|
|
463
|
+
return formatNumberValue(value, {
|
|
464
|
+
decimalPlaces: dataFormat === "cnyDecimal" ? 2 : 0,
|
|
465
|
+
thousandsSeparator: true,
|
|
466
|
+
symbol: "\xA5"
|
|
467
|
+
});
|
|
468
|
+
}
|
|
469
|
+
if (dataFormat === "usd" || dataFormat === "usdDecimal") {
|
|
470
|
+
return formatNumberValue(value, {
|
|
471
|
+
decimalPlaces: dataFormat === "usdDecimal" ? 2 : 0,
|
|
472
|
+
thousandsSeparator: true,
|
|
473
|
+
symbol: "$"
|
|
474
|
+
});
|
|
475
|
+
}
|
|
476
|
+
const pattern = DATE_PATTERNS[dataFormat];
|
|
477
|
+
return pattern ? formatDateTimeValue(value, pattern) ?? String(value ?? "") : String(value ?? "");
|
|
478
|
+
}
|
|
479
|
+
function formatPivotIndicatorValueWithOptions(value, dataFormat) {
|
|
480
|
+
if (dataFormat.pattern && dataFormat.type !== "custom") {
|
|
481
|
+
const formatted = formatDateTimeValue(value, dataFormat.pattern);
|
|
482
|
+
if (formatted != null) return formatted;
|
|
483
|
+
}
|
|
484
|
+
const decimalPlaces = Math.max(
|
|
485
|
+
0,
|
|
486
|
+
Math.min(20, dataFormat.decimalPlaces ?? 2)
|
|
487
|
+
);
|
|
488
|
+
if (dataFormat.type === "percent" || dataFormat.type === "percentDecimal") {
|
|
489
|
+
return formatPercentValue(value, decimalPlaces);
|
|
490
|
+
}
|
|
491
|
+
if (dataFormat.type === "scientific") {
|
|
492
|
+
return formatScientificValue(value, decimalPlaces);
|
|
493
|
+
}
|
|
494
|
+
if (dataFormat.type === "cny" || dataFormat.type === "cnyDecimal" || dataFormat.type === "usd" || dataFormat.type === "usdDecimal") {
|
|
495
|
+
return formatNumberValue(value, {
|
|
496
|
+
decimalPlaces,
|
|
497
|
+
thousandsSeparator: dataFormat.useGrouping ?? true,
|
|
498
|
+
symbol: dataFormat.symbol ?? (dataFormat.type.startsWith("usd") ? "$" : "\xA5")
|
|
499
|
+
});
|
|
500
|
+
}
|
|
501
|
+
if (dataFormat.type === "number" || dataFormat.type === "thousands" || dataFormat.type === "decimal") {
|
|
502
|
+
return formatNumberValue(value, {
|
|
503
|
+
decimalPlaces,
|
|
504
|
+
thousandsSeparator: dataFormat.useGrouping ?? dataFormat.type !== "number"
|
|
505
|
+
});
|
|
506
|
+
}
|
|
507
|
+
return formatPivotIndicatorValue(value, dataFormat.type);
|
|
508
|
+
}
|
|
414
509
|
|
|
415
510
|
// src/domain/aggregation/aggregate-records.ts
|
|
416
511
|
function aggregatePivotRecords(params) {
|
|
@@ -475,7 +570,7 @@ function aggregatePivotRecords(params) {
|
|
|
475
570
|
recordCount: item.recordIndexes.length,
|
|
476
571
|
rowPathKey: item.rowPathKey,
|
|
477
572
|
columnPathKey: item.columnPathKey
|
|
478
|
-
}) ?? formatAggregateValue(value, item.indicator.precision),
|
|
573
|
+
}) ?? (item.indicator.dataFormat ? formatPivotIndicatorValue(value, item.indicator.dataFormat) : formatAggregateValue(value, item.indicator.precision)),
|
|
479
574
|
recordCount: item.recordIndexes.length,
|
|
480
575
|
recordIndexes: item.recordIndexes
|
|
481
576
|
});
|
|
@@ -1025,7 +1120,6 @@ function resolveThumbOffset(scroll, maxScroll, track, thumb) {
|
|
|
1025
1120
|
// src/domain/layout/pivot-layout.ts
|
|
1026
1121
|
var DEFAULT_ROW_HEIGHT = 40;
|
|
1027
1122
|
var DEFAULT_COLUMN_WIDTH = 120;
|
|
1028
|
-
var DEFAULT_HEADER_SIZE = 40;
|
|
1029
1123
|
var DEFAULT_ROW_DIMENSION_WIDTH2 = 140;
|
|
1030
1124
|
var SCROLLBAR_THICKNESS2 = 10;
|
|
1031
1125
|
function buildPivotLayoutSnapshot(params) {
|
|
@@ -1057,7 +1151,7 @@ function buildPivotLayoutSnapshot(params) {
|
|
|
1057
1151
|
options.columns.length + (indicatorLayout === "columns" && hasIndicators ? 1 : 0),
|
|
1058
1152
|
1
|
|
1059
1153
|
);
|
|
1060
|
-
const columnHeaderHeight = columnHeaderLevels *
|
|
1154
|
+
const columnHeaderHeight = columnHeaderLevels * rowHeight;
|
|
1061
1155
|
const reportFilterHeight = (options.filterDimensions?.length ?? 0) * rowHeight;
|
|
1062
1156
|
const tableHeaderHeight = reportFilterHeight + columnHeaderHeight;
|
|
1063
1157
|
const rawBodyWidth = Math.max(width - rowHeaderWidth, 0);
|
|
@@ -1191,6 +1285,7 @@ function buildPivotLayoutSnapshot(params) {
|
|
|
1191
1285
|
rowHeaderWidth,
|
|
1192
1286
|
scrollLeft: scroll.left,
|
|
1193
1287
|
levels: columnHeaderLevels,
|
|
1288
|
+
headerSize: rowHeight,
|
|
1194
1289
|
headerTop: reportFilterHeight,
|
|
1195
1290
|
expandedNodeIds: params.expandedColumnNodeIds
|
|
1196
1291
|
});
|
|
@@ -1591,7 +1686,7 @@ function buildColumnHeaderCells(params) {
|
|
|
1591
1686
|
x,
|
|
1592
1687
|
y: params.headerTop,
|
|
1593
1688
|
width: columnWidth,
|
|
1594
|
-
height:
|
|
1689
|
+
height: params.headerSize * (params.levels - (slot.indicatorKey ? 1 : 0))
|
|
1595
1690
|
}
|
|
1596
1691
|
});
|
|
1597
1692
|
} else {
|
|
@@ -1631,9 +1726,9 @@ function buildColumnHeaderCells(params) {
|
|
|
1631
1726
|
),
|
|
1632
1727
|
rect: {
|
|
1633
1728
|
x,
|
|
1634
|
-
y: params.headerTop + level *
|
|
1729
|
+
y: params.headerTop + level * params.headerSize,
|
|
1635
1730
|
width: columnWidth,
|
|
1636
|
-
height:
|
|
1731
|
+
height: params.headerSize
|
|
1637
1732
|
}
|
|
1638
1733
|
});
|
|
1639
1734
|
});
|
|
@@ -1660,9 +1755,9 @@ function buildColumnHeaderCells(params) {
|
|
|
1660
1755
|
sortDirection: null,
|
|
1661
1756
|
rect: {
|
|
1662
1757
|
x,
|
|
1663
|
-
y: params.headerTop + (params.levels - 1) *
|
|
1758
|
+
y: params.headerTop + (params.levels - 1) * params.headerSize,
|
|
1664
1759
|
width: columnWidth,
|
|
1665
|
-
height:
|
|
1760
|
+
height: params.headerSize
|
|
1666
1761
|
}
|
|
1667
1762
|
});
|
|
1668
1763
|
}
|
|
@@ -2699,6 +2794,15 @@ function resolvePivotSelectionRect(layout, selection) {
|
|
|
2699
2794
|
const visibleStartY = Math.max(startY, clipRect.y);
|
|
2700
2795
|
const visibleEndY = Math.min(endY, clipRect.y + clipRect.height);
|
|
2701
2796
|
if (visibleStartX >= visibleEndX || visibleStartY >= visibleEndY) return null;
|
|
2797
|
+
const singleCell = kind === "cell" && indexes.startRowIndex === indexes.endRowIndex && indexes.startColumnIndex === indexes.endColumnIndex;
|
|
2798
|
+
if (singleCell) {
|
|
2799
|
+
return {
|
|
2800
|
+
x: startX,
|
|
2801
|
+
y: startY,
|
|
2802
|
+
width: endX - startX,
|
|
2803
|
+
height: endY - startY
|
|
2804
|
+
};
|
|
2805
|
+
}
|
|
2702
2806
|
return {
|
|
2703
2807
|
x: visibleStartX,
|
|
2704
2808
|
y: visibleStartY,
|
|
@@ -3251,8 +3355,16 @@ function renderPivotTable(params) {
|
|
|
3251
3355
|
);
|
|
3252
3356
|
layout.columnHeaderCells.forEach(
|
|
3253
3357
|
(cell) => drawPivotGridRect(context, cell.rect, theme.borderColor, {
|
|
3254
|
-
skipTop:
|
|
3255
|
-
|
|
3358
|
+
skipTop: hasColumnHeaderCellAbove(
|
|
3359
|
+
layout.columnHeaderCells,
|
|
3360
|
+
cell,
|
|
3361
|
+
layout.columnHeaderRect.y
|
|
3362
|
+
),
|
|
3363
|
+
skipLeft: hasColumnHeaderCellLeft(
|
|
3364
|
+
layout.columnHeaderCells,
|
|
3365
|
+
cell,
|
|
3366
|
+
layout.columnHeaderRect.x
|
|
3367
|
+
),
|
|
3256
3368
|
skipRight: isSameCoordinate2(
|
|
3257
3369
|
cell.rect.x + cell.rect.width,
|
|
3258
3370
|
layout.tableWidth
|
|
@@ -3322,10 +3434,12 @@ function renderPivotTable(params) {
|
|
|
3322
3434
|
drawPivotResizeGuide(context, params.resizeGuideX, contentHeight);
|
|
3323
3435
|
}
|
|
3324
3436
|
context.restore();
|
|
3325
|
-
|
|
3326
|
-
|
|
3327
|
-
|
|
3328
|
-
|
|
3437
|
+
if (!layout.scrollbar.vertical) {
|
|
3438
|
+
context.save();
|
|
3439
|
+
clipPivotViewport(context, tableViewportRect);
|
|
3440
|
+
drawPivotViewportRightBorder(context, tableViewportRect, theme.borderColor);
|
|
3441
|
+
context.restore();
|
|
3442
|
+
}
|
|
3329
3443
|
drawPivotViewportBorder(
|
|
3330
3444
|
context,
|
|
3331
3445
|
{ x: 0, y: 0, width: viewportWidth, height: contentHeight },
|
|
@@ -3354,6 +3468,34 @@ function renderPivotTable(params) {
|
|
|
3354
3468
|
bodyCellCount: layout.bodyCells.length
|
|
3355
3469
|
};
|
|
3356
3470
|
}
|
|
3471
|
+
function hasColumnHeaderCellAbove(cells, cell, headerTop) {
|
|
3472
|
+
if (isSameCoordinate2(cell.rect.y, headerTop)) return headerTop > 0;
|
|
3473
|
+
const left = cell.rect.x;
|
|
3474
|
+
const right = left + cell.rect.width;
|
|
3475
|
+
return cells.some((candidate) => {
|
|
3476
|
+
if (candidate === cell) return false;
|
|
3477
|
+
const candidateBottom = candidate.rect.y + candidate.rect.height;
|
|
3478
|
+
if (!isSameCoordinate2(candidateBottom, cell.rect.y)) return false;
|
|
3479
|
+
const candidateLeft = candidate.rect.x;
|
|
3480
|
+
const candidateRight = candidateLeft + candidate.rect.width;
|
|
3481
|
+
return candidateLeft < right && candidateRight > left;
|
|
3482
|
+
});
|
|
3483
|
+
}
|
|
3484
|
+
function hasColumnHeaderCellLeft(cells, cell, headerLeft) {
|
|
3485
|
+
if (cell.rect.x <= headerLeft + 0.01) {
|
|
3486
|
+
return true;
|
|
3487
|
+
}
|
|
3488
|
+
const top = cell.rect.y;
|
|
3489
|
+
const bottom = top + cell.rect.height;
|
|
3490
|
+
return cells.some((candidate) => {
|
|
3491
|
+
if (candidate === cell) return false;
|
|
3492
|
+
const candidateRight = candidate.rect.x + candidate.rect.width;
|
|
3493
|
+
if (!isSameCoordinate2(candidateRight, cell.rect.x)) return false;
|
|
3494
|
+
const candidateTop = candidate.rect.y;
|
|
3495
|
+
const candidateBottom = candidateTop + candidate.rect.height;
|
|
3496
|
+
return candidateTop < bottom && candidateBottom > top;
|
|
3497
|
+
});
|
|
3498
|
+
}
|
|
3357
3499
|
function resolvePivotViewportWidth(layout) {
|
|
3358
3500
|
const horizontalRight = layout.scrollbar.horizontal ? layout.scrollbar.horizontal.increaseButton.x + layout.scrollbar.horizontal.increaseButton.width : 0;
|
|
3359
3501
|
const verticalRight = layout.scrollbar.vertical ? layout.scrollbar.vertical.decreaseButton.x + layout.scrollbar.vertical.decreaseButton.width : 0;
|
|
@@ -3656,7 +3798,7 @@ function isSameCoordinate2(first, second) {
|
|
|
3656
3798
|
return Math.abs(first - second) < 0.01;
|
|
3657
3799
|
}
|
|
3658
3800
|
function resolveBodyDisplayText(cell, indicator) {
|
|
3659
|
-
if (!indicator || indicator.formatter || typeof cell.value !== "number") {
|
|
3801
|
+
if (!indicator || indicator.formatter || indicator.dataFormat || typeof cell.value !== "number") {
|
|
3660
3802
|
return cell.formattedValue;
|
|
3661
3803
|
}
|
|
3662
3804
|
if (indicator.cellType === "percent") {
|
|
@@ -3793,10 +3935,18 @@ function drawSortIcon(context, rect, direction, rightPadding) {
|
|
|
3793
3935
|
function drawText(context, text, rect, color, padding, align) {
|
|
3794
3936
|
const availableWidth = Math.max(rect.width - padding * 2, 0);
|
|
3795
3937
|
const value = truncateText(context, text, availableWidth);
|
|
3938
|
+
context.textBaseline = "alphabetic";
|
|
3939
|
+
const metrics = context.measureText(value);
|
|
3940
|
+
const ascent = metrics.actualBoundingBoxAscent ?? 0;
|
|
3941
|
+
const descent = metrics.actualBoundingBoxDescent ?? 0;
|
|
3942
|
+
const hasGlyphBounds = ascent > 0 || descent > 0;
|
|
3943
|
+
const centerY = rect.y + rect.height / 2;
|
|
3796
3944
|
context.fillStyle = color;
|
|
3797
3945
|
context.textAlign = align;
|
|
3946
|
+
context.textBaseline = hasGlyphBounds ? "alphabetic" : "middle";
|
|
3798
3947
|
const x = align === "right" ? rect.x + rect.width - padding : align === "center" ? rect.x + rect.width / 2 : rect.x + padding;
|
|
3799
|
-
|
|
3948
|
+
const y = hasGlyphBounds ? centerY + (ascent - descent) / 2 : centerY;
|
|
3949
|
+
context.fillText(value, x, y, availableWidth);
|
|
3800
3950
|
}
|
|
3801
3951
|
function truncateText(context, text, maxWidth) {
|
|
3802
3952
|
if (context.measureText(text).width <= maxWidth) return text;
|
|
@@ -3844,7 +3994,7 @@ function hitTestPivotLayout(layout, x, y) {
|
|
|
3844
3994
|
onSortIcon: false
|
|
3845
3995
|
};
|
|
3846
3996
|
}
|
|
3847
|
-
const bodyCell = findLastContaining(layout.bodyCells, x, y);
|
|
3997
|
+
const bodyCell = contains(layout.bodyRect, x, y) ? findLastContaining(layout.bodyCells, x, y) : void 0;
|
|
3848
3998
|
if (bodyCell) {
|
|
3849
3999
|
return {
|
|
3850
4000
|
region: "body",
|
|
@@ -3854,7 +4004,7 @@ function hitTestPivotLayout(layout, x, y) {
|
|
|
3854
4004
|
onSortIcon: false
|
|
3855
4005
|
};
|
|
3856
4006
|
}
|
|
3857
|
-
const rowHeaderCell = findLastContaining(layout.rowHeaderCells, x, y);
|
|
4007
|
+
const rowHeaderCell = contains(layout.rowHeaderRect, x, y) ? findLastContaining(layout.rowHeaderCells, x, y) : void 0;
|
|
3858
4008
|
if (rowHeaderCell) {
|
|
3859
4009
|
return {
|
|
3860
4010
|
region: "rowHeader",
|
|
@@ -3864,9 +4014,7 @@ function hitTestPivotLayout(layout, x, y) {
|
|
|
3864
4014
|
onSortIcon: isSortIconHit(rowHeaderCell, x)
|
|
3865
4015
|
};
|
|
3866
4016
|
}
|
|
3867
|
-
const columnHeaderCell = layout.columnHeaderCells.find(
|
|
3868
|
-
(cell) => contains(cell.rect, x, y)
|
|
3869
|
-
);
|
|
4017
|
+
const columnHeaderCell = contains(layout.columnHeaderRect, x, y) ? layout.columnHeaderCells.find((cell) => contains(cell.rect, x, y)) : void 0;
|
|
3870
4018
|
if (columnHeaderCell) {
|
|
3871
4019
|
return {
|
|
3872
4020
|
region: "columnHeader",
|
|
@@ -4382,7 +4530,7 @@ var PivotTable = class {
|
|
|
4382
4530
|
}
|
|
4383
4531
|
if (hit.reportFilterCell) return;
|
|
4384
4532
|
if (hit.bodyCell) {
|
|
4385
|
-
this.
|
|
4533
|
+
this.selectPointerCell(toCellAddress(hit.bodyCell), event.shiftKey);
|
|
4386
4534
|
return;
|
|
4387
4535
|
}
|
|
4388
4536
|
const header = hit.headerCell;
|
|
@@ -4484,7 +4632,76 @@ var PivotTable = class {
|
|
|
4484
4632
|
});
|
|
4485
4633
|
__publicField(this, "handleContextMenu", (event) => {
|
|
4486
4634
|
const hit = this.resolveHit(event);
|
|
4487
|
-
if (!hit
|
|
4635
|
+
if (!hit || !this.core || !this.layout) return;
|
|
4636
|
+
if (hit.reportFilterCell) {
|
|
4637
|
+
const cell = hit.reportFilterCell;
|
|
4638
|
+
event.preventDefault();
|
|
4639
|
+
this.options.ui?.onContextMenuRequest?.({
|
|
4640
|
+
x: event.clientX,
|
|
4641
|
+
y: event.clientY,
|
|
4642
|
+
target: "header",
|
|
4643
|
+
title: cell.label,
|
|
4644
|
+
axis: "filter",
|
|
4645
|
+
onCopy: () => this.copyPivotText(`${cell.label} ${cell.valueLabel}`),
|
|
4646
|
+
onOpenFilter: () => this.openReportFilterPanel(cell),
|
|
4647
|
+
...this.createContextMenuSettings(),
|
|
4648
|
+
onExportCsv: () => downloadBlob(
|
|
4649
|
+
new Blob([this.exportCsv()], { type: "text/csv;charset=utf-8" }),
|
|
4650
|
+
"pivot-table.csv"
|
|
4651
|
+
),
|
|
4652
|
+
onExportXlsx: () => downloadBlob(
|
|
4653
|
+
new Blob([toArrayBuffer(this.exportXlsx())], {
|
|
4654
|
+
type: "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"
|
|
4655
|
+
}),
|
|
4656
|
+
"pivot-table.xlsx"
|
|
4657
|
+
),
|
|
4658
|
+
onClose: () => this.options.ui?.onContextMenuRequest?.(null)
|
|
4659
|
+
});
|
|
4660
|
+
return;
|
|
4661
|
+
}
|
|
4662
|
+
if (hit.headerCell) {
|
|
4663
|
+
const header = hit.headerCell;
|
|
4664
|
+
const model2 = this.core.getPivotModel();
|
|
4665
|
+
const node = header.nodeId ? (header.axis === "row" ? model2?.rowNodes : model2?.columnNodes)?.get(
|
|
4666
|
+
header.nodeId
|
|
4667
|
+
) : void 0;
|
|
4668
|
+
const dimensionKey = header.dimensionKey ?? node?.dimensionKey;
|
|
4669
|
+
event.preventDefault();
|
|
4670
|
+
this.options.ui?.onContextMenuRequest?.({
|
|
4671
|
+
x: event.clientX,
|
|
4672
|
+
y: event.clientY,
|
|
4673
|
+
target: "header",
|
|
4674
|
+
title: header.label,
|
|
4675
|
+
axis: header.axis,
|
|
4676
|
+
nodeKind: node?.kind ?? header.kind,
|
|
4677
|
+
indicatorKey: header.indicatorKey ?? void 0,
|
|
4678
|
+
onCopy: () => this.copyPivotText(header.label),
|
|
4679
|
+
sortDirection: header.sortDirection,
|
|
4680
|
+
onSortAsc: header.sortable && dimensionKey ? () => this.setDimensionSort(header.axis, dimensionKey, "asc") : void 0,
|
|
4681
|
+
onSortDesc: header.sortable && dimensionKey ? () => this.setDimensionSort(header.axis, dimensionKey, "desc") : void 0,
|
|
4682
|
+
onClearSort: header.sortable && dimensionKey ? () => this.setDimensionSort(header.axis, dimensionKey, null) : void 0,
|
|
4683
|
+
onOpenFilter: header.filterable ? () => this.openFilterPanel(hit) : void 0,
|
|
4684
|
+
expanded: header.expandable ? header.expanded : void 0,
|
|
4685
|
+
onExpand: header.expandable && header.nodeId ? () => header.axis === "row" ? this.expandRowNode(header.nodeId) : this.expandColumnNode(header.nodeId) : void 0,
|
|
4686
|
+
onCollapse: header.expandable && header.nodeId ? () => header.axis === "row" ? this.collapseRowNode(header.nodeId) : this.collapseColumnNode(header.nodeId) : void 0,
|
|
4687
|
+
onExpandAll: () => this.expandAll(header.axis),
|
|
4688
|
+
onCollapseAll: () => this.collapseAll(header.axis),
|
|
4689
|
+
...this.createContextMenuSettings(header.indicatorKey ?? void 0),
|
|
4690
|
+
onExportCsv: () => downloadBlob(
|
|
4691
|
+
new Blob([this.exportCsv()], { type: "text/csv;charset=utf-8" }),
|
|
4692
|
+
"pivot-table.csv"
|
|
4693
|
+
),
|
|
4694
|
+
onExportXlsx: () => downloadBlob(
|
|
4695
|
+
new Blob([toArrayBuffer(this.exportXlsx())], {
|
|
4696
|
+
type: "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"
|
|
4697
|
+
}),
|
|
4698
|
+
"pivot-table.xlsx"
|
|
4699
|
+
),
|
|
4700
|
+
onClose: () => this.options.ui?.onContextMenuRequest?.(null)
|
|
4701
|
+
});
|
|
4702
|
+
return;
|
|
4703
|
+
}
|
|
4704
|
+
if (!hit.bodyCell) return;
|
|
4488
4705
|
event.preventDefault();
|
|
4489
4706
|
const address = toCellAddress(hit.bodyCell);
|
|
4490
4707
|
const currentRange = resolvePivotSelectionRange(
|
|
@@ -4494,7 +4711,7 @@ var PivotTable = class {
|
|
|
4494
4711
|
const insideCurrentRange = Boolean(
|
|
4495
4712
|
currentRange && hit.bodyCell.rowIndex >= currentRange.startRowIndex && hit.bodyCell.rowIndex <= currentRange.endRowIndex && hit.bodyCell.columnIndex >= currentRange.startColumnIndex && hit.bodyCell.columnIndex <= currentRange.endColumnIndex
|
|
4496
4713
|
);
|
|
4497
|
-
if (!insideCurrentRange) this.
|
|
4714
|
+
if (!insideCurrentRange) this.selectPointerCell(address);
|
|
4498
4715
|
const selectionRange = resolvePivotSelectionRange(
|
|
4499
4716
|
this.layout,
|
|
4500
4717
|
this.core.getSelection()
|
|
@@ -4504,15 +4721,28 @@ var PivotTable = class {
|
|
|
4504
4721
|
columnPathKey: hit.bodyCell.columnPathKey,
|
|
4505
4722
|
indicatorKey: hit.bodyCell.indicatorKey
|
|
4506
4723
|
});
|
|
4724
|
+
const model = this.core.getPivotModel();
|
|
4725
|
+
const rowNode = hit.bodyCell.rowNodeId ? model?.rowNodes.get(hit.bodyCell.rowNodeId) : void 0;
|
|
4726
|
+
const summary = rowNode?.kind === "subtotal" || rowNode?.kind === "grandTotal";
|
|
4727
|
+
const indicator = this.options.indicators.find(
|
|
4728
|
+
(item) => item.key === hit.bodyCell?.indicatorKey
|
|
4729
|
+
);
|
|
4730
|
+
const summaryAggregator = typeof indicator?.aggregator === "string" && isBuiltInPivotAggregator(indicator.aggregator) ? indicator.aggregator : "sum";
|
|
4507
4731
|
this.options.ui?.onContextMenuRequest?.({
|
|
4508
4732
|
x: event.clientX,
|
|
4509
4733
|
y: event.clientY,
|
|
4734
|
+
target: summary ? "summary" : "body",
|
|
4735
|
+
title: summary ? rowNode.label : void 0,
|
|
4736
|
+
nodeKind: rowNode?.kind,
|
|
4737
|
+
indicatorKey: hit.bodyCell.indicatorKey,
|
|
4510
4738
|
recordCount: records.length,
|
|
4739
|
+
summaryAggregator: summary ? summaryAggregator : void 0,
|
|
4740
|
+
summaryPrecision: summary ? indicator?.precision : void 0,
|
|
4511
4741
|
onCopy: () => {
|
|
4512
|
-
const
|
|
4513
|
-
const text =
|
|
4742
|
+
const model2 = this.core?.getPivotModel();
|
|
4743
|
+
const text = model2 ? createPivotRangeSelectionText({
|
|
4514
4744
|
layout: this.layout,
|
|
4515
|
-
model,
|
|
4745
|
+
model: model2,
|
|
4516
4746
|
startRowIndex: hit.bodyCell.rowIndex,
|
|
4517
4747
|
endRowIndex: hit.bodyCell.rowIndex,
|
|
4518
4748
|
startColumnIndex: hit.bodyCell.columnIndex,
|
|
@@ -4537,11 +4767,11 @@ var PivotTable = class {
|
|
|
4537
4767
|
columnCount: this.layout.columnSlots.length
|
|
4538
4768
|
},
|
|
4539
4769
|
onCopyCustomSelection: (range) => {
|
|
4540
|
-
const
|
|
4541
|
-
if (!
|
|
4770
|
+
const model2 = this.core?.getPivotModel();
|
|
4771
|
+
if (!model2 || !this.layout) return;
|
|
4542
4772
|
const text = createPivotRangeSelectionText({
|
|
4543
4773
|
layout: this.layout,
|
|
4544
|
-
model,
|
|
4774
|
+
model: model2,
|
|
4545
4775
|
startRowIndex: range.startRow - 1,
|
|
4546
4776
|
endRowIndex: range.endRow - 1,
|
|
4547
4777
|
startColumnIndex: range.startColumn - 1,
|
|
@@ -4551,6 +4781,7 @@ var PivotTable = class {
|
|
|
4551
4781
|
this.copyPivotText(text);
|
|
4552
4782
|
}
|
|
4553
4783
|
},
|
|
4784
|
+
...this.createContextMenuSettings(hit.bodyCell.indicatorKey),
|
|
4554
4785
|
onExportCsv: () => downloadBlob(
|
|
4555
4786
|
new Blob([this.exportCsv()], { type: "text/csv;charset=utf-8" }),
|
|
4556
4787
|
"pivot-table.csv"
|
|
@@ -4566,9 +4797,9 @@ var PivotTable = class {
|
|
|
4566
4797
|
});
|
|
4567
4798
|
__publicField(this, "handlePointerDown", (event) => {
|
|
4568
4799
|
if (!this.layout || !this.host) return;
|
|
4569
|
-
this.stopWheelScrollSmoothing();
|
|
4570
4800
|
const scrollbarHit = this.resolveScrollbarHit(event);
|
|
4571
4801
|
if (scrollbarHit && event.button === 0) {
|
|
4802
|
+
this.stopWheelScrollSmoothing();
|
|
4572
4803
|
this.beginScrollbarInteraction(event, scrollbarHit);
|
|
4573
4804
|
return;
|
|
4574
4805
|
}
|
|
@@ -4579,7 +4810,7 @@ var PivotTable = class {
|
|
|
4579
4810
|
if (cellHit?.bodyCell) {
|
|
4580
4811
|
const address = toCellAddress(cellHit.bodyCell);
|
|
4581
4812
|
this.host.root.focus({ preventScroll: true });
|
|
4582
|
-
const selection = this.
|
|
4813
|
+
const selection = this.selectPointerCell(address, event.shiftKey);
|
|
4583
4814
|
const text = this.resolveBodyText(cellHit.bodyCell);
|
|
4584
4815
|
const textOriginX = this.resolveBodyTextOriginX(cellHit.bodyCell, text);
|
|
4585
4816
|
const anchorOffset = text ? resolvePivotTextOffset({
|
|
@@ -4665,8 +4896,12 @@ var PivotTable = class {
|
|
|
4665
4896
|
if (selection) this.setSelection(selection);
|
|
4666
4897
|
this.host.root.setPointerCapture(event.pointerId);
|
|
4667
4898
|
}
|
|
4899
|
+
if (cellHit?.headerCell || cellHit?.reportFilterCell) {
|
|
4900
|
+
event.preventDefault();
|
|
4901
|
+
}
|
|
4668
4902
|
return;
|
|
4669
4903
|
}
|
|
4904
|
+
this.stopWheelScrollSmoothing();
|
|
4670
4905
|
event.preventDefault();
|
|
4671
4906
|
const startWidth = hit.kind === "rowDimension" ? this.layout.rowDimensionWidths[hit.index] ?? 140 : this.layout.columnWidths[hit.index] ?? this.layout.columnWidth;
|
|
4672
4907
|
this.resizeSession = {
|
|
@@ -4856,9 +5091,16 @@ var PivotTable = class {
|
|
|
4856
5091
|
}
|
|
4857
5092
|
/** 选中指定值单元格。 */
|
|
4858
5093
|
selectCell(address, extend = false) {
|
|
5094
|
+
return this.commitCellSelection(address, extend, true);
|
|
5095
|
+
}
|
|
5096
|
+
/** 鼠标命中的单元格已在视口内,选中时不再校正滚动位置。 */
|
|
5097
|
+
selectPointerCell(address, extend = false) {
|
|
5098
|
+
return this.commitCellSelection(address, extend, false);
|
|
5099
|
+
}
|
|
5100
|
+
commitCellSelection(address, extend, reveal) {
|
|
4859
5101
|
const selection = this.core?.selectCell(address, extend) ?? null;
|
|
4860
5102
|
if (selection) {
|
|
4861
|
-
this.scrollToCell(address);
|
|
5103
|
+
if (reveal) this.scrollToCell(address);
|
|
4862
5104
|
this.scheduleRender();
|
|
4863
5105
|
}
|
|
4864
5106
|
return selection;
|
|
@@ -5016,6 +5258,27 @@ var PivotTable = class {
|
|
|
5016
5258
|
this.advanceWheelScrollSmoothing(nextTime);
|
|
5017
5259
|
});
|
|
5018
5260
|
}
|
|
5261
|
+
createContextMenuSettings(indicatorKey) {
|
|
5262
|
+
const indicator = indicatorKey ? this.options.indicators.find((item) => item.key === indicatorKey) : void 0;
|
|
5263
|
+
return {
|
|
5264
|
+
density: resolvePivotTableDensity(this.options.rowHeight),
|
|
5265
|
+
onChangeDensity: (density) => {
|
|
5266
|
+
this.updateOptions({
|
|
5267
|
+
...this.options,
|
|
5268
|
+
rowHeight: resolvePivotTableDensityRowHeight(density)
|
|
5269
|
+
});
|
|
5270
|
+
},
|
|
5271
|
+
dataFormat: indicator?.dataFormat ?? "default",
|
|
5272
|
+
onChangeDataFormat: indicator ? (dataFormat) => {
|
|
5273
|
+
this.updateOptions({
|
|
5274
|
+
...this.options,
|
|
5275
|
+
indicators: this.options.indicators.map(
|
|
5276
|
+
(item) => item.key === indicator.key ? { ...item, dataFormat } : item
|
|
5277
|
+
)
|
|
5278
|
+
});
|
|
5279
|
+
} : void 0
|
|
5280
|
+
};
|
|
5281
|
+
}
|
|
5019
5282
|
updateTooltip(hit, event) {
|
|
5020
5283
|
if (!this.host) return;
|
|
5021
5284
|
const cell = hit?.bodyCell ?? hit?.headerCell;
|
|
@@ -5221,7 +5484,7 @@ var PivotTable = class {
|
|
|
5221
5484
|
const indicator = this.options.indicators.find(
|
|
5222
5485
|
(item) => item.key === cell.indicatorKey
|
|
5223
5486
|
);
|
|
5224
|
-
if (!indicator || indicator.formatter || typeof cell.value !== "number")
|
|
5487
|
+
if (!indicator || indicator.formatter || indicator.dataFormat || typeof cell.value !== "number")
|
|
5225
5488
|
return cell.formattedValue;
|
|
5226
5489
|
if (indicator.cellType === "percent")
|
|
5227
5490
|
return `${(cell.value * 100).toFixed(indicator.precision ?? 2)}%`;
|
|
@@ -5583,6 +5846,9 @@ function toCellAddress(cell) {
|
|
|
5583
5846
|
indicatorKey: cell.indicatorKey
|
|
5584
5847
|
};
|
|
5585
5848
|
}
|
|
5849
|
+
function isBuiltInPivotAggregator(value) {
|
|
5850
|
+
return ["sum", "avg", "count", "distinctCount", "min", "max"].includes(value);
|
|
5851
|
+
}
|
|
5586
5852
|
function isSameAddress(left, right) {
|
|
5587
5853
|
return Boolean(
|
|
5588
5854
|
left === right || left && right && left.rowNodeId === right.rowNodeId && left.columnNodeId === right.columnNodeId && left.indicatorKey === right.indicatorKey
|
|
@@ -5595,6 +5861,20 @@ function resolveNavigationDirection(key) {
|
|
|
5595
5861
|
if (key === "ArrowRight") return { row: 0, column: 1 };
|
|
5596
5862
|
return null;
|
|
5597
5863
|
}
|
|
5864
|
+
function resolvePivotTableDensity(rowHeight) {
|
|
5865
|
+
if (rowHeight == null) return "auto";
|
|
5866
|
+
if (rowHeight <= 26) return "ultra-compact";
|
|
5867
|
+
if (rowHeight <= 32) return "compact";
|
|
5868
|
+
if (rowHeight >= 56) return "comfortable";
|
|
5869
|
+
return "middle";
|
|
5870
|
+
}
|
|
5871
|
+
function resolvePivotTableDensityRowHeight(density) {
|
|
5872
|
+
if (density === "comfortable") return 56;
|
|
5873
|
+
if (density === "middle") return 48;
|
|
5874
|
+
if (density === "compact") return 32;
|
|
5875
|
+
if (density === "ultra-compact") return 26;
|
|
5876
|
+
return void 0;
|
|
5877
|
+
}
|
|
5598
5878
|
function resolveAdjacentAddress(layout, address, direction) {
|
|
5599
5879
|
const indexes = resolveAddressIndexes3(layout, address);
|
|
5600
5880
|
if (!indexes) return null;
|