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