@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/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 stretchable = columns.map((column, index) => ({ column, index })).filter(({ column }) => column.fixed !== "right" && !column.rowSelection && !column.rowDragHandle && !column.rowNumber);
818
- const targets = stretchable.length > 0 ? stretchable : columns.map((column, index) => ({ column, index })).filter(({ column }) => column.fixed !== "right");
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" ? Math.max(value, metrics[index].right) : value, 0);
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" ? metrics[index].left : columns[index].fixed === "right" ? width - (rightOffsets.get(index) ?? 0) - metrics[index].width : metrics[index].left - scrollLeft;
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), headerHeight - 2);
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 + headerHeight / 2);
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 = false,
1332
- rowHeight = 36,
1333
- headerHeight = 40,
1370
+ autoHeight = true,
1371
+ layout,
1372
+ rowHeight: rowHeightProp,
1373
+ headerHeight: headerHeightProp,
1334
1374
  fixedHeader = true,
1335
1375
  locale = "zh-CN",
1336
- verticalBorderless = false,
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 hasVerticalBorders = !verticalBorderless;
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
- return [...utilityColumns, ...sourceColumns];
1410
- }, [rowDraggable, rowNumber, rowSelection, sourceColumns, sourceRows.length]);
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" ? Math.max(width2, metrics[index].right) : width2, 0), [columns, metrics]);
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) - topSummaryHeight);
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 (cellSpans.length === 0) return createEmptyCellSpanLookup();
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 cellSpans) {
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
- }, [cellSpans, columns, getRowKey, rows]);
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" ? metric.left : column.fixed === "right" ? viewport.width - (rightFixedOffsets.get(selection.columnIndex) ?? 0) - metric.width : metric.left - element.scrollLeft;
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 = hitTestColumn(metrics, localX);
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
- columnIndex = hitTestColumn(metrics, localX + scrollRef.current.left);
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 metric.left;
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 metric.left - scrollRef.current.left;
1926
- }, [columns, metrics, rightFixedOffsets, viewport.width]);
1927
- const locateResizeHandle = (0, import_react5.useCallback)((clientX) => {
1928
- if (!columnResizable || !canvasRef.current) return -1;
1929
- const localX = clientX - canvasRef.current.getBoundingClientRect().left;
1930
- let closest = -1;
1931
- let distance = 6;
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 closest;
1941
- }, [columnResizable, columns, getDisplayedColumnLeft, metrics]);
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 setDragGuide = (0, import_react5.useCallback)((x) => {
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) guide.style.transform = `translateX(${Math.round(x)}px)`;
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 index = getHeaderColumn(input.clientX, input.clientY);
2009
- if (columnDraggable && index >= 0) return locateHeaderDragHandle(input.clientX, index) && locateResizeHandle(input.clientX) < 0;
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 headerColumn = getHeaderColumn(input.clientX, input.clientY);
2015
- if (headerColumn < 0) {
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 = headerColumn;
2481
+ const sourceIndex = headerDragCell.startIndex;
2019
2482
  columnDragPreviewRef.current = { sourceIndex, targetIndex: sourceIndex };
2020
- return { type: "table-column", sourceIndex };
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) || columns[targetIndex]?.fixed !== columns[sourceIndex]?.fixed) {
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
- let destinationIndex = targetIndex + (edge === "right" ? 1 : 0);
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 = getDisplayedColumnLeft(targetIndex);
2133
- setDragGuide(edge === "right" ? targetLeft + metrics[targetIndex].width : targetLeft);
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 rawTargetIndex = Number(target?.targetIndex);
2140
- let targetIndex2 = rawTargetIndex + (target?.rowEdge === "bottom" ? 1 : 0);
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(rawTargetIndex) && sourceIndex !== targetIndex2) onRowOrderChange?.(sourceIndex, targetIndex2);
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 targetIndex = columnDragPreviewRef.current?.targetIndex ?? Number(target?.targetIndex);
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 && columns[targetIndex]?.fixed === columns[sourceIndex]?.fixed) {
2161
- onColumnOrderChange?.(sourceIndex - utilityColumnCount, targetIndex - utilityColumnCount);
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
- let targetIndex = getHeaderColumn(input.clientX, input.clientY);
2180
- if (targetIndex < 0) return { targetIndex: -1 };
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 = getDisplayedColumnLeft(targetIndex);
2188
- const columnEdge = input.clientX - canvas.getBoundingClientRect().left < left + metrics[targetIndex].width / 2 ? "left" : "right";
2189
- return (0, import_closest_edge.attachClosestEdge)({ targetIndex, columnEdge }, { element, input, allowedEdges: ["left", "right"] });
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, locateHeaderDragHandle, locateResizeHandle, locateRow, metrics, onColumnOrderChange, onRowOrderChange, rowDraggable, rowHeight, rows, selectedRowKeySet, setDragGuide, setRowDragGuide, utilityColumnCount]);
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
- if (metric.left - nextLeft < fixedWidth) nextLeft = Math.max(0, metric.left - fixedWidth - horizontalFocusGap);
2304
- if (metric.left + cellWidth - nextLeft > viewport.width - rightFixedWidth) nextLeft = metric.left + cellWidth - viewport.width + rightFixedWidth + horizontalFocusGap;
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
- if (metric.left - nextLeft < fixedWidth) nextLeft = Math.max(0, metric.left - fixedWidth - horizontalFocusGap);
2339
- if (metric.left + cellWidth - nextLeft > viewport.width - rightFixedWidth) nextLeft = metric.left + cellWidth - viewport.width + rightFixedWidth + horizontalFocusGap;
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 && metric.left < scroller.scrollLeft + fixedWidth) scroller.scrollLeft = Math.max(0, metric.left - fixedWidth);
2493
- if (columns[columnIndex].fixed === void 0 && metric.left + cellWidth > scroller.scrollLeft + viewport.width - rightFixedWidth) scroller.scrollLeft = metric.left + cellWidth - viewport.width + rightFixedWidth;
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" ? metric.left : fixedSide === "right" ? viewport.width - (rightFixedOffsets.get(editing.columnIndex) ?? 0) - metric.width : metric.left - scrollRef.current.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 resizeIndex = locateResizeHandle(event.clientX);
2517
- if (resizeIndex >= 0) {
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
- columnDragRef.current = { type: "resize", columnIndex: resizeIndex, startX: event.clientX, startWidth: metrics[resizeIndex].width };
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
- setDragGuide(getDisplayedColumnLeft(resizeIndex) + metrics[resizeIndex].width);
3038
+ setResizeGuideTop(headerRowOffsets[resizeCell.level] ?? 0);
3039
+ setResizeGuideX(startGuideX);
2523
3040
  return;
2524
3041
  }
2525
- }, [fixedHeader, getDisplayedColumnLeft, headerHeight, locateResizeHandle, metrics, setDragGuide]);
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 width2 = Math.max(getMinimumColumnWidth(columns[drag.columnIndex], columnDraggable), Math.round(drag.startWidth + event.clientX - drag.startX));
2530
- onColumnResize?.(columns[drag.columnIndex].key, width2);
2531
- setDragGuide(getDisplayedColumnLeft(drag.columnIndex) + width2);
2532
- }, [columnDraggable, columns, getDisplayedColumnLeft, onColumnResize, setDragGuide]);
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
- setDragGuide(null);
2539
- }, [setDragGuide]);
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 = (headerHeight - 14) / 2;
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 headerTooltip = (() => {
2578
- if (!visibleHeaderTooltip) return null;
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 columnRight = getDisplayedColumnLeft(columnIndex) + metrics[columnIndex].width;
2583
- const visibleActions = getVisibleHeaderActions(column, metrics[columnIndex].width, columnDraggable, measureHeaderTitleWidth(columnIndex));
2584
- let anchorX = columnRight - (visibleActions.drag ? 18 : 4);
2585
- let anchorLeft = anchorX;
2586
- let label;
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 titleWidth = measureHeaderTitleWidth(columnIndex);
2589
- const actionWidth = (Number(visibleActions.drag) + Number(visibleActions.filter) + Number(visibleActions.sort)) * 18;
2590
- const contentWidth2 = Math.max(0, metrics[columnIndex].width - actionWidth);
2591
- const textWidth = Math.min(titleWidth, Math.max(0, contentWidth2 - 20));
2592
- const columnLeft = getDisplayedColumnLeft(columnIndex);
2593
- anchorX = column.align === "right" ? columnLeft + contentWidth2 - 10 : column.align === "center" ? columnLeft + metrics[columnIndex].width / 2 + textWidth / 2 : columnLeft + 10 + textWidth;
2594
- anchorLeft = column.align === "right" ? anchorX - textWidth : column.align === "center" ? columnLeft + metrics[columnIndex].width / 2 - textWidth / 2 : columnLeft + 10;
2595
- label = column.title;
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 = columnRight;
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
- } else {
2604
- if (visibleActions.sort) anchorX -= 18;
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: (fixedHeader ? 0 : -scrollPosition.top) + headerHeight / 2,
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 ? `${next.columnIndex}:${next.action}` : "";
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: fixedHeader ? 0 : -currentScrollTop, width: metrics[columnIndex].width, height: headerHeight, justifyContent: "center", padding: 0, textAlign: "center" },
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: fixedHeader ? 0 : -currentScrollTop, width: metrics[columnIndex].width, height: headerHeight },
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 hoveredAction = inHeader ? locateHeaderAction(event.clientX, columnIndex) : null;
3140
- const hoveredDragHandle = inHeader && locateHeaderDragHandle(event.clientX, columnIndex);
3141
- const hoveredTitle = inHeader && !hoveredAction && !hoveredDragHandle && columnIndex >= 0 && Boolean(columns[columnIndex]?.title) && (tooltipConfig.header || isHeaderTitleTruncated(columnIndex));
3142
- const nextHover = hoveredAction ? { columnIndex, action: hoveredAction } : hoveredDragHandle ? { columnIndex, action: "drag" } : hoveredTitle ? { columnIndex, action: "title" } : null;
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(nextHover);
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 nextTooltip = { 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 };
3172
- showCellTooltip(nextTooltip);
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 nextTooltip = {
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(nextTooltip);
3885
+ showCellTooltipAfterDelay(nextTooltip2);
3208
3886
  }
3209
3887
  }
3210
3888
  }
3211
3889
  }
3212
3890
  }
3213
- const cursor = inHeader && locateResizeHandle(event.clientX) >= 0 ? "col-resize" : hoveredAction ? "pointer" : hoveredDragHandle ? "move" : columnIndex >= 0 && columns[columnIndex].rowSelection ? "pointer" : !inHeader && rowDraggable && columnIndex >= 0 && columns[columnIndex].rowDragHandle ? "move" : "default";
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, metrics[columnIndex].left) }, column.key) : null),
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, metrics[columnIndex].left);
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" ? metrics[columnIndex].left : viewport.width - (rightFixedOffsets.get(columnIndex) ?? 0) - metrics[columnIndex].width;
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
- columns.map((column, columnIndex) => column.fixed === "right" ? renderHeaderTitle(columnIndex, viewport.width - (rightFixedOffsets.get(columnIndex) ?? 0) - metrics[columnIndex].width) : null)
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, metrics[columnIndex].left))
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, metrics[columnIndex].left) : null),
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, metrics[index].left) }, column.key))
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, metrics[index].left) }, column.key) : null),
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)(