@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.d.cts
CHANGED
package/dist/index.d.ts
CHANGED
package/dist/index.js
CHANGED
|
@@ -974,7 +974,7 @@ function computePivotScrollbarLayout(params) {
|
|
|
974
974
|
}
|
|
975
975
|
if (hasVertical) {
|
|
976
976
|
const fullHeight = Math.max(
|
|
977
|
-
params.bodyHeight - VERTICAL_TOP_INSET
|
|
977
|
+
params.bodyHeight - VERTICAL_TOP_INSET,
|
|
978
978
|
0
|
|
979
979
|
);
|
|
980
980
|
const buttonSize = Math.min(SCROLLBAR_THICKNESS, fullHeight / 3);
|
|
@@ -1030,6 +1030,7 @@ var DEFAULT_ROW_HEIGHT = 40;
|
|
|
1030
1030
|
var DEFAULT_COLUMN_WIDTH = 120;
|
|
1031
1031
|
var DEFAULT_HEADER_SIZE = 40;
|
|
1032
1032
|
var DEFAULT_ROW_DIMENSION_WIDTH2 = 140;
|
|
1033
|
+
var SCROLLBAR_THICKNESS2 = 10;
|
|
1033
1034
|
function buildPivotLayoutSnapshot(params) {
|
|
1034
1035
|
const { model, options, visibleRowNodes, visibleColumnNodes, width, height } = params;
|
|
1035
1036
|
const indicatorLayout = options.indicatorLayout ?? "columns";
|
|
@@ -1062,8 +1063,8 @@ function buildPivotLayoutSnapshot(params) {
|
|
|
1062
1063
|
const columnHeaderHeight = columnHeaderLevels * DEFAULT_HEADER_SIZE;
|
|
1063
1064
|
const reportFilterHeight = (options.filterDimensions?.length ?? 0) * rowHeight;
|
|
1064
1065
|
const tableHeaderHeight = reportFilterHeight + columnHeaderHeight;
|
|
1065
|
-
const
|
|
1066
|
-
const
|
|
1066
|
+
const rawBodyWidth = Math.max(width - rowHeaderWidth, 0);
|
|
1067
|
+
const rawAvailableBodyHeight = Math.max(height - tableHeaderHeight, 0);
|
|
1067
1068
|
const rowSlots = createPivotAxisSlots({
|
|
1068
1069
|
nodes: visibleRowNodes,
|
|
1069
1070
|
indicators: options.indicators.map((indicator) => indicator.key),
|
|
@@ -1086,19 +1087,52 @@ function buildPivotLayoutSnapshot(params) {
|
|
|
1086
1087
|
const columnOffsets = createOffsets(columnWidths);
|
|
1087
1088
|
const totalBodyWidth = columnOffsets[columnOffsets.length - 1] ?? 0;
|
|
1088
1089
|
const totalBodyHeight = rowSlots.length * rowHeight;
|
|
1090
|
+
let hasHorizontalScrollbar = totalBodyWidth > rawBodyWidth;
|
|
1091
|
+
let hasVerticalScrollbar = totalBodyHeight > rawAvailableBodyHeight;
|
|
1092
|
+
for (let index = 0; index < 2; index += 1) {
|
|
1093
|
+
hasHorizontalScrollbar = totalBodyWidth > Math.max(
|
|
1094
|
+
rawBodyWidth - (hasVerticalScrollbar ? SCROLLBAR_THICKNESS2 : 0),
|
|
1095
|
+
0
|
|
1096
|
+
);
|
|
1097
|
+
hasVerticalScrollbar = totalBodyHeight > Math.max(
|
|
1098
|
+
rawAvailableBodyHeight - (hasHorizontalScrollbar ? SCROLLBAR_THICKNESS2 : 0),
|
|
1099
|
+
0
|
|
1100
|
+
);
|
|
1101
|
+
}
|
|
1102
|
+
const bodyWidth = Math.max(
|
|
1103
|
+
rawBodyWidth - (hasVerticalScrollbar ? SCROLLBAR_THICKNESS2 : 0),
|
|
1104
|
+
0
|
|
1105
|
+
);
|
|
1106
|
+
const availableBodyHeight = Math.max(
|
|
1107
|
+
rawAvailableBodyHeight - (hasHorizontalScrollbar ? SCROLLBAR_THICKNESS2 : 0),
|
|
1108
|
+
0
|
|
1109
|
+
);
|
|
1110
|
+
const frozenBottomRowRange = resolveFrozenBottomRowRange(rowSlots);
|
|
1111
|
+
const frozenBottomRowCount = frozenBottomRowRange ? frozenBottomRowRange.end - frozenBottomRowRange.start : 0;
|
|
1112
|
+
const frozenBottomHeight = frozenBottomRowCount * rowHeight;
|
|
1113
|
+
const scrollableRowCount = frozenBottomRowRange?.start ?? rowSlots.length;
|
|
1114
|
+
const totalScrollableBodyHeight = scrollableRowCount * rowHeight;
|
|
1089
1115
|
const tableWidth = rowHeaderWidth + Math.min(bodyWidth, totalBodyWidth);
|
|
1090
1116
|
const bodyHeight = Math.min(availableBodyHeight, totalBodyHeight);
|
|
1091
|
-
const
|
|
1117
|
+
const visibleFrozenBottomHeight = Math.min(frozenBottomHeight, bodyHeight);
|
|
1118
|
+
const scrollableBodyHeight = Math.max(
|
|
1119
|
+
bodyHeight - visibleFrozenBottomHeight,
|
|
1120
|
+
0
|
|
1121
|
+
);
|
|
1122
|
+
const fittedHeight = tableHeaderHeight + bodyHeight + (hasHorizontalScrollbar ? SCROLLBAR_THICKNESS2 : 0);
|
|
1092
1123
|
const maxScrollLeft = Math.max(totalBodyWidth - bodyWidth, 0);
|
|
1093
|
-
const maxScrollTop = Math.max(
|
|
1124
|
+
const maxScrollTop = Math.max(
|
|
1125
|
+
totalScrollableBodyHeight - scrollableBodyHeight,
|
|
1126
|
+
0
|
|
1127
|
+
);
|
|
1094
1128
|
const scroll = {
|
|
1095
1129
|
left: clamp(params.scroll.left, 0, maxScrollLeft),
|
|
1096
1130
|
top: clamp(params.scroll.top, 0, maxScrollTop)
|
|
1097
1131
|
};
|
|
1098
1132
|
const visibleRowRange = resolveVisibleRange({
|
|
1099
|
-
count:
|
|
1133
|
+
count: scrollableRowCount,
|
|
1100
1134
|
itemSize: rowHeight,
|
|
1101
|
-
viewportSize:
|
|
1135
|
+
viewportSize: scrollableBodyHeight,
|
|
1102
1136
|
offset: scroll.top,
|
|
1103
1137
|
overscan: options.overscanRowCount ?? 1
|
|
1104
1138
|
});
|
|
@@ -1133,6 +1167,23 @@ function buildPivotLayoutSnapshot(params) {
|
|
|
1133
1167
|
dimensionCount: options.rows.length,
|
|
1134
1168
|
indicators: options.indicators
|
|
1135
1169
|
});
|
|
1170
|
+
if (frozenBottomRowRange) {
|
|
1171
|
+
rowHeaderCells.push(
|
|
1172
|
+
...buildRowHeaderCells({
|
|
1173
|
+
model,
|
|
1174
|
+
options,
|
|
1175
|
+
slots: rowSlots,
|
|
1176
|
+
range: frozenBottomRowRange,
|
|
1177
|
+
rowHeight,
|
|
1178
|
+
rowDimensionWidths,
|
|
1179
|
+
columnHeaderHeight: tableHeaderHeight,
|
|
1180
|
+
scrollTop: frozenBottomRowRange.start * rowHeight - scrollableBodyHeight,
|
|
1181
|
+
expandedNodeIds: params.expandedRowNodeIds,
|
|
1182
|
+
dimensionCount: options.rows.length,
|
|
1183
|
+
indicators: options.indicators
|
|
1184
|
+
})
|
|
1185
|
+
);
|
|
1186
|
+
}
|
|
1136
1187
|
const columnHeaderCells = buildColumnHeaderCells({
|
|
1137
1188
|
model,
|
|
1138
1189
|
options,
|
|
@@ -1159,14 +1210,34 @@ function buildPivotLayoutSnapshot(params) {
|
|
|
1159
1210
|
columnHeaderHeight: tableHeaderHeight,
|
|
1160
1211
|
scroll
|
|
1161
1212
|
});
|
|
1213
|
+
if (frozenBottomRowRange) {
|
|
1214
|
+
bodyCells.push(
|
|
1215
|
+
...buildBodyCells({
|
|
1216
|
+
model,
|
|
1217
|
+
rowSlots,
|
|
1218
|
+
columnSlots,
|
|
1219
|
+
rowRange: frozenBottomRowRange,
|
|
1220
|
+
columnRange: visibleColumnRange,
|
|
1221
|
+
rowHeight,
|
|
1222
|
+
columnWidths,
|
|
1223
|
+
columnOffsets,
|
|
1224
|
+
rowHeaderWidth,
|
|
1225
|
+
columnHeaderHeight: tableHeaderHeight,
|
|
1226
|
+
scroll: {
|
|
1227
|
+
left: scroll.left,
|
|
1228
|
+
top: frozenBottomRowRange.start * rowHeight - scrollableBodyHeight
|
|
1229
|
+
}
|
|
1230
|
+
})
|
|
1231
|
+
);
|
|
1232
|
+
}
|
|
1162
1233
|
const scrollbar = computePivotScrollbarLayout({
|
|
1163
|
-
width,
|
|
1234
|
+
width: tableWidth + (hasVerticalScrollbar ? SCROLLBAR_THICKNESS2 : 0),
|
|
1164
1235
|
height: fittedHeight,
|
|
1165
1236
|
bodyTop: tableHeaderHeight,
|
|
1166
|
-
bodyHeight,
|
|
1237
|
+
bodyHeight: scrollableBodyHeight,
|
|
1167
1238
|
bodyWidth,
|
|
1168
1239
|
totalBodyWidth,
|
|
1169
|
-
totalBodyHeight,
|
|
1240
|
+
totalBodyHeight: totalScrollableBodyHeight,
|
|
1170
1241
|
scrollLeft: scroll.left,
|
|
1171
1242
|
scrollTop: scroll.top,
|
|
1172
1243
|
maxScrollLeft,
|
|
@@ -1218,6 +1289,7 @@ function buildPivotLayoutSnapshot(params) {
|
|
|
1218
1289
|
rowSlots,
|
|
1219
1290
|
columnSlots,
|
|
1220
1291
|
visibleRowRange,
|
|
1292
|
+
frozenBottomRowRange,
|
|
1221
1293
|
visibleColumnRange,
|
|
1222
1294
|
reportFilterCells,
|
|
1223
1295
|
rowFieldHeaderCells,
|
|
@@ -1352,6 +1424,13 @@ function createAxisSlot(node, indicatorKey) {
|
|
|
1352
1424
|
kind: node?.kind ?? "empty"
|
|
1353
1425
|
};
|
|
1354
1426
|
}
|
|
1427
|
+
function resolveFrozenBottomRowRange(slots) {
|
|
1428
|
+
let start = slots.length;
|
|
1429
|
+
while (start > 0 && slots[start - 1]?.kind === "grandTotal") {
|
|
1430
|
+
start -= 1;
|
|
1431
|
+
}
|
|
1432
|
+
return start < slots.length ? { start, end: slots.length } : null;
|
|
1433
|
+
}
|
|
1355
1434
|
function buildRowHeaderCells(params) {
|
|
1356
1435
|
const cells = [];
|
|
1357
1436
|
for (let rowIndex = params.range.start; rowIndex < params.range.end; rowIndex += 1) {
|
|
@@ -2614,8 +2693,8 @@ function resolvePivotSelectionRect(layout, selection) {
|
|
|
2614
2693
|
const kind = resolvePivotSelectionKind(selection);
|
|
2615
2694
|
const startX = kind === "row" || kind === "row-cell" ? layout.rowHeaderRect.x : layout.bodyRect.x + (layout.columnOffsets[indexes.startColumnIndex] ?? 0) - layout.scroll.left;
|
|
2616
2695
|
const endX = layout.bodyRect.x + (layout.columnOffsets[indexes.endColumnIndex] ?? 0) + (layout.columnWidths[indexes.endColumnIndex] ?? layout.columnWidth) - layout.scroll.left;
|
|
2617
|
-
const startY = kind === "column" ? layout.columnHeaderRect.y : layout
|
|
2618
|
-
const endY = layout
|
|
2696
|
+
const startY = kind === "column" ? layout.columnHeaderRect.y : resolvePivotRowTop(layout, indexes.startRowIndex);
|
|
2697
|
+
const endY = resolvePivotRowTop(layout, indexes.endRowIndex) + layout.rowHeight;
|
|
2619
2698
|
const clipRect = resolvePivotSelectionClipRect(layout, selection);
|
|
2620
2699
|
const visibleStartX = Math.max(startX, clipRect.x);
|
|
2621
2700
|
const visibleEndX = Math.min(endX, clipRect.x + clipRect.width);
|
|
@@ -2629,6 +2708,13 @@ function resolvePivotSelectionRect(layout, selection) {
|
|
|
2629
2708
|
height: visibleEndY - visibleStartY
|
|
2630
2709
|
};
|
|
2631
2710
|
}
|
|
2711
|
+
function resolvePivotRowTop(layout, rowIndex) {
|
|
2712
|
+
const frozenRange = layout.frozenBottomRowRange;
|
|
2713
|
+
if (frozenRange && rowIndex >= frozenRange.start && rowIndex < frozenRange.end) {
|
|
2714
|
+
return layout.bodyRect.y + layout.bodyRect.height - (frozenRange.end - rowIndex) * layout.rowHeight;
|
|
2715
|
+
}
|
|
2716
|
+
return layout.bodyRect.y + rowIndex * layout.rowHeight - layout.scroll.top;
|
|
2717
|
+
}
|
|
2632
2718
|
function resolvePivotSelectionClipRect(layout, selection) {
|
|
2633
2719
|
const kind = resolvePivotSelectionKind(selection);
|
|
2634
2720
|
if (kind === "row" || kind === "row-cell") {
|
|
@@ -2773,26 +2859,29 @@ var defaultPivotTableTheme = {
|
|
|
2773
2859
|
};
|
|
2774
2860
|
|
|
2775
2861
|
// src/rendering/draw-scrollbars.ts
|
|
2776
|
-
function drawPivotScrollbars(context, layout) {
|
|
2862
|
+
function drawPivotScrollbars(context, layout, borderColor) {
|
|
2777
2863
|
context.save();
|
|
2778
2864
|
if (layout.horizontal) {
|
|
2779
|
-
drawAxis(context, layout.horizontal, "horizontal");
|
|
2865
|
+
drawAxis(context, layout.horizontal, "horizontal", borderColor);
|
|
2780
2866
|
}
|
|
2781
2867
|
if (layout.vertical) {
|
|
2782
|
-
drawAxis(context, layout.vertical, "vertical");
|
|
2868
|
+
drawAxis(context, layout.vertical, "vertical", borderColor);
|
|
2783
2869
|
}
|
|
2784
2870
|
context.restore();
|
|
2785
2871
|
}
|
|
2786
|
-
function drawAxis(context, layout, axis) {
|
|
2787
|
-
drawTrack(context, layout.track);
|
|
2872
|
+
function drawAxis(context, layout, axis, borderColor) {
|
|
2873
|
+
drawTrack(context, layout.track, borderColor);
|
|
2788
2874
|
drawButton(context, layout.decreaseButton, axis, "decrease");
|
|
2789
2875
|
drawButton(context, layout.increaseButton, axis, "increase");
|
|
2790
2876
|
drawThumb(context, layout.thumb);
|
|
2791
2877
|
}
|
|
2792
|
-
function drawTrack(context, rect) {
|
|
2878
|
+
function drawTrack(context, rect, borderColor) {
|
|
2793
2879
|
context.fillStyle = "rgba(148, 163, 184, 0.16)";
|
|
2880
|
+
context.strokeStyle = borderColor;
|
|
2881
|
+
context.lineWidth = 1;
|
|
2794
2882
|
roundRect(context, rect, rect.height / 2);
|
|
2795
2883
|
context.fill();
|
|
2884
|
+
context.stroke();
|
|
2796
2885
|
}
|
|
2797
2886
|
function drawThumb(context, rect) {
|
|
2798
2887
|
context.fillStyle = "rgba(100, 116, 139, 0.8)";
|
|
@@ -3060,7 +3149,7 @@ function renderPivotTable(params) {
|
|
|
3060
3149
|
theme.borderColor
|
|
3061
3150
|
);
|
|
3062
3151
|
if (options.debug) drawDebugOverlay(context, layout, theme);
|
|
3063
|
-
drawPivotScrollbars(context, layout.scrollbar);
|
|
3152
|
+
drawPivotScrollbars(context, layout.scrollbar, theme.borderColor);
|
|
3064
3153
|
drawPivotViewportRightBorder(
|
|
3065
3154
|
context,
|
|
3066
3155
|
{ x: 0, y: 0, width: layout.tableWidth, height: contentHeight },
|
|
@@ -3578,7 +3667,7 @@ function hitTestPivotLayout(layout, x, y) {
|
|
|
3578
3667
|
onSortIcon: false
|
|
3579
3668
|
};
|
|
3580
3669
|
}
|
|
3581
|
-
const bodyCell = layout.bodyCells
|
|
3670
|
+
const bodyCell = findLastContaining(layout.bodyCells, x, y);
|
|
3582
3671
|
if (bodyCell) {
|
|
3583
3672
|
return {
|
|
3584
3673
|
region: "body",
|
|
@@ -3588,9 +3677,7 @@ function hitTestPivotLayout(layout, x, y) {
|
|
|
3588
3677
|
onSortIcon: false
|
|
3589
3678
|
};
|
|
3590
3679
|
}
|
|
3591
|
-
const rowHeaderCell = layout.rowHeaderCells
|
|
3592
|
-
(cell) => contains(cell.rect, x, y)
|
|
3593
|
-
);
|
|
3680
|
+
const rowHeaderCell = findLastContaining(layout.rowHeaderCells, x, y);
|
|
3594
3681
|
if (rowHeaderCell) {
|
|
3595
3682
|
return {
|
|
3596
3683
|
region: "rowHeader",
|
|
@@ -3643,6 +3730,13 @@ function isSortIconHit(cell, x) {
|
|
|
3643
3730
|
function contains(rect, x, y) {
|
|
3644
3731
|
return x >= rect.x && x < rect.x + rect.width && y >= rect.y && y < rect.y + rect.height;
|
|
3645
3732
|
}
|
|
3733
|
+
function findLastContaining(cells, x, y) {
|
|
3734
|
+
for (let index = cells.length - 1; index >= 0; index -= 1) {
|
|
3735
|
+
const cell = cells[index];
|
|
3736
|
+
if (cell && contains(cell.rect, x, y)) return cell;
|
|
3737
|
+
}
|
|
3738
|
+
return void 0;
|
|
3739
|
+
}
|
|
3646
3740
|
|
|
3647
3741
|
// src/interaction/text-selection.ts
|
|
3648
3742
|
function resolvePivotTextOffset(params) {
|
|
@@ -3879,6 +3973,16 @@ function createAddress(rowSlot, columnSlot, indicatorKey) {
|
|
|
3879
3973
|
// src/domain/interaction/row-selection.ts
|
|
3880
3974
|
function resolvePivotRowIndexAtY(layout, y) {
|
|
3881
3975
|
if (layout.rowSlots.length === 0 || layout.rowHeight <= 0) return null;
|
|
3976
|
+
const frozenRange = layout.frozenBottomRowRange;
|
|
3977
|
+
if (frozenRange) {
|
|
3978
|
+
const frozenTop = layout.bodyRect.y + layout.bodyRect.height - (frozenRange.end - frozenRange.start) * layout.rowHeight;
|
|
3979
|
+
if (y >= frozenTop) {
|
|
3980
|
+
return Math.min(
|
|
3981
|
+
frozenRange.start + Math.floor((y - frozenTop) / layout.rowHeight),
|
|
3982
|
+
frozenRange.end - 1
|
|
3983
|
+
);
|
|
3984
|
+
}
|
|
3985
|
+
}
|
|
3882
3986
|
const index = Math.floor(
|
|
3883
3987
|
(y - layout.bodyRect.y + layout.scroll.top) / layout.rowHeight
|
|
3884
3988
|
);
|
|
@@ -4584,11 +4688,16 @@ var PivotTable = class {
|
|
|
4584
4688
|
(slot) => (slot.nodeId ?? "") === address.columnNodeId && (!slot.indicatorKey || slot.indicatorKey === address.indicatorKey)
|
|
4585
4689
|
);
|
|
4586
4690
|
if (rowIndex < 0 || columnIndex < 0) return false;
|
|
4587
|
-
const
|
|
4691
|
+
const frozenRange = this.layout.frozenBottomRowRange;
|
|
4692
|
+
const frozenBottomHeight = frozenRange ? (frozenRange.end - frozenRange.start) * this.layout.rowHeight : 0;
|
|
4693
|
+
const top = frozenRange && rowIndex >= frozenRange.start ? this.scroll.top : resolveVisibleOffset({
|
|
4588
4694
|
current: this.scroll.top,
|
|
4589
4695
|
itemStart: rowIndex * this.layout.rowHeight,
|
|
4590
4696
|
itemSize: this.layout.rowHeight,
|
|
4591
|
-
viewportSize:
|
|
4697
|
+
viewportSize: Math.max(
|
|
4698
|
+
this.layout.bodyRect.height - frozenBottomHeight,
|
|
4699
|
+
0
|
|
4700
|
+
)
|
|
4592
4701
|
});
|
|
4593
4702
|
const left = resolveVisibleOffset({
|
|
4594
4703
|
current: this.scroll.left,
|