@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 +485 -61
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +73 -23
- package/dist/index.d.ts +73 -23
- package/dist/index.js +484 -63
- 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, 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
|
|
|
@@ -309,8 +309,6 @@ function indexPivotNodes(root) {
|
|
|
309
309
|
visit(root);
|
|
310
310
|
return nodeMap;
|
|
311
311
|
}
|
|
312
|
-
|
|
313
|
-
// src/domain/aggregation/built-in-aggregators.ts
|
|
314
312
|
function createBuiltInPivotAggregators() {
|
|
315
313
|
return {
|
|
316
314
|
sum: createNumericAggregator("sum"),
|
|
@@ -341,14 +339,14 @@ function createNumericAggregator(type) {
|
|
|
341
339
|
return;
|
|
342
340
|
}
|
|
343
341
|
state.numericCount += 1;
|
|
344
|
-
state.sum
|
|
342
|
+
state.sum = addPreciseNumbers(state.sum, numericValue) ?? state.sum;
|
|
345
343
|
state.min = state.min == null ? numericValue : Math.min(state.min, numericValue);
|
|
346
344
|
state.max = state.max == null ? numericValue : Math.max(state.max, numericValue);
|
|
347
345
|
},
|
|
348
346
|
merge(state, source) {
|
|
349
347
|
state.count += source.count;
|
|
350
348
|
state.numericCount += source.numericCount;
|
|
351
|
-
state.sum
|
|
349
|
+
state.sum = addPreciseNumbers(state.sum, source.sum) ?? state.sum;
|
|
352
350
|
state.min = mergeMinimum(state.min, source.min);
|
|
353
351
|
state.max = mergeMaximum(state.max, source.max);
|
|
354
352
|
source.distinctValues.forEach((value) => state.distinctValues.add(value));
|
|
@@ -356,9 +354,11 @@ function createNumericAggregator(type) {
|
|
|
356
354
|
finalize(state) {
|
|
357
355
|
if (type === "count") return state.count;
|
|
358
356
|
if (type === "distinctCount") return state.distinctValues.size;
|
|
359
|
-
if (type === "sum") return state.sum;
|
|
357
|
+
if (type === "sum") return Number(state.sum);
|
|
360
358
|
if (type === "avg") {
|
|
361
|
-
|
|
359
|
+
if (state.numericCount === 0) return null;
|
|
360
|
+
const average = dividePreciseNumber(state.sum, state.numericCount);
|
|
361
|
+
return average == null ? null : Number(average);
|
|
362
362
|
}
|
|
363
363
|
if (type === "min") return state.min;
|
|
364
364
|
return state.max;
|
|
@@ -369,7 +369,7 @@ function createAccumulator() {
|
|
|
369
369
|
return {
|
|
370
370
|
count: 0,
|
|
371
371
|
numericCount: 0,
|
|
372
|
-
sum: 0,
|
|
372
|
+
sum: "0",
|
|
373
373
|
min: null,
|
|
374
374
|
max: null,
|
|
375
375
|
distinctValues: /* @__PURE__ */ new Set()
|
|
@@ -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,
|
|
@@ -2748,18 +2852,50 @@ function resolvePivotSelectionKind(selection) {
|
|
|
2748
2852
|
}
|
|
2749
2853
|
return selection?.activeNode?.axis ?? "cell";
|
|
2750
2854
|
}
|
|
2751
|
-
function drawPivotSelectionBorder(context, rect, color) {
|
|
2855
|
+
function drawPivotSelectionBorder(context, rect, color, options) {
|
|
2752
2856
|
context.save();
|
|
2753
2857
|
context.strokeStyle = color;
|
|
2754
2858
|
context.lineWidth = 1;
|
|
2755
|
-
|
|
2756
|
-
|
|
2757
|
-
|
|
2758
|
-
|
|
2759
|
-
|
|
2760
|
-
|
|
2859
|
+
const left = rect.x + 0.5;
|
|
2860
|
+
const top = rect.y + 0.5;
|
|
2861
|
+
const width = Math.max(rect.width - 1, 0);
|
|
2862
|
+
const height = Math.max(rect.height - 1, 0);
|
|
2863
|
+
const right = left + width;
|
|
2864
|
+
const bottom = top + height;
|
|
2865
|
+
const viewport = options?.viewportRect;
|
|
2866
|
+
const radius = Math.min(options?.radius ?? 8, width / 2, height / 2);
|
|
2867
|
+
const topLeft = viewport && isSameCoordinate(rect.x, viewport.x) && isSameCoordinate(rect.y, viewport.y) ? radius : 0;
|
|
2868
|
+
const topRight = viewport && isSameCoordinate(rect.x + rect.width, viewport.x + viewport.width) && isSameCoordinate(rect.y, viewport.y) ? radius : 0;
|
|
2869
|
+
const bottomRight = viewport && isSameCoordinate(rect.x + rect.width, viewport.x + viewport.width) && isSameCoordinate(rect.y + rect.height, viewport.y + viewport.height) ? radius : 0;
|
|
2870
|
+
const bottomLeft = viewport && isSameCoordinate(rect.x, viewport.x) && isSameCoordinate(rect.y + rect.height, viewport.y + viewport.height) ? radius : 0;
|
|
2871
|
+
if (!topLeft && !topRight && !bottomRight && !bottomLeft) {
|
|
2872
|
+
context.strokeRect(left, top, width, height);
|
|
2873
|
+
context.restore();
|
|
2874
|
+
return;
|
|
2875
|
+
}
|
|
2876
|
+
context.beginPath();
|
|
2877
|
+
context.moveTo(left + topLeft, top);
|
|
2878
|
+
context.lineTo(right - topRight, top);
|
|
2879
|
+
if (topRight) context.arcTo(right, top, right, top + topRight, topRight);
|
|
2880
|
+
else context.lineTo(right, top);
|
|
2881
|
+
context.lineTo(right, bottom - bottomRight);
|
|
2882
|
+
if (bottomRight) {
|
|
2883
|
+
context.arcTo(right, bottom, right - bottomRight, bottom, bottomRight);
|
|
2884
|
+
} else context.lineTo(right, bottom);
|
|
2885
|
+
context.lineTo(left + bottomLeft, bottom);
|
|
2886
|
+
if (bottomLeft) {
|
|
2887
|
+
context.arcTo(left, bottom, left, bottom - bottomLeft, bottomLeft);
|
|
2888
|
+
} else context.lineTo(left, bottom);
|
|
2889
|
+
context.lineTo(left, top + topLeft);
|
|
2890
|
+
if (topLeft) context.arcTo(left, top, left + topLeft, top, topLeft);
|
|
2891
|
+
else context.lineTo(left, top);
|
|
2892
|
+
context.closePath();
|
|
2893
|
+
context.stroke();
|
|
2761
2894
|
context.restore();
|
|
2762
2895
|
}
|
|
2896
|
+
function isSameCoordinate(first, second) {
|
|
2897
|
+
return Math.abs(first - second) < 0.01;
|
|
2898
|
+
}
|
|
2763
2899
|
function resolveSelectionIndexes(layout, selection) {
|
|
2764
2900
|
const range = selection?.ranges[0];
|
|
2765
2901
|
if (range) {
|
|
@@ -3056,6 +3192,17 @@ function drawPivotGridRect(context, rect, color, options) {
|
|
|
3056
3192
|
context.stroke();
|
|
3057
3193
|
context.restore();
|
|
3058
3194
|
}
|
|
3195
|
+
function drawPivotViewportRightBorder(context, rect, color) {
|
|
3196
|
+
const x = rect.x + rect.width - 0.5;
|
|
3197
|
+
context.save();
|
|
3198
|
+
context.strokeStyle = color;
|
|
3199
|
+
context.lineWidth = 1;
|
|
3200
|
+
context.beginPath();
|
|
3201
|
+
context.moveTo(x, rect.y);
|
|
3202
|
+
context.lineTo(x, rect.y + rect.height);
|
|
3203
|
+
context.stroke();
|
|
3204
|
+
context.restore();
|
|
3205
|
+
}
|
|
3059
3206
|
function clipPivotViewport(context, rect, radius = 8) {
|
|
3060
3207
|
appendRoundedRectPath(context, rect, radius);
|
|
3061
3208
|
context.clip();
|
|
@@ -3141,6 +3288,12 @@ function renderPivotTable(params) {
|
|
|
3141
3288
|
);
|
|
3142
3289
|
const areaSelection = isPivotAreaSelection(layout, params.selection);
|
|
3143
3290
|
const viewportWidth = resolvePivotViewportWidth(layout);
|
|
3291
|
+
const tableViewportRect = {
|
|
3292
|
+
x: 0,
|
|
3293
|
+
y: 0,
|
|
3294
|
+
width: layout.tableWidth,
|
|
3295
|
+
height: layout.height
|
|
3296
|
+
};
|
|
3144
3297
|
context.clearRect(0, 0, layout.width, layout.height);
|
|
3145
3298
|
context.save();
|
|
3146
3299
|
clipPivotViewport(context, {
|
|
@@ -3181,7 +3334,7 @@ function renderPivotTable(params) {
|
|
|
3181
3334
|
(cell) => drawPivotGridRect(context, cell.rect, theme.borderColor, {
|
|
3182
3335
|
skipTop: true,
|
|
3183
3336
|
skipLeft: cell.rect.x > 0,
|
|
3184
|
-
skipRight:
|
|
3337
|
+
skipRight: isSameCoordinate2(
|
|
3185
3338
|
cell.rect.x + cell.rect.width,
|
|
3186
3339
|
layout.rowHeaderRect.x + layout.rowHeaderRect.width
|
|
3187
3340
|
)
|
|
@@ -3202,13 +3355,21 @@ function renderPivotTable(params) {
|
|
|
3202
3355
|
);
|
|
3203
3356
|
layout.columnHeaderCells.forEach(
|
|
3204
3357
|
(cell) => drawPivotGridRect(context, cell.rect, theme.borderColor, {
|
|
3205
|
-
skipTop:
|
|
3206
|
-
|
|
3207
|
-
|
|
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
|
+
),
|
|
3368
|
+
skipRight: isSameCoordinate2(
|
|
3208
3369
|
cell.rect.x + cell.rect.width,
|
|
3209
3370
|
layout.tableWidth
|
|
3210
3371
|
),
|
|
3211
|
-
skipBottom:
|
|
3372
|
+
skipBottom: isSameCoordinate2(
|
|
3212
3373
|
cell.rect.y + cell.rect.height,
|
|
3213
3374
|
layout.columnHeaderRect.y + layout.columnHeaderRect.height
|
|
3214
3375
|
)
|
|
@@ -3240,7 +3401,7 @@ function renderPivotTable(params) {
|
|
|
3240
3401
|
(cell) => drawPivotGridRect(context, cell.rect, theme.borderColor, {
|
|
3241
3402
|
skipTop: true,
|
|
3242
3403
|
skipLeft: true,
|
|
3243
|
-
skipRight:
|
|
3404
|
+
skipRight: isSameCoordinate2(
|
|
3244
3405
|
cell.rect.x + cell.rect.width,
|
|
3245
3406
|
layout.tableWidth
|
|
3246
3407
|
)
|
|
@@ -3269,34 +3430,72 @@ function renderPivotTable(params) {
|
|
|
3269
3430
|
headerBackgroundColor: theme.columnHeaderBackgroundColor,
|
|
3270
3431
|
bodyBackgroundColor: theme.backgroundColor
|
|
3271
3432
|
});
|
|
3433
|
+
if (params.resizeGuideX != null) {
|
|
3434
|
+
drawPivotResizeGuide(context, params.resizeGuideX, contentHeight);
|
|
3435
|
+
}
|
|
3436
|
+
context.restore();
|
|
3437
|
+
if (!layout.scrollbar.vertical) {
|
|
3438
|
+
context.save();
|
|
3439
|
+
clipPivotViewport(context, tableViewportRect);
|
|
3440
|
+
drawPivotViewportRightBorder(context, tableViewportRect, theme.borderColor);
|
|
3441
|
+
context.restore();
|
|
3442
|
+
}
|
|
3443
|
+
drawPivotViewportBorder(
|
|
3444
|
+
context,
|
|
3445
|
+
{ x: 0, y: 0, width: viewportWidth, height: contentHeight },
|
|
3446
|
+
theme.borderColor
|
|
3447
|
+
);
|
|
3272
3448
|
const selectionRect = resolvePivotSelectionRect(layout, params.selection);
|
|
3273
3449
|
if (selectionRect) {
|
|
3274
3450
|
const selectionClipRect = resolvePivotSelectionClipRect(
|
|
3275
3451
|
layout,
|
|
3276
3452
|
params.selection
|
|
3277
3453
|
);
|
|
3454
|
+
context.save();
|
|
3455
|
+
clipPivotViewport(context, tableViewportRect);
|
|
3278
3456
|
withClip(context, selectionClipRect, () => {
|
|
3279
3457
|
drawPivotSelectionBorder(
|
|
3280
3458
|
context,
|
|
3281
3459
|
selectionRect,
|
|
3282
|
-
theme.selectionBorderColor
|
|
3460
|
+
theme.selectionBorderColor,
|
|
3461
|
+
{ viewportRect: tableViewportRect }
|
|
3283
3462
|
);
|
|
3284
3463
|
});
|
|
3464
|
+
context.restore();
|
|
3285
3465
|
}
|
|
3286
|
-
if (params.resizeGuideX != null) {
|
|
3287
|
-
drawPivotResizeGuide(context, params.resizeGuideX, contentHeight);
|
|
3288
|
-
}
|
|
3289
|
-
context.restore();
|
|
3290
|
-
drawPivotViewportBorder(
|
|
3291
|
-
context,
|
|
3292
|
-
{ x: 0, y: 0, width: viewportWidth, height: contentHeight },
|
|
3293
|
-
theme.borderColor
|
|
3294
|
-
);
|
|
3295
3466
|
return {
|
|
3296
3467
|
headerCellCount: layout.reportFilterCells.length + layout.rowFieldHeaderCells.length + layout.rowHeaderCells.length + layout.columnHeaderCells.length,
|
|
3297
3468
|
bodyCellCount: layout.bodyCells.length
|
|
3298
3469
|
};
|
|
3299
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
|
+
}
|
|
3300
3499
|
function resolvePivotViewportWidth(layout) {
|
|
3301
3500
|
const horizontalRight = layout.scrollbar.horizontal ? layout.scrollbar.horizontal.increaseButton.x + layout.scrollbar.horizontal.increaseButton.width : 0;
|
|
3302
3501
|
const verticalRight = layout.scrollbar.vertical ? layout.scrollbar.vertical.decreaseButton.x + layout.scrollbar.vertical.decreaseButton.width : 0;
|
|
@@ -3336,7 +3535,7 @@ function drawDebugOverlay(context, layout, theme) {
|
|
|
3336
3535
|
layout.bodyRect
|
|
3337
3536
|
].forEach((rect) => strokeRect(context, rect, "#f04438"));
|
|
3338
3537
|
context.setLineDash([]);
|
|
3339
|
-
context.font =
|
|
3538
|
+
context.font = `${theme.fontSize}px ${theme.fontFamily}`;
|
|
3340
3539
|
context.textAlign = "left";
|
|
3341
3540
|
context.textBaseline = "top";
|
|
3342
3541
|
context.fillStyle = "#f04438";
|
|
@@ -3595,11 +3794,11 @@ function resolveSlotPathKey(slots, nodeId, indicatorKey) {
|
|
|
3595
3794
|
(slot) => (slot.nodeId ?? "") === nodeId && (!slot.indicatorKey || slot.indicatorKey === indicatorKey)
|
|
3596
3795
|
)?.pathKey ?? null;
|
|
3597
3796
|
}
|
|
3598
|
-
function
|
|
3797
|
+
function isSameCoordinate2(first, second) {
|
|
3599
3798
|
return Math.abs(first - second) < 0.01;
|
|
3600
3799
|
}
|
|
3601
3800
|
function resolveBodyDisplayText(cell, indicator) {
|
|
3602
|
-
if (!indicator || indicator.formatter || typeof cell.value !== "number") {
|
|
3801
|
+
if (!indicator || indicator.formatter || indicator.dataFormat || typeof cell.value !== "number") {
|
|
3603
3802
|
return cell.formattedValue;
|
|
3604
3803
|
}
|
|
3605
3804
|
if (indicator.cellType === "percent") {
|
|
@@ -3736,10 +3935,18 @@ function drawSortIcon(context, rect, direction, rightPadding) {
|
|
|
3736
3935
|
function drawText(context, text, rect, color, padding, align) {
|
|
3737
3936
|
const availableWidth = Math.max(rect.width - padding * 2, 0);
|
|
3738
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;
|
|
3739
3944
|
context.fillStyle = color;
|
|
3740
3945
|
context.textAlign = align;
|
|
3946
|
+
context.textBaseline = hasGlyphBounds ? "alphabetic" : "middle";
|
|
3741
3947
|
const x = align === "right" ? rect.x + rect.width - padding : align === "center" ? rect.x + rect.width / 2 : rect.x + padding;
|
|
3742
|
-
|
|
3948
|
+
const y = hasGlyphBounds ? centerY + (ascent - descent) / 2 : centerY;
|
|
3949
|
+
context.fillText(value, x, y, availableWidth);
|
|
3743
3950
|
}
|
|
3744
3951
|
function truncateText(context, text, maxWidth) {
|
|
3745
3952
|
if (context.measureText(text).width <= maxWidth) return text;
|
|
@@ -3787,7 +3994,7 @@ function hitTestPivotLayout(layout, x, y) {
|
|
|
3787
3994
|
onSortIcon: false
|
|
3788
3995
|
};
|
|
3789
3996
|
}
|
|
3790
|
-
const bodyCell = findLastContaining(layout.bodyCells, x, y);
|
|
3997
|
+
const bodyCell = contains(layout.bodyRect, x, y) ? findLastContaining(layout.bodyCells, x, y) : void 0;
|
|
3791
3998
|
if (bodyCell) {
|
|
3792
3999
|
return {
|
|
3793
4000
|
region: "body",
|
|
@@ -3797,7 +4004,7 @@ function hitTestPivotLayout(layout, x, y) {
|
|
|
3797
4004
|
onSortIcon: false
|
|
3798
4005
|
};
|
|
3799
4006
|
}
|
|
3800
|
-
const rowHeaderCell = findLastContaining(layout.rowHeaderCells, x, y);
|
|
4007
|
+
const rowHeaderCell = contains(layout.rowHeaderRect, x, y) ? findLastContaining(layout.rowHeaderCells, x, y) : void 0;
|
|
3801
4008
|
if (rowHeaderCell) {
|
|
3802
4009
|
return {
|
|
3803
4010
|
region: "rowHeader",
|
|
@@ -3807,9 +4014,7 @@ function hitTestPivotLayout(layout, x, y) {
|
|
|
3807
4014
|
onSortIcon: isSortIconHit(rowHeaderCell, x)
|
|
3808
4015
|
};
|
|
3809
4016
|
}
|
|
3810
|
-
const columnHeaderCell = layout.columnHeaderCells.find(
|
|
3811
|
-
(cell) => contains(cell.rect, x, y)
|
|
3812
|
-
);
|
|
4017
|
+
const columnHeaderCell = contains(layout.columnHeaderRect, x, y) ? layout.columnHeaderCells.find((cell) => contains(cell.rect, x, y)) : void 0;
|
|
3813
4018
|
if (columnHeaderCell) {
|
|
3814
4019
|
return {
|
|
3815
4020
|
region: "columnHeader",
|
|
@@ -4325,7 +4530,7 @@ var PivotTable = class {
|
|
|
4325
4530
|
}
|
|
4326
4531
|
if (hit.reportFilterCell) return;
|
|
4327
4532
|
if (hit.bodyCell) {
|
|
4328
|
-
this.
|
|
4533
|
+
this.selectPointerCell(toCellAddress(hit.bodyCell), event.shiftKey);
|
|
4329
4534
|
return;
|
|
4330
4535
|
}
|
|
4331
4536
|
const header = hit.headerCell;
|
|
@@ -4427,7 +4632,76 @@ var PivotTable = class {
|
|
|
4427
4632
|
});
|
|
4428
4633
|
__publicField(this, "handleContextMenu", (event) => {
|
|
4429
4634
|
const hit = this.resolveHit(event);
|
|
4430
|
-
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;
|
|
4431
4705
|
event.preventDefault();
|
|
4432
4706
|
const address = toCellAddress(hit.bodyCell);
|
|
4433
4707
|
const currentRange = resolvePivotSelectionRange(
|
|
@@ -4437,7 +4711,7 @@ var PivotTable = class {
|
|
|
4437
4711
|
const insideCurrentRange = Boolean(
|
|
4438
4712
|
currentRange && hit.bodyCell.rowIndex >= currentRange.startRowIndex && hit.bodyCell.rowIndex <= currentRange.endRowIndex && hit.bodyCell.columnIndex >= currentRange.startColumnIndex && hit.bodyCell.columnIndex <= currentRange.endColumnIndex
|
|
4439
4713
|
);
|
|
4440
|
-
if (!insideCurrentRange) this.
|
|
4714
|
+
if (!insideCurrentRange) this.selectPointerCell(address);
|
|
4441
4715
|
const selectionRange = resolvePivotSelectionRange(
|
|
4442
4716
|
this.layout,
|
|
4443
4717
|
this.core.getSelection()
|
|
@@ -4447,15 +4721,28 @@ var PivotTable = class {
|
|
|
4447
4721
|
columnPathKey: hit.bodyCell.columnPathKey,
|
|
4448
4722
|
indicatorKey: hit.bodyCell.indicatorKey
|
|
4449
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";
|
|
4450
4731
|
this.options.ui?.onContextMenuRequest?.({
|
|
4451
4732
|
x: event.clientX,
|
|
4452
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,
|
|
4453
4738
|
recordCount: records.length,
|
|
4739
|
+
summaryAggregator: summary ? summaryAggregator : void 0,
|
|
4740
|
+
summaryPrecision: summary ? indicator?.precision : void 0,
|
|
4454
4741
|
onCopy: () => {
|
|
4455
|
-
const
|
|
4456
|
-
const text =
|
|
4742
|
+
const model2 = this.core?.getPivotModel();
|
|
4743
|
+
const text = model2 ? createPivotRangeSelectionText({
|
|
4457
4744
|
layout: this.layout,
|
|
4458
|
-
model,
|
|
4745
|
+
model: model2,
|
|
4459
4746
|
startRowIndex: hit.bodyCell.rowIndex,
|
|
4460
4747
|
endRowIndex: hit.bodyCell.rowIndex,
|
|
4461
4748
|
startColumnIndex: hit.bodyCell.columnIndex,
|
|
@@ -4480,11 +4767,11 @@ var PivotTable = class {
|
|
|
4480
4767
|
columnCount: this.layout.columnSlots.length
|
|
4481
4768
|
},
|
|
4482
4769
|
onCopyCustomSelection: (range) => {
|
|
4483
|
-
const
|
|
4484
|
-
if (!
|
|
4770
|
+
const model2 = this.core?.getPivotModel();
|
|
4771
|
+
if (!model2 || !this.layout) return;
|
|
4485
4772
|
const text = createPivotRangeSelectionText({
|
|
4486
4773
|
layout: this.layout,
|
|
4487
|
-
model,
|
|
4774
|
+
model: model2,
|
|
4488
4775
|
startRowIndex: range.startRow - 1,
|
|
4489
4776
|
endRowIndex: range.endRow - 1,
|
|
4490
4777
|
startColumnIndex: range.startColumn - 1,
|
|
@@ -4494,6 +4781,7 @@ var PivotTable = class {
|
|
|
4494
4781
|
this.copyPivotText(text);
|
|
4495
4782
|
}
|
|
4496
4783
|
},
|
|
4784
|
+
...this.createContextMenuSettings(hit.bodyCell.indicatorKey),
|
|
4497
4785
|
onExportCsv: () => downloadBlob(
|
|
4498
4786
|
new Blob([this.exportCsv()], { type: "text/csv;charset=utf-8" }),
|
|
4499
4787
|
"pivot-table.csv"
|
|
@@ -4509,9 +4797,9 @@ var PivotTable = class {
|
|
|
4509
4797
|
});
|
|
4510
4798
|
__publicField(this, "handlePointerDown", (event) => {
|
|
4511
4799
|
if (!this.layout || !this.host) return;
|
|
4512
|
-
this.stopWheelScrollSmoothing();
|
|
4513
4800
|
const scrollbarHit = this.resolveScrollbarHit(event);
|
|
4514
4801
|
if (scrollbarHit && event.button === 0) {
|
|
4802
|
+
this.stopWheelScrollSmoothing();
|
|
4515
4803
|
this.beginScrollbarInteraction(event, scrollbarHit);
|
|
4516
4804
|
return;
|
|
4517
4805
|
}
|
|
@@ -4522,7 +4810,7 @@ var PivotTable = class {
|
|
|
4522
4810
|
if (cellHit?.bodyCell) {
|
|
4523
4811
|
const address = toCellAddress(cellHit.bodyCell);
|
|
4524
4812
|
this.host.root.focus({ preventScroll: true });
|
|
4525
|
-
const selection = this.
|
|
4813
|
+
const selection = this.selectPointerCell(address, event.shiftKey);
|
|
4526
4814
|
const text = this.resolveBodyText(cellHit.bodyCell);
|
|
4527
4815
|
const textOriginX = this.resolveBodyTextOriginX(cellHit.bodyCell, text);
|
|
4528
4816
|
const anchorOffset = text ? resolvePivotTextOffset({
|
|
@@ -4608,8 +4896,12 @@ var PivotTable = class {
|
|
|
4608
4896
|
if (selection) this.setSelection(selection);
|
|
4609
4897
|
this.host.root.setPointerCapture(event.pointerId);
|
|
4610
4898
|
}
|
|
4899
|
+
if (cellHit?.headerCell || cellHit?.reportFilterCell) {
|
|
4900
|
+
event.preventDefault();
|
|
4901
|
+
}
|
|
4611
4902
|
return;
|
|
4612
4903
|
}
|
|
4904
|
+
this.stopWheelScrollSmoothing();
|
|
4613
4905
|
event.preventDefault();
|
|
4614
4906
|
const startWidth = hit.kind === "rowDimension" ? this.layout.rowDimensionWidths[hit.index] ?? 140 : this.layout.columnWidths[hit.index] ?? this.layout.columnWidth;
|
|
4615
4907
|
this.resizeSession = {
|
|
@@ -4799,9 +5091,16 @@ var PivotTable = class {
|
|
|
4799
5091
|
}
|
|
4800
5092
|
/** 选中指定值单元格。 */
|
|
4801
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) {
|
|
4802
5101
|
const selection = this.core?.selectCell(address, extend) ?? null;
|
|
4803
5102
|
if (selection) {
|
|
4804
|
-
this.scrollToCell(address);
|
|
5103
|
+
if (reveal) this.scrollToCell(address);
|
|
4805
5104
|
this.scheduleRender();
|
|
4806
5105
|
}
|
|
4807
5106
|
return selection;
|
|
@@ -4959,6 +5258,27 @@ var PivotTable = class {
|
|
|
4959
5258
|
this.advanceWheelScrollSmoothing(nextTime);
|
|
4960
5259
|
});
|
|
4961
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
|
+
}
|
|
4962
5282
|
updateTooltip(hit, event) {
|
|
4963
5283
|
if (!this.host) return;
|
|
4964
5284
|
const cell = hit?.bodyCell ?? hit?.headerCell;
|
|
@@ -5164,7 +5484,7 @@ var PivotTable = class {
|
|
|
5164
5484
|
const indicator = this.options.indicators.find(
|
|
5165
5485
|
(item) => item.key === cell.indicatorKey
|
|
5166
5486
|
);
|
|
5167
|
-
if (!indicator || indicator.formatter || typeof cell.value !== "number")
|
|
5487
|
+
if (!indicator || indicator.formatter || indicator.dataFormat || typeof cell.value !== "number")
|
|
5168
5488
|
return cell.formattedValue;
|
|
5169
5489
|
if (indicator.cellType === "percent")
|
|
5170
5490
|
return `${(cell.value * 100).toFixed(indicator.precision ?? 2)}%`;
|
|
@@ -5202,7 +5522,7 @@ var PivotTable = class {
|
|
|
5202
5522
|
measurePivotText(text) {
|
|
5203
5523
|
const context = this.host?.canvas.getContext("2d");
|
|
5204
5524
|
if (!context) return text.length * 8;
|
|
5205
|
-
context.font = `${this.options.theme?.fontSize ??
|
|
5525
|
+
context.font = `${this.options.theme?.fontSize ?? 14}px ${this.options.theme?.fontFamily ?? "sans-serif"}`;
|
|
5206
5526
|
return context.measureText(text).width;
|
|
5207
5527
|
}
|
|
5208
5528
|
updateRowHeaderSelectionDrag(event) {
|
|
@@ -5526,6 +5846,9 @@ function toCellAddress(cell) {
|
|
|
5526
5846
|
indicatorKey: cell.indicatorKey
|
|
5527
5847
|
};
|
|
5528
5848
|
}
|
|
5849
|
+
function isBuiltInPivotAggregator(value) {
|
|
5850
|
+
return ["sum", "avg", "count", "distinctCount", "min", "max"].includes(value);
|
|
5851
|
+
}
|
|
5529
5852
|
function isSameAddress(left, right) {
|
|
5530
5853
|
return Boolean(
|
|
5531
5854
|
left === right || left && right && left.rowNodeId === right.rowNodeId && left.columnNodeId === right.columnNodeId && left.indicatorKey === right.indicatorKey
|
|
@@ -5538,6 +5861,20 @@ function resolveNavigationDirection(key) {
|
|
|
5538
5861
|
if (key === "ArrowRight") return { row: 0, column: 1 };
|
|
5539
5862
|
return null;
|
|
5540
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
|
+
}
|
|
5541
5878
|
function resolveAdjacentAddress(layout, address, direction) {
|
|
5542
5879
|
const indexes = resolveAddressIndexes3(layout, address);
|
|
5543
5880
|
if (!indexes) return null;
|
|
@@ -5588,6 +5925,90 @@ function toArrayBuffer(value) {
|
|
|
5588
5925
|
return buffer;
|
|
5589
5926
|
}
|
|
5590
5927
|
|
|
5928
|
+
// src/adapters/storage/indexeddb-field-layout-storage.ts
|
|
5929
|
+
var DATABASE_NAME = "canvas_components_pivot_table_config";
|
|
5930
|
+
var STORE_NAME = "field_layout";
|
|
5931
|
+
var DB_VERSION = 1;
|
|
5932
|
+
var dbPromise = null;
|
|
5933
|
+
async function savePivotFieldLayout(key, layout) {
|
|
5934
|
+
try {
|
|
5935
|
+
const db = await openDatabase();
|
|
5936
|
+
await requestToPromise(
|
|
5937
|
+
db.transaction(STORE_NAME, "readwrite").objectStore(STORE_NAME).put(cloneLayout(layout), key)
|
|
5938
|
+
);
|
|
5939
|
+
} catch (error) {
|
|
5940
|
+
console.error("[PivotTable] \u5B57\u6BB5\u5E03\u5C40\u4FDD\u5B58\u5931\u8D25\uFF1A", error);
|
|
5941
|
+
}
|
|
5942
|
+
}
|
|
5943
|
+
async function loadPivotFieldLayout(key) {
|
|
5944
|
+
try {
|
|
5945
|
+
const db = await openDatabase();
|
|
5946
|
+
const value = await requestToPromise(
|
|
5947
|
+
db.transaction(STORE_NAME, "readonly").objectStore(STORE_NAME).get(key)
|
|
5948
|
+
);
|
|
5949
|
+
return isPivotFieldLayout(value) ? cloneLayout(value) : void 0;
|
|
5950
|
+
} catch (error) {
|
|
5951
|
+
console.error("[PivotTable] \u5B57\u6BB5\u5E03\u5C40\u8BFB\u53D6\u5931\u8D25\uFF1A", error);
|
|
5952
|
+
return void 0;
|
|
5953
|
+
}
|
|
5954
|
+
}
|
|
5955
|
+
async function clearPivotFieldLayout(key) {
|
|
5956
|
+
try {
|
|
5957
|
+
const db = await openDatabase();
|
|
5958
|
+
await requestToPromise(
|
|
5959
|
+
db.transaction(STORE_NAME, "readwrite").objectStore(STORE_NAME).delete(key)
|
|
5960
|
+
);
|
|
5961
|
+
} catch (error) {
|
|
5962
|
+
console.error("[PivotTable] \u5B57\u6BB5\u5E03\u5C40\u6E05\u9664\u5931\u8D25\uFF1A", error);
|
|
5963
|
+
}
|
|
5964
|
+
}
|
|
5965
|
+
function openDatabase() {
|
|
5966
|
+
if (dbPromise) return dbPromise;
|
|
5967
|
+
dbPromise = new Promise((resolve, reject) => {
|
|
5968
|
+
if (typeof indexedDB === "undefined") {
|
|
5969
|
+
reject(new Error("\u5F53\u524D\u73AF\u5883\u4E0D\u652F\u6301 IndexedDB"));
|
|
5970
|
+
return;
|
|
5971
|
+
}
|
|
5972
|
+
const request = indexedDB.open(DATABASE_NAME, DB_VERSION);
|
|
5973
|
+
request.onupgradeneeded = () => {
|
|
5974
|
+
const db = request.result;
|
|
5975
|
+
if (!db.objectStoreNames.contains(STORE_NAME)) {
|
|
5976
|
+
db.createObjectStore(STORE_NAME);
|
|
5977
|
+
}
|
|
5978
|
+
};
|
|
5979
|
+
request.onsuccess = () => resolve(request.result);
|
|
5980
|
+
request.onerror = () => reject(request.error);
|
|
5981
|
+
});
|
|
5982
|
+
dbPromise.catch(() => {
|
|
5983
|
+
dbPromise = null;
|
|
5984
|
+
});
|
|
5985
|
+
return dbPromise;
|
|
5986
|
+
}
|
|
5987
|
+
function requestToPromise(request) {
|
|
5988
|
+
return new Promise((resolve, reject) => {
|
|
5989
|
+
request.onsuccess = () => resolve(request.result);
|
|
5990
|
+
request.onerror = () => reject(request.error);
|
|
5991
|
+
});
|
|
5992
|
+
}
|
|
5993
|
+
function isPivotFieldLayout(value) {
|
|
5994
|
+
if (!value || typeof value !== "object") return false;
|
|
5995
|
+
const layout = value;
|
|
5996
|
+
return ["filters", "columns", "rows", "values"].every(
|
|
5997
|
+
(key) => isStringArray(layout[key])
|
|
5998
|
+
);
|
|
5999
|
+
}
|
|
6000
|
+
function isStringArray(value) {
|
|
6001
|
+
return Array.isArray(value) && value.every((item) => typeof item === "string");
|
|
6002
|
+
}
|
|
6003
|
+
function cloneLayout(layout) {
|
|
6004
|
+
return {
|
|
6005
|
+
filters: [...layout.filters],
|
|
6006
|
+
columns: [...layout.columns],
|
|
6007
|
+
rows: [...layout.rows],
|
|
6008
|
+
values: [...layout.values]
|
|
6009
|
+
};
|
|
6010
|
+
}
|
|
6011
|
+
|
|
5591
6012
|
// src/domain/field/pivot-field-layout.ts
|
|
5592
6013
|
function createEmptyPivotFieldLayout() {
|
|
5593
6014
|
return { filters: [], columns: [], rows: [], values: [] };
|
|
@@ -5735,6 +6156,6 @@ function areaToLayoutKey(area) {
|
|
|
5735
6156
|
return "values";
|
|
5736
6157
|
}
|
|
5737
6158
|
|
|
5738
|
-
export { PivotTable, aggregatePivotRecords, buildPivotDimensionTree, buildPivotLayoutSnapshot, computePivotScrollbarLayout, createBuiltInPivotAggregators, createEmptyPivotFieldLayout, createEmptyPivotFilterState, createPivotAggregatorRegistry, createPivotAxisSlots, createPivotCellKey, createPivotExportMatrix, createPivotFieldCatalog, createPivotFieldLayout, createPivotSelectionText, defaultPivotTableTheme, encodePivotDimensionValue, encodePivotPath, exportPivotMatrixToCsv, exportPivotMatrixToXlsx, filterPivotRecords, findPivotFieldArea, flattenVisiblePivotLeaves, getPivotDimensionFilterOptions, hitTestPivotLayout, hitTestPivotResize, hitTestPivotScrollbar, movePivotField, removePivotField, renderPivotTable, resolvePivotDefaultSorts, resolvePivotDimensionKey, resolvePivotDimensionTitle, resolvePivotDimensionValue, resolvePivotDimensionWidth, resolvePivotFieldLayoutOptions, resolvePivotIndicatorAggregator, resolvePivotValueFieldTitle };
|
|
6159
|
+
export { PivotTable, aggregatePivotRecords, buildPivotDimensionTree, buildPivotLayoutSnapshot, clearPivotFieldLayout, computePivotScrollbarLayout, createBuiltInPivotAggregators, createEmptyPivotFieldLayout, createEmptyPivotFilterState, createPivotAggregatorRegistry, createPivotAxisSlots, createPivotCellKey, createPivotExportMatrix, createPivotFieldCatalog, createPivotFieldLayout, createPivotSelectionText, defaultPivotTableTheme, encodePivotDimensionValue, encodePivotPath, exportPivotMatrixToCsv, exportPivotMatrixToXlsx, filterPivotRecords, findPivotFieldArea, flattenVisiblePivotLeaves, getPivotDimensionFilterOptions, hitTestPivotLayout, hitTestPivotResize, hitTestPivotScrollbar, loadPivotFieldLayout, movePivotField, removePivotField, renderPivotTable, resolvePivotDefaultSorts, resolvePivotDimensionKey, resolvePivotDimensionTitle, resolvePivotDimensionValue, resolvePivotDimensionWidth, resolvePivotFieldLayoutOptions, resolvePivotIndicatorAggregator, resolvePivotValueFieldTitle, savePivotFieldLayout };
|
|
5739
6160
|
//# sourceMappingURL=index.js.map
|
|
5740
6161
|
//# sourceMappingURL=index.js.map
|