@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.cjs
CHANGED
|
@@ -814,8 +814,9 @@ function buildColumnMetrics(columns, columnDraggableOrOptions = false) {
|
|
|
814
814
|
});
|
|
815
815
|
const viewportWidth = options.viewportWidth ?? 0;
|
|
816
816
|
if (viewportWidth <= left || columns.length === 0) return metrics;
|
|
817
|
-
const
|
|
818
|
-
const
|
|
817
|
+
const unsetWidthStretchable = columns.map((column, index) => ({ column, index })).filter(({ column }) => column.width === void 0 && column.fixed === void 0 && !column.rowSelection && !column.rowDragHandle && !column.rowNumber);
|
|
818
|
+
const dataStretchable = columns.map((column, index) => ({ column, index })).filter(({ column }) => column.fixed === void 0 && !column.rowSelection && !column.rowDragHandle && !column.rowNumber);
|
|
819
|
+
const targets = unsetWidthStretchable.length > 0 ? unsetWidthStretchable : dataStretchable.length > 0 ? dataStretchable : [];
|
|
819
820
|
if (targets.length === 0) return metrics;
|
|
820
821
|
const extra = viewportWidth - left;
|
|
821
822
|
const baseTotal = targets.reduce((sum, { index }) => sum + metrics[index].width, 0);
|
|
@@ -892,6 +893,8 @@ function ellipsizeText(context, text, maxWidth) {
|
|
|
892
893
|
}
|
|
893
894
|
function paintGrid(options) {
|
|
894
895
|
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;
|
|
896
|
+
const headerLeafTop = options.headerLeafTop ?? 0;
|
|
897
|
+
const headerLeafHeight = options.headerLeafHeight ?? headerHeight;
|
|
895
898
|
const colors = { ...COLORS, ...options.colors };
|
|
896
899
|
const bodyTop = options.bodyTop ?? headerHeight;
|
|
897
900
|
const getCellSpan = (rowIndex, columnIndex) => options.cellSpans.get(getCellCoordKey(rowIndex, columnIndex));
|
|
@@ -908,9 +911,16 @@ function paintGrid(options) {
|
|
|
908
911
|
ctx.fillRect(0, 0, width, height);
|
|
909
912
|
ctx.font = "13px Inter, ui-sans-serif, system-ui, sans-serif";
|
|
910
913
|
ctx.textBaseline = "middle";
|
|
911
|
-
const leftFixedWidth = columns.reduce((value, column, index) => column.fixed === "left" ?
|
|
914
|
+
const leftFixedWidth = columns.reduce((value, column, index) => column.fixed === "left" ? value + metrics[index].width : value, 0);
|
|
912
915
|
const rightFixedWidth = columns.reduce((value, column, index) => column.fixed === "right" ? value + metrics[index].width : value, 0);
|
|
913
916
|
const rightFixedLeft = width - rightFixedWidth;
|
|
917
|
+
const leftOffsets = /* @__PURE__ */ new Map();
|
|
918
|
+
let leftOffset = 0;
|
|
919
|
+
for (let index = 0; index < columns.length; index += 1) {
|
|
920
|
+
if (columns[index].fixed !== "left") continue;
|
|
921
|
+
leftOffsets.set(index, leftOffset);
|
|
922
|
+
leftOffset += metrics[index].width;
|
|
923
|
+
}
|
|
914
924
|
const rightOffsets = /* @__PURE__ */ new Map();
|
|
915
925
|
let rightOffset = 0;
|
|
916
926
|
for (let index = columns.length - 1; index >= 0; index -= 1) {
|
|
@@ -918,8 +928,14 @@ function paintGrid(options) {
|
|
|
918
928
|
rightOffsets.set(index, rightOffset);
|
|
919
929
|
rightOffset += metrics[index].width;
|
|
920
930
|
}
|
|
931
|
+
const scrollableLefts = /* @__PURE__ */ new Map();
|
|
932
|
+
let scrollableLeft = leftFixedWidth;
|
|
933
|
+
for (let index = 0; index < columns.length; index += 1) {
|
|
934
|
+
scrollableLefts.set(index, scrollableLeft);
|
|
935
|
+
if (columns[index].fixed === void 0) scrollableLeft += metrics[index].width;
|
|
936
|
+
}
|
|
921
937
|
const getColumnX = (index) => {
|
|
922
|
-
return columns[index].fixed === "left" ?
|
|
938
|
+
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;
|
|
923
939
|
};
|
|
924
940
|
const paintBodyColumns = (layer) => {
|
|
925
941
|
const rowStart = Math.max(0, range.rowStart - Math.max(0, options.maxRowSpan - 1));
|
|
@@ -1069,7 +1085,7 @@ function paintGrid(options) {
|
|
|
1069
1085
|
if (!verticalBorderless) ctx.fillRect(x + metric.width - 1, headerY, 1, headerHeight);
|
|
1070
1086
|
ctx.fillRect(x, headerY + headerHeight - 1, metric.width, 1);
|
|
1071
1087
|
const column = columns[columnIndex];
|
|
1072
|
-
if (!column.rowSelection && !column.rowDragHandle && !column.rowNumber && column.title) {
|
|
1088
|
+
if (headerLeafTop === 0 && !column.rowSelection && !column.rowDragHandle && !column.rowNumber && !column.renderHeader && column.title) {
|
|
1073
1089
|
ctx.font = "600 13px Inter, ui-sans-serif, system-ui, sans-serif";
|
|
1074
1090
|
const titleWidth = ctx.measureText(column.title).width;
|
|
1075
1091
|
const visibleActions = getVisibleHeaderActions(column, metric.width, columnDraggable, titleWidth);
|
|
@@ -1086,11 +1102,11 @@ function paintGrid(options) {
|
|
|
1086
1102
|
const textX = titleAlign === "right" ? x + contentWidth - padding : titleAlign === "center" ? x + centeredTextX : x + padding;
|
|
1087
1103
|
ctx.save();
|
|
1088
1104
|
ctx.beginPath();
|
|
1089
|
-
ctx.rect(x + 2, headerY + 1, Math.max(0, clipWidth - 4),
|
|
1105
|
+
ctx.rect(x + 2, headerY + headerLeafTop + 1, Math.max(0, clipWidth - 4), headerLeafHeight - 2);
|
|
1090
1106
|
ctx.clip();
|
|
1091
1107
|
ctx.fillStyle = colors.muted;
|
|
1092
1108
|
ctx.textAlign = titleAlign === "right" ? "right" : titleAlign === "center" ? "center" : "left";
|
|
1093
|
-
ctx.fillText(title, textX, headerY +
|
|
1109
|
+
ctx.fillText(title, textX, headerY + headerLeafTop + headerLeafHeight / 2);
|
|
1094
1110
|
ctx.restore();
|
|
1095
1111
|
}
|
|
1096
1112
|
}
|
|
@@ -1098,22 +1114,6 @@ function paintGrid(options) {
|
|
|
1098
1114
|
paintHeaderColumns("scroll");
|
|
1099
1115
|
paintHeaderColumns("left");
|
|
1100
1116
|
paintHeaderColumns("right");
|
|
1101
|
-
if (leftFixedWidth > 0 && scrollLeft > 0) {
|
|
1102
|
-
const shadowWidth = 10;
|
|
1103
|
-
const shadow = ctx.createLinearGradient(leftFixedWidth, 0, leftFixedWidth + shadowWidth, 0);
|
|
1104
|
-
shadow.addColorStop(0, "rgba(5, 5, 5, 0.06)");
|
|
1105
|
-
shadow.addColorStop(1, "rgba(5, 5, 5, 0)");
|
|
1106
|
-
ctx.fillStyle = shadow;
|
|
1107
|
-
ctx.fillRect(leftFixedWidth, 0, shadowWidth, height);
|
|
1108
|
-
}
|
|
1109
|
-
if (rightFixedWidth > 0 && scrollLeft < (metrics[metrics.length - 1]?.right ?? 0) - width) {
|
|
1110
|
-
const shadowWidth = 10;
|
|
1111
|
-
const shadow = ctx.createLinearGradient(rightFixedLeft - shadowWidth, 0, rightFixedLeft, 0);
|
|
1112
|
-
shadow.addColorStop(0, "rgba(5, 5, 5, 0)");
|
|
1113
|
-
shadow.addColorStop(1, "rgba(5, 5, 5, 0.06)");
|
|
1114
|
-
ctx.fillStyle = shadow;
|
|
1115
|
-
ctx.fillRect(rightFixedLeft - shadowWidth, 0, shadowWidth, height);
|
|
1116
|
-
}
|
|
1117
1117
|
if (rightFixedWidth > 0 && !verticalBorderless) {
|
|
1118
1118
|
ctx.fillStyle = colors.grid;
|
|
1119
1119
|
ctx.fillRect(rightFixedLeft - 1, 0, 1, height);
|
|
@@ -1265,6 +1265,45 @@ var getRowNumberColumnWidth = (rowCount) => {
|
|
|
1265
1265
|
const digits = String(Math.max(1, rowCount)).length;
|
|
1266
1266
|
return digits <= 4 ? 44 : 44 + (digits - 4) * 8;
|
|
1267
1267
|
};
|
|
1268
|
+
function getColumnDepth(column) {
|
|
1269
|
+
if (!column.children?.length) return 1;
|
|
1270
|
+
return 1 + Math.max(...column.children.map(getColumnDepth));
|
|
1271
|
+
}
|
|
1272
|
+
function flattenDataColumns(columns, inheritedFixed, insideGroup = false) {
|
|
1273
|
+
return columns.flatMap((column) => {
|
|
1274
|
+
if (!column.children?.length) {
|
|
1275
|
+
return insideGroup ? [{ ...column, fixed: inheritedFixed }] : [column];
|
|
1276
|
+
}
|
|
1277
|
+
return flattenDataColumns(column.children, column.fixed ?? inheritedFixed, true);
|
|
1278
|
+
});
|
|
1279
|
+
}
|
|
1280
|
+
function buildHeaderCells(sourceColumns, utilityColumnCount, depth) {
|
|
1281
|
+
const cells = [];
|
|
1282
|
+
let leafIndex = utilityColumnCount;
|
|
1283
|
+
const walk = (column, level, inheritedFixed, insideGroup, rootIndex, parentKey) => {
|
|
1284
|
+
const fixedColumn = insideGroup ? { ...column, fixed: inheritedFixed } : column;
|
|
1285
|
+
if (!fixedColumn.children?.length) {
|
|
1286
|
+
const startIndex2 = leafIndex;
|
|
1287
|
+
leafIndex += 1;
|
|
1288
|
+
cells.push({ key: fixedColumn.key, column: fixedColumn, level: depth - 1, startIndex: startIndex2, endIndex: startIndex2, leaf: true, parentKey, rootIndex });
|
|
1289
|
+
return { startIndex: startIndex2, endIndex: startIndex2 };
|
|
1290
|
+
}
|
|
1291
|
+
const startIndex = leafIndex;
|
|
1292
|
+
fixedColumn.children.forEach((child) => walk(child, level + 1, fixedColumn.fixed, true, rootIndex, fixedColumn.key));
|
|
1293
|
+
const endIndex = leafIndex - 1;
|
|
1294
|
+
cells.push({ key: fixedColumn.key, column: fixedColumn, level, startIndex, endIndex, leaf: false, parentKey, rootIndex });
|
|
1295
|
+
return { startIndex, endIndex };
|
|
1296
|
+
};
|
|
1297
|
+
sourceColumns.forEach((column, index) => walk(column, 0, void 0, false, index));
|
|
1298
|
+
return cells;
|
|
1299
|
+
}
|
|
1300
|
+
function hasNestedFixedColumns(columns, insideGroup = false) {
|
|
1301
|
+
return columns.some((column) => insideGroup && column.fixed !== void 0 || (column.children?.length ? hasNestedFixedColumns(column.children, true) : false));
|
|
1302
|
+
}
|
|
1303
|
+
function isProductionRuntime() {
|
|
1304
|
+
const env = globalThis.process?.env?.NODE_ENV;
|
|
1305
|
+
return env === "production";
|
|
1306
|
+
}
|
|
1268
1307
|
var createPendingInsertId = () => `table-inserted-${globalThis.crypto?.randomUUID?.() ?? `${Date.now()}-${Math.random().toString(36).slice(2)}`}`;
|
|
1269
1308
|
var waitForPaint = () => new Promise((resolve) => {
|
|
1270
1309
|
requestAnimationFrame(() => requestAnimationFrame(() => resolve()));
|
|
@@ -1328,12 +1367,14 @@ function Table({
|
|
|
1328
1367
|
rowKey,
|
|
1329
1368
|
width = "100%",
|
|
1330
1369
|
height = "100%",
|
|
1331
|
-
autoHeight =
|
|
1332
|
-
|
|
1333
|
-
|
|
1370
|
+
autoHeight = true,
|
|
1371
|
+
layout,
|
|
1372
|
+
rowHeight: rowHeightProp,
|
|
1373
|
+
headerHeight: headerHeightProp,
|
|
1334
1374
|
fixedHeader = true,
|
|
1335
1375
|
locale = "zh-CN",
|
|
1336
|
-
|
|
1376
|
+
borderless,
|
|
1377
|
+
verticalBorderless,
|
|
1337
1378
|
striped = false,
|
|
1338
1379
|
highlight,
|
|
1339
1380
|
cellSpans = [],
|
|
@@ -1375,8 +1416,27 @@ function Table({
|
|
|
1375
1416
|
style,
|
|
1376
1417
|
ariaLabel = "Data grid"
|
|
1377
1418
|
}) {
|
|
1419
|
+
const rowHeight = layout?.rowHeight ?? rowHeightProp ?? 36;
|
|
1420
|
+
const baseHeaderHeight = layout?.headerHeight ?? headerHeightProp ?? 40;
|
|
1421
|
+
const headerDepth = (0, import_react5.useMemo)(() => Math.max(1, ...sourceColumns.map(getColumnDepth)), [sourceColumns]);
|
|
1378
1422
|
const { language, labels } = (0, import_react5.useMemo)(() => resolveGridLocale(locale), [locale]);
|
|
1379
|
-
const
|
|
1423
|
+
const customHeaderContentRefs = (0, import_react5.useRef)(/* @__PURE__ */ new Map());
|
|
1424
|
+
const customHeaderMeasurementsRef = (0, import_react5.useRef)(/* @__PURE__ */ new Map());
|
|
1425
|
+
const [measuredHeaderRowHeights, setMeasuredHeaderRowHeights] = (0, import_react5.useState)([]);
|
|
1426
|
+
const headerRowHeights = (0, import_react5.useMemo)(() => Array.from({ length: headerDepth }, (_, level) => Math.max(baseHeaderHeight, measuredHeaderRowHeights[level] ?? 0)), [baseHeaderHeight, headerDepth, measuredHeaderRowHeights]);
|
|
1427
|
+
const headerRowOffsets = (0, import_react5.useMemo)(() => {
|
|
1428
|
+
const offsets = [];
|
|
1429
|
+
let top = 0;
|
|
1430
|
+
headerRowHeights.forEach((height2) => {
|
|
1431
|
+
offsets.push(top);
|
|
1432
|
+
top += height2;
|
|
1433
|
+
});
|
|
1434
|
+
return offsets;
|
|
1435
|
+
}, [headerRowHeights]);
|
|
1436
|
+
const headerHeight = headerRowHeights.reduce((total, height2) => total + height2, 0);
|
|
1437
|
+
const headerLeafTop = headerRowOffsets[headerDepth - 1] ?? 0;
|
|
1438
|
+
const headerLeafHeight = headerRowHeights[headerDepth - 1] ?? baseHeaderHeight;
|
|
1439
|
+
const hasVerticalBorders = !(borderless ?? verticalBorderless ?? false);
|
|
1380
1440
|
const hasStripedRows = Boolean(striped);
|
|
1381
1441
|
const stripedColor = typeof striped === "string" ? striped : void 0;
|
|
1382
1442
|
const editedCellsHighlight = highlight?.editedCells ?? false;
|
|
@@ -1395,6 +1455,13 @@ function Table({
|
|
|
1395
1455
|
const summaryEmptyValue = typeof summary === "object" ? summary.emptyValue ?? "" : "";
|
|
1396
1456
|
const contextMenuEnabled = contextMenuConfig !== false;
|
|
1397
1457
|
const customContextMenuConfig = typeof contextMenuConfig === "object" ? contextMenuConfig : void 0;
|
|
1458
|
+
const [resizedColumnWidths, setResizedColumnWidths] = (0, import_react5.useState)({});
|
|
1459
|
+
const leafSourceColumns = (0, import_react5.useMemo)(() => flattenDataColumns(sourceColumns), [sourceColumns]);
|
|
1460
|
+
(0, import_react5.useEffect)(() => {
|
|
1461
|
+
if (!isProductionRuntime() && hasNestedFixedColumns(sourceColumns)) {
|
|
1462
|
+
console.warn("[Table] Multi-level headers only support fixed columns on top-level columns. Nested fixed values are ignored.");
|
|
1463
|
+
}
|
|
1464
|
+
}, [sourceColumns]);
|
|
1398
1465
|
const columns = (0, import_react5.useMemo)(() => {
|
|
1399
1466
|
const utilityColumns = [];
|
|
1400
1467
|
if (rowDraggable) {
|
|
@@ -1406,9 +1473,24 @@ function Table({
|
|
|
1406
1473
|
if (rowNumber) {
|
|
1407
1474
|
utilityColumns.push({ key: "__rvg_row_number__", title: "#", width: getRowNumberColumnWidth(sourceRows.length), align: "center", fixed: "left", rowNumber: true });
|
|
1408
1475
|
}
|
|
1409
|
-
|
|
1410
|
-
|
|
1476
|
+
const dataColumns = leafSourceColumns.map((column) => {
|
|
1477
|
+
const resizedWidth = resizedColumnWidths[column.key];
|
|
1478
|
+
return resizedWidth === void 0 ? column : { ...column, width: resizedWidth };
|
|
1479
|
+
});
|
|
1480
|
+
return [...utilityColumns, ...dataColumns];
|
|
1481
|
+
}, [leafSourceColumns, resizedColumnWidths, rowDraggable, rowNumber, rowSelection, sourceRows.length]);
|
|
1411
1482
|
const utilityColumnCount = (rowDraggable ? 1 : 0) + (rowSelection ? 1 : 0) + (rowNumber ? 1 : 0);
|
|
1483
|
+
const headerCells = (0, import_react5.useMemo)(() => buildHeaderCells(sourceColumns, utilityColumnCount, headerDepth), [headerDepth, sourceColumns, utilityColumnCount]);
|
|
1484
|
+
const activeCustomHeaderLevels = (0, import_react5.useMemo)(() => {
|
|
1485
|
+
const levels = /* @__PURE__ */ new Map();
|
|
1486
|
+
columns.forEach((column) => {
|
|
1487
|
+
if (column.renderHeader) levels.set(column.key, headerDepth - 1);
|
|
1488
|
+
});
|
|
1489
|
+
headerCells.forEach((cell) => {
|
|
1490
|
+
if (cell.column.renderHeader) levels.set(`group:${cell.key}:${cell.level}`, cell.level);
|
|
1491
|
+
});
|
|
1492
|
+
return levels;
|
|
1493
|
+
}, [columns, headerCells, headerDepth]);
|
|
1412
1494
|
const canvasRef = (0, import_react5.useRef)(null);
|
|
1413
1495
|
const scrollerRef = (0, import_react5.useRef)(null);
|
|
1414
1496
|
const frameRef = (0, import_react5.useRef)(null);
|
|
@@ -1426,6 +1508,14 @@ function Table({
|
|
|
1426
1508
|
const dragGuideRef = (0, import_react5.useRef)(null);
|
|
1427
1509
|
const rowDragGuideRef = (0, import_react5.useRef)(null);
|
|
1428
1510
|
const selectionFocusRef = (0, import_react5.useRef)(null);
|
|
1511
|
+
const verticalScrollbarRef = (0, import_react5.useRef)(null);
|
|
1512
|
+
const verticalScrollbarThumbRef = (0, import_react5.useRef)(null);
|
|
1513
|
+
const verticalScrollbarDragRef = (0, import_react5.useRef)(null);
|
|
1514
|
+
const horizontalScrollbarRef = (0, import_react5.useRef)(null);
|
|
1515
|
+
const horizontalScrollbarThumbRef = (0, import_react5.useRef)(null);
|
|
1516
|
+
const horizontalScrollbarDragRef = (0, import_react5.useRef)(null);
|
|
1517
|
+
const verticalScrollbarVisibilityTimerRef = (0, import_react5.useRef)(null);
|
|
1518
|
+
const horizontalScrollbarVisibilityTimerRef = (0, import_react5.useRef)(null);
|
|
1429
1519
|
const rowByKeyRef = (0, import_react5.useRef)(/* @__PURE__ */ new Map());
|
|
1430
1520
|
const rowIndexByKeyRef = (0, import_react5.useRef)(/* @__PURE__ */ new Map());
|
|
1431
1521
|
const headerIconsRef = (0, import_react5.useRef)(null);
|
|
@@ -1478,10 +1568,65 @@ function Table({
|
|
|
1478
1568
|
rangeDragRef.current = null;
|
|
1479
1569
|
setSelectionRange(null);
|
|
1480
1570
|
}, [rangeSelection]);
|
|
1571
|
+
(0, import_react5.useEffect)(() => {
|
|
1572
|
+
setResizedColumnWidths((current) => {
|
|
1573
|
+
const sourceColumnByKey = new Map(leafSourceColumns.map((column) => [column.key, column]));
|
|
1574
|
+
let changed = false;
|
|
1575
|
+
const next = {};
|
|
1576
|
+
Object.entries(current).forEach(([key, value]) => {
|
|
1577
|
+
const sourceColumn = sourceColumnByKey.get(key);
|
|
1578
|
+
if (!sourceColumn) {
|
|
1579
|
+
changed = true;
|
|
1580
|
+
return;
|
|
1581
|
+
}
|
|
1582
|
+
if (sourceColumn.width !== void 0 && sourceColumn.width !== value) {
|
|
1583
|
+
changed = true;
|
|
1584
|
+
return;
|
|
1585
|
+
}
|
|
1586
|
+
next[key] = value;
|
|
1587
|
+
});
|
|
1588
|
+
return changed ? next : current;
|
|
1589
|
+
});
|
|
1590
|
+
}, [leafSourceColumns]);
|
|
1481
1591
|
(0, import_react5.useEffect)(() => () => {
|
|
1482
1592
|
if (headerTooltipTimerRef.current !== null) clearTimeout(headerTooltipTimerRef.current);
|
|
1483
1593
|
if (cellTooltipTimerRef.current !== null) clearTimeout(cellTooltipTimerRef.current);
|
|
1484
1594
|
}, []);
|
|
1595
|
+
const measureCustomHeaderHeight = (0, import_react5.useCallback)(() => {
|
|
1596
|
+
const nextHeights = Array.from({ length: headerDepth }, () => baseHeaderHeight);
|
|
1597
|
+
customHeaderContentRefs.current.forEach((node) => {
|
|
1598
|
+
const headerCell = node.closest(".rvg-header-title");
|
|
1599
|
+
const key = headerCell?.dataset.measureKey;
|
|
1600
|
+
const level = key ? activeCustomHeaderLevels.get(key) : void 0;
|
|
1601
|
+
if (!key || level === void 0 || level < 0 || level >= nextHeights.length) return;
|
|
1602
|
+
customHeaderMeasurementsRef.current.set(key, { level, height: Math.ceil(node.scrollHeight) + 16 });
|
|
1603
|
+
});
|
|
1604
|
+
customHeaderMeasurementsRef.current.forEach((measurement, key) => {
|
|
1605
|
+
if (!activeCustomHeaderLevels.has(key)) return;
|
|
1606
|
+
nextHeights[measurement.level] = Math.max(nextHeights[measurement.level], measurement.height);
|
|
1607
|
+
});
|
|
1608
|
+
setMeasuredHeaderRowHeights((current) => {
|
|
1609
|
+
const changed = nextHeights.length !== current.length || nextHeights.some((height2, index) => height2 !== current[index]);
|
|
1610
|
+
return changed ? nextHeights : current;
|
|
1611
|
+
});
|
|
1612
|
+
}, [activeCustomHeaderLevels, baseHeaderHeight, headerDepth]);
|
|
1613
|
+
(0, import_react5.useLayoutEffect)(() => {
|
|
1614
|
+
customHeaderContentRefs.current.forEach((_, key) => {
|
|
1615
|
+
if (!activeCustomHeaderLevels.has(key)) customHeaderContentRefs.current.delete(key);
|
|
1616
|
+
});
|
|
1617
|
+
customHeaderMeasurementsRef.current.forEach((_, key) => {
|
|
1618
|
+
if (!activeCustomHeaderLevels.has(key)) customHeaderMeasurementsRef.current.delete(key);
|
|
1619
|
+
});
|
|
1620
|
+
if (activeCustomHeaderLevels.size === 0) {
|
|
1621
|
+
setMeasuredHeaderRowHeights([]);
|
|
1622
|
+
return;
|
|
1623
|
+
}
|
|
1624
|
+
measureCustomHeaderHeight();
|
|
1625
|
+
if (typeof ResizeObserver === "undefined") return;
|
|
1626
|
+
const observer = new ResizeObserver(measureCustomHeaderHeight);
|
|
1627
|
+
customHeaderContentRefs.current.forEach((node) => observer.observe(node));
|
|
1628
|
+
return () => observer.disconnect();
|
|
1629
|
+
}, [activeCustomHeaderLevels, measureCustomHeaderHeight, viewport.width]);
|
|
1485
1630
|
const [filterEditor, setFilterEditor] = (0, import_react5.useState)(null);
|
|
1486
1631
|
const [contextMenu, setContextMenu] = (0, import_react5.useState)(null);
|
|
1487
1632
|
const [insertCounts, setInsertCounts] = (0, import_react5.useState)({ before: 1, after: 1 });
|
|
@@ -1490,6 +1635,8 @@ function Table({
|
|
|
1490
1635
|
const [insertBusy, setInsertBusy] = (0, import_react5.useState)(false);
|
|
1491
1636
|
const [deleteBusy, setDeleteBusy] = (0, import_react5.useState)(false);
|
|
1492
1637
|
const [toast, setToast] = (0, import_react5.useState)(null);
|
|
1638
|
+
const [resizeGuideX, setResizeGuideX] = (0, import_react5.useState)(null);
|
|
1639
|
+
const [resizeGuideTop, setResizeGuideTop] = (0, import_react5.useState)(0);
|
|
1493
1640
|
const [hoveredHeaderAction, setHoveredHeaderAction] = (0, import_react5.useState)(null);
|
|
1494
1641
|
const [visibleHeaderTooltip, setVisibleHeaderTooltip] = (0, import_react5.useState)(null);
|
|
1495
1642
|
const [hoveredCellTooltip, setHoveredCellTooltip] = (0, import_react5.useState)(null);
|
|
@@ -1574,7 +1721,17 @@ function Table({
|
|
|
1574
1721
|
const metrics = (0, import_react5.useMemo)(() => buildColumnMetrics(columns, { columnDraggable, viewportWidth: viewport.width }), [columnDraggable, columns, viewport.width]);
|
|
1575
1722
|
const contentWidth = metrics.length > 0 ? metrics[metrics.length - 1].right : 0;
|
|
1576
1723
|
const contentHeight = rows.length * rowHeight;
|
|
1577
|
-
const fixedWidth = (0, import_react5.useMemo)(() => columns.reduce((width2, column, index) => column.fixed === "left" ?
|
|
1724
|
+
const fixedWidth = (0, import_react5.useMemo)(() => columns.reduce((width2, column, index) => column.fixed === "left" ? width2 + metrics[index].width : width2, 0), [columns, metrics]);
|
|
1725
|
+
const leftFixedOffsets = (0, import_react5.useMemo)(() => {
|
|
1726
|
+
const offsets = /* @__PURE__ */ new Map();
|
|
1727
|
+
let offset = 0;
|
|
1728
|
+
for (let index = 0; index < columns.length; index += 1) {
|
|
1729
|
+
if (columns[index].fixed !== "left") continue;
|
|
1730
|
+
offsets.set(index, offset);
|
|
1731
|
+
offset += metrics[index].width;
|
|
1732
|
+
}
|
|
1733
|
+
return offsets;
|
|
1734
|
+
}, [columns, metrics]);
|
|
1578
1735
|
const rightFixedWidth = (0, import_react5.useMemo)(() => columns.reduce((width2, column, index) => column.fixed === "right" ? width2 + metrics[index].width : width2, 0), [columns, metrics]);
|
|
1579
1736
|
const rightFixedOffsets = (0, import_react5.useMemo)(() => {
|
|
1580
1737
|
const offsets = /* @__PURE__ */ new Map();
|
|
@@ -1586,6 +1743,16 @@ function Table({
|
|
|
1586
1743
|
}
|
|
1587
1744
|
return offsets;
|
|
1588
1745
|
}, [columns, metrics]);
|
|
1746
|
+
const scrollableColumnLefts = (0, import_react5.useMemo)(() => {
|
|
1747
|
+
const lefts = [];
|
|
1748
|
+
let width2 = fixedWidth;
|
|
1749
|
+
for (let index = 0; index < columns.length; index += 1) {
|
|
1750
|
+
lefts[index] = width2;
|
|
1751
|
+
if (columns[index].fixed === void 0) width2 += metrics[index].width;
|
|
1752
|
+
}
|
|
1753
|
+
return lefts;
|
|
1754
|
+
}, [columns, fixedWidth, metrics]);
|
|
1755
|
+
const getScrollableColumnLeft = (0, import_react5.useCallback)((columnIndex) => scrollableColumnLefts[columnIndex] ?? fixedWidth, [fixedWidth, scrollableColumnLefts]);
|
|
1589
1756
|
const hasRowSelectionColumn = (0, import_react5.useMemo)(() => columns.some((column) => column.rowSelection), [columns]);
|
|
1590
1757
|
const rowSelectionMode = typeof rowSelection === "object" ? rowSelection.mode ?? "multiple" : "multiple";
|
|
1591
1758
|
const hasSummaryRow = (0, import_react5.useMemo)(() => rows.length > 0 && summaryEnabled && columns.some((column) => Boolean(column.summary)), [columns, rows.length, summaryEnabled]);
|
|
@@ -1596,7 +1763,7 @@ function Table({
|
|
|
1596
1763
|
const realContentHeight = bodyTop + bodyContentHeight + bottomSummaryHeight + (contentWidth > viewport.width ? horizontalScrollbarHeight : 0);
|
|
1597
1764
|
const effectiveHeight = autoHeight ? Math.max(bodyTop + bottomSummaryHeight, Math.min(resolvedHeight, realContentHeight)) : resolvedHeight;
|
|
1598
1765
|
const renderHeight = Math.max(0, effectiveHeight - bottomSummaryHeight);
|
|
1599
|
-
const bodyViewportHeight = Math.max(0, renderHeight - (fixedHeader ? headerHeight : 0)
|
|
1766
|
+
const bodyViewportHeight = Math.max(0, renderHeight - (fixedHeader ? headerHeight + topSummaryHeight : 0));
|
|
1600
1767
|
const [summaryState, setSummaryState] = (0, import_react5.useState)(() => ({ columns: null, rows: null, values: /* @__PURE__ */ new Map(), pending: false }));
|
|
1601
1768
|
const summaryPending = hasSummaryRow && (loading || summaryState.pending || summaryState.columns !== columns || summaryState.rows !== rows);
|
|
1602
1769
|
const summaryValues = hasSummaryRow && summaryState.columns === columns && summaryState.rows === rows ? summaryState.values : /* @__PURE__ */ new Map();
|
|
@@ -1643,14 +1810,30 @@ function Table({
|
|
|
1643
1810
|
rowIndexByKeyRef.current.set(key, index);
|
|
1644
1811
|
return key;
|
|
1645
1812
|
}, [getDataRowKey]);
|
|
1813
|
+
const resolvedCellSpans = (0, import_react5.useMemo)(() => {
|
|
1814
|
+
if (Array.isArray(cellSpans)) return cellSpans;
|
|
1815
|
+
const spans = [];
|
|
1816
|
+
rows.forEach((row, rowIndex) => {
|
|
1817
|
+
columns.forEach((column, columnIndex) => {
|
|
1818
|
+
const span = cellSpans({ row, rowIndex, column, columnIndex, rows, columns });
|
|
1819
|
+
if (!span) return;
|
|
1820
|
+
spans.push({
|
|
1821
|
+
rowIndex,
|
|
1822
|
+
columnKey: column.key,
|
|
1823
|
+
...span
|
|
1824
|
+
});
|
|
1825
|
+
});
|
|
1826
|
+
});
|
|
1827
|
+
return spans;
|
|
1828
|
+
}, [cellSpans, columns, rows]);
|
|
1646
1829
|
const cellSpanLookup = (0, import_react5.useMemo)(() => {
|
|
1647
|
-
if (
|
|
1830
|
+
if (resolvedCellSpans.length === 0) return createEmptyCellSpanLookup();
|
|
1648
1831
|
const rowIndexByKey = /* @__PURE__ */ new Map();
|
|
1649
1832
|
rows.forEach((row, index) => rowIndexByKey.set(getRowKey(row, index), index));
|
|
1650
1833
|
const columnIndexByKey = new Map(columns.map((column, index) => [column.key, index]));
|
|
1651
1834
|
const lookup = createEmptyCellSpanLookup();
|
|
1652
1835
|
const occupied = /* @__PURE__ */ new Set();
|
|
1653
|
-
for (const span of
|
|
1836
|
+
for (const span of resolvedCellSpans) {
|
|
1654
1837
|
const rowIndex = span.rowKey !== void 0 ? rowIndexByKey.get(span.rowKey) : span.rowIndex;
|
|
1655
1838
|
const columnIndex = columnIndexByKey.get(span.columnKey);
|
|
1656
1839
|
if (rowIndex === void 0 || columnIndex === void 0) continue;
|
|
@@ -1686,7 +1869,7 @@ function Table({
|
|
|
1686
1869
|
}
|
|
1687
1870
|
}
|
|
1688
1871
|
return lookup;
|
|
1689
|
-
}, [
|
|
1872
|
+
}, [columns, getRowKey, resolvedCellSpans, rows]);
|
|
1690
1873
|
const selectedRowKeySet = (0, import_react5.useMemo)(() => new Set(rowKeys), [rowKeys]);
|
|
1691
1874
|
const selectedColumnKeySet = (0, import_react5.useMemo)(() => new Set(columnKeys), [columnKeys]);
|
|
1692
1875
|
const insertedRowKeySet = (0, import_react5.useMemo)(() => insertedRowKeys, [insertedRowKeys]);
|
|
@@ -1756,8 +1939,8 @@ function Table({
|
|
|
1756
1939
|
const scroll = scrollRef.current;
|
|
1757
1940
|
const rowScrollTop = fixedHeader ? scroll.top : Math.max(0, scroll.top - bodyTop);
|
|
1758
1941
|
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 };
|
|
1759
|
-
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 });
|
|
1760
|
-
}, [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]);
|
|
1942
|
+
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 });
|
|
1943
|
+
}, [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]);
|
|
1761
1944
|
const scheduleDraw = (0, import_react5.useCallback)(() => {
|
|
1762
1945
|
if (frameRef.current === null) frameRef.current = requestAnimationFrame(draw);
|
|
1763
1946
|
}, [draw]);
|
|
@@ -1794,6 +1977,8 @@ function Table({
|
|
|
1794
1977
|
if (sortDebounceRef.current !== null) clearTimeout(sortDebounceRef.current);
|
|
1795
1978
|
if (textFrameRef.current !== null) cancelAnimationFrame(textFrameRef.current);
|
|
1796
1979
|
if (toastTimerRef.current !== null) clearTimeout(toastTimerRef.current);
|
|
1980
|
+
if (verticalScrollbarVisibilityTimerRef.current !== null) clearTimeout(verticalScrollbarVisibilityTimerRef.current);
|
|
1981
|
+
if (horizontalScrollbarVisibilityTimerRef.current !== null) clearTimeout(horizontalScrollbarVisibilityTimerRef.current);
|
|
1797
1982
|
}, []);
|
|
1798
1983
|
(0, import_react5.useEffect)(() => {
|
|
1799
1984
|
const scroller = scrollerRef.current;
|
|
@@ -1845,9 +2030,204 @@ function Table({
|
|
|
1845
2030
|
scroller.addEventListener("scroll", lockScroll);
|
|
1846
2031
|
return () => scroller.removeEventListener("scroll", lockScroll);
|
|
1847
2032
|
}, [insertBusy]);
|
|
2033
|
+
const showCustomScrollbar = (0, import_react5.useCallback)((axis, persist = false) => {
|
|
2034
|
+
const showVertical = axis === "vertical" || axis === "both";
|
|
2035
|
+
const showHorizontal = axis === "horizontal" || axis === "both";
|
|
2036
|
+
if (showVertical) {
|
|
2037
|
+
verticalScrollbarRef.current?.classList.add("is-active");
|
|
2038
|
+
if (verticalScrollbarVisibilityTimerRef.current !== null) clearTimeout(verticalScrollbarVisibilityTimerRef.current);
|
|
2039
|
+
if (!persist) {
|
|
2040
|
+
verticalScrollbarVisibilityTimerRef.current = setTimeout(() => {
|
|
2041
|
+
verticalScrollbarRef.current?.classList.remove("is-active");
|
|
2042
|
+
verticalScrollbarVisibilityTimerRef.current = null;
|
|
2043
|
+
}, 900);
|
|
2044
|
+
}
|
|
2045
|
+
}
|
|
2046
|
+
if (showHorizontal) {
|
|
2047
|
+
horizontalScrollbarRef.current?.classList.add("is-active");
|
|
2048
|
+
if (horizontalScrollbarVisibilityTimerRef.current !== null) clearTimeout(horizontalScrollbarVisibilityTimerRef.current);
|
|
2049
|
+
if (!persist) {
|
|
2050
|
+
horizontalScrollbarVisibilityTimerRef.current = setTimeout(() => {
|
|
2051
|
+
horizontalScrollbarRef.current?.classList.remove("is-active");
|
|
2052
|
+
horizontalScrollbarVisibilityTimerRef.current = null;
|
|
2053
|
+
}, 900);
|
|
2054
|
+
}
|
|
2055
|
+
}
|
|
2056
|
+
}, []);
|
|
2057
|
+
const keepCustomScrollbarVisible = (0, import_react5.useCallback)((axis) => {
|
|
2058
|
+
showCustomScrollbar(axis, true);
|
|
2059
|
+
}, [showCustomScrollbar]);
|
|
2060
|
+
const releaseCustomScrollbarVisible = (0, import_react5.useCallback)((axis) => {
|
|
2061
|
+
if (axis === "vertical" && verticalScrollbarDragRef.current) return;
|
|
2062
|
+
if (axis === "horizontal" && horizontalScrollbarDragRef.current) return;
|
|
2063
|
+
showCustomScrollbar(axis);
|
|
2064
|
+
}, [showCustomScrollbar]);
|
|
2065
|
+
const updateCustomScrollbars = (0, import_react5.useCallback)(() => {
|
|
2066
|
+
const scroller = scrollerRef.current;
|
|
2067
|
+
const verticalTrack = verticalScrollbarRef.current;
|
|
2068
|
+
const verticalThumb = verticalScrollbarThumbRef.current;
|
|
2069
|
+
const horizontalTrack = horizontalScrollbarRef.current;
|
|
2070
|
+
const horizontalThumb = horizontalScrollbarThumbRef.current;
|
|
2071
|
+
if (!scroller || !verticalTrack || !verticalThumb || !horizontalTrack || !horizontalThumb) return;
|
|
2072
|
+
const maxScrollTop = scroller.scrollHeight - scroller.clientHeight;
|
|
2073
|
+
if (maxScrollTop <= 1 || renderHeight <= 0) {
|
|
2074
|
+
verticalTrack.style.display = "none";
|
|
2075
|
+
} else {
|
|
2076
|
+
const top = fixedHeader ? bodyTop : 0;
|
|
2077
|
+
const bottom = bottomSummaryHeight;
|
|
2078
|
+
const trackHeight = Math.max(0, effectiveHeight - top - bottom);
|
|
2079
|
+
if (trackHeight <= 0) {
|
|
2080
|
+
verticalTrack.style.display = "none";
|
|
2081
|
+
} else {
|
|
2082
|
+
const thumbHeight = Math.max(28, Math.round(trackHeight * (scroller.clientHeight / scroller.scrollHeight)));
|
|
2083
|
+
const maxThumbTop = Math.max(0, trackHeight - thumbHeight);
|
|
2084
|
+
const thumbTop = maxScrollTop > 0 ? Math.round(scroller.scrollTop / maxScrollTop * maxThumbTop) : 0;
|
|
2085
|
+
verticalTrack.style.display = "block";
|
|
2086
|
+
verticalTrack.style.top = `${top}px`;
|
|
2087
|
+
verticalTrack.style.bottom = `${bottom}px`;
|
|
2088
|
+
verticalThumb.style.height = `${Math.min(trackHeight, thumbHeight)}px`;
|
|
2089
|
+
verticalThumb.style.transform = `translateY(${thumbTop}px)`;
|
|
2090
|
+
}
|
|
2091
|
+
}
|
|
2092
|
+
const maxScrollLeft = scroller.scrollWidth - scroller.clientWidth;
|
|
2093
|
+
if (maxScrollLeft <= 1 || viewport.width <= 0) {
|
|
2094
|
+
horizontalTrack.style.display = "none";
|
|
2095
|
+
return;
|
|
2096
|
+
}
|
|
2097
|
+
const trackLeft = fixedWidth;
|
|
2098
|
+
const trackRight = rightFixedWidth;
|
|
2099
|
+
const trackWidth = Math.max(0, viewport.width - trackLeft - trackRight);
|
|
2100
|
+
if (trackWidth <= 0) {
|
|
2101
|
+
horizontalTrack.style.display = "none";
|
|
2102
|
+
return;
|
|
2103
|
+
}
|
|
2104
|
+
const thumbWidth = Math.max(36, Math.round(trackWidth * (scroller.clientWidth / scroller.scrollWidth)));
|
|
2105
|
+
const maxThumbLeft = Math.max(0, trackWidth - thumbWidth);
|
|
2106
|
+
const thumbLeft = maxScrollLeft > 0 ? Math.round(scroller.scrollLeft / maxScrollLeft * maxThumbLeft) : 0;
|
|
2107
|
+
horizontalTrack.style.display = "block";
|
|
2108
|
+
horizontalTrack.style.left = `${trackLeft}px`;
|
|
2109
|
+
horizontalTrack.style.right = `${trackRight}px`;
|
|
2110
|
+
horizontalTrack.style.bottom = `${bottomSummaryHeight + 2}px`;
|
|
2111
|
+
horizontalThumb.style.width = `${Math.min(trackWidth, thumbWidth)}px`;
|
|
2112
|
+
horizontalThumb.style.transform = `translateX(${thumbLeft}px)`;
|
|
2113
|
+
}, [bodyTop, bottomSummaryHeight, effectiveHeight, fixedHeader, fixedWidth, renderHeight, rightFixedWidth, viewport.width]);
|
|
2114
|
+
(0, import_react5.useLayoutEffect)(() => {
|
|
2115
|
+
updateCustomScrollbars();
|
|
2116
|
+
}, [contentHeight, contentWidth, updateCustomScrollbars, viewport.height, viewport.width]);
|
|
2117
|
+
const handleVerticalScrollbarPointerDown = (0, import_react5.useCallback)((event) => {
|
|
2118
|
+
const scroller = scrollerRef.current;
|
|
2119
|
+
const track = verticalScrollbarRef.current;
|
|
2120
|
+
const thumb = verticalScrollbarThumbRef.current;
|
|
2121
|
+
if (!scroller || !track || !thumb || event.button !== 0) return;
|
|
2122
|
+
event.preventDefault();
|
|
2123
|
+
event.currentTarget.setPointerCapture(event.pointerId);
|
|
2124
|
+
const trackRect = track.getBoundingClientRect();
|
|
2125
|
+
const thumbRect = thumb.getBoundingClientRect();
|
|
2126
|
+
const maxScrollTop = scroller.scrollHeight - scroller.clientHeight;
|
|
2127
|
+
const trackHeight = trackRect.height;
|
|
2128
|
+
const thumbHeight = thumbRect.height;
|
|
2129
|
+
showCustomScrollbar("vertical", true);
|
|
2130
|
+
if (event.target === thumb) {
|
|
2131
|
+
verticalScrollbarDragRef.current = {
|
|
2132
|
+
pointerId: event.pointerId,
|
|
2133
|
+
startY: event.clientY,
|
|
2134
|
+
startTop: thumbRect.top - trackRect.top,
|
|
2135
|
+
trackHeight,
|
|
2136
|
+
thumbHeight,
|
|
2137
|
+
maxScrollTop
|
|
2138
|
+
};
|
|
2139
|
+
return;
|
|
2140
|
+
}
|
|
2141
|
+
const nextThumbTop = Math.max(0, Math.min(trackHeight - thumbHeight, event.clientY - trackRect.top - thumbHeight / 2));
|
|
2142
|
+
const maxThumbTop = Math.max(1, trackHeight - thumbHeight);
|
|
2143
|
+
scroller.scrollTop = nextThumbTop / maxThumbTop * maxScrollTop;
|
|
2144
|
+
updateCustomScrollbars();
|
|
2145
|
+
verticalScrollbarDragRef.current = {
|
|
2146
|
+
pointerId: event.pointerId,
|
|
2147
|
+
startY: event.clientY,
|
|
2148
|
+
startTop: nextThumbTop,
|
|
2149
|
+
trackHeight,
|
|
2150
|
+
thumbHeight,
|
|
2151
|
+
maxScrollTop
|
|
2152
|
+
};
|
|
2153
|
+
}, [showCustomScrollbar, updateCustomScrollbars]);
|
|
2154
|
+
const handleVerticalScrollbarPointerMove = (0, import_react5.useCallback)((event) => {
|
|
2155
|
+
const drag = verticalScrollbarDragRef.current;
|
|
2156
|
+
const scroller = scrollerRef.current;
|
|
2157
|
+
if (!drag || !scroller || drag.pointerId !== event.pointerId) return;
|
|
2158
|
+
event.preventDefault();
|
|
2159
|
+
const maxThumbTop = Math.max(1, drag.trackHeight - drag.thumbHeight);
|
|
2160
|
+
const nextThumbTop = Math.max(0, Math.min(maxThumbTop, drag.startTop + event.clientY - drag.startY));
|
|
2161
|
+
scroller.scrollTop = nextThumbTop / maxThumbTop * drag.maxScrollTop;
|
|
2162
|
+
updateCustomScrollbars();
|
|
2163
|
+
}, [updateCustomScrollbars]);
|
|
2164
|
+
const handleVerticalScrollbarPointerUp = (0, import_react5.useCallback)((event) => {
|
|
2165
|
+
const drag = verticalScrollbarDragRef.current;
|
|
2166
|
+
if (!drag || drag.pointerId !== event.pointerId) return;
|
|
2167
|
+
if (event.currentTarget.hasPointerCapture(event.pointerId)) event.currentTarget.releasePointerCapture(event.pointerId);
|
|
2168
|
+
verticalScrollbarDragRef.current = null;
|
|
2169
|
+
showCustomScrollbar("vertical");
|
|
2170
|
+
}, [showCustomScrollbar]);
|
|
2171
|
+
const handleHorizontalScrollbarPointerDown = (0, import_react5.useCallback)((event) => {
|
|
2172
|
+
const scroller = scrollerRef.current;
|
|
2173
|
+
const track = horizontalScrollbarRef.current;
|
|
2174
|
+
const thumb = horizontalScrollbarThumbRef.current;
|
|
2175
|
+
if (!scroller || !track || !thumb || event.button !== 0) return;
|
|
2176
|
+
event.preventDefault();
|
|
2177
|
+
event.currentTarget.setPointerCapture(event.pointerId);
|
|
2178
|
+
const trackRect = track.getBoundingClientRect();
|
|
2179
|
+
const thumbRect = thumb.getBoundingClientRect();
|
|
2180
|
+
const maxScrollLeft = scroller.scrollWidth - scroller.clientWidth;
|
|
2181
|
+
const trackWidth = trackRect.width;
|
|
2182
|
+
const thumbWidth = thumbRect.width;
|
|
2183
|
+
showCustomScrollbar("horizontal", true);
|
|
2184
|
+
if (event.target === thumb) {
|
|
2185
|
+
horizontalScrollbarDragRef.current = {
|
|
2186
|
+
pointerId: event.pointerId,
|
|
2187
|
+
startX: event.clientX,
|
|
2188
|
+
startLeft: thumbRect.left - trackRect.left,
|
|
2189
|
+
trackWidth,
|
|
2190
|
+
thumbWidth,
|
|
2191
|
+
maxScrollLeft
|
|
2192
|
+
};
|
|
2193
|
+
return;
|
|
2194
|
+
}
|
|
2195
|
+
const nextThumbLeft = Math.max(0, Math.min(trackWidth - thumbWidth, event.clientX - trackRect.left - thumbWidth / 2));
|
|
2196
|
+
const maxThumbLeft = Math.max(1, trackWidth - thumbWidth);
|
|
2197
|
+
scroller.scrollLeft = nextThumbLeft / maxThumbLeft * maxScrollLeft;
|
|
2198
|
+
updateCustomScrollbars();
|
|
2199
|
+
horizontalScrollbarDragRef.current = {
|
|
2200
|
+
pointerId: event.pointerId,
|
|
2201
|
+
startX: event.clientX,
|
|
2202
|
+
startLeft: nextThumbLeft,
|
|
2203
|
+
trackWidth,
|
|
2204
|
+
thumbWidth,
|
|
2205
|
+
maxScrollLeft
|
|
2206
|
+
};
|
|
2207
|
+
}, [showCustomScrollbar, updateCustomScrollbars]);
|
|
2208
|
+
const handleHorizontalScrollbarPointerMove = (0, import_react5.useCallback)((event) => {
|
|
2209
|
+
const drag = horizontalScrollbarDragRef.current;
|
|
2210
|
+
const scroller = scrollerRef.current;
|
|
2211
|
+
if (!drag || !scroller || drag.pointerId !== event.pointerId) return;
|
|
2212
|
+
event.preventDefault();
|
|
2213
|
+
const maxThumbLeft = Math.max(1, drag.trackWidth - drag.thumbWidth);
|
|
2214
|
+
const nextThumbLeft = Math.max(0, Math.min(maxThumbLeft, drag.startLeft + event.clientX - drag.startX));
|
|
2215
|
+
scroller.scrollLeft = nextThumbLeft / maxThumbLeft * drag.maxScrollLeft;
|
|
2216
|
+
updateCustomScrollbars();
|
|
2217
|
+
}, [updateCustomScrollbars]);
|
|
2218
|
+
const handleHorizontalScrollbarPointerUp = (0, import_react5.useCallback)((event) => {
|
|
2219
|
+
const drag = horizontalScrollbarDragRef.current;
|
|
2220
|
+
if (!drag || drag.pointerId !== event.pointerId) return;
|
|
2221
|
+
if (event.currentTarget.hasPointerCapture(event.pointerId)) event.currentTarget.releasePointerCapture(event.pointerId);
|
|
2222
|
+
horizontalScrollbarDragRef.current = null;
|
|
2223
|
+
showCustomScrollbar("horizontal");
|
|
2224
|
+
}, [showCustomScrollbar]);
|
|
1848
2225
|
const handleScroll = (0, import_react5.useCallback)(() => {
|
|
1849
2226
|
const element = scrollerRef.current;
|
|
1850
2227
|
if (!element) return;
|
|
2228
|
+
const previousScroll = scrollRef.current;
|
|
2229
|
+
const scrollDeltaX = element.scrollLeft - previousScroll.left;
|
|
2230
|
+
const scrollDeltaY = element.scrollTop - previousScroll.top;
|
|
1851
2231
|
scrollRef.current = { left: element.scrollLeft, top: element.scrollTop };
|
|
1852
2232
|
const focus = selectionFocusRef.current;
|
|
1853
2233
|
if (focus && selection && !selectionRange) {
|
|
@@ -1858,7 +2238,7 @@ function Table({
|
|
|
1858
2238
|
if (!validSelection) {
|
|
1859
2239
|
focus.style.display = "none";
|
|
1860
2240
|
} else {
|
|
1861
|
-
const rawLeft = column.fixed === "left" ?
|
|
2241
|
+
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;
|
|
1862
2242
|
const rawTop = bodyTop + selection.rowIndex * rowHeight - element.scrollTop;
|
|
1863
2243
|
const span = getCellSpan(selection.rowIndex, selection.columnIndex);
|
|
1864
2244
|
const rawRight = rawLeft + getCellDisplayWidth(selection.columnIndex, span?.colSpan ?? 1);
|
|
@@ -1891,6 +2271,10 @@ function Table({
|
|
|
1891
2271
|
if (scrollingTextRef.current) scrollingTextRef.current.style.transform = `translateX(${-element.scrollLeft}px)`;
|
|
1892
2272
|
if (scrollingHeaderIconsRef.current) scrollingHeaderIconsRef.current.style.transform = `translateX(${-element.scrollLeft}px)`;
|
|
1893
2273
|
if (headerIconsRef.current) headerIconsRef.current.style.transform = `translateY(${fixedHeader ? 0 : -element.scrollTop}px)`;
|
|
2274
|
+
updateCustomScrollbars();
|
|
2275
|
+
if (scrollDeltaX !== 0 || scrollDeltaY !== 0) {
|
|
2276
|
+
showCustomScrollbar(scrollDeltaX !== 0 && scrollDeltaY !== 0 ? "both" : scrollDeltaX !== 0 ? "horizontal" : "vertical");
|
|
2277
|
+
}
|
|
1894
2278
|
if (hoveredHeaderActionRef.current) {
|
|
1895
2279
|
hoveredHeaderActionRef.current = null;
|
|
1896
2280
|
setHoveredHeaderAction(null);
|
|
@@ -1898,7 +2282,7 @@ function Table({
|
|
|
1898
2282
|
setHoveredCellTooltip(null);
|
|
1899
2283
|
scheduleDraw();
|
|
1900
2284
|
setEditing(null);
|
|
1901
|
-
}, [bodyTop, columns, renderHeight, fixedHeader, fixedWidth, getCellDisplayWidth, getCellSpan, getRowKey, metrics, rightFixedOffsets, rightFixedWidth, rowHeight, rows, scheduleDraw, selection, selectionRange, viewport.width]);
|
|
2285
|
+
}, [bodyTop, columns, renderHeight, fixedHeader, fixedWidth, getCellDisplayWidth, getCellSpan, getRowKey, getScrollableColumnLeft, metrics, rightFixedOffsets, rightFixedWidth, rowHeight, rows, scheduleDraw, selection, selectionRange, showCustomScrollbar, updateCustomScrollbars, viewport.width]);
|
|
1902
2286
|
const locateColumn = (0, import_react5.useCallback)((clientX) => {
|
|
1903
2287
|
const canvas = canvasRef.current;
|
|
1904
2288
|
if (!canvas) return -1;
|
|
@@ -1906,7 +2290,11 @@ function Table({
|
|
|
1906
2290
|
const localX = clientX - rect.left;
|
|
1907
2291
|
let columnIndex;
|
|
1908
2292
|
if (localX < fixedWidth) {
|
|
1909
|
-
columnIndex =
|
|
2293
|
+
columnIndex = columns.findIndex((column, index) => {
|
|
2294
|
+
if (column.fixed !== "left") return false;
|
|
2295
|
+
const left = leftFixedOffsets.get(index) ?? 0;
|
|
2296
|
+
return localX >= left && localX < left + metrics[index].width;
|
|
2297
|
+
});
|
|
1910
2298
|
} else if (rightFixedWidth > 0 && localX >= viewport.width - rightFixedWidth) {
|
|
1911
2299
|
columnIndex = columns.findIndex((column, index) => {
|
|
1912
2300
|
if (column.fixed !== "right") return false;
|
|
@@ -1914,31 +2302,33 @@ function Table({
|
|
|
1914
2302
|
return localX >= left && localX < left + metrics[index].width;
|
|
1915
2303
|
});
|
|
1916
2304
|
} else {
|
|
1917
|
-
|
|
2305
|
+
const scrollX = localX + scrollRef.current.left;
|
|
2306
|
+
columnIndex = columns.findIndex((column, index) => {
|
|
2307
|
+
if (column.fixed !== void 0) return false;
|
|
2308
|
+
const left = getScrollableColumnLeft(index);
|
|
2309
|
+
return scrollX >= left && scrollX < left + metrics[index].width;
|
|
2310
|
+
});
|
|
1918
2311
|
}
|
|
1919
2312
|
return columnIndex;
|
|
1920
|
-
}, [columns, fixedWidth, metrics, rightFixedOffsets, rightFixedWidth, viewport.width]);
|
|
2313
|
+
}, [columns, fixedWidth, getScrollableColumnLeft, leftFixedOffsets, metrics, rightFixedOffsets, rightFixedWidth, viewport.width]);
|
|
1921
2314
|
const getDisplayedColumnLeft = (0, import_react5.useCallback)((columnIndex) => {
|
|
1922
2315
|
const metric = metrics[columnIndex];
|
|
1923
|
-
if (columns[columnIndex].fixed === "left") return
|
|
2316
|
+
if (columns[columnIndex].fixed === "left") return leftFixedOffsets.get(columnIndex) ?? 0;
|
|
1924
2317
|
if (columns[columnIndex].fixed === "right") return viewport.width - (rightFixedOffsets.get(columnIndex) ?? 0) - metric.width;
|
|
1925
|
-
return
|
|
1926
|
-
}, [columns, metrics, rightFixedOffsets, viewport.width]);
|
|
1927
|
-
const
|
|
1928
|
-
|
|
1929
|
-
|
|
1930
|
-
|
|
1931
|
-
|
|
1932
|
-
for (let index = 0; index < columns.length; index += 1) {
|
|
1933
|
-
if (columns[index].rowSelection || columns[index].rowDragHandle || columns[index].rowNumber) continue;
|
|
1934
|
-
const nextDistance = Math.abs(localX - (getDisplayedColumnLeft(index) + metrics[index].width));
|
|
1935
|
-
if (nextDistance < distance) {
|
|
1936
|
-
distance = nextDistance;
|
|
1937
|
-
closest = index;
|
|
1938
|
-
}
|
|
2318
|
+
return getScrollableColumnLeft(columnIndex) - scrollRef.current.left;
|
|
2319
|
+
}, [columns, getScrollableColumnLeft, leftFixedOffsets, metrics, rightFixedOffsets, viewport.width]);
|
|
2320
|
+
const getHeaderLevelAtY = (0, import_react5.useCallback)((localY, headerY) => {
|
|
2321
|
+
const y = localY - headerY;
|
|
2322
|
+
for (let level = 0; level < headerRowHeights.length; level += 1) {
|
|
2323
|
+
const top = headerRowOffsets[level] ?? 0;
|
|
2324
|
+
if (y >= top && y < top + headerRowHeights[level]) return level;
|
|
1939
2325
|
}
|
|
1940
|
-
return
|
|
1941
|
-
}, [
|
|
2326
|
+
return headerDepth - 1;
|
|
2327
|
+
}, [headerDepth, headerRowHeights, headerRowOffsets]);
|
|
2328
|
+
const resizeColumn = (0, import_react5.useCallback)((columnKey, width2) => {
|
|
2329
|
+
setResizedColumnWidths((current) => current[columnKey] === width2 ? current : { ...current, [columnKey]: width2 });
|
|
2330
|
+
onColumnResize?.(columnKey, width2);
|
|
2331
|
+
}, [onColumnResize]);
|
|
1942
2332
|
const locateHeaderAction = (0, import_react5.useCallback)((clientX, columnIndex) => {
|
|
1943
2333
|
if (columnIndex < 0 || !canvasRef.current) return null;
|
|
1944
2334
|
const column = columns[columnIndex];
|
|
@@ -1973,11 +2363,70 @@ function Table({
|
|
|
1973
2363
|
const columnRight = getDisplayedColumnLeft(columnIndex) + metrics[columnIndex].width;
|
|
1974
2364
|
return localX >= columnRight - 16 && localX < columnRight - 5;
|
|
1975
2365
|
}, [columnDraggable, columns, getDisplayedColumnLeft, measureHeaderTitleWidth, metrics]);
|
|
1976
|
-
const
|
|
2366
|
+
const getHeaderCellLeft = (0, import_react5.useCallback)((cell) => {
|
|
2367
|
+
const startMetric = metrics[cell.startIndex];
|
|
2368
|
+
const fixedSide = columns[cell.startIndex]?.fixed;
|
|
2369
|
+
if (!startMetric) return 0;
|
|
2370
|
+
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;
|
|
2371
|
+
}, [columns, getScrollableColumnLeft, leftFixedOffsets, metrics, rightFixedOffsets, viewport.width]);
|
|
2372
|
+
const getHeaderCellWidth = (0, import_react5.useCallback)((cell) => cell.leaf ? metrics[cell.startIndex]?.width ?? 0 : getCellDisplayWidth(cell.startIndex, cell.endIndex - cell.startIndex + 1), [getCellDisplayWidth, metrics]);
|
|
2373
|
+
const locateHeaderResizeHit = (0, import_react5.useCallback)((clientX, clientY) => {
|
|
2374
|
+
if (!columnResizable || !canvasRef.current) return null;
|
|
2375
|
+
const rect = canvasRef.current.getBoundingClientRect();
|
|
2376
|
+
const localX = clientX - rect.left;
|
|
2377
|
+
const localY = clientY - rect.top;
|
|
2378
|
+
const headerY = fixedHeader ? 0 : -scrollRef.current.top;
|
|
2379
|
+
if (localY < headerY || localY >= headerY + headerHeight) return null;
|
|
2380
|
+
const level = getHeaderLevelAtY(localY, headerY);
|
|
2381
|
+
let closest = null;
|
|
2382
|
+
let distance = 6;
|
|
2383
|
+
for (const cell of headerCells) {
|
|
2384
|
+
if (cell.level !== level) continue;
|
|
2385
|
+
if (columns[cell.startIndex]?.rowSelection || columns[cell.startIndex]?.rowDragHandle || columns[cell.startIndex]?.rowNumber) continue;
|
|
2386
|
+
const fixedSide = columns[cell.startIndex]?.fixed;
|
|
2387
|
+
const edge = fixedSide === "right" && cell.leaf ? "left" : "right";
|
|
2388
|
+
const edgeX = getHeaderCellLeft(cell) + (edge === "right" ? getHeaderCellWidth(cell) : 0);
|
|
2389
|
+
const nextDistance = Math.abs(localX - edgeX);
|
|
2390
|
+
if (nextDistance < distance) {
|
|
2391
|
+
distance = nextDistance;
|
|
2392
|
+
closest = { cell, edge };
|
|
2393
|
+
}
|
|
2394
|
+
}
|
|
2395
|
+
return closest;
|
|
2396
|
+
}, [columnResizable, columns, fixedHeader, getHeaderCellLeft, getHeaderCellWidth, getHeaderLevelAtY, headerCells, headerHeight]);
|
|
2397
|
+
const locateHeaderResizeCell = (0, import_react5.useCallback)((clientX, clientY) => locateHeaderResizeHit(clientX, clientY)?.cell ?? null, [locateHeaderResizeHit]);
|
|
2398
|
+
const locateHeaderCellDragHandle = (0, import_react5.useCallback)((clientX, cell) => {
|
|
2399
|
+
if (!columnDraggable || !canvasRef.current) return false;
|
|
2400
|
+
if (cell.leaf) return locateHeaderDragHandle(clientX, cell.startIndex);
|
|
2401
|
+
const localX = clientX - canvasRef.current.getBoundingClientRect().left;
|
|
2402
|
+
const right = getHeaderCellLeft(cell) + getHeaderCellWidth(cell);
|
|
2403
|
+
return localX >= right - 16 && localX < right - 5;
|
|
2404
|
+
}, [columnDraggable, getHeaderCellLeft, getHeaderCellWidth, locateHeaderDragHandle]);
|
|
2405
|
+
const locateHeaderCell = (0, import_react5.useCallback)((clientX, clientY) => {
|
|
2406
|
+
const canvas = canvasRef.current;
|
|
2407
|
+
if (!canvas) return null;
|
|
2408
|
+
const rect = canvas.getBoundingClientRect();
|
|
2409
|
+
const headerY = fixedHeader ? 0 : -scrollRef.current.top;
|
|
2410
|
+
const localX = clientX - rect.left;
|
|
2411
|
+
const localY = clientY - rect.top;
|
|
2412
|
+
if (localY < headerY || localY >= headerY + headerHeight) return null;
|
|
2413
|
+
const level = getHeaderLevelAtY(localY, headerY);
|
|
2414
|
+
for (const cell of headerCells) {
|
|
2415
|
+
if (cell.level !== level) continue;
|
|
2416
|
+
const left = getHeaderCellLeft(cell);
|
|
2417
|
+
const width2 = getHeaderCellWidth(cell);
|
|
2418
|
+
if (localX >= left && localX < left + width2) return cell;
|
|
2419
|
+
}
|
|
2420
|
+
return null;
|
|
2421
|
+
}, [fixedHeader, getHeaderCellLeft, getHeaderCellWidth, getHeaderLevelAtY, headerCells, headerHeight]);
|
|
2422
|
+
const setDragGuide = (0, import_react5.useCallback)((x, top = 0) => {
|
|
1977
2423
|
const guide = dragGuideRef.current;
|
|
1978
2424
|
if (!guide) return;
|
|
1979
2425
|
guide.style.display = x === null ? "none" : "block";
|
|
1980
|
-
if (x !== null)
|
|
2426
|
+
if (x !== null) {
|
|
2427
|
+
guide.style.top = `${top}px`;
|
|
2428
|
+
guide.style.transform = `translateX(${Math.round(x)}px)`;
|
|
2429
|
+
}
|
|
1981
2430
|
}, []);
|
|
1982
2431
|
const setRowDragGuide = (0, import_react5.useCallback)((y) => {
|
|
1983
2432
|
const guide = rowDragGuideRef.current;
|
|
@@ -1995,6 +2444,15 @@ function Table({
|
|
|
1995
2444
|
(0, import_react5.useEffect)(() => {
|
|
1996
2445
|
const canvas = canvasRef.current;
|
|
1997
2446
|
if (!canvas || !columnDraggable && !rowDraggable) return;
|
|
2447
|
+
const getColumnDropIndex = (sourceIndex, targetIndex, edge) => {
|
|
2448
|
+
if (edge === "right") return targetIndex;
|
|
2449
|
+
const destinationIndex = targetIndex - (sourceIndex < targetIndex ? 1 : 0);
|
|
2450
|
+
return Math.max(0, Math.min(columns.length - 1, destinationIndex));
|
|
2451
|
+
};
|
|
2452
|
+
const getColumnDropPlacement = (sourceIndex, targetIndex, edge) => {
|
|
2453
|
+
if (edge !== "right") return "before";
|
|
2454
|
+
return targetIndex < sourceIndex ? "before" : "after";
|
|
2455
|
+
};
|
|
1998
2456
|
const getHeaderColumn = (clientX, clientY) => {
|
|
1999
2457
|
const rect = canvas.getBoundingClientRect();
|
|
2000
2458
|
const headerY = fixedHeader ? 0 : -scrollRef.current.top;
|
|
@@ -2002,22 +2460,35 @@ function Table({
|
|
|
2002
2460
|
if (localY < headerY || localY >= headerY + headerHeight) return -1;
|
|
2003
2461
|
return locateColumn(clientX);
|
|
2004
2462
|
};
|
|
2463
|
+
const getHeaderDragCell = (clientX, clientY) => {
|
|
2464
|
+
const cell = locateHeaderCell(clientX, clientY);
|
|
2465
|
+
if (!cell) return null;
|
|
2466
|
+
return locateHeaderCellDragHandle(clientX, cell) && !locateHeaderResizeCell(clientX, clientY) ? cell : null;
|
|
2467
|
+
};
|
|
2005
2468
|
const cleanupDrag = (0, import_adapter.draggable)({
|
|
2006
2469
|
element: canvas,
|
|
2007
2470
|
canDrag: ({ input }) => {
|
|
2008
|
-
const
|
|
2009
|
-
if (
|
|
2471
|
+
const headerDragCell = columnDraggable ? getHeaderDragCell(input.clientX, input.clientY) : null;
|
|
2472
|
+
if (headerDragCell) return true;
|
|
2010
2473
|
const columnIndex = locateColumn(input.clientX);
|
|
2011
2474
|
return rowDraggable && columnIndex >= 0 && Boolean(columns[columnIndex].rowDragHandle) && locateRow(input.clientY) >= 0;
|
|
2012
2475
|
},
|
|
2013
2476
|
getInitialData: ({ input }) => {
|
|
2014
|
-
const
|
|
2015
|
-
if (
|
|
2477
|
+
const headerDragCell = columnDraggable ? getHeaderDragCell(input.clientX, input.clientY) : null;
|
|
2478
|
+
if (!headerDragCell) {
|
|
2016
2479
|
return { type: "table-row", sourceIndex: locateRow(input.clientY) };
|
|
2017
2480
|
}
|
|
2018
|
-
const sourceIndex =
|
|
2481
|
+
const sourceIndex = headerDragCell.startIndex;
|
|
2019
2482
|
columnDragPreviewRef.current = { sourceIndex, targetIndex: sourceIndex };
|
|
2020
|
-
return {
|
|
2483
|
+
return {
|
|
2484
|
+
type: "table-column",
|
|
2485
|
+
sourceIndex,
|
|
2486
|
+
sourceKey: headerDragCell.key,
|
|
2487
|
+
parentKey: headerDragCell.parentKey,
|
|
2488
|
+
headerLevel: headerDragCell.level,
|
|
2489
|
+
headerKind: headerDragCell.leaf ? "column" : "group",
|
|
2490
|
+
fixed: columns[headerDragCell.startIndex]?.fixed
|
|
2491
|
+
};
|
|
2021
2492
|
},
|
|
2022
2493
|
onGenerateDragPreview: ({ nativeSetDragImage, source }) => {
|
|
2023
2494
|
const sourceIndex = Number(source.data.sourceIndex);
|
|
@@ -2120,31 +2591,31 @@ function Table({
|
|
|
2120
2591
|
return;
|
|
2121
2592
|
}
|
|
2122
2593
|
const targetIndex = Number(target?.targetIndex);
|
|
2123
|
-
if (!Number.isInteger(targetIndex) ||
|
|
2594
|
+
if (!Number.isInteger(targetIndex) || target?.headerKind !== source.data.headerKind || target?.parentKey !== source.data.parentKey || target?.fixed !== source.data.fixed) {
|
|
2124
2595
|
setDragGuide(null);
|
|
2125
2596
|
return;
|
|
2126
2597
|
}
|
|
2127
2598
|
const edge = target?.columnEdge;
|
|
2128
|
-
|
|
2129
|
-
if (sourceIndex < destinationIndex) destinationIndex -= 1;
|
|
2130
|
-
destinationIndex = Math.max(0, Math.min(columns.length - 1, destinationIndex));
|
|
2599
|
+
const destinationIndex = getColumnDropIndex(sourceIndex, targetIndex, edge);
|
|
2131
2600
|
columnDragPreviewRef.current = { sourceIndex, targetIndex: destinationIndex };
|
|
2132
|
-
const targetLeft =
|
|
2133
|
-
|
|
2601
|
+
const targetLeft = Number(target?.targetLeft);
|
|
2602
|
+
const targetWidth = Number(target?.targetWidth);
|
|
2603
|
+
const guideTop = headerRowOffsets[Number(source.data.headerLevel)] ?? 0;
|
|
2604
|
+
setDragGuide(edge === "right" ? targetLeft + targetWidth : targetLeft, guideTop);
|
|
2134
2605
|
},
|
|
2135
2606
|
onDrop: ({ source, location }) => {
|
|
2136
2607
|
const target = location.current.dropTargets[0]?.data;
|
|
2137
2608
|
const sourceIndex = Number(source.data.sourceIndex);
|
|
2138
2609
|
if (source.data.type === "table-row") {
|
|
2139
|
-
const
|
|
2140
|
-
let targetIndex2 =
|
|
2610
|
+
const rawTargetIndex2 = Number(target?.targetIndex);
|
|
2611
|
+
let targetIndex2 = rawTargetIndex2 + (target?.rowEdge === "bottom" ? 1 : 0);
|
|
2141
2612
|
if (sourceIndex < targetIndex2) targetIndex2 -= 1;
|
|
2142
2613
|
targetIndex2 = Math.max(0, Math.min(rows.length - 1, targetIndex2));
|
|
2143
2614
|
scrollerRef.current?.classList.add("rvg-row-drop-settling");
|
|
2144
2615
|
setRowDragPreview(null);
|
|
2145
2616
|
setRowDragGuide(null);
|
|
2146
2617
|
canvas.style.cursor = "default";
|
|
2147
|
-
if (Number.isInteger(
|
|
2618
|
+
if (Number.isInteger(rawTargetIndex2) && sourceIndex !== targetIndex2) onRowOrderChange?.(sourceIndex, targetIndex2);
|
|
2148
2619
|
requestAnimationFrame(() => {
|
|
2149
2620
|
requestAnimationFrame(() => scrollerRef.current?.classList.remove("rvg-row-drop-settling"));
|
|
2150
2621
|
});
|
|
@@ -2153,12 +2624,23 @@ function Table({
|
|
|
2153
2624
|
}, 0);
|
|
2154
2625
|
return;
|
|
2155
2626
|
}
|
|
2156
|
-
const
|
|
2627
|
+
const rawTargetIndex = Number(target?.targetIndex);
|
|
2628
|
+
const targetIndex = columnDragPreviewRef.current?.targetIndex ?? (Number.isInteger(rawTargetIndex) ? getColumnDropIndex(sourceIndex, rawTargetIndex, target?.columnEdge) : rawTargetIndex);
|
|
2157
2629
|
canvas.style.cursor = "default";
|
|
2158
2630
|
setDragGuide(null);
|
|
2159
2631
|
columnDragPreviewRef.current = null;
|
|
2160
|
-
if (Number.isInteger(targetIndex) && sourceIndex !== targetIndex &&
|
|
2161
|
-
onColumnOrderChange?.(
|
|
2632
|
+
if (Number.isInteger(rawTargetIndex) && Number.isInteger(targetIndex) && sourceIndex !== targetIndex && target?.headerKind === source.data.headerKind && target?.parentKey === source.data.parentKey && target?.fixed === source.data.fixed) {
|
|
2633
|
+
onColumnOrderChange?.(
|
|
2634
|
+
sourceIndex - utilityColumnCount,
|
|
2635
|
+
targetIndex - utilityColumnCount,
|
|
2636
|
+
{
|
|
2637
|
+
type: source.data.headerKind === "group" ? "group" : "column",
|
|
2638
|
+
sourceKey: String(source.data.sourceKey),
|
|
2639
|
+
targetKey: String(target.targetKey),
|
|
2640
|
+
parentKey: typeof source.data.parentKey === "string" ? source.data.parentKey : void 0,
|
|
2641
|
+
placement: getColumnDropPlacement(sourceIndex, rawTargetIndex, target.columnEdge)
|
|
2642
|
+
}
|
|
2643
|
+
);
|
|
2162
2644
|
}
|
|
2163
2645
|
window.setTimeout(() => {
|
|
2164
2646
|
suppressClickRef.current = false;
|
|
@@ -2176,24 +2658,38 @@ function Table({
|
|
|
2176
2658
|
const rowEdge = input.clientY - canvas.getBoundingClientRect().top < rowTop + rowHeight / 2 ? "top" : "bottom";
|
|
2177
2659
|
return (0, import_closest_edge.attachClosestEdge)({ targetIndex: targetIndex2, rowEdge }, { element, input, allowedEdges: ["top", "bottom"] });
|
|
2178
2660
|
}
|
|
2179
|
-
|
|
2180
|
-
if (
|
|
2661
|
+
const targetCell = locateHeaderCell(input.clientX, input.clientY);
|
|
2662
|
+
if (!targetCell) return { targetIndex: -1 };
|
|
2663
|
+
let targetIndex = targetCell.startIndex;
|
|
2664
|
+
const targetFixed = columns[targetCell.startIndex]?.fixed;
|
|
2665
|
+
const headerKind = targetCell.leaf ? "column" : "group";
|
|
2666
|
+
if (source.data.headerKind !== headerKind || source.data.parentKey !== targetCell.parentKey || source.data.fixed !== targetFixed) return { targetIndex: -1 };
|
|
2181
2667
|
if (columns[targetIndex].rowSelection || columns[targetIndex].rowDragHandle || columns[targetIndex].rowNumber) {
|
|
2182
2668
|
const fixedSide = columns[Number(source.data.sourceIndex)]?.fixed;
|
|
2183
2669
|
targetIndex = columns.findIndex((column, index) => index > targetIndex && column.fixed === fixedSide && !column.rowSelection && !column.rowDragHandle && !column.rowNumber);
|
|
2184
2670
|
if (targetIndex < 0) return { targetIndex: -1 };
|
|
2185
2671
|
return { targetIndex, columnEdge: "left" };
|
|
2186
2672
|
}
|
|
2187
|
-
const left =
|
|
2188
|
-
const
|
|
2189
|
-
|
|
2673
|
+
const left = getHeaderCellLeft(targetCell);
|
|
2674
|
+
const targetWidth = targetCell.leaf ? metrics[targetIndex].width : getHeaderCellWidth(targetCell);
|
|
2675
|
+
const columnEdge = input.clientX - canvas.getBoundingClientRect().left < left + targetWidth / 2 ? "left" : "right";
|
|
2676
|
+
return (0, import_closest_edge.attachClosestEdge)({
|
|
2677
|
+
targetIndex,
|
|
2678
|
+
targetKey: targetCell.key,
|
|
2679
|
+
parentKey: targetCell.parentKey,
|
|
2680
|
+
headerKind,
|
|
2681
|
+
fixed: targetFixed,
|
|
2682
|
+
targetLeft: left,
|
|
2683
|
+
targetWidth,
|
|
2684
|
+
columnEdge
|
|
2685
|
+
}, { element, input, allowedEdges: ["left", "right"] });
|
|
2190
2686
|
}
|
|
2191
2687
|
});
|
|
2192
2688
|
return () => {
|
|
2193
2689
|
cleanupDrag();
|
|
2194
2690
|
cleanupDrop();
|
|
2195
2691
|
};
|
|
2196
|
-
}, [bodyTop, columnDraggable, columns, fixedHeader, getCellLabel, getDisplayedColumnLeft, getRowKey, headerHeight, locateColumn,
|
|
2692
|
+
}, [bodyTop, columnDraggable, columns, fixedHeader, getCellLabel, getDisplayedColumnLeft, getHeaderCellWidth, getRowKey, headerHeight, locateColumn, locateHeaderCell, locateHeaderCellDragHandle, locateHeaderResizeCell, locateRow, metrics, onColumnOrderChange, onRowOrderChange, rowDraggable, rowHeight, rows, selectedRowKeySet, setDragGuide, setRowDragGuide, utilityColumnCount]);
|
|
2197
2693
|
const locateCell = (0, import_react5.useCallback)((clientX, clientY) => {
|
|
2198
2694
|
const canvas = canvasRef.current;
|
|
2199
2695
|
if (!canvas) return null;
|
|
@@ -2300,8 +2796,9 @@ function Table({
|
|
|
2300
2796
|
const horizontalFocusGap = 0;
|
|
2301
2797
|
const verticalFocusGap = 0;
|
|
2302
2798
|
if (column.fixed === void 0) {
|
|
2303
|
-
|
|
2304
|
-
if (
|
|
2799
|
+
const cellLeft = getScrollableColumnLeft(cell.columnIndex);
|
|
2800
|
+
if (cellLeft - nextLeft < fixedWidth) nextLeft = Math.max(0, cellLeft - fixedWidth - horizontalFocusGap);
|
|
2801
|
+
if (cellLeft + cellWidth - nextLeft > viewport.width - rightFixedWidth) nextLeft = cellLeft + cellWidth - viewport.width + rightFixedWidth + horizontalFocusGap;
|
|
2305
2802
|
}
|
|
2306
2803
|
const rowTop = (fixedHeader ? 0 : bodyTop) + cell.rowIndex * rowHeight;
|
|
2307
2804
|
const rowBottom = rowTop + rowHeight * rowSpan;
|
|
@@ -2314,7 +2811,7 @@ function Table({
|
|
|
2314
2811
|
scroller.scrollTop = nextTop;
|
|
2315
2812
|
scrollRef.current = { left: scroller.scrollLeft, top: scroller.scrollTop };
|
|
2316
2813
|
scheduleDraw();
|
|
2317
|
-
}, [bodyTop, columns, fixedHeader, fixedWidth, getCellDisplayWidth, getCellSpan, metrics, rightFixedWidth, rowHeight, scheduleDraw, viewport.width]);
|
|
2814
|
+
}, [bodyTop, columns, fixedHeader, fixedWidth, getCellDisplayWidth, getCellSpan, getScrollableColumnLeft, metrics, rightFixedWidth, rowHeight, scheduleDraw, viewport.width]);
|
|
2318
2815
|
const beginEdit = (0, import_react5.useCallback)((cell) => {
|
|
2319
2816
|
const column = columns[cell.columnIndex];
|
|
2320
2817
|
if (!column?.editable || column.dataIndex === void 0) return;
|
|
@@ -2335,8 +2832,9 @@ function Table({
|
|
|
2335
2832
|
const horizontalFocusGap = 0;
|
|
2336
2833
|
const verticalFocusGap = 0;
|
|
2337
2834
|
if (column.fixed === void 0) {
|
|
2338
|
-
|
|
2339
|
-
if (
|
|
2835
|
+
const cellLeft = getScrollableColumnLeft(cell.columnIndex);
|
|
2836
|
+
if (cellLeft - nextLeft < fixedWidth) nextLeft = Math.max(0, cellLeft - fixedWidth - horizontalFocusGap);
|
|
2837
|
+
if (cellLeft + cellWidth - nextLeft > viewport.width - rightFixedWidth) nextLeft = cellLeft + cellWidth - viewport.width + rightFixedWidth + horizontalFocusGap;
|
|
2340
2838
|
}
|
|
2341
2839
|
const rowTop = (fixedHeader ? 0 : bodyTop) + cell.rowIndex * rowHeight;
|
|
2342
2840
|
const rowBottom = rowTop + rowHeight * rowSpan;
|
|
@@ -2358,7 +2856,7 @@ function Table({
|
|
|
2358
2856
|
return;
|
|
2359
2857
|
}
|
|
2360
2858
|
setEditing(cell);
|
|
2361
|
-
}, [columns, fixedHeader, fixedWidth, getCellDisplayWidth, getCellSpan, metrics, rightFixedWidth, rowHeight, rows, scheduleDraw, viewport.width]);
|
|
2859
|
+
}, [columns, fixedHeader, fixedWidth, getCellDisplayWidth, getCellSpan, getScrollableColumnLeft, metrics, rightFixedWidth, rowHeight, rows, scheduleDraw, viewport.width]);
|
|
2362
2860
|
const getCellEditKey = (cell) => `${typeof cell.rowKey}:${String(cell.rowKey)}\0${cell.columnKey}`;
|
|
2363
2861
|
const markCellEdited = (cell, previousValue, nextValue) => {
|
|
2364
2862
|
const key = getCellEditKey(cell);
|
|
@@ -2489,10 +2987,13 @@ function Table({
|
|
|
2489
2987
|
const visibleRowHeight = fixedHeader ? renderHeight - bodyTop : renderHeight;
|
|
2490
2988
|
if (rowTop < scroller.scrollTop) scroller.scrollTop = rowTop;
|
|
2491
2989
|
if (rowBottom > scroller.scrollTop + visibleRowHeight) scroller.scrollTop = rowBottom - visibleRowHeight;
|
|
2492
|
-
if (columns[columnIndex].fixed === void 0
|
|
2493
|
-
|
|
2990
|
+
if (columns[columnIndex].fixed === void 0) {
|
|
2991
|
+
const cellLeft = getScrollableColumnLeft(columnIndex);
|
|
2992
|
+
if (cellLeft < scroller.scrollLeft + fixedWidth) scroller.scrollLeft = Math.max(0, cellLeft - fixedWidth);
|
|
2993
|
+
if (cellLeft + cellWidth > scroller.scrollLeft + viewport.width - rightFixedWidth) scroller.scrollLeft = cellLeft + cellWidth - viewport.width + rightFixedWidth;
|
|
2994
|
+
}
|
|
2494
2995
|
}
|
|
2495
|
-
}, [beginEdit, bodyTop, columns, editing, renderHeight, fixedHeader, fixedWidth, getCellDisplayWidth, getCellSpan, getRowKey, metrics, rightFixedWidth, rowHeight, rows, selection, setSelection, viewport.width]);
|
|
2996
|
+
}, [beginEdit, bodyTop, columns, editing, renderHeight, fixedHeader, fixedWidth, getCellDisplayWidth, getCellSpan, getRowKey, getScrollableColumnLeft, metrics, rightFixedWidth, rowHeight, rows, selection, setSelection, viewport.width]);
|
|
2496
2997
|
const editorStyle = (0, import_react5.useMemo)(() => {
|
|
2497
2998
|
if (!editing) return void 0;
|
|
2498
2999
|
const metric = metrics[editing.columnIndex];
|
|
@@ -2500,43 +3001,83 @@ function Table({
|
|
|
2500
3001
|
const span = getCellSpan(editing.rowIndex, editing.columnIndex);
|
|
2501
3002
|
const cellWidth = getCellDisplayWidth(editing.columnIndex, span?.colSpan ?? 1);
|
|
2502
3003
|
const cellHeight = rowHeight * (span?.rowSpan ?? 1);
|
|
2503
|
-
const cellLeft = fixedSide === "left" ?
|
|
3004
|
+
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;
|
|
2504
3005
|
return {
|
|
2505
3006
|
left: (fixedSide ? cellLeft : Math.max(cellLeft, fixedWidth)) + 1,
|
|
2506
3007
|
top: Math.max(fixedHeader ? bodyTop : 0, bodyTop + editing.rowIndex * rowHeight - scrollRef.current.top) + 1,
|
|
2507
3008
|
width: cellWidth - 2,
|
|
2508
3009
|
height: cellHeight - 2
|
|
2509
3010
|
};
|
|
2510
|
-
}, [bodyTop, columns, editing, fixedHeader, fixedWidth, getCellDisplayWidth, getCellSpan, metrics, rightFixedOffsets, rowHeight, viewport.width]);
|
|
3011
|
+
}, [bodyTop, columns, editing, fixedHeader, fixedWidth, getCellDisplayWidth, getCellSpan, getScrollableColumnLeft, leftFixedOffsets, metrics, rightFixedOffsets, rowHeight, viewport.width]);
|
|
2511
3012
|
const handleColumnPointerDown = (0, import_react5.useCallback)((event) => {
|
|
2512
3013
|
const rect = event.currentTarget.getBoundingClientRect();
|
|
2513
3014
|
const localY = event.clientY - rect.top;
|
|
2514
3015
|
const headerY = fixedHeader ? 0 : -scrollRef.current.top;
|
|
2515
3016
|
if (localY < headerY || localY >= headerY + headerHeight) return;
|
|
2516
|
-
const
|
|
2517
|
-
if (
|
|
3017
|
+
const resizeHit = locateHeaderResizeHit(event.clientX, event.clientY);
|
|
3018
|
+
if (resizeHit) {
|
|
3019
|
+
const { cell: resizeCell, edge: resizeEdge } = resizeHit;
|
|
2518
3020
|
event.preventDefault();
|
|
2519
3021
|
event.currentTarget.setPointerCapture(event.pointerId);
|
|
2520
|
-
|
|
3022
|
+
const resizeIndex = resizeCell.endIndex;
|
|
3023
|
+
const resizeIndices = resizeCell.leaf ? [resizeIndex] : Array.from({ length: resizeCell.endIndex - resizeCell.startIndex + 1 }, (_, index) => resizeCell.startIndex + index);
|
|
3024
|
+
const startWidths = resizeIndices.map((index) => metrics[index].width);
|
|
3025
|
+
const startGuideX = getHeaderCellLeft(resizeCell) + (resizeEdge === "right" ? getHeaderCellWidth(resizeCell) : 0);
|
|
3026
|
+
columnDragRef.current = {
|
|
3027
|
+
type: "resize",
|
|
3028
|
+
columnIndex: resizeIndex,
|
|
3029
|
+
startX: event.clientX,
|
|
3030
|
+
startWidth: startWidths.reduce((total, width2) => total + width2, 0),
|
|
3031
|
+
startGuideX,
|
|
3032
|
+
guideTop: headerRowOffsets[resizeCell.level] ?? 0,
|
|
3033
|
+
resizeEdge,
|
|
3034
|
+
resizeIndices,
|
|
3035
|
+
startWidths
|
|
3036
|
+
};
|
|
2521
3037
|
suppressClickRef.current = true;
|
|
2522
|
-
|
|
3038
|
+
setResizeGuideTop(headerRowOffsets[resizeCell.level] ?? 0);
|
|
3039
|
+
setResizeGuideX(startGuideX);
|
|
2523
3040
|
return;
|
|
2524
3041
|
}
|
|
2525
|
-
}, [fixedHeader,
|
|
3042
|
+
}, [fixedHeader, getHeaderCellLeft, getHeaderCellWidth, headerHeight, headerRowOffsets, locateHeaderResizeHit, metrics]);
|
|
2526
3043
|
const handleColumnPointerMove = (0, import_react5.useCallback)((event) => {
|
|
2527
3044
|
const drag = columnDragRef.current;
|
|
2528
3045
|
if (!drag) return;
|
|
2529
|
-
const
|
|
2530
|
-
|
|
2531
|
-
|
|
2532
|
-
|
|
3046
|
+
const column = columns[drag.columnIndex];
|
|
3047
|
+
const minWidth = getMinimumColumnWidth(column, columnDraggable);
|
|
3048
|
+
const pointerDelta = Math.round(event.clientX - drag.startX);
|
|
3049
|
+
const delta = drag.resizeEdge === "left" ? -pointerDelta : pointerDelta;
|
|
3050
|
+
if (drag.resizeIndices.length > 1) {
|
|
3051
|
+
const minWidths = drag.resizeIndices.map((index) => getMinimumColumnWidth(columns[index], columnDraggable));
|
|
3052
|
+
const minTotalWidth = minWidths.reduce((total, width3) => total + width3, 0);
|
|
3053
|
+
const targetTotalWidth = Math.max(minTotalWidth, drag.startWidth + delta);
|
|
3054
|
+
const ratio = drag.startWidth > 0 ? targetTotalWidth / drag.startWidth : 1;
|
|
3055
|
+
const nextWidths = drag.startWidths.map((width3, index) => Math.max(minWidths[index], Math.round(width3 * ratio)));
|
|
3056
|
+
let remainder = targetTotalWidth - nextWidths.reduce((total, width3) => total + width3, 0);
|
|
3057
|
+
for (let index = nextWidths.length - 1; index >= 0 && remainder !== 0; index -= 1) {
|
|
3058
|
+
const next = nextWidths[index] + (remainder > 0 ? 1 : -1);
|
|
3059
|
+
if (next < minWidths[index]) continue;
|
|
3060
|
+
nextWidths[index] = next;
|
|
3061
|
+
remainder += remainder > 0 ? -1 : 1;
|
|
3062
|
+
}
|
|
3063
|
+
drag.resizeIndices.forEach((columnIndex, index) => {
|
|
3064
|
+
resizeColumn(columns[columnIndex].key, nextWidths[index]);
|
|
3065
|
+
});
|
|
3066
|
+
setResizeGuideX(drag.startGuideX + (drag.resizeEdge === "left" ? drag.startWidth - targetTotalWidth : targetTotalWidth - drag.startWidth));
|
|
3067
|
+
return;
|
|
3068
|
+
}
|
|
3069
|
+
const width2 = Math.max(minWidth, Math.round(drag.startWidth + delta));
|
|
3070
|
+
resizeColumn(column.key, width2);
|
|
3071
|
+
setResizeGuideX(drag.startGuideX + (drag.resizeEdge === "left" ? drag.startWidth - width2 : width2 - drag.startWidth));
|
|
3072
|
+
}, [columnDraggable, columns, resizeColumn]);
|
|
2533
3073
|
const handleColumnPointerUp = (0, import_react5.useCallback)((event) => {
|
|
2534
3074
|
const drag = columnDragRef.current;
|
|
2535
3075
|
if (!drag) return;
|
|
2536
3076
|
if (event.currentTarget.hasPointerCapture(event.pointerId)) event.currentTarget.releasePointerCapture(event.pointerId);
|
|
2537
3077
|
columnDragRef.current = null;
|
|
2538
|
-
|
|
2539
|
-
|
|
3078
|
+
setResizeGuideX(null);
|
|
3079
|
+
setResizeGuideTop(0);
|
|
3080
|
+
}, []);
|
|
2540
3081
|
const queueSortStateChange = (0, import_react5.useCallback)((next) => {
|
|
2541
3082
|
setDisplaySortState(next);
|
|
2542
3083
|
if (sortDebounceRef.current !== null) clearTimeout(sortDebounceRef.current);
|
|
@@ -2548,7 +3089,7 @@ function Table({
|
|
|
2548
3089
|
const renderHeaderIcons = (columnIndex, left) => {
|
|
2549
3090
|
const column = columns[columnIndex];
|
|
2550
3091
|
if (column.rowSelection || column.rowDragHandle || column.rowNumber) return null;
|
|
2551
|
-
const top = (
|
|
3092
|
+
const top = headerLeafTop + (headerLeafHeight - 14) / 2;
|
|
2552
3093
|
const visibleActions = getVisibleHeaderActions(column, metrics[columnIndex].width, columnDraggable, measureHeaderTitleWidth(columnIndex));
|
|
2553
3094
|
let right = left + metrics[columnIndex].width - (visibleActions.drag ? 18 : 4);
|
|
2554
3095
|
const icons = [];
|
|
@@ -2574,44 +3115,87 @@ function Table({
|
|
|
2574
3115
|
}
|
|
2575
3116
|
return icons;
|
|
2576
3117
|
};
|
|
2577
|
-
const
|
|
2578
|
-
|
|
2579
|
-
const { columnIndex, action } = visibleHeaderTooltip;
|
|
2580
|
-
const column = columns[columnIndex];
|
|
3118
|
+
const createHeaderTooltip = (0, import_react5.useCallback)((action, columnIndex, cell) => {
|
|
3119
|
+
const column = cell?.column ?? columns[columnIndex];
|
|
2581
3120
|
if (!column) return null;
|
|
2582
|
-
const
|
|
2583
|
-
const
|
|
2584
|
-
|
|
2585
|
-
|
|
2586
|
-
|
|
3121
|
+
const cellLeft = cell ? getHeaderCellLeft(cell) : getDisplayedColumnLeft(columnIndex);
|
|
3122
|
+
const cellWidth = cell ? getHeaderCellWidth(cell) : metrics[columnIndex]?.width ?? 0;
|
|
3123
|
+
const level = cell?.level ?? headerDepth - 1;
|
|
3124
|
+
const rowTop = (fixedHeader ? 0 : -scrollPosition.top) + (headerRowOffsets[level] ?? 0);
|
|
3125
|
+
const rowHeight2 = headerRowHeights[level] ?? baseHeaderHeight;
|
|
3126
|
+
let anchorX = cellLeft + cellWidth / 2;
|
|
3127
|
+
let anchorLeft = cellLeft;
|
|
3128
|
+
let label = column.title;
|
|
2587
3129
|
if (action === "title") {
|
|
2588
|
-
const
|
|
2589
|
-
const
|
|
2590
|
-
|
|
2591
|
-
const
|
|
2592
|
-
const
|
|
2593
|
-
|
|
2594
|
-
|
|
2595
|
-
|
|
3130
|
+
const canvas = canvasRef.current;
|
|
3131
|
+
const context = canvas?.getContext("2d");
|
|
3132
|
+
if (context) context.font = "600 13px Inter, ui-sans-serif, system-ui, sans-serif";
|
|
3133
|
+
const titleWidth = context?.measureText(column.title).width ?? column.title.length * 13;
|
|
3134
|
+
const visibleActions = cell?.leaf || !cell ? getVisibleHeaderActions(column, cellWidth, columnDraggable, titleWidth) : null;
|
|
3135
|
+
const actionWidth = visibleActions ? (Number(visibleActions.drag) + Number(visibleActions.filter) + Number(visibleActions.sort)) * 18 : columnDraggable ? 18 : 0;
|
|
3136
|
+
const contentWidth2 = Math.max(0, cellWidth - actionWidth);
|
|
3137
|
+
const maxTextWidth = Math.max(0, contentWidth2 - 20);
|
|
3138
|
+
const textWidth = Math.min(titleWidth, maxTextWidth);
|
|
3139
|
+
if (column.align === "right") {
|
|
3140
|
+
anchorX = cellLeft + contentWidth2 - 10;
|
|
3141
|
+
anchorLeft = anchorX - textWidth;
|
|
3142
|
+
} else if (column.align === "center") {
|
|
3143
|
+
anchorX = cellLeft + contentWidth2 / 2 + textWidth / 2;
|
|
3144
|
+
anchorLeft = cellLeft + contentWidth2 / 2 - textWidth / 2;
|
|
3145
|
+
} else {
|
|
3146
|
+
anchorLeft = cellLeft + 10;
|
|
3147
|
+
anchorX = anchorLeft + textWidth;
|
|
3148
|
+
}
|
|
2596
3149
|
} else if (action === "drag") {
|
|
2597
|
-
anchorX =
|
|
3150
|
+
anchorX = cellLeft + cellWidth;
|
|
2598
3151
|
anchorLeft = anchorX;
|
|
2599
3152
|
label = labels.dragColumn;
|
|
2600
3153
|
} else if (action === "sort") {
|
|
2601
3154
|
const direction = displaySortState?.columnKey === column.key ? displaySortState.direction : null;
|
|
2602
3155
|
label = direction === "asc" ? labels.sortAsc : direction === "desc" ? labels.sortDesc : labels.sortBoth;
|
|
2603
|
-
|
|
2604
|
-
|
|
3156
|
+
anchorX = cellLeft + cellWidth - 22;
|
|
3157
|
+
anchorLeft = anchorX;
|
|
3158
|
+
} else if (action === "filter") {
|
|
2605
3159
|
const value = filterValues[column.key];
|
|
2606
3160
|
label = value ? labels.filterWithValue(value) : labels.filter;
|
|
3161
|
+
anchorX = cellLeft + cellWidth - 40;
|
|
3162
|
+
anchorLeft = anchorX;
|
|
2607
3163
|
}
|
|
2608
3164
|
const placement = anchorX + 328 > viewport.width ? "left" : "right";
|
|
2609
3165
|
return {
|
|
3166
|
+
key: `${action}:${cell ? `cell:${cell.key}:${cell.level}` : `column:${column.key}`}:${Math.round(anchorX)}:${Math.round(rowTop)}`,
|
|
3167
|
+
columnIndex,
|
|
3168
|
+
action,
|
|
2610
3169
|
left: placement === "right" ? anchorX + 8 : Math.max(8, anchorLeft - 8),
|
|
2611
|
-
top:
|
|
3170
|
+
top: rowTop + rowHeight2 / 2,
|
|
2612
3171
|
label,
|
|
2613
3172
|
placement
|
|
2614
3173
|
};
|
|
3174
|
+
}, [baseHeaderHeight, columnDraggable, columns, displaySortState, filterValues, fixedHeader, getDisplayedColumnLeft, getHeaderCellLeft, getHeaderCellWidth, headerDepth, headerRowHeights, headerRowOffsets, labels, metrics, scrollPosition.top, viewport.width]);
|
|
3175
|
+
const isPointerOnHeaderTitle = (0, import_react5.useCallback)((clientX, clientY, cell) => {
|
|
3176
|
+
const canvas = canvasRef.current;
|
|
3177
|
+
if (!canvas) return false;
|
|
3178
|
+
const rect = canvas.getBoundingClientRect();
|
|
3179
|
+
const localX = clientX - rect.left;
|
|
3180
|
+
const localY = clientY - rect.top;
|
|
3181
|
+
const column = cell.column;
|
|
3182
|
+
const cellLeft = getHeaderCellLeft(cell);
|
|
3183
|
+
const cellWidth = getHeaderCellWidth(cell);
|
|
3184
|
+
const rowTop = (fixedHeader ? 0 : -scrollRef.current.top) + (headerRowOffsets[cell.level] ?? 0);
|
|
3185
|
+
const context = canvas.getContext("2d");
|
|
3186
|
+
if (context) context.font = "600 13px Inter, ui-sans-serif, system-ui, sans-serif";
|
|
3187
|
+
const titleWidth = context?.measureText(column.title).width ?? column.title.length * 13;
|
|
3188
|
+
const visibleActions = cell.leaf ? getVisibleHeaderActions(column, cellWidth, columnDraggable, titleWidth) : null;
|
|
3189
|
+
const actionWidth = visibleActions ? (Number(visibleActions.drag) + Number(visibleActions.filter) + Number(visibleActions.sort)) * 18 : columnDraggable ? 18 : 0;
|
|
3190
|
+
const contentWidth2 = Math.max(0, cellWidth - actionWidth);
|
|
3191
|
+
const textWidth = Math.min(titleWidth, Math.max(0, contentWidth2 - 20));
|
|
3192
|
+
const textLeft = column.align === "right" ? cellLeft + contentWidth2 - 10 - textWidth : column.align === "center" ? cellLeft + contentWidth2 / 2 - textWidth / 2 : cellLeft + 10;
|
|
3193
|
+
const textTop = rowTop + 8;
|
|
3194
|
+
return localX >= textLeft - 4 && localX <= textLeft + textWidth + 4 && localY >= textTop - 2 && localY <= textTop + 18;
|
|
3195
|
+
}, [columnDraggable, fixedHeader, getHeaderCellLeft, getHeaderCellWidth, headerRowOffsets]);
|
|
3196
|
+
const headerTooltip = (() => {
|
|
3197
|
+
if (!visibleHeaderTooltip) return null;
|
|
3198
|
+
return visibleHeaderTooltip;
|
|
2615
3199
|
})();
|
|
2616
3200
|
const getRangeBounds = () => selectionRange ? {
|
|
2617
3201
|
rowStart: Math.min(selectionRange.anchor.rowIndex, selectionRange.focus.rowIndex),
|
|
@@ -2670,7 +3254,7 @@ function Table({
|
|
|
2670
3254
|
}, 1800);
|
|
2671
3255
|
};
|
|
2672
3256
|
const showHeaderTooltipAfterDelay = (next) => {
|
|
2673
|
-
const nextKey = next ?
|
|
3257
|
+
const nextKey = next ? next.key : "";
|
|
2674
3258
|
if (nextKey && pendingHeaderTooltipKeyRef.current === nextKey) return;
|
|
2675
3259
|
pendingHeaderTooltipKeyRef.current = nextKey;
|
|
2676
3260
|
if (headerTooltipTimerRef.current !== null) clearTimeout(headerTooltipTimerRef.current);
|
|
@@ -2918,12 +3502,14 @@ function Table({
|
|
|
2918
3502
|
const renderHeaderTitle = (columnIndex, left) => {
|
|
2919
3503
|
const column = columns[columnIndex];
|
|
2920
3504
|
const currentScrollTop = scrollRef.current.top;
|
|
3505
|
+
const headerTop = fixedHeader ? 0 : -currentScrollTop;
|
|
3506
|
+
const leafTop = headerTop + headerLeafTop;
|
|
2921
3507
|
if (column.rowNumber) {
|
|
2922
3508
|
return /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
|
|
2923
3509
|
"div",
|
|
2924
3510
|
{
|
|
2925
3511
|
className: "rvg-header-title",
|
|
2926
|
-
style: { left, top:
|
|
3512
|
+
style: { left, top: headerTop, width: metrics[columnIndex].width, height: headerHeight, justifyContent: "center", padding: 0, textAlign: "center" },
|
|
2927
3513
|
children: /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("span", { children: column.title })
|
|
2928
3514
|
},
|
|
2929
3515
|
column.key
|
|
@@ -2937,14 +3523,102 @@ function Table({
|
|
|
2937
3523
|
"div",
|
|
2938
3524
|
{
|
|
2939
3525
|
className: "rvg-header-selection-icon",
|
|
2940
|
-
style: { left, top:
|
|
3526
|
+
style: { left, top: headerTop, width: metrics[columnIndex].width, height: headerHeight },
|
|
2941
3527
|
children: /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(SelectionIcon, { checked: allSelected, indeterminate })
|
|
2942
3528
|
},
|
|
2943
3529
|
column.key
|
|
2944
3530
|
);
|
|
2945
3531
|
}
|
|
3532
|
+
if (column.renderHeader) {
|
|
3533
|
+
const visibleActions = getVisibleHeaderActions(column, metrics[columnIndex].width, columnDraggable, measureHeaderTitleWidth(columnIndex));
|
|
3534
|
+
const actionWidth = (Number(visibleActions.drag) + Number(visibleActions.filter) + Number(visibleActions.sort)) * 18;
|
|
3535
|
+
return /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
|
|
3536
|
+
"div",
|
|
3537
|
+
{
|
|
3538
|
+
className: "rvg-header-title is-custom",
|
|
3539
|
+
"data-level": headerDepth - 1,
|
|
3540
|
+
"data-measure-key": column.key,
|
|
3541
|
+
style: {
|
|
3542
|
+
left,
|
|
3543
|
+
top: leafTop,
|
|
3544
|
+
width: Math.max(0, metrics[columnIndex].width - actionWidth),
|
|
3545
|
+
height: headerLeafHeight,
|
|
3546
|
+
justifyContent: column.align === "right" ? "flex-end" : column.align === "center" ? "center" : "flex-start",
|
|
3547
|
+
textAlign: column.align === "right" ? "right" : column.align === "center" ? "center" : "left"
|
|
3548
|
+
},
|
|
3549
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
|
|
3550
|
+
"span",
|
|
3551
|
+
{
|
|
3552
|
+
ref: (node) => {
|
|
3553
|
+
if (node) {
|
|
3554
|
+
customHeaderContentRefs.current.set(column.key, node);
|
|
3555
|
+
requestAnimationFrame(measureCustomHeaderHeight);
|
|
3556
|
+
} else customHeaderContentRefs.current.delete(column.key);
|
|
3557
|
+
},
|
|
3558
|
+
children: column.renderHeader(column)
|
|
3559
|
+
}
|
|
3560
|
+
)
|
|
3561
|
+
},
|
|
3562
|
+
column.key
|
|
3563
|
+
);
|
|
3564
|
+
}
|
|
2946
3565
|
return null;
|
|
2947
3566
|
};
|
|
3567
|
+
const renderGroupedHeaderCell = (cell) => {
|
|
3568
|
+
const startMetric = metrics[cell.startIndex];
|
|
3569
|
+
const endMetric = metrics[cell.endIndex];
|
|
3570
|
+
if (!startMetric || !endMetric) return null;
|
|
3571
|
+
const column = cell.column;
|
|
3572
|
+
const fixedSide = columns[cell.startIndex]?.fixed;
|
|
3573
|
+
const currentScrollTop = scrollRef.current.top;
|
|
3574
|
+
const rowHeightForLevel = headerRowHeights[cell.level] ?? baseHeaderHeight;
|
|
3575
|
+
const headerTop = (fixedHeader ? 0 : -currentScrollTop) + (headerRowOffsets[cell.level] ?? 0);
|
|
3576
|
+
const left = fixedSide === "left" ? leftFixedOffsets.get(cell.startIndex) ?? 0 : fixedSide === "right" ? viewport.width - (rightFixedOffsets.get(cell.startIndex) ?? 0) - startMetric.width : getScrollableColumnLeft(cell.startIndex);
|
|
3577
|
+
const width2 = cell.leaf ? startMetric.width : getCellDisplayWidth(cell.startIndex, cell.endIndex - cell.startIndex + 1);
|
|
3578
|
+
const visibleActions = cell.leaf ? getVisibleHeaderActions(column, startMetric.width, columnDraggable, measureHeaderTitleWidth(cell.startIndex)) : null;
|
|
3579
|
+
const actionWidth = visibleActions ? (Number(visibleActions.drag) + Number(visibleActions.filter) + Number(visibleActions.sort)) * 18 : columnDraggable && !cell.leaf ? 18 : 0;
|
|
3580
|
+
return /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)(
|
|
3581
|
+
"div",
|
|
3582
|
+
{
|
|
3583
|
+
className: `rvg-header-title rvg-header-group${column.renderHeader ? " is-custom" : ""}${cell.leaf ? " is-leaf" : ""}`,
|
|
3584
|
+
"data-level": cell.level,
|
|
3585
|
+
"data-measure-key": `group:${cell.key}:${cell.level}`,
|
|
3586
|
+
style: {
|
|
3587
|
+
left,
|
|
3588
|
+
top: headerTop,
|
|
3589
|
+
width: width2,
|
|
3590
|
+
height: rowHeightForLevel,
|
|
3591
|
+
paddingRight: cell.leaf ? actionWidth + 10 : 10,
|
|
3592
|
+
justifyContent: column.align === "right" ? "flex-end" : column.align === "center" ? "center" : "flex-start",
|
|
3593
|
+
textAlign: column.align === "right" ? "right" : column.align === "center" ? "center" : "left"
|
|
3594
|
+
},
|
|
3595
|
+
children: [
|
|
3596
|
+
/* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
|
|
3597
|
+
"span",
|
|
3598
|
+
{
|
|
3599
|
+
ref: (node) => {
|
|
3600
|
+
if (!column.renderHeader) return;
|
|
3601
|
+
const key = `group:${cell.key}:${cell.level}`;
|
|
3602
|
+
if (node) {
|
|
3603
|
+
customHeaderContentRefs.current.set(key, node);
|
|
3604
|
+
requestAnimationFrame(measureCustomHeaderHeight);
|
|
3605
|
+
} else customHeaderContentRefs.current.delete(key);
|
|
3606
|
+
},
|
|
3607
|
+
children: column.renderHeader ? column.renderHeader(column) : column.title
|
|
3608
|
+
}
|
|
3609
|
+
),
|
|
3610
|
+
!cell.leaf && columnDraggable && /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
|
|
3611
|
+
HeaderDragIcon,
|
|
3612
|
+
{
|
|
3613
|
+
className: `rvg-header-icon rvg-header-drag-icon${hoveredHeaderAction?.columnIndex === cell.startIndex && hoveredHeaderAction.action === "drag" ? " is-hovered" : ""}`,
|
|
3614
|
+
style: { left: width2 - 16, top: (rowHeightForLevel - 14) / 2 }
|
|
3615
|
+
}
|
|
3616
|
+
)
|
|
3617
|
+
]
|
|
3618
|
+
},
|
|
3619
|
+
`group:${cell.key}:${cell.level}`
|
|
3620
|
+
);
|
|
3621
|
+
};
|
|
2948
3622
|
const rangeHandlePosition = (() => {
|
|
2949
3623
|
if (!rangeSelection) return null;
|
|
2950
3624
|
const bounds = getRangeBounds();
|
|
@@ -3136,15 +3810,19 @@ function Table({
|
|
|
3136
3810
|
const headerY = fixedHeader ? 0 : -scrollRef.current.top;
|
|
3137
3811
|
const columnIndex = locateColumn(event.clientX);
|
|
3138
3812
|
const inHeader = localY >= headerY && localY < headerY + headerHeight;
|
|
3139
|
-
const
|
|
3140
|
-
const
|
|
3141
|
-
const
|
|
3142
|
-
const
|
|
3813
|
+
const inLeafHeader = inHeader && localY >= headerY + headerLeafTop;
|
|
3814
|
+
const hoveredHeaderCell = inHeader ? locateHeaderCell(event.clientX, event.clientY) : null;
|
|
3815
|
+
const hoveredAction = inLeafHeader ? locateHeaderAction(event.clientX, columnIndex) : null;
|
|
3816
|
+
const hoveredDragHandle = Boolean(hoveredHeaderCell && locateHeaderCellDragHandle(event.clientX, hoveredHeaderCell));
|
|
3817
|
+
const hoveredDragColumnIndex = hoveredHeaderCell?.startIndex ?? columnIndex;
|
|
3818
|
+
const hoveredTitle = inHeader && !hoveredAction && !hoveredDragHandle && hoveredHeaderCell && Boolean(hoveredHeaderCell.column.title) && isPointerOnHeaderTitle(event.clientX, event.clientY, hoveredHeaderCell) && (tooltipConfig.header || hoveredHeaderCell.leaf && isHeaderTitleTruncated(hoveredHeaderCell.startIndex));
|
|
3819
|
+
const nextHover = hoveredAction ? { columnIndex, action: hoveredAction } : hoveredDragHandle ? { columnIndex: hoveredDragColumnIndex, action: "drag" } : hoveredTitle ? { columnIndex: hoveredHeaderCell.startIndex, action: "title" } : null;
|
|
3820
|
+
const nextTooltip = hoveredAction && hoveredHeaderCell ? createHeaderTooltip(hoveredAction, columnIndex, hoveredHeaderCell) : hoveredDragHandle && hoveredHeaderCell ? createHeaderTooltip("drag", hoveredDragColumnIndex, hoveredHeaderCell) : hoveredTitle && hoveredHeaderCell ? createHeaderTooltip("title", hoveredHeaderCell.startIndex, hoveredHeaderCell) : null;
|
|
3143
3821
|
const previousHover = hoveredHeaderActionRef.current;
|
|
3144
3822
|
if (previousHover?.columnIndex !== nextHover?.columnIndex || previousHover?.action !== nextHover?.action) {
|
|
3145
3823
|
hoveredHeaderActionRef.current = nextHover;
|
|
3146
3824
|
setHoveredHeaderAction(nextHover);
|
|
3147
|
-
showHeaderTooltipAfterDelay(
|
|
3825
|
+
showHeaderTooltipAfterDelay(nextTooltip);
|
|
3148
3826
|
scheduleDraw();
|
|
3149
3827
|
}
|
|
3150
3828
|
if (inHeader) {
|
|
@@ -3168,8 +3846,8 @@ function Table({
|
|
|
3168
3846
|
if (annotation?.content.trim()) {
|
|
3169
3847
|
const cellLeft = getDisplayedColumnLeft(cell.columnIndex);
|
|
3170
3848
|
const cellTop = bodyTop + cell.rowIndex * rowHeight - scrollRef.current.top;
|
|
3171
|
-
const
|
|
3172
|
-
showCellTooltip(
|
|
3849
|
+
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 };
|
|
3850
|
+
showCellTooltip(nextTooltip2);
|
|
3173
3851
|
} else {
|
|
3174
3852
|
const label = getCellLabel(cell.rowIndex, cell.columnIndex);
|
|
3175
3853
|
const context = event.currentTarget.getContext("2d");
|
|
@@ -3196,7 +3874,7 @@ function Table({
|
|
|
3196
3874
|
showCellTooltipAfterDelay(null);
|
|
3197
3875
|
} else {
|
|
3198
3876
|
const placement = textRight + 330 > viewport.width ? "left" : "right";
|
|
3199
|
-
const
|
|
3877
|
+
const nextTooltip2 = {
|
|
3200
3878
|
rowIndex: cell.rowIndex,
|
|
3201
3879
|
columnIndex: cell.columnIndex,
|
|
3202
3880
|
left: placement === "right" ? textRight + 16 : Math.max(8, textLeft - 16),
|
|
@@ -3204,13 +3882,13 @@ function Table({
|
|
|
3204
3882
|
label,
|
|
3205
3883
|
placement
|
|
3206
3884
|
};
|
|
3207
|
-
showCellTooltipAfterDelay(
|
|
3885
|
+
showCellTooltipAfterDelay(nextTooltip2);
|
|
3208
3886
|
}
|
|
3209
3887
|
}
|
|
3210
3888
|
}
|
|
3211
3889
|
}
|
|
3212
3890
|
}
|
|
3213
|
-
const cursor = inHeader &&
|
|
3891
|
+
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";
|
|
3214
3892
|
if (event.currentTarget.style.cursor !== cursor) event.currentTarget.style.cursor = cursor;
|
|
3215
3893
|
},
|
|
3216
3894
|
onMouseLeave: (event) => {
|
|
@@ -3253,6 +3931,7 @@ function Table({
|
|
|
3253
3931
|
const headerY = fixedHeader ? 0 : -scrollRef.current.top;
|
|
3254
3932
|
if (localY >= headerY && localY < headerY + headerHeight) {
|
|
3255
3933
|
clickedCellRef.current = null;
|
|
3934
|
+
if (localY < headerY + headerLeafTop) return;
|
|
3256
3935
|
const columnIndex = locateColumn(event.clientX);
|
|
3257
3936
|
const action = locateHeaderAction(event.clientX, columnIndex);
|
|
3258
3937
|
if (action === "sort") {
|
|
@@ -3314,21 +3993,30 @@ function Table({
|
|
|
3314
3993
|
className: "rvg-text-scroll-content",
|
|
3315
3994
|
style: { left: -fixedWidth, width: contentWidth, transform: `translateX(${-currentScroll.left}px)` },
|
|
3316
3995
|
children: [
|
|
3317
|
-
columns.map((column, columnIndex) => column.fixed ? null : columnIndex >= domRange.columnStart && columnIndex < domRange.columnEnd ? /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("span", { children: renderHeaderTitle(columnIndex,
|
|
3996
|
+
columns.map((column, columnIndex) => column.fixed || columnIndex < utilityColumnCount || headerDepth > 1 ? null : columnIndex >= domRange.columnStart && columnIndex < domRange.columnEnd ? /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("span", { children: renderHeaderTitle(columnIndex, getScrollableColumnLeft(columnIndex)) }, column.key) : null),
|
|
3997
|
+
headerCells.map((cell) => {
|
|
3998
|
+
if (columns[cell.startIndex]?.fixed) return null;
|
|
3999
|
+
if (cell.endIndex < domRange.columnStart || cell.startIndex >= domRange.columnEnd) return null;
|
|
4000
|
+
return renderGroupedHeaderCell(cell);
|
|
4001
|
+
}),
|
|
3318
4002
|
/* @__PURE__ */ (0, import_jsx_runtime7.jsx)("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) => {
|
|
3319
4003
|
if (column.fixed || columnIndex < domRange.columnStart || columnIndex >= domRange.columnEnd) return null;
|
|
3320
|
-
return renderCellText(rowIndex, columnIndex,
|
|
4004
|
+
return renderCellText(rowIndex, columnIndex, getScrollableColumnLeft(columnIndex));
|
|
3321
4005
|
})) })
|
|
3322
4006
|
]
|
|
3323
4007
|
}
|
|
3324
4008
|
) }),
|
|
3325
4009
|
/* @__PURE__ */ (0, import_jsx_runtime7.jsx)("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) => {
|
|
3326
4010
|
if (!column.fixed) return null;
|
|
3327
|
-
const left = column.fixed === "left" ?
|
|
4011
|
+
const left = column.fixed === "left" ? leftFixedOffsets.get(columnIndex) ?? 0 : viewport.width - (rightFixedOffsets.get(columnIndex) ?? 0) - metrics[columnIndex].width;
|
|
3328
4012
|
return renderCellText(rowIndex, columnIndex, left);
|
|
3329
4013
|
})) }),
|
|
3330
|
-
columns.map((column, columnIndex) => column.fixed === "left" ? renderHeaderTitle(columnIndex, metrics[columnIndex].left) : null),
|
|
3331
|
-
|
|
4014
|
+
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),
|
|
4015
|
+
headerCells.map((cell) => columns[cell.startIndex]?.fixed ? renderGroupedHeaderCell(cell) : null),
|
|
4016
|
+
fixedWidth > 0 && currentScroll.left > 0 && /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("div", { className: "rvg-text-fixed-shadow is-left", style: { left: fixedWidth } }),
|
|
4017
|
+
rightFixedWidth > 0 && currentScroll.left < contentWidth - viewport.width && /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("div", { className: "rvg-text-fixed-shadow is-right", style: { right: rightFixedWidth } }),
|
|
4018
|
+
hasVerticalBorders && fixedWidth > 0 && /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("div", { className: "rvg-text-fixed-border is-left", style: { left: fixedWidth - 1 } }),
|
|
4019
|
+
hasVerticalBorders && rightFixedWidth > 0 && /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("div", { className: "rvg-text-fixed-border is-right", style: { right: rightFixedWidth } })
|
|
3332
4020
|
] }),
|
|
3333
4021
|
/* @__PURE__ */ (0, import_jsx_runtime7.jsx)("div", { className: "rvg-spacer", style: { width: contentWidth, height: contentHeight + bodyTop, marginTop: -renderHeight } })
|
|
3334
4022
|
] }),
|
|
@@ -3336,18 +4024,18 @@ function Table({
|
|
|
3336
4024
|
"div",
|
|
3337
4025
|
{
|
|
3338
4026
|
className: `rvg-summary-row is-${summaryPosition}${hasVerticalBorders ? "" : " is-vertical-borderless"}`,
|
|
3339
|
-
style: { height: rowHeight, top: summaryPosition === "top" ? headerHeight : void 0 },
|
|
4027
|
+
style: { height: rowHeight, top: summaryPosition === "top" ? fixedHeader ? headerHeight : headerHeight - currentScroll.top : void 0 },
|
|
3340
4028
|
children: [
|
|
3341
4029
|
/* @__PURE__ */ (0, import_jsx_runtime7.jsx)("div", { className: "rvg-summary-scroll-clip", style: { left: fixedWidth, right: rightFixedWidth }, children: /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
|
|
3342
4030
|
"div",
|
|
3343
4031
|
{
|
|
3344
4032
|
className: "rvg-summary-scroll-content",
|
|
3345
4033
|
style: { left: -fixedWidth, width: contentWidth, transform: `translateX(${-currentScroll.left}px)` },
|
|
3346
|
-
children: columns.map((column, columnIndex) => column.fixed ? null : renderSummaryCell(columnIndex,
|
|
4034
|
+
children: columns.map((column, columnIndex) => column.fixed ? null : renderSummaryCell(columnIndex, getScrollableColumnLeft(columnIndex)))
|
|
3347
4035
|
}
|
|
3348
4036
|
) }),
|
|
3349
4037
|
/* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("div", { className: "rvg-summary-fixed-layer", children: [
|
|
3350
|
-
columns.map((column, columnIndex) => column.fixed === "left" ? renderSummaryCell(columnIndex,
|
|
4038
|
+
columns.map((column, columnIndex) => column.fixed === "left" ? renderSummaryCell(columnIndex, leftFixedOffsets.get(columnIndex) ?? 0) : null),
|
|
3351
4039
|
columns.map((column, columnIndex) => column.fixed === "right" ? renderSummaryCell(columnIndex, viewport.width - (rightFixedOffsets.get(columnIndex) ?? 0) - metrics[columnIndex].width) : null)
|
|
3352
4040
|
] }),
|
|
3353
4041
|
fixedWidth > 0 && currentScroll.left > 0 && /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("div", { className: "rvg-summary-fixed-shadow is-left", style: { left: fixedWidth } }),
|
|
@@ -3364,11 +4052,11 @@ function Table({
|
|
|
3364
4052
|
ref: scrollingHeaderIconsRef,
|
|
3365
4053
|
className: "rvg-header-icon-layer rvg-header-icon-layer-scroll",
|
|
3366
4054
|
style: { left: -fixedWidth, width: contentWidth, transform: `translateX(${-scrollRef.current.left}px)` },
|
|
3367
|
-
children: columns.map((column, index) => column.fixed ? null : /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("span", { children: renderHeaderIcons(index,
|
|
4055
|
+
children: columns.map((column, index) => column.fixed ? null : /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("span", { children: renderHeaderIcons(index, getScrollableColumnLeft(index)) }, column.key))
|
|
3368
4056
|
}
|
|
3369
4057
|
) }),
|
|
3370
4058
|
/* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("div", { className: "rvg-header-icon-layer", children: [
|
|
3371
|
-
columns.map((column, index) => column.fixed === "left" ? /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("span", { children: renderHeaderIcons(index,
|
|
4059
|
+
columns.map((column, index) => column.fixed === "left" ? /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("span", { children: renderHeaderIcons(index, leftFixedOffsets.get(index) ?? 0) }, column.key) : null),
|
|
3372
4060
|
columns.map((column, index) => column.fixed === "right" ? /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("span", { children: renderHeaderIcons(index, viewport.width - (rightFixedOffsets.get(index) ?? 0) - metrics[index].width) }, column.key) : null)
|
|
3373
4061
|
] })
|
|
3374
4062
|
] }),
|
|
@@ -3645,7 +4333,42 @@ function Table({
|
|
|
3645
4333
|
] })
|
|
3646
4334
|
] }) }),
|
|
3647
4335
|
toast && /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("div", { className: `rvg-toast is-${toast.type}`, role: "status", children: toast.message }),
|
|
4336
|
+
/* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
|
|
4337
|
+
"div",
|
|
4338
|
+
{
|
|
4339
|
+
ref: verticalScrollbarRef,
|
|
4340
|
+
className: "rvg-vertical-scrollbar",
|
|
4341
|
+
onPointerEnter: () => keepCustomScrollbarVisible("vertical"),
|
|
4342
|
+
onPointerLeave: () => releaseCustomScrollbarVisible("vertical"),
|
|
4343
|
+
onPointerDown: handleVerticalScrollbarPointerDown,
|
|
4344
|
+
onPointerMove: handleVerticalScrollbarPointerMove,
|
|
4345
|
+
onPointerUp: handleVerticalScrollbarPointerUp,
|
|
4346
|
+
onPointerCancel: handleVerticalScrollbarPointerUp,
|
|
4347
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("div", { ref: verticalScrollbarThumbRef, className: "rvg-vertical-scrollbar-thumb" })
|
|
4348
|
+
}
|
|
4349
|
+
),
|
|
4350
|
+
/* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
|
|
4351
|
+
"div",
|
|
4352
|
+
{
|
|
4353
|
+
ref: horizontalScrollbarRef,
|
|
4354
|
+
className: "rvg-horizontal-scrollbar",
|
|
4355
|
+
onPointerEnter: () => keepCustomScrollbarVisible("horizontal"),
|
|
4356
|
+
onPointerLeave: () => releaseCustomScrollbarVisible("horizontal"),
|
|
4357
|
+
onPointerDown: handleHorizontalScrollbarPointerDown,
|
|
4358
|
+
onPointerMove: handleHorizontalScrollbarPointerMove,
|
|
4359
|
+
onPointerUp: handleHorizontalScrollbarPointerUp,
|
|
4360
|
+
onPointerCancel: handleHorizontalScrollbarPointerUp,
|
|
4361
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("div", { ref: horizontalScrollbarThumbRef, className: "rvg-horizontal-scrollbar-thumb" })
|
|
4362
|
+
}
|
|
4363
|
+
),
|
|
3648
4364
|
/* @__PURE__ */ (0, import_jsx_runtime7.jsx)("div", { ref: dragGuideRef, className: "rvg-column-guide" }),
|
|
4365
|
+
resizeGuideX !== null && /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
|
|
4366
|
+
"div",
|
|
4367
|
+
{
|
|
4368
|
+
className: "rvg-column-guide is-resizing",
|
|
4369
|
+
style: { display: "block", top: resizeGuideTop, transform: `translateX(${Math.round(resizeGuideX)}px)` }
|
|
4370
|
+
}
|
|
4371
|
+
),
|
|
3649
4372
|
/* @__PURE__ */ (0, import_jsx_runtime7.jsx)("div", { ref: rowDragGuideRef, className: "rvg-row-guide" }),
|
|
3650
4373
|
filterEditor && /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("div", { ref: filterRef, className: "rvg-filter", style: { left: filterEditor.left, top: filterEditor.top }, children: [
|
|
3651
4374
|
/* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
|