@lensui/lens-table 0.1.2 → 0.1.4
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 +49 -8
- package/dist/index.cjs +875 -152
- package/dist/index.css +134 -9
- package/dist/index.d.cts +38 -6
- package/dist/index.d.ts +38 -6
- package/dist/index.js +875 -152
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -784,8 +784,9 @@ function buildColumnMetrics(columns, columnDraggableOrOptions = false) {
|
|
|
784
784
|
});
|
|
785
785
|
const viewportWidth = options.viewportWidth ?? 0;
|
|
786
786
|
if (viewportWidth <= left || columns.length === 0) return metrics;
|
|
787
|
-
const
|
|
788
|
-
const
|
|
787
|
+
const unsetWidthStretchable = columns.map((column, index) => ({ column, index })).filter(({ column }) => column.width === void 0 && column.fixed === void 0 && !column.rowSelection && !column.rowDragHandle && !column.rowNumber);
|
|
788
|
+
const dataStretchable = columns.map((column, index) => ({ column, index })).filter(({ column }) => column.fixed === void 0 && !column.rowSelection && !column.rowDragHandle && !column.rowNumber);
|
|
789
|
+
const targets = unsetWidthStretchable.length > 0 ? unsetWidthStretchable : dataStretchable.length > 0 ? dataStretchable : [];
|
|
789
790
|
if (targets.length === 0) return metrics;
|
|
790
791
|
const extra = viewportWidth - left;
|
|
791
792
|
const baseTotal = targets.reduce((sum, { index }) => sum + metrics[index].width, 0);
|
|
@@ -862,6 +863,8 @@ function ellipsizeText(context, text, maxWidth) {
|
|
|
862
863
|
}
|
|
863
864
|
function paintGrid(options) {
|
|
864
865
|
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;
|
|
866
|
+
const headerLeafTop = options.headerLeafTop ?? 0;
|
|
867
|
+
const headerLeafHeight = options.headerLeafHeight ?? headerHeight;
|
|
865
868
|
const colors = { ...COLORS, ...options.colors };
|
|
866
869
|
const bodyTop = options.bodyTop ?? headerHeight;
|
|
867
870
|
const getCellSpan = (rowIndex, columnIndex) => options.cellSpans.get(getCellCoordKey(rowIndex, columnIndex));
|
|
@@ -878,9 +881,16 @@ function paintGrid(options) {
|
|
|
878
881
|
ctx.fillRect(0, 0, width, height);
|
|
879
882
|
ctx.font = "13px Inter, ui-sans-serif, system-ui, sans-serif";
|
|
880
883
|
ctx.textBaseline = "middle";
|
|
881
|
-
const leftFixedWidth = columns.reduce((value, column, index) => column.fixed === "left" ?
|
|
884
|
+
const leftFixedWidth = columns.reduce((value, column, index) => column.fixed === "left" ? value + metrics[index].width : value, 0);
|
|
882
885
|
const rightFixedWidth = columns.reduce((value, column, index) => column.fixed === "right" ? value + metrics[index].width : value, 0);
|
|
883
886
|
const rightFixedLeft = width - rightFixedWidth;
|
|
887
|
+
const leftOffsets = /* @__PURE__ */ new Map();
|
|
888
|
+
let leftOffset = 0;
|
|
889
|
+
for (let index = 0; index < columns.length; index += 1) {
|
|
890
|
+
if (columns[index].fixed !== "left") continue;
|
|
891
|
+
leftOffsets.set(index, leftOffset);
|
|
892
|
+
leftOffset += metrics[index].width;
|
|
893
|
+
}
|
|
884
894
|
const rightOffsets = /* @__PURE__ */ new Map();
|
|
885
895
|
let rightOffset = 0;
|
|
886
896
|
for (let index = columns.length - 1; index >= 0; index -= 1) {
|
|
@@ -888,8 +898,14 @@ function paintGrid(options) {
|
|
|
888
898
|
rightOffsets.set(index, rightOffset);
|
|
889
899
|
rightOffset += metrics[index].width;
|
|
890
900
|
}
|
|
901
|
+
const scrollableLefts = /* @__PURE__ */ new Map();
|
|
902
|
+
let scrollableLeft = leftFixedWidth;
|
|
903
|
+
for (let index = 0; index < columns.length; index += 1) {
|
|
904
|
+
scrollableLefts.set(index, scrollableLeft);
|
|
905
|
+
if (columns[index].fixed === void 0) scrollableLeft += metrics[index].width;
|
|
906
|
+
}
|
|
891
907
|
const getColumnX = (index) => {
|
|
892
|
-
return columns[index].fixed === "left" ?
|
|
908
|
+
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;
|
|
893
909
|
};
|
|
894
910
|
const paintBodyColumns = (layer) => {
|
|
895
911
|
const rowStart = Math.max(0, range.rowStart - Math.max(0, options.maxRowSpan - 1));
|
|
@@ -1039,7 +1055,7 @@ function paintGrid(options) {
|
|
|
1039
1055
|
if (!verticalBorderless) ctx.fillRect(x + metric.width - 1, headerY, 1, headerHeight);
|
|
1040
1056
|
ctx.fillRect(x, headerY + headerHeight - 1, metric.width, 1);
|
|
1041
1057
|
const column = columns[columnIndex];
|
|
1042
|
-
if (!column.rowSelection && !column.rowDragHandle && !column.rowNumber && column.title) {
|
|
1058
|
+
if (headerLeafTop === 0 && !column.rowSelection && !column.rowDragHandle && !column.rowNumber && !column.renderHeader && column.title) {
|
|
1043
1059
|
ctx.font = "600 13px Inter, ui-sans-serif, system-ui, sans-serif";
|
|
1044
1060
|
const titleWidth = ctx.measureText(column.title).width;
|
|
1045
1061
|
const visibleActions = getVisibleHeaderActions(column, metric.width, columnDraggable, titleWidth);
|
|
@@ -1056,11 +1072,11 @@ function paintGrid(options) {
|
|
|
1056
1072
|
const textX = titleAlign === "right" ? x + contentWidth - padding : titleAlign === "center" ? x + centeredTextX : x + padding;
|
|
1057
1073
|
ctx.save();
|
|
1058
1074
|
ctx.beginPath();
|
|
1059
|
-
ctx.rect(x + 2, headerY + 1, Math.max(0, clipWidth - 4),
|
|
1075
|
+
ctx.rect(x + 2, headerY + headerLeafTop + 1, Math.max(0, clipWidth - 4), headerLeafHeight - 2);
|
|
1060
1076
|
ctx.clip();
|
|
1061
1077
|
ctx.fillStyle = colors.muted;
|
|
1062
1078
|
ctx.textAlign = titleAlign === "right" ? "right" : titleAlign === "center" ? "center" : "left";
|
|
1063
|
-
ctx.fillText(title, textX, headerY +
|
|
1079
|
+
ctx.fillText(title, textX, headerY + headerLeafTop + headerLeafHeight / 2);
|
|
1064
1080
|
ctx.restore();
|
|
1065
1081
|
}
|
|
1066
1082
|
}
|
|
@@ -1068,22 +1084,6 @@ function paintGrid(options) {
|
|
|
1068
1084
|
paintHeaderColumns("scroll");
|
|
1069
1085
|
paintHeaderColumns("left");
|
|
1070
1086
|
paintHeaderColumns("right");
|
|
1071
|
-
if (leftFixedWidth > 0 && scrollLeft > 0) {
|
|
1072
|
-
const shadowWidth = 10;
|
|
1073
|
-
const shadow = ctx.createLinearGradient(leftFixedWidth, 0, leftFixedWidth + shadowWidth, 0);
|
|
1074
|
-
shadow.addColorStop(0, "rgba(5, 5, 5, 0.06)");
|
|
1075
|
-
shadow.addColorStop(1, "rgba(5, 5, 5, 0)");
|
|
1076
|
-
ctx.fillStyle = shadow;
|
|
1077
|
-
ctx.fillRect(leftFixedWidth, 0, shadowWidth, height);
|
|
1078
|
-
}
|
|
1079
|
-
if (rightFixedWidth > 0 && scrollLeft < (metrics[metrics.length - 1]?.right ?? 0) - width) {
|
|
1080
|
-
const shadowWidth = 10;
|
|
1081
|
-
const shadow = ctx.createLinearGradient(rightFixedLeft - shadowWidth, 0, rightFixedLeft, 0);
|
|
1082
|
-
shadow.addColorStop(0, "rgba(5, 5, 5, 0)");
|
|
1083
|
-
shadow.addColorStop(1, "rgba(5, 5, 5, 0.06)");
|
|
1084
|
-
ctx.fillStyle = shadow;
|
|
1085
|
-
ctx.fillRect(rightFixedLeft - shadowWidth, 0, shadowWidth, height);
|
|
1086
|
-
}
|
|
1087
1087
|
if (rightFixedWidth > 0 && !verticalBorderless) {
|
|
1088
1088
|
ctx.fillStyle = colors.grid;
|
|
1089
1089
|
ctx.fillRect(rightFixedLeft - 1, 0, 1, height);
|
|
@@ -1235,6 +1235,45 @@ var getRowNumberColumnWidth = (rowCount) => {
|
|
|
1235
1235
|
const digits = String(Math.max(1, rowCount)).length;
|
|
1236
1236
|
return digits <= 4 ? 44 : 44 + (digits - 4) * 8;
|
|
1237
1237
|
};
|
|
1238
|
+
function getColumnDepth(column) {
|
|
1239
|
+
if (!column.children?.length) return 1;
|
|
1240
|
+
return 1 + Math.max(...column.children.map(getColumnDepth));
|
|
1241
|
+
}
|
|
1242
|
+
function flattenDataColumns(columns, inheritedFixed, insideGroup = false) {
|
|
1243
|
+
return columns.flatMap((column) => {
|
|
1244
|
+
if (!column.children?.length) {
|
|
1245
|
+
return insideGroup ? [{ ...column, fixed: inheritedFixed }] : [column];
|
|
1246
|
+
}
|
|
1247
|
+
return flattenDataColumns(column.children, column.fixed ?? inheritedFixed, true);
|
|
1248
|
+
});
|
|
1249
|
+
}
|
|
1250
|
+
function buildHeaderCells(sourceColumns, utilityColumnCount, depth) {
|
|
1251
|
+
const cells = [];
|
|
1252
|
+
let leafIndex = utilityColumnCount;
|
|
1253
|
+
const walk = (column, level, inheritedFixed, insideGroup, rootIndex, parentKey) => {
|
|
1254
|
+
const fixedColumn = insideGroup ? { ...column, fixed: inheritedFixed } : column;
|
|
1255
|
+
if (!fixedColumn.children?.length) {
|
|
1256
|
+
const startIndex2 = leafIndex;
|
|
1257
|
+
leafIndex += 1;
|
|
1258
|
+
cells.push({ key: fixedColumn.key, column: fixedColumn, level: depth - 1, startIndex: startIndex2, endIndex: startIndex2, leaf: true, parentKey, rootIndex });
|
|
1259
|
+
return { startIndex: startIndex2, endIndex: startIndex2 };
|
|
1260
|
+
}
|
|
1261
|
+
const startIndex = leafIndex;
|
|
1262
|
+
fixedColumn.children.forEach((child) => walk(child, level + 1, fixedColumn.fixed, true, rootIndex, fixedColumn.key));
|
|
1263
|
+
const endIndex = leafIndex - 1;
|
|
1264
|
+
cells.push({ key: fixedColumn.key, column: fixedColumn, level, startIndex, endIndex, leaf: false, parentKey, rootIndex });
|
|
1265
|
+
return { startIndex, endIndex };
|
|
1266
|
+
};
|
|
1267
|
+
sourceColumns.forEach((column, index) => walk(column, 0, void 0, false, index));
|
|
1268
|
+
return cells;
|
|
1269
|
+
}
|
|
1270
|
+
function hasNestedFixedColumns(columns, insideGroup = false) {
|
|
1271
|
+
return columns.some((column) => insideGroup && column.fixed !== void 0 || (column.children?.length ? hasNestedFixedColumns(column.children, true) : false));
|
|
1272
|
+
}
|
|
1273
|
+
function isProductionRuntime() {
|
|
1274
|
+
const env = globalThis.process?.env?.NODE_ENV;
|
|
1275
|
+
return env === "production";
|
|
1276
|
+
}
|
|
1238
1277
|
var createPendingInsertId = () => `table-inserted-${globalThis.crypto?.randomUUID?.() ?? `${Date.now()}-${Math.random().toString(36).slice(2)}`}`;
|
|
1239
1278
|
var waitForPaint = () => new Promise((resolve) => {
|
|
1240
1279
|
requestAnimationFrame(() => requestAnimationFrame(() => resolve()));
|
|
@@ -1298,12 +1337,14 @@ function Table({
|
|
|
1298
1337
|
rowKey,
|
|
1299
1338
|
width = "100%",
|
|
1300
1339
|
height = "100%",
|
|
1301
|
-
autoHeight =
|
|
1302
|
-
|
|
1303
|
-
|
|
1340
|
+
autoHeight = true,
|
|
1341
|
+
layout,
|
|
1342
|
+
rowHeight: rowHeightProp,
|
|
1343
|
+
headerHeight: headerHeightProp,
|
|
1304
1344
|
fixedHeader = true,
|
|
1305
1345
|
locale = "zh-CN",
|
|
1306
|
-
|
|
1346
|
+
borderless,
|
|
1347
|
+
verticalBorderless,
|
|
1307
1348
|
striped = false,
|
|
1308
1349
|
highlight,
|
|
1309
1350
|
cellSpans = [],
|
|
@@ -1345,8 +1386,27 @@ function Table({
|
|
|
1345
1386
|
style,
|
|
1346
1387
|
ariaLabel = "Data grid"
|
|
1347
1388
|
}) {
|
|
1389
|
+
const rowHeight = layout?.rowHeight ?? rowHeightProp ?? 36;
|
|
1390
|
+
const baseHeaderHeight = layout?.headerHeight ?? headerHeightProp ?? 40;
|
|
1391
|
+
const headerDepth = useMemo(() => Math.max(1, ...sourceColumns.map(getColumnDepth)), [sourceColumns]);
|
|
1348
1392
|
const { language, labels } = useMemo(() => resolveGridLocale(locale), [locale]);
|
|
1349
|
-
const
|
|
1393
|
+
const customHeaderContentRefs = useRef2(/* @__PURE__ */ new Map());
|
|
1394
|
+
const customHeaderMeasurementsRef = useRef2(/* @__PURE__ */ new Map());
|
|
1395
|
+
const [measuredHeaderRowHeights, setMeasuredHeaderRowHeights] = useState4([]);
|
|
1396
|
+
const headerRowHeights = useMemo(() => Array.from({ length: headerDepth }, (_, level) => Math.max(baseHeaderHeight, measuredHeaderRowHeights[level] ?? 0)), [baseHeaderHeight, headerDepth, measuredHeaderRowHeights]);
|
|
1397
|
+
const headerRowOffsets = useMemo(() => {
|
|
1398
|
+
const offsets = [];
|
|
1399
|
+
let top = 0;
|
|
1400
|
+
headerRowHeights.forEach((height2) => {
|
|
1401
|
+
offsets.push(top);
|
|
1402
|
+
top += height2;
|
|
1403
|
+
});
|
|
1404
|
+
return offsets;
|
|
1405
|
+
}, [headerRowHeights]);
|
|
1406
|
+
const headerHeight = headerRowHeights.reduce((total, height2) => total + height2, 0);
|
|
1407
|
+
const headerLeafTop = headerRowOffsets[headerDepth - 1] ?? 0;
|
|
1408
|
+
const headerLeafHeight = headerRowHeights[headerDepth - 1] ?? baseHeaderHeight;
|
|
1409
|
+
const hasVerticalBorders = !(borderless ?? verticalBorderless ?? false);
|
|
1350
1410
|
const hasStripedRows = Boolean(striped);
|
|
1351
1411
|
const stripedColor = typeof striped === "string" ? striped : void 0;
|
|
1352
1412
|
const editedCellsHighlight = highlight?.editedCells ?? false;
|
|
@@ -1365,6 +1425,13 @@ function Table({
|
|
|
1365
1425
|
const summaryEmptyValue = typeof summary === "object" ? summary.emptyValue ?? "" : "";
|
|
1366
1426
|
const contextMenuEnabled = contextMenuConfig !== false;
|
|
1367
1427
|
const customContextMenuConfig = typeof contextMenuConfig === "object" ? contextMenuConfig : void 0;
|
|
1428
|
+
const [resizedColumnWidths, setResizedColumnWidths] = useState4({});
|
|
1429
|
+
const leafSourceColumns = useMemo(() => flattenDataColumns(sourceColumns), [sourceColumns]);
|
|
1430
|
+
useEffect2(() => {
|
|
1431
|
+
if (!isProductionRuntime() && hasNestedFixedColumns(sourceColumns)) {
|
|
1432
|
+
console.warn("[Table] Multi-level headers only support fixed columns on top-level columns. Nested fixed values are ignored.");
|
|
1433
|
+
}
|
|
1434
|
+
}, [sourceColumns]);
|
|
1368
1435
|
const columns = useMemo(() => {
|
|
1369
1436
|
const utilityColumns = [];
|
|
1370
1437
|
if (rowDraggable) {
|
|
@@ -1376,9 +1443,24 @@ function Table({
|
|
|
1376
1443
|
if (rowNumber) {
|
|
1377
1444
|
utilityColumns.push({ key: "__rvg_row_number__", title: "#", width: getRowNumberColumnWidth(sourceRows.length), align: "center", fixed: "left", rowNumber: true });
|
|
1378
1445
|
}
|
|
1379
|
-
|
|
1380
|
-
|
|
1446
|
+
const dataColumns = leafSourceColumns.map((column) => {
|
|
1447
|
+
const resizedWidth = resizedColumnWidths[column.key];
|
|
1448
|
+
return resizedWidth === void 0 ? column : { ...column, width: resizedWidth };
|
|
1449
|
+
});
|
|
1450
|
+
return [...utilityColumns, ...dataColumns];
|
|
1451
|
+
}, [leafSourceColumns, resizedColumnWidths, rowDraggable, rowNumber, rowSelection, sourceRows.length]);
|
|
1381
1452
|
const utilityColumnCount = (rowDraggable ? 1 : 0) + (rowSelection ? 1 : 0) + (rowNumber ? 1 : 0);
|
|
1453
|
+
const headerCells = useMemo(() => buildHeaderCells(sourceColumns, utilityColumnCount, headerDepth), [headerDepth, sourceColumns, utilityColumnCount]);
|
|
1454
|
+
const activeCustomHeaderLevels = useMemo(() => {
|
|
1455
|
+
const levels = /* @__PURE__ */ new Map();
|
|
1456
|
+
columns.forEach((column) => {
|
|
1457
|
+
if (column.renderHeader) levels.set(column.key, headerDepth - 1);
|
|
1458
|
+
});
|
|
1459
|
+
headerCells.forEach((cell) => {
|
|
1460
|
+
if (cell.column.renderHeader) levels.set(`group:${cell.key}:${cell.level}`, cell.level);
|
|
1461
|
+
});
|
|
1462
|
+
return levels;
|
|
1463
|
+
}, [columns, headerCells, headerDepth]);
|
|
1382
1464
|
const canvasRef = useRef2(null);
|
|
1383
1465
|
const scrollerRef = useRef2(null);
|
|
1384
1466
|
const frameRef = useRef2(null);
|
|
@@ -1396,6 +1478,14 @@ function Table({
|
|
|
1396
1478
|
const dragGuideRef = useRef2(null);
|
|
1397
1479
|
const rowDragGuideRef = useRef2(null);
|
|
1398
1480
|
const selectionFocusRef = useRef2(null);
|
|
1481
|
+
const verticalScrollbarRef = useRef2(null);
|
|
1482
|
+
const verticalScrollbarThumbRef = useRef2(null);
|
|
1483
|
+
const verticalScrollbarDragRef = useRef2(null);
|
|
1484
|
+
const horizontalScrollbarRef = useRef2(null);
|
|
1485
|
+
const horizontalScrollbarThumbRef = useRef2(null);
|
|
1486
|
+
const horizontalScrollbarDragRef = useRef2(null);
|
|
1487
|
+
const verticalScrollbarVisibilityTimerRef = useRef2(null);
|
|
1488
|
+
const horizontalScrollbarVisibilityTimerRef = useRef2(null);
|
|
1399
1489
|
const rowByKeyRef = useRef2(/* @__PURE__ */ new Map());
|
|
1400
1490
|
const rowIndexByKeyRef = useRef2(/* @__PURE__ */ new Map());
|
|
1401
1491
|
const headerIconsRef = useRef2(null);
|
|
@@ -1448,10 +1538,65 @@ function Table({
|
|
|
1448
1538
|
rangeDragRef.current = null;
|
|
1449
1539
|
setSelectionRange(null);
|
|
1450
1540
|
}, [rangeSelection]);
|
|
1541
|
+
useEffect2(() => {
|
|
1542
|
+
setResizedColumnWidths((current) => {
|
|
1543
|
+
const sourceColumnByKey = new Map(leafSourceColumns.map((column) => [column.key, column]));
|
|
1544
|
+
let changed = false;
|
|
1545
|
+
const next = {};
|
|
1546
|
+
Object.entries(current).forEach(([key, value]) => {
|
|
1547
|
+
const sourceColumn = sourceColumnByKey.get(key);
|
|
1548
|
+
if (!sourceColumn) {
|
|
1549
|
+
changed = true;
|
|
1550
|
+
return;
|
|
1551
|
+
}
|
|
1552
|
+
if (sourceColumn.width !== void 0 && sourceColumn.width !== value) {
|
|
1553
|
+
changed = true;
|
|
1554
|
+
return;
|
|
1555
|
+
}
|
|
1556
|
+
next[key] = value;
|
|
1557
|
+
});
|
|
1558
|
+
return changed ? next : current;
|
|
1559
|
+
});
|
|
1560
|
+
}, [leafSourceColumns]);
|
|
1451
1561
|
useEffect2(() => () => {
|
|
1452
1562
|
if (headerTooltipTimerRef.current !== null) clearTimeout(headerTooltipTimerRef.current);
|
|
1453
1563
|
if (cellTooltipTimerRef.current !== null) clearTimeout(cellTooltipTimerRef.current);
|
|
1454
1564
|
}, []);
|
|
1565
|
+
const measureCustomHeaderHeight = useCallback2(() => {
|
|
1566
|
+
const nextHeights = Array.from({ length: headerDepth }, () => baseHeaderHeight);
|
|
1567
|
+
customHeaderContentRefs.current.forEach((node) => {
|
|
1568
|
+
const headerCell = node.closest(".rvg-header-title");
|
|
1569
|
+
const key = headerCell?.dataset.measureKey;
|
|
1570
|
+
const level = key ? activeCustomHeaderLevels.get(key) : void 0;
|
|
1571
|
+
if (!key || level === void 0 || level < 0 || level >= nextHeights.length) return;
|
|
1572
|
+
customHeaderMeasurementsRef.current.set(key, { level, height: Math.ceil(node.scrollHeight) + 16 });
|
|
1573
|
+
});
|
|
1574
|
+
customHeaderMeasurementsRef.current.forEach((measurement, key) => {
|
|
1575
|
+
if (!activeCustomHeaderLevels.has(key)) return;
|
|
1576
|
+
nextHeights[measurement.level] = Math.max(nextHeights[measurement.level], measurement.height);
|
|
1577
|
+
});
|
|
1578
|
+
setMeasuredHeaderRowHeights((current) => {
|
|
1579
|
+
const changed = nextHeights.length !== current.length || nextHeights.some((height2, index) => height2 !== current[index]);
|
|
1580
|
+
return changed ? nextHeights : current;
|
|
1581
|
+
});
|
|
1582
|
+
}, [activeCustomHeaderLevels, baseHeaderHeight, headerDepth]);
|
|
1583
|
+
useLayoutEffect2(() => {
|
|
1584
|
+
customHeaderContentRefs.current.forEach((_, key) => {
|
|
1585
|
+
if (!activeCustomHeaderLevels.has(key)) customHeaderContentRefs.current.delete(key);
|
|
1586
|
+
});
|
|
1587
|
+
customHeaderMeasurementsRef.current.forEach((_, key) => {
|
|
1588
|
+
if (!activeCustomHeaderLevels.has(key)) customHeaderMeasurementsRef.current.delete(key);
|
|
1589
|
+
});
|
|
1590
|
+
if (activeCustomHeaderLevels.size === 0) {
|
|
1591
|
+
setMeasuredHeaderRowHeights([]);
|
|
1592
|
+
return;
|
|
1593
|
+
}
|
|
1594
|
+
measureCustomHeaderHeight();
|
|
1595
|
+
if (typeof ResizeObserver === "undefined") return;
|
|
1596
|
+
const observer = new ResizeObserver(measureCustomHeaderHeight);
|
|
1597
|
+
customHeaderContentRefs.current.forEach((node) => observer.observe(node));
|
|
1598
|
+
return () => observer.disconnect();
|
|
1599
|
+
}, [activeCustomHeaderLevels, measureCustomHeaderHeight, viewport.width]);
|
|
1455
1600
|
const [filterEditor, setFilterEditor] = useState4(null);
|
|
1456
1601
|
const [contextMenu, setContextMenu] = useState4(null);
|
|
1457
1602
|
const [insertCounts, setInsertCounts] = useState4({ before: 1, after: 1 });
|
|
@@ -1460,6 +1605,8 @@ function Table({
|
|
|
1460
1605
|
const [insertBusy, setInsertBusy] = useState4(false);
|
|
1461
1606
|
const [deleteBusy, setDeleteBusy] = useState4(false);
|
|
1462
1607
|
const [toast, setToast] = useState4(null);
|
|
1608
|
+
const [resizeGuideX, setResizeGuideX] = useState4(null);
|
|
1609
|
+
const [resizeGuideTop, setResizeGuideTop] = useState4(0);
|
|
1463
1610
|
const [hoveredHeaderAction, setHoveredHeaderAction] = useState4(null);
|
|
1464
1611
|
const [visibleHeaderTooltip, setVisibleHeaderTooltip] = useState4(null);
|
|
1465
1612
|
const [hoveredCellTooltip, setHoveredCellTooltip] = useState4(null);
|
|
@@ -1544,7 +1691,17 @@ function Table({
|
|
|
1544
1691
|
const metrics = useMemo(() => buildColumnMetrics(columns, { columnDraggable, viewportWidth: viewport.width }), [columnDraggable, columns, viewport.width]);
|
|
1545
1692
|
const contentWidth = metrics.length > 0 ? metrics[metrics.length - 1].right : 0;
|
|
1546
1693
|
const contentHeight = rows.length * rowHeight;
|
|
1547
|
-
const fixedWidth = useMemo(() => columns.reduce((width2, column, index) => column.fixed === "left" ?
|
|
1694
|
+
const fixedWidth = useMemo(() => columns.reduce((width2, column, index) => column.fixed === "left" ? width2 + metrics[index].width : width2, 0), [columns, metrics]);
|
|
1695
|
+
const leftFixedOffsets = useMemo(() => {
|
|
1696
|
+
const offsets = /* @__PURE__ */ new Map();
|
|
1697
|
+
let offset = 0;
|
|
1698
|
+
for (let index = 0; index < columns.length; index += 1) {
|
|
1699
|
+
if (columns[index].fixed !== "left") continue;
|
|
1700
|
+
offsets.set(index, offset);
|
|
1701
|
+
offset += metrics[index].width;
|
|
1702
|
+
}
|
|
1703
|
+
return offsets;
|
|
1704
|
+
}, [columns, metrics]);
|
|
1548
1705
|
const rightFixedWidth = useMemo(() => columns.reduce((width2, column, index) => column.fixed === "right" ? width2 + metrics[index].width : width2, 0), [columns, metrics]);
|
|
1549
1706
|
const rightFixedOffsets = useMemo(() => {
|
|
1550
1707
|
const offsets = /* @__PURE__ */ new Map();
|
|
@@ -1556,6 +1713,16 @@ function Table({
|
|
|
1556
1713
|
}
|
|
1557
1714
|
return offsets;
|
|
1558
1715
|
}, [columns, metrics]);
|
|
1716
|
+
const scrollableColumnLefts = useMemo(() => {
|
|
1717
|
+
const lefts = [];
|
|
1718
|
+
let width2 = fixedWidth;
|
|
1719
|
+
for (let index = 0; index < columns.length; index += 1) {
|
|
1720
|
+
lefts[index] = width2;
|
|
1721
|
+
if (columns[index].fixed === void 0) width2 += metrics[index].width;
|
|
1722
|
+
}
|
|
1723
|
+
return lefts;
|
|
1724
|
+
}, [columns, fixedWidth, metrics]);
|
|
1725
|
+
const getScrollableColumnLeft = useCallback2((columnIndex) => scrollableColumnLefts[columnIndex] ?? fixedWidth, [fixedWidth, scrollableColumnLefts]);
|
|
1559
1726
|
const hasRowSelectionColumn = useMemo(() => columns.some((column) => column.rowSelection), [columns]);
|
|
1560
1727
|
const rowSelectionMode = typeof rowSelection === "object" ? rowSelection.mode ?? "multiple" : "multiple";
|
|
1561
1728
|
const hasSummaryRow = useMemo(() => rows.length > 0 && summaryEnabled && columns.some((column) => Boolean(column.summary)), [columns, rows.length, summaryEnabled]);
|
|
@@ -1566,7 +1733,7 @@ function Table({
|
|
|
1566
1733
|
const realContentHeight = bodyTop + bodyContentHeight + bottomSummaryHeight + (contentWidth > viewport.width ? horizontalScrollbarHeight : 0);
|
|
1567
1734
|
const effectiveHeight = autoHeight ? Math.max(bodyTop + bottomSummaryHeight, Math.min(resolvedHeight, realContentHeight)) : resolvedHeight;
|
|
1568
1735
|
const renderHeight = Math.max(0, effectiveHeight - bottomSummaryHeight);
|
|
1569
|
-
const bodyViewportHeight = Math.max(0, renderHeight - (fixedHeader ? headerHeight : 0)
|
|
1736
|
+
const bodyViewportHeight = Math.max(0, renderHeight - (fixedHeader ? headerHeight + topSummaryHeight : 0));
|
|
1570
1737
|
const [summaryState, setSummaryState] = useState4(() => ({ columns: null, rows: null, values: /* @__PURE__ */ new Map(), pending: false }));
|
|
1571
1738
|
const summaryPending = hasSummaryRow && (loading || summaryState.pending || summaryState.columns !== columns || summaryState.rows !== rows);
|
|
1572
1739
|
const summaryValues = hasSummaryRow && summaryState.columns === columns && summaryState.rows === rows ? summaryState.values : /* @__PURE__ */ new Map();
|
|
@@ -1613,14 +1780,30 @@ function Table({
|
|
|
1613
1780
|
rowIndexByKeyRef.current.set(key, index);
|
|
1614
1781
|
return key;
|
|
1615
1782
|
}, [getDataRowKey]);
|
|
1783
|
+
const resolvedCellSpans = useMemo(() => {
|
|
1784
|
+
if (Array.isArray(cellSpans)) return cellSpans;
|
|
1785
|
+
const spans = [];
|
|
1786
|
+
rows.forEach((row, rowIndex) => {
|
|
1787
|
+
columns.forEach((column, columnIndex) => {
|
|
1788
|
+
const span = cellSpans({ row, rowIndex, column, columnIndex, rows, columns });
|
|
1789
|
+
if (!span) return;
|
|
1790
|
+
spans.push({
|
|
1791
|
+
rowIndex,
|
|
1792
|
+
columnKey: column.key,
|
|
1793
|
+
...span
|
|
1794
|
+
});
|
|
1795
|
+
});
|
|
1796
|
+
});
|
|
1797
|
+
return spans;
|
|
1798
|
+
}, [cellSpans, columns, rows]);
|
|
1616
1799
|
const cellSpanLookup = useMemo(() => {
|
|
1617
|
-
if (
|
|
1800
|
+
if (resolvedCellSpans.length === 0) return createEmptyCellSpanLookup();
|
|
1618
1801
|
const rowIndexByKey = /* @__PURE__ */ new Map();
|
|
1619
1802
|
rows.forEach((row, index) => rowIndexByKey.set(getRowKey(row, index), index));
|
|
1620
1803
|
const columnIndexByKey = new Map(columns.map((column, index) => [column.key, index]));
|
|
1621
1804
|
const lookup = createEmptyCellSpanLookup();
|
|
1622
1805
|
const occupied = /* @__PURE__ */ new Set();
|
|
1623
|
-
for (const span of
|
|
1806
|
+
for (const span of resolvedCellSpans) {
|
|
1624
1807
|
const rowIndex = span.rowKey !== void 0 ? rowIndexByKey.get(span.rowKey) : span.rowIndex;
|
|
1625
1808
|
const columnIndex = columnIndexByKey.get(span.columnKey);
|
|
1626
1809
|
if (rowIndex === void 0 || columnIndex === void 0) continue;
|
|
@@ -1656,7 +1839,7 @@ function Table({
|
|
|
1656
1839
|
}
|
|
1657
1840
|
}
|
|
1658
1841
|
return lookup;
|
|
1659
|
-
}, [
|
|
1842
|
+
}, [columns, getRowKey, resolvedCellSpans, rows]);
|
|
1660
1843
|
const selectedRowKeySet = useMemo(() => new Set(rowKeys), [rowKeys]);
|
|
1661
1844
|
const selectedColumnKeySet = useMemo(() => new Set(columnKeys), [columnKeys]);
|
|
1662
1845
|
const insertedRowKeySet = useMemo(() => insertedRowKeys, [insertedRowKeys]);
|
|
@@ -1726,8 +1909,8 @@ function Table({
|
|
|
1726
1909
|
const scroll = scrollRef.current;
|
|
1727
1910
|
const rowScrollTop = fixedHeader ? scroll.top : Math.max(0, scroll.top - bodyTop);
|
|
1728
1911
|
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 };
|
|
1729
|
-
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 });
|
|
1730
|
-
}, [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]);
|
|
1912
|
+
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 });
|
|
1913
|
+
}, [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]);
|
|
1731
1914
|
const scheduleDraw = useCallback2(() => {
|
|
1732
1915
|
if (frameRef.current === null) frameRef.current = requestAnimationFrame(draw);
|
|
1733
1916
|
}, [draw]);
|
|
@@ -1764,6 +1947,8 @@ function Table({
|
|
|
1764
1947
|
if (sortDebounceRef.current !== null) clearTimeout(sortDebounceRef.current);
|
|
1765
1948
|
if (textFrameRef.current !== null) cancelAnimationFrame(textFrameRef.current);
|
|
1766
1949
|
if (toastTimerRef.current !== null) clearTimeout(toastTimerRef.current);
|
|
1950
|
+
if (verticalScrollbarVisibilityTimerRef.current !== null) clearTimeout(verticalScrollbarVisibilityTimerRef.current);
|
|
1951
|
+
if (horizontalScrollbarVisibilityTimerRef.current !== null) clearTimeout(horizontalScrollbarVisibilityTimerRef.current);
|
|
1767
1952
|
}, []);
|
|
1768
1953
|
useEffect2(() => {
|
|
1769
1954
|
const scroller = scrollerRef.current;
|
|
@@ -1815,9 +2000,204 @@ function Table({
|
|
|
1815
2000
|
scroller.addEventListener("scroll", lockScroll);
|
|
1816
2001
|
return () => scroller.removeEventListener("scroll", lockScroll);
|
|
1817
2002
|
}, [insertBusy]);
|
|
2003
|
+
const showCustomScrollbar = useCallback2((axis, persist = false) => {
|
|
2004
|
+
const showVertical = axis === "vertical" || axis === "both";
|
|
2005
|
+
const showHorizontal = axis === "horizontal" || axis === "both";
|
|
2006
|
+
if (showVertical) {
|
|
2007
|
+
verticalScrollbarRef.current?.classList.add("is-active");
|
|
2008
|
+
if (verticalScrollbarVisibilityTimerRef.current !== null) clearTimeout(verticalScrollbarVisibilityTimerRef.current);
|
|
2009
|
+
if (!persist) {
|
|
2010
|
+
verticalScrollbarVisibilityTimerRef.current = setTimeout(() => {
|
|
2011
|
+
verticalScrollbarRef.current?.classList.remove("is-active");
|
|
2012
|
+
verticalScrollbarVisibilityTimerRef.current = null;
|
|
2013
|
+
}, 900);
|
|
2014
|
+
}
|
|
2015
|
+
}
|
|
2016
|
+
if (showHorizontal) {
|
|
2017
|
+
horizontalScrollbarRef.current?.classList.add("is-active");
|
|
2018
|
+
if (horizontalScrollbarVisibilityTimerRef.current !== null) clearTimeout(horizontalScrollbarVisibilityTimerRef.current);
|
|
2019
|
+
if (!persist) {
|
|
2020
|
+
horizontalScrollbarVisibilityTimerRef.current = setTimeout(() => {
|
|
2021
|
+
horizontalScrollbarRef.current?.classList.remove("is-active");
|
|
2022
|
+
horizontalScrollbarVisibilityTimerRef.current = null;
|
|
2023
|
+
}, 900);
|
|
2024
|
+
}
|
|
2025
|
+
}
|
|
2026
|
+
}, []);
|
|
2027
|
+
const keepCustomScrollbarVisible = useCallback2((axis) => {
|
|
2028
|
+
showCustomScrollbar(axis, true);
|
|
2029
|
+
}, [showCustomScrollbar]);
|
|
2030
|
+
const releaseCustomScrollbarVisible = useCallback2((axis) => {
|
|
2031
|
+
if (axis === "vertical" && verticalScrollbarDragRef.current) return;
|
|
2032
|
+
if (axis === "horizontal" && horizontalScrollbarDragRef.current) return;
|
|
2033
|
+
showCustomScrollbar(axis);
|
|
2034
|
+
}, [showCustomScrollbar]);
|
|
2035
|
+
const updateCustomScrollbars = useCallback2(() => {
|
|
2036
|
+
const scroller = scrollerRef.current;
|
|
2037
|
+
const verticalTrack = verticalScrollbarRef.current;
|
|
2038
|
+
const verticalThumb = verticalScrollbarThumbRef.current;
|
|
2039
|
+
const horizontalTrack = horizontalScrollbarRef.current;
|
|
2040
|
+
const horizontalThumb = horizontalScrollbarThumbRef.current;
|
|
2041
|
+
if (!scroller || !verticalTrack || !verticalThumb || !horizontalTrack || !horizontalThumb) return;
|
|
2042
|
+
const maxScrollTop = scroller.scrollHeight - scroller.clientHeight;
|
|
2043
|
+
if (maxScrollTop <= 1 || renderHeight <= 0) {
|
|
2044
|
+
verticalTrack.style.display = "none";
|
|
2045
|
+
} else {
|
|
2046
|
+
const top = fixedHeader ? bodyTop : 0;
|
|
2047
|
+
const bottom = bottomSummaryHeight;
|
|
2048
|
+
const trackHeight = Math.max(0, effectiveHeight - top - bottom);
|
|
2049
|
+
if (trackHeight <= 0) {
|
|
2050
|
+
verticalTrack.style.display = "none";
|
|
2051
|
+
} else {
|
|
2052
|
+
const thumbHeight = Math.max(28, Math.round(trackHeight * (scroller.clientHeight / scroller.scrollHeight)));
|
|
2053
|
+
const maxThumbTop = Math.max(0, trackHeight - thumbHeight);
|
|
2054
|
+
const thumbTop = maxScrollTop > 0 ? Math.round(scroller.scrollTop / maxScrollTop * maxThumbTop) : 0;
|
|
2055
|
+
verticalTrack.style.display = "block";
|
|
2056
|
+
verticalTrack.style.top = `${top}px`;
|
|
2057
|
+
verticalTrack.style.bottom = `${bottom}px`;
|
|
2058
|
+
verticalThumb.style.height = `${Math.min(trackHeight, thumbHeight)}px`;
|
|
2059
|
+
verticalThumb.style.transform = `translateY(${thumbTop}px)`;
|
|
2060
|
+
}
|
|
2061
|
+
}
|
|
2062
|
+
const maxScrollLeft = scroller.scrollWidth - scroller.clientWidth;
|
|
2063
|
+
if (maxScrollLeft <= 1 || viewport.width <= 0) {
|
|
2064
|
+
horizontalTrack.style.display = "none";
|
|
2065
|
+
return;
|
|
2066
|
+
}
|
|
2067
|
+
const trackLeft = fixedWidth;
|
|
2068
|
+
const trackRight = rightFixedWidth;
|
|
2069
|
+
const trackWidth = Math.max(0, viewport.width - trackLeft - trackRight);
|
|
2070
|
+
if (trackWidth <= 0) {
|
|
2071
|
+
horizontalTrack.style.display = "none";
|
|
2072
|
+
return;
|
|
2073
|
+
}
|
|
2074
|
+
const thumbWidth = Math.max(36, Math.round(trackWidth * (scroller.clientWidth / scroller.scrollWidth)));
|
|
2075
|
+
const maxThumbLeft = Math.max(0, trackWidth - thumbWidth);
|
|
2076
|
+
const thumbLeft = maxScrollLeft > 0 ? Math.round(scroller.scrollLeft / maxScrollLeft * maxThumbLeft) : 0;
|
|
2077
|
+
horizontalTrack.style.display = "block";
|
|
2078
|
+
horizontalTrack.style.left = `${trackLeft}px`;
|
|
2079
|
+
horizontalTrack.style.right = `${trackRight}px`;
|
|
2080
|
+
horizontalTrack.style.bottom = `${bottomSummaryHeight + 2}px`;
|
|
2081
|
+
horizontalThumb.style.width = `${Math.min(trackWidth, thumbWidth)}px`;
|
|
2082
|
+
horizontalThumb.style.transform = `translateX(${thumbLeft}px)`;
|
|
2083
|
+
}, [bodyTop, bottomSummaryHeight, effectiveHeight, fixedHeader, fixedWidth, renderHeight, rightFixedWidth, viewport.width]);
|
|
2084
|
+
useLayoutEffect2(() => {
|
|
2085
|
+
updateCustomScrollbars();
|
|
2086
|
+
}, [contentHeight, contentWidth, updateCustomScrollbars, viewport.height, viewport.width]);
|
|
2087
|
+
const handleVerticalScrollbarPointerDown = useCallback2((event) => {
|
|
2088
|
+
const scroller = scrollerRef.current;
|
|
2089
|
+
const track = verticalScrollbarRef.current;
|
|
2090
|
+
const thumb = verticalScrollbarThumbRef.current;
|
|
2091
|
+
if (!scroller || !track || !thumb || event.button !== 0) return;
|
|
2092
|
+
event.preventDefault();
|
|
2093
|
+
event.currentTarget.setPointerCapture(event.pointerId);
|
|
2094
|
+
const trackRect = track.getBoundingClientRect();
|
|
2095
|
+
const thumbRect = thumb.getBoundingClientRect();
|
|
2096
|
+
const maxScrollTop = scroller.scrollHeight - scroller.clientHeight;
|
|
2097
|
+
const trackHeight = trackRect.height;
|
|
2098
|
+
const thumbHeight = thumbRect.height;
|
|
2099
|
+
showCustomScrollbar("vertical", true);
|
|
2100
|
+
if (event.target === thumb) {
|
|
2101
|
+
verticalScrollbarDragRef.current = {
|
|
2102
|
+
pointerId: event.pointerId,
|
|
2103
|
+
startY: event.clientY,
|
|
2104
|
+
startTop: thumbRect.top - trackRect.top,
|
|
2105
|
+
trackHeight,
|
|
2106
|
+
thumbHeight,
|
|
2107
|
+
maxScrollTop
|
|
2108
|
+
};
|
|
2109
|
+
return;
|
|
2110
|
+
}
|
|
2111
|
+
const nextThumbTop = Math.max(0, Math.min(trackHeight - thumbHeight, event.clientY - trackRect.top - thumbHeight / 2));
|
|
2112
|
+
const maxThumbTop = Math.max(1, trackHeight - thumbHeight);
|
|
2113
|
+
scroller.scrollTop = nextThumbTop / maxThumbTop * maxScrollTop;
|
|
2114
|
+
updateCustomScrollbars();
|
|
2115
|
+
verticalScrollbarDragRef.current = {
|
|
2116
|
+
pointerId: event.pointerId,
|
|
2117
|
+
startY: event.clientY,
|
|
2118
|
+
startTop: nextThumbTop,
|
|
2119
|
+
trackHeight,
|
|
2120
|
+
thumbHeight,
|
|
2121
|
+
maxScrollTop
|
|
2122
|
+
};
|
|
2123
|
+
}, [showCustomScrollbar, updateCustomScrollbars]);
|
|
2124
|
+
const handleVerticalScrollbarPointerMove = useCallback2((event) => {
|
|
2125
|
+
const drag = verticalScrollbarDragRef.current;
|
|
2126
|
+
const scroller = scrollerRef.current;
|
|
2127
|
+
if (!drag || !scroller || drag.pointerId !== event.pointerId) return;
|
|
2128
|
+
event.preventDefault();
|
|
2129
|
+
const maxThumbTop = Math.max(1, drag.trackHeight - drag.thumbHeight);
|
|
2130
|
+
const nextThumbTop = Math.max(0, Math.min(maxThumbTop, drag.startTop + event.clientY - drag.startY));
|
|
2131
|
+
scroller.scrollTop = nextThumbTop / maxThumbTop * drag.maxScrollTop;
|
|
2132
|
+
updateCustomScrollbars();
|
|
2133
|
+
}, [updateCustomScrollbars]);
|
|
2134
|
+
const handleVerticalScrollbarPointerUp = useCallback2((event) => {
|
|
2135
|
+
const drag = verticalScrollbarDragRef.current;
|
|
2136
|
+
if (!drag || drag.pointerId !== event.pointerId) return;
|
|
2137
|
+
if (event.currentTarget.hasPointerCapture(event.pointerId)) event.currentTarget.releasePointerCapture(event.pointerId);
|
|
2138
|
+
verticalScrollbarDragRef.current = null;
|
|
2139
|
+
showCustomScrollbar("vertical");
|
|
2140
|
+
}, [showCustomScrollbar]);
|
|
2141
|
+
const handleHorizontalScrollbarPointerDown = useCallback2((event) => {
|
|
2142
|
+
const scroller = scrollerRef.current;
|
|
2143
|
+
const track = horizontalScrollbarRef.current;
|
|
2144
|
+
const thumb = horizontalScrollbarThumbRef.current;
|
|
2145
|
+
if (!scroller || !track || !thumb || event.button !== 0) return;
|
|
2146
|
+
event.preventDefault();
|
|
2147
|
+
event.currentTarget.setPointerCapture(event.pointerId);
|
|
2148
|
+
const trackRect = track.getBoundingClientRect();
|
|
2149
|
+
const thumbRect = thumb.getBoundingClientRect();
|
|
2150
|
+
const maxScrollLeft = scroller.scrollWidth - scroller.clientWidth;
|
|
2151
|
+
const trackWidth = trackRect.width;
|
|
2152
|
+
const thumbWidth = thumbRect.width;
|
|
2153
|
+
showCustomScrollbar("horizontal", true);
|
|
2154
|
+
if (event.target === thumb) {
|
|
2155
|
+
horizontalScrollbarDragRef.current = {
|
|
2156
|
+
pointerId: event.pointerId,
|
|
2157
|
+
startX: event.clientX,
|
|
2158
|
+
startLeft: thumbRect.left - trackRect.left,
|
|
2159
|
+
trackWidth,
|
|
2160
|
+
thumbWidth,
|
|
2161
|
+
maxScrollLeft
|
|
2162
|
+
};
|
|
2163
|
+
return;
|
|
2164
|
+
}
|
|
2165
|
+
const nextThumbLeft = Math.max(0, Math.min(trackWidth - thumbWidth, event.clientX - trackRect.left - thumbWidth / 2));
|
|
2166
|
+
const maxThumbLeft = Math.max(1, trackWidth - thumbWidth);
|
|
2167
|
+
scroller.scrollLeft = nextThumbLeft / maxThumbLeft * maxScrollLeft;
|
|
2168
|
+
updateCustomScrollbars();
|
|
2169
|
+
horizontalScrollbarDragRef.current = {
|
|
2170
|
+
pointerId: event.pointerId,
|
|
2171
|
+
startX: event.clientX,
|
|
2172
|
+
startLeft: nextThumbLeft,
|
|
2173
|
+
trackWidth,
|
|
2174
|
+
thumbWidth,
|
|
2175
|
+
maxScrollLeft
|
|
2176
|
+
};
|
|
2177
|
+
}, [showCustomScrollbar, updateCustomScrollbars]);
|
|
2178
|
+
const handleHorizontalScrollbarPointerMove = useCallback2((event) => {
|
|
2179
|
+
const drag = horizontalScrollbarDragRef.current;
|
|
2180
|
+
const scroller = scrollerRef.current;
|
|
2181
|
+
if (!drag || !scroller || drag.pointerId !== event.pointerId) return;
|
|
2182
|
+
event.preventDefault();
|
|
2183
|
+
const maxThumbLeft = Math.max(1, drag.trackWidth - drag.thumbWidth);
|
|
2184
|
+
const nextThumbLeft = Math.max(0, Math.min(maxThumbLeft, drag.startLeft + event.clientX - drag.startX));
|
|
2185
|
+
scroller.scrollLeft = nextThumbLeft / maxThumbLeft * drag.maxScrollLeft;
|
|
2186
|
+
updateCustomScrollbars();
|
|
2187
|
+
}, [updateCustomScrollbars]);
|
|
2188
|
+
const handleHorizontalScrollbarPointerUp = useCallback2((event) => {
|
|
2189
|
+
const drag = horizontalScrollbarDragRef.current;
|
|
2190
|
+
if (!drag || drag.pointerId !== event.pointerId) return;
|
|
2191
|
+
if (event.currentTarget.hasPointerCapture(event.pointerId)) event.currentTarget.releasePointerCapture(event.pointerId);
|
|
2192
|
+
horizontalScrollbarDragRef.current = null;
|
|
2193
|
+
showCustomScrollbar("horizontal");
|
|
2194
|
+
}, [showCustomScrollbar]);
|
|
1818
2195
|
const handleScroll = useCallback2(() => {
|
|
1819
2196
|
const element = scrollerRef.current;
|
|
1820
2197
|
if (!element) return;
|
|
2198
|
+
const previousScroll = scrollRef.current;
|
|
2199
|
+
const scrollDeltaX = element.scrollLeft - previousScroll.left;
|
|
2200
|
+
const scrollDeltaY = element.scrollTop - previousScroll.top;
|
|
1821
2201
|
scrollRef.current = { left: element.scrollLeft, top: element.scrollTop };
|
|
1822
2202
|
const focus = selectionFocusRef.current;
|
|
1823
2203
|
if (focus && selection && !selectionRange) {
|
|
@@ -1828,7 +2208,7 @@ function Table({
|
|
|
1828
2208
|
if (!validSelection) {
|
|
1829
2209
|
focus.style.display = "none";
|
|
1830
2210
|
} else {
|
|
1831
|
-
const rawLeft = column.fixed === "left" ?
|
|
2211
|
+
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;
|
|
1832
2212
|
const rawTop = bodyTop + selection.rowIndex * rowHeight - element.scrollTop;
|
|
1833
2213
|
const span = getCellSpan(selection.rowIndex, selection.columnIndex);
|
|
1834
2214
|
const rawRight = rawLeft + getCellDisplayWidth(selection.columnIndex, span?.colSpan ?? 1);
|
|
@@ -1861,6 +2241,10 @@ function Table({
|
|
|
1861
2241
|
if (scrollingTextRef.current) scrollingTextRef.current.style.transform = `translateX(${-element.scrollLeft}px)`;
|
|
1862
2242
|
if (scrollingHeaderIconsRef.current) scrollingHeaderIconsRef.current.style.transform = `translateX(${-element.scrollLeft}px)`;
|
|
1863
2243
|
if (headerIconsRef.current) headerIconsRef.current.style.transform = `translateY(${fixedHeader ? 0 : -element.scrollTop}px)`;
|
|
2244
|
+
updateCustomScrollbars();
|
|
2245
|
+
if (scrollDeltaX !== 0 || scrollDeltaY !== 0) {
|
|
2246
|
+
showCustomScrollbar(scrollDeltaX !== 0 && scrollDeltaY !== 0 ? "both" : scrollDeltaX !== 0 ? "horizontal" : "vertical");
|
|
2247
|
+
}
|
|
1864
2248
|
if (hoveredHeaderActionRef.current) {
|
|
1865
2249
|
hoveredHeaderActionRef.current = null;
|
|
1866
2250
|
setHoveredHeaderAction(null);
|
|
@@ -1868,7 +2252,7 @@ function Table({
|
|
|
1868
2252
|
setHoveredCellTooltip(null);
|
|
1869
2253
|
scheduleDraw();
|
|
1870
2254
|
setEditing(null);
|
|
1871
|
-
}, [bodyTop, columns, renderHeight, fixedHeader, fixedWidth, getCellDisplayWidth, getCellSpan, getRowKey, metrics, rightFixedOffsets, rightFixedWidth, rowHeight, rows, scheduleDraw, selection, selectionRange, viewport.width]);
|
|
2255
|
+
}, [bodyTop, columns, renderHeight, fixedHeader, fixedWidth, getCellDisplayWidth, getCellSpan, getRowKey, getScrollableColumnLeft, metrics, rightFixedOffsets, rightFixedWidth, rowHeight, rows, scheduleDraw, selection, selectionRange, showCustomScrollbar, updateCustomScrollbars, viewport.width]);
|
|
1872
2256
|
const locateColumn = useCallback2((clientX) => {
|
|
1873
2257
|
const canvas = canvasRef.current;
|
|
1874
2258
|
if (!canvas) return -1;
|
|
@@ -1876,7 +2260,11 @@ function Table({
|
|
|
1876
2260
|
const localX = clientX - rect.left;
|
|
1877
2261
|
let columnIndex;
|
|
1878
2262
|
if (localX < fixedWidth) {
|
|
1879
|
-
columnIndex =
|
|
2263
|
+
columnIndex = columns.findIndex((column, index) => {
|
|
2264
|
+
if (column.fixed !== "left") return false;
|
|
2265
|
+
const left = leftFixedOffsets.get(index) ?? 0;
|
|
2266
|
+
return localX >= left && localX < left + metrics[index].width;
|
|
2267
|
+
});
|
|
1880
2268
|
} else if (rightFixedWidth > 0 && localX >= viewport.width - rightFixedWidth) {
|
|
1881
2269
|
columnIndex = columns.findIndex((column, index) => {
|
|
1882
2270
|
if (column.fixed !== "right") return false;
|
|
@@ -1884,31 +2272,33 @@ function Table({
|
|
|
1884
2272
|
return localX >= left && localX < left + metrics[index].width;
|
|
1885
2273
|
});
|
|
1886
2274
|
} else {
|
|
1887
|
-
|
|
2275
|
+
const scrollX = localX + scrollRef.current.left;
|
|
2276
|
+
columnIndex = columns.findIndex((column, index) => {
|
|
2277
|
+
if (column.fixed !== void 0) return false;
|
|
2278
|
+
const left = getScrollableColumnLeft(index);
|
|
2279
|
+
return scrollX >= left && scrollX < left + metrics[index].width;
|
|
2280
|
+
});
|
|
1888
2281
|
}
|
|
1889
2282
|
return columnIndex;
|
|
1890
|
-
}, [columns, fixedWidth, metrics, rightFixedOffsets, rightFixedWidth, viewport.width]);
|
|
2283
|
+
}, [columns, fixedWidth, getScrollableColumnLeft, leftFixedOffsets, metrics, rightFixedOffsets, rightFixedWidth, viewport.width]);
|
|
1891
2284
|
const getDisplayedColumnLeft = useCallback2((columnIndex) => {
|
|
1892
2285
|
const metric = metrics[columnIndex];
|
|
1893
|
-
if (columns[columnIndex].fixed === "left") return
|
|
2286
|
+
if (columns[columnIndex].fixed === "left") return leftFixedOffsets.get(columnIndex) ?? 0;
|
|
1894
2287
|
if (columns[columnIndex].fixed === "right") return viewport.width - (rightFixedOffsets.get(columnIndex) ?? 0) - metric.width;
|
|
1895
|
-
return
|
|
1896
|
-
}, [columns, metrics, rightFixedOffsets, viewport.width]);
|
|
1897
|
-
const
|
|
1898
|
-
|
|
1899
|
-
|
|
1900
|
-
|
|
1901
|
-
|
|
1902
|
-
for (let index = 0; index < columns.length; index += 1) {
|
|
1903
|
-
if (columns[index].rowSelection || columns[index].rowDragHandle || columns[index].rowNumber) continue;
|
|
1904
|
-
const nextDistance = Math.abs(localX - (getDisplayedColumnLeft(index) + metrics[index].width));
|
|
1905
|
-
if (nextDistance < distance) {
|
|
1906
|
-
distance = nextDistance;
|
|
1907
|
-
closest = index;
|
|
1908
|
-
}
|
|
2288
|
+
return getScrollableColumnLeft(columnIndex) - scrollRef.current.left;
|
|
2289
|
+
}, [columns, getScrollableColumnLeft, leftFixedOffsets, metrics, rightFixedOffsets, viewport.width]);
|
|
2290
|
+
const getHeaderLevelAtY = useCallback2((localY, headerY) => {
|
|
2291
|
+
const y = localY - headerY;
|
|
2292
|
+
for (let level = 0; level < headerRowHeights.length; level += 1) {
|
|
2293
|
+
const top = headerRowOffsets[level] ?? 0;
|
|
2294
|
+
if (y >= top && y < top + headerRowHeights[level]) return level;
|
|
1909
2295
|
}
|
|
1910
|
-
return
|
|
1911
|
-
}, [
|
|
2296
|
+
return headerDepth - 1;
|
|
2297
|
+
}, [headerDepth, headerRowHeights, headerRowOffsets]);
|
|
2298
|
+
const resizeColumn = useCallback2((columnKey, width2) => {
|
|
2299
|
+
setResizedColumnWidths((current) => current[columnKey] === width2 ? current : { ...current, [columnKey]: width2 });
|
|
2300
|
+
onColumnResize?.(columnKey, width2);
|
|
2301
|
+
}, [onColumnResize]);
|
|
1912
2302
|
const locateHeaderAction = useCallback2((clientX, columnIndex) => {
|
|
1913
2303
|
if (columnIndex < 0 || !canvasRef.current) return null;
|
|
1914
2304
|
const column = columns[columnIndex];
|
|
@@ -1943,11 +2333,70 @@ function Table({
|
|
|
1943
2333
|
const columnRight = getDisplayedColumnLeft(columnIndex) + metrics[columnIndex].width;
|
|
1944
2334
|
return localX >= columnRight - 16 && localX < columnRight - 5;
|
|
1945
2335
|
}, [columnDraggable, columns, getDisplayedColumnLeft, measureHeaderTitleWidth, metrics]);
|
|
1946
|
-
const
|
|
2336
|
+
const getHeaderCellLeft = useCallback2((cell) => {
|
|
2337
|
+
const startMetric = metrics[cell.startIndex];
|
|
2338
|
+
const fixedSide = columns[cell.startIndex]?.fixed;
|
|
2339
|
+
if (!startMetric) return 0;
|
|
2340
|
+
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;
|
|
2341
|
+
}, [columns, getScrollableColumnLeft, leftFixedOffsets, metrics, rightFixedOffsets, viewport.width]);
|
|
2342
|
+
const getHeaderCellWidth = useCallback2((cell) => cell.leaf ? metrics[cell.startIndex]?.width ?? 0 : getCellDisplayWidth(cell.startIndex, cell.endIndex - cell.startIndex + 1), [getCellDisplayWidth, metrics]);
|
|
2343
|
+
const locateHeaderResizeHit = useCallback2((clientX, clientY) => {
|
|
2344
|
+
if (!columnResizable || !canvasRef.current) return null;
|
|
2345
|
+
const rect = canvasRef.current.getBoundingClientRect();
|
|
2346
|
+
const localX = clientX - rect.left;
|
|
2347
|
+
const localY = clientY - rect.top;
|
|
2348
|
+
const headerY = fixedHeader ? 0 : -scrollRef.current.top;
|
|
2349
|
+
if (localY < headerY || localY >= headerY + headerHeight) return null;
|
|
2350
|
+
const level = getHeaderLevelAtY(localY, headerY);
|
|
2351
|
+
let closest = null;
|
|
2352
|
+
let distance = 6;
|
|
2353
|
+
for (const cell of headerCells) {
|
|
2354
|
+
if (cell.level !== level) continue;
|
|
2355
|
+
if (columns[cell.startIndex]?.rowSelection || columns[cell.startIndex]?.rowDragHandle || columns[cell.startIndex]?.rowNumber) continue;
|
|
2356
|
+
const fixedSide = columns[cell.startIndex]?.fixed;
|
|
2357
|
+
const edge = fixedSide === "right" && cell.leaf ? "left" : "right";
|
|
2358
|
+
const edgeX = getHeaderCellLeft(cell) + (edge === "right" ? getHeaderCellWidth(cell) : 0);
|
|
2359
|
+
const nextDistance = Math.abs(localX - edgeX);
|
|
2360
|
+
if (nextDistance < distance) {
|
|
2361
|
+
distance = nextDistance;
|
|
2362
|
+
closest = { cell, edge };
|
|
2363
|
+
}
|
|
2364
|
+
}
|
|
2365
|
+
return closest;
|
|
2366
|
+
}, [columnResizable, columns, fixedHeader, getHeaderCellLeft, getHeaderCellWidth, getHeaderLevelAtY, headerCells, headerHeight]);
|
|
2367
|
+
const locateHeaderResizeCell = useCallback2((clientX, clientY) => locateHeaderResizeHit(clientX, clientY)?.cell ?? null, [locateHeaderResizeHit]);
|
|
2368
|
+
const locateHeaderCellDragHandle = useCallback2((clientX, cell) => {
|
|
2369
|
+
if (!columnDraggable || !canvasRef.current) return false;
|
|
2370
|
+
if (cell.leaf) return locateHeaderDragHandle(clientX, cell.startIndex);
|
|
2371
|
+
const localX = clientX - canvasRef.current.getBoundingClientRect().left;
|
|
2372
|
+
const right = getHeaderCellLeft(cell) + getHeaderCellWidth(cell);
|
|
2373
|
+
return localX >= right - 16 && localX < right - 5;
|
|
2374
|
+
}, [columnDraggable, getHeaderCellLeft, getHeaderCellWidth, locateHeaderDragHandle]);
|
|
2375
|
+
const locateHeaderCell = useCallback2((clientX, clientY) => {
|
|
2376
|
+
const canvas = canvasRef.current;
|
|
2377
|
+
if (!canvas) return null;
|
|
2378
|
+
const rect = canvas.getBoundingClientRect();
|
|
2379
|
+
const headerY = fixedHeader ? 0 : -scrollRef.current.top;
|
|
2380
|
+
const localX = clientX - rect.left;
|
|
2381
|
+
const localY = clientY - rect.top;
|
|
2382
|
+
if (localY < headerY || localY >= headerY + headerHeight) return null;
|
|
2383
|
+
const level = getHeaderLevelAtY(localY, headerY);
|
|
2384
|
+
for (const cell of headerCells) {
|
|
2385
|
+
if (cell.level !== level) continue;
|
|
2386
|
+
const left = getHeaderCellLeft(cell);
|
|
2387
|
+
const width2 = getHeaderCellWidth(cell);
|
|
2388
|
+
if (localX >= left && localX < left + width2) return cell;
|
|
2389
|
+
}
|
|
2390
|
+
return null;
|
|
2391
|
+
}, [fixedHeader, getHeaderCellLeft, getHeaderCellWidth, getHeaderLevelAtY, headerCells, headerHeight]);
|
|
2392
|
+
const setDragGuide = useCallback2((x, top = 0) => {
|
|
1947
2393
|
const guide = dragGuideRef.current;
|
|
1948
2394
|
if (!guide) return;
|
|
1949
2395
|
guide.style.display = x === null ? "none" : "block";
|
|
1950
|
-
if (x !== null)
|
|
2396
|
+
if (x !== null) {
|
|
2397
|
+
guide.style.top = `${top}px`;
|
|
2398
|
+
guide.style.transform = `translateX(${Math.round(x)}px)`;
|
|
2399
|
+
}
|
|
1951
2400
|
}, []);
|
|
1952
2401
|
const setRowDragGuide = useCallback2((y) => {
|
|
1953
2402
|
const guide = rowDragGuideRef.current;
|
|
@@ -1965,6 +2414,15 @@ function Table({
|
|
|
1965
2414
|
useEffect2(() => {
|
|
1966
2415
|
const canvas = canvasRef.current;
|
|
1967
2416
|
if (!canvas || !columnDraggable && !rowDraggable) return;
|
|
2417
|
+
const getColumnDropIndex = (sourceIndex, targetIndex, edge) => {
|
|
2418
|
+
if (edge === "right") return targetIndex;
|
|
2419
|
+
const destinationIndex = targetIndex - (sourceIndex < targetIndex ? 1 : 0);
|
|
2420
|
+
return Math.max(0, Math.min(columns.length - 1, destinationIndex));
|
|
2421
|
+
};
|
|
2422
|
+
const getColumnDropPlacement = (sourceIndex, targetIndex, edge) => {
|
|
2423
|
+
if (edge !== "right") return "before";
|
|
2424
|
+
return targetIndex < sourceIndex ? "before" : "after";
|
|
2425
|
+
};
|
|
1968
2426
|
const getHeaderColumn = (clientX, clientY) => {
|
|
1969
2427
|
const rect = canvas.getBoundingClientRect();
|
|
1970
2428
|
const headerY = fixedHeader ? 0 : -scrollRef.current.top;
|
|
@@ -1972,22 +2430,35 @@ function Table({
|
|
|
1972
2430
|
if (localY < headerY || localY >= headerY + headerHeight) return -1;
|
|
1973
2431
|
return locateColumn(clientX);
|
|
1974
2432
|
};
|
|
2433
|
+
const getHeaderDragCell = (clientX, clientY) => {
|
|
2434
|
+
const cell = locateHeaderCell(clientX, clientY);
|
|
2435
|
+
if (!cell) return null;
|
|
2436
|
+
return locateHeaderCellDragHandle(clientX, cell) && !locateHeaderResizeCell(clientX, clientY) ? cell : null;
|
|
2437
|
+
};
|
|
1975
2438
|
const cleanupDrag = draggable({
|
|
1976
2439
|
element: canvas,
|
|
1977
2440
|
canDrag: ({ input }) => {
|
|
1978
|
-
const
|
|
1979
|
-
if (
|
|
2441
|
+
const headerDragCell = columnDraggable ? getHeaderDragCell(input.clientX, input.clientY) : null;
|
|
2442
|
+
if (headerDragCell) return true;
|
|
1980
2443
|
const columnIndex = locateColumn(input.clientX);
|
|
1981
2444
|
return rowDraggable && columnIndex >= 0 && Boolean(columns[columnIndex].rowDragHandle) && locateRow(input.clientY) >= 0;
|
|
1982
2445
|
},
|
|
1983
2446
|
getInitialData: ({ input }) => {
|
|
1984
|
-
const
|
|
1985
|
-
if (
|
|
2447
|
+
const headerDragCell = columnDraggable ? getHeaderDragCell(input.clientX, input.clientY) : null;
|
|
2448
|
+
if (!headerDragCell) {
|
|
1986
2449
|
return { type: "table-row", sourceIndex: locateRow(input.clientY) };
|
|
1987
2450
|
}
|
|
1988
|
-
const sourceIndex =
|
|
2451
|
+
const sourceIndex = headerDragCell.startIndex;
|
|
1989
2452
|
columnDragPreviewRef.current = { sourceIndex, targetIndex: sourceIndex };
|
|
1990
|
-
return {
|
|
2453
|
+
return {
|
|
2454
|
+
type: "table-column",
|
|
2455
|
+
sourceIndex,
|
|
2456
|
+
sourceKey: headerDragCell.key,
|
|
2457
|
+
parentKey: headerDragCell.parentKey,
|
|
2458
|
+
headerLevel: headerDragCell.level,
|
|
2459
|
+
headerKind: headerDragCell.leaf ? "column" : "group",
|
|
2460
|
+
fixed: columns[headerDragCell.startIndex]?.fixed
|
|
2461
|
+
};
|
|
1991
2462
|
},
|
|
1992
2463
|
onGenerateDragPreview: ({ nativeSetDragImage, source }) => {
|
|
1993
2464
|
const sourceIndex = Number(source.data.sourceIndex);
|
|
@@ -2090,31 +2561,31 @@ function Table({
|
|
|
2090
2561
|
return;
|
|
2091
2562
|
}
|
|
2092
2563
|
const targetIndex = Number(target?.targetIndex);
|
|
2093
|
-
if (!Number.isInteger(targetIndex) ||
|
|
2564
|
+
if (!Number.isInteger(targetIndex) || target?.headerKind !== source.data.headerKind || target?.parentKey !== source.data.parentKey || target?.fixed !== source.data.fixed) {
|
|
2094
2565
|
setDragGuide(null);
|
|
2095
2566
|
return;
|
|
2096
2567
|
}
|
|
2097
2568
|
const edge = target?.columnEdge;
|
|
2098
|
-
|
|
2099
|
-
if (sourceIndex < destinationIndex) destinationIndex -= 1;
|
|
2100
|
-
destinationIndex = Math.max(0, Math.min(columns.length - 1, destinationIndex));
|
|
2569
|
+
const destinationIndex = getColumnDropIndex(sourceIndex, targetIndex, edge);
|
|
2101
2570
|
columnDragPreviewRef.current = { sourceIndex, targetIndex: destinationIndex };
|
|
2102
|
-
const targetLeft =
|
|
2103
|
-
|
|
2571
|
+
const targetLeft = Number(target?.targetLeft);
|
|
2572
|
+
const targetWidth = Number(target?.targetWidth);
|
|
2573
|
+
const guideTop = headerRowOffsets[Number(source.data.headerLevel)] ?? 0;
|
|
2574
|
+
setDragGuide(edge === "right" ? targetLeft + targetWidth : targetLeft, guideTop);
|
|
2104
2575
|
},
|
|
2105
2576
|
onDrop: ({ source, location }) => {
|
|
2106
2577
|
const target = location.current.dropTargets[0]?.data;
|
|
2107
2578
|
const sourceIndex = Number(source.data.sourceIndex);
|
|
2108
2579
|
if (source.data.type === "table-row") {
|
|
2109
|
-
const
|
|
2110
|
-
let targetIndex2 =
|
|
2580
|
+
const rawTargetIndex2 = Number(target?.targetIndex);
|
|
2581
|
+
let targetIndex2 = rawTargetIndex2 + (target?.rowEdge === "bottom" ? 1 : 0);
|
|
2111
2582
|
if (sourceIndex < targetIndex2) targetIndex2 -= 1;
|
|
2112
2583
|
targetIndex2 = Math.max(0, Math.min(rows.length - 1, targetIndex2));
|
|
2113
2584
|
scrollerRef.current?.classList.add("rvg-row-drop-settling");
|
|
2114
2585
|
setRowDragPreview(null);
|
|
2115
2586
|
setRowDragGuide(null);
|
|
2116
2587
|
canvas.style.cursor = "default";
|
|
2117
|
-
if (Number.isInteger(
|
|
2588
|
+
if (Number.isInteger(rawTargetIndex2) && sourceIndex !== targetIndex2) onRowOrderChange?.(sourceIndex, targetIndex2);
|
|
2118
2589
|
requestAnimationFrame(() => {
|
|
2119
2590
|
requestAnimationFrame(() => scrollerRef.current?.classList.remove("rvg-row-drop-settling"));
|
|
2120
2591
|
});
|
|
@@ -2123,12 +2594,23 @@ function Table({
|
|
|
2123
2594
|
}, 0);
|
|
2124
2595
|
return;
|
|
2125
2596
|
}
|
|
2126
|
-
const
|
|
2597
|
+
const rawTargetIndex = Number(target?.targetIndex);
|
|
2598
|
+
const targetIndex = columnDragPreviewRef.current?.targetIndex ?? (Number.isInteger(rawTargetIndex) ? getColumnDropIndex(sourceIndex, rawTargetIndex, target?.columnEdge) : rawTargetIndex);
|
|
2127
2599
|
canvas.style.cursor = "default";
|
|
2128
2600
|
setDragGuide(null);
|
|
2129
2601
|
columnDragPreviewRef.current = null;
|
|
2130
|
-
if (Number.isInteger(targetIndex) && sourceIndex !== targetIndex &&
|
|
2131
|
-
onColumnOrderChange?.(
|
|
2602
|
+
if (Number.isInteger(rawTargetIndex) && Number.isInteger(targetIndex) && sourceIndex !== targetIndex && target?.headerKind === source.data.headerKind && target?.parentKey === source.data.parentKey && target?.fixed === source.data.fixed) {
|
|
2603
|
+
onColumnOrderChange?.(
|
|
2604
|
+
sourceIndex - utilityColumnCount,
|
|
2605
|
+
targetIndex - utilityColumnCount,
|
|
2606
|
+
{
|
|
2607
|
+
type: source.data.headerKind === "group" ? "group" : "column",
|
|
2608
|
+
sourceKey: String(source.data.sourceKey),
|
|
2609
|
+
targetKey: String(target.targetKey),
|
|
2610
|
+
parentKey: typeof source.data.parentKey === "string" ? source.data.parentKey : void 0,
|
|
2611
|
+
placement: getColumnDropPlacement(sourceIndex, rawTargetIndex, target.columnEdge)
|
|
2612
|
+
}
|
|
2613
|
+
);
|
|
2132
2614
|
}
|
|
2133
2615
|
window.setTimeout(() => {
|
|
2134
2616
|
suppressClickRef.current = false;
|
|
@@ -2146,24 +2628,38 @@ function Table({
|
|
|
2146
2628
|
const rowEdge = input.clientY - canvas.getBoundingClientRect().top < rowTop + rowHeight / 2 ? "top" : "bottom";
|
|
2147
2629
|
return attachClosestEdge({ targetIndex: targetIndex2, rowEdge }, { element, input, allowedEdges: ["top", "bottom"] });
|
|
2148
2630
|
}
|
|
2149
|
-
|
|
2150
|
-
if (
|
|
2631
|
+
const targetCell = locateHeaderCell(input.clientX, input.clientY);
|
|
2632
|
+
if (!targetCell) return { targetIndex: -1 };
|
|
2633
|
+
let targetIndex = targetCell.startIndex;
|
|
2634
|
+
const targetFixed = columns[targetCell.startIndex]?.fixed;
|
|
2635
|
+
const headerKind = targetCell.leaf ? "column" : "group";
|
|
2636
|
+
if (source.data.headerKind !== headerKind || source.data.parentKey !== targetCell.parentKey || source.data.fixed !== targetFixed) return { targetIndex: -1 };
|
|
2151
2637
|
if (columns[targetIndex].rowSelection || columns[targetIndex].rowDragHandle || columns[targetIndex].rowNumber) {
|
|
2152
2638
|
const fixedSide = columns[Number(source.data.sourceIndex)]?.fixed;
|
|
2153
2639
|
targetIndex = columns.findIndex((column, index) => index > targetIndex && column.fixed === fixedSide && !column.rowSelection && !column.rowDragHandle && !column.rowNumber);
|
|
2154
2640
|
if (targetIndex < 0) return { targetIndex: -1 };
|
|
2155
2641
|
return { targetIndex, columnEdge: "left" };
|
|
2156
2642
|
}
|
|
2157
|
-
const left =
|
|
2158
|
-
const
|
|
2159
|
-
|
|
2643
|
+
const left = getHeaderCellLeft(targetCell);
|
|
2644
|
+
const targetWidth = targetCell.leaf ? metrics[targetIndex].width : getHeaderCellWidth(targetCell);
|
|
2645
|
+
const columnEdge = input.clientX - canvas.getBoundingClientRect().left < left + targetWidth / 2 ? "left" : "right";
|
|
2646
|
+
return attachClosestEdge({
|
|
2647
|
+
targetIndex,
|
|
2648
|
+
targetKey: targetCell.key,
|
|
2649
|
+
parentKey: targetCell.parentKey,
|
|
2650
|
+
headerKind,
|
|
2651
|
+
fixed: targetFixed,
|
|
2652
|
+
targetLeft: left,
|
|
2653
|
+
targetWidth,
|
|
2654
|
+
columnEdge
|
|
2655
|
+
}, { element, input, allowedEdges: ["left", "right"] });
|
|
2160
2656
|
}
|
|
2161
2657
|
});
|
|
2162
2658
|
return () => {
|
|
2163
2659
|
cleanupDrag();
|
|
2164
2660
|
cleanupDrop();
|
|
2165
2661
|
};
|
|
2166
|
-
}, [bodyTop, columnDraggable, columns, fixedHeader, getCellLabel, getDisplayedColumnLeft, getRowKey, headerHeight, locateColumn,
|
|
2662
|
+
}, [bodyTop, columnDraggable, columns, fixedHeader, getCellLabel, getDisplayedColumnLeft, getHeaderCellWidth, getRowKey, headerHeight, locateColumn, locateHeaderCell, locateHeaderCellDragHandle, locateHeaderResizeCell, locateRow, metrics, onColumnOrderChange, onRowOrderChange, rowDraggable, rowHeight, rows, selectedRowKeySet, setDragGuide, setRowDragGuide, utilityColumnCount]);
|
|
2167
2663
|
const locateCell = useCallback2((clientX, clientY) => {
|
|
2168
2664
|
const canvas = canvasRef.current;
|
|
2169
2665
|
if (!canvas) return null;
|
|
@@ -2270,8 +2766,9 @@ function Table({
|
|
|
2270
2766
|
const horizontalFocusGap = 0;
|
|
2271
2767
|
const verticalFocusGap = 0;
|
|
2272
2768
|
if (column.fixed === void 0) {
|
|
2273
|
-
|
|
2274
|
-
if (
|
|
2769
|
+
const cellLeft = getScrollableColumnLeft(cell.columnIndex);
|
|
2770
|
+
if (cellLeft - nextLeft < fixedWidth) nextLeft = Math.max(0, cellLeft - fixedWidth - horizontalFocusGap);
|
|
2771
|
+
if (cellLeft + cellWidth - nextLeft > viewport.width - rightFixedWidth) nextLeft = cellLeft + cellWidth - viewport.width + rightFixedWidth + horizontalFocusGap;
|
|
2275
2772
|
}
|
|
2276
2773
|
const rowTop = (fixedHeader ? 0 : bodyTop) + cell.rowIndex * rowHeight;
|
|
2277
2774
|
const rowBottom = rowTop + rowHeight * rowSpan;
|
|
@@ -2284,7 +2781,7 @@ function Table({
|
|
|
2284
2781
|
scroller.scrollTop = nextTop;
|
|
2285
2782
|
scrollRef.current = { left: scroller.scrollLeft, top: scroller.scrollTop };
|
|
2286
2783
|
scheduleDraw();
|
|
2287
|
-
}, [bodyTop, columns, fixedHeader, fixedWidth, getCellDisplayWidth, getCellSpan, metrics, rightFixedWidth, rowHeight, scheduleDraw, viewport.width]);
|
|
2784
|
+
}, [bodyTop, columns, fixedHeader, fixedWidth, getCellDisplayWidth, getCellSpan, getScrollableColumnLeft, metrics, rightFixedWidth, rowHeight, scheduleDraw, viewport.width]);
|
|
2288
2785
|
const beginEdit = useCallback2((cell) => {
|
|
2289
2786
|
const column = columns[cell.columnIndex];
|
|
2290
2787
|
if (!column?.editable || column.dataIndex === void 0) return;
|
|
@@ -2305,8 +2802,9 @@ function Table({
|
|
|
2305
2802
|
const horizontalFocusGap = 0;
|
|
2306
2803
|
const verticalFocusGap = 0;
|
|
2307
2804
|
if (column.fixed === void 0) {
|
|
2308
|
-
|
|
2309
|
-
if (
|
|
2805
|
+
const cellLeft = getScrollableColumnLeft(cell.columnIndex);
|
|
2806
|
+
if (cellLeft - nextLeft < fixedWidth) nextLeft = Math.max(0, cellLeft - fixedWidth - horizontalFocusGap);
|
|
2807
|
+
if (cellLeft + cellWidth - nextLeft > viewport.width - rightFixedWidth) nextLeft = cellLeft + cellWidth - viewport.width + rightFixedWidth + horizontalFocusGap;
|
|
2310
2808
|
}
|
|
2311
2809
|
const rowTop = (fixedHeader ? 0 : bodyTop) + cell.rowIndex * rowHeight;
|
|
2312
2810
|
const rowBottom = rowTop + rowHeight * rowSpan;
|
|
@@ -2328,7 +2826,7 @@ function Table({
|
|
|
2328
2826
|
return;
|
|
2329
2827
|
}
|
|
2330
2828
|
setEditing(cell);
|
|
2331
|
-
}, [columns, fixedHeader, fixedWidth, getCellDisplayWidth, getCellSpan, metrics, rightFixedWidth, rowHeight, rows, scheduleDraw, viewport.width]);
|
|
2829
|
+
}, [columns, fixedHeader, fixedWidth, getCellDisplayWidth, getCellSpan, getScrollableColumnLeft, metrics, rightFixedWidth, rowHeight, rows, scheduleDraw, viewport.width]);
|
|
2332
2830
|
const getCellEditKey = (cell) => `${typeof cell.rowKey}:${String(cell.rowKey)}\0${cell.columnKey}`;
|
|
2333
2831
|
const markCellEdited = (cell, previousValue, nextValue) => {
|
|
2334
2832
|
const key = getCellEditKey(cell);
|
|
@@ -2459,10 +2957,13 @@ function Table({
|
|
|
2459
2957
|
const visibleRowHeight = fixedHeader ? renderHeight - bodyTop : renderHeight;
|
|
2460
2958
|
if (rowTop < scroller.scrollTop) scroller.scrollTop = rowTop;
|
|
2461
2959
|
if (rowBottom > scroller.scrollTop + visibleRowHeight) scroller.scrollTop = rowBottom - visibleRowHeight;
|
|
2462
|
-
if (columns[columnIndex].fixed === void 0
|
|
2463
|
-
|
|
2960
|
+
if (columns[columnIndex].fixed === void 0) {
|
|
2961
|
+
const cellLeft = getScrollableColumnLeft(columnIndex);
|
|
2962
|
+
if (cellLeft < scroller.scrollLeft + fixedWidth) scroller.scrollLeft = Math.max(0, cellLeft - fixedWidth);
|
|
2963
|
+
if (cellLeft + cellWidth > scroller.scrollLeft + viewport.width - rightFixedWidth) scroller.scrollLeft = cellLeft + cellWidth - viewport.width + rightFixedWidth;
|
|
2964
|
+
}
|
|
2464
2965
|
}
|
|
2465
|
-
}, [beginEdit, bodyTop, columns, editing, renderHeight, fixedHeader, fixedWidth, getCellDisplayWidth, getCellSpan, getRowKey, metrics, rightFixedWidth, rowHeight, rows, selection, setSelection, viewport.width]);
|
|
2966
|
+
}, [beginEdit, bodyTop, columns, editing, renderHeight, fixedHeader, fixedWidth, getCellDisplayWidth, getCellSpan, getRowKey, getScrollableColumnLeft, metrics, rightFixedWidth, rowHeight, rows, selection, setSelection, viewport.width]);
|
|
2466
2967
|
const editorStyle = useMemo(() => {
|
|
2467
2968
|
if (!editing) return void 0;
|
|
2468
2969
|
const metric = metrics[editing.columnIndex];
|
|
@@ -2470,43 +2971,83 @@ function Table({
|
|
|
2470
2971
|
const span = getCellSpan(editing.rowIndex, editing.columnIndex);
|
|
2471
2972
|
const cellWidth = getCellDisplayWidth(editing.columnIndex, span?.colSpan ?? 1);
|
|
2472
2973
|
const cellHeight = rowHeight * (span?.rowSpan ?? 1);
|
|
2473
|
-
const cellLeft = fixedSide === "left" ?
|
|
2974
|
+
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;
|
|
2474
2975
|
return {
|
|
2475
2976
|
left: (fixedSide ? cellLeft : Math.max(cellLeft, fixedWidth)) + 1,
|
|
2476
2977
|
top: Math.max(fixedHeader ? bodyTop : 0, bodyTop + editing.rowIndex * rowHeight - scrollRef.current.top) + 1,
|
|
2477
2978
|
width: cellWidth - 2,
|
|
2478
2979
|
height: cellHeight - 2
|
|
2479
2980
|
};
|
|
2480
|
-
}, [bodyTop, columns, editing, fixedHeader, fixedWidth, getCellDisplayWidth, getCellSpan, metrics, rightFixedOffsets, rowHeight, viewport.width]);
|
|
2981
|
+
}, [bodyTop, columns, editing, fixedHeader, fixedWidth, getCellDisplayWidth, getCellSpan, getScrollableColumnLeft, leftFixedOffsets, metrics, rightFixedOffsets, rowHeight, viewport.width]);
|
|
2481
2982
|
const handleColumnPointerDown = useCallback2((event) => {
|
|
2482
2983
|
const rect = event.currentTarget.getBoundingClientRect();
|
|
2483
2984
|
const localY = event.clientY - rect.top;
|
|
2484
2985
|
const headerY = fixedHeader ? 0 : -scrollRef.current.top;
|
|
2485
2986
|
if (localY < headerY || localY >= headerY + headerHeight) return;
|
|
2486
|
-
const
|
|
2487
|
-
if (
|
|
2987
|
+
const resizeHit = locateHeaderResizeHit(event.clientX, event.clientY);
|
|
2988
|
+
if (resizeHit) {
|
|
2989
|
+
const { cell: resizeCell, edge: resizeEdge } = resizeHit;
|
|
2488
2990
|
event.preventDefault();
|
|
2489
2991
|
event.currentTarget.setPointerCapture(event.pointerId);
|
|
2490
|
-
|
|
2992
|
+
const resizeIndex = resizeCell.endIndex;
|
|
2993
|
+
const resizeIndices = resizeCell.leaf ? [resizeIndex] : Array.from({ length: resizeCell.endIndex - resizeCell.startIndex + 1 }, (_, index) => resizeCell.startIndex + index);
|
|
2994
|
+
const startWidths = resizeIndices.map((index) => metrics[index].width);
|
|
2995
|
+
const startGuideX = getHeaderCellLeft(resizeCell) + (resizeEdge === "right" ? getHeaderCellWidth(resizeCell) : 0);
|
|
2996
|
+
columnDragRef.current = {
|
|
2997
|
+
type: "resize",
|
|
2998
|
+
columnIndex: resizeIndex,
|
|
2999
|
+
startX: event.clientX,
|
|
3000
|
+
startWidth: startWidths.reduce((total, width2) => total + width2, 0),
|
|
3001
|
+
startGuideX,
|
|
3002
|
+
guideTop: headerRowOffsets[resizeCell.level] ?? 0,
|
|
3003
|
+
resizeEdge,
|
|
3004
|
+
resizeIndices,
|
|
3005
|
+
startWidths
|
|
3006
|
+
};
|
|
2491
3007
|
suppressClickRef.current = true;
|
|
2492
|
-
|
|
3008
|
+
setResizeGuideTop(headerRowOffsets[resizeCell.level] ?? 0);
|
|
3009
|
+
setResizeGuideX(startGuideX);
|
|
2493
3010
|
return;
|
|
2494
3011
|
}
|
|
2495
|
-
}, [fixedHeader,
|
|
3012
|
+
}, [fixedHeader, getHeaderCellLeft, getHeaderCellWidth, headerHeight, headerRowOffsets, locateHeaderResizeHit, metrics]);
|
|
2496
3013
|
const handleColumnPointerMove = useCallback2((event) => {
|
|
2497
3014
|
const drag = columnDragRef.current;
|
|
2498
3015
|
if (!drag) return;
|
|
2499
|
-
const
|
|
2500
|
-
|
|
2501
|
-
|
|
2502
|
-
|
|
3016
|
+
const column = columns[drag.columnIndex];
|
|
3017
|
+
const minWidth = getMinimumColumnWidth(column, columnDraggable);
|
|
3018
|
+
const pointerDelta = Math.round(event.clientX - drag.startX);
|
|
3019
|
+
const delta = drag.resizeEdge === "left" ? -pointerDelta : pointerDelta;
|
|
3020
|
+
if (drag.resizeIndices.length > 1) {
|
|
3021
|
+
const minWidths = drag.resizeIndices.map((index) => getMinimumColumnWidth(columns[index], columnDraggable));
|
|
3022
|
+
const minTotalWidth = minWidths.reduce((total, width3) => total + width3, 0);
|
|
3023
|
+
const targetTotalWidth = Math.max(minTotalWidth, drag.startWidth + delta);
|
|
3024
|
+
const ratio = drag.startWidth > 0 ? targetTotalWidth / drag.startWidth : 1;
|
|
3025
|
+
const nextWidths = drag.startWidths.map((width3, index) => Math.max(minWidths[index], Math.round(width3 * ratio)));
|
|
3026
|
+
let remainder = targetTotalWidth - nextWidths.reduce((total, width3) => total + width3, 0);
|
|
3027
|
+
for (let index = nextWidths.length - 1; index >= 0 && remainder !== 0; index -= 1) {
|
|
3028
|
+
const next = nextWidths[index] + (remainder > 0 ? 1 : -1);
|
|
3029
|
+
if (next < minWidths[index]) continue;
|
|
3030
|
+
nextWidths[index] = next;
|
|
3031
|
+
remainder += remainder > 0 ? -1 : 1;
|
|
3032
|
+
}
|
|
3033
|
+
drag.resizeIndices.forEach((columnIndex, index) => {
|
|
3034
|
+
resizeColumn(columns[columnIndex].key, nextWidths[index]);
|
|
3035
|
+
});
|
|
3036
|
+
setResizeGuideX(drag.startGuideX + (drag.resizeEdge === "left" ? drag.startWidth - targetTotalWidth : targetTotalWidth - drag.startWidth));
|
|
3037
|
+
return;
|
|
3038
|
+
}
|
|
3039
|
+
const width2 = Math.max(minWidth, Math.round(drag.startWidth + delta));
|
|
3040
|
+
resizeColumn(column.key, width2);
|
|
3041
|
+
setResizeGuideX(drag.startGuideX + (drag.resizeEdge === "left" ? drag.startWidth - width2 : width2 - drag.startWidth));
|
|
3042
|
+
}, [columnDraggable, columns, resizeColumn]);
|
|
2503
3043
|
const handleColumnPointerUp = useCallback2((event) => {
|
|
2504
3044
|
const drag = columnDragRef.current;
|
|
2505
3045
|
if (!drag) return;
|
|
2506
3046
|
if (event.currentTarget.hasPointerCapture(event.pointerId)) event.currentTarget.releasePointerCapture(event.pointerId);
|
|
2507
3047
|
columnDragRef.current = null;
|
|
2508
|
-
|
|
2509
|
-
|
|
3048
|
+
setResizeGuideX(null);
|
|
3049
|
+
setResizeGuideTop(0);
|
|
3050
|
+
}, []);
|
|
2510
3051
|
const queueSortStateChange = useCallback2((next) => {
|
|
2511
3052
|
setDisplaySortState(next);
|
|
2512
3053
|
if (sortDebounceRef.current !== null) clearTimeout(sortDebounceRef.current);
|
|
@@ -2518,7 +3059,7 @@ function Table({
|
|
|
2518
3059
|
const renderHeaderIcons = (columnIndex, left) => {
|
|
2519
3060
|
const column = columns[columnIndex];
|
|
2520
3061
|
if (column.rowSelection || column.rowDragHandle || column.rowNumber) return null;
|
|
2521
|
-
const top = (
|
|
3062
|
+
const top = headerLeafTop + (headerLeafHeight - 14) / 2;
|
|
2522
3063
|
const visibleActions = getVisibleHeaderActions(column, metrics[columnIndex].width, columnDraggable, measureHeaderTitleWidth(columnIndex));
|
|
2523
3064
|
let right = left + metrics[columnIndex].width - (visibleActions.drag ? 18 : 4);
|
|
2524
3065
|
const icons = [];
|
|
@@ -2544,44 +3085,87 @@ function Table({
|
|
|
2544
3085
|
}
|
|
2545
3086
|
return icons;
|
|
2546
3087
|
};
|
|
2547
|
-
const
|
|
2548
|
-
|
|
2549
|
-
const { columnIndex, action } = visibleHeaderTooltip;
|
|
2550
|
-
const column = columns[columnIndex];
|
|
3088
|
+
const createHeaderTooltip = useCallback2((action, columnIndex, cell) => {
|
|
3089
|
+
const column = cell?.column ?? columns[columnIndex];
|
|
2551
3090
|
if (!column) return null;
|
|
2552
|
-
const
|
|
2553
|
-
const
|
|
2554
|
-
|
|
2555
|
-
|
|
2556
|
-
|
|
3091
|
+
const cellLeft = cell ? getHeaderCellLeft(cell) : getDisplayedColumnLeft(columnIndex);
|
|
3092
|
+
const cellWidth = cell ? getHeaderCellWidth(cell) : metrics[columnIndex]?.width ?? 0;
|
|
3093
|
+
const level = cell?.level ?? headerDepth - 1;
|
|
3094
|
+
const rowTop = (fixedHeader ? 0 : -scrollPosition.top) + (headerRowOffsets[level] ?? 0);
|
|
3095
|
+
const rowHeight2 = headerRowHeights[level] ?? baseHeaderHeight;
|
|
3096
|
+
let anchorX = cellLeft + cellWidth / 2;
|
|
3097
|
+
let anchorLeft = cellLeft;
|
|
3098
|
+
let label = column.title;
|
|
2557
3099
|
if (action === "title") {
|
|
2558
|
-
const
|
|
2559
|
-
const
|
|
2560
|
-
|
|
2561
|
-
const
|
|
2562
|
-
const
|
|
2563
|
-
|
|
2564
|
-
|
|
2565
|
-
|
|
3100
|
+
const canvas = canvasRef.current;
|
|
3101
|
+
const context = canvas?.getContext("2d");
|
|
3102
|
+
if (context) context.font = "600 13px Inter, ui-sans-serif, system-ui, sans-serif";
|
|
3103
|
+
const titleWidth = context?.measureText(column.title).width ?? column.title.length * 13;
|
|
3104
|
+
const visibleActions = cell?.leaf || !cell ? getVisibleHeaderActions(column, cellWidth, columnDraggable, titleWidth) : null;
|
|
3105
|
+
const actionWidth = visibleActions ? (Number(visibleActions.drag) + Number(visibleActions.filter) + Number(visibleActions.sort)) * 18 : columnDraggable ? 18 : 0;
|
|
3106
|
+
const contentWidth2 = Math.max(0, cellWidth - actionWidth);
|
|
3107
|
+
const maxTextWidth = Math.max(0, contentWidth2 - 20);
|
|
3108
|
+
const textWidth = Math.min(titleWidth, maxTextWidth);
|
|
3109
|
+
if (column.align === "right") {
|
|
3110
|
+
anchorX = cellLeft + contentWidth2 - 10;
|
|
3111
|
+
anchorLeft = anchorX - textWidth;
|
|
3112
|
+
} else if (column.align === "center") {
|
|
3113
|
+
anchorX = cellLeft + contentWidth2 / 2 + textWidth / 2;
|
|
3114
|
+
anchorLeft = cellLeft + contentWidth2 / 2 - textWidth / 2;
|
|
3115
|
+
} else {
|
|
3116
|
+
anchorLeft = cellLeft + 10;
|
|
3117
|
+
anchorX = anchorLeft + textWidth;
|
|
3118
|
+
}
|
|
2566
3119
|
} else if (action === "drag") {
|
|
2567
|
-
anchorX =
|
|
3120
|
+
anchorX = cellLeft + cellWidth;
|
|
2568
3121
|
anchorLeft = anchorX;
|
|
2569
3122
|
label = labels.dragColumn;
|
|
2570
3123
|
} else if (action === "sort") {
|
|
2571
3124
|
const direction = displaySortState?.columnKey === column.key ? displaySortState.direction : null;
|
|
2572
3125
|
label = direction === "asc" ? labels.sortAsc : direction === "desc" ? labels.sortDesc : labels.sortBoth;
|
|
2573
|
-
|
|
2574
|
-
|
|
3126
|
+
anchorX = cellLeft + cellWidth - 22;
|
|
3127
|
+
anchorLeft = anchorX;
|
|
3128
|
+
} else if (action === "filter") {
|
|
2575
3129
|
const value = filterValues[column.key];
|
|
2576
3130
|
label = value ? labels.filterWithValue(value) : labels.filter;
|
|
3131
|
+
anchorX = cellLeft + cellWidth - 40;
|
|
3132
|
+
anchorLeft = anchorX;
|
|
2577
3133
|
}
|
|
2578
3134
|
const placement = anchorX + 328 > viewport.width ? "left" : "right";
|
|
2579
3135
|
return {
|
|
3136
|
+
key: `${action}:${cell ? `cell:${cell.key}:${cell.level}` : `column:${column.key}`}:${Math.round(anchorX)}:${Math.round(rowTop)}`,
|
|
3137
|
+
columnIndex,
|
|
3138
|
+
action,
|
|
2580
3139
|
left: placement === "right" ? anchorX + 8 : Math.max(8, anchorLeft - 8),
|
|
2581
|
-
top:
|
|
3140
|
+
top: rowTop + rowHeight2 / 2,
|
|
2582
3141
|
label,
|
|
2583
3142
|
placement
|
|
2584
3143
|
};
|
|
3144
|
+
}, [baseHeaderHeight, columnDraggable, columns, displaySortState, filterValues, fixedHeader, getDisplayedColumnLeft, getHeaderCellLeft, getHeaderCellWidth, headerDepth, headerRowHeights, headerRowOffsets, labels, metrics, scrollPosition.top, viewport.width]);
|
|
3145
|
+
const isPointerOnHeaderTitle = useCallback2((clientX, clientY, cell) => {
|
|
3146
|
+
const canvas = canvasRef.current;
|
|
3147
|
+
if (!canvas) return false;
|
|
3148
|
+
const rect = canvas.getBoundingClientRect();
|
|
3149
|
+
const localX = clientX - rect.left;
|
|
3150
|
+
const localY = clientY - rect.top;
|
|
3151
|
+
const column = cell.column;
|
|
3152
|
+
const cellLeft = getHeaderCellLeft(cell);
|
|
3153
|
+
const cellWidth = getHeaderCellWidth(cell);
|
|
3154
|
+
const rowTop = (fixedHeader ? 0 : -scrollRef.current.top) + (headerRowOffsets[cell.level] ?? 0);
|
|
3155
|
+
const context = canvas.getContext("2d");
|
|
3156
|
+
if (context) context.font = "600 13px Inter, ui-sans-serif, system-ui, sans-serif";
|
|
3157
|
+
const titleWidth = context?.measureText(column.title).width ?? column.title.length * 13;
|
|
3158
|
+
const visibleActions = cell.leaf ? getVisibleHeaderActions(column, cellWidth, columnDraggable, titleWidth) : null;
|
|
3159
|
+
const actionWidth = visibleActions ? (Number(visibleActions.drag) + Number(visibleActions.filter) + Number(visibleActions.sort)) * 18 : columnDraggable ? 18 : 0;
|
|
3160
|
+
const contentWidth2 = Math.max(0, cellWidth - actionWidth);
|
|
3161
|
+
const textWidth = Math.min(titleWidth, Math.max(0, contentWidth2 - 20));
|
|
3162
|
+
const textLeft = column.align === "right" ? cellLeft + contentWidth2 - 10 - textWidth : column.align === "center" ? cellLeft + contentWidth2 / 2 - textWidth / 2 : cellLeft + 10;
|
|
3163
|
+
const textTop = rowTop + 8;
|
|
3164
|
+
return localX >= textLeft - 4 && localX <= textLeft + textWidth + 4 && localY >= textTop - 2 && localY <= textTop + 18;
|
|
3165
|
+
}, [columnDraggable, fixedHeader, getHeaderCellLeft, getHeaderCellWidth, headerRowOffsets]);
|
|
3166
|
+
const headerTooltip = (() => {
|
|
3167
|
+
if (!visibleHeaderTooltip) return null;
|
|
3168
|
+
return visibleHeaderTooltip;
|
|
2585
3169
|
})();
|
|
2586
3170
|
const getRangeBounds = () => selectionRange ? {
|
|
2587
3171
|
rowStart: Math.min(selectionRange.anchor.rowIndex, selectionRange.focus.rowIndex),
|
|
@@ -2640,7 +3224,7 @@ function Table({
|
|
|
2640
3224
|
}, 1800);
|
|
2641
3225
|
};
|
|
2642
3226
|
const showHeaderTooltipAfterDelay = (next) => {
|
|
2643
|
-
const nextKey = next ?
|
|
3227
|
+
const nextKey = next ? next.key : "";
|
|
2644
3228
|
if (nextKey && pendingHeaderTooltipKeyRef.current === nextKey) return;
|
|
2645
3229
|
pendingHeaderTooltipKeyRef.current = nextKey;
|
|
2646
3230
|
if (headerTooltipTimerRef.current !== null) clearTimeout(headerTooltipTimerRef.current);
|
|
@@ -2888,12 +3472,14 @@ function Table({
|
|
|
2888
3472
|
const renderHeaderTitle = (columnIndex, left) => {
|
|
2889
3473
|
const column = columns[columnIndex];
|
|
2890
3474
|
const currentScrollTop = scrollRef.current.top;
|
|
3475
|
+
const headerTop = fixedHeader ? 0 : -currentScrollTop;
|
|
3476
|
+
const leafTop = headerTop + headerLeafTop;
|
|
2891
3477
|
if (column.rowNumber) {
|
|
2892
3478
|
return /* @__PURE__ */ jsx7(
|
|
2893
3479
|
"div",
|
|
2894
3480
|
{
|
|
2895
3481
|
className: "rvg-header-title",
|
|
2896
|
-
style: { left, top:
|
|
3482
|
+
style: { left, top: headerTop, width: metrics[columnIndex].width, height: headerHeight, justifyContent: "center", padding: 0, textAlign: "center" },
|
|
2897
3483
|
children: /* @__PURE__ */ jsx7("span", { children: column.title })
|
|
2898
3484
|
},
|
|
2899
3485
|
column.key
|
|
@@ -2907,14 +3493,102 @@ function Table({
|
|
|
2907
3493
|
"div",
|
|
2908
3494
|
{
|
|
2909
3495
|
className: "rvg-header-selection-icon",
|
|
2910
|
-
style: { left, top:
|
|
3496
|
+
style: { left, top: headerTop, width: metrics[columnIndex].width, height: headerHeight },
|
|
2911
3497
|
children: /* @__PURE__ */ jsx7(SelectionIcon, { checked: allSelected, indeterminate })
|
|
2912
3498
|
},
|
|
2913
3499
|
column.key
|
|
2914
3500
|
);
|
|
2915
3501
|
}
|
|
3502
|
+
if (column.renderHeader) {
|
|
3503
|
+
const visibleActions = getVisibleHeaderActions(column, metrics[columnIndex].width, columnDraggable, measureHeaderTitleWidth(columnIndex));
|
|
3504
|
+
const actionWidth = (Number(visibleActions.drag) + Number(visibleActions.filter) + Number(visibleActions.sort)) * 18;
|
|
3505
|
+
return /* @__PURE__ */ jsx7(
|
|
3506
|
+
"div",
|
|
3507
|
+
{
|
|
3508
|
+
className: "rvg-header-title is-custom",
|
|
3509
|
+
"data-level": headerDepth - 1,
|
|
3510
|
+
"data-measure-key": column.key,
|
|
3511
|
+
style: {
|
|
3512
|
+
left,
|
|
3513
|
+
top: leafTop,
|
|
3514
|
+
width: Math.max(0, metrics[columnIndex].width - actionWidth),
|
|
3515
|
+
height: headerLeafHeight,
|
|
3516
|
+
justifyContent: column.align === "right" ? "flex-end" : column.align === "center" ? "center" : "flex-start",
|
|
3517
|
+
textAlign: column.align === "right" ? "right" : column.align === "center" ? "center" : "left"
|
|
3518
|
+
},
|
|
3519
|
+
children: /* @__PURE__ */ jsx7(
|
|
3520
|
+
"span",
|
|
3521
|
+
{
|
|
3522
|
+
ref: (node) => {
|
|
3523
|
+
if (node) {
|
|
3524
|
+
customHeaderContentRefs.current.set(column.key, node);
|
|
3525
|
+
requestAnimationFrame(measureCustomHeaderHeight);
|
|
3526
|
+
} else customHeaderContentRefs.current.delete(column.key);
|
|
3527
|
+
},
|
|
3528
|
+
children: column.renderHeader(column)
|
|
3529
|
+
}
|
|
3530
|
+
)
|
|
3531
|
+
},
|
|
3532
|
+
column.key
|
|
3533
|
+
);
|
|
3534
|
+
}
|
|
2916
3535
|
return null;
|
|
2917
3536
|
};
|
|
3537
|
+
const renderGroupedHeaderCell = (cell) => {
|
|
3538
|
+
const startMetric = metrics[cell.startIndex];
|
|
3539
|
+
const endMetric = metrics[cell.endIndex];
|
|
3540
|
+
if (!startMetric || !endMetric) return null;
|
|
3541
|
+
const column = cell.column;
|
|
3542
|
+
const fixedSide = columns[cell.startIndex]?.fixed;
|
|
3543
|
+
const currentScrollTop = scrollRef.current.top;
|
|
3544
|
+
const rowHeightForLevel = headerRowHeights[cell.level] ?? baseHeaderHeight;
|
|
3545
|
+
const headerTop = (fixedHeader ? 0 : -currentScrollTop) + (headerRowOffsets[cell.level] ?? 0);
|
|
3546
|
+
const left = fixedSide === "left" ? leftFixedOffsets.get(cell.startIndex) ?? 0 : fixedSide === "right" ? viewport.width - (rightFixedOffsets.get(cell.startIndex) ?? 0) - startMetric.width : getScrollableColumnLeft(cell.startIndex);
|
|
3547
|
+
const width2 = cell.leaf ? startMetric.width : getCellDisplayWidth(cell.startIndex, cell.endIndex - cell.startIndex + 1);
|
|
3548
|
+
const visibleActions = cell.leaf ? getVisibleHeaderActions(column, startMetric.width, columnDraggable, measureHeaderTitleWidth(cell.startIndex)) : null;
|
|
3549
|
+
const actionWidth = visibleActions ? (Number(visibleActions.drag) + Number(visibleActions.filter) + Number(visibleActions.sort)) * 18 : columnDraggable && !cell.leaf ? 18 : 0;
|
|
3550
|
+
return /* @__PURE__ */ jsxs7(
|
|
3551
|
+
"div",
|
|
3552
|
+
{
|
|
3553
|
+
className: `rvg-header-title rvg-header-group${column.renderHeader ? " is-custom" : ""}${cell.leaf ? " is-leaf" : ""}`,
|
|
3554
|
+
"data-level": cell.level,
|
|
3555
|
+
"data-measure-key": `group:${cell.key}:${cell.level}`,
|
|
3556
|
+
style: {
|
|
3557
|
+
left,
|
|
3558
|
+
top: headerTop,
|
|
3559
|
+
width: width2,
|
|
3560
|
+
height: rowHeightForLevel,
|
|
3561
|
+
paddingRight: cell.leaf ? actionWidth + 10 : 10,
|
|
3562
|
+
justifyContent: column.align === "right" ? "flex-end" : column.align === "center" ? "center" : "flex-start",
|
|
3563
|
+
textAlign: column.align === "right" ? "right" : column.align === "center" ? "center" : "left"
|
|
3564
|
+
},
|
|
3565
|
+
children: [
|
|
3566
|
+
/* @__PURE__ */ jsx7(
|
|
3567
|
+
"span",
|
|
3568
|
+
{
|
|
3569
|
+
ref: (node) => {
|
|
3570
|
+
if (!column.renderHeader) return;
|
|
3571
|
+
const key = `group:${cell.key}:${cell.level}`;
|
|
3572
|
+
if (node) {
|
|
3573
|
+
customHeaderContentRefs.current.set(key, node);
|
|
3574
|
+
requestAnimationFrame(measureCustomHeaderHeight);
|
|
3575
|
+
} else customHeaderContentRefs.current.delete(key);
|
|
3576
|
+
},
|
|
3577
|
+
children: column.renderHeader ? column.renderHeader(column) : column.title
|
|
3578
|
+
}
|
|
3579
|
+
),
|
|
3580
|
+
!cell.leaf && columnDraggable && /* @__PURE__ */ jsx7(
|
|
3581
|
+
HeaderDragIcon,
|
|
3582
|
+
{
|
|
3583
|
+
className: `rvg-header-icon rvg-header-drag-icon${hoveredHeaderAction?.columnIndex === cell.startIndex && hoveredHeaderAction.action === "drag" ? " is-hovered" : ""}`,
|
|
3584
|
+
style: { left: width2 - 16, top: (rowHeightForLevel - 14) / 2 }
|
|
3585
|
+
}
|
|
3586
|
+
)
|
|
3587
|
+
]
|
|
3588
|
+
},
|
|
3589
|
+
`group:${cell.key}:${cell.level}`
|
|
3590
|
+
);
|
|
3591
|
+
};
|
|
2918
3592
|
const rangeHandlePosition = (() => {
|
|
2919
3593
|
if (!rangeSelection) return null;
|
|
2920
3594
|
const bounds = getRangeBounds();
|
|
@@ -3106,15 +3780,19 @@ function Table({
|
|
|
3106
3780
|
const headerY = fixedHeader ? 0 : -scrollRef.current.top;
|
|
3107
3781
|
const columnIndex = locateColumn(event.clientX);
|
|
3108
3782
|
const inHeader = localY >= headerY && localY < headerY + headerHeight;
|
|
3109
|
-
const
|
|
3110
|
-
const
|
|
3111
|
-
const
|
|
3112
|
-
const
|
|
3783
|
+
const inLeafHeader = inHeader && localY >= headerY + headerLeafTop;
|
|
3784
|
+
const hoveredHeaderCell = inHeader ? locateHeaderCell(event.clientX, event.clientY) : null;
|
|
3785
|
+
const hoveredAction = inLeafHeader ? locateHeaderAction(event.clientX, columnIndex) : null;
|
|
3786
|
+
const hoveredDragHandle = Boolean(hoveredHeaderCell && locateHeaderCellDragHandle(event.clientX, hoveredHeaderCell));
|
|
3787
|
+
const hoveredDragColumnIndex = hoveredHeaderCell?.startIndex ?? columnIndex;
|
|
3788
|
+
const hoveredTitle = inHeader && !hoveredAction && !hoveredDragHandle && hoveredHeaderCell && Boolean(hoveredHeaderCell.column.title) && isPointerOnHeaderTitle(event.clientX, event.clientY, hoveredHeaderCell) && (tooltipConfig.header || hoveredHeaderCell.leaf && isHeaderTitleTruncated(hoveredHeaderCell.startIndex));
|
|
3789
|
+
const nextHover = hoveredAction ? { columnIndex, action: hoveredAction } : hoveredDragHandle ? { columnIndex: hoveredDragColumnIndex, action: "drag" } : hoveredTitle ? { columnIndex: hoveredHeaderCell.startIndex, action: "title" } : null;
|
|
3790
|
+
const nextTooltip = hoveredAction && hoveredHeaderCell ? createHeaderTooltip(hoveredAction, columnIndex, hoveredHeaderCell) : hoveredDragHandle && hoveredHeaderCell ? createHeaderTooltip("drag", hoveredDragColumnIndex, hoveredHeaderCell) : hoveredTitle && hoveredHeaderCell ? createHeaderTooltip("title", hoveredHeaderCell.startIndex, hoveredHeaderCell) : null;
|
|
3113
3791
|
const previousHover = hoveredHeaderActionRef.current;
|
|
3114
3792
|
if (previousHover?.columnIndex !== nextHover?.columnIndex || previousHover?.action !== nextHover?.action) {
|
|
3115
3793
|
hoveredHeaderActionRef.current = nextHover;
|
|
3116
3794
|
setHoveredHeaderAction(nextHover);
|
|
3117
|
-
showHeaderTooltipAfterDelay(
|
|
3795
|
+
showHeaderTooltipAfterDelay(nextTooltip);
|
|
3118
3796
|
scheduleDraw();
|
|
3119
3797
|
}
|
|
3120
3798
|
if (inHeader) {
|
|
@@ -3138,8 +3816,8 @@ function Table({
|
|
|
3138
3816
|
if (annotation?.content.trim()) {
|
|
3139
3817
|
const cellLeft = getDisplayedColumnLeft(cell.columnIndex);
|
|
3140
3818
|
const cellTop = bodyTop + cell.rowIndex * rowHeight - scrollRef.current.top;
|
|
3141
|
-
const
|
|
3142
|
-
showCellTooltip(
|
|
3819
|
+
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 };
|
|
3820
|
+
showCellTooltip(nextTooltip2);
|
|
3143
3821
|
} else {
|
|
3144
3822
|
const label = getCellLabel(cell.rowIndex, cell.columnIndex);
|
|
3145
3823
|
const context = event.currentTarget.getContext("2d");
|
|
@@ -3166,7 +3844,7 @@ function Table({
|
|
|
3166
3844
|
showCellTooltipAfterDelay(null);
|
|
3167
3845
|
} else {
|
|
3168
3846
|
const placement = textRight + 330 > viewport.width ? "left" : "right";
|
|
3169
|
-
const
|
|
3847
|
+
const nextTooltip2 = {
|
|
3170
3848
|
rowIndex: cell.rowIndex,
|
|
3171
3849
|
columnIndex: cell.columnIndex,
|
|
3172
3850
|
left: placement === "right" ? textRight + 16 : Math.max(8, textLeft - 16),
|
|
@@ -3174,13 +3852,13 @@ function Table({
|
|
|
3174
3852
|
label,
|
|
3175
3853
|
placement
|
|
3176
3854
|
};
|
|
3177
|
-
showCellTooltipAfterDelay(
|
|
3855
|
+
showCellTooltipAfterDelay(nextTooltip2);
|
|
3178
3856
|
}
|
|
3179
3857
|
}
|
|
3180
3858
|
}
|
|
3181
3859
|
}
|
|
3182
3860
|
}
|
|
3183
|
-
const cursor = inHeader &&
|
|
3861
|
+
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";
|
|
3184
3862
|
if (event.currentTarget.style.cursor !== cursor) event.currentTarget.style.cursor = cursor;
|
|
3185
3863
|
},
|
|
3186
3864
|
onMouseLeave: (event) => {
|
|
@@ -3223,6 +3901,7 @@ function Table({
|
|
|
3223
3901
|
const headerY = fixedHeader ? 0 : -scrollRef.current.top;
|
|
3224
3902
|
if (localY >= headerY && localY < headerY + headerHeight) {
|
|
3225
3903
|
clickedCellRef.current = null;
|
|
3904
|
+
if (localY < headerY + headerLeafTop) return;
|
|
3226
3905
|
const columnIndex = locateColumn(event.clientX);
|
|
3227
3906
|
const action = locateHeaderAction(event.clientX, columnIndex);
|
|
3228
3907
|
if (action === "sort") {
|
|
@@ -3284,21 +3963,30 @@ function Table({
|
|
|
3284
3963
|
className: "rvg-text-scroll-content",
|
|
3285
3964
|
style: { left: -fixedWidth, width: contentWidth, transform: `translateX(${-currentScroll.left}px)` },
|
|
3286
3965
|
children: [
|
|
3287
|
-
columns.map((column, columnIndex) => column.fixed ? null : columnIndex >= domRange.columnStart && columnIndex < domRange.columnEnd ? /* @__PURE__ */ jsx7("span", { children: renderHeaderTitle(columnIndex,
|
|
3966
|
+
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),
|
|
3967
|
+
headerCells.map((cell) => {
|
|
3968
|
+
if (columns[cell.startIndex]?.fixed) return null;
|
|
3969
|
+
if (cell.endIndex < domRange.columnStart || cell.startIndex >= domRange.columnEnd) return null;
|
|
3970
|
+
return renderGroupedHeaderCell(cell);
|
|
3971
|
+
}),
|
|
3288
3972
|
/* @__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) => {
|
|
3289
3973
|
if (column.fixed || columnIndex < domRange.columnStart || columnIndex >= domRange.columnEnd) return null;
|
|
3290
|
-
return renderCellText(rowIndex, columnIndex,
|
|
3974
|
+
return renderCellText(rowIndex, columnIndex, getScrollableColumnLeft(columnIndex));
|
|
3291
3975
|
})) })
|
|
3292
3976
|
]
|
|
3293
3977
|
}
|
|
3294
3978
|
) }),
|
|
3295
3979
|
/* @__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) => {
|
|
3296
3980
|
if (!column.fixed) return null;
|
|
3297
|
-
const left = column.fixed === "left" ?
|
|
3981
|
+
const left = column.fixed === "left" ? leftFixedOffsets.get(columnIndex) ?? 0 : viewport.width - (rightFixedOffsets.get(columnIndex) ?? 0) - metrics[columnIndex].width;
|
|
3298
3982
|
return renderCellText(rowIndex, columnIndex, left);
|
|
3299
3983
|
})) }),
|
|
3300
|
-
columns.map((column, columnIndex) => column.fixed === "left" ? renderHeaderTitle(columnIndex, metrics[columnIndex].left) : null),
|
|
3301
|
-
|
|
3984
|
+
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),
|
|
3985
|
+
headerCells.map((cell) => columns[cell.startIndex]?.fixed ? renderGroupedHeaderCell(cell) : null),
|
|
3986
|
+
fixedWidth > 0 && currentScroll.left > 0 && /* @__PURE__ */ jsx7("div", { className: "rvg-text-fixed-shadow is-left", style: { left: fixedWidth } }),
|
|
3987
|
+
rightFixedWidth > 0 && currentScroll.left < contentWidth - viewport.width && /* @__PURE__ */ jsx7("div", { className: "rvg-text-fixed-shadow is-right", style: { right: rightFixedWidth } }),
|
|
3988
|
+
hasVerticalBorders && fixedWidth > 0 && /* @__PURE__ */ jsx7("div", { className: "rvg-text-fixed-border is-left", style: { left: fixedWidth - 1 } }),
|
|
3989
|
+
hasVerticalBorders && rightFixedWidth > 0 && /* @__PURE__ */ jsx7("div", { className: "rvg-text-fixed-border is-right", style: { right: rightFixedWidth } })
|
|
3302
3990
|
] }),
|
|
3303
3991
|
/* @__PURE__ */ jsx7("div", { className: "rvg-spacer", style: { width: contentWidth, height: contentHeight + bodyTop, marginTop: -renderHeight } })
|
|
3304
3992
|
] }),
|
|
@@ -3306,18 +3994,18 @@ function Table({
|
|
|
3306
3994
|
"div",
|
|
3307
3995
|
{
|
|
3308
3996
|
className: `rvg-summary-row is-${summaryPosition}${hasVerticalBorders ? "" : " is-vertical-borderless"}`,
|
|
3309
|
-
style: { height: rowHeight, top: summaryPosition === "top" ? headerHeight : void 0 },
|
|
3997
|
+
style: { height: rowHeight, top: summaryPosition === "top" ? fixedHeader ? headerHeight : headerHeight - currentScroll.top : void 0 },
|
|
3310
3998
|
children: [
|
|
3311
3999
|
/* @__PURE__ */ jsx7("div", { className: "rvg-summary-scroll-clip", style: { left: fixedWidth, right: rightFixedWidth }, children: /* @__PURE__ */ jsx7(
|
|
3312
4000
|
"div",
|
|
3313
4001
|
{
|
|
3314
4002
|
className: "rvg-summary-scroll-content",
|
|
3315
4003
|
style: { left: -fixedWidth, width: contentWidth, transform: `translateX(${-currentScroll.left}px)` },
|
|
3316
|
-
children: columns.map((column, columnIndex) => column.fixed ? null : renderSummaryCell(columnIndex,
|
|
4004
|
+
children: columns.map((column, columnIndex) => column.fixed ? null : renderSummaryCell(columnIndex, getScrollableColumnLeft(columnIndex)))
|
|
3317
4005
|
}
|
|
3318
4006
|
) }),
|
|
3319
4007
|
/* @__PURE__ */ jsxs7("div", { className: "rvg-summary-fixed-layer", children: [
|
|
3320
|
-
columns.map((column, columnIndex) => column.fixed === "left" ? renderSummaryCell(columnIndex,
|
|
4008
|
+
columns.map((column, columnIndex) => column.fixed === "left" ? renderSummaryCell(columnIndex, leftFixedOffsets.get(columnIndex) ?? 0) : null),
|
|
3321
4009
|
columns.map((column, columnIndex) => column.fixed === "right" ? renderSummaryCell(columnIndex, viewport.width - (rightFixedOffsets.get(columnIndex) ?? 0) - metrics[columnIndex].width) : null)
|
|
3322
4010
|
] }),
|
|
3323
4011
|
fixedWidth > 0 && currentScroll.left > 0 && /* @__PURE__ */ jsx7("div", { className: "rvg-summary-fixed-shadow is-left", style: { left: fixedWidth } }),
|
|
@@ -3334,11 +4022,11 @@ function Table({
|
|
|
3334
4022
|
ref: scrollingHeaderIconsRef,
|
|
3335
4023
|
className: "rvg-header-icon-layer rvg-header-icon-layer-scroll",
|
|
3336
4024
|
style: { left: -fixedWidth, width: contentWidth, transform: `translateX(${-scrollRef.current.left}px)` },
|
|
3337
|
-
children: columns.map((column, index) => column.fixed ? null : /* @__PURE__ */ jsx7("span", { children: renderHeaderIcons(index,
|
|
4025
|
+
children: columns.map((column, index) => column.fixed ? null : /* @__PURE__ */ jsx7("span", { children: renderHeaderIcons(index, getScrollableColumnLeft(index)) }, column.key))
|
|
3338
4026
|
}
|
|
3339
4027
|
) }),
|
|
3340
4028
|
/* @__PURE__ */ jsxs7("div", { className: "rvg-header-icon-layer", children: [
|
|
3341
|
-
columns.map((column, index) => column.fixed === "left" ? /* @__PURE__ */ jsx7("span", { children: renderHeaderIcons(index,
|
|
4029
|
+
columns.map((column, index) => column.fixed === "left" ? /* @__PURE__ */ jsx7("span", { children: renderHeaderIcons(index, leftFixedOffsets.get(index) ?? 0) }, column.key) : null),
|
|
3342
4030
|
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)
|
|
3343
4031
|
] })
|
|
3344
4032
|
] }),
|
|
@@ -3615,7 +4303,42 @@ function Table({
|
|
|
3615
4303
|
] })
|
|
3616
4304
|
] }) }),
|
|
3617
4305
|
toast && /* @__PURE__ */ jsx7("div", { className: `rvg-toast is-${toast.type}`, role: "status", children: toast.message }),
|
|
4306
|
+
/* @__PURE__ */ jsx7(
|
|
4307
|
+
"div",
|
|
4308
|
+
{
|
|
4309
|
+
ref: verticalScrollbarRef,
|
|
4310
|
+
className: "rvg-vertical-scrollbar",
|
|
4311
|
+
onPointerEnter: () => keepCustomScrollbarVisible("vertical"),
|
|
4312
|
+
onPointerLeave: () => releaseCustomScrollbarVisible("vertical"),
|
|
4313
|
+
onPointerDown: handleVerticalScrollbarPointerDown,
|
|
4314
|
+
onPointerMove: handleVerticalScrollbarPointerMove,
|
|
4315
|
+
onPointerUp: handleVerticalScrollbarPointerUp,
|
|
4316
|
+
onPointerCancel: handleVerticalScrollbarPointerUp,
|
|
4317
|
+
children: /* @__PURE__ */ jsx7("div", { ref: verticalScrollbarThumbRef, className: "rvg-vertical-scrollbar-thumb" })
|
|
4318
|
+
}
|
|
4319
|
+
),
|
|
4320
|
+
/* @__PURE__ */ jsx7(
|
|
4321
|
+
"div",
|
|
4322
|
+
{
|
|
4323
|
+
ref: horizontalScrollbarRef,
|
|
4324
|
+
className: "rvg-horizontal-scrollbar",
|
|
4325
|
+
onPointerEnter: () => keepCustomScrollbarVisible("horizontal"),
|
|
4326
|
+
onPointerLeave: () => releaseCustomScrollbarVisible("horizontal"),
|
|
4327
|
+
onPointerDown: handleHorizontalScrollbarPointerDown,
|
|
4328
|
+
onPointerMove: handleHorizontalScrollbarPointerMove,
|
|
4329
|
+
onPointerUp: handleHorizontalScrollbarPointerUp,
|
|
4330
|
+
onPointerCancel: handleHorizontalScrollbarPointerUp,
|
|
4331
|
+
children: /* @__PURE__ */ jsx7("div", { ref: horizontalScrollbarThumbRef, className: "rvg-horizontal-scrollbar-thumb" })
|
|
4332
|
+
}
|
|
4333
|
+
),
|
|
3618
4334
|
/* @__PURE__ */ jsx7("div", { ref: dragGuideRef, className: "rvg-column-guide" }),
|
|
4335
|
+
resizeGuideX !== null && /* @__PURE__ */ jsx7(
|
|
4336
|
+
"div",
|
|
4337
|
+
{
|
|
4338
|
+
className: "rvg-column-guide is-resizing",
|
|
4339
|
+
style: { display: "block", top: resizeGuideTop, transform: `translateX(${Math.round(resizeGuideX)}px)` }
|
|
4340
|
+
}
|
|
4341
|
+
),
|
|
3619
4342
|
/* @__PURE__ */ jsx7("div", { ref: rowDragGuideRef, className: "rvg-row-guide" }),
|
|
3620
4343
|
filterEditor && /* @__PURE__ */ jsxs7("div", { ref: filterRef, className: "rvg-filter", style: { left: filterEditor.left, top: filterEditor.top }, children: [
|
|
3621
4344
|
/* @__PURE__ */ jsx7(
|