@canvas-components/pivot-table 0.1.9 → 0.2.2
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 +377 -72
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +34 -21
- package/dist/index.d.ts +34 -21
- package/dist/index.js +376 -74
- 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()
|
|
@@ -749,14 +749,11 @@ function matchesSearchKeyword(value, filter) {
|
|
|
749
749
|
return filter.searchExact ? text === keyword : text.includes(keyword);
|
|
750
750
|
}
|
|
751
751
|
function matchesFilterConditions(value, filter) {
|
|
752
|
-
const
|
|
753
|
-
|
|
754
|
-
|
|
755
|
-
|
|
756
|
-
|
|
757
|
-
if (!firstActive) return evaluateCondition(value, second);
|
|
758
|
-
if (!secondActive) return evaluateCondition(value, first);
|
|
759
|
-
return filter.conditionRelation === "or" ? evaluateCondition(value, first) || evaluateCondition(value, second) : evaluateCondition(value, first) && evaluateCondition(value, second);
|
|
752
|
+
const conditions = (filter.conditions?.length ? filter.conditions : [filter.condition, filter.secondCondition]).filter(
|
|
753
|
+
(condition) => condition != null && condition.operator !== "none"
|
|
754
|
+
);
|
|
755
|
+
if (conditions.length === 0) return true;
|
|
756
|
+
return filter.conditionRelation === "or" ? conditions.some((condition) => evaluateCondition(value, condition)) : conditions.every((condition) => evaluateCondition(value, condition));
|
|
760
757
|
}
|
|
761
758
|
function evaluateCondition(value, condition) {
|
|
762
759
|
const text = value == null ? "" : String(value);
|
|
@@ -2652,6 +2649,7 @@ function createPivotDomHost(container) {
|
|
|
2652
2649
|
right: "0",
|
|
2653
2650
|
height: "100%",
|
|
2654
2651
|
overflow: "hidden",
|
|
2652
|
+
borderRadius: "8px",
|
|
2655
2653
|
touchAction: "none"
|
|
2656
2654
|
});
|
|
2657
2655
|
Object.assign(canvas.style, {
|
|
@@ -2771,18 +2769,50 @@ function resolvePivotSelectionKind(selection) {
|
|
|
2771
2769
|
}
|
|
2772
2770
|
return selection?.activeNode?.axis ?? "cell";
|
|
2773
2771
|
}
|
|
2774
|
-
function drawPivotSelectionBorder(context, rect, color) {
|
|
2772
|
+
function drawPivotSelectionBorder(context, rect, color, options) {
|
|
2775
2773
|
context.save();
|
|
2776
2774
|
context.strokeStyle = color;
|
|
2777
2775
|
context.lineWidth = 1;
|
|
2778
|
-
|
|
2779
|
-
|
|
2780
|
-
|
|
2781
|
-
|
|
2782
|
-
|
|
2783
|
-
|
|
2776
|
+
const left = rect.x + 0.5;
|
|
2777
|
+
const top = rect.y + 0.5;
|
|
2778
|
+
const width = Math.max(rect.width - 1, 0);
|
|
2779
|
+
const height = Math.max(rect.height - 1, 0);
|
|
2780
|
+
const right = left + width;
|
|
2781
|
+
const bottom = top + height;
|
|
2782
|
+
const viewport = options?.viewportRect;
|
|
2783
|
+
const radius = Math.min(options?.radius ?? 8, width / 2, height / 2);
|
|
2784
|
+
const topLeft = viewport && isSameCoordinate(rect.x, viewport.x) && isSameCoordinate(rect.y, viewport.y) ? radius : 0;
|
|
2785
|
+
const topRight = viewport && isSameCoordinate(rect.x + rect.width, viewport.x + viewport.width) && isSameCoordinate(rect.y, viewport.y) ? radius : 0;
|
|
2786
|
+
const bottomRight = viewport && isSameCoordinate(rect.x + rect.width, viewport.x + viewport.width) && isSameCoordinate(rect.y + rect.height, viewport.y + viewport.height) ? radius : 0;
|
|
2787
|
+
const bottomLeft = viewport && isSameCoordinate(rect.x, viewport.x) && isSameCoordinate(rect.y + rect.height, viewport.y + viewport.height) ? radius : 0;
|
|
2788
|
+
if (!topLeft && !topRight && !bottomRight && !bottomLeft) {
|
|
2789
|
+
context.strokeRect(left, top, width, height);
|
|
2790
|
+
context.restore();
|
|
2791
|
+
return;
|
|
2792
|
+
}
|
|
2793
|
+
context.beginPath();
|
|
2794
|
+
context.moveTo(left + topLeft, top);
|
|
2795
|
+
context.lineTo(right - topRight, top);
|
|
2796
|
+
if (topRight) context.arcTo(right, top, right, top + topRight, topRight);
|
|
2797
|
+
else context.lineTo(right, top);
|
|
2798
|
+
context.lineTo(right, bottom - bottomRight);
|
|
2799
|
+
if (bottomRight) {
|
|
2800
|
+
context.arcTo(right, bottom, right - bottomRight, bottom, bottomRight);
|
|
2801
|
+
} else context.lineTo(right, bottom);
|
|
2802
|
+
context.lineTo(left + bottomLeft, bottom);
|
|
2803
|
+
if (bottomLeft) {
|
|
2804
|
+
context.arcTo(left, bottom, left, bottom - bottomLeft, bottomLeft);
|
|
2805
|
+
} else context.lineTo(left, bottom);
|
|
2806
|
+
context.lineTo(left, top + topLeft);
|
|
2807
|
+
if (topLeft) context.arcTo(left, top, left + topLeft, top, topLeft);
|
|
2808
|
+
else context.lineTo(left, top);
|
|
2809
|
+
context.closePath();
|
|
2810
|
+
context.stroke();
|
|
2784
2811
|
context.restore();
|
|
2785
2812
|
}
|
|
2813
|
+
function isSameCoordinate(first, second) {
|
|
2814
|
+
return Math.abs(first - second) < 0.01;
|
|
2815
|
+
}
|
|
2786
2816
|
function resolveSelectionIndexes(layout, selection) {
|
|
2787
2817
|
const range = selection?.ranges[0];
|
|
2788
2818
|
if (range) {
|
|
@@ -2880,29 +2910,127 @@ var defaultPivotTableTheme = {
|
|
|
2880
2910
|
};
|
|
2881
2911
|
|
|
2882
2912
|
// src/rendering/draw-scrollbars.ts
|
|
2883
|
-
function drawPivotScrollbars(context, layout, borderColor) {
|
|
2913
|
+
function drawPivotScrollbars(context, layout, borderColor, verticalFrame) {
|
|
2884
2914
|
context.save();
|
|
2915
|
+
if (layout.horizontal && verticalFrame) {
|
|
2916
|
+
drawHorizontalScrollbarMask(
|
|
2917
|
+
context,
|
|
2918
|
+
layout.horizontal,
|
|
2919
|
+
verticalFrame.bodyBackgroundColor
|
|
2920
|
+
);
|
|
2921
|
+
}
|
|
2922
|
+
if (layout.vertical && verticalFrame) {
|
|
2923
|
+
drawVerticalScrollbarFrame(
|
|
2924
|
+
context,
|
|
2925
|
+
layout.vertical,
|
|
2926
|
+
verticalFrame,
|
|
2927
|
+
borderColor
|
|
2928
|
+
);
|
|
2929
|
+
}
|
|
2885
2930
|
if (layout.horizontal) {
|
|
2886
2931
|
drawAxis(context, layout.horizontal, "horizontal", borderColor);
|
|
2887
2932
|
}
|
|
2888
2933
|
if (layout.vertical) {
|
|
2889
2934
|
drawAxis(context, layout.vertical, "vertical", borderColor);
|
|
2890
2935
|
}
|
|
2936
|
+
if (layout.horizontal) {
|
|
2937
|
+
drawHorizontalScrollbarTopBorder(
|
|
2938
|
+
context,
|
|
2939
|
+
layout.horizontal,
|
|
2940
|
+
layout.vertical,
|
|
2941
|
+
borderColor
|
|
2942
|
+
);
|
|
2943
|
+
}
|
|
2944
|
+
context.restore();
|
|
2945
|
+
}
|
|
2946
|
+
function drawHorizontalScrollbarMask(context, layout, backgroundColor) {
|
|
2947
|
+
const left = layout.decreaseButton.x;
|
|
2948
|
+
const right = layout.increaseButton.x + layout.increaseButton.width;
|
|
2949
|
+
const top = layout.decreaseButton.y;
|
|
2950
|
+
const height = layout.decreaseButton.height;
|
|
2951
|
+
if (right <= left || height <= 0) return;
|
|
2952
|
+
context.save();
|
|
2953
|
+
context.fillStyle = backgroundColor;
|
|
2954
|
+
context.fillRect(left, top, right - left, height);
|
|
2955
|
+
context.restore();
|
|
2956
|
+
}
|
|
2957
|
+
function drawHorizontalScrollbarTopBorder(context, horizontal, vertical, borderColor) {
|
|
2958
|
+
const left = Math.round(horizontal.decreaseButton.x) + 0.5;
|
|
2959
|
+
const horizontalRight = horizontal.increaseButton.x + horizontal.increaseButton.width;
|
|
2960
|
+
const verticalRight = vertical ? vertical.decreaseButton.x + vertical.decreaseButton.width : horizontalRight;
|
|
2961
|
+
const right = Math.round(Math.max(horizontalRight, verticalRight)) - 0.5;
|
|
2962
|
+
const top = Math.round(horizontal.decreaseButton.y) + 0.5;
|
|
2963
|
+
if (right <= left) return;
|
|
2964
|
+
context.save();
|
|
2965
|
+
context.strokeStyle = borderColor;
|
|
2966
|
+
context.lineWidth = 1;
|
|
2967
|
+
context.beginPath();
|
|
2968
|
+
context.moveTo(left, top);
|
|
2969
|
+
context.lineTo(right, top);
|
|
2970
|
+
context.stroke();
|
|
2891
2971
|
context.restore();
|
|
2892
2972
|
}
|
|
2893
2973
|
function drawAxis(context, layout, axis, borderColor) {
|
|
2894
|
-
drawTrack(context, layout.track, borderColor);
|
|
2895
2974
|
drawButton(context, layout.decreaseButton, axis, "decrease");
|
|
2896
2975
|
drawButton(context, layout.increaseButton, axis, "increase");
|
|
2897
2976
|
drawThumb(context, layout.thumb);
|
|
2977
|
+
drawTrackBorder(context, layout.track, axis, borderColor);
|
|
2978
|
+
}
|
|
2979
|
+
function drawTrackBorder(context, rect, axis, borderColor) {
|
|
2980
|
+
const left = Math.round(rect.x) + 0.5;
|
|
2981
|
+
const right = Math.round(rect.x + rect.width) - 0.5;
|
|
2982
|
+
const top = Math.round(rect.y) + 0.5;
|
|
2983
|
+
const bottom = Math.round(rect.y + rect.height) - 0.5;
|
|
2984
|
+
if (right <= left || bottom <= top) return;
|
|
2985
|
+
context.save();
|
|
2986
|
+
context.strokeStyle = borderColor;
|
|
2987
|
+
context.lineWidth = 1;
|
|
2988
|
+
context.beginPath();
|
|
2989
|
+
if (axis === "horizontal") {
|
|
2990
|
+
context.moveTo(left, top);
|
|
2991
|
+
context.lineTo(right, top);
|
|
2992
|
+
} else {
|
|
2993
|
+
context.moveTo(left, top);
|
|
2994
|
+
context.lineTo(left, bottom);
|
|
2995
|
+
}
|
|
2996
|
+
context.stroke();
|
|
2997
|
+
context.restore();
|
|
2898
2998
|
}
|
|
2899
|
-
function
|
|
2900
|
-
|
|
2999
|
+
function drawVerticalScrollbarFrame(context, layout, frame, borderColor) {
|
|
3000
|
+
const left = Math.round(layout.decreaseButton.x) + 0.5;
|
|
3001
|
+
const top = Math.round(frame.top) + 0.5;
|
|
3002
|
+
const bottom = Math.round(frame.top + frame.height) - 0.5;
|
|
3003
|
+
const headerBottom = Math.round(frame.headerBottom) + 0.5;
|
|
3004
|
+
if (bottom <= top) return;
|
|
3005
|
+
context.save();
|
|
3006
|
+
context.fillStyle = frame.headerBackgroundColor;
|
|
3007
|
+
context.fillRect(
|
|
3008
|
+
layout.decreaseButton.x,
|
|
3009
|
+
frame.top,
|
|
3010
|
+
layout.decreaseButton.width,
|
|
3011
|
+
Math.max(frame.headerBottom - frame.top, 0)
|
|
3012
|
+
);
|
|
3013
|
+
context.fillStyle = frame.bodyBackgroundColor;
|
|
3014
|
+
context.fillRect(
|
|
3015
|
+
layout.decreaseButton.x,
|
|
3016
|
+
frame.headerBottom,
|
|
3017
|
+
layout.decreaseButton.width,
|
|
3018
|
+
Math.max(frame.top + frame.height - frame.headerBottom, 0)
|
|
3019
|
+
);
|
|
2901
3020
|
context.strokeStyle = borderColor;
|
|
2902
3021
|
context.lineWidth = 1;
|
|
2903
|
-
|
|
2904
|
-
context.
|
|
3022
|
+
context.beginPath();
|
|
3023
|
+
context.moveTo(left, top);
|
|
3024
|
+
context.lineTo(left, bottom);
|
|
2905
3025
|
context.stroke();
|
|
3026
|
+
if (headerBottom > top && headerBottom < bottom) {
|
|
3027
|
+
const right = Math.round(layout.decreaseButton.x + layout.decreaseButton.width) - 0.5;
|
|
3028
|
+
context.beginPath();
|
|
3029
|
+
context.moveTo(left, headerBottom);
|
|
3030
|
+
context.lineTo(right, headerBottom);
|
|
3031
|
+
context.stroke();
|
|
3032
|
+
}
|
|
3033
|
+
context.restore();
|
|
2906
3034
|
}
|
|
2907
3035
|
function drawThumb(context, rect) {
|
|
2908
3036
|
context.fillStyle = "rgba(100, 116, 139, 0.8)";
|
|
@@ -2910,28 +3038,25 @@ function drawThumb(context, rect) {
|
|
|
2910
3038
|
context.fill();
|
|
2911
3039
|
}
|
|
2912
3040
|
function drawButton(context, rect, axis, direction) {
|
|
2913
|
-
context.fillStyle = "rgba(226, 232, 240, 0.92)";
|
|
2914
|
-
context.strokeStyle = "rgba(148, 163, 184, 0.65)";
|
|
2915
|
-
context.lineWidth = 1;
|
|
2916
|
-
roundRect(context, rect, rect.height / 2);
|
|
2917
|
-
context.fill();
|
|
2918
|
-
context.stroke();
|
|
2919
3041
|
context.fillStyle = "rgba(71, 85, 105, 0.88)";
|
|
2920
3042
|
context.beginPath();
|
|
2921
3043
|
const centerX = rect.x + rect.width / 2;
|
|
2922
3044
|
const centerY = rect.y + rect.height / 2;
|
|
3045
|
+
const iconSize = Math.min(rect.width, rect.height) * 0.64;
|
|
3046
|
+
const directionOffset = iconSize * 0.4;
|
|
3047
|
+
const crossAxisOffset = iconSize / 2;
|
|
2923
3048
|
if (axis === "horizontal") {
|
|
2924
|
-
const tipX = centerX + (direction === "decrease" ? -1 : 1) *
|
|
2925
|
-
const tailX = centerX + (direction === "decrease" ? 1 : -1) *
|
|
3049
|
+
const tipX = centerX + (direction === "decrease" ? -1 : 1) * directionOffset;
|
|
3050
|
+
const tailX = centerX + (direction === "decrease" ? 1 : -1) * directionOffset;
|
|
2926
3051
|
context.moveTo(tipX, centerY);
|
|
2927
|
-
context.lineTo(tailX, centerY -
|
|
2928
|
-
context.lineTo(tailX, centerY +
|
|
3052
|
+
context.lineTo(tailX, centerY - crossAxisOffset);
|
|
3053
|
+
context.lineTo(tailX, centerY + crossAxisOffset);
|
|
2929
3054
|
} else {
|
|
2930
|
-
const tipY = centerY + (direction === "decrease" ? -1 : 1) *
|
|
2931
|
-
const tailY = centerY + (direction === "decrease" ? 1 : -1) *
|
|
3055
|
+
const tipY = centerY + (direction === "decrease" ? -1 : 1) * directionOffset;
|
|
3056
|
+
const tailY = centerY + (direction === "decrease" ? 1 : -1) * directionOffset;
|
|
2932
3057
|
context.moveTo(centerX, tipY);
|
|
2933
|
-
context.lineTo(centerX -
|
|
2934
|
-
context.lineTo(centerX +
|
|
3058
|
+
context.lineTo(centerX - crossAxisOffset, tailY);
|
|
3059
|
+
context.lineTo(centerX + crossAxisOffset, tailY);
|
|
2935
3060
|
}
|
|
2936
3061
|
context.closePath();
|
|
2937
3062
|
context.fill();
|
|
@@ -2995,14 +3120,22 @@ function drawPivotViewportRightBorder(context, rect, color) {
|
|
|
2995
3120
|
context.stroke();
|
|
2996
3121
|
context.restore();
|
|
2997
3122
|
}
|
|
2998
|
-
function
|
|
2999
|
-
|
|
3123
|
+
function clipPivotViewport(context, rect, radius = 8) {
|
|
3124
|
+
appendRoundedRectPath(context, rect, radius);
|
|
3125
|
+
context.clip();
|
|
3126
|
+
}
|
|
3127
|
+
function drawPivotViewportBorder(context, rect, color, radius = 8) {
|
|
3128
|
+
const borderRect = {
|
|
3129
|
+
x: rect.x + 0.5,
|
|
3130
|
+
y: rect.y + 0.5,
|
|
3131
|
+
width: Math.max(rect.width - 1, 0),
|
|
3132
|
+
height: Math.max(rect.height - 1, 0)
|
|
3133
|
+
};
|
|
3134
|
+
if (borderRect.width <= 0 || borderRect.height <= 0) return;
|
|
3000
3135
|
context.save();
|
|
3001
3136
|
context.strokeStyle = color;
|
|
3002
3137
|
context.lineWidth = 1;
|
|
3003
|
-
context
|
|
3004
|
-
context.moveTo(rect.x, y);
|
|
3005
|
-
context.lineTo(rect.x + rect.width, y);
|
|
3138
|
+
appendRoundedRectPath(context, borderRect, radius);
|
|
3006
3139
|
context.stroke();
|
|
3007
3140
|
context.restore();
|
|
3008
3141
|
}
|
|
@@ -3020,6 +3153,21 @@ function drawPivotHeaderBoundaries(context, options, color) {
|
|
|
3020
3153
|
context.stroke();
|
|
3021
3154
|
context.restore();
|
|
3022
3155
|
}
|
|
3156
|
+
function appendRoundedRectPath(context, rect, radius) {
|
|
3157
|
+
const nextRadius = Math.max(
|
|
3158
|
+
Math.min(radius, rect.width / 2, rect.height / 2),
|
|
3159
|
+
0
|
|
3160
|
+
);
|
|
3161
|
+
const right = rect.x + rect.width;
|
|
3162
|
+
const bottom = rect.y + rect.height;
|
|
3163
|
+
context.beginPath();
|
|
3164
|
+
context.moveTo(rect.x + nextRadius, rect.y);
|
|
3165
|
+
context.arcTo(right, rect.y, right, bottom, nextRadius);
|
|
3166
|
+
context.arcTo(right, bottom, rect.x, bottom, nextRadius);
|
|
3167
|
+
context.arcTo(rect.x, bottom, rect.x, rect.y, nextRadius);
|
|
3168
|
+
context.arcTo(rect.x, rect.y, right, rect.y, nextRadius);
|
|
3169
|
+
context.closePath();
|
|
3170
|
+
}
|
|
3023
3171
|
|
|
3024
3172
|
// src/domain/layout/sticky-row-header.ts
|
|
3025
3173
|
function resolveStickyRowHeaderContentRect(params) {
|
|
@@ -3056,7 +3204,21 @@ function renderPivotTable(params) {
|
|
|
3056
3204
|
activeNodePathKey
|
|
3057
3205
|
);
|
|
3058
3206
|
const areaSelection = isPivotAreaSelection(layout, params.selection);
|
|
3207
|
+
const viewportWidth = resolvePivotViewportWidth(layout);
|
|
3208
|
+
const tableViewportRect = {
|
|
3209
|
+
x: 0,
|
|
3210
|
+
y: 0,
|
|
3211
|
+
width: layout.tableWidth,
|
|
3212
|
+
height: layout.height
|
|
3213
|
+
};
|
|
3059
3214
|
context.clearRect(0, 0, layout.width, layout.height);
|
|
3215
|
+
context.save();
|
|
3216
|
+
clipPivotViewport(context, {
|
|
3217
|
+
x: 0,
|
|
3218
|
+
y: 0,
|
|
3219
|
+
width: viewportWidth,
|
|
3220
|
+
height: layout.height
|
|
3221
|
+
});
|
|
3060
3222
|
context.font = `${theme.fontSize}px ${theme.fontFamily}`;
|
|
3061
3223
|
context.textBaseline = "middle";
|
|
3062
3224
|
withClip(context, layout.reportFilterRect, () => {
|
|
@@ -3089,7 +3251,7 @@ function renderPivotTable(params) {
|
|
|
3089
3251
|
(cell) => drawPivotGridRect(context, cell.rect, theme.borderColor, {
|
|
3090
3252
|
skipTop: true,
|
|
3091
3253
|
skipLeft: cell.rect.x > 0,
|
|
3092
|
-
skipRight:
|
|
3254
|
+
skipRight: isSameCoordinate2(
|
|
3093
3255
|
cell.rect.x + cell.rect.width,
|
|
3094
3256
|
layout.rowHeaderRect.x + layout.rowHeaderRect.width
|
|
3095
3257
|
)
|
|
@@ -3112,11 +3274,11 @@ function renderPivotTable(params) {
|
|
|
3112
3274
|
(cell) => drawPivotGridRect(context, cell.rect, theme.borderColor, {
|
|
3113
3275
|
skipTop: cell.rect.y > 0,
|
|
3114
3276
|
skipLeft: true,
|
|
3115
|
-
skipRight:
|
|
3277
|
+
skipRight: isSameCoordinate2(
|
|
3116
3278
|
cell.rect.x + cell.rect.width,
|
|
3117
3279
|
layout.tableWidth
|
|
3118
3280
|
),
|
|
3119
|
-
skipBottom:
|
|
3281
|
+
skipBottom: isSameCoordinate2(
|
|
3120
3282
|
cell.rect.y + cell.rect.height,
|
|
3121
3283
|
layout.columnHeaderRect.y + layout.columnHeaderRect.height
|
|
3122
3284
|
)
|
|
@@ -3148,7 +3310,7 @@ function renderPivotTable(params) {
|
|
|
3148
3310
|
(cell) => drawPivotGridRect(context, cell.rect, theme.borderColor, {
|
|
3149
3311
|
skipTop: true,
|
|
3150
3312
|
skipLeft: true,
|
|
3151
|
-
skipRight:
|
|
3313
|
+
skipRight: isSameCoordinate2(
|
|
3152
3314
|
cell.rect.x + cell.rect.width,
|
|
3153
3315
|
layout.tableWidth
|
|
3154
3316
|
)
|
|
@@ -3170,15 +3332,24 @@ function renderPivotTable(params) {
|
|
|
3170
3332
|
theme.borderColor
|
|
3171
3333
|
);
|
|
3172
3334
|
if (options.debug) drawDebugOverlay(context, layout, theme);
|
|
3173
|
-
drawPivotScrollbars(context, layout.scrollbar, theme.borderColor
|
|
3174
|
-
|
|
3175
|
-
|
|
3176
|
-
|
|
3177
|
-
theme.
|
|
3178
|
-
|
|
3179
|
-
|
|
3335
|
+
drawPivotScrollbars(context, layout.scrollbar, theme.borderColor, {
|
|
3336
|
+
top: 0,
|
|
3337
|
+
height: contentHeight,
|
|
3338
|
+
headerBottom: layout.bodyRect.y,
|
|
3339
|
+
headerBackgroundColor: theme.columnHeaderBackgroundColor,
|
|
3340
|
+
bodyBackgroundColor: theme.backgroundColor
|
|
3341
|
+
});
|
|
3342
|
+
if (params.resizeGuideX != null) {
|
|
3343
|
+
drawPivotResizeGuide(context, params.resizeGuideX, contentHeight);
|
|
3344
|
+
}
|
|
3345
|
+
context.restore();
|
|
3346
|
+
context.save();
|
|
3347
|
+
clipPivotViewport(context, tableViewportRect);
|
|
3348
|
+
drawPivotViewportRightBorder(context, tableViewportRect, theme.borderColor);
|
|
3349
|
+
context.restore();
|
|
3350
|
+
drawPivotViewportBorder(
|
|
3180
3351
|
context,
|
|
3181
|
-
{ x: 0, y: 0, width:
|
|
3352
|
+
{ x: 0, y: 0, width: viewportWidth, height: contentHeight },
|
|
3182
3353
|
theme.borderColor
|
|
3183
3354
|
);
|
|
3184
3355
|
const selectionRect = resolvePivotSelectionRect(layout, params.selection);
|
|
@@ -3187,22 +3358,28 @@ function renderPivotTable(params) {
|
|
|
3187
3358
|
layout,
|
|
3188
3359
|
params.selection
|
|
3189
3360
|
);
|
|
3361
|
+
context.save();
|
|
3362
|
+
clipPivotViewport(context, tableViewportRect);
|
|
3190
3363
|
withClip(context, selectionClipRect, () => {
|
|
3191
3364
|
drawPivotSelectionBorder(
|
|
3192
3365
|
context,
|
|
3193
3366
|
selectionRect,
|
|
3194
|
-
theme.selectionBorderColor
|
|
3367
|
+
theme.selectionBorderColor,
|
|
3368
|
+
{ viewportRect: tableViewportRect }
|
|
3195
3369
|
);
|
|
3196
3370
|
});
|
|
3197
|
-
|
|
3198
|
-
if (params.resizeGuideX != null) {
|
|
3199
|
-
drawPivotResizeGuide(context, params.resizeGuideX, contentHeight);
|
|
3371
|
+
context.restore();
|
|
3200
3372
|
}
|
|
3201
3373
|
return {
|
|
3202
3374
|
headerCellCount: layout.reportFilterCells.length + layout.rowFieldHeaderCells.length + layout.rowHeaderCells.length + layout.columnHeaderCells.length,
|
|
3203
3375
|
bodyCellCount: layout.bodyCells.length
|
|
3204
3376
|
};
|
|
3205
3377
|
}
|
|
3378
|
+
function resolvePivotViewportWidth(layout) {
|
|
3379
|
+
const horizontalRight = layout.scrollbar.horizontal ? layout.scrollbar.horizontal.increaseButton.x + layout.scrollbar.horizontal.increaseButton.width : 0;
|
|
3380
|
+
const verticalRight = layout.scrollbar.vertical ? layout.scrollbar.vertical.decreaseButton.x + layout.scrollbar.vertical.decreaseButton.width : 0;
|
|
3381
|
+
return Math.max(layout.tableWidth, horizontalRight, verticalRight);
|
|
3382
|
+
}
|
|
3206
3383
|
function drawPivotResizeGuide(context, x, height) {
|
|
3207
3384
|
context.save();
|
|
3208
3385
|
context.strokeStyle = "#1677ff";
|
|
@@ -3237,7 +3414,7 @@ function drawDebugOverlay(context, layout, theme) {
|
|
|
3237
3414
|
layout.bodyRect
|
|
3238
3415
|
].forEach((rect) => strokeRect(context, rect, "#f04438"));
|
|
3239
3416
|
context.setLineDash([]);
|
|
3240
|
-
context.font =
|
|
3417
|
+
context.font = `${theme.fontSize}px ${theme.fontFamily}`;
|
|
3241
3418
|
context.textAlign = "left";
|
|
3242
3419
|
context.textBaseline = "top";
|
|
3243
3420
|
context.fillStyle = "#f04438";
|
|
@@ -3496,7 +3673,7 @@ function resolveSlotPathKey(slots, nodeId, indicatorKey) {
|
|
|
3496
3673
|
(slot) => (slot.nodeId ?? "") === nodeId && (!slot.indicatorKey || slot.indicatorKey === indicatorKey)
|
|
3497
3674
|
)?.pathKey ?? null;
|
|
3498
3675
|
}
|
|
3499
|
-
function
|
|
3676
|
+
function isSameCoordinate2(first, second) {
|
|
3500
3677
|
return Math.abs(first - second) < 0.01;
|
|
3501
3678
|
}
|
|
3502
3679
|
function resolveBodyDisplayText(cell, indicator) {
|
|
@@ -3956,6 +4133,16 @@ function hitTestPivotScrollbar(layout, x, y) {
|
|
|
3956
4133
|
}
|
|
3957
4134
|
return null;
|
|
3958
4135
|
}
|
|
4136
|
+
function resolvePivotScrollbarTrackScroll(params) {
|
|
4137
|
+
const movableLength = Math.max(params.trackLength - params.thumbLength, 0);
|
|
4138
|
+
if (movableLength === 0) return 0;
|
|
4139
|
+
const thumbStart = utils.clamp(
|
|
4140
|
+
params.pointerValue - params.thumbLength / 2,
|
|
4141
|
+
params.trackStart,
|
|
4142
|
+
params.trackStart + movableLength
|
|
4143
|
+
);
|
|
4144
|
+
return (thumbStart - params.trackStart) / movableLength * params.maxScroll;
|
|
4145
|
+
}
|
|
3959
4146
|
function resolveRole(layout, x, y) {
|
|
3960
4147
|
if (utils.isPointInRect(x, y, layout.thumb)) return "thumb";
|
|
3961
4148
|
if (utils.isPointInRect(x, y, layout.decreaseButton)) return "decrease-button";
|
|
@@ -4122,6 +4309,8 @@ var PivotTable = class {
|
|
|
4122
4309
|
__publicField(this, "wheelScrollTarget", null);
|
|
4123
4310
|
__publicField(this, "wheelScrollFrameId", null);
|
|
4124
4311
|
__publicField(this, "wheelScrollLastTime", 0);
|
|
4312
|
+
__publicField(this, "continuousScrollTimer", null);
|
|
4313
|
+
__publicField(this, "continuousScrollPointerId", null);
|
|
4125
4314
|
__publicField(this, "resizeSession", null);
|
|
4126
4315
|
__publicField(this, "resizeGuideX", null);
|
|
4127
4316
|
__publicField(this, "suppressNextClick", false);
|
|
@@ -4200,6 +4389,7 @@ var PivotTable = class {
|
|
|
4200
4389
|
this.scheduleRender();
|
|
4201
4390
|
});
|
|
4202
4391
|
__publicField(this, "handleClick", (event) => {
|
|
4392
|
+
this.options.ui?.onContextMenuRequest?.(null);
|
|
4203
4393
|
if (this.suppressNextClick) {
|
|
4204
4394
|
this.suppressNextClick = false;
|
|
4205
4395
|
return;
|
|
@@ -4518,6 +4708,8 @@ var PivotTable = class {
|
|
|
4518
4708
|
});
|
|
4519
4709
|
__publicField(this, "handlePointerUp", (event) => {
|
|
4520
4710
|
if (!this.host) return;
|
|
4711
|
+
const endedContinuousScroll = this.continuousScrollPointerId === event.pointerId;
|
|
4712
|
+
if (endedContinuousScroll) this.stopContinuousScrollbarScroll();
|
|
4521
4713
|
const endedCellSelection = this.cellSelectionDrag?.pointerId === event.pointerId;
|
|
4522
4714
|
if (endedCellSelection) this.cellSelectionDrag = null;
|
|
4523
4715
|
if (this.textSelectionDrag?.pointerId === event.pointerId) {
|
|
@@ -4536,7 +4728,7 @@ var PivotTable = class {
|
|
|
4536
4728
|
if (this.scrollbarDrag?.pointerId === event.pointerId) {
|
|
4537
4729
|
this.scrollbarDrag = null;
|
|
4538
4730
|
}
|
|
4539
|
-
if (!this.resizeSession && !this.scrollbarDrag && !endedCellSelection && !endedRowHeaderSelection && !this.host.root.hasPointerCapture(event.pointerId))
|
|
4731
|
+
if (!this.resizeSession && !this.scrollbarDrag && !endedContinuousScroll && !endedCellSelection && !endedRowHeaderSelection && !this.host.root.hasPointerCapture(event.pointerId))
|
|
4540
4732
|
return;
|
|
4541
4733
|
if (this.host.root.hasPointerCapture(event.pointerId)) {
|
|
4542
4734
|
this.host.root.releasePointerCapture(event.pointerId);
|
|
@@ -4782,6 +4974,7 @@ var PivotTable = class {
|
|
|
4782
4974
|
if (this.frameId !== null) cancelAnimationFrame(this.frameId);
|
|
4783
4975
|
this.frameId = null;
|
|
4784
4976
|
this.stopWheelScrollSmoothing();
|
|
4977
|
+
this.stopContinuousScrollbarScroll();
|
|
4785
4978
|
this.stopResizeObserver?.();
|
|
4786
4979
|
this.stopResizeObserver = null;
|
|
4787
4980
|
this.host?.root.removeEventListener("pointermove", this.handlePointerMove);
|
|
@@ -5087,7 +5280,7 @@ var PivotTable = class {
|
|
|
5087
5280
|
measurePivotText(text) {
|
|
5088
5281
|
const context = this.host?.canvas.getContext("2d");
|
|
5089
5282
|
if (!context) return text.length * 8;
|
|
5090
|
-
context.font = `${this.options.theme?.fontSize ??
|
|
5283
|
+
context.font = `${this.options.theme?.fontSize ?? 14}px ${this.options.theme?.fontFamily ?? "sans-serif"}`;
|
|
5091
5284
|
return context.measureText(text).width;
|
|
5092
5285
|
}
|
|
5093
5286
|
updateRowHeaderSelectionDrag(event) {
|
|
@@ -5150,17 +5343,27 @@ var PivotTable = class {
|
|
|
5150
5343
|
event.preventDefault();
|
|
5151
5344
|
this.suppressNextClick = true;
|
|
5152
5345
|
if (hit.role === "decrease-button" || hit.role === "increase-button") {
|
|
5153
|
-
this.
|
|
5346
|
+
this.startContinuousScrollbarScroll(
|
|
5347
|
+
hit.axis,
|
|
5348
|
+
hit.role === "decrease-button" ? -1 : 1,
|
|
5349
|
+
event.pointerId
|
|
5350
|
+
);
|
|
5351
|
+
this.host.root.setPointerCapture(event.pointerId);
|
|
5154
5352
|
return;
|
|
5155
5353
|
}
|
|
5156
5354
|
const rootRect = this.host.root.getBoundingClientRect();
|
|
5157
5355
|
const pointer = hit.axis === "horizontal" ? event.clientX - rootRect.left : event.clientY - rootRect.top;
|
|
5158
5356
|
if (hit.role === "track") {
|
|
5159
|
-
const
|
|
5160
|
-
|
|
5357
|
+
const nextScroll = resolvePivotScrollbarTrackScroll({
|
|
5358
|
+
pointerValue: pointer,
|
|
5359
|
+
trackStart: hit.axis === "horizontal" ? axisLayout.track.x : axisLayout.track.y,
|
|
5360
|
+
trackLength: hit.axis === "horizontal" ? axisLayout.track.width : axisLayout.track.height,
|
|
5361
|
+
thumbLength: hit.axis === "horizontal" ? axisLayout.thumb.width : axisLayout.thumb.height,
|
|
5362
|
+
maxScroll: hit.axis === "horizontal" ? this.layout.viewport.maxScrollLeft : this.layout.viewport.maxScrollTop
|
|
5363
|
+
});
|
|
5161
5364
|
this.applyScrollPosition(
|
|
5162
|
-
|
|
5163
|
-
|
|
5365
|
+
hit.axis === "horizontal" ? nextScroll : this.scroll.left,
|
|
5366
|
+
hit.axis === "vertical" ? nextScroll : this.scroll.top
|
|
5164
5367
|
);
|
|
5165
5368
|
return;
|
|
5166
5369
|
}
|
|
@@ -5204,6 +5407,21 @@ var PivotTable = class {
|
|
|
5204
5407
|
this.scroll.top + (axis === "vertical" ? direction * this.layout.rowHeight : 0)
|
|
5205
5408
|
);
|
|
5206
5409
|
}
|
|
5410
|
+
startContinuousScrollbarScroll(axis, direction, pointerId) {
|
|
5411
|
+
this.stopContinuousScrollbarScroll();
|
|
5412
|
+
this.continuousScrollPointerId = pointerId;
|
|
5413
|
+
this.stepScrollbar(axis, direction);
|
|
5414
|
+
this.continuousScrollTimer = setInterval(() => {
|
|
5415
|
+
this.stepScrollbar(axis, direction);
|
|
5416
|
+
}, 80);
|
|
5417
|
+
}
|
|
5418
|
+
stopContinuousScrollbarScroll() {
|
|
5419
|
+
if (this.continuousScrollTimer !== null) {
|
|
5420
|
+
clearInterval(this.continuousScrollTimer);
|
|
5421
|
+
this.continuousScrollTimer = null;
|
|
5422
|
+
}
|
|
5423
|
+
this.continuousScrollPointerId = null;
|
|
5424
|
+
}
|
|
5207
5425
|
applyScrollPosition(left, top) {
|
|
5208
5426
|
if (!this.layout) return;
|
|
5209
5427
|
const next = {
|
|
@@ -5448,6 +5666,90 @@ function toArrayBuffer(value) {
|
|
|
5448
5666
|
return buffer;
|
|
5449
5667
|
}
|
|
5450
5668
|
|
|
5669
|
+
// src/adapters/storage/indexeddb-field-layout-storage.ts
|
|
5670
|
+
var DATABASE_NAME = "canvas_components_pivot_table_config";
|
|
5671
|
+
var STORE_NAME = "field_layout";
|
|
5672
|
+
var DB_VERSION = 1;
|
|
5673
|
+
var dbPromise = null;
|
|
5674
|
+
async function savePivotFieldLayout(key, layout) {
|
|
5675
|
+
try {
|
|
5676
|
+
const db = await openDatabase();
|
|
5677
|
+
await requestToPromise(
|
|
5678
|
+
db.transaction(STORE_NAME, "readwrite").objectStore(STORE_NAME).put(cloneLayout(layout), key)
|
|
5679
|
+
);
|
|
5680
|
+
} catch (error) {
|
|
5681
|
+
console.error("[PivotTable] \u5B57\u6BB5\u5E03\u5C40\u4FDD\u5B58\u5931\u8D25\uFF1A", error);
|
|
5682
|
+
}
|
|
5683
|
+
}
|
|
5684
|
+
async function loadPivotFieldLayout(key) {
|
|
5685
|
+
try {
|
|
5686
|
+
const db = await openDatabase();
|
|
5687
|
+
const value = await requestToPromise(
|
|
5688
|
+
db.transaction(STORE_NAME, "readonly").objectStore(STORE_NAME).get(key)
|
|
5689
|
+
);
|
|
5690
|
+
return isPivotFieldLayout(value) ? cloneLayout(value) : void 0;
|
|
5691
|
+
} catch (error) {
|
|
5692
|
+
console.error("[PivotTable] \u5B57\u6BB5\u5E03\u5C40\u8BFB\u53D6\u5931\u8D25\uFF1A", error);
|
|
5693
|
+
return void 0;
|
|
5694
|
+
}
|
|
5695
|
+
}
|
|
5696
|
+
async function clearPivotFieldLayout(key) {
|
|
5697
|
+
try {
|
|
5698
|
+
const db = await openDatabase();
|
|
5699
|
+
await requestToPromise(
|
|
5700
|
+
db.transaction(STORE_NAME, "readwrite").objectStore(STORE_NAME).delete(key)
|
|
5701
|
+
);
|
|
5702
|
+
} catch (error) {
|
|
5703
|
+
console.error("[PivotTable] \u5B57\u6BB5\u5E03\u5C40\u6E05\u9664\u5931\u8D25\uFF1A", error);
|
|
5704
|
+
}
|
|
5705
|
+
}
|
|
5706
|
+
function openDatabase() {
|
|
5707
|
+
if (dbPromise) return dbPromise;
|
|
5708
|
+
dbPromise = new Promise((resolve, reject) => {
|
|
5709
|
+
if (typeof indexedDB === "undefined") {
|
|
5710
|
+
reject(new Error("\u5F53\u524D\u73AF\u5883\u4E0D\u652F\u6301 IndexedDB"));
|
|
5711
|
+
return;
|
|
5712
|
+
}
|
|
5713
|
+
const request = indexedDB.open(DATABASE_NAME, DB_VERSION);
|
|
5714
|
+
request.onupgradeneeded = () => {
|
|
5715
|
+
const db = request.result;
|
|
5716
|
+
if (!db.objectStoreNames.contains(STORE_NAME)) {
|
|
5717
|
+
db.createObjectStore(STORE_NAME);
|
|
5718
|
+
}
|
|
5719
|
+
};
|
|
5720
|
+
request.onsuccess = () => resolve(request.result);
|
|
5721
|
+
request.onerror = () => reject(request.error);
|
|
5722
|
+
});
|
|
5723
|
+
dbPromise.catch(() => {
|
|
5724
|
+
dbPromise = null;
|
|
5725
|
+
});
|
|
5726
|
+
return dbPromise;
|
|
5727
|
+
}
|
|
5728
|
+
function requestToPromise(request) {
|
|
5729
|
+
return new Promise((resolve, reject) => {
|
|
5730
|
+
request.onsuccess = () => resolve(request.result);
|
|
5731
|
+
request.onerror = () => reject(request.error);
|
|
5732
|
+
});
|
|
5733
|
+
}
|
|
5734
|
+
function isPivotFieldLayout(value) {
|
|
5735
|
+
if (!value || typeof value !== "object") return false;
|
|
5736
|
+
const layout = value;
|
|
5737
|
+
return ["filters", "columns", "rows", "values"].every(
|
|
5738
|
+
(key) => isStringArray(layout[key])
|
|
5739
|
+
);
|
|
5740
|
+
}
|
|
5741
|
+
function isStringArray(value) {
|
|
5742
|
+
return Array.isArray(value) && value.every((item) => typeof item === "string");
|
|
5743
|
+
}
|
|
5744
|
+
function cloneLayout(layout) {
|
|
5745
|
+
return {
|
|
5746
|
+
filters: [...layout.filters],
|
|
5747
|
+
columns: [...layout.columns],
|
|
5748
|
+
rows: [...layout.rows],
|
|
5749
|
+
values: [...layout.values]
|
|
5750
|
+
};
|
|
5751
|
+
}
|
|
5752
|
+
|
|
5451
5753
|
// src/domain/field/pivot-field-layout.ts
|
|
5452
5754
|
function createEmptyPivotFieldLayout() {
|
|
5453
5755
|
return { filters: [], columns: [], rows: [], values: [] };
|
|
@@ -5611,6 +5913,7 @@ exports.PivotTable = PivotTable;
|
|
|
5611
5913
|
exports.aggregatePivotRecords = aggregatePivotRecords;
|
|
5612
5914
|
exports.buildPivotDimensionTree = buildPivotDimensionTree;
|
|
5613
5915
|
exports.buildPivotLayoutSnapshot = buildPivotLayoutSnapshot;
|
|
5916
|
+
exports.clearPivotFieldLayout = clearPivotFieldLayout;
|
|
5614
5917
|
exports.computePivotScrollbarLayout = computePivotScrollbarLayout;
|
|
5615
5918
|
exports.createBuiltInPivotAggregators = createBuiltInPivotAggregators;
|
|
5616
5919
|
exports.createEmptyPivotFieldLayout = createEmptyPivotFieldLayout;
|
|
@@ -5634,6 +5937,7 @@ exports.getPivotDimensionFilterOptions = getPivotDimensionFilterOptions;
|
|
|
5634
5937
|
exports.hitTestPivotLayout = hitTestPivotLayout;
|
|
5635
5938
|
exports.hitTestPivotResize = hitTestPivotResize;
|
|
5636
5939
|
exports.hitTestPivotScrollbar = hitTestPivotScrollbar;
|
|
5940
|
+
exports.loadPivotFieldLayout = loadPivotFieldLayout;
|
|
5637
5941
|
exports.movePivotField = movePivotField;
|
|
5638
5942
|
exports.removePivotField = removePivotField;
|
|
5639
5943
|
exports.renderPivotTable = renderPivotTable;
|
|
@@ -5645,5 +5949,6 @@ exports.resolvePivotDimensionWidth = resolvePivotDimensionWidth;
|
|
|
5645
5949
|
exports.resolvePivotFieldLayoutOptions = resolvePivotFieldLayoutOptions;
|
|
5646
5950
|
exports.resolvePivotIndicatorAggregator = resolvePivotIndicatorAggregator;
|
|
5647
5951
|
exports.resolvePivotValueFieldTitle = resolvePivotValueFieldTitle;
|
|
5952
|
+
exports.savePivotFieldLayout = savePivotFieldLayout;
|
|
5648
5953
|
//# sourceMappingURL=index.cjs.map
|
|
5649
5954
|
//# sourceMappingURL=index.cjs.map
|