@canvas-components/pivot-table 0.1.8 → 0.1.9
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 +134 -25
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +5 -0
- package/dist/index.d.ts +5 -0
- package/dist/index.js +134 -25
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.cjs
CHANGED
|
@@ -995,7 +995,7 @@ function computePivotScrollbarLayout(params) {
|
|
|
995
995
|
}
|
|
996
996
|
if (hasVertical) {
|
|
997
997
|
const fullHeight = Math.max(
|
|
998
|
-
params.bodyHeight - VERTICAL_TOP_INSET
|
|
998
|
+
params.bodyHeight - VERTICAL_TOP_INSET,
|
|
999
999
|
0
|
|
1000
1000
|
);
|
|
1001
1001
|
const buttonSize = Math.min(SCROLLBAR_THICKNESS, fullHeight / 3);
|
|
@@ -1051,6 +1051,7 @@ var DEFAULT_ROW_HEIGHT = 40;
|
|
|
1051
1051
|
var DEFAULT_COLUMN_WIDTH = 120;
|
|
1052
1052
|
var DEFAULT_HEADER_SIZE = 40;
|
|
1053
1053
|
var DEFAULT_ROW_DIMENSION_WIDTH2 = 140;
|
|
1054
|
+
var SCROLLBAR_THICKNESS2 = 10;
|
|
1054
1055
|
function buildPivotLayoutSnapshot(params) {
|
|
1055
1056
|
const { model, options, visibleRowNodes, visibleColumnNodes, width, height } = params;
|
|
1056
1057
|
const indicatorLayout = options.indicatorLayout ?? "columns";
|
|
@@ -1083,8 +1084,8 @@ function buildPivotLayoutSnapshot(params) {
|
|
|
1083
1084
|
const columnHeaderHeight = columnHeaderLevels * DEFAULT_HEADER_SIZE;
|
|
1084
1085
|
const reportFilterHeight = (options.filterDimensions?.length ?? 0) * rowHeight;
|
|
1085
1086
|
const tableHeaderHeight = reportFilterHeight + columnHeaderHeight;
|
|
1086
|
-
const
|
|
1087
|
-
const
|
|
1087
|
+
const rawBodyWidth = Math.max(width - rowHeaderWidth, 0);
|
|
1088
|
+
const rawAvailableBodyHeight = Math.max(height - tableHeaderHeight, 0);
|
|
1088
1089
|
const rowSlots = createPivotAxisSlots({
|
|
1089
1090
|
nodes: visibleRowNodes,
|
|
1090
1091
|
indicators: options.indicators.map((indicator) => indicator.key),
|
|
@@ -1107,19 +1108,52 @@ function buildPivotLayoutSnapshot(params) {
|
|
|
1107
1108
|
const columnOffsets = createOffsets(columnWidths);
|
|
1108
1109
|
const totalBodyWidth = columnOffsets[columnOffsets.length - 1] ?? 0;
|
|
1109
1110
|
const totalBodyHeight = rowSlots.length * rowHeight;
|
|
1111
|
+
let hasHorizontalScrollbar = totalBodyWidth > rawBodyWidth;
|
|
1112
|
+
let hasVerticalScrollbar = totalBodyHeight > rawAvailableBodyHeight;
|
|
1113
|
+
for (let index = 0; index < 2; index += 1) {
|
|
1114
|
+
hasHorizontalScrollbar = totalBodyWidth > Math.max(
|
|
1115
|
+
rawBodyWidth - (hasVerticalScrollbar ? SCROLLBAR_THICKNESS2 : 0),
|
|
1116
|
+
0
|
|
1117
|
+
);
|
|
1118
|
+
hasVerticalScrollbar = totalBodyHeight > Math.max(
|
|
1119
|
+
rawAvailableBodyHeight - (hasHorizontalScrollbar ? SCROLLBAR_THICKNESS2 : 0),
|
|
1120
|
+
0
|
|
1121
|
+
);
|
|
1122
|
+
}
|
|
1123
|
+
const bodyWidth = Math.max(
|
|
1124
|
+
rawBodyWidth - (hasVerticalScrollbar ? SCROLLBAR_THICKNESS2 : 0),
|
|
1125
|
+
0
|
|
1126
|
+
);
|
|
1127
|
+
const availableBodyHeight = Math.max(
|
|
1128
|
+
rawAvailableBodyHeight - (hasHorizontalScrollbar ? SCROLLBAR_THICKNESS2 : 0),
|
|
1129
|
+
0
|
|
1130
|
+
);
|
|
1131
|
+
const frozenBottomRowRange = resolveFrozenBottomRowRange(rowSlots);
|
|
1132
|
+
const frozenBottomRowCount = frozenBottomRowRange ? frozenBottomRowRange.end - frozenBottomRowRange.start : 0;
|
|
1133
|
+
const frozenBottomHeight = frozenBottomRowCount * rowHeight;
|
|
1134
|
+
const scrollableRowCount = frozenBottomRowRange?.start ?? rowSlots.length;
|
|
1135
|
+
const totalScrollableBodyHeight = scrollableRowCount * rowHeight;
|
|
1110
1136
|
const tableWidth = rowHeaderWidth + Math.min(bodyWidth, totalBodyWidth);
|
|
1111
1137
|
const bodyHeight = Math.min(availableBodyHeight, totalBodyHeight);
|
|
1112
|
-
const
|
|
1138
|
+
const visibleFrozenBottomHeight = Math.min(frozenBottomHeight, bodyHeight);
|
|
1139
|
+
const scrollableBodyHeight = Math.max(
|
|
1140
|
+
bodyHeight - visibleFrozenBottomHeight,
|
|
1141
|
+
0
|
|
1142
|
+
);
|
|
1143
|
+
const fittedHeight = tableHeaderHeight + bodyHeight + (hasHorizontalScrollbar ? SCROLLBAR_THICKNESS2 : 0);
|
|
1113
1144
|
const maxScrollLeft = Math.max(totalBodyWidth - bodyWidth, 0);
|
|
1114
|
-
const maxScrollTop = Math.max(
|
|
1145
|
+
const maxScrollTop = Math.max(
|
|
1146
|
+
totalScrollableBodyHeight - scrollableBodyHeight,
|
|
1147
|
+
0
|
|
1148
|
+
);
|
|
1115
1149
|
const scroll = {
|
|
1116
1150
|
left: utils.clamp(params.scroll.left, 0, maxScrollLeft),
|
|
1117
1151
|
top: utils.clamp(params.scroll.top, 0, maxScrollTop)
|
|
1118
1152
|
};
|
|
1119
1153
|
const visibleRowRange = resolveVisibleRange({
|
|
1120
|
-
count:
|
|
1154
|
+
count: scrollableRowCount,
|
|
1121
1155
|
itemSize: rowHeight,
|
|
1122
|
-
viewportSize:
|
|
1156
|
+
viewportSize: scrollableBodyHeight,
|
|
1123
1157
|
offset: scroll.top,
|
|
1124
1158
|
overscan: options.overscanRowCount ?? 1
|
|
1125
1159
|
});
|
|
@@ -1154,6 +1188,23 @@ function buildPivotLayoutSnapshot(params) {
|
|
|
1154
1188
|
dimensionCount: options.rows.length,
|
|
1155
1189
|
indicators: options.indicators
|
|
1156
1190
|
});
|
|
1191
|
+
if (frozenBottomRowRange) {
|
|
1192
|
+
rowHeaderCells.push(
|
|
1193
|
+
...buildRowHeaderCells({
|
|
1194
|
+
model,
|
|
1195
|
+
options,
|
|
1196
|
+
slots: rowSlots,
|
|
1197
|
+
range: frozenBottomRowRange,
|
|
1198
|
+
rowHeight,
|
|
1199
|
+
rowDimensionWidths,
|
|
1200
|
+
columnHeaderHeight: tableHeaderHeight,
|
|
1201
|
+
scrollTop: frozenBottomRowRange.start * rowHeight - scrollableBodyHeight,
|
|
1202
|
+
expandedNodeIds: params.expandedRowNodeIds,
|
|
1203
|
+
dimensionCount: options.rows.length,
|
|
1204
|
+
indicators: options.indicators
|
|
1205
|
+
})
|
|
1206
|
+
);
|
|
1207
|
+
}
|
|
1157
1208
|
const columnHeaderCells = buildColumnHeaderCells({
|
|
1158
1209
|
model,
|
|
1159
1210
|
options,
|
|
@@ -1180,14 +1231,34 @@ function buildPivotLayoutSnapshot(params) {
|
|
|
1180
1231
|
columnHeaderHeight: tableHeaderHeight,
|
|
1181
1232
|
scroll
|
|
1182
1233
|
});
|
|
1234
|
+
if (frozenBottomRowRange) {
|
|
1235
|
+
bodyCells.push(
|
|
1236
|
+
...buildBodyCells({
|
|
1237
|
+
model,
|
|
1238
|
+
rowSlots,
|
|
1239
|
+
columnSlots,
|
|
1240
|
+
rowRange: frozenBottomRowRange,
|
|
1241
|
+
columnRange: visibleColumnRange,
|
|
1242
|
+
rowHeight,
|
|
1243
|
+
columnWidths,
|
|
1244
|
+
columnOffsets,
|
|
1245
|
+
rowHeaderWidth,
|
|
1246
|
+
columnHeaderHeight: tableHeaderHeight,
|
|
1247
|
+
scroll: {
|
|
1248
|
+
left: scroll.left,
|
|
1249
|
+
top: frozenBottomRowRange.start * rowHeight - scrollableBodyHeight
|
|
1250
|
+
}
|
|
1251
|
+
})
|
|
1252
|
+
);
|
|
1253
|
+
}
|
|
1183
1254
|
const scrollbar = computePivotScrollbarLayout({
|
|
1184
|
-
width,
|
|
1255
|
+
width: tableWidth + (hasVerticalScrollbar ? SCROLLBAR_THICKNESS2 : 0),
|
|
1185
1256
|
height: fittedHeight,
|
|
1186
1257
|
bodyTop: tableHeaderHeight,
|
|
1187
|
-
bodyHeight,
|
|
1258
|
+
bodyHeight: scrollableBodyHeight,
|
|
1188
1259
|
bodyWidth,
|
|
1189
1260
|
totalBodyWidth,
|
|
1190
|
-
totalBodyHeight,
|
|
1261
|
+
totalBodyHeight: totalScrollableBodyHeight,
|
|
1191
1262
|
scrollLeft: scroll.left,
|
|
1192
1263
|
scrollTop: scroll.top,
|
|
1193
1264
|
maxScrollLeft,
|
|
@@ -1239,6 +1310,7 @@ function buildPivotLayoutSnapshot(params) {
|
|
|
1239
1310
|
rowSlots,
|
|
1240
1311
|
columnSlots,
|
|
1241
1312
|
visibleRowRange,
|
|
1313
|
+
frozenBottomRowRange,
|
|
1242
1314
|
visibleColumnRange,
|
|
1243
1315
|
reportFilterCells,
|
|
1244
1316
|
rowFieldHeaderCells,
|
|
@@ -1373,6 +1445,13 @@ function createAxisSlot(node, indicatorKey) {
|
|
|
1373
1445
|
kind: node?.kind ?? "empty"
|
|
1374
1446
|
};
|
|
1375
1447
|
}
|
|
1448
|
+
function resolveFrozenBottomRowRange(slots) {
|
|
1449
|
+
let start = slots.length;
|
|
1450
|
+
while (start > 0 && slots[start - 1]?.kind === "grandTotal") {
|
|
1451
|
+
start -= 1;
|
|
1452
|
+
}
|
|
1453
|
+
return start < slots.length ? { start, end: slots.length } : null;
|
|
1454
|
+
}
|
|
1376
1455
|
function buildRowHeaderCells(params) {
|
|
1377
1456
|
const cells = [];
|
|
1378
1457
|
for (let rowIndex = params.range.start; rowIndex < params.range.end; rowIndex += 1) {
|
|
@@ -2635,8 +2714,8 @@ function resolvePivotSelectionRect(layout, selection) {
|
|
|
2635
2714
|
const kind = resolvePivotSelectionKind(selection);
|
|
2636
2715
|
const startX = kind === "row" || kind === "row-cell" ? layout.rowHeaderRect.x : layout.bodyRect.x + (layout.columnOffsets[indexes.startColumnIndex] ?? 0) - layout.scroll.left;
|
|
2637
2716
|
const endX = layout.bodyRect.x + (layout.columnOffsets[indexes.endColumnIndex] ?? 0) + (layout.columnWidths[indexes.endColumnIndex] ?? layout.columnWidth) - layout.scroll.left;
|
|
2638
|
-
const startY = kind === "column" ? layout.columnHeaderRect.y : layout
|
|
2639
|
-
const endY = layout
|
|
2717
|
+
const startY = kind === "column" ? layout.columnHeaderRect.y : resolvePivotRowTop(layout, indexes.startRowIndex);
|
|
2718
|
+
const endY = resolvePivotRowTop(layout, indexes.endRowIndex) + layout.rowHeight;
|
|
2640
2719
|
const clipRect = resolvePivotSelectionClipRect(layout, selection);
|
|
2641
2720
|
const visibleStartX = Math.max(startX, clipRect.x);
|
|
2642
2721
|
const visibleEndX = Math.min(endX, clipRect.x + clipRect.width);
|
|
@@ -2650,6 +2729,13 @@ function resolvePivotSelectionRect(layout, selection) {
|
|
|
2650
2729
|
height: visibleEndY - visibleStartY
|
|
2651
2730
|
};
|
|
2652
2731
|
}
|
|
2732
|
+
function resolvePivotRowTop(layout, rowIndex) {
|
|
2733
|
+
const frozenRange = layout.frozenBottomRowRange;
|
|
2734
|
+
if (frozenRange && rowIndex >= frozenRange.start && rowIndex < frozenRange.end) {
|
|
2735
|
+
return layout.bodyRect.y + layout.bodyRect.height - (frozenRange.end - rowIndex) * layout.rowHeight;
|
|
2736
|
+
}
|
|
2737
|
+
return layout.bodyRect.y + rowIndex * layout.rowHeight - layout.scroll.top;
|
|
2738
|
+
}
|
|
2653
2739
|
function resolvePivotSelectionClipRect(layout, selection) {
|
|
2654
2740
|
const kind = resolvePivotSelectionKind(selection);
|
|
2655
2741
|
if (kind === "row" || kind === "row-cell") {
|
|
@@ -2794,26 +2880,29 @@ var defaultPivotTableTheme = {
|
|
|
2794
2880
|
};
|
|
2795
2881
|
|
|
2796
2882
|
// src/rendering/draw-scrollbars.ts
|
|
2797
|
-
function drawPivotScrollbars(context, layout) {
|
|
2883
|
+
function drawPivotScrollbars(context, layout, borderColor) {
|
|
2798
2884
|
context.save();
|
|
2799
2885
|
if (layout.horizontal) {
|
|
2800
|
-
drawAxis(context, layout.horizontal, "horizontal");
|
|
2886
|
+
drawAxis(context, layout.horizontal, "horizontal", borderColor);
|
|
2801
2887
|
}
|
|
2802
2888
|
if (layout.vertical) {
|
|
2803
|
-
drawAxis(context, layout.vertical, "vertical");
|
|
2889
|
+
drawAxis(context, layout.vertical, "vertical", borderColor);
|
|
2804
2890
|
}
|
|
2805
2891
|
context.restore();
|
|
2806
2892
|
}
|
|
2807
|
-
function drawAxis(context, layout, axis) {
|
|
2808
|
-
drawTrack(context, layout.track);
|
|
2893
|
+
function drawAxis(context, layout, axis, borderColor) {
|
|
2894
|
+
drawTrack(context, layout.track, borderColor);
|
|
2809
2895
|
drawButton(context, layout.decreaseButton, axis, "decrease");
|
|
2810
2896
|
drawButton(context, layout.increaseButton, axis, "increase");
|
|
2811
2897
|
drawThumb(context, layout.thumb);
|
|
2812
2898
|
}
|
|
2813
|
-
function drawTrack(context, rect) {
|
|
2899
|
+
function drawTrack(context, rect, borderColor) {
|
|
2814
2900
|
context.fillStyle = "rgba(148, 163, 184, 0.16)";
|
|
2901
|
+
context.strokeStyle = borderColor;
|
|
2902
|
+
context.lineWidth = 1;
|
|
2815
2903
|
roundRect(context, rect, rect.height / 2);
|
|
2816
2904
|
context.fill();
|
|
2905
|
+
context.stroke();
|
|
2817
2906
|
}
|
|
2818
2907
|
function drawThumb(context, rect) {
|
|
2819
2908
|
context.fillStyle = "rgba(100, 116, 139, 0.8)";
|
|
@@ -3081,7 +3170,7 @@ function renderPivotTable(params) {
|
|
|
3081
3170
|
theme.borderColor
|
|
3082
3171
|
);
|
|
3083
3172
|
if (options.debug) drawDebugOverlay(context, layout, theme);
|
|
3084
|
-
drawPivotScrollbars(context, layout.scrollbar);
|
|
3173
|
+
drawPivotScrollbars(context, layout.scrollbar, theme.borderColor);
|
|
3085
3174
|
drawPivotViewportRightBorder(
|
|
3086
3175
|
context,
|
|
3087
3176
|
{ x: 0, y: 0, width: layout.tableWidth, height: contentHeight },
|
|
@@ -3599,7 +3688,7 @@ function hitTestPivotLayout(layout, x, y) {
|
|
|
3599
3688
|
onSortIcon: false
|
|
3600
3689
|
};
|
|
3601
3690
|
}
|
|
3602
|
-
const bodyCell = layout.bodyCells
|
|
3691
|
+
const bodyCell = findLastContaining(layout.bodyCells, x, y);
|
|
3603
3692
|
if (bodyCell) {
|
|
3604
3693
|
return {
|
|
3605
3694
|
region: "body",
|
|
@@ -3609,9 +3698,7 @@ function hitTestPivotLayout(layout, x, y) {
|
|
|
3609
3698
|
onSortIcon: false
|
|
3610
3699
|
};
|
|
3611
3700
|
}
|
|
3612
|
-
const rowHeaderCell = layout.rowHeaderCells
|
|
3613
|
-
(cell) => contains(cell.rect, x, y)
|
|
3614
|
-
);
|
|
3701
|
+
const rowHeaderCell = findLastContaining(layout.rowHeaderCells, x, y);
|
|
3615
3702
|
if (rowHeaderCell) {
|
|
3616
3703
|
return {
|
|
3617
3704
|
region: "rowHeader",
|
|
@@ -3664,6 +3751,13 @@ function isSortIconHit(cell, x) {
|
|
|
3664
3751
|
function contains(rect, x, y) {
|
|
3665
3752
|
return x >= rect.x && x < rect.x + rect.width && y >= rect.y && y < rect.y + rect.height;
|
|
3666
3753
|
}
|
|
3754
|
+
function findLastContaining(cells, x, y) {
|
|
3755
|
+
for (let index = cells.length - 1; index >= 0; index -= 1) {
|
|
3756
|
+
const cell = cells[index];
|
|
3757
|
+
if (cell && contains(cell.rect, x, y)) return cell;
|
|
3758
|
+
}
|
|
3759
|
+
return void 0;
|
|
3760
|
+
}
|
|
3667
3761
|
|
|
3668
3762
|
// src/interaction/text-selection.ts
|
|
3669
3763
|
function resolvePivotTextOffset(params) {
|
|
@@ -3900,6 +3994,16 @@ function createAddress(rowSlot, columnSlot, indicatorKey) {
|
|
|
3900
3994
|
// src/domain/interaction/row-selection.ts
|
|
3901
3995
|
function resolvePivotRowIndexAtY(layout, y) {
|
|
3902
3996
|
if (layout.rowSlots.length === 0 || layout.rowHeight <= 0) return null;
|
|
3997
|
+
const frozenRange = layout.frozenBottomRowRange;
|
|
3998
|
+
if (frozenRange) {
|
|
3999
|
+
const frozenTop = layout.bodyRect.y + layout.bodyRect.height - (frozenRange.end - frozenRange.start) * layout.rowHeight;
|
|
4000
|
+
if (y >= frozenTop) {
|
|
4001
|
+
return Math.min(
|
|
4002
|
+
frozenRange.start + Math.floor((y - frozenTop) / layout.rowHeight),
|
|
4003
|
+
frozenRange.end - 1
|
|
4004
|
+
);
|
|
4005
|
+
}
|
|
4006
|
+
}
|
|
3903
4007
|
const index = Math.floor(
|
|
3904
4008
|
(y - layout.bodyRect.y + layout.scroll.top) / layout.rowHeight
|
|
3905
4009
|
);
|
|
@@ -4605,11 +4709,16 @@ var PivotTable = class {
|
|
|
4605
4709
|
(slot) => (slot.nodeId ?? "") === address.columnNodeId && (!slot.indicatorKey || slot.indicatorKey === address.indicatorKey)
|
|
4606
4710
|
);
|
|
4607
4711
|
if (rowIndex < 0 || columnIndex < 0) return false;
|
|
4608
|
-
const
|
|
4712
|
+
const frozenRange = this.layout.frozenBottomRowRange;
|
|
4713
|
+
const frozenBottomHeight = frozenRange ? (frozenRange.end - frozenRange.start) * this.layout.rowHeight : 0;
|
|
4714
|
+
const top = frozenRange && rowIndex >= frozenRange.start ? this.scroll.top : resolveVisibleOffset({
|
|
4609
4715
|
current: this.scroll.top,
|
|
4610
4716
|
itemStart: rowIndex * this.layout.rowHeight,
|
|
4611
4717
|
itemSize: this.layout.rowHeight,
|
|
4612
|
-
viewportSize:
|
|
4718
|
+
viewportSize: Math.max(
|
|
4719
|
+
this.layout.bodyRect.height - frozenBottomHeight,
|
|
4720
|
+
0
|
|
4721
|
+
)
|
|
4613
4722
|
});
|
|
4614
4723
|
const left = resolveVisibleOffset({
|
|
4615
4724
|
current: this.scroll.left,
|