@lensui/lens-table 0.1.3 → 0.1.5
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/README.md +28 -2
- package/dist/index.cjs +1087 -209
- package/dist/index.css +414 -4
- package/dist/index.d.cts +10 -1
- package/dist/index.d.ts +10 -1
- package/dist/index.js +1087 -209
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -27,6 +27,7 @@ var ZH_CN_GRID_LABELS = {
|
|
|
27
27
|
sortDesc: "\u5F53\u524D\uFF1A\u964D\u5E8F",
|
|
28
28
|
sortBoth: "\u5347\u5E8F / \u964D\u5E8F",
|
|
29
29
|
dragColumn: "\u62D6\u52A8\u5217\u6392\u5E8F",
|
|
30
|
+
revealSelectedCell: "\u5B9A\u4F4D\u5230\u9009\u4E2D\u5355\u5143\u683C",
|
|
30
31
|
previousYear: "\u4E0A\u4E00\u5E74",
|
|
31
32
|
nextYear: "\u4E0B\u4E00\u5E74",
|
|
32
33
|
previousMonth: "\u4E0A\u4E2A\u6708",
|
|
@@ -95,6 +96,7 @@ var EN_US_GRID_LABELS = {
|
|
|
95
96
|
sortDesc: "Current: descending",
|
|
96
97
|
sortBoth: "Ascending / descending",
|
|
97
98
|
dragColumn: "Drag column to reorder",
|
|
99
|
+
revealSelectedCell: "Reveal selected cell",
|
|
98
100
|
previousYear: "Previous year",
|
|
99
101
|
nextYear: "Next year",
|
|
100
102
|
previousMonth: "Previous month",
|
|
@@ -303,7 +305,6 @@ function LoadingSpinnerIcon() {
|
|
|
303
305
|
// src/components/editors/ChoiceEditor.tsx
|
|
304
306
|
import { jsx as jsx3, jsxs as jsxs3 } from "react/jsx-runtime";
|
|
305
307
|
function ChoiceEditor({ value, options, style, labels, onChange, onCommit, onCancel }) {
|
|
306
|
-
const editorStyle = typeof style.width === "number" ? { ...style, width: Math.max(0, style.width - 1) } : style;
|
|
307
308
|
const collection = createListCollection({
|
|
308
309
|
items: options,
|
|
309
310
|
itemToValue: (item) => item.value,
|
|
@@ -313,7 +314,7 @@ function ChoiceEditor({ value, options, style, labels, onChange, onCommit, onCan
|
|
|
313
314
|
Select.Root,
|
|
314
315
|
{
|
|
315
316
|
className: "rvg-choice-editor",
|
|
316
|
-
style
|
|
317
|
+
style,
|
|
317
318
|
collection,
|
|
318
319
|
value: value ? [value] : [],
|
|
319
320
|
defaultOpen: true,
|
|
@@ -863,6 +864,8 @@ function ellipsizeText(context, text, maxWidth) {
|
|
|
863
864
|
}
|
|
864
865
|
function paintGrid(options) {
|
|
865
866
|
const { context: ctx, width, height, pixelRatio, scrollLeft, scrollTop, rowHeight, headerHeight, fixedHeader, verticalBorderless, striped, columnDraggable, sortState, filterValues, hoveredHeaderAction, rows, columns, metrics, range, selection, editing, hoveredRowIndex, selectionRange, selectedRowKeys, selectedColumnKeys, highlightEditedCells, highlightInsertedRows, insertedRowKeys, editedCellKeys, cellAnnotations, getRowKey, rowDragPreview } = options;
|
|
867
|
+
const headerLeafTop = options.headerLeafTop ?? 0;
|
|
868
|
+
const headerLeafHeight = options.headerLeafHeight ?? headerHeight;
|
|
866
869
|
const colors = { ...COLORS, ...options.colors };
|
|
867
870
|
const bodyTop = options.bodyTop ?? headerHeight;
|
|
868
871
|
const getCellSpan = (rowIndex, columnIndex) => options.cellSpans.get(getCellCoordKey(rowIndex, columnIndex));
|
|
@@ -879,9 +882,17 @@ function paintGrid(options) {
|
|
|
879
882
|
ctx.fillRect(0, 0, width, height);
|
|
880
883
|
ctx.font = "13px Inter, ui-sans-serif, system-ui, sans-serif";
|
|
881
884
|
ctx.textBaseline = "middle";
|
|
882
|
-
const leftFixedWidth = columns.reduce((value, column, index) => column.fixed === "left" ?
|
|
885
|
+
const leftFixedWidth = columns.reduce((value, column, index) => column.fixed === "left" ? value + metrics[index].width : value, 0);
|
|
883
886
|
const rightFixedWidth = columns.reduce((value, column, index) => column.fixed === "right" ? value + metrics[index].width : value, 0);
|
|
884
887
|
const rightFixedLeft = width - rightFixedWidth;
|
|
888
|
+
const rightFixedBoundaryInset = rightFixedWidth > 0 && !verticalBorderless ? 1 : 0;
|
|
889
|
+
const leftOffsets = /* @__PURE__ */ new Map();
|
|
890
|
+
let leftOffset = 0;
|
|
891
|
+
for (let index = 0; index < columns.length; index += 1) {
|
|
892
|
+
if (columns[index].fixed !== "left") continue;
|
|
893
|
+
leftOffsets.set(index, leftOffset);
|
|
894
|
+
leftOffset += metrics[index].width;
|
|
895
|
+
}
|
|
885
896
|
const rightOffsets = /* @__PURE__ */ new Map();
|
|
886
897
|
let rightOffset = 0;
|
|
887
898
|
for (let index = columns.length - 1; index >= 0; index -= 1) {
|
|
@@ -889,8 +900,14 @@ function paintGrid(options) {
|
|
|
889
900
|
rightOffsets.set(index, rightOffset);
|
|
890
901
|
rightOffset += metrics[index].width;
|
|
891
902
|
}
|
|
903
|
+
const scrollableLefts = /* @__PURE__ */ new Map();
|
|
904
|
+
let scrollableLeft = leftFixedWidth;
|
|
905
|
+
for (let index = 0; index < columns.length; index += 1) {
|
|
906
|
+
scrollableLefts.set(index, scrollableLeft);
|
|
907
|
+
if (columns[index].fixed === void 0) scrollableLeft += metrics[index].width;
|
|
908
|
+
}
|
|
892
909
|
const getColumnX = (index) => {
|
|
893
|
-
return columns[index].fixed === "left" ?
|
|
910
|
+
return columns[index].fixed === "left" ? leftOffsets.get(index) ?? 0 : columns[index].fixed === "right" ? width - (rightOffsets.get(index) ?? 0) - metrics[index].width : (scrollableLefts.get(index) ?? leftFixedWidth) - scrollLeft;
|
|
894
911
|
};
|
|
895
912
|
const paintBodyColumns = (layer) => {
|
|
896
913
|
const rowStart = Math.max(0, range.rowStart - Math.max(0, options.maxRowSpan - 1));
|
|
@@ -1040,7 +1057,7 @@ function paintGrid(options) {
|
|
|
1040
1057
|
if (!verticalBorderless) ctx.fillRect(x + metric.width - 1, headerY, 1, headerHeight);
|
|
1041
1058
|
ctx.fillRect(x, headerY + headerHeight - 1, metric.width, 1);
|
|
1042
1059
|
const column = columns[columnIndex];
|
|
1043
|
-
if (!column.rowSelection && !column.rowDragHandle && !column.rowNumber && !column.renderHeader && column.title) {
|
|
1060
|
+
if (headerLeafTop === 0 && !column.rowSelection && !column.rowDragHandle && !column.rowNumber && !column.renderHeader && column.title) {
|
|
1044
1061
|
ctx.font = "600 13px Inter, ui-sans-serif, system-ui, sans-serif";
|
|
1045
1062
|
const titleWidth = ctx.measureText(column.title).width;
|
|
1046
1063
|
const visibleActions = getVisibleHeaderActions(column, metric.width, columnDraggable, titleWidth);
|
|
@@ -1057,11 +1074,11 @@ function paintGrid(options) {
|
|
|
1057
1074
|
const textX = titleAlign === "right" ? x + contentWidth - padding : titleAlign === "center" ? x + centeredTextX : x + padding;
|
|
1058
1075
|
ctx.save();
|
|
1059
1076
|
ctx.beginPath();
|
|
1060
|
-
ctx.rect(x + 2, headerY + 1, Math.max(0, clipWidth - 4),
|
|
1077
|
+
ctx.rect(x + 2, headerY + headerLeafTop + 1, Math.max(0, clipWidth - 4), headerLeafHeight - 2);
|
|
1061
1078
|
ctx.clip();
|
|
1062
1079
|
ctx.fillStyle = colors.muted;
|
|
1063
1080
|
ctx.textAlign = titleAlign === "right" ? "right" : titleAlign === "center" ? "center" : "left";
|
|
1064
|
-
ctx.fillText(title, textX, headerY +
|
|
1081
|
+
ctx.fillText(title, textX, headerY + headerLeafTop + headerLeafHeight / 2);
|
|
1065
1082
|
ctx.restore();
|
|
1066
1083
|
}
|
|
1067
1084
|
}
|
|
@@ -1069,22 +1086,6 @@ function paintGrid(options) {
|
|
|
1069
1086
|
paintHeaderColumns("scroll");
|
|
1070
1087
|
paintHeaderColumns("left");
|
|
1071
1088
|
paintHeaderColumns("right");
|
|
1072
|
-
if (leftFixedWidth > 0 && scrollLeft > 0) {
|
|
1073
|
-
const shadowWidth = 10;
|
|
1074
|
-
const shadow = ctx.createLinearGradient(leftFixedWidth, 0, leftFixedWidth + shadowWidth, 0);
|
|
1075
|
-
shadow.addColorStop(0, "rgba(5, 5, 5, 0.06)");
|
|
1076
|
-
shadow.addColorStop(1, "rgba(5, 5, 5, 0)");
|
|
1077
|
-
ctx.fillStyle = shadow;
|
|
1078
|
-
ctx.fillRect(leftFixedWidth, 0, shadowWidth, height);
|
|
1079
|
-
}
|
|
1080
|
-
if (rightFixedWidth > 0 && scrollLeft < (metrics[metrics.length - 1]?.right ?? 0) - width) {
|
|
1081
|
-
const shadowWidth = 10;
|
|
1082
|
-
const shadow = ctx.createLinearGradient(rightFixedLeft - shadowWidth, 0, rightFixedLeft, 0);
|
|
1083
|
-
shadow.addColorStop(0, "rgba(5, 5, 5, 0)");
|
|
1084
|
-
shadow.addColorStop(1, "rgba(5, 5, 5, 0.06)");
|
|
1085
|
-
ctx.fillStyle = shadow;
|
|
1086
|
-
ctx.fillRect(rightFixedLeft - shadowWidth, 0, shadowWidth, height);
|
|
1087
|
-
}
|
|
1088
1089
|
if (rightFixedWidth > 0 && !verticalBorderless) {
|
|
1089
1090
|
ctx.fillStyle = colors.grid;
|
|
1090
1091
|
ctx.fillRect(rightFixedLeft - 1, 0, 1, height);
|
|
@@ -1108,22 +1109,22 @@ function paintGrid(options) {
|
|
|
1108
1109
|
const markerBottom = Math.min(bottom, height);
|
|
1109
1110
|
const startX = getColumnX(columnStart);
|
|
1110
1111
|
const endX = getColumnX(columnEnd) + metrics[columnEnd].width;
|
|
1111
|
-
const visibleLeft = columns[columnStart].fixed === "left" ? Math.max(0, startX) : Math.max(leftFixedWidth
|
|
1112
|
+
const visibleLeft = columns[columnStart].fixed === "left" ? Math.max(0, startX) : Math.max(leftFixedWidth, startX);
|
|
1112
1113
|
const visibleRight = columns[columnEnd].fixed === "right" ? Math.min(width, endX) : Math.min(rightFixedLeft, endX);
|
|
1113
1114
|
ctx.fillStyle = colors.selection;
|
|
1114
1115
|
if (markerBottom > markerTop) {
|
|
1115
1116
|
if (columns[columnStart].fixed === void 0 && startX < leftFixedWidth) {
|
|
1116
|
-
ctx.fillRect(Math.max(0, leftFixedWidth
|
|
1117
|
+
ctx.fillRect(Math.max(0, leftFixedWidth), markerTop, 2, markerBottom - markerTop);
|
|
1117
1118
|
}
|
|
1118
1119
|
if (columns[columnEnd].fixed === void 0 && endX > rightFixedLeft) {
|
|
1119
|
-
ctx.fillRect(Math.max(0, rightFixedLeft -
|
|
1120
|
+
ctx.fillRect(Math.max(0, rightFixedLeft - 2 - rightFixedBoundaryInset), markerTop, 2, markerBottom - markerTop);
|
|
1120
1121
|
}
|
|
1121
1122
|
}
|
|
1122
1123
|
if (visibleRight > visibleLeft) {
|
|
1123
1124
|
if (top < clipTop) ctx.fillRect(visibleLeft, clipTop, visibleRight - visibleLeft, 2);
|
|
1124
1125
|
if (bottom > height) ctx.fillRect(visibleLeft, height - 2, visibleRight - visibleLeft, 2);
|
|
1125
1126
|
} else if (top < clipTop || bottom > height) {
|
|
1126
|
-
const markerX = endX <= leftFixedWidth ? Math.max(0, leftFixedWidth
|
|
1127
|
+
const markerX = endX <= leftFixedWidth ? Math.max(0, leftFixedWidth) : Math.max(0, rightFixedLeft - 2 - rightFixedBoundaryInset);
|
|
1127
1128
|
ctx.fillRect(markerX, top < clipTop ? clipTop : height - 2, 2, 2);
|
|
1128
1129
|
}
|
|
1129
1130
|
}
|
|
@@ -1145,15 +1146,15 @@ function paintGrid(options) {
|
|
|
1145
1146
|
const visibleRight = x + selectionWidth;
|
|
1146
1147
|
const visibleBottom = y + selectionHeight;
|
|
1147
1148
|
const selectionTop = fixedHeader ? bodyTop : 0;
|
|
1148
|
-
const selectionClipTop =
|
|
1149
|
+
const selectionClipTop = selectionTop;
|
|
1149
1150
|
const hiddenAboveHeader = visibleBottom <= selectionTop;
|
|
1150
1151
|
const hiddenBelowViewport = y >= height;
|
|
1151
1152
|
const hiddenBehindLeft = !isFixed && visibleRight <= visibleLeft;
|
|
1152
1153
|
const hiddenBehindRight = !isFixed && x >= selectionAreaRight;
|
|
1153
|
-
const leftMarkerX = Math.max(0, leftFixedWidth
|
|
1154
|
+
const leftMarkerX = Math.max(0, leftFixedWidth);
|
|
1154
1155
|
if ((hiddenAboveHeader || hiddenBelowViewport) && (hiddenBehindLeft || hiddenBehindRight)) {
|
|
1155
1156
|
const markerSize = 2;
|
|
1156
|
-
const markerX = hiddenBehindLeft ? leftMarkerX : selectionAreaRight - borderWidth;
|
|
1157
|
+
const markerX = hiddenBehindLeft ? leftMarkerX : selectionAreaRight - borderWidth - rightFixedBoundaryInset;
|
|
1157
1158
|
const markerY = hiddenAboveHeader ? selectionClipTop : height - markerSize;
|
|
1158
1159
|
ctx.fillStyle = colors.selection;
|
|
1159
1160
|
ctx.fillRect(markerX, markerY, markerSize, markerSize);
|
|
@@ -1161,7 +1162,7 @@ function paintGrid(options) {
|
|
|
1161
1162
|
}
|
|
1162
1163
|
ctx.save();
|
|
1163
1164
|
ctx.beginPath();
|
|
1164
|
-
const clipLeft = fixedSide === "left" ? 0 : Math.max(0, leftFixedWidth
|
|
1165
|
+
const clipLeft = fixedSide === "left" ? 0 : Math.max(0, leftFixedWidth);
|
|
1165
1166
|
const clipRight = Math.min(width, selectionAreaRight + 1);
|
|
1166
1167
|
ctx.rect(clipLeft, selectionClipTop, clipRight - clipLeft, height - selectionClipTop);
|
|
1167
1168
|
ctx.clip();
|
|
@@ -1179,14 +1180,14 @@ function paintGrid(options) {
|
|
|
1179
1180
|
if (hiddenBehindLeft || hiddenBehindRight) {
|
|
1180
1181
|
const markerTop = Math.max(y - borderWidth, selectionTop);
|
|
1181
1182
|
const markerBottom = Math.min(y + selectionHeight + 1, height);
|
|
1182
|
-
const markerX = hiddenBehindLeft ? leftMarkerX : selectionAreaRight - borderWidth;
|
|
1183
|
+
const markerX = hiddenBehindLeft ? leftMarkerX : selectionAreaRight - borderWidth - rightFixedBoundaryInset;
|
|
1183
1184
|
ctx.fillRect(markerX, markerTop, borderWidth, Math.max(0, markerBottom - markerTop));
|
|
1184
1185
|
}
|
|
1185
1186
|
if (hiddenAboveHeader || hiddenBelowViewport || hiddenBehindLeft || hiddenBehindRight) {
|
|
1186
1187
|
ctx.restore();
|
|
1187
1188
|
return;
|
|
1188
1189
|
}
|
|
1189
|
-
const borderLeft = x <= 0 ? 0 : Math.max(x - borderWidth, visibleLeft
|
|
1190
|
+
const borderLeft = x <= 0 ? 0 : Math.max(x - borderWidth, visibleLeft);
|
|
1190
1191
|
const borderRight = Math.min(x + selectionWidth - 1, selectionAreaRight - borderWidth, width - borderWidth);
|
|
1191
1192
|
const borderTop = Math.max(y - borderWidth, selectionClipTop);
|
|
1192
1193
|
const borderBottom = Math.min(y + selectionHeight - 1, height - borderWidth);
|
|
@@ -1236,6 +1237,45 @@ var getRowNumberColumnWidth = (rowCount) => {
|
|
|
1236
1237
|
const digits = String(Math.max(1, rowCount)).length;
|
|
1237
1238
|
return digits <= 4 ? 44 : 44 + (digits - 4) * 8;
|
|
1238
1239
|
};
|
|
1240
|
+
function getColumnDepth(column) {
|
|
1241
|
+
if (!column.children?.length) return 1;
|
|
1242
|
+
return 1 + Math.max(...column.children.map(getColumnDepth));
|
|
1243
|
+
}
|
|
1244
|
+
function flattenDataColumns(columns, inheritedFixed, insideGroup = false) {
|
|
1245
|
+
return columns.flatMap((column) => {
|
|
1246
|
+
if (!column.children?.length) {
|
|
1247
|
+
return insideGroup ? [{ ...column, fixed: inheritedFixed }] : [column];
|
|
1248
|
+
}
|
|
1249
|
+
return flattenDataColumns(column.children, column.fixed ?? inheritedFixed, true);
|
|
1250
|
+
});
|
|
1251
|
+
}
|
|
1252
|
+
function buildHeaderCells(sourceColumns, utilityColumnCount, depth) {
|
|
1253
|
+
const cells = [];
|
|
1254
|
+
let leafIndex = utilityColumnCount;
|
|
1255
|
+
const walk = (column, level, inheritedFixed, insideGroup, rootIndex, parentKey) => {
|
|
1256
|
+
const fixedColumn = insideGroup ? { ...column, fixed: inheritedFixed } : column;
|
|
1257
|
+
if (!fixedColumn.children?.length) {
|
|
1258
|
+
const startIndex2 = leafIndex;
|
|
1259
|
+
leafIndex += 1;
|
|
1260
|
+
cells.push({ key: fixedColumn.key, column: fixedColumn, level: depth - 1, startIndex: startIndex2, endIndex: startIndex2, leaf: true, parentKey, rootIndex });
|
|
1261
|
+
return { startIndex: startIndex2, endIndex: startIndex2 };
|
|
1262
|
+
}
|
|
1263
|
+
const startIndex = leafIndex;
|
|
1264
|
+
fixedColumn.children.forEach((child) => walk(child, level + 1, fixedColumn.fixed, true, rootIndex, fixedColumn.key));
|
|
1265
|
+
const endIndex = leafIndex - 1;
|
|
1266
|
+
cells.push({ key: fixedColumn.key, column: fixedColumn, level, startIndex, endIndex, leaf: false, parentKey, rootIndex });
|
|
1267
|
+
return { startIndex, endIndex };
|
|
1268
|
+
};
|
|
1269
|
+
sourceColumns.forEach((column, index) => walk(column, 0, void 0, false, index));
|
|
1270
|
+
return cells;
|
|
1271
|
+
}
|
|
1272
|
+
function hasNestedFixedColumns(columns, insideGroup = false) {
|
|
1273
|
+
return columns.some((column) => insideGroup && column.fixed !== void 0 || (column.children?.length ? hasNestedFixedColumns(column.children, true) : false));
|
|
1274
|
+
}
|
|
1275
|
+
function isProductionRuntime() {
|
|
1276
|
+
const env = globalThis.process?.env?.NODE_ENV;
|
|
1277
|
+
return env === "production";
|
|
1278
|
+
}
|
|
1239
1279
|
var createPendingInsertId = () => `table-inserted-${globalThis.crypto?.randomUUID?.() ?? `${Date.now()}-${Math.random().toString(36).slice(2)}`}`;
|
|
1240
1280
|
var waitForPaint = () => new Promise((resolve) => {
|
|
1241
1281
|
requestAnimationFrame(() => requestAnimationFrame(() => resolve()));
|
|
@@ -1256,6 +1296,25 @@ var createEmptyCellSpanLookup = () => ({
|
|
|
1256
1296
|
covered: /* @__PURE__ */ new Map(),
|
|
1257
1297
|
maxRowSpan: 1
|
|
1258
1298
|
});
|
|
1299
|
+
var summaryFunctionIds = /* @__PURE__ */ new WeakMap();
|
|
1300
|
+
var nextSummaryFunctionId = 1;
|
|
1301
|
+
function getSummaryFunctionId(fn) {
|
|
1302
|
+
if (!fn) return "";
|
|
1303
|
+
const existing = summaryFunctionIds.get(fn);
|
|
1304
|
+
if (existing !== void 0) return existing;
|
|
1305
|
+
const next = nextSummaryFunctionId;
|
|
1306
|
+
nextSummaryFunctionId += 1;
|
|
1307
|
+
summaryFunctionIds.set(fn, next);
|
|
1308
|
+
return next;
|
|
1309
|
+
}
|
|
1310
|
+
function getSummarySignature(columns) {
|
|
1311
|
+
return columns.filter((column) => Boolean(column.summary)).map((column) => [
|
|
1312
|
+
column.key,
|
|
1313
|
+
String(column.dataIndex ?? ""),
|
|
1314
|
+
typeof column.summary === "function" ? getSummaryFunctionId(column.summary) : "sum",
|
|
1315
|
+
getSummaryFunctionId(column.formatter)
|
|
1316
|
+
].join("")).join("");
|
|
1317
|
+
}
|
|
1259
1318
|
async function calculateSummaryValues(columns, rows) {
|
|
1260
1319
|
const values = /* @__PURE__ */ new Map();
|
|
1261
1320
|
for (const column of columns) {
|
|
@@ -1350,10 +1409,24 @@ function Table({
|
|
|
1350
1409
|
}) {
|
|
1351
1410
|
const rowHeight = layout?.rowHeight ?? rowHeightProp ?? 36;
|
|
1352
1411
|
const baseHeaderHeight = layout?.headerHeight ?? headerHeightProp ?? 40;
|
|
1412
|
+
const headerDepth = useMemo(() => Math.max(1, ...sourceColumns.map(getColumnDepth)), [sourceColumns]);
|
|
1353
1413
|
const { language, labels } = useMemo(() => resolveGridLocale(locale), [locale]);
|
|
1354
1414
|
const customHeaderContentRefs = useRef2(/* @__PURE__ */ new Map());
|
|
1355
|
-
const
|
|
1356
|
-
const
|
|
1415
|
+
const customHeaderMeasurementsRef = useRef2(/* @__PURE__ */ new Map());
|
|
1416
|
+
const [measuredHeaderRowHeights, setMeasuredHeaderRowHeights] = useState4([]);
|
|
1417
|
+
const headerRowHeights = useMemo(() => Array.from({ length: headerDepth }, (_, level) => Math.max(baseHeaderHeight, measuredHeaderRowHeights[level] ?? 0)), [baseHeaderHeight, headerDepth, measuredHeaderRowHeights]);
|
|
1418
|
+
const headerRowOffsets = useMemo(() => {
|
|
1419
|
+
const offsets = [];
|
|
1420
|
+
let top = 0;
|
|
1421
|
+
headerRowHeights.forEach((height2) => {
|
|
1422
|
+
offsets.push(top);
|
|
1423
|
+
top += height2;
|
|
1424
|
+
});
|
|
1425
|
+
return offsets;
|
|
1426
|
+
}, [headerRowHeights]);
|
|
1427
|
+
const headerHeight = headerRowHeights.reduce((total, height2) => total + height2, 0);
|
|
1428
|
+
const headerLeafTop = headerRowOffsets[headerDepth - 1] ?? 0;
|
|
1429
|
+
const headerLeafHeight = headerRowHeights[headerDepth - 1] ?? baseHeaderHeight;
|
|
1357
1430
|
const hasVerticalBorders = !(borderless ?? verticalBorderless ?? false);
|
|
1358
1431
|
const hasStripedRows = Boolean(striped);
|
|
1359
1432
|
const stripedColor = typeof striped === "string" ? striped : void 0;
|
|
@@ -1373,6 +1446,13 @@ function Table({
|
|
|
1373
1446
|
const summaryEmptyValue = typeof summary === "object" ? summary.emptyValue ?? "" : "";
|
|
1374
1447
|
const contextMenuEnabled = contextMenuConfig !== false;
|
|
1375
1448
|
const customContextMenuConfig = typeof contextMenuConfig === "object" ? contextMenuConfig : void 0;
|
|
1449
|
+
const [resizedColumnWidths, setResizedColumnWidths] = useState4({});
|
|
1450
|
+
const leafSourceColumns = useMemo(() => flattenDataColumns(sourceColumns), [sourceColumns]);
|
|
1451
|
+
useEffect2(() => {
|
|
1452
|
+
if (!isProductionRuntime() && hasNestedFixedColumns(sourceColumns)) {
|
|
1453
|
+
console.warn("[Table] Multi-level headers only support fixed columns on top-level columns. Nested fixed values are ignored.");
|
|
1454
|
+
}
|
|
1455
|
+
}, [sourceColumns]);
|
|
1376
1456
|
const columns = useMemo(() => {
|
|
1377
1457
|
const utilityColumns = [];
|
|
1378
1458
|
if (rowDraggable) {
|
|
@@ -1384,9 +1464,24 @@ function Table({
|
|
|
1384
1464
|
if (rowNumber) {
|
|
1385
1465
|
utilityColumns.push({ key: "__rvg_row_number__", title: "#", width: getRowNumberColumnWidth(sourceRows.length), align: "center", fixed: "left", rowNumber: true });
|
|
1386
1466
|
}
|
|
1387
|
-
|
|
1388
|
-
|
|
1467
|
+
const dataColumns = leafSourceColumns.map((column) => {
|
|
1468
|
+
const resizedWidth = resizedColumnWidths[column.key];
|
|
1469
|
+
return resizedWidth === void 0 ? column : { ...column, width: resizedWidth };
|
|
1470
|
+
});
|
|
1471
|
+
return [...utilityColumns, ...dataColumns];
|
|
1472
|
+
}, [leafSourceColumns, resizedColumnWidths, rowDraggable, rowNumber, rowSelection, sourceRows.length]);
|
|
1389
1473
|
const utilityColumnCount = (rowDraggable ? 1 : 0) + (rowSelection ? 1 : 0) + (rowNumber ? 1 : 0);
|
|
1474
|
+
const headerCells = useMemo(() => buildHeaderCells(sourceColumns, utilityColumnCount, headerDepth), [headerDepth, sourceColumns, utilityColumnCount]);
|
|
1475
|
+
const activeCustomHeaderLevels = useMemo(() => {
|
|
1476
|
+
const levels = /* @__PURE__ */ new Map();
|
|
1477
|
+
columns.forEach((column) => {
|
|
1478
|
+
if (column.renderHeader) levels.set(column.key, headerDepth - 1);
|
|
1479
|
+
});
|
|
1480
|
+
headerCells.forEach((cell) => {
|
|
1481
|
+
if (cell.column.renderHeader) levels.set(`group:${cell.key}:${cell.level}`, cell.level);
|
|
1482
|
+
});
|
|
1483
|
+
return levels;
|
|
1484
|
+
}, [columns, headerCells, headerDepth]);
|
|
1390
1485
|
const canvasRef = useRef2(null);
|
|
1391
1486
|
const scrollerRef = useRef2(null);
|
|
1392
1487
|
const frameRef = useRef2(null);
|
|
@@ -1404,6 +1499,15 @@ function Table({
|
|
|
1404
1499
|
const dragGuideRef = useRef2(null);
|
|
1405
1500
|
const rowDragGuideRef = useRef2(null);
|
|
1406
1501
|
const selectionFocusRef = useRef2(null);
|
|
1502
|
+
const selectionEdgeTargetRef = useRef2(null);
|
|
1503
|
+
const verticalScrollbarRef = useRef2(null);
|
|
1504
|
+
const verticalScrollbarThumbRef = useRef2(null);
|
|
1505
|
+
const verticalScrollbarDragRef = useRef2(null);
|
|
1506
|
+
const horizontalScrollbarRef = useRef2(null);
|
|
1507
|
+
const horizontalScrollbarThumbRef = useRef2(null);
|
|
1508
|
+
const horizontalScrollbarDragRef = useRef2(null);
|
|
1509
|
+
const verticalScrollbarVisibilityTimerRef = useRef2(null);
|
|
1510
|
+
const horizontalScrollbarVisibilityTimerRef = useRef2(null);
|
|
1407
1511
|
const rowByKeyRef = useRef2(/* @__PURE__ */ new Map());
|
|
1408
1512
|
const rowIndexByKeyRef = useRef2(/* @__PURE__ */ new Map());
|
|
1409
1513
|
const headerIconsRef = useRef2(null);
|
|
@@ -1456,24 +1560,57 @@ function Table({
|
|
|
1456
1560
|
rangeDragRef.current = null;
|
|
1457
1561
|
setSelectionRange(null);
|
|
1458
1562
|
}, [rangeSelection]);
|
|
1563
|
+
useEffect2(() => {
|
|
1564
|
+
setResizedColumnWidths((current) => {
|
|
1565
|
+
const sourceColumnByKey = new Map(leafSourceColumns.map((column) => [column.key, column]));
|
|
1566
|
+
let changed = false;
|
|
1567
|
+
const next = {};
|
|
1568
|
+
Object.entries(current).forEach(([key, value]) => {
|
|
1569
|
+
const sourceColumn = sourceColumnByKey.get(key);
|
|
1570
|
+
if (!sourceColumn) {
|
|
1571
|
+
changed = true;
|
|
1572
|
+
return;
|
|
1573
|
+
}
|
|
1574
|
+
if (sourceColumn.width !== void 0 && sourceColumn.width !== value) {
|
|
1575
|
+
changed = true;
|
|
1576
|
+
return;
|
|
1577
|
+
}
|
|
1578
|
+
next[key] = value;
|
|
1579
|
+
});
|
|
1580
|
+
return changed ? next : current;
|
|
1581
|
+
});
|
|
1582
|
+
}, [leafSourceColumns]);
|
|
1459
1583
|
useEffect2(() => () => {
|
|
1460
1584
|
if (headerTooltipTimerRef.current !== null) clearTimeout(headerTooltipTimerRef.current);
|
|
1461
1585
|
if (cellTooltipTimerRef.current !== null) clearTimeout(cellTooltipTimerRef.current);
|
|
1462
1586
|
}, []);
|
|
1463
1587
|
const measureCustomHeaderHeight = useCallback2(() => {
|
|
1464
|
-
|
|
1588
|
+
const nextHeights = Array.from({ length: headerDepth }, () => baseHeaderHeight);
|
|
1465
1589
|
customHeaderContentRefs.current.forEach((node) => {
|
|
1466
|
-
|
|
1590
|
+
const headerCell = node.closest(".rvg-header-title");
|
|
1591
|
+
const key = headerCell?.dataset.measureKey;
|
|
1592
|
+
const level = key ? activeCustomHeaderLevels.get(key) : void 0;
|
|
1593
|
+
if (!key || level === void 0 || level < 0 || level >= nextHeights.length) return;
|
|
1594
|
+
customHeaderMeasurementsRef.current.set(key, { level, height: Math.ceil(node.scrollHeight) + 16 });
|
|
1467
1595
|
});
|
|
1468
|
-
|
|
1469
|
-
|
|
1596
|
+
customHeaderMeasurementsRef.current.forEach((measurement, key) => {
|
|
1597
|
+
if (!activeCustomHeaderLevels.has(key)) return;
|
|
1598
|
+
nextHeights[measurement.level] = Math.max(nextHeights[measurement.level], measurement.height);
|
|
1599
|
+
});
|
|
1600
|
+
setMeasuredHeaderRowHeights((current) => {
|
|
1601
|
+
const changed = nextHeights.length !== current.length || nextHeights.some((height2, index) => height2 !== current[index]);
|
|
1602
|
+
return changed ? nextHeights : current;
|
|
1603
|
+
});
|
|
1604
|
+
}, [activeCustomHeaderLevels, baseHeaderHeight, headerDepth]);
|
|
1470
1605
|
useLayoutEffect2(() => {
|
|
1471
|
-
const activeCustomHeaderKeys = new Set(columns.filter((column) => column.renderHeader).map((column) => column.key));
|
|
1472
1606
|
customHeaderContentRefs.current.forEach((_, key) => {
|
|
1473
|
-
if (!
|
|
1607
|
+
if (!activeCustomHeaderLevels.has(key)) customHeaderContentRefs.current.delete(key);
|
|
1608
|
+
});
|
|
1609
|
+
customHeaderMeasurementsRef.current.forEach((_, key) => {
|
|
1610
|
+
if (!activeCustomHeaderLevels.has(key)) customHeaderMeasurementsRef.current.delete(key);
|
|
1474
1611
|
});
|
|
1475
|
-
if (
|
|
1476
|
-
|
|
1612
|
+
if (activeCustomHeaderLevels.size === 0) {
|
|
1613
|
+
setMeasuredHeaderRowHeights([]);
|
|
1477
1614
|
return;
|
|
1478
1615
|
}
|
|
1479
1616
|
measureCustomHeaderHeight();
|
|
@@ -1481,7 +1618,7 @@ function Table({
|
|
|
1481
1618
|
const observer = new ResizeObserver(measureCustomHeaderHeight);
|
|
1482
1619
|
customHeaderContentRefs.current.forEach((node) => observer.observe(node));
|
|
1483
1620
|
return () => observer.disconnect();
|
|
1484
|
-
}, [
|
|
1621
|
+
}, [activeCustomHeaderLevels, measureCustomHeaderHeight, viewport.width]);
|
|
1485
1622
|
const [filterEditor, setFilterEditor] = useState4(null);
|
|
1486
1623
|
const [contextMenu, setContextMenu] = useState4(null);
|
|
1487
1624
|
const [insertCounts, setInsertCounts] = useState4({ before: 1, after: 1 });
|
|
@@ -1491,6 +1628,7 @@ function Table({
|
|
|
1491
1628
|
const [deleteBusy, setDeleteBusy] = useState4(false);
|
|
1492
1629
|
const [toast, setToast] = useState4(null);
|
|
1493
1630
|
const [resizeGuideX, setResizeGuideX] = useState4(null);
|
|
1631
|
+
const [resizeGuideTop, setResizeGuideTop] = useState4(0);
|
|
1494
1632
|
const [hoveredHeaderAction, setHoveredHeaderAction] = useState4(null);
|
|
1495
1633
|
const [visibleHeaderTooltip, setVisibleHeaderTooltip] = useState4(null);
|
|
1496
1634
|
const [hoveredCellTooltip, setHoveredCellTooltip] = useState4(null);
|
|
@@ -1575,7 +1713,17 @@ function Table({
|
|
|
1575
1713
|
const metrics = useMemo(() => buildColumnMetrics(columns, { columnDraggable, viewportWidth: viewport.width }), [columnDraggable, columns, viewport.width]);
|
|
1576
1714
|
const contentWidth = metrics.length > 0 ? metrics[metrics.length - 1].right : 0;
|
|
1577
1715
|
const contentHeight = rows.length * rowHeight;
|
|
1578
|
-
const fixedWidth = useMemo(() => columns.reduce((width2, column, index) => column.fixed === "left" ?
|
|
1716
|
+
const fixedWidth = useMemo(() => columns.reduce((width2, column, index) => column.fixed === "left" ? width2 + metrics[index].width : width2, 0), [columns, metrics]);
|
|
1717
|
+
const leftFixedOffsets = useMemo(() => {
|
|
1718
|
+
const offsets = /* @__PURE__ */ new Map();
|
|
1719
|
+
let offset = 0;
|
|
1720
|
+
for (let index = 0; index < columns.length; index += 1) {
|
|
1721
|
+
if (columns[index].fixed !== "left") continue;
|
|
1722
|
+
offsets.set(index, offset);
|
|
1723
|
+
offset += metrics[index].width;
|
|
1724
|
+
}
|
|
1725
|
+
return offsets;
|
|
1726
|
+
}, [columns, metrics]);
|
|
1579
1727
|
const rightFixedWidth = useMemo(() => columns.reduce((width2, column, index) => column.fixed === "right" ? width2 + metrics[index].width : width2, 0), [columns, metrics]);
|
|
1580
1728
|
const rightFixedOffsets = useMemo(() => {
|
|
1581
1729
|
const offsets = /* @__PURE__ */ new Map();
|
|
@@ -1587,9 +1735,20 @@ function Table({
|
|
|
1587
1735
|
}
|
|
1588
1736
|
return offsets;
|
|
1589
1737
|
}, [columns, metrics]);
|
|
1738
|
+
const scrollableColumnLefts = useMemo(() => {
|
|
1739
|
+
const lefts = [];
|
|
1740
|
+
let width2 = fixedWidth;
|
|
1741
|
+
for (let index = 0; index < columns.length; index += 1) {
|
|
1742
|
+
lefts[index] = width2;
|
|
1743
|
+
if (columns[index].fixed === void 0) width2 += metrics[index].width;
|
|
1744
|
+
}
|
|
1745
|
+
return lefts;
|
|
1746
|
+
}, [columns, fixedWidth, metrics]);
|
|
1747
|
+
const getScrollableColumnLeft = useCallback2((columnIndex) => scrollableColumnLefts[columnIndex] ?? fixedWidth, [fixedWidth, scrollableColumnLefts]);
|
|
1590
1748
|
const hasRowSelectionColumn = useMemo(() => columns.some((column) => column.rowSelection), [columns]);
|
|
1591
1749
|
const rowSelectionMode = typeof rowSelection === "object" ? rowSelection.mode ?? "multiple" : "multiple";
|
|
1592
|
-
const
|
|
1750
|
+
const summarySignature = useMemo(() => getSummarySignature(columns), [columns]);
|
|
1751
|
+
const hasSummaryRow = rows.length > 0 && summaryEnabled && summarySignature.length > 0;
|
|
1593
1752
|
const topSummaryHeight = hasSummaryRow && summaryPosition === "top" ? rowHeight : 0;
|
|
1594
1753
|
const bottomSummaryHeight = hasSummaryRow && summaryPosition === "bottom" ? rowHeight : 0;
|
|
1595
1754
|
const bodyTop = headerHeight + topSummaryHeight;
|
|
@@ -1597,22 +1756,22 @@ function Table({
|
|
|
1597
1756
|
const realContentHeight = bodyTop + bodyContentHeight + bottomSummaryHeight + (contentWidth > viewport.width ? horizontalScrollbarHeight : 0);
|
|
1598
1757
|
const effectiveHeight = autoHeight ? Math.max(bodyTop + bottomSummaryHeight, Math.min(resolvedHeight, realContentHeight)) : resolvedHeight;
|
|
1599
1758
|
const renderHeight = Math.max(0, effectiveHeight - bottomSummaryHeight);
|
|
1600
|
-
const bodyViewportHeight = Math.max(0, renderHeight - (fixedHeader ? headerHeight : 0)
|
|
1601
|
-
const [summaryState, setSummaryState] = useState4(() => ({
|
|
1602
|
-
const summaryPending = hasSummaryRow && (loading || summaryState.pending || summaryState.
|
|
1603
|
-
const summaryValues = hasSummaryRow && summaryState.
|
|
1759
|
+
const bodyViewportHeight = Math.max(0, renderHeight - (fixedHeader ? headerHeight + topSummaryHeight : 0));
|
|
1760
|
+
const [summaryState, setSummaryState] = useState4(() => ({ signature: "", rows: null, values: /* @__PURE__ */ new Map(), pending: false }));
|
|
1761
|
+
const summaryPending = hasSummaryRow && (loading || summaryState.pending || summaryState.signature !== summarySignature || summaryState.rows !== rows);
|
|
1762
|
+
const summaryValues = hasSummaryRow && summaryState.signature === summarySignature && summaryState.rows === rows ? summaryState.values : /* @__PURE__ */ new Map();
|
|
1604
1763
|
useEffect2(() => {
|
|
1605
1764
|
let cancelled = false;
|
|
1606
1765
|
if (!hasSummaryRow) {
|
|
1607
|
-
setSummaryState((current) => current.
|
|
1766
|
+
setSummaryState((current) => current.signature === "" && current.rows === null && current.values.size === 0 && !current.pending ? current : { signature: "", rows: null, values: /* @__PURE__ */ new Map(), pending: false });
|
|
1608
1767
|
return () => {
|
|
1609
1768
|
cancelled = true;
|
|
1610
1769
|
};
|
|
1611
1770
|
}
|
|
1612
1771
|
setSummaryState((current) => ({
|
|
1613
|
-
|
|
1772
|
+
signature: summarySignature,
|
|
1614
1773
|
rows,
|
|
1615
|
-
values: current.
|
|
1774
|
+
values: current.signature === summarySignature && current.rows === rows ? current.values : /* @__PURE__ */ new Map(),
|
|
1616
1775
|
pending: true
|
|
1617
1776
|
}));
|
|
1618
1777
|
if (loading) {
|
|
@@ -1625,13 +1784,13 @@ function Table({
|
|
|
1625
1784
|
if (cancelled) return;
|
|
1626
1785
|
const values = await calculateSummaryValues(columns, rows);
|
|
1627
1786
|
if (cancelled) return;
|
|
1628
|
-
setSummaryState({
|
|
1787
|
+
setSummaryState({ signature: summarySignature, rows, values, pending: false });
|
|
1629
1788
|
};
|
|
1630
1789
|
void run();
|
|
1631
1790
|
return () => {
|
|
1632
1791
|
cancelled = true;
|
|
1633
1792
|
};
|
|
1634
|
-
}, [
|
|
1793
|
+
}, [hasSummaryRow, loading, rows, summarySignature]);
|
|
1635
1794
|
const domRange = useMemo(() => {
|
|
1636
1795
|
if (!isVirtualized) return { rowStart: 0, rowEnd: rows.length, columnStart: 0, columnEnd: columns.length };
|
|
1637
1796
|
const currentScroll2 = scrollRef.current;
|
|
@@ -1773,11 +1932,163 @@ function Table({
|
|
|
1773
1932
|
const scroll = scrollRef.current;
|
|
1774
1933
|
const rowScrollTop = fixedHeader ? scroll.top : Math.max(0, scroll.top - bodyTop);
|
|
1775
1934
|
const range = isVirtualized ? getViewportRange(scroll.left, rowScrollTop, viewport.width, bodyViewportHeight, rows.length, rowHeight, metrics, virtualOverscan) : { rowStart: 0, rowEnd: rows.length, columnStart: 0, columnEnd: columns.length };
|
|
1776
|
-
paintGrid({ context, width: viewport.width, height: renderHeight, pixelRatio: ratio, scrollLeft: scroll.left, scrollTop: scroll.top, rowHeight, headerHeight, bodyTop, suppressLastRowBottomBorder: bottomSummaryHeight > 0, suppressFrameBottomBorder: bottomSummaryHeight > 0, fixedHeader, verticalBorderless: !hasVerticalBorders, striped: hasStripedRows, columnDraggable, sortState, filterValues, hoveredHeaderAction: hoveredHeaderActionRef.current, rows, columns, metrics, range, selection, editing, hoveredRowIndex: hoveredRowIndexRef.current, selectionRange, selectedRowKeys: selectedRowKeySet, selectedColumnKeys: selectedColumnKeySet, cellSpans: cellSpanLookup.covered, maxRowSpan: cellSpanLookup.maxRowSpan, highlightEditedCells: highlightsEditedCells, highlightInsertedRows: highlightsInsertedRows, insertedRowKeys: insertedRowKeySet, editedCellKeys, cellAnnotations, getRowKey, rowDragPreview, colors: themeColors });
|
|
1777
|
-
}, [bodyTop, bodyViewportHeight, bottomSummaryHeight, cellAnnotations, cellSpanLookup, columnDraggable, columns, editedCellHighlightColor, editedCellKeys, editing, renderHeight, filterValues, fixedHeader, getRowKey, hasStripedRows, hasVerticalBorders, headerHeight, highlightsEditedCells, highlightsInsertedRows, insertedRowHighlightColor, insertedRowKeySet, isVirtualized, metrics, rowDragPreview, rowHeight, rows, selectedColumnKeySet, selectedRowKeySet, selection, selectionRange, sortState, stripedColor, viewport.width, virtualOverscan]);
|
|
1935
|
+
paintGrid({ context, width: viewport.width, height: renderHeight, pixelRatio: ratio, scrollLeft: scroll.left, scrollTop: scroll.top, rowHeight, headerHeight, headerLeafTop, headerLeafHeight, bodyTop, suppressLastRowBottomBorder: bottomSummaryHeight > 0, suppressFrameBottomBorder: bottomSummaryHeight > 0, fixedHeader, verticalBorderless: !hasVerticalBorders, striped: hasStripedRows, columnDraggable, sortState, filterValues, hoveredHeaderAction: hoveredHeaderActionRef.current, rows, columns, metrics, range, selection, editing, hoveredRowIndex: hoveredRowIndexRef.current, selectionRange, selectedRowKeys: selectedRowKeySet, selectedColumnKeys: selectedColumnKeySet, cellSpans: cellSpanLookup.covered, maxRowSpan: cellSpanLookup.maxRowSpan, highlightEditedCells: highlightsEditedCells, highlightInsertedRows: highlightsInsertedRows, insertedRowKeys: insertedRowKeySet, editedCellKeys, cellAnnotations, getRowKey, rowDragPreview, colors: themeColors });
|
|
1936
|
+
}, [bodyTop, bodyViewportHeight, bottomSummaryHeight, cellAnnotations, cellSpanLookup, columnDraggable, columns, editedCellHighlightColor, editedCellKeys, editing, renderHeight, filterValues, fixedHeader, getRowKey, hasStripedRows, hasVerticalBorders, headerDepth, headerHeight, headerLeafHeight, headerLeafTop, highlightsEditedCells, highlightsInsertedRows, insertedRowHighlightColor, insertedRowKeySet, isVirtualized, metrics, rowDragPreview, rowHeight, rows, selectedColumnKeySet, selectedRowKeySet, selection, selectionRange, sortState, stripedColor, viewport.width, virtualOverscan]);
|
|
1778
1937
|
const scheduleDraw = useCallback2(() => {
|
|
1779
1938
|
if (frameRef.current === null) frameRef.current = requestAnimationFrame(draw);
|
|
1780
1939
|
}, [draw]);
|
|
1940
|
+
const drawImmediately = useCallback2(() => {
|
|
1941
|
+
if (frameRef.current !== null) cancelAnimationFrame(frameRef.current);
|
|
1942
|
+
frameRef.current = null;
|
|
1943
|
+
draw();
|
|
1944
|
+
}, [draw]);
|
|
1945
|
+
const getSelectionHorizontalVisibility = useCallback2((scrollLeft) => {
|
|
1946
|
+
if (!selection || selectionRange) return "visible";
|
|
1947
|
+
const column = columns[selection.columnIndex];
|
|
1948
|
+
if (!column || column.fixed !== void 0) return "visible";
|
|
1949
|
+
const span = getCellSpan(selection.rowIndex, selection.columnIndex);
|
|
1950
|
+
const left = getScrollableColumnLeft(selection.columnIndex) - scrollLeft;
|
|
1951
|
+
const right = left + getCellDisplayWidth(selection.columnIndex, span?.colSpan ?? 1);
|
|
1952
|
+
if (right <= fixedWidth) return "hidden-left";
|
|
1953
|
+
if (left >= viewport.width - rightFixedWidth) return "hidden-right";
|
|
1954
|
+
return "visible";
|
|
1955
|
+
}, [columns, fixedWidth, getCellDisplayWidth, getCellSpan, getScrollableColumnLeft, rightFixedWidth, selection, selectionRange, viewport.width]);
|
|
1956
|
+
const getSelectionEdgeIndicator = useCallback2((scrollLeft, scrollTop) => {
|
|
1957
|
+
if (!selection || selectionRange) return null;
|
|
1958
|
+
const row = rows[selection.rowIndex];
|
|
1959
|
+
const metric = metrics[selection.columnIndex];
|
|
1960
|
+
const column = columns[selection.columnIndex];
|
|
1961
|
+
if (!row || !metric || !column || getRowKey(row, selection.rowIndex) !== selection.rowKey) return null;
|
|
1962
|
+
const span = getCellSpan(selection.rowIndex, selection.columnIndex);
|
|
1963
|
+
const rawLeft = column.fixed === "left" ? leftFixedOffsets.get(selection.columnIndex) ?? 0 : column.fixed === "right" ? viewport.width - (rightFixedOffsets.get(selection.columnIndex) ?? 0) - metric.width : getScrollableColumnLeft(selection.columnIndex) - scrollLeft;
|
|
1964
|
+
const rawRight = rawLeft + getCellDisplayWidth(selection.columnIndex, span?.colSpan ?? 1);
|
|
1965
|
+
const rawTop = bodyTop + selection.rowIndex * rowHeight - scrollTop;
|
|
1966
|
+
const rawBottom = rawTop + rowHeight * (span?.rowSpan ?? 1);
|
|
1967
|
+
const horizontalStart = column.fixed === void 0 ? fixedWidth : 0;
|
|
1968
|
+
const horizontalEnd = column.fixed === void 0 ? viewport.width - rightFixedWidth : viewport.width;
|
|
1969
|
+
const verticalStart = fixedHeader ? bodyTop : 0;
|
|
1970
|
+
const hiddenLeft = column.fixed === void 0 && rawRight <= horizontalStart;
|
|
1971
|
+
const hiddenRight = column.fixed === void 0 && rawLeft >= horizontalEnd;
|
|
1972
|
+
const hiddenTop = rawBottom <= verticalStart;
|
|
1973
|
+
const hiddenBottom = rawTop >= renderHeight;
|
|
1974
|
+
if (!hiddenLeft && !hiddenRight && !hiddenTop && !hiddenBottom) return null;
|
|
1975
|
+
const borderWidth = 2;
|
|
1976
|
+
const cornerLength = 10;
|
|
1977
|
+
const hitSlop = 6;
|
|
1978
|
+
const targetSize = borderWidth + hitSlop * 2;
|
|
1979
|
+
const rightBoundaryInset = rightFixedWidth > 0 && hasVerticalBorders ? 1 : 0;
|
|
1980
|
+
const markerX = hiddenLeft ? horizontalStart : horizontalEnd - borderWidth - rightBoundaryInset;
|
|
1981
|
+
const markerY = hiddenTop ? verticalStart : renderHeight - borderWidth;
|
|
1982
|
+
if (hiddenLeft || hiddenRight) {
|
|
1983
|
+
let markerTop = Math.max(rawTop - borderWidth, verticalStart);
|
|
1984
|
+
let markerBottom = Math.min(rawBottom + 1, renderHeight);
|
|
1985
|
+
const topClip = Math.max(0, verticalStart - (rawTop - borderWidth));
|
|
1986
|
+
const bottomClip = Math.max(0, rawBottom + 1 - renderHeight);
|
|
1987
|
+
if (topClip > 0 || bottomClip > 0) {
|
|
1988
|
+
const foldAtTop = topClip > 0;
|
|
1989
|
+
const fold = Math.min(cornerLength, foldAtTop ? topClip : bottomClip);
|
|
1990
|
+
if (fold > borderWidth) {
|
|
1991
|
+
const cornerWidth = fold;
|
|
1992
|
+
const cornerHeight = Math.max(cornerLength, markerBottom - markerTop);
|
|
1993
|
+
const cornerOnLeft = hiddenLeft;
|
|
1994
|
+
const shapeLeft = cornerOnLeft ? markerX : markerX + borderWidth - cornerWidth;
|
|
1995
|
+
const shapeTop = foldAtTop ? verticalStart : renderHeight - cornerHeight;
|
|
1996
|
+
return {
|
|
1997
|
+
side: `${foldAtTop ? "top" : "bottom"}-${cornerOnLeft ? "left" : "right"}`,
|
|
1998
|
+
orientation: "corner",
|
|
1999
|
+
style: {
|
|
2000
|
+
left: shapeLeft - hitSlop,
|
|
2001
|
+
top: shapeTop - hitSlop,
|
|
2002
|
+
width: cornerWidth + hitSlop * 2,
|
|
2003
|
+
height: cornerHeight + hitSlop * 2
|
|
2004
|
+
},
|
|
2005
|
+
corner: { width: cornerWidth, height: cornerHeight }
|
|
2006
|
+
};
|
|
2007
|
+
}
|
|
2008
|
+
}
|
|
2009
|
+
if (markerBottom <= markerTop) return null;
|
|
2010
|
+
return {
|
|
2011
|
+
side: hiddenLeft ? "left" : "right",
|
|
2012
|
+
orientation: "vertical",
|
|
2013
|
+
viewportEdge: hiddenLeft && horizontalStart === 0 ? "left" : hiddenRight && horizontalEnd === viewport.width ? "right" : void 0,
|
|
2014
|
+
style: {
|
|
2015
|
+
left: markerX - hitSlop,
|
|
2016
|
+
top: markerTop,
|
|
2017
|
+
width: targetSize,
|
|
2018
|
+
height: markerBottom - markerTop
|
|
2019
|
+
}
|
|
2020
|
+
};
|
|
2021
|
+
}
|
|
2022
|
+
const horizontalMarkerEnd = horizontalEnd - rightBoundaryInset;
|
|
2023
|
+
let markerLeft = Math.max(rawLeft - borderWidth, horizontalStart);
|
|
2024
|
+
let markerRight = Math.min(rawRight + 1, horizontalMarkerEnd);
|
|
2025
|
+
const leftClip = Math.max(0, horizontalStart - (rawLeft - borderWidth));
|
|
2026
|
+
const rightClip = Math.max(0, rawRight + 1 - horizontalMarkerEnd);
|
|
2027
|
+
if (leftClip > 0 || rightClip > 0) {
|
|
2028
|
+
const foldAtLeft = leftClip > 0;
|
|
2029
|
+
const fold = Math.min(cornerLength, foldAtLeft ? leftClip : rightClip);
|
|
2030
|
+
if (fold > borderWidth) {
|
|
2031
|
+
const cornerWidth = Math.max(cornerLength, markerRight - markerLeft);
|
|
2032
|
+
const cornerHeight = fold;
|
|
2033
|
+
const shapeLeft = foldAtLeft ? horizontalStart : horizontalMarkerEnd - cornerWidth;
|
|
2034
|
+
const shapeTop = hiddenTop ? verticalStart : renderHeight - cornerHeight;
|
|
2035
|
+
return {
|
|
2036
|
+
side: `${hiddenTop ? "top" : "bottom"}-${foldAtLeft ? "left" : "right"}`,
|
|
2037
|
+
orientation: "corner",
|
|
2038
|
+
style: {
|
|
2039
|
+
left: shapeLeft - hitSlop,
|
|
2040
|
+
top: shapeTop - hitSlop,
|
|
2041
|
+
width: cornerWidth + hitSlop * 2,
|
|
2042
|
+
height: cornerHeight + hitSlop * 2
|
|
2043
|
+
},
|
|
2044
|
+
corner: { width: cornerWidth, height: cornerHeight }
|
|
2045
|
+
};
|
|
2046
|
+
}
|
|
2047
|
+
}
|
|
2048
|
+
if (markerRight <= markerLeft) return null;
|
|
2049
|
+
return {
|
|
2050
|
+
side: hiddenTop ? "top" : "bottom",
|
|
2051
|
+
orientation: "horizontal",
|
|
2052
|
+
viewportEdge: hiddenTop && verticalStart === 0 ? "top" : hiddenBottom ? "bottom" : void 0,
|
|
2053
|
+
style: {
|
|
2054
|
+
left: markerLeft,
|
|
2055
|
+
top: markerY - hitSlop,
|
|
2056
|
+
width: markerRight - markerLeft,
|
|
2057
|
+
height: targetSize
|
|
2058
|
+
}
|
|
2059
|
+
};
|
|
2060
|
+
}, [bodyTop, columns, fixedHeader, fixedWidth, getCellDisplayWidth, getCellSpan, getRowKey, getScrollableColumnLeft, hasVerticalBorders, leftFixedOffsets, metrics, renderHeight, rightFixedOffsets, rightFixedWidth, rowHeight, rows, selection, selectionRange, viewport.width]);
|
|
2061
|
+
const syncSelectionEdgeTarget = useCallback2((scrollLeft, scrollTop) => {
|
|
2062
|
+
const target = selectionEdgeTargetRef.current;
|
|
2063
|
+
if (!target) return;
|
|
2064
|
+
const indicator = getSelectionEdgeIndicator(scrollLeft, scrollTop);
|
|
2065
|
+
if (!indicator) {
|
|
2066
|
+
target.style.visibility = "hidden";
|
|
2067
|
+
return;
|
|
2068
|
+
}
|
|
2069
|
+
const orientationChanged = !target.classList.contains(`is-${indicator.orientation}`);
|
|
2070
|
+
target.className = `rvg-selection-edge-target is-${indicator.orientation} is-${indicator.side}${indicator.viewportEdge ? ` is-viewport-${indicator.viewportEdge}` : ""}${orientationChanged ? " is-shape-syncing" : ""}`;
|
|
2071
|
+
target.style.left = `${Number(indicator.style.left)}px`;
|
|
2072
|
+
target.style.top = `${Number(indicator.style.top)}px`;
|
|
2073
|
+
target.style.width = `${Number(indicator.style.width)}px`;
|
|
2074
|
+
target.style.height = `${Number(indicator.style.height)}px`;
|
|
2075
|
+
if (indicator.corner) {
|
|
2076
|
+
target.style.setProperty("--rvg-corner-width", `${indicator.corner.width}px`);
|
|
2077
|
+
target.style.setProperty("--rvg-corner-height", `${indicator.corner.height}px`);
|
|
2078
|
+
target.style.setProperty("--rvg-corner-hover-width-extension", "4px");
|
|
2079
|
+
target.style.setProperty("--rvg-corner-hover-height-extension", "4px");
|
|
2080
|
+
} else {
|
|
2081
|
+
target.style.removeProperty("--rvg-corner-width");
|
|
2082
|
+
target.style.removeProperty("--rvg-corner-height");
|
|
2083
|
+
target.style.removeProperty("--rvg-corner-hover-width-extension");
|
|
2084
|
+
target.style.removeProperty("--rvg-corner-hover-height-extension");
|
|
2085
|
+
}
|
|
2086
|
+
target.style.visibility = "visible";
|
|
2087
|
+
if (orientationChanged) {
|
|
2088
|
+
void target.offsetWidth;
|
|
2089
|
+
target.classList.remove("is-shape-syncing");
|
|
2090
|
+
}
|
|
2091
|
+
}, [getSelectionEdgeIndicator]);
|
|
1781
2092
|
useLayoutEffect2(() => {
|
|
1782
2093
|
const element = scrollerRef.current;
|
|
1783
2094
|
if (!element) return;
|
|
@@ -1811,6 +2122,8 @@ function Table({
|
|
|
1811
2122
|
if (sortDebounceRef.current !== null) clearTimeout(sortDebounceRef.current);
|
|
1812
2123
|
if (textFrameRef.current !== null) cancelAnimationFrame(textFrameRef.current);
|
|
1813
2124
|
if (toastTimerRef.current !== null) clearTimeout(toastTimerRef.current);
|
|
2125
|
+
if (verticalScrollbarVisibilityTimerRef.current !== null) clearTimeout(verticalScrollbarVisibilityTimerRef.current);
|
|
2126
|
+
if (horizontalScrollbarVisibilityTimerRef.current !== null) clearTimeout(horizontalScrollbarVisibilityTimerRef.current);
|
|
1814
2127
|
}, []);
|
|
1815
2128
|
useEffect2(() => {
|
|
1816
2129
|
const scroller = scrollerRef.current;
|
|
@@ -1862,10 +2175,218 @@ function Table({
|
|
|
1862
2175
|
scroller.addEventListener("scroll", lockScroll);
|
|
1863
2176
|
return () => scroller.removeEventListener("scroll", lockScroll);
|
|
1864
2177
|
}, [insertBusy]);
|
|
2178
|
+
const showCustomScrollbar = useCallback2((axis, persist = false) => {
|
|
2179
|
+
const showVertical = axis === "vertical" || axis === "both";
|
|
2180
|
+
const showHorizontal = axis === "horizontal" || axis === "both";
|
|
2181
|
+
if (showVertical) {
|
|
2182
|
+
verticalScrollbarRef.current?.classList.add("is-active");
|
|
2183
|
+
if (verticalScrollbarVisibilityTimerRef.current !== null) clearTimeout(verticalScrollbarVisibilityTimerRef.current);
|
|
2184
|
+
if (!persist) {
|
|
2185
|
+
verticalScrollbarVisibilityTimerRef.current = setTimeout(() => {
|
|
2186
|
+
verticalScrollbarRef.current?.classList.remove("is-active");
|
|
2187
|
+
verticalScrollbarVisibilityTimerRef.current = null;
|
|
2188
|
+
}, 900);
|
|
2189
|
+
}
|
|
2190
|
+
}
|
|
2191
|
+
if (showHorizontal) {
|
|
2192
|
+
horizontalScrollbarRef.current?.classList.add("is-active");
|
|
2193
|
+
if (horizontalScrollbarVisibilityTimerRef.current !== null) clearTimeout(horizontalScrollbarVisibilityTimerRef.current);
|
|
2194
|
+
if (!persist) {
|
|
2195
|
+
horizontalScrollbarVisibilityTimerRef.current = setTimeout(() => {
|
|
2196
|
+
horizontalScrollbarRef.current?.classList.remove("is-active");
|
|
2197
|
+
horizontalScrollbarVisibilityTimerRef.current = null;
|
|
2198
|
+
}, 900);
|
|
2199
|
+
}
|
|
2200
|
+
}
|
|
2201
|
+
}, []);
|
|
2202
|
+
const keepCustomScrollbarVisible = useCallback2((axis) => {
|
|
2203
|
+
showCustomScrollbar(axis, true);
|
|
2204
|
+
}, [showCustomScrollbar]);
|
|
2205
|
+
const releaseCustomScrollbarVisible = useCallback2((axis) => {
|
|
2206
|
+
if (axis === "vertical" && verticalScrollbarDragRef.current) return;
|
|
2207
|
+
if (axis === "horizontal" && horizontalScrollbarDragRef.current) return;
|
|
2208
|
+
showCustomScrollbar(axis);
|
|
2209
|
+
}, [showCustomScrollbar]);
|
|
2210
|
+
const updateCustomScrollbars = useCallback2(() => {
|
|
2211
|
+
const scroller = scrollerRef.current;
|
|
2212
|
+
const verticalTrack = verticalScrollbarRef.current;
|
|
2213
|
+
const verticalThumb = verticalScrollbarThumbRef.current;
|
|
2214
|
+
const horizontalTrack = horizontalScrollbarRef.current;
|
|
2215
|
+
const horizontalThumb = horizontalScrollbarThumbRef.current;
|
|
2216
|
+
if (!scroller || !verticalTrack || !verticalThumb || !horizontalTrack || !horizontalThumb) return;
|
|
2217
|
+
const maxScrollTop = scroller.scrollHeight - scroller.clientHeight;
|
|
2218
|
+
if (maxScrollTop <= 1 || renderHeight <= 0) {
|
|
2219
|
+
verticalTrack.style.display = "none";
|
|
2220
|
+
} else {
|
|
2221
|
+
const top = fixedHeader ? bodyTop : 0;
|
|
2222
|
+
const bottom = bottomSummaryHeight;
|
|
2223
|
+
const trackHeight = Math.max(0, effectiveHeight - top - bottom);
|
|
2224
|
+
if (trackHeight <= 0) {
|
|
2225
|
+
verticalTrack.style.display = "none";
|
|
2226
|
+
} else {
|
|
2227
|
+
const thumbHeight = Math.max(28, Math.round(trackHeight * (scroller.clientHeight / scroller.scrollHeight)));
|
|
2228
|
+
const maxThumbTop = Math.max(0, trackHeight - thumbHeight);
|
|
2229
|
+
const thumbTop = maxScrollTop > 0 ? Math.round(scroller.scrollTop / maxScrollTop * maxThumbTop) : 0;
|
|
2230
|
+
verticalTrack.style.display = "block";
|
|
2231
|
+
verticalTrack.style.top = `${top}px`;
|
|
2232
|
+
verticalTrack.style.bottom = `${bottom}px`;
|
|
2233
|
+
verticalThumb.style.height = `${Math.min(trackHeight, thumbHeight)}px`;
|
|
2234
|
+
verticalThumb.style.transform = `translateY(${thumbTop}px)`;
|
|
2235
|
+
}
|
|
2236
|
+
}
|
|
2237
|
+
const maxScrollLeft = scroller.scrollWidth - scroller.clientWidth;
|
|
2238
|
+
if (maxScrollLeft <= 1 || viewport.width <= 0) {
|
|
2239
|
+
horizontalTrack.style.display = "none";
|
|
2240
|
+
return;
|
|
2241
|
+
}
|
|
2242
|
+
const trackLeft = fixedWidth;
|
|
2243
|
+
const trackRight = rightFixedWidth;
|
|
2244
|
+
const trackWidth = Math.max(0, viewport.width - trackLeft - trackRight);
|
|
2245
|
+
if (trackWidth <= 0) {
|
|
2246
|
+
horizontalTrack.style.display = "none";
|
|
2247
|
+
return;
|
|
2248
|
+
}
|
|
2249
|
+
const thumbWidth = Math.max(36, Math.round(trackWidth * (scroller.clientWidth / scroller.scrollWidth)));
|
|
2250
|
+
const maxThumbLeft = Math.max(0, trackWidth - thumbWidth);
|
|
2251
|
+
const thumbLeft = maxScrollLeft > 0 ? Math.round(scroller.scrollLeft / maxScrollLeft * maxThumbLeft) : 0;
|
|
2252
|
+
horizontalTrack.style.display = "block";
|
|
2253
|
+
horizontalTrack.style.left = `${trackLeft}px`;
|
|
2254
|
+
horizontalTrack.style.right = `${trackRight}px`;
|
|
2255
|
+
horizontalTrack.style.bottom = `${bottomSummaryHeight + 2}px`;
|
|
2256
|
+
horizontalThumb.style.width = `${Math.min(trackWidth, thumbWidth)}px`;
|
|
2257
|
+
horizontalThumb.style.transform = `translateX(${thumbLeft}px)`;
|
|
2258
|
+
}, [bodyTop, bottomSummaryHeight, effectiveHeight, fixedHeader, fixedWidth, renderHeight, rightFixedWidth, viewport.width]);
|
|
2259
|
+
useLayoutEffect2(() => {
|
|
2260
|
+
updateCustomScrollbars();
|
|
2261
|
+
}, [contentHeight, contentWidth, updateCustomScrollbars, viewport.height, viewport.width]);
|
|
2262
|
+
const handleVerticalScrollbarPointerDown = useCallback2((event) => {
|
|
2263
|
+
const scroller = scrollerRef.current;
|
|
2264
|
+
const track = verticalScrollbarRef.current;
|
|
2265
|
+
const thumb = verticalScrollbarThumbRef.current;
|
|
2266
|
+
if (!scroller || !track || !thumb || event.button !== 0) return;
|
|
2267
|
+
event.preventDefault();
|
|
2268
|
+
event.currentTarget.setPointerCapture(event.pointerId);
|
|
2269
|
+
const trackRect = track.getBoundingClientRect();
|
|
2270
|
+
const thumbRect = thumb.getBoundingClientRect();
|
|
2271
|
+
const maxScrollTop = scroller.scrollHeight - scroller.clientHeight;
|
|
2272
|
+
const trackHeight = trackRect.height;
|
|
2273
|
+
const thumbHeight = thumbRect.height;
|
|
2274
|
+
showCustomScrollbar("vertical", true);
|
|
2275
|
+
if (event.target === thumb) {
|
|
2276
|
+
verticalScrollbarDragRef.current = {
|
|
2277
|
+
pointerId: event.pointerId,
|
|
2278
|
+
startY: event.clientY,
|
|
2279
|
+
startTop: thumbRect.top - trackRect.top,
|
|
2280
|
+
trackHeight,
|
|
2281
|
+
thumbHeight,
|
|
2282
|
+
maxScrollTop
|
|
2283
|
+
};
|
|
2284
|
+
return;
|
|
2285
|
+
}
|
|
2286
|
+
const nextThumbTop = Math.max(0, Math.min(trackHeight - thumbHeight, event.clientY - trackRect.top - thumbHeight / 2));
|
|
2287
|
+
const maxThumbTop = Math.max(1, trackHeight - thumbHeight);
|
|
2288
|
+
scroller.scrollTop = nextThumbTop / maxThumbTop * maxScrollTop;
|
|
2289
|
+
updateCustomScrollbars();
|
|
2290
|
+
verticalScrollbarDragRef.current = {
|
|
2291
|
+
pointerId: event.pointerId,
|
|
2292
|
+
startY: event.clientY,
|
|
2293
|
+
startTop: nextThumbTop,
|
|
2294
|
+
trackHeight,
|
|
2295
|
+
thumbHeight,
|
|
2296
|
+
maxScrollTop
|
|
2297
|
+
};
|
|
2298
|
+
}, [showCustomScrollbar, updateCustomScrollbars]);
|
|
2299
|
+
const handleVerticalScrollbarPointerMove = useCallback2((event) => {
|
|
2300
|
+
const drag = verticalScrollbarDragRef.current;
|
|
2301
|
+
const scroller = scrollerRef.current;
|
|
2302
|
+
if (!drag || !scroller || drag.pointerId !== event.pointerId) return;
|
|
2303
|
+
event.preventDefault();
|
|
2304
|
+
const maxThumbTop = Math.max(1, drag.trackHeight - drag.thumbHeight);
|
|
2305
|
+
const nextThumbTop = Math.max(0, Math.min(maxThumbTop, drag.startTop + event.clientY - drag.startY));
|
|
2306
|
+
scroller.scrollTop = nextThumbTop / maxThumbTop * drag.maxScrollTop;
|
|
2307
|
+
updateCustomScrollbars();
|
|
2308
|
+
}, [updateCustomScrollbars]);
|
|
2309
|
+
const handleVerticalScrollbarPointerUp = useCallback2((event) => {
|
|
2310
|
+
const drag = verticalScrollbarDragRef.current;
|
|
2311
|
+
if (!drag || drag.pointerId !== event.pointerId) return;
|
|
2312
|
+
if (event.currentTarget.hasPointerCapture(event.pointerId)) event.currentTarget.releasePointerCapture(event.pointerId);
|
|
2313
|
+
verticalScrollbarDragRef.current = null;
|
|
2314
|
+
showCustomScrollbar("vertical");
|
|
2315
|
+
}, [showCustomScrollbar]);
|
|
2316
|
+
const handleHorizontalScrollbarPointerDown = useCallback2((event) => {
|
|
2317
|
+
const scroller = scrollerRef.current;
|
|
2318
|
+
const track = horizontalScrollbarRef.current;
|
|
2319
|
+
const thumb = horizontalScrollbarThumbRef.current;
|
|
2320
|
+
if (!scroller || !track || !thumb || event.button !== 0) return;
|
|
2321
|
+
event.preventDefault();
|
|
2322
|
+
event.currentTarget.setPointerCapture(event.pointerId);
|
|
2323
|
+
const trackRect = track.getBoundingClientRect();
|
|
2324
|
+
const thumbRect = thumb.getBoundingClientRect();
|
|
2325
|
+
const maxScrollLeft = scroller.scrollWidth - scroller.clientWidth;
|
|
2326
|
+
const trackWidth = trackRect.width;
|
|
2327
|
+
const thumbWidth = thumbRect.width;
|
|
2328
|
+
showCustomScrollbar("horizontal", true);
|
|
2329
|
+
if (event.target === thumb) {
|
|
2330
|
+
horizontalScrollbarDragRef.current = {
|
|
2331
|
+
pointerId: event.pointerId,
|
|
2332
|
+
startX: event.clientX,
|
|
2333
|
+
startLeft: thumbRect.left - trackRect.left,
|
|
2334
|
+
trackWidth,
|
|
2335
|
+
thumbWidth,
|
|
2336
|
+
maxScrollLeft
|
|
2337
|
+
};
|
|
2338
|
+
return;
|
|
2339
|
+
}
|
|
2340
|
+
const nextThumbLeft = Math.max(0, Math.min(trackWidth - thumbWidth, event.clientX - trackRect.left - thumbWidth / 2));
|
|
2341
|
+
const maxThumbLeft = Math.max(1, trackWidth - thumbWidth);
|
|
2342
|
+
const previousScrollLeft = scroller.scrollLeft;
|
|
2343
|
+
const nextScrollLeft = nextThumbLeft / maxThumbLeft * maxScrollLeft;
|
|
2344
|
+
const visibilityChanged = getSelectionHorizontalVisibility(previousScrollLeft) !== getSelectionHorizontalVisibility(nextScrollLeft);
|
|
2345
|
+
scroller.scrollLeft = nextScrollLeft;
|
|
2346
|
+
scrollRef.current = { left: scroller.scrollLeft, top: scroller.scrollTop };
|
|
2347
|
+
syncSelectionEdgeTarget(scroller.scrollLeft, scroller.scrollTop);
|
|
2348
|
+
if (visibilityChanged) drawImmediately();
|
|
2349
|
+
updateCustomScrollbars();
|
|
2350
|
+
horizontalScrollbarDragRef.current = {
|
|
2351
|
+
pointerId: event.pointerId,
|
|
2352
|
+
startX: event.clientX,
|
|
2353
|
+
startLeft: nextThumbLeft,
|
|
2354
|
+
trackWidth,
|
|
2355
|
+
thumbWidth,
|
|
2356
|
+
maxScrollLeft
|
|
2357
|
+
};
|
|
2358
|
+
}, [drawImmediately, getSelectionHorizontalVisibility, showCustomScrollbar, syncSelectionEdgeTarget, updateCustomScrollbars]);
|
|
2359
|
+
const handleHorizontalScrollbarPointerMove = useCallback2((event) => {
|
|
2360
|
+
const drag = horizontalScrollbarDragRef.current;
|
|
2361
|
+
const scroller = scrollerRef.current;
|
|
2362
|
+
if (!drag || !scroller || drag.pointerId !== event.pointerId) return;
|
|
2363
|
+
event.preventDefault();
|
|
2364
|
+
const maxThumbLeft = Math.max(1, drag.trackWidth - drag.thumbWidth);
|
|
2365
|
+
const nextThumbLeft = Math.max(0, Math.min(maxThumbLeft, drag.startLeft + event.clientX - drag.startX));
|
|
2366
|
+
const previousScrollLeft = scroller.scrollLeft;
|
|
2367
|
+
const nextScrollLeft = nextThumbLeft / maxThumbLeft * drag.maxScrollLeft;
|
|
2368
|
+
const visibilityChanged = getSelectionHorizontalVisibility(previousScrollLeft) !== getSelectionHorizontalVisibility(nextScrollLeft);
|
|
2369
|
+
scroller.scrollLeft = nextScrollLeft;
|
|
2370
|
+
scrollRef.current = { left: scroller.scrollLeft, top: scroller.scrollTop };
|
|
2371
|
+
syncSelectionEdgeTarget(scroller.scrollLeft, scroller.scrollTop);
|
|
2372
|
+
if (visibilityChanged) drawImmediately();
|
|
2373
|
+
updateCustomScrollbars();
|
|
2374
|
+
}, [drawImmediately, getSelectionHorizontalVisibility, syncSelectionEdgeTarget, updateCustomScrollbars]);
|
|
2375
|
+
const handleHorizontalScrollbarPointerUp = useCallback2((event) => {
|
|
2376
|
+
const drag = horizontalScrollbarDragRef.current;
|
|
2377
|
+
if (!drag || drag.pointerId !== event.pointerId) return;
|
|
2378
|
+
if (event.currentTarget.hasPointerCapture(event.pointerId)) event.currentTarget.releasePointerCapture(event.pointerId);
|
|
2379
|
+
horizontalScrollbarDragRef.current = null;
|
|
2380
|
+
showCustomScrollbar("horizontal");
|
|
2381
|
+
}, [showCustomScrollbar]);
|
|
1865
2382
|
const handleScroll = useCallback2(() => {
|
|
1866
2383
|
const element = scrollerRef.current;
|
|
1867
2384
|
if (!element) return;
|
|
2385
|
+
const previousScroll = scrollRef.current;
|
|
2386
|
+
const scrollDeltaX = element.scrollLeft - previousScroll.left;
|
|
2387
|
+
const scrollDeltaY = element.scrollTop - previousScroll.top;
|
|
1868
2388
|
scrollRef.current = { left: element.scrollLeft, top: element.scrollTop };
|
|
2389
|
+
syncSelectionEdgeTarget(element.scrollLeft, element.scrollTop);
|
|
1869
2390
|
const focus = selectionFocusRef.current;
|
|
1870
2391
|
if (focus && selection && !selectionRange) {
|
|
1871
2392
|
const row = rows[selection.rowIndex];
|
|
@@ -1875,7 +2396,7 @@ function Table({
|
|
|
1875
2396
|
if (!validSelection) {
|
|
1876
2397
|
focus.style.display = "none";
|
|
1877
2398
|
} else {
|
|
1878
|
-
const rawLeft = column.fixed === "left" ?
|
|
2399
|
+
const rawLeft = column.fixed === "left" ? leftFixedOffsets.get(selection.columnIndex) ?? 0 : column.fixed === "right" ? viewport.width - (rightFixedOffsets.get(selection.columnIndex) ?? 0) - metric.width : getScrollableColumnLeft(selection.columnIndex) - element.scrollLeft;
|
|
1879
2400
|
const rawTop = bodyTop + selection.rowIndex * rowHeight - element.scrollTop;
|
|
1880
2401
|
const span = getCellSpan(selection.rowIndex, selection.columnIndex);
|
|
1881
2402
|
const rawRight = rawLeft + getCellDisplayWidth(selection.columnIndex, span?.colSpan ?? 1);
|
|
@@ -1887,9 +2408,9 @@ function Table({
|
|
|
1887
2408
|
focus.style.display = "none";
|
|
1888
2409
|
} else {
|
|
1889
2410
|
const borderWidth = 2;
|
|
1890
|
-
const left = Math.round(Math.max(rawLeft - borderWidth, horizontalStart
|
|
2411
|
+
const left = Math.round(Math.max(rawLeft - borderWidth, horizontalStart));
|
|
1891
2412
|
const right = Math.round(Math.min(rawRight + 1, horizontalEnd, viewport.width));
|
|
1892
|
-
const top = Math.round(Math.max(rawTop - borderWidth, verticalStart
|
|
2413
|
+
const top = Math.round(Math.max(rawTop - borderWidth, verticalStart));
|
|
1893
2414
|
const bottom = Math.round(Math.min(rawBottom + 1, renderHeight));
|
|
1894
2415
|
focus.style.display = "block";
|
|
1895
2416
|
focus.style.left = `${left}px`;
|
|
@@ -1908,6 +2429,10 @@ function Table({
|
|
|
1908
2429
|
if (scrollingTextRef.current) scrollingTextRef.current.style.transform = `translateX(${-element.scrollLeft}px)`;
|
|
1909
2430
|
if (scrollingHeaderIconsRef.current) scrollingHeaderIconsRef.current.style.transform = `translateX(${-element.scrollLeft}px)`;
|
|
1910
2431
|
if (headerIconsRef.current) headerIconsRef.current.style.transform = `translateY(${fixedHeader ? 0 : -element.scrollTop}px)`;
|
|
2432
|
+
updateCustomScrollbars();
|
|
2433
|
+
if (scrollDeltaX !== 0 || scrollDeltaY !== 0) {
|
|
2434
|
+
showCustomScrollbar(scrollDeltaX !== 0 && scrollDeltaY !== 0 ? "both" : scrollDeltaX !== 0 ? "horizontal" : "vertical");
|
|
2435
|
+
}
|
|
1911
2436
|
if (hoveredHeaderActionRef.current) {
|
|
1912
2437
|
hoveredHeaderActionRef.current = null;
|
|
1913
2438
|
setHoveredHeaderAction(null);
|
|
@@ -1915,7 +2440,7 @@ function Table({
|
|
|
1915
2440
|
setHoveredCellTooltip(null);
|
|
1916
2441
|
scheduleDraw();
|
|
1917
2442
|
setEditing(null);
|
|
1918
|
-
}, [bodyTop, columns, renderHeight, fixedHeader, fixedWidth, getCellDisplayWidth, getCellSpan, getRowKey, metrics, rightFixedOffsets, rightFixedWidth, rowHeight, rows, scheduleDraw, selection, selectionRange, viewport.width]);
|
|
2443
|
+
}, [bodyTop, columns, renderHeight, fixedHeader, fixedWidth, getCellDisplayWidth, getCellSpan, getRowKey, getScrollableColumnLeft, metrics, rightFixedOffsets, rightFixedWidth, rowHeight, rows, scheduleDraw, selection, selectionRange, showCustomScrollbar, syncSelectionEdgeTarget, updateCustomScrollbars, viewport.width]);
|
|
1919
2444
|
const locateColumn = useCallback2((clientX) => {
|
|
1920
2445
|
const canvas = canvasRef.current;
|
|
1921
2446
|
if (!canvas) return -1;
|
|
@@ -1923,7 +2448,11 @@ function Table({
|
|
|
1923
2448
|
const localX = clientX - rect.left;
|
|
1924
2449
|
let columnIndex;
|
|
1925
2450
|
if (localX < fixedWidth) {
|
|
1926
|
-
columnIndex =
|
|
2451
|
+
columnIndex = columns.findIndex((column, index) => {
|
|
2452
|
+
if (column.fixed !== "left") return false;
|
|
2453
|
+
const left = leftFixedOffsets.get(index) ?? 0;
|
|
2454
|
+
return localX >= left && localX < left + metrics[index].width;
|
|
2455
|
+
});
|
|
1927
2456
|
} else if (rightFixedWidth > 0 && localX >= viewport.width - rightFixedWidth) {
|
|
1928
2457
|
columnIndex = columns.findIndex((column, index) => {
|
|
1929
2458
|
if (column.fixed !== "right") return false;
|
|
@@ -1931,41 +2460,33 @@ function Table({
|
|
|
1931
2460
|
return localX >= left && localX < left + metrics[index].width;
|
|
1932
2461
|
});
|
|
1933
2462
|
} else {
|
|
1934
|
-
|
|
2463
|
+
const scrollX = localX + scrollRef.current.left;
|
|
2464
|
+
columnIndex = columns.findIndex((column, index) => {
|
|
2465
|
+
if (column.fixed !== void 0) return false;
|
|
2466
|
+
const left = getScrollableColumnLeft(index);
|
|
2467
|
+
return scrollX >= left && scrollX < left + metrics[index].width;
|
|
2468
|
+
});
|
|
1935
2469
|
}
|
|
1936
2470
|
return columnIndex;
|
|
1937
|
-
}, [columns, fixedWidth, metrics, rightFixedOffsets, rightFixedWidth, viewport.width]);
|
|
2471
|
+
}, [columns, fixedWidth, getScrollableColumnLeft, leftFixedOffsets, metrics, rightFixedOffsets, rightFixedWidth, viewport.width]);
|
|
1938
2472
|
const getDisplayedColumnLeft = useCallback2((columnIndex) => {
|
|
1939
2473
|
const metric = metrics[columnIndex];
|
|
1940
|
-
if (columns[columnIndex].fixed === "left") return
|
|
2474
|
+
if (columns[columnIndex].fixed === "left") return leftFixedOffsets.get(columnIndex) ?? 0;
|
|
1941
2475
|
if (columns[columnIndex].fixed === "right") return viewport.width - (rightFixedOffsets.get(columnIndex) ?? 0) - metric.width;
|
|
1942
|
-
return
|
|
1943
|
-
}, [columns, metrics, rightFixedOffsets, viewport.width]);
|
|
1944
|
-
const
|
|
1945
|
-
|
|
1946
|
-
|
|
1947
|
-
|
|
1948
|
-
|
|
1949
|
-
for (let index = 0; index < columns.length; index += 1) {
|
|
1950
|
-
if (columns[index].rowSelection || columns[index].rowDragHandle || columns[index].rowNumber) continue;
|
|
1951
|
-
const nextDistance = Math.abs(localX - (getDisplayedColumnLeft(index) + metrics[index].width));
|
|
1952
|
-
if (nextDistance < distance) {
|
|
1953
|
-
distance = nextDistance;
|
|
1954
|
-
closest = index;
|
|
1955
|
-
}
|
|
1956
|
-
}
|
|
1957
|
-
return closest;
|
|
1958
|
-
}, [columnResizable, columns, getDisplayedColumnLeft, metrics]);
|
|
1959
|
-
const findResizeNeighbor = useCallback2((columnIndex) => {
|
|
1960
|
-
const resizedColumn = columns[columnIndex];
|
|
1961
|
-
for (let index = columnIndex + 1; index < columns.length; index += 1) {
|
|
1962
|
-
const column = columns[index];
|
|
1963
|
-
if (column.rowSelection || column.rowDragHandle || column.rowNumber) continue;
|
|
1964
|
-
if (resizedColumn?.fixed && column.fixed !== resizedColumn.fixed) return -1;
|
|
1965
|
-
return index;
|
|
2476
|
+
return getScrollableColumnLeft(columnIndex) - scrollRef.current.left;
|
|
2477
|
+
}, [columns, getScrollableColumnLeft, leftFixedOffsets, metrics, rightFixedOffsets, viewport.width]);
|
|
2478
|
+
const getHeaderLevelAtY = useCallback2((localY, headerY) => {
|
|
2479
|
+
const y = localY - headerY;
|
|
2480
|
+
for (let level = 0; level < headerRowHeights.length; level += 1) {
|
|
2481
|
+
const top = headerRowOffsets[level] ?? 0;
|
|
2482
|
+
if (y >= top && y < top + headerRowHeights[level]) return level;
|
|
1966
2483
|
}
|
|
1967
|
-
return -1;
|
|
1968
|
-
}, [
|
|
2484
|
+
return headerDepth - 1;
|
|
2485
|
+
}, [headerDepth, headerRowHeights, headerRowOffsets]);
|
|
2486
|
+
const resizeColumn = useCallback2((columnKey, width2) => {
|
|
2487
|
+
setResizedColumnWidths((current) => current[columnKey] === width2 ? current : { ...current, [columnKey]: width2 });
|
|
2488
|
+
onColumnResize?.(columnKey, width2);
|
|
2489
|
+
}, [onColumnResize]);
|
|
1969
2490
|
const locateHeaderAction = useCallback2((clientX, columnIndex) => {
|
|
1970
2491
|
if (columnIndex < 0 || !canvasRef.current) return null;
|
|
1971
2492
|
const column = columns[columnIndex];
|
|
@@ -2000,11 +2521,97 @@ function Table({
|
|
|
2000
2521
|
const columnRight = getDisplayedColumnLeft(columnIndex) + metrics[columnIndex].width;
|
|
2001
2522
|
return localX >= columnRight - 16 && localX < columnRight - 5;
|
|
2002
2523
|
}, [columnDraggable, columns, getDisplayedColumnLeft, measureHeaderTitleWidth, metrics]);
|
|
2003
|
-
const
|
|
2524
|
+
const getHeaderCellLeft = useCallback2((cell) => {
|
|
2525
|
+
const startMetric = metrics[cell.startIndex];
|
|
2526
|
+
const fixedSide = columns[cell.startIndex]?.fixed;
|
|
2527
|
+
if (!startMetric) return 0;
|
|
2528
|
+
return fixedSide === "left" ? leftFixedOffsets.get(cell.startIndex) ?? 0 : fixedSide === "right" ? viewport.width - (rightFixedOffsets.get(cell.startIndex) ?? 0) - startMetric.width : getScrollableColumnLeft(cell.startIndex) - scrollRef.current.left;
|
|
2529
|
+
}, [columns, getScrollableColumnLeft, leftFixedOffsets, metrics, rightFixedOffsets, viewport.width]);
|
|
2530
|
+
const getHeaderCellWidth = useCallback2((cell) => cell.leaf ? metrics[cell.startIndex]?.width ?? 0 : getCellDisplayWidth(cell.startIndex, cell.endIndex - cell.startIndex + 1), [getCellDisplayWidth, metrics]);
|
|
2531
|
+
const getHeaderCellEdgeFromMetrics = useCallback2((startIndex, endIndex, edge, nextMetrics) => {
|
|
2532
|
+
const fixedSide = columns[startIndex]?.fixed;
|
|
2533
|
+
const width2 = nextMetrics.slice(startIndex, endIndex + 1).reduce((total, metric) => total + metric.width, 0);
|
|
2534
|
+
let left;
|
|
2535
|
+
if (fixedSide === "left") {
|
|
2536
|
+
left = 0;
|
|
2537
|
+
for (let index = 0; index < startIndex; index += 1) {
|
|
2538
|
+
if (columns[index].fixed === "left") left += nextMetrics[index]?.width ?? 0;
|
|
2539
|
+
}
|
|
2540
|
+
} else if (fixedSide === "right") {
|
|
2541
|
+
let rightOffset = 0;
|
|
2542
|
+
for (let index = columns.length - 1; index > startIndex; index -= 1) {
|
|
2543
|
+
if (columns[index].fixed === "right") rightOffset += nextMetrics[index]?.width ?? 0;
|
|
2544
|
+
}
|
|
2545
|
+
left = viewport.width - rightOffset - width2;
|
|
2546
|
+
} else {
|
|
2547
|
+
left = 0;
|
|
2548
|
+
for (let index = 0; index < startIndex; index += 1) {
|
|
2549
|
+
if (columns[index].fixed === void 0) left += nextMetrics[index]?.width ?? 0;
|
|
2550
|
+
}
|
|
2551
|
+
left += fixedWidth - scrollRef.current.left;
|
|
2552
|
+
}
|
|
2553
|
+
return left + (edge === "right" ? width2 : 0);
|
|
2554
|
+
}, [columns, fixedWidth, viewport.width]);
|
|
2555
|
+
const locateHeaderResizeHit = useCallback2((clientX, clientY) => {
|
|
2556
|
+
if (!columnResizable || !canvasRef.current) return null;
|
|
2557
|
+
const rect = canvasRef.current.getBoundingClientRect();
|
|
2558
|
+
const localX = clientX - rect.left;
|
|
2559
|
+
const localY = clientY - rect.top;
|
|
2560
|
+
const headerY = fixedHeader ? 0 : -scrollRef.current.top;
|
|
2561
|
+
if (localY < headerY || localY >= headerY + headerHeight) return null;
|
|
2562
|
+
const level = getHeaderLevelAtY(localY, headerY);
|
|
2563
|
+
let closest = null;
|
|
2564
|
+
let distance = 6;
|
|
2565
|
+
let priority = -1;
|
|
2566
|
+
for (const cell of headerCells) {
|
|
2567
|
+
if (cell.level !== level) continue;
|
|
2568
|
+
if (columns[cell.startIndex]?.rowSelection || columns[cell.startIndex]?.rowDragHandle || columns[cell.startIndex]?.rowNumber) continue;
|
|
2569
|
+
const fixedSide = columns[cell.startIndex]?.fixed;
|
|
2570
|
+
const edge = fixedSide === "right" && cell.leaf ? "left" : "right";
|
|
2571
|
+
const edgeX = getHeaderCellLeft(cell) + (edge === "right" ? getHeaderCellWidth(cell) : 0);
|
|
2572
|
+
const nextDistance = Math.abs(localX - edgeX);
|
|
2573
|
+
const nextPriority = fixedSide === "right" ? 2 : fixedSide === "left" ? 1 : 0;
|
|
2574
|
+
if (nextDistance < distance || nextDistance === distance && nextPriority > priority) {
|
|
2575
|
+
distance = nextDistance;
|
|
2576
|
+
priority = nextPriority;
|
|
2577
|
+
closest = { cell, edge };
|
|
2578
|
+
}
|
|
2579
|
+
}
|
|
2580
|
+
return closest;
|
|
2581
|
+
}, [columnResizable, columns, fixedHeader, getHeaderCellLeft, getHeaderCellWidth, getHeaderLevelAtY, headerCells, headerHeight]);
|
|
2582
|
+
const locateHeaderResizeCell = useCallback2((clientX, clientY) => locateHeaderResizeHit(clientX, clientY)?.cell ?? null, [locateHeaderResizeHit]);
|
|
2583
|
+
const locateHeaderCellDragHandle = useCallback2((clientX, cell) => {
|
|
2584
|
+
if (!columnDraggable || !canvasRef.current) return false;
|
|
2585
|
+
if (cell.leaf) return locateHeaderDragHandle(clientX, cell.startIndex);
|
|
2586
|
+
const localX = clientX - canvasRef.current.getBoundingClientRect().left;
|
|
2587
|
+
const right = getHeaderCellLeft(cell) + getHeaderCellWidth(cell);
|
|
2588
|
+
return localX >= right - 16 && localX < right - 5;
|
|
2589
|
+
}, [columnDraggable, getHeaderCellLeft, getHeaderCellWidth, locateHeaderDragHandle]);
|
|
2590
|
+
const locateHeaderCell = useCallback2((clientX, clientY) => {
|
|
2591
|
+
const canvas = canvasRef.current;
|
|
2592
|
+
if (!canvas) return null;
|
|
2593
|
+
const rect = canvas.getBoundingClientRect();
|
|
2594
|
+
const headerY = fixedHeader ? 0 : -scrollRef.current.top;
|
|
2595
|
+
const localX = clientX - rect.left;
|
|
2596
|
+
const localY = clientY - rect.top;
|
|
2597
|
+
if (localY < headerY || localY >= headerY + headerHeight) return null;
|
|
2598
|
+
const level = getHeaderLevelAtY(localY, headerY);
|
|
2599
|
+
for (const cell of headerCells) {
|
|
2600
|
+
if (cell.level !== level) continue;
|
|
2601
|
+
const left = getHeaderCellLeft(cell);
|
|
2602
|
+
const width2 = getHeaderCellWidth(cell);
|
|
2603
|
+
if (localX >= left && localX < left + width2) return cell;
|
|
2604
|
+
}
|
|
2605
|
+
return null;
|
|
2606
|
+
}, [fixedHeader, getHeaderCellLeft, getHeaderCellWidth, getHeaderLevelAtY, headerCells, headerHeight]);
|
|
2607
|
+
const setDragGuide = useCallback2((x, top = 0) => {
|
|
2004
2608
|
const guide = dragGuideRef.current;
|
|
2005
2609
|
if (!guide) return;
|
|
2006
2610
|
guide.style.display = x === null ? "none" : "block";
|
|
2007
|
-
if (x !== null)
|
|
2611
|
+
if (x !== null) {
|
|
2612
|
+
guide.style.top = `${top}px`;
|
|
2613
|
+
guide.style.transform = `translateX(${Math.round(x)}px)`;
|
|
2614
|
+
}
|
|
2008
2615
|
}, []);
|
|
2009
2616
|
const setRowDragGuide = useCallback2((y) => {
|
|
2010
2617
|
const guide = rowDragGuideRef.current;
|
|
@@ -2022,6 +2629,15 @@ function Table({
|
|
|
2022
2629
|
useEffect2(() => {
|
|
2023
2630
|
const canvas = canvasRef.current;
|
|
2024
2631
|
if (!canvas || !columnDraggable && !rowDraggable) return;
|
|
2632
|
+
const getColumnDropIndex = (sourceIndex, targetIndex, edge) => {
|
|
2633
|
+
if (edge === "right") return targetIndex;
|
|
2634
|
+
const destinationIndex = targetIndex - (sourceIndex < targetIndex ? 1 : 0);
|
|
2635
|
+
return Math.max(0, Math.min(columns.length - 1, destinationIndex));
|
|
2636
|
+
};
|
|
2637
|
+
const getColumnDropPlacement = (sourceIndex, targetIndex, edge) => {
|
|
2638
|
+
if (edge !== "right") return "before";
|
|
2639
|
+
return targetIndex < sourceIndex ? "before" : "after";
|
|
2640
|
+
};
|
|
2025
2641
|
const getHeaderColumn = (clientX, clientY) => {
|
|
2026
2642
|
const rect = canvas.getBoundingClientRect();
|
|
2027
2643
|
const headerY = fixedHeader ? 0 : -scrollRef.current.top;
|
|
@@ -2029,22 +2645,35 @@ function Table({
|
|
|
2029
2645
|
if (localY < headerY || localY >= headerY + headerHeight) return -1;
|
|
2030
2646
|
return locateColumn(clientX);
|
|
2031
2647
|
};
|
|
2648
|
+
const getHeaderDragCell = (clientX, clientY) => {
|
|
2649
|
+
const cell = locateHeaderCell(clientX, clientY);
|
|
2650
|
+
if (!cell) return null;
|
|
2651
|
+
return locateHeaderCellDragHandle(clientX, cell) && !locateHeaderResizeCell(clientX, clientY) ? cell : null;
|
|
2652
|
+
};
|
|
2032
2653
|
const cleanupDrag = draggable({
|
|
2033
2654
|
element: canvas,
|
|
2034
2655
|
canDrag: ({ input }) => {
|
|
2035
|
-
const
|
|
2036
|
-
if (
|
|
2656
|
+
const headerDragCell = columnDraggable ? getHeaderDragCell(input.clientX, input.clientY) : null;
|
|
2657
|
+
if (headerDragCell) return true;
|
|
2037
2658
|
const columnIndex = locateColumn(input.clientX);
|
|
2038
2659
|
return rowDraggable && columnIndex >= 0 && Boolean(columns[columnIndex].rowDragHandle) && locateRow(input.clientY) >= 0;
|
|
2039
2660
|
},
|
|
2040
2661
|
getInitialData: ({ input }) => {
|
|
2041
|
-
const
|
|
2042
|
-
if (
|
|
2662
|
+
const headerDragCell = columnDraggable ? getHeaderDragCell(input.clientX, input.clientY) : null;
|
|
2663
|
+
if (!headerDragCell) {
|
|
2043
2664
|
return { type: "table-row", sourceIndex: locateRow(input.clientY) };
|
|
2044
2665
|
}
|
|
2045
|
-
const sourceIndex =
|
|
2666
|
+
const sourceIndex = headerDragCell.startIndex;
|
|
2046
2667
|
columnDragPreviewRef.current = { sourceIndex, targetIndex: sourceIndex };
|
|
2047
|
-
return {
|
|
2668
|
+
return {
|
|
2669
|
+
type: "table-column",
|
|
2670
|
+
sourceIndex,
|
|
2671
|
+
sourceKey: headerDragCell.key,
|
|
2672
|
+
parentKey: headerDragCell.parentKey,
|
|
2673
|
+
headerLevel: headerDragCell.level,
|
|
2674
|
+
headerKind: headerDragCell.leaf ? "column" : "group",
|
|
2675
|
+
fixed: columns[headerDragCell.startIndex]?.fixed
|
|
2676
|
+
};
|
|
2048
2677
|
},
|
|
2049
2678
|
onGenerateDragPreview: ({ nativeSetDragImage, source }) => {
|
|
2050
2679
|
const sourceIndex = Number(source.data.sourceIndex);
|
|
@@ -2147,31 +2776,35 @@ function Table({
|
|
|
2147
2776
|
return;
|
|
2148
2777
|
}
|
|
2149
2778
|
const targetIndex = Number(target?.targetIndex);
|
|
2150
|
-
if (!Number.isInteger(targetIndex) ||
|
|
2779
|
+
if (!Number.isInteger(targetIndex) || target?.headerKind !== source.data.headerKind || target?.parentKey !== source.data.parentKey || target?.fixed !== source.data.fixed) {
|
|
2151
2780
|
setDragGuide(null);
|
|
2152
2781
|
return;
|
|
2153
2782
|
}
|
|
2154
2783
|
const edge = target?.columnEdge;
|
|
2155
|
-
|
|
2156
|
-
if (sourceIndex < destinationIndex) destinationIndex -= 1;
|
|
2157
|
-
destinationIndex = Math.max(0, Math.min(columns.length - 1, destinationIndex));
|
|
2784
|
+
const destinationIndex = getColumnDropIndex(sourceIndex, targetIndex, edge);
|
|
2158
2785
|
columnDragPreviewRef.current = { sourceIndex, targetIndex: destinationIndex };
|
|
2159
|
-
const targetLeft =
|
|
2160
|
-
|
|
2786
|
+
const targetLeft = Number(target?.targetLeft);
|
|
2787
|
+
const targetWidth = Number(target?.targetWidth);
|
|
2788
|
+
if (!Number.isFinite(targetLeft) || !Number.isFinite(targetWidth)) {
|
|
2789
|
+
setDragGuide(null);
|
|
2790
|
+
return;
|
|
2791
|
+
}
|
|
2792
|
+
const guideTop = headerRowOffsets[Number(source.data.headerLevel)] ?? 0;
|
|
2793
|
+
setDragGuide(edge === "right" ? targetLeft + targetWidth : targetLeft, guideTop);
|
|
2161
2794
|
},
|
|
2162
2795
|
onDrop: ({ source, location }) => {
|
|
2163
2796
|
const target = location.current.dropTargets[0]?.data;
|
|
2164
2797
|
const sourceIndex = Number(source.data.sourceIndex);
|
|
2165
2798
|
if (source.data.type === "table-row") {
|
|
2166
|
-
const
|
|
2167
|
-
let targetIndex2 =
|
|
2799
|
+
const rawTargetIndex2 = Number(target?.targetIndex);
|
|
2800
|
+
let targetIndex2 = rawTargetIndex2 + (target?.rowEdge === "bottom" ? 1 : 0);
|
|
2168
2801
|
if (sourceIndex < targetIndex2) targetIndex2 -= 1;
|
|
2169
2802
|
targetIndex2 = Math.max(0, Math.min(rows.length - 1, targetIndex2));
|
|
2170
2803
|
scrollerRef.current?.classList.add("rvg-row-drop-settling");
|
|
2171
2804
|
setRowDragPreview(null);
|
|
2172
2805
|
setRowDragGuide(null);
|
|
2173
2806
|
canvas.style.cursor = "default";
|
|
2174
|
-
if (Number.isInteger(
|
|
2807
|
+
if (Number.isInteger(rawTargetIndex2) && sourceIndex !== targetIndex2) onRowOrderChange?.(sourceIndex, targetIndex2);
|
|
2175
2808
|
requestAnimationFrame(() => {
|
|
2176
2809
|
requestAnimationFrame(() => scrollerRef.current?.classList.remove("rvg-row-drop-settling"));
|
|
2177
2810
|
});
|
|
@@ -2180,12 +2813,23 @@ function Table({
|
|
|
2180
2813
|
}, 0);
|
|
2181
2814
|
return;
|
|
2182
2815
|
}
|
|
2183
|
-
const
|
|
2816
|
+
const rawTargetIndex = Number(target?.targetIndex);
|
|
2817
|
+
const targetIndex = columnDragPreviewRef.current?.targetIndex ?? (Number.isInteger(rawTargetIndex) ? getColumnDropIndex(sourceIndex, rawTargetIndex, target?.columnEdge) : rawTargetIndex);
|
|
2184
2818
|
canvas.style.cursor = "default";
|
|
2185
2819
|
setDragGuide(null);
|
|
2186
2820
|
columnDragPreviewRef.current = null;
|
|
2187
|
-
if (Number.isInteger(targetIndex) && sourceIndex !== targetIndex &&
|
|
2188
|
-
onColumnOrderChange?.(
|
|
2821
|
+
if (Number.isInteger(rawTargetIndex) && Number.isInteger(targetIndex) && sourceIndex !== targetIndex && target?.headerKind === source.data.headerKind && target?.parentKey === source.data.parentKey && target?.fixed === source.data.fixed) {
|
|
2822
|
+
onColumnOrderChange?.(
|
|
2823
|
+
sourceIndex - utilityColumnCount,
|
|
2824
|
+
targetIndex - utilityColumnCount,
|
|
2825
|
+
{
|
|
2826
|
+
type: source.data.headerKind === "group" ? "group" : "column",
|
|
2827
|
+
sourceKey: String(source.data.sourceKey),
|
|
2828
|
+
targetKey: String(target.targetKey),
|
|
2829
|
+
parentKey: typeof source.data.parentKey === "string" ? source.data.parentKey : void 0,
|
|
2830
|
+
placement: getColumnDropPlacement(sourceIndex, rawTargetIndex, target.columnEdge)
|
|
2831
|
+
}
|
|
2832
|
+
);
|
|
2189
2833
|
}
|
|
2190
2834
|
window.setTimeout(() => {
|
|
2191
2835
|
suppressClickRef.current = false;
|
|
@@ -2203,24 +2847,38 @@ function Table({
|
|
|
2203
2847
|
const rowEdge = input.clientY - canvas.getBoundingClientRect().top < rowTop + rowHeight / 2 ? "top" : "bottom";
|
|
2204
2848
|
return attachClosestEdge({ targetIndex: targetIndex2, rowEdge }, { element, input, allowedEdges: ["top", "bottom"] });
|
|
2205
2849
|
}
|
|
2206
|
-
|
|
2207
|
-
if (
|
|
2850
|
+
const targetCell = locateHeaderCell(input.clientX, input.clientY);
|
|
2851
|
+
if (!targetCell) return { targetIndex: -1 };
|
|
2852
|
+
let targetIndex = targetCell.startIndex;
|
|
2853
|
+
const targetFixed = columns[targetCell.startIndex]?.fixed;
|
|
2854
|
+
const headerKind = targetCell.leaf ? "column" : "group";
|
|
2855
|
+
if (source.data.headerKind !== headerKind || source.data.parentKey !== targetCell.parentKey || source.data.fixed !== targetFixed) return { targetIndex: -1 };
|
|
2208
2856
|
if (columns[targetIndex].rowSelection || columns[targetIndex].rowDragHandle || columns[targetIndex].rowNumber) {
|
|
2209
2857
|
const fixedSide = columns[Number(source.data.sourceIndex)]?.fixed;
|
|
2210
2858
|
targetIndex = columns.findIndex((column, index) => index > targetIndex && column.fixed === fixedSide && !column.rowSelection && !column.rowDragHandle && !column.rowNumber);
|
|
2211
2859
|
if (targetIndex < 0) return { targetIndex: -1 };
|
|
2212
2860
|
return { targetIndex, columnEdge: "left" };
|
|
2213
2861
|
}
|
|
2214
|
-
const left =
|
|
2215
|
-
const
|
|
2216
|
-
|
|
2862
|
+
const left = getHeaderCellLeft(targetCell);
|
|
2863
|
+
const targetWidth = targetCell.leaf ? metrics[targetIndex].width : getHeaderCellWidth(targetCell);
|
|
2864
|
+
const columnEdge = input.clientX - canvas.getBoundingClientRect().left < left + targetWidth / 2 ? "left" : "right";
|
|
2865
|
+
return attachClosestEdge({
|
|
2866
|
+
targetIndex,
|
|
2867
|
+
targetKey: targetCell.key,
|
|
2868
|
+
parentKey: targetCell.parentKey,
|
|
2869
|
+
headerKind,
|
|
2870
|
+
fixed: targetFixed,
|
|
2871
|
+
targetLeft: left,
|
|
2872
|
+
targetWidth,
|
|
2873
|
+
columnEdge
|
|
2874
|
+
}, { element, input, allowedEdges: ["left", "right"] });
|
|
2217
2875
|
}
|
|
2218
2876
|
});
|
|
2219
2877
|
return () => {
|
|
2220
2878
|
cleanupDrag();
|
|
2221
2879
|
cleanupDrop();
|
|
2222
2880
|
};
|
|
2223
|
-
}, [bodyTop, columnDraggable, columns, fixedHeader, getCellLabel, getDisplayedColumnLeft, getRowKey, headerHeight, locateColumn,
|
|
2881
|
+
}, [bodyTop, columnDraggable, columns, fixedHeader, getCellLabel, getDisplayedColumnLeft, getHeaderCellWidth, getRowKey, headerHeight, locateColumn, locateHeaderCell, locateHeaderCellDragHandle, locateHeaderResizeCell, locateRow, metrics, onColumnOrderChange, onRowOrderChange, rowDraggable, rowHeight, rows, selectedRowKeySet, setDragGuide, setRowDragGuide, utilityColumnCount]);
|
|
2224
2882
|
const locateCell = useCallback2((clientX, clientY) => {
|
|
2225
2883
|
const canvas = canvasRef.current;
|
|
2226
2884
|
if (!canvas) return null;
|
|
@@ -2327,8 +2985,9 @@ function Table({
|
|
|
2327
2985
|
const horizontalFocusGap = 0;
|
|
2328
2986
|
const verticalFocusGap = 0;
|
|
2329
2987
|
if (column.fixed === void 0) {
|
|
2330
|
-
|
|
2331
|
-
if (
|
|
2988
|
+
const cellLeft = getScrollableColumnLeft(cell.columnIndex);
|
|
2989
|
+
if (cellLeft - nextLeft < fixedWidth) nextLeft = Math.max(0, cellLeft - fixedWidth - horizontalFocusGap);
|
|
2990
|
+
if (cellLeft + cellWidth - nextLeft > viewport.width - rightFixedWidth) nextLeft = cellLeft + cellWidth - viewport.width + rightFixedWidth + horizontalFocusGap;
|
|
2332
2991
|
}
|
|
2333
2992
|
const rowTop = (fixedHeader ? 0 : bodyTop) + cell.rowIndex * rowHeight;
|
|
2334
2993
|
const rowBottom = rowTop + rowHeight * rowSpan;
|
|
@@ -2341,7 +3000,7 @@ function Table({
|
|
|
2341
3000
|
scroller.scrollTop = nextTop;
|
|
2342
3001
|
scrollRef.current = { left: scroller.scrollLeft, top: scroller.scrollTop };
|
|
2343
3002
|
scheduleDraw();
|
|
2344
|
-
}, [bodyTop, columns, fixedHeader, fixedWidth, getCellDisplayWidth, getCellSpan, metrics, rightFixedWidth, rowHeight, scheduleDraw, viewport.width]);
|
|
3003
|
+
}, [bodyTop, columns, fixedHeader, fixedWidth, getCellDisplayWidth, getCellSpan, getScrollableColumnLeft, metrics, rightFixedWidth, rowHeight, scheduleDraw, viewport.width]);
|
|
2345
3004
|
const beginEdit = useCallback2((cell) => {
|
|
2346
3005
|
const column = columns[cell.columnIndex];
|
|
2347
3006
|
if (!column?.editable || column.dataIndex === void 0) return;
|
|
@@ -2362,8 +3021,9 @@ function Table({
|
|
|
2362
3021
|
const horizontalFocusGap = 0;
|
|
2363
3022
|
const verticalFocusGap = 0;
|
|
2364
3023
|
if (column.fixed === void 0) {
|
|
2365
|
-
|
|
2366
|
-
if (
|
|
3024
|
+
const cellLeft = getScrollableColumnLeft(cell.columnIndex);
|
|
3025
|
+
if (cellLeft - nextLeft < fixedWidth) nextLeft = Math.max(0, cellLeft - fixedWidth - horizontalFocusGap);
|
|
3026
|
+
if (cellLeft + cellWidth - nextLeft > viewport.width - rightFixedWidth) nextLeft = cellLeft + cellWidth - viewport.width + rightFixedWidth + horizontalFocusGap;
|
|
2367
3027
|
}
|
|
2368
3028
|
const rowTop = (fixedHeader ? 0 : bodyTop) + cell.rowIndex * rowHeight;
|
|
2369
3029
|
const rowBottom = rowTop + rowHeight * rowSpan;
|
|
@@ -2385,7 +3045,7 @@ function Table({
|
|
|
2385
3045
|
return;
|
|
2386
3046
|
}
|
|
2387
3047
|
setEditing(cell);
|
|
2388
|
-
}, [columns, fixedHeader, fixedWidth, getCellDisplayWidth, getCellSpan, metrics, rightFixedWidth, rowHeight, rows, scheduleDraw, viewport.width]);
|
|
3048
|
+
}, [columns, fixedHeader, fixedWidth, getCellDisplayWidth, getCellSpan, getScrollableColumnLeft, metrics, rightFixedWidth, rowHeight, rows, scheduleDraw, viewport.width]);
|
|
2389
3049
|
const getCellEditKey = (cell) => `${typeof cell.rowKey}:${String(cell.rowKey)}\0${cell.columnKey}`;
|
|
2390
3050
|
const markCellEdited = (cell, previousValue, nextValue) => {
|
|
2391
3051
|
const key = getCellEditKey(cell);
|
|
@@ -2516,75 +3176,113 @@ function Table({
|
|
|
2516
3176
|
const visibleRowHeight = fixedHeader ? renderHeight - bodyTop : renderHeight;
|
|
2517
3177
|
if (rowTop < scroller.scrollTop) scroller.scrollTop = rowTop;
|
|
2518
3178
|
if (rowBottom > scroller.scrollTop + visibleRowHeight) scroller.scrollTop = rowBottom - visibleRowHeight;
|
|
2519
|
-
if (columns[columnIndex].fixed === void 0
|
|
2520
|
-
|
|
3179
|
+
if (columns[columnIndex].fixed === void 0) {
|
|
3180
|
+
const cellLeft = getScrollableColumnLeft(columnIndex);
|
|
3181
|
+
if (cellLeft < scroller.scrollLeft + fixedWidth) scroller.scrollLeft = Math.max(0, cellLeft - fixedWidth);
|
|
3182
|
+
if (cellLeft + cellWidth > scroller.scrollLeft + viewport.width - rightFixedWidth) scroller.scrollLeft = cellLeft + cellWidth - viewport.width + rightFixedWidth;
|
|
3183
|
+
}
|
|
2521
3184
|
}
|
|
2522
|
-
}, [beginEdit, bodyTop, columns, editing, renderHeight, fixedHeader, fixedWidth, getCellDisplayWidth, getCellSpan, getRowKey, metrics, rightFixedWidth, rowHeight, rows, selection, setSelection, viewport.width]);
|
|
3185
|
+
}, [beginEdit, bodyTop, columns, editing, renderHeight, fixedHeader, fixedWidth, getCellDisplayWidth, getCellSpan, getRowKey, getScrollableColumnLeft, metrics, rightFixedWidth, rowHeight, rows, selection, setSelection, viewport.width]);
|
|
2523
3186
|
const editorStyle = useMemo(() => {
|
|
2524
3187
|
if (!editing) return void 0;
|
|
2525
3188
|
const metric = metrics[editing.columnIndex];
|
|
2526
|
-
const
|
|
3189
|
+
const column = columns[editing.columnIndex];
|
|
3190
|
+
if (!metric || !column) return void 0;
|
|
3191
|
+
const fixedSide = column.fixed;
|
|
2527
3192
|
const span = getCellSpan(editing.rowIndex, editing.columnIndex);
|
|
2528
3193
|
const cellWidth = getCellDisplayWidth(editing.columnIndex, span?.colSpan ?? 1);
|
|
2529
3194
|
const cellHeight = rowHeight * (span?.rowSpan ?? 1);
|
|
2530
|
-
const cellLeft = fixedSide === "left" ?
|
|
3195
|
+
const cellLeft = fixedSide === "left" ? leftFixedOffsets.get(editing.columnIndex) ?? 0 : fixedSide === "right" ? viewport.width - (rightFixedOffsets.get(editing.columnIndex) ?? 0) - metric.width : getScrollableColumnLeft(editing.columnIndex) - scrollRef.current.left;
|
|
3196
|
+
const cellTop = bodyTop + editing.rowIndex * rowHeight - scrollRef.current.top;
|
|
3197
|
+
const horizontalStart = fixedSide === void 0 ? fixedWidth : 0;
|
|
3198
|
+
const horizontalEnd = fixedSide === void 0 ? viewport.width - rightFixedWidth : viewport.width;
|
|
3199
|
+
const verticalStart = fixedHeader ? bodyTop : 0;
|
|
3200
|
+
const visibleLeft = Math.max(cellLeft, horizontalStart);
|
|
3201
|
+
const visibleRight = Math.min(cellLeft + cellWidth, horizontalEnd, viewport.width);
|
|
3202
|
+
const visibleTop = Math.max(cellTop, verticalStart);
|
|
3203
|
+
const visibleBottom = Math.min(cellTop + cellHeight, renderHeight);
|
|
2531
3204
|
return {
|
|
2532
|
-
left:
|
|
2533
|
-
top:
|
|
2534
|
-
width:
|
|
2535
|
-
height:
|
|
3205
|
+
left: visibleLeft,
|
|
3206
|
+
top: visibleTop,
|
|
3207
|
+
width: Math.max(0, visibleRight - visibleLeft),
|
|
3208
|
+
height: Math.max(0, visibleBottom - visibleTop)
|
|
2536
3209
|
};
|
|
2537
|
-
}, [bodyTop, columns, editing, fixedHeader, fixedWidth, getCellDisplayWidth, getCellSpan, metrics, rightFixedOffsets, rowHeight, viewport.width]);
|
|
3210
|
+
}, [bodyTop, columns, editing, fixedHeader, fixedWidth, getCellDisplayWidth, getCellSpan, getScrollableColumnLeft, leftFixedOffsets, metrics, renderHeight, rightFixedOffsets, rightFixedWidth, rowHeight, viewport.width]);
|
|
2538
3211
|
const handleColumnPointerDown = useCallback2((event) => {
|
|
2539
3212
|
const rect = event.currentTarget.getBoundingClientRect();
|
|
2540
3213
|
const localY = event.clientY - rect.top;
|
|
2541
3214
|
const headerY = fixedHeader ? 0 : -scrollRef.current.top;
|
|
2542
3215
|
if (localY < headerY || localY >= headerY + headerHeight) return;
|
|
2543
|
-
const
|
|
2544
|
-
if (
|
|
3216
|
+
const resizeHit = locateHeaderResizeHit(event.clientX, event.clientY);
|
|
3217
|
+
if (resizeHit) {
|
|
3218
|
+
const { cell: resizeCell, edge: resizeEdge } = resizeHit;
|
|
2545
3219
|
event.preventDefault();
|
|
2546
3220
|
event.currentTarget.setPointerCapture(event.pointerId);
|
|
2547
|
-
const
|
|
2548
|
-
const
|
|
3221
|
+
const resizeIndex = resizeCell.endIndex;
|
|
3222
|
+
const resizeIndices = resizeCell.leaf ? [resizeIndex] : Array.from({ length: resizeCell.endIndex - resizeCell.startIndex + 1 }, (_, index) => resizeCell.startIndex + index);
|
|
3223
|
+
const startWidths = resizeIndices.map((index) => metrics[index].width);
|
|
3224
|
+
const startGuideX = getHeaderCellLeft(resizeCell) + (resizeEdge === "right" ? getHeaderCellWidth(resizeCell) : 0);
|
|
2549
3225
|
columnDragRef.current = {
|
|
2550
3226
|
type: "resize",
|
|
2551
3227
|
columnIndex: resizeIndex,
|
|
2552
3228
|
startX: event.clientX,
|
|
2553
|
-
startWidth:
|
|
2554
|
-
|
|
2555
|
-
|
|
2556
|
-
|
|
3229
|
+
startWidth: startWidths.reduce((total, width2) => total + width2, 0),
|
|
3230
|
+
guideTop: headerRowOffsets[resizeCell.level] ?? 0,
|
|
3231
|
+
resizeEdge,
|
|
3232
|
+
resizeStartIndex: resizeCell.startIndex,
|
|
3233
|
+
resizeEndIndex: resizeCell.endIndex,
|
|
3234
|
+
resizeIndices,
|
|
3235
|
+
startWidths
|
|
2557
3236
|
};
|
|
2558
3237
|
suppressClickRef.current = true;
|
|
3238
|
+
setResizeGuideTop(headerRowOffsets[resizeCell.level] ?? 0);
|
|
2559
3239
|
setResizeGuideX(startGuideX);
|
|
2560
3240
|
return;
|
|
2561
3241
|
}
|
|
2562
|
-
}, [
|
|
3242
|
+
}, [fixedHeader, getHeaderCellLeft, getHeaderCellWidth, headerHeight, headerRowOffsets, locateHeaderResizeHit, metrics]);
|
|
2563
3243
|
const handleColumnPointerMove = useCallback2((event) => {
|
|
2564
3244
|
const drag = columnDragRef.current;
|
|
2565
3245
|
if (!drag) return;
|
|
2566
3246
|
const column = columns[drag.columnIndex];
|
|
2567
3247
|
const minWidth = getMinimumColumnWidth(column, columnDraggable);
|
|
2568
|
-
|
|
2569
|
-
|
|
2570
|
-
|
|
2571
|
-
const
|
|
2572
|
-
|
|
2573
|
-
|
|
2574
|
-
|
|
2575
|
-
|
|
3248
|
+
const pointerDelta = Math.round(event.clientX - drag.startX);
|
|
3249
|
+
const delta = drag.resizeEdge === "left" ? -pointerDelta : pointerDelta;
|
|
3250
|
+
const setGuideFromWidths = (nextWidths) => {
|
|
3251
|
+
const widthByIndex = new Map(drag.resizeIndices.map((columnIndex, index) => [columnIndex, nextWidths[index]]));
|
|
3252
|
+
const nextColumns = columns.map((column2, index) => widthByIndex.has(index) ? { ...column2, width: widthByIndex.get(index) } : column2);
|
|
3253
|
+
const nextMetrics = buildColumnMetrics(nextColumns, { columnDraggable, viewportWidth: viewport.width });
|
|
3254
|
+
setResizeGuideX(getHeaderCellEdgeFromMetrics(drag.resizeStartIndex, drag.resizeEndIndex, drag.resizeEdge, nextMetrics));
|
|
3255
|
+
};
|
|
3256
|
+
if (drag.resizeIndices.length > 1) {
|
|
3257
|
+
const minWidths = drag.resizeIndices.map((index) => getMinimumColumnWidth(columns[index], columnDraggable));
|
|
3258
|
+
const minTotalWidth = minWidths.reduce((total, width3) => total + width3, 0);
|
|
3259
|
+
const targetTotalWidth = Math.max(minTotalWidth, drag.startWidth + delta);
|
|
3260
|
+
const ratio = drag.startWidth > 0 ? targetTotalWidth / drag.startWidth : 1;
|
|
3261
|
+
const nextWidths = drag.startWidths.map((width3, index) => Math.max(minWidths[index], Math.round(width3 * ratio)));
|
|
3262
|
+
let remainder = targetTotalWidth - nextWidths.reduce((total, width3) => total + width3, 0);
|
|
3263
|
+
for (let index = nextWidths.length - 1; index >= 0 && remainder !== 0; index -= 1) {
|
|
3264
|
+
const next = nextWidths[index] + (remainder > 0 ? 1 : -1);
|
|
3265
|
+
if (next < minWidths[index]) continue;
|
|
3266
|
+
nextWidths[index] = next;
|
|
3267
|
+
remainder += remainder > 0 ? -1 : 1;
|
|
3268
|
+
}
|
|
3269
|
+
drag.resizeIndices.forEach((columnIndex, index) => {
|
|
3270
|
+
resizeColumn(columns[columnIndex].key, nextWidths[index]);
|
|
3271
|
+
});
|
|
3272
|
+
setGuideFromWidths(nextWidths);
|
|
2576
3273
|
return;
|
|
2577
3274
|
}
|
|
2578
3275
|
const width2 = Math.max(minWidth, Math.round(drag.startWidth + delta));
|
|
2579
|
-
|
|
2580
|
-
|
|
2581
|
-
}, [columnDraggable, columns,
|
|
3276
|
+
resizeColumn(column.key, width2);
|
|
3277
|
+
setGuideFromWidths([width2]);
|
|
3278
|
+
}, [columnDraggable, columns, getHeaderCellEdgeFromMetrics, resizeColumn, viewport.width]);
|
|
2582
3279
|
const handleColumnPointerUp = useCallback2((event) => {
|
|
2583
3280
|
const drag = columnDragRef.current;
|
|
2584
3281
|
if (!drag) return;
|
|
2585
3282
|
if (event.currentTarget.hasPointerCapture(event.pointerId)) event.currentTarget.releasePointerCapture(event.pointerId);
|
|
2586
3283
|
columnDragRef.current = null;
|
|
2587
3284
|
setResizeGuideX(null);
|
|
3285
|
+
setResizeGuideTop(0);
|
|
2588
3286
|
}, []);
|
|
2589
3287
|
const queueSortStateChange = useCallback2((next) => {
|
|
2590
3288
|
setDisplaySortState(next);
|
|
@@ -2597,7 +3295,7 @@ function Table({
|
|
|
2597
3295
|
const renderHeaderIcons = (columnIndex, left) => {
|
|
2598
3296
|
const column = columns[columnIndex];
|
|
2599
3297
|
if (column.rowSelection || column.rowDragHandle || column.rowNumber) return null;
|
|
2600
|
-
const top = (
|
|
3298
|
+
const top = headerLeafTop + (headerLeafHeight - 14) / 2;
|
|
2601
3299
|
const visibleActions = getVisibleHeaderActions(column, metrics[columnIndex].width, columnDraggable, measureHeaderTitleWidth(columnIndex));
|
|
2602
3300
|
let right = left + metrics[columnIndex].width - (visibleActions.drag ? 18 : 4);
|
|
2603
3301
|
const icons = [];
|
|
@@ -2623,44 +3321,87 @@ function Table({
|
|
|
2623
3321
|
}
|
|
2624
3322
|
return icons;
|
|
2625
3323
|
};
|
|
2626
|
-
const
|
|
2627
|
-
|
|
2628
|
-
const { columnIndex, action } = visibleHeaderTooltip;
|
|
2629
|
-
const column = columns[columnIndex];
|
|
3324
|
+
const createHeaderTooltip = useCallback2((action, columnIndex, cell) => {
|
|
3325
|
+
const column = cell?.column ?? columns[columnIndex];
|
|
2630
3326
|
if (!column) return null;
|
|
2631
|
-
const
|
|
2632
|
-
const
|
|
2633
|
-
|
|
2634
|
-
|
|
2635
|
-
|
|
3327
|
+
const cellLeft = cell ? getHeaderCellLeft(cell) : getDisplayedColumnLeft(columnIndex);
|
|
3328
|
+
const cellWidth = cell ? getHeaderCellWidth(cell) : metrics[columnIndex]?.width ?? 0;
|
|
3329
|
+
const level = cell?.level ?? headerDepth - 1;
|
|
3330
|
+
const rowTop = (fixedHeader ? 0 : -scrollPosition.top) + (headerRowOffsets[level] ?? 0);
|
|
3331
|
+
const rowHeight2 = headerRowHeights[level] ?? baseHeaderHeight;
|
|
3332
|
+
let anchorX = cellLeft + cellWidth / 2;
|
|
3333
|
+
let anchorLeft = cellLeft;
|
|
3334
|
+
let label = column.title;
|
|
2636
3335
|
if (action === "title") {
|
|
2637
|
-
const
|
|
2638
|
-
const
|
|
2639
|
-
|
|
2640
|
-
const
|
|
2641
|
-
const
|
|
2642
|
-
|
|
2643
|
-
|
|
2644
|
-
|
|
3336
|
+
const canvas = canvasRef.current;
|
|
3337
|
+
const context = canvas?.getContext("2d");
|
|
3338
|
+
if (context) context.font = "600 13px Inter, ui-sans-serif, system-ui, sans-serif";
|
|
3339
|
+
const titleWidth = context?.measureText(column.title).width ?? column.title.length * 13;
|
|
3340
|
+
const visibleActions = cell?.leaf || !cell ? getVisibleHeaderActions(column, cellWidth, columnDraggable, titleWidth) : null;
|
|
3341
|
+
const actionWidth = visibleActions ? (Number(visibleActions.drag) + Number(visibleActions.filter) + Number(visibleActions.sort)) * 18 : columnDraggable ? 18 : 0;
|
|
3342
|
+
const contentWidth2 = Math.max(0, cellWidth - actionWidth);
|
|
3343
|
+
const maxTextWidth = Math.max(0, contentWidth2 - 20);
|
|
3344
|
+
const textWidth = Math.min(titleWidth, maxTextWidth);
|
|
3345
|
+
if (column.align === "right") {
|
|
3346
|
+
anchorX = cellLeft + contentWidth2 - 10;
|
|
3347
|
+
anchorLeft = anchorX - textWidth;
|
|
3348
|
+
} else if (column.align === "center") {
|
|
3349
|
+
anchorX = cellLeft + contentWidth2 / 2 + textWidth / 2;
|
|
3350
|
+
anchorLeft = cellLeft + contentWidth2 / 2 - textWidth / 2;
|
|
3351
|
+
} else {
|
|
3352
|
+
anchorLeft = cellLeft + 10;
|
|
3353
|
+
anchorX = anchorLeft + textWidth;
|
|
3354
|
+
}
|
|
2645
3355
|
} else if (action === "drag") {
|
|
2646
|
-
anchorX =
|
|
3356
|
+
anchorX = cellLeft + cellWidth;
|
|
2647
3357
|
anchorLeft = anchorX;
|
|
2648
3358
|
label = labels.dragColumn;
|
|
2649
3359
|
} else if (action === "sort") {
|
|
2650
3360
|
const direction = displaySortState?.columnKey === column.key ? displaySortState.direction : null;
|
|
2651
3361
|
label = direction === "asc" ? labels.sortAsc : direction === "desc" ? labels.sortDesc : labels.sortBoth;
|
|
2652
|
-
|
|
2653
|
-
|
|
3362
|
+
anchorX = cellLeft + cellWidth - 22;
|
|
3363
|
+
anchorLeft = anchorX;
|
|
3364
|
+
} else if (action === "filter") {
|
|
2654
3365
|
const value = filterValues[column.key];
|
|
2655
3366
|
label = value ? labels.filterWithValue(value) : labels.filter;
|
|
3367
|
+
anchorX = cellLeft + cellWidth - 40;
|
|
3368
|
+
anchorLeft = anchorX;
|
|
2656
3369
|
}
|
|
2657
3370
|
const placement = anchorX + 328 > viewport.width ? "left" : "right";
|
|
2658
3371
|
return {
|
|
3372
|
+
key: `${action}:${cell ? `cell:${cell.key}:${cell.level}` : `column:${column.key}`}:${Math.round(anchorX)}:${Math.round(rowTop)}`,
|
|
3373
|
+
columnIndex,
|
|
3374
|
+
action,
|
|
2659
3375
|
left: placement === "right" ? anchorX + 8 : Math.max(8, anchorLeft - 8),
|
|
2660
|
-
top:
|
|
3376
|
+
top: rowTop + rowHeight2 / 2,
|
|
2661
3377
|
label,
|
|
2662
3378
|
placement
|
|
2663
3379
|
};
|
|
3380
|
+
}, [baseHeaderHeight, columnDraggable, columns, displaySortState, filterValues, fixedHeader, getDisplayedColumnLeft, getHeaderCellLeft, getHeaderCellWidth, headerDepth, headerRowHeights, headerRowOffsets, labels, metrics, scrollPosition.top, viewport.width]);
|
|
3381
|
+
const isPointerOnHeaderTitle = useCallback2((clientX, clientY, cell) => {
|
|
3382
|
+
const canvas = canvasRef.current;
|
|
3383
|
+
if (!canvas) return false;
|
|
3384
|
+
const rect = canvas.getBoundingClientRect();
|
|
3385
|
+
const localX = clientX - rect.left;
|
|
3386
|
+
const localY = clientY - rect.top;
|
|
3387
|
+
const column = cell.column;
|
|
3388
|
+
const cellLeft = getHeaderCellLeft(cell);
|
|
3389
|
+
const cellWidth = getHeaderCellWidth(cell);
|
|
3390
|
+
const rowTop = (fixedHeader ? 0 : -scrollRef.current.top) + (headerRowOffsets[cell.level] ?? 0);
|
|
3391
|
+
const context = canvas.getContext("2d");
|
|
3392
|
+
if (context) context.font = "600 13px Inter, ui-sans-serif, system-ui, sans-serif";
|
|
3393
|
+
const titleWidth = context?.measureText(column.title).width ?? column.title.length * 13;
|
|
3394
|
+
const visibleActions = cell.leaf ? getVisibleHeaderActions(column, cellWidth, columnDraggable, titleWidth) : null;
|
|
3395
|
+
const actionWidth = visibleActions ? (Number(visibleActions.drag) + Number(visibleActions.filter) + Number(visibleActions.sort)) * 18 : columnDraggable ? 18 : 0;
|
|
3396
|
+
const contentWidth2 = Math.max(0, cellWidth - actionWidth);
|
|
3397
|
+
const textWidth = Math.min(titleWidth, Math.max(0, contentWidth2 - 20));
|
|
3398
|
+
const textLeft = column.align === "right" ? cellLeft + contentWidth2 - 10 - textWidth : column.align === "center" ? cellLeft + contentWidth2 / 2 - textWidth / 2 : cellLeft + 10;
|
|
3399
|
+
const textTop = rowTop + 8;
|
|
3400
|
+
return localX >= textLeft - 4 && localX <= textLeft + textWidth + 4 && localY >= textTop - 2 && localY <= textTop + 18;
|
|
3401
|
+
}, [columnDraggable, fixedHeader, getHeaderCellLeft, getHeaderCellWidth, headerRowOffsets]);
|
|
3402
|
+
const headerTooltip = (() => {
|
|
3403
|
+
if (!visibleHeaderTooltip) return null;
|
|
3404
|
+
return visibleHeaderTooltip;
|
|
2664
3405
|
})();
|
|
2665
3406
|
const getRangeBounds = () => selectionRange ? {
|
|
2666
3407
|
rowStart: Math.min(selectionRange.anchor.rowIndex, selectionRange.focus.rowIndex),
|
|
@@ -2719,7 +3460,7 @@ function Table({
|
|
|
2719
3460
|
}, 1800);
|
|
2720
3461
|
};
|
|
2721
3462
|
const showHeaderTooltipAfterDelay = (next) => {
|
|
2722
|
-
const nextKey = next ?
|
|
3463
|
+
const nextKey = next ? next.key : "";
|
|
2723
3464
|
if (nextKey && pendingHeaderTooltipKeyRef.current === nextKey) return;
|
|
2724
3465
|
pendingHeaderTooltipKeyRef.current = nextKey;
|
|
2725
3466
|
if (headerTooltipTimerRef.current !== null) clearTimeout(headerTooltipTimerRef.current);
|
|
@@ -2967,12 +3708,14 @@ function Table({
|
|
|
2967
3708
|
const renderHeaderTitle = (columnIndex, left) => {
|
|
2968
3709
|
const column = columns[columnIndex];
|
|
2969
3710
|
const currentScrollTop = scrollRef.current.top;
|
|
3711
|
+
const headerTop = fixedHeader ? 0 : -currentScrollTop;
|
|
3712
|
+
const leafTop = headerTop + headerLeafTop;
|
|
2970
3713
|
if (column.rowNumber) {
|
|
2971
3714
|
return /* @__PURE__ */ jsx7(
|
|
2972
3715
|
"div",
|
|
2973
3716
|
{
|
|
2974
3717
|
className: "rvg-header-title",
|
|
2975
|
-
style: { left, top:
|
|
3718
|
+
style: { left, top: headerTop, width: metrics[columnIndex].width, height: headerHeight, justifyContent: "center", padding: 0, textAlign: "center" },
|
|
2976
3719
|
children: /* @__PURE__ */ jsx7("span", { children: column.title })
|
|
2977
3720
|
},
|
|
2978
3721
|
column.key
|
|
@@ -2986,7 +3729,7 @@ function Table({
|
|
|
2986
3729
|
"div",
|
|
2987
3730
|
{
|
|
2988
3731
|
className: "rvg-header-selection-icon",
|
|
2989
|
-
style: { left, top:
|
|
3732
|
+
style: { left, top: headerTop, width: metrics[columnIndex].width, height: headerHeight },
|
|
2990
3733
|
children: /* @__PURE__ */ jsx7(SelectionIcon, { checked: allSelected, indeterminate })
|
|
2991
3734
|
},
|
|
2992
3735
|
column.key
|
|
@@ -2999,11 +3742,13 @@ function Table({
|
|
|
2999
3742
|
"div",
|
|
3000
3743
|
{
|
|
3001
3744
|
className: "rvg-header-title is-custom",
|
|
3745
|
+
"data-level": headerDepth - 1,
|
|
3746
|
+
"data-measure-key": column.key,
|
|
3002
3747
|
style: {
|
|
3003
3748
|
left,
|
|
3004
|
-
top:
|
|
3749
|
+
top: leafTop,
|
|
3005
3750
|
width: Math.max(0, metrics[columnIndex].width - actionWidth),
|
|
3006
|
-
height:
|
|
3751
|
+
height: headerLeafHeight,
|
|
3007
3752
|
justifyContent: column.align === "right" ? "flex-end" : column.align === "center" ? "center" : "flex-start",
|
|
3008
3753
|
textAlign: column.align === "right" ? "right" : column.align === "center" ? "center" : "left"
|
|
3009
3754
|
},
|
|
@@ -3011,8 +3756,10 @@ function Table({
|
|
|
3011
3756
|
"span",
|
|
3012
3757
|
{
|
|
3013
3758
|
ref: (node) => {
|
|
3014
|
-
if (node)
|
|
3015
|
-
|
|
3759
|
+
if (node) {
|
|
3760
|
+
customHeaderContentRefs.current.set(column.key, node);
|
|
3761
|
+
requestAnimationFrame(measureCustomHeaderHeight);
|
|
3762
|
+
} else customHeaderContentRefs.current.delete(column.key);
|
|
3016
3763
|
},
|
|
3017
3764
|
children: column.renderHeader(column)
|
|
3018
3765
|
}
|
|
@@ -3023,6 +3770,61 @@ function Table({
|
|
|
3023
3770
|
}
|
|
3024
3771
|
return null;
|
|
3025
3772
|
};
|
|
3773
|
+
const renderGroupedHeaderCell = (cell) => {
|
|
3774
|
+
const startMetric = metrics[cell.startIndex];
|
|
3775
|
+
const endMetric = metrics[cell.endIndex];
|
|
3776
|
+
if (!startMetric || !endMetric) return null;
|
|
3777
|
+
const column = cell.column;
|
|
3778
|
+
const fixedSide = columns[cell.startIndex]?.fixed;
|
|
3779
|
+
const currentScrollTop = scrollRef.current.top;
|
|
3780
|
+
const rowHeightForLevel = headerRowHeights[cell.level] ?? baseHeaderHeight;
|
|
3781
|
+
const headerTop = (fixedHeader ? 0 : -currentScrollTop) + (headerRowOffsets[cell.level] ?? 0);
|
|
3782
|
+
const left = fixedSide === "left" ? leftFixedOffsets.get(cell.startIndex) ?? 0 : fixedSide === "right" ? viewport.width - (rightFixedOffsets.get(cell.startIndex) ?? 0) - startMetric.width : getScrollableColumnLeft(cell.startIndex);
|
|
3783
|
+
const width2 = cell.leaf ? startMetric.width : getCellDisplayWidth(cell.startIndex, cell.endIndex - cell.startIndex + 1);
|
|
3784
|
+
const visibleActions = cell.leaf ? getVisibleHeaderActions(column, startMetric.width, columnDraggable, measureHeaderTitleWidth(cell.startIndex)) : null;
|
|
3785
|
+
const actionWidth = visibleActions ? (Number(visibleActions.drag) + Number(visibleActions.filter) + Number(visibleActions.sort)) * 18 : columnDraggable && !cell.leaf ? 18 : 0;
|
|
3786
|
+
return /* @__PURE__ */ jsxs7(
|
|
3787
|
+
"div",
|
|
3788
|
+
{
|
|
3789
|
+
className: `rvg-header-title rvg-header-group${column.renderHeader ? " is-custom" : ""}${cell.leaf ? " is-leaf" : ""}`,
|
|
3790
|
+
"data-level": cell.level,
|
|
3791
|
+
"data-measure-key": `group:${cell.key}:${cell.level}`,
|
|
3792
|
+
style: {
|
|
3793
|
+
left,
|
|
3794
|
+
top: headerTop,
|
|
3795
|
+
width: width2,
|
|
3796
|
+
height: rowHeightForLevel,
|
|
3797
|
+
paddingRight: cell.leaf ? actionWidth + 10 : 10,
|
|
3798
|
+
justifyContent: column.align === "right" ? "flex-end" : column.align === "center" ? "center" : "flex-start",
|
|
3799
|
+
textAlign: column.align === "right" ? "right" : column.align === "center" ? "center" : "left"
|
|
3800
|
+
},
|
|
3801
|
+
children: [
|
|
3802
|
+
/* @__PURE__ */ jsx7(
|
|
3803
|
+
"span",
|
|
3804
|
+
{
|
|
3805
|
+
ref: (node) => {
|
|
3806
|
+
if (!column.renderHeader) return;
|
|
3807
|
+
const key = `group:${cell.key}:${cell.level}`;
|
|
3808
|
+
if (node) {
|
|
3809
|
+
customHeaderContentRefs.current.set(key, node);
|
|
3810
|
+
requestAnimationFrame(measureCustomHeaderHeight);
|
|
3811
|
+
} else customHeaderContentRefs.current.delete(key);
|
|
3812
|
+
},
|
|
3813
|
+
children: column.renderHeader ? column.renderHeader(column) : column.title
|
|
3814
|
+
}
|
|
3815
|
+
),
|
|
3816
|
+
!cell.leaf && columnDraggable && /* @__PURE__ */ jsx7(
|
|
3817
|
+
HeaderDragIcon,
|
|
3818
|
+
{
|
|
3819
|
+
className: `rvg-header-icon rvg-header-drag-icon${hoveredHeaderAction?.columnIndex === cell.startIndex && hoveredHeaderAction.action === "drag" ? " is-hovered" : ""}`,
|
|
3820
|
+
style: { left: width2 - 16, top: (rowHeightForLevel - 14) / 2 }
|
|
3821
|
+
}
|
|
3822
|
+
)
|
|
3823
|
+
]
|
|
3824
|
+
},
|
|
3825
|
+
`group:${cell.key}:${cell.level}`
|
|
3826
|
+
);
|
|
3827
|
+
};
|
|
3026
3828
|
const rangeHandlePosition = (() => {
|
|
3027
3829
|
if (!rangeSelection) return null;
|
|
3028
3830
|
const bounds = getRangeBounds();
|
|
@@ -3054,9 +3856,9 @@ function Table({
|
|
|
3054
3856
|
const verticalStart = fixedHeader ? bodyTop : 0;
|
|
3055
3857
|
if (rawRight <= horizontalStart || rawLeft >= horizontalEnd || rawBottom <= verticalStart || rawTop >= renderHeight) return null;
|
|
3056
3858
|
const borderWidth = 2;
|
|
3057
|
-
const left = Math.round(Math.max(rawLeft - borderWidth, horizontalStart
|
|
3859
|
+
const left = Math.round(Math.max(rawLeft - borderWidth, horizontalStart));
|
|
3058
3860
|
const right = Math.round(Math.min(rawRight + 1, horizontalEnd, viewport.width));
|
|
3059
|
-
const top = Math.round(Math.max(rawTop - borderWidth, verticalStart
|
|
3861
|
+
const top = Math.round(Math.max(rawTop - borderWidth, verticalStart));
|
|
3060
3862
|
const bottom = Math.round(Math.min(rawBottom + 1, renderHeight));
|
|
3061
3863
|
return {
|
|
3062
3864
|
left,
|
|
@@ -3065,6 +3867,19 @@ function Table({
|
|
|
3065
3867
|
height: Math.max(0, bottom - top)
|
|
3066
3868
|
};
|
|
3067
3869
|
})();
|
|
3870
|
+
const selectionEdgeIndicator = getSelectionEdgeIndicator(scrollPosition.left, scrollPosition.top);
|
|
3871
|
+
const selectionEdgeStyle = selectionEdgeIndicator ? {
|
|
3872
|
+
...selectionEdgeIndicator.style,
|
|
3873
|
+
...selectionEdgeIndicator.corner ? {
|
|
3874
|
+
"--rvg-corner-width": `${selectionEdgeIndicator.corner.width}px`,
|
|
3875
|
+
"--rvg-corner-height": `${selectionEdgeIndicator.corner.height}px`,
|
|
3876
|
+
"--rvg-corner-hover-width-extension": "4px",
|
|
3877
|
+
"--rvg-corner-hover-height-extension": "4px"
|
|
3878
|
+
} : null
|
|
3879
|
+
} : void 0;
|
|
3880
|
+
useLayoutEffect2(() => {
|
|
3881
|
+
syncSelectionEdgeTarget(scrollPosition.left, scrollPosition.top);
|
|
3882
|
+
}, [scrollPosition.left, scrollPosition.top, syncSelectionEdgeTarget]);
|
|
3068
3883
|
const handleContextMenu = (event) => {
|
|
3069
3884
|
event.preventDefault();
|
|
3070
3885
|
event.stopPropagation();
|
|
@@ -3214,15 +4029,19 @@ function Table({
|
|
|
3214
4029
|
const headerY = fixedHeader ? 0 : -scrollRef.current.top;
|
|
3215
4030
|
const columnIndex = locateColumn(event.clientX);
|
|
3216
4031
|
const inHeader = localY >= headerY && localY < headerY + headerHeight;
|
|
3217
|
-
const
|
|
3218
|
-
const
|
|
3219
|
-
const
|
|
3220
|
-
const
|
|
4032
|
+
const inLeafHeader = inHeader && localY >= headerY + headerLeafTop;
|
|
4033
|
+
const hoveredHeaderCell = inHeader ? locateHeaderCell(event.clientX, event.clientY) : null;
|
|
4034
|
+
const hoveredAction = inLeafHeader ? locateHeaderAction(event.clientX, columnIndex) : null;
|
|
4035
|
+
const hoveredDragHandle = Boolean(hoveredHeaderCell && locateHeaderCellDragHandle(event.clientX, hoveredHeaderCell));
|
|
4036
|
+
const hoveredDragColumnIndex = hoveredHeaderCell?.startIndex ?? columnIndex;
|
|
4037
|
+
const hoveredTitle = inHeader && !hoveredAction && !hoveredDragHandle && hoveredHeaderCell && Boolean(hoveredHeaderCell.column.title) && isPointerOnHeaderTitle(event.clientX, event.clientY, hoveredHeaderCell) && (tooltipConfig.header || hoveredHeaderCell.leaf && isHeaderTitleTruncated(hoveredHeaderCell.startIndex));
|
|
4038
|
+
const nextHover = hoveredAction ? { columnIndex, action: hoveredAction } : hoveredDragHandle ? { columnIndex: hoveredDragColumnIndex, action: "drag" } : hoveredTitle ? { columnIndex: hoveredHeaderCell.startIndex, action: "title" } : null;
|
|
4039
|
+
const nextTooltip = hoveredAction && hoveredHeaderCell ? createHeaderTooltip(hoveredAction, columnIndex, hoveredHeaderCell) : hoveredDragHandle && hoveredHeaderCell ? createHeaderTooltip("drag", hoveredDragColumnIndex, hoveredHeaderCell) : hoveredTitle && hoveredHeaderCell ? createHeaderTooltip("title", hoveredHeaderCell.startIndex, hoveredHeaderCell) : null;
|
|
3221
4040
|
const previousHover = hoveredHeaderActionRef.current;
|
|
3222
4041
|
if (previousHover?.columnIndex !== nextHover?.columnIndex || previousHover?.action !== nextHover?.action) {
|
|
3223
4042
|
hoveredHeaderActionRef.current = nextHover;
|
|
3224
4043
|
setHoveredHeaderAction(nextHover);
|
|
3225
|
-
showHeaderTooltipAfterDelay(
|
|
4044
|
+
showHeaderTooltipAfterDelay(nextTooltip);
|
|
3226
4045
|
scheduleDraw();
|
|
3227
4046
|
}
|
|
3228
4047
|
if (inHeader) {
|
|
@@ -3246,8 +4065,8 @@ function Table({
|
|
|
3246
4065
|
if (annotation?.content.trim()) {
|
|
3247
4066
|
const cellLeft = getDisplayedColumnLeft(cell.columnIndex);
|
|
3248
4067
|
const cellTop = bodyTop + cell.rowIndex * rowHeight - scrollRef.current.top;
|
|
3249
|
-
const
|
|
3250
|
-
showCellTooltip(
|
|
4068
|
+
const nextTooltip2 = { rowIndex: cell.rowIndex, columnIndex: cell.columnIndex, left: cellLeft + metrics[cell.columnIndex].width + 6, top: cellTop + rowHeight / 2, label: annotation.content, color: annotation.color, annotation: true };
|
|
4069
|
+
showCellTooltip(nextTooltip2);
|
|
3251
4070
|
} else {
|
|
3252
4071
|
const label = getCellLabel(cell.rowIndex, cell.columnIndex);
|
|
3253
4072
|
const context = event.currentTarget.getContext("2d");
|
|
@@ -3274,7 +4093,7 @@ function Table({
|
|
|
3274
4093
|
showCellTooltipAfterDelay(null);
|
|
3275
4094
|
} else {
|
|
3276
4095
|
const placement = textRight + 330 > viewport.width ? "left" : "right";
|
|
3277
|
-
const
|
|
4096
|
+
const nextTooltip2 = {
|
|
3278
4097
|
rowIndex: cell.rowIndex,
|
|
3279
4098
|
columnIndex: cell.columnIndex,
|
|
3280
4099
|
left: placement === "right" ? textRight + 16 : Math.max(8, textLeft - 16),
|
|
@@ -3282,13 +4101,13 @@ function Table({
|
|
|
3282
4101
|
label,
|
|
3283
4102
|
placement
|
|
3284
4103
|
};
|
|
3285
|
-
showCellTooltipAfterDelay(
|
|
4104
|
+
showCellTooltipAfterDelay(nextTooltip2);
|
|
3286
4105
|
}
|
|
3287
4106
|
}
|
|
3288
4107
|
}
|
|
3289
4108
|
}
|
|
3290
4109
|
}
|
|
3291
|
-
const cursor = inHeader &&
|
|
4110
|
+
const cursor = inHeader && locateHeaderResizeCell(event.clientX, event.clientY) ? "col-resize" : hoveredAction ? "pointer" : hoveredDragHandle ? "move" : columnIndex >= 0 && columns[columnIndex].rowSelection ? "pointer" : !inHeader && rowDraggable && columnIndex >= 0 && columns[columnIndex].rowDragHandle ? "move" : "default";
|
|
3292
4111
|
if (event.currentTarget.style.cursor !== cursor) event.currentTarget.style.cursor = cursor;
|
|
3293
4112
|
},
|
|
3294
4113
|
onMouseLeave: (event) => {
|
|
@@ -3331,6 +4150,7 @@ function Table({
|
|
|
3331
4150
|
const headerY = fixedHeader ? 0 : -scrollRef.current.top;
|
|
3332
4151
|
if (localY >= headerY && localY < headerY + headerHeight) {
|
|
3333
4152
|
clickedCellRef.current = null;
|
|
4153
|
+
if (localY < headerY + headerLeafTop) return;
|
|
3334
4154
|
const columnIndex = locateColumn(event.clientX);
|
|
3335
4155
|
const action = locateHeaderAction(event.clientX, columnIndex);
|
|
3336
4156
|
if (action === "sort") {
|
|
@@ -3392,21 +4212,30 @@ function Table({
|
|
|
3392
4212
|
className: "rvg-text-scroll-content",
|
|
3393
4213
|
style: { left: -fixedWidth, width: contentWidth, transform: `translateX(${-currentScroll.left}px)` },
|
|
3394
4214
|
children: [
|
|
3395
|
-
columns.map((column, columnIndex) => column.fixed ? null : columnIndex >= domRange.columnStart && columnIndex < domRange.columnEnd ? /* @__PURE__ */ jsx7("span", { children: renderHeaderTitle(columnIndex,
|
|
4215
|
+
columns.map((column, columnIndex) => column.fixed || columnIndex < utilityColumnCount || headerDepth > 1 ? null : columnIndex >= domRange.columnStart && columnIndex < domRange.columnEnd ? /* @__PURE__ */ jsx7("span", { children: renderHeaderTitle(columnIndex, getScrollableColumnLeft(columnIndex)) }, column.key) : null),
|
|
4216
|
+
headerCells.map((cell) => {
|
|
4217
|
+
if (columns[cell.startIndex]?.fixed) return null;
|
|
4218
|
+
if (cell.endIndex < domRange.columnStart || cell.startIndex >= domRange.columnEnd) return null;
|
|
4219
|
+
return renderGroupedHeaderCell(cell);
|
|
4220
|
+
}),
|
|
3396
4221
|
/* @__PURE__ */ jsx7("div", { className: "rvg-body-text-clip", style: { top: fixedHeader ? bodyTop : 0, bottom: 0 }, children: Array.from({ length: domRange.rowEnd - domRange.rowStart }, (_, offset) => domRange.rowStart + offset).flatMap((rowIndex) => columns.map((column, columnIndex) => {
|
|
3397
4222
|
if (column.fixed || columnIndex < domRange.columnStart || columnIndex >= domRange.columnEnd) return null;
|
|
3398
|
-
return renderCellText(rowIndex, columnIndex,
|
|
4223
|
+
return renderCellText(rowIndex, columnIndex, getScrollableColumnLeft(columnIndex));
|
|
3399
4224
|
})) })
|
|
3400
4225
|
]
|
|
3401
4226
|
}
|
|
3402
4227
|
) }),
|
|
3403
4228
|
/* @__PURE__ */ jsx7("div", { className: "rvg-body-text-clip", style: { top: fixedHeader ? bodyTop : 0, bottom: 0 }, children: Array.from({ length: domRange.rowEnd - domRange.rowStart }, (_, offset) => domRange.rowStart + offset).flatMap((rowIndex) => columns.map((column, columnIndex) => {
|
|
3404
4229
|
if (!column.fixed) return null;
|
|
3405
|
-
const left = column.fixed === "left" ?
|
|
4230
|
+
const left = column.fixed === "left" ? leftFixedOffsets.get(columnIndex) ?? 0 : viewport.width - (rightFixedOffsets.get(columnIndex) ?? 0) - metrics[columnIndex].width;
|
|
3406
4231
|
return renderCellText(rowIndex, columnIndex, left);
|
|
3407
4232
|
})) }),
|
|
3408
|
-
columns.map((column, columnIndex) => column.fixed === "left" ? renderHeaderTitle(columnIndex, metrics[columnIndex].left) : null),
|
|
3409
|
-
|
|
4233
|
+
columns.map((column, columnIndex) => columnIndex < utilityColumnCount || headerDepth === 1 && (column.fixed === "left" || column.fixed === "right") ? renderHeaderTitle(columnIndex, column.fixed === "right" ? viewport.width - (rightFixedOffsets.get(columnIndex) ?? 0) - metrics[columnIndex].width : column.fixed === "left" ? leftFixedOffsets.get(columnIndex) ?? 0 : getScrollableColumnLeft(columnIndex)) : null),
|
|
4234
|
+
headerCells.map((cell) => columns[cell.startIndex]?.fixed ? renderGroupedHeaderCell(cell) : null),
|
|
4235
|
+
fixedWidth > 0 && currentScroll.left > 0 && /* @__PURE__ */ jsx7("div", { className: "rvg-text-fixed-shadow is-left", style: { left: fixedWidth } }),
|
|
4236
|
+
rightFixedWidth > 0 && currentScroll.left < contentWidth - viewport.width && /* @__PURE__ */ jsx7("div", { className: "rvg-text-fixed-shadow is-right", style: { right: rightFixedWidth } }),
|
|
4237
|
+
hasVerticalBorders && fixedWidth > 0 && /* @__PURE__ */ jsx7("div", { className: "rvg-text-fixed-border is-left", style: { left: fixedWidth - 1 } }),
|
|
4238
|
+
hasVerticalBorders && rightFixedWidth > 0 && /* @__PURE__ */ jsx7("div", { className: "rvg-text-fixed-border is-right", style: { right: rightFixedWidth } })
|
|
3410
4239
|
] }),
|
|
3411
4240
|
/* @__PURE__ */ jsx7("div", { className: "rvg-spacer", style: { width: contentWidth, height: contentHeight + bodyTop, marginTop: -renderHeight } })
|
|
3412
4241
|
] }),
|
|
@@ -3414,18 +4243,18 @@ function Table({
|
|
|
3414
4243
|
"div",
|
|
3415
4244
|
{
|
|
3416
4245
|
className: `rvg-summary-row is-${summaryPosition}${hasVerticalBorders ? "" : " is-vertical-borderless"}`,
|
|
3417
|
-
style: { height: rowHeight, top: summaryPosition === "top" ? headerHeight : void 0 },
|
|
4246
|
+
style: { height: rowHeight, top: summaryPosition === "top" ? fixedHeader ? headerHeight : headerHeight - currentScroll.top : void 0 },
|
|
3418
4247
|
children: [
|
|
3419
4248
|
/* @__PURE__ */ jsx7("div", { className: "rvg-summary-scroll-clip", style: { left: fixedWidth, right: rightFixedWidth }, children: /* @__PURE__ */ jsx7(
|
|
3420
4249
|
"div",
|
|
3421
4250
|
{
|
|
3422
4251
|
className: "rvg-summary-scroll-content",
|
|
3423
4252
|
style: { left: -fixedWidth, width: contentWidth, transform: `translateX(${-currentScroll.left}px)` },
|
|
3424
|
-
children: columns.map((column, columnIndex) => column.fixed ? null : renderSummaryCell(columnIndex,
|
|
4253
|
+
children: columns.map((column, columnIndex) => column.fixed ? null : renderSummaryCell(columnIndex, getScrollableColumnLeft(columnIndex)))
|
|
3425
4254
|
}
|
|
3426
4255
|
) }),
|
|
3427
4256
|
/* @__PURE__ */ jsxs7("div", { className: "rvg-summary-fixed-layer", children: [
|
|
3428
|
-
columns.map((column, columnIndex) => column.fixed === "left" ? renderSummaryCell(columnIndex,
|
|
4257
|
+
columns.map((column, columnIndex) => column.fixed === "left" ? renderSummaryCell(columnIndex, leftFixedOffsets.get(columnIndex) ?? 0) : null),
|
|
3429
4258
|
columns.map((column, columnIndex) => column.fixed === "right" ? renderSummaryCell(columnIndex, viewport.width - (rightFixedOffsets.get(columnIndex) ?? 0) - metrics[columnIndex].width) : null)
|
|
3430
4259
|
] }),
|
|
3431
4260
|
fixedWidth > 0 && currentScroll.left > 0 && /* @__PURE__ */ jsx7("div", { className: "rvg-summary-fixed-shadow is-left", style: { left: fixedWidth } }),
|
|
@@ -3442,11 +4271,11 @@ function Table({
|
|
|
3442
4271
|
ref: scrollingHeaderIconsRef,
|
|
3443
4272
|
className: "rvg-header-icon-layer rvg-header-icon-layer-scroll",
|
|
3444
4273
|
style: { left: -fixedWidth, width: contentWidth, transform: `translateX(${-scrollRef.current.left}px)` },
|
|
3445
|
-
children: columns.map((column, index) => column.fixed ? null : /* @__PURE__ */ jsx7("span", { children: renderHeaderIcons(index,
|
|
4274
|
+
children: columns.map((column, index) => column.fixed ? null : /* @__PURE__ */ jsx7("span", { children: renderHeaderIcons(index, getScrollableColumnLeft(index)) }, column.key))
|
|
3446
4275
|
}
|
|
3447
4276
|
) }),
|
|
3448
4277
|
/* @__PURE__ */ jsxs7("div", { className: "rvg-header-icon-layer", children: [
|
|
3449
|
-
columns.map((column, index) => column.fixed === "left" ? /* @__PURE__ */ jsx7("span", { children: renderHeaderIcons(index,
|
|
4278
|
+
columns.map((column, index) => column.fixed === "left" ? /* @__PURE__ */ jsx7("span", { children: renderHeaderIcons(index, leftFixedOffsets.get(index) ?? 0) }, column.key) : null),
|
|
3450
4279
|
columns.map((column, index) => column.fixed === "right" ? /* @__PURE__ */ jsx7("span", { children: renderHeaderIcons(index, viewport.width - (rightFixedOffsets.get(index) ?? 0) - metrics[index].width) }, column.key) : null)
|
|
3451
4280
|
] })
|
|
3452
4281
|
] }),
|
|
@@ -3498,6 +4327,27 @@ function Table({
|
|
|
3498
4327
|
}
|
|
3499
4328
|
),
|
|
3500
4329
|
selectionOutlineStyle && /* @__PURE__ */ jsx7("div", { ref: selectionFocusRef, className: "rvg-selection-focus", style: selectionOutlineStyle, "aria-hidden": "true" }),
|
|
4330
|
+
selectionEdgeIndicator && selection && /* @__PURE__ */ jsx7(
|
|
4331
|
+
"button",
|
|
4332
|
+
{
|
|
4333
|
+
ref: selectionEdgeTargetRef,
|
|
4334
|
+
type: "button",
|
|
4335
|
+
className: `rvg-selection-edge-target is-${selectionEdgeIndicator.orientation} is-${selectionEdgeIndicator.side}${selectionEdgeIndicator.viewportEdge ? ` is-viewport-${selectionEdgeIndicator.viewportEdge}` : ""}`,
|
|
4336
|
+
style: selectionEdgeStyle,
|
|
4337
|
+
"aria-label": labels.revealSelectedCell,
|
|
4338
|
+
title: labels.revealSelectedCell,
|
|
4339
|
+
onPointerDown: (event) => {
|
|
4340
|
+
event.preventDefault();
|
|
4341
|
+
event.stopPropagation();
|
|
4342
|
+
},
|
|
4343
|
+
onClick: (event) => {
|
|
4344
|
+
event.stopPropagation();
|
|
4345
|
+
scrollerRef.current?.focus({ preventScroll: true });
|
|
4346
|
+
revealCell(selection);
|
|
4347
|
+
},
|
|
4348
|
+
children: /* @__PURE__ */ jsx7("span", { className: "rvg-selection-edge-halo", "aria-hidden": "true", children: /* @__PURE__ */ jsx7("svg", { viewBox: "0 0 100 100", preserveAspectRatio: "none", children: /* @__PURE__ */ jsx7("path", { d: "M 0 100 L 0 0 L 100 0" }) }) })
|
|
4349
|
+
}
|
|
4350
|
+
),
|
|
3501
4351
|
contextMenu && /* @__PURE__ */ jsx7("div", { ref: contextMenuRef, className: "rvg-context-menu", role: "menu", style: { left: contextMenu.left, top: contextMenu.top }, children: contextMenu.type === "header" ? (() => {
|
|
3502
4352
|
const column = columns[contextMenu.columnIndex];
|
|
3503
4353
|
const headerContext = { column, columnIndex: contextMenu.columnIndex };
|
|
@@ -3723,12 +4573,40 @@ function Table({
|
|
|
3723
4573
|
] })
|
|
3724
4574
|
] }) }),
|
|
3725
4575
|
toast && /* @__PURE__ */ jsx7("div", { className: `rvg-toast is-${toast.type}`, role: "status", children: toast.message }),
|
|
4576
|
+
/* @__PURE__ */ jsx7(
|
|
4577
|
+
"div",
|
|
4578
|
+
{
|
|
4579
|
+
ref: verticalScrollbarRef,
|
|
4580
|
+
className: "rvg-vertical-scrollbar",
|
|
4581
|
+
onPointerEnter: () => keepCustomScrollbarVisible("vertical"),
|
|
4582
|
+
onPointerLeave: () => releaseCustomScrollbarVisible("vertical"),
|
|
4583
|
+
onPointerDown: handleVerticalScrollbarPointerDown,
|
|
4584
|
+
onPointerMove: handleVerticalScrollbarPointerMove,
|
|
4585
|
+
onPointerUp: handleVerticalScrollbarPointerUp,
|
|
4586
|
+
onPointerCancel: handleVerticalScrollbarPointerUp,
|
|
4587
|
+
children: /* @__PURE__ */ jsx7("div", { ref: verticalScrollbarThumbRef, className: "rvg-vertical-scrollbar-thumb" })
|
|
4588
|
+
}
|
|
4589
|
+
),
|
|
4590
|
+
/* @__PURE__ */ jsx7(
|
|
4591
|
+
"div",
|
|
4592
|
+
{
|
|
4593
|
+
ref: horizontalScrollbarRef,
|
|
4594
|
+
className: "rvg-horizontal-scrollbar",
|
|
4595
|
+
onPointerEnter: () => keepCustomScrollbarVisible("horizontal"),
|
|
4596
|
+
onPointerLeave: () => releaseCustomScrollbarVisible("horizontal"),
|
|
4597
|
+
onPointerDown: handleHorizontalScrollbarPointerDown,
|
|
4598
|
+
onPointerMove: handleHorizontalScrollbarPointerMove,
|
|
4599
|
+
onPointerUp: handleHorizontalScrollbarPointerUp,
|
|
4600
|
+
onPointerCancel: handleHorizontalScrollbarPointerUp,
|
|
4601
|
+
children: /* @__PURE__ */ jsx7("div", { ref: horizontalScrollbarThumbRef, className: "rvg-horizontal-scrollbar-thumb" })
|
|
4602
|
+
}
|
|
4603
|
+
),
|
|
3726
4604
|
/* @__PURE__ */ jsx7("div", { ref: dragGuideRef, className: "rvg-column-guide" }),
|
|
3727
4605
|
resizeGuideX !== null && /* @__PURE__ */ jsx7(
|
|
3728
4606
|
"div",
|
|
3729
4607
|
{
|
|
3730
4608
|
className: "rvg-column-guide is-resizing",
|
|
3731
|
-
style: { display: "block", transform: `translateX(${Math.round(resizeGuideX)}px)` }
|
|
4609
|
+
style: { display: "block", top: resizeGuideTop, transform: `translateX(${Math.round(resizeGuideX)}px)` }
|
|
3732
4610
|
}
|
|
3733
4611
|
),
|
|
3734
4612
|
/* @__PURE__ */ jsx7("div", { ref: rowDragGuideRef, className: "rvg-row-guide" }),
|
|
@@ -3794,8 +4672,8 @@ function Table({
|
|
|
3794
4672
|
column.key
|
|
3795
4673
|
);
|
|
3796
4674
|
}) }, rowIndex)),
|
|
3797
|
-
fixedWidth > 0 && /* @__PURE__ */ jsx7("div", { className: "rvg-loading-fixed-shadow is-left", style: { left: fixedWidth } }),
|
|
3798
|
-
rightFixedWidth > 0 && /* @__PURE__ */ jsx7("div", { className: "rvg-loading-fixed-shadow is-right", style: { right: rightFixedWidth } })
|
|
4675
|
+
fixedWidth > 0 && currentScroll.left > 0 && /* @__PURE__ */ jsx7("div", { className: "rvg-loading-fixed-shadow is-left", style: { left: fixedWidth } }),
|
|
4676
|
+
rightFixedWidth > 0 && currentScroll.left < contentWidth - viewport.width && /* @__PURE__ */ jsx7("div", { className: "rvg-loading-fixed-shadow is-right", style: { right: rightFixedWidth } })
|
|
3799
4677
|
] }),
|
|
3800
4678
|
loading && !hasCustomLoading && hasCompletedLoadRef.current && /* @__PURE__ */ jsx7("div", { className: "rvg-refresh-loading", "aria-label": labels.refreshing, children: /* @__PURE__ */ jsx7(LoadingSpinnerIcon, {}) })
|
|
3801
4679
|
] });
|