@lensui/lens-table 0.1.5 → 0.1.6

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
@@ -318,16 +318,6 @@ function ContextMenuIcon({ type }) {
318
318
  };
319
319
  return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("svg", { className: "rvg-context-menu-svg", viewBox: "0 0 16 16", "aria-hidden": "true", children: paths[type].map((path) => /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("path", { d: path }, path)) });
320
320
  }
321
- function RowDragHandleIcon() {
322
- return /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("svg", { viewBox: "0 0 16 16", focusable: "false", children: [
323
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("circle", { cx: "5", cy: "4", r: "1" }),
324
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("circle", { cx: "11", cy: "4", r: "1" }),
325
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("circle", { cx: "5", cy: "8", r: "1" }),
326
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("circle", { cx: "11", cy: "8", r: "1" }),
327
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("circle", { cx: "5", cy: "12", r: "1" }),
328
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("circle", { cx: "11", cy: "12", r: "1" })
329
- ] });
330
- }
331
321
  function LoadingSpinnerIcon() {
332
322
  return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("svg", { viewBox: "0 0 24 24", "aria-hidden": "true", children: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("path", { d: "M20 12a8 8 0 1 1-3.1-6.3" }) });
333
323
  }
@@ -755,6 +745,48 @@ function createRowDragHandleSvg() {
755
745
  }
756
746
  return svg;
757
747
  }
748
+ function createHeaderDragHandleSvg() {
749
+ const svg = document.createElementNS("http://www.w3.org/2000/svg", "svg");
750
+ svg.setAttribute("viewBox", "0 0 14 14");
751
+ svg.setAttribute("aria-hidden", "true");
752
+ for (const cy of [2.5, 7, 11.5]) {
753
+ const circle = document.createElementNS("http://www.w3.org/2000/svg", "circle");
754
+ circle.setAttribute("cx", "7");
755
+ circle.setAttribute("cy", String(cy));
756
+ circle.setAttribute("r", "1.25");
757
+ svg.appendChild(circle);
758
+ }
759
+ return svg;
760
+ }
761
+ function createHeaderSortSvg(direction) {
762
+ const svg = document.createElementNS("http://www.w3.org/2000/svg", "svg");
763
+ svg.setAttribute("viewBox", "0 0 14 14");
764
+ svg.setAttribute("aria-hidden", "true");
765
+ for (const [value, active] of [
766
+ ["M3 5.5 7 1.5l4 4Z", direction === "asc"],
767
+ ["M3 8.5 7 12.5l4-4Z", direction === "desc"]
768
+ ]) {
769
+ const path = document.createElementNS("http://www.w3.org/2000/svg", "path");
770
+ path.setAttribute("d", value);
771
+ if (active) path.classList.add("is-active");
772
+ svg.appendChild(path);
773
+ }
774
+ return svg;
775
+ }
776
+ function createHeaderSearchSvg(active) {
777
+ const svg = document.createElementNS("http://www.w3.org/2000/svg", "svg");
778
+ svg.setAttribute("viewBox", "0 0 14 14");
779
+ svg.setAttribute("aria-hidden", "true");
780
+ if (active) svg.classList.add("is-active");
781
+ const circle = document.createElementNS("http://www.w3.org/2000/svg", "circle");
782
+ circle.setAttribute("cx", "5.5");
783
+ circle.setAttribute("cy", "5.5");
784
+ circle.setAttribute("r", "4.25");
785
+ const path = document.createElementNS("http://www.w3.org/2000/svg", "path");
786
+ path.setAttribute("d", "m8.75 8.75 2.25 2.25");
787
+ svg.append(circle, path);
788
+ return svg;
789
+ }
758
790
 
759
791
  // src/Table.tsx
760
792
  var import_adapter = require("@atlaskit/pragmatic-drag-and-drop/element/adapter");
@@ -775,6 +807,7 @@ function getDisplayLabel(column, row, rowIndex) {
775
807
 
776
808
  // src/core/layout.ts
777
809
  var DEFAULT_COLUMN_WIDTH = 140;
810
+ var HEADER_ACTION_SLOT_WIDTH = 16;
778
811
  function isDataColumn(column) {
779
812
  return !column.rowSelection && !column.rowDragHandle && !column.rowNumber;
780
813
  }
@@ -789,7 +822,7 @@ function getHeaderTitleRequiredWidth(column, titleWidth) {
789
822
  return minimumReadableTitleWidth + (column.align === "center" ? 20 : 22);
790
823
  }
791
824
  function getVisibleHeaderActions(column, width, columnDraggable = false, titleWidth = 26) {
792
- const capacity = Math.max(0, Math.floor((width - getHeaderTitleRequiredWidth(column, titleWidth)) / 18));
825
+ const capacity = Math.max(0, Math.floor((width - getHeaderTitleRequiredWidth(column, titleWidth)) / HEADER_ACTION_SLOT_WIDTH));
793
826
  const candidates = [
794
827
  isColumnFilterable(column) ? "filter" : null,
795
828
  isColumnSortable(column) ? "sort" : null,
@@ -868,12 +901,15 @@ var COLORS = {
868
901
  grid: "#e3e6e8",
869
902
  text: "#202124",
870
903
  muted: "#5f6368",
904
+ icon: "#9aa0a6",
905
+ borderStrong: "#b8bec4",
871
906
  selection: "#1677ff",
872
907
  selectionFill: "#edf4ff",
873
908
  axisSelectionFill: "#e8f2ff",
874
909
  rowHoverFill: "#f6f9fc",
875
910
  editedFill: "#fff1b8",
876
911
  insertedFill: "#c8ead4",
912
+ columnDropTargetFill: "#eeeeee",
877
913
  stripe: "#fafbfc"
878
914
  };
879
915
  function ellipsizeText(context, text, maxWidth) {
@@ -893,7 +929,7 @@ function ellipsizeText(context, text, maxWidth) {
893
929
  return `${text.slice(0, start)}${ellipsis}`;
894
930
  }
895
931
  function paintGrid(options) {
896
- 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;
932
+ 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, rowSelectionMode, selectedColumnKeys, highlightEditedCells, highlightInsertedRows, insertedRowKeys, editedCellKeys, cellAnnotations, getRowKey, rowDragPreview, columnDropTarget } = options;
897
933
  const headerLeafTop = options.headerLeafTop ?? 0;
898
934
  const headerLeafHeight = options.headerLeafHeight ?? headerHeight;
899
935
  const colors = { ...COLORS, ...options.colors };
@@ -939,11 +975,19 @@ function paintGrid(options) {
939
975
  const getColumnX = (index) => {
940
976
  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;
941
977
  };
978
+ const getRowDragOffset = (rowIndex) => {
979
+ if (!rowDragPreview || rowIndex === rowDragPreview.sourceIndex) return 0;
980
+ if (rowDragPreview.sourceIndex < rowDragPreview.targetIndex) {
981
+ return rowIndex > rowDragPreview.sourceIndex && rowIndex <= rowDragPreview.targetIndex ? -rowHeight : 0;
982
+ }
983
+ return rowIndex >= rowDragPreview.targetIndex && rowIndex < rowDragPreview.sourceIndex ? rowHeight : 0;
984
+ };
985
+ const rowBelowDragGap = rowDragPreview ? rowDragPreview.sourceIndex <= rowDragPreview.targetIndex ? rowDragPreview.targetIndex + 1 : rowDragPreview.targetIndex : -1;
942
986
  const paintBodyColumns = (layer) => {
943
987
  const rowStart = Math.max(0, range.rowStart - Math.max(0, options.maxRowSpan - 1));
944
988
  for (let rowIndex = rowStart; rowIndex < range.rowEnd; rowIndex += 1) {
945
989
  if (rowDragPreview?.sourceIndex === rowIndex) continue;
946
- const shift = rowDragPreview ? rowDragPreview.sourceIndex < rowDragPreview.targetIndex ? rowIndex > rowDragPreview.sourceIndex && rowIndex <= rowDragPreview.targetIndex ? -rowHeight : 0 : rowIndex >= rowDragPreview.targetIndex && rowIndex < rowDragPreview.sourceIndex ? rowHeight : 0 : 0;
990
+ const shift = getRowDragOffset(rowIndex);
947
991
  const y = bodyTop + rowIndex * rowHeight - scrollTop + shift;
948
992
  const row = rows[rowIndex];
949
993
  const rowKey = getRowKey(row, rowIndex);
@@ -976,6 +1020,10 @@ function paintGrid(options) {
976
1020
  ctx.fillStyle = colors.rowHoverFill;
977
1021
  ctx.fillRect(x, y, cellWidth, cellHeight);
978
1022
  }
1023
+ if (columnDropTarget && columnIndex >= columnDropTarget.startIndex && columnIndex <= columnDropTarget.endIndex) {
1024
+ ctx.fillStyle = colors.columnDropTargetFill;
1025
+ ctx.fillRect(x, y, cellWidth, cellHeight);
1026
+ }
979
1027
  if (highlightInsertedRows && insertedRowKeys.has(rowKey)) {
980
1028
  ctx.fillStyle = colors.insertedFill;
981
1029
  ctx.fillRect(x, y, cellWidth, cellHeight);
@@ -1002,6 +1050,7 @@ function paintGrid(options) {
1002
1050
  }
1003
1051
  ctx.fillStyle = colors.grid;
1004
1052
  if (!verticalBorderless) ctx.fillRect(x + cellWidth - 1, y, 1, cellHeight);
1053
+ if (rowIndex === rowBelowDragGap) ctx.fillRect(x, y, cellWidth, 1);
1005
1054
  if (!options.suppressLastRowBottomBorder || rowIndex !== rows.length - 1) {
1006
1055
  ctx.fillRect(x, y + cellHeight - 1, cellWidth, 1);
1007
1056
  }
@@ -1028,6 +1077,59 @@ function paintGrid(options) {
1028
1077
  if (columnIndex === rangeColumnStart) ctx.fillRect(x, y, 2, cellHeight);
1029
1078
  if (columnIndex === rangeColumnEnd) ctx.fillRect(x + cellWidth - 2, y, 2, cellHeight);
1030
1079
  }
1080
+ if (column.rowDragHandle) {
1081
+ const iconLeft = x + (cellWidth - 16) / 2;
1082
+ const iconTop = y + (cellHeight - 16) / 2;
1083
+ ctx.fillStyle = colors.icon;
1084
+ ctx.beginPath();
1085
+ for (const circleX of [5, 11]) {
1086
+ for (const circleY of [4, 8, 12]) {
1087
+ ctx.moveTo(iconLeft + circleX + 1, iconTop + circleY);
1088
+ ctx.arc(iconLeft + circleX, iconTop + circleY, 1, 0, Math.PI * 2);
1089
+ }
1090
+ }
1091
+ ctx.fill();
1092
+ }
1093
+ if (column.rowSelection) {
1094
+ const iconLeft = x + (cellWidth - 16) / 2;
1095
+ const iconTop = y + (cellHeight - 16) / 2;
1096
+ const checked = selectedRowKeys.has(rowKey);
1097
+ ctx.save();
1098
+ ctx.lineWidth = 1;
1099
+ if (rowSelectionMode === "single") {
1100
+ ctx.beginPath();
1101
+ ctx.arc(iconLeft + 8, iconTop + 8, 6.25, 0, Math.PI * 2);
1102
+ ctx.fillStyle = colors.background;
1103
+ ctx.fill();
1104
+ ctx.strokeStyle = checked ? colors.selection : colors.borderStrong;
1105
+ ctx.stroke();
1106
+ if (checked) {
1107
+ ctx.beginPath();
1108
+ ctx.arc(iconLeft + 8, iconTop + 8, 3.25, 0, Math.PI * 2);
1109
+ ctx.fillStyle = colors.selection;
1110
+ ctx.fill();
1111
+ }
1112
+ } else {
1113
+ ctx.beginPath();
1114
+ ctx.roundRect(iconLeft + 1.5, iconTop + 1.5, 13, 13, 3);
1115
+ ctx.fillStyle = checked ? colors.selection : colors.background;
1116
+ ctx.fill();
1117
+ ctx.strokeStyle = checked ? colors.selection : colors.borderStrong;
1118
+ ctx.stroke();
1119
+ if (checked) {
1120
+ ctx.beginPath();
1121
+ ctx.moveTo(iconLeft + 4.1, iconTop + 8);
1122
+ ctx.lineTo(iconLeft + 6.45, iconTop + 10.35);
1123
+ ctx.lineTo(iconLeft + 11.9, iconTop + 5.15);
1124
+ ctx.strokeStyle = colors.background;
1125
+ ctx.lineWidth = 1.75;
1126
+ ctx.lineCap = "round";
1127
+ ctx.lineJoin = "round";
1128
+ ctx.stroke();
1129
+ }
1130
+ }
1131
+ ctx.restore();
1132
+ }
1031
1133
  if (!column.rowDragHandle && !column.rowSelection && !column.renderCell && !(editing?.rowIndex === rowIndex && editing.columnIndex === columnIndex)) {
1032
1134
  let label = "";
1033
1135
  try {
@@ -1083,6 +1185,10 @@ function paintGrid(options) {
1083
1185
  ctx.fillStyle = colors.selectionFill;
1084
1186
  ctx.fillRect(x, headerY, metric.width, headerHeight);
1085
1187
  }
1188
+ if (columnDropTarget && columnIndex >= columnDropTarget.startIndex && columnIndex <= columnDropTarget.endIndex) {
1189
+ ctx.fillStyle = colors.columnDropTargetFill;
1190
+ ctx.fillRect(x, headerY, metric.width, headerHeight);
1191
+ }
1086
1192
  ctx.fillStyle = colors.grid;
1087
1193
  if (!verticalBorderless) ctx.fillRect(x + metric.width - 1, headerY, 1, headerHeight);
1088
1194
  ctx.fillRect(x, headerY + headerHeight - 1, metric.width, 1);
@@ -1091,7 +1197,7 @@ function paintGrid(options) {
1091
1197
  ctx.font = "600 13px Inter, ui-sans-serif, system-ui, sans-serif";
1092
1198
  const titleWidth = ctx.measureText(column.title).width;
1093
1199
  const visibleActions = getVisibleHeaderActions(column, metric.width, columnDraggable, titleWidth);
1094
- const actionWidth = (Number(visibleActions.drag) + Number(visibleActions.filter) + Number(visibleActions.sort)) * 18;
1200
+ const actionWidth = (Number(visibleActions.drag) + Number(visibleActions.filter) + Number(visibleActions.sort)) * HEADER_ACTION_SLOT_WIDTH;
1095
1201
  const contentWidth = Math.max(0, metric.width - actionWidth);
1096
1202
  const padding = 10;
1097
1203
  const clipWidth = contentWidth;
@@ -1160,7 +1266,7 @@ function paintGrid(options) {
1160
1266
  }
1161
1267
  const selectedRow = selection ? rows[selection.rowIndex] : void 0;
1162
1268
  const hasValidSelection = Boolean(selection && selectedRow && getRowKey(selectedRow, selection.rowIndex) === selection.rowKey);
1163
- if (selection && hasValidSelection && !selectionRange) {
1269
+ if (selection && hasValidSelection && !selectionRange && !rowDragPreview) {
1164
1270
  const metric = metrics[selection.columnIndex];
1165
1271
  if (metric) {
1166
1272
  const fixedSide = columns[selection.columnIndex].fixed;
@@ -1169,7 +1275,7 @@ function paintGrid(options) {
1169
1275
  const selectionWidth = getCellWidth(selection.columnIndex, span?.colSpan ?? 1);
1170
1276
  const selectionHeight = rowHeight * (span?.rowSpan ?? 1);
1171
1277
  const x = Math.round(getColumnX(selection.columnIndex));
1172
- const y = Math.round(bodyTop + selection.rowIndex * rowHeight - scrollTop);
1278
+ const y = Math.round(bodyTop + selection.rowIndex * rowHeight - scrollTop + getRowDragOffset(selection.rowIndex));
1173
1279
  const borderWidth = 2;
1174
1280
  const visibleLeft = fixedSide === "left" ? 0 : leftFixedWidth;
1175
1281
  const selectionAreaRight = fixedSide === "right" ? width : width - rightFixedWidth;
@@ -1279,6 +1385,40 @@ function flattenDataColumns(columns, inheritedFixed, insideGroup = false) {
1279
1385
  return flattenDataColumns(column.children, column.fixed ?? inheritedFixed, true);
1280
1386
  });
1281
1387
  }
1388
+ function reconcileColumns(current, incoming) {
1389
+ const incomingByKey = new Map(incoming.map((column) => [column.key, column]));
1390
+ const ordered = current.flatMap((column) => {
1391
+ const replacement = incomingByKey.get(column.key);
1392
+ if (!replacement) return [];
1393
+ incomingByKey.delete(column.key);
1394
+ return [{
1395
+ ...replacement,
1396
+ children: replacement.children ? reconcileColumns(column.children ?? [], replacement.children) : void 0
1397
+ }];
1398
+ });
1399
+ incoming.forEach((column) => {
1400
+ if (incomingByKey.has(column.key)) ordered.push(column);
1401
+ });
1402
+ return ordered;
1403
+ }
1404
+ function reorderColumns(columns, detail) {
1405
+ const reorder = (items) => {
1406
+ const sourceIndex = items.findIndex((column2) => column2.key === detail.sourceKey);
1407
+ const targetIndex = items.findIndex((column2) => column2.key === detail.targetKey);
1408
+ if (sourceIndex < 0 || targetIndex < 0 || sourceIndex === targetIndex) return items;
1409
+ const next = items.slice();
1410
+ const [column] = next.splice(sourceIndex, 1);
1411
+ let insertIndex = targetIndex + (detail.placement === "after" ? 1 : 0);
1412
+ if (sourceIndex < insertIndex) insertIndex -= 1;
1413
+ next.splice(insertIndex, 0, column);
1414
+ return next;
1415
+ };
1416
+ if (!detail.parentKey) return reorder(columns);
1417
+ return columns.map((column) => {
1418
+ if (column.key === detail.parentKey && column.children) return { ...column, children: reorder(column.children) };
1419
+ return column.children ? { ...column, children: reorderColumns(column.children, detail) } : column;
1420
+ });
1421
+ }
1282
1422
  function buildHeaderCells(sourceColumns, utilityColumnCount, depth) {
1283
1423
  const cells = [];
1284
1424
  let leafIndex = utilityColumnCount;
@@ -1383,7 +1523,7 @@ function resolveContextMenuSection(section, defaults) {
1383
1523
  return defaults;
1384
1524
  }
1385
1525
  function Table({
1386
- columns: sourceColumns,
1526
+ columns: columnProps,
1387
1527
  rows: sourceRows,
1388
1528
  rowKey,
1389
1529
  width = "100%",
@@ -1412,23 +1552,21 @@ function Table({
1412
1552
  rowSelection,
1413
1553
  selectedRowKeys,
1414
1554
  defaultSelectedRowKeys,
1415
- onSelectedRowKeysChange,
1555
+ onSelectedRowChange,
1416
1556
  columnSelection,
1417
1557
  selectedColumnKeys,
1418
1558
  defaultSelectedColumnKeys,
1419
- onSelectedColumnKeysChange,
1559
+ onSelectedColumnChange,
1420
1560
  columnDraggable = true,
1421
- columnResizable = false,
1422
- onColumnOrderChange,
1561
+ columnResizable = true,
1562
+ onColumnsReorder,
1423
1563
  onColumnResize,
1424
1564
  rowDraggable = false,
1425
- onRowOrderChange,
1565
+ onRowsReorder,
1426
1566
  onInsertRows,
1427
1567
  onDeleteRows,
1428
- sortState = null,
1429
- onSortStateChange,
1430
- filterValues = {},
1431
- onFilterValuesChange,
1568
+ onSortChange,
1569
+ onFilterChange,
1432
1570
  onCellChange,
1433
1571
  onCellContextMenu,
1434
1572
  contextMenu: contextMenuConfig = true,
@@ -1437,6 +1575,10 @@ function Table({
1437
1575
  style,
1438
1576
  ariaLabel = "Data grid"
1439
1577
  }) {
1578
+ const [sourceColumns, setSourceColumns] = (0, import_react5.useState)(columnProps);
1579
+ (0, import_react5.useEffect)(() => {
1580
+ setSourceColumns((current) => reconcileColumns(current, columnProps));
1581
+ }, [columnProps]);
1440
1582
  const rowHeight = layout?.rowHeight ?? rowHeightProp ?? 36;
1441
1583
  const baseHeaderHeight = layout?.headerHeight ?? headerHeightProp ?? 40;
1442
1584
  const headerDepth = (0, import_react5.useMemo)(() => Math.max(1, ...sourceColumns.map(getColumnDepth)), [sourceColumns]);
@@ -1517,7 +1659,6 @@ function Table({
1517
1659
  const frameRef = (0, import_react5.useRef)(null);
1518
1660
  const textFrameRef = (0, import_react5.useRef)(null);
1519
1661
  const toastTimerRef = (0, import_react5.useRef)(null);
1520
- const sortDebounceRef = (0, import_react5.useRef)(null);
1521
1662
  const headerTooltipTimerRef = (0, import_react5.useRef)(null);
1522
1663
  const cellTooltipTimerRef = (0, import_react5.useRef)(null);
1523
1664
  const pendingHeaderTooltipKeyRef = (0, import_react5.useRef)("");
@@ -1548,6 +1689,7 @@ function Table({
1548
1689
  const suppressClickRef = (0, import_react5.useRef)(false);
1549
1690
  const hasCompletedLoadRef = (0, import_react5.useRef)(!loading);
1550
1691
  const columnDragPreviewRef = (0, import_react5.useRef)(null);
1692
+ const validColumnDropRef = (0, import_react5.useRef)(null);
1551
1693
  const hoveredHeaderActionRef = (0, import_react5.useRef)(null);
1552
1694
  const hoveredRowIndexRef = (0, import_react5.useRef)(null);
1553
1695
  const [viewport, setViewport] = (0, import_react5.useState)({ width: 0, height: typeof height === "number" ? height : FALLBACK_HEIGHT });
@@ -1555,11 +1697,8 @@ function Table({
1555
1697
  const resolvedHeight = typeof height === "number" ? height : viewport.height || FALLBACK_HEIGHT;
1556
1698
  const [scrollPosition, setScrollPosition] = (0, import_react5.useState)({ left: 0, top: 0 });
1557
1699
  const scrollRef = (0, import_react5.useRef)({ left: 0, top: 0 });
1558
- const [selection, setSelection] = useControllableValue(selectedCell, defaultSelectedCell, onSelectedCellChange);
1559
- const [selectionRange, setSelectionRange] = (0, import_react5.useState)(null);
1560
- const rangeDragRef = (0, import_react5.useRef)(null);
1561
1700
  const [rowKeys, setRowKeys] = useControllableKeys(selectedRowKeys, defaultSelectedRowKeys, (keys) => {
1562
- if (!onSelectedRowKeysChange) return;
1701
+ if (!onSelectedRowChange) return;
1563
1702
  const missing = new Set(keys.filter((key) => !rowByKeyRef.current.has(key)));
1564
1703
  if (missing.size > 0) {
1565
1704
  for (let index = 0; index < rows.length && missing.size > 0; index += 1) {
@@ -1576,12 +1715,13 @@ function Table({
1576
1715
  const index = rowIndexByKeyRef.current.get(key);
1577
1716
  return row && index !== void 0 ? [{ row, index }] : [];
1578
1717
  });
1579
- onSelectedRowKeysChange(keys, entries.map(({ row }) => row), entries.map(({ index }) => index));
1718
+ onSelectedRowChange(keys, entries.map(({ row }) => row), entries.map(({ index }) => index));
1580
1719
  });
1581
- const [columnKeys, setColumnKeys] = useControllableKeys(selectedColumnKeys, defaultSelectedColumnKeys, onSelectedColumnKeysChange);
1720
+ const [columnKeys, setColumnKeys] = useControllableKeys(selectedColumnKeys, defaultSelectedColumnKeys, onSelectedColumnChange);
1582
1721
  const [editing, setEditing] = (0, import_react5.useState)(null);
1583
1722
  const [draft, setDraft] = (0, import_react5.useState)("");
1584
1723
  const [rowDragPreview, setRowDragPreview] = (0, import_react5.useState)(null);
1724
+ const [columnDropTarget, setColumnDropTarget] = (0, import_react5.useState)(null);
1585
1725
  (0, import_react5.useEffect)(() => {
1586
1726
  if (!loading) hasCompletedLoadRef.current = true;
1587
1727
  }, [loading]);
@@ -1650,6 +1790,8 @@ function Table({
1650
1790
  return () => observer.disconnect();
1651
1791
  }, [activeCustomHeaderLevels, measureCustomHeaderHeight, viewport.width]);
1652
1792
  const [filterEditor, setFilterEditor] = (0, import_react5.useState)(null);
1793
+ const [filterValues, setFilterValues] = (0, import_react5.useState)({});
1794
+ const [sortState, setSortState] = (0, import_react5.useState)(null);
1653
1795
  const [contextMenu, setContextMenu] = (0, import_react5.useState)(null);
1654
1796
  const [insertCounts, setInsertCounts] = (0, import_react5.useState)({ before: 1, after: 1 });
1655
1797
  const [confirmAction, setConfirmAction] = (0, import_react5.useState)(null);
@@ -1662,7 +1804,20 @@ function Table({
1662
1804
  const [hoveredHeaderAction, setHoveredHeaderAction] = (0, import_react5.useState)(null);
1663
1805
  const [visibleHeaderTooltip, setVisibleHeaderTooltip] = (0, import_react5.useState)(null);
1664
1806
  const [hoveredCellTooltip, setHoveredCellTooltip] = (0, import_react5.useState)(null);
1665
- const [displaySortState, setDisplaySortState] = (0, import_react5.useState)(sortState);
1807
+ const hideDragTooltips = (0, import_react5.useCallback)(() => {
1808
+ if (headerTooltipTimerRef.current !== null) {
1809
+ clearTimeout(headerTooltipTimerRef.current);
1810
+ headerTooltipTimerRef.current = null;
1811
+ }
1812
+ if (cellTooltipTimerRef.current !== null) {
1813
+ clearTimeout(cellTooltipTimerRef.current);
1814
+ cellTooltipTimerRef.current = null;
1815
+ }
1816
+ pendingHeaderTooltipKeyRef.current = "";
1817
+ pendingCellTooltipKeyRef.current = "";
1818
+ setVisibleHeaderTooltip(null);
1819
+ setHoveredCellTooltip(null);
1820
+ }, []);
1666
1821
  const [editedCellKeys, setEditedCellKeys] = (0, import_react5.useState)(() => /* @__PURE__ */ new Set());
1667
1822
  const [cellAnnotations, setCellAnnotations] = (0, import_react5.useState)(() => /* @__PURE__ */ new Map());
1668
1823
  const [annotationType, setAnnotationType] = (0, import_react5.useState)("corner");
@@ -1674,6 +1829,7 @@ function Table({
1674
1829
  const [pendingDeletedRowKeys, setPendingDeletedRowKeys] = (0, import_react5.useState)(() => /* @__PURE__ */ new Set());
1675
1830
  const [pendingInserts, setPendingInserts] = (0, import_react5.useState)(() => []);
1676
1831
  const [insertedRowKeys, setInsertedRowKeys] = (0, import_react5.useState)(() => /* @__PURE__ */ new Set());
1832
+ const [rowOrderKeys, setRowOrderKeys] = (0, import_react5.useState)(null);
1677
1833
  const settledPendingInsertIdsRef = (0, import_react5.useRef)(/* @__PURE__ */ new Set());
1678
1834
  const getDataRowKey = (0, import_react5.useCallback)((row, index) => {
1679
1835
  return typeof rowKey === "function" ? rowKey(row, index) : rowKey ? row[rowKey] : getDefaultRowKey(row, index);
@@ -1688,7 +1844,7 @@ function Table({
1688
1844
  }
1689
1845
  return sourceRows.findIndex((sourceRow, index) => getDataRowKey(sourceRow, index) === key);
1690
1846
  }, [getDataRowKey, sourceRows]);
1691
- const rows = (0, import_react5.useMemo)(() => {
1847
+ const orderedRows = (0, import_react5.useMemo)(() => {
1692
1848
  let next = sourceRows;
1693
1849
  if (pendingInserts.length > 0) {
1694
1850
  const activeInserts = pendingInserts.filter((insert) => {
@@ -1707,9 +1863,91 @@ function Table({
1707
1863
  insertedOffset += insert.rows.length;
1708
1864
  });
1709
1865
  }
1710
- if (pendingDeletedRowKeys.size === 0) return next;
1711
- return next.filter((row, index) => !pendingDeletedRowKeys.has(getDataRowKey(row, index)));
1712
- }, [getDataRowKey, pendingDeletedRowKeys, pendingInserts, sourceRows]);
1866
+ if (pendingDeletedRowKeys.size > 0) {
1867
+ next = next.filter((row, index) => !pendingDeletedRowKeys.has(getDataRowKey(row, index)));
1868
+ }
1869
+ if (!rowOrderKeys) return next;
1870
+ const rowsByKey = new Map(next.map((row, index) => [getDataRowKey(row, index), row]));
1871
+ const ordered = rowOrderKeys.flatMap((key) => {
1872
+ const row = rowsByKey.get(key);
1873
+ if (!row) return [];
1874
+ rowsByKey.delete(key);
1875
+ return [row];
1876
+ });
1877
+ next.forEach((row, index) => {
1878
+ const key = getDataRowKey(row, index);
1879
+ if (rowsByKey.has(key)) ordered.push(row);
1880
+ });
1881
+ return ordered;
1882
+ }, [getDataRowKey, pendingDeletedRowKeys, pendingInserts, rowOrderKeys, sourceRows]);
1883
+ const activeFilterValues = (0, import_react5.useMemo)(() => Object.fromEntries(
1884
+ Object.entries(filterValues).filter(([, value]) => value.trim() !== "")
1885
+ ), [filterValues]);
1886
+ const filteredRows = (0, import_react5.useMemo)(() => {
1887
+ if (Object.keys(activeFilterValues).length === 0) return orderedRows;
1888
+ if (onFilterChange) return onFilterChange(orderedRows, activeFilterValues);
1889
+ return orderedRows.filter((row, rowIndex) => Object.entries(activeFilterValues).every(([columnKey, query]) => {
1890
+ const column = columns.find((candidate) => candidate.key === columnKey);
1891
+ if (!column) return true;
1892
+ return getDisplayLabel(column, row, rowIndex).toLocaleLowerCase().includes(query.trim().toLocaleLowerCase());
1893
+ }));
1894
+ }, [activeFilterValues, columns, onFilterChange, orderedRows]);
1895
+ const rows = (0, import_react5.useMemo)(() => {
1896
+ if (!sortState) return filteredRows;
1897
+ if (onSortChange) return onSortChange(filteredRows, sortState);
1898
+ const column = columns.find((candidate) => candidate.key === sortState.columnKey);
1899
+ if (!column) return filteredRows;
1900
+ const direction = sortState.direction === "asc" ? 1 : -1;
1901
+ return filteredRows.slice().sort((left, right) => {
1902
+ const leftValue = column.dataIndex === void 0 ? void 0 : left[column.dataIndex];
1903
+ const rightValue = column.dataIndex === void 0 ? void 0 : right[column.dataIndex];
1904
+ const result = typeof leftValue === "number" && typeof rightValue === "number" ? leftValue - rightValue : getDisplayLabel(column, left, 0).localeCompare(getDisplayLabel(column, right, 0), language);
1905
+ return result * direction;
1906
+ });
1907
+ }, [columns, filteredRows, language, onSortChange, sortState]);
1908
+ const resolveSelectionTarget = (0, import_react5.useCallback)((target) => {
1909
+ if (target == null) return target;
1910
+ const columnIndex = columns.findIndex((column) => column.key === target.columnKey);
1911
+ const rowIndex = target.rowKey !== void 0 ? rows.findIndex((row2, index) => getDataRowKey(row2, index) === target.rowKey) : target.rowIndex ?? -1;
1912
+ const row = rows[rowIndex];
1913
+ if (columnIndex < 0 || !row) return null;
1914
+ return {
1915
+ rowIndex,
1916
+ columnIndex,
1917
+ rowKey: getDataRowKey(row, rowIndex),
1918
+ columnKey: target.columnKey
1919
+ };
1920
+ }, [columns, getDataRowKey, rows]);
1921
+ const resolvedSelectedCell = (0, import_react5.useMemo)(() => resolveSelectionTarget(selectedCell), [resolveSelectionTarget, selectedCell]);
1922
+ const resolvedDefaultSelectedCell = (0, import_react5.useMemo)(() => resolveSelectionTarget(defaultSelectedCell), [defaultSelectedCell, resolveSelectionTarget]);
1923
+ const handleSelectedCellChange = (0, import_react5.useCallback)((next) => {
1924
+ if (!onSelectedCellChange) return;
1925
+ if (!next) {
1926
+ onSelectedCellChange(null);
1927
+ return;
1928
+ }
1929
+ const row = rows[next.rowIndex];
1930
+ const column = columns[next.columnIndex];
1931
+ const value = row && column?.dataIndex !== void 0 ? row[column.dataIndex] : void 0;
1932
+ onSelectedCellChange({ ...next, value });
1933
+ }, [columns, onSelectedCellChange, rows]);
1934
+ const [selection, setSelection] = useControllableValue(resolvedSelectedCell, resolvedDefaultSelectedCell, handleSelectedCellChange);
1935
+ const [selectionRange, setSelectionRange] = (0, import_react5.useState)(null);
1936
+ const rangeDragRef = (0, import_react5.useRef)(null);
1937
+ const applyRowOrder = (0, import_react5.useCallback)((sourceIndex, targetIndex) => {
1938
+ if (sourceIndex === targetIndex || sourceIndex < 0 || targetIndex < 0 || sourceIndex >= rows.length || targetIndex >= rows.length) return;
1939
+ const nextEntries = rows.map((row, index) => ({ row, key: getDataRowKey(row, index) }));
1940
+ const [entry] = nextEntries.splice(sourceIndex, 1);
1941
+ nextEntries.splice(targetIndex, 0, entry);
1942
+ const nextRows = nextEntries.map(({ row }) => row);
1943
+ setRowOrderKeys(nextEntries.map(({ key }) => key));
1944
+ onRowsReorder?.(nextRows, { sourceIndex, targetIndex });
1945
+ }, [getDataRowKey, onRowsReorder, rows]);
1946
+ const applyColumnOrder = (0, import_react5.useCallback)((sourceIndex, targetIndex, detail) => {
1947
+ const nextColumns = reorderColumns(sourceColumns, detail);
1948
+ setSourceColumns(nextColumns);
1949
+ onColumnsReorder?.(nextColumns, { sourceIndex, targetIndex, ...detail });
1950
+ }, [onColumnsReorder, sourceColumns]);
1713
1951
  (0, import_react5.useEffect)(() => {
1714
1952
  if (insertBusy || settledPendingInsertIdsRef.current.size === 0) return;
1715
1953
  setPendingInserts((current) => {
@@ -1951,19 +2189,22 @@ function Table({
1951
2189
  grid: readThemeColor(themeStyles, "--rvg-color-grid", "#e3e6e8"),
1952
2190
  text: readThemeColor(themeStyles, "--rvg-color-text", "#202124"),
1953
2191
  muted: readThemeColor(themeStyles, "--rvg-color-muted", "#5f6368"),
2192
+ icon: readThemeColor(themeStyles, "--rvg-color-icon", "#9aa0a6"),
2193
+ borderStrong: readThemeColor(themeStyles, "--rvg-color-border-strong", "#b8bec4"),
1954
2194
  selection: readThemeColor(themeStyles, "--rvg-color-primary", "#1677ff"),
1955
2195
  selectionFill: readThemeColor(themeStyles, "--rvg-color-selection-fill", "#edf4ff"),
1956
2196
  axisSelectionFill: readThemeColor(themeStyles, "--rvg-color-axis-selection-fill", "#e8f2ff"),
1957
2197
  rowHoverFill: readThemeColor(themeStyles, "--rvg-color-row-hover-fill", "#f6f9fc"),
1958
2198
  editedFill: editedCellHighlightColor ?? readThemeColor(themeStyles, "--rvg-color-edited-fill", "#fff1b8"),
1959
2199
  insertedFill: insertedRowHighlightColor ?? readThemeColor(themeStyles, "--rvg-color-inserted-fill", "#c8ead4"),
2200
+ columnDropTargetFill: readThemeColor(themeStyles, "--rvg-color-column-drop-target-fill", "#eeeeee"),
1960
2201
  stripe: stripedColor ?? readThemeColor(themeStyles, "--rvg-color-stripe", "#fafbfc")
1961
2202
  };
1962
2203
  const scroll = scrollRef.current;
1963
2204
  const rowScrollTop = fixedHeader ? scroll.top : Math.max(0, scroll.top - bodyTop);
1964
2205
  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 };
1965
- 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 });
1966
- }, [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]);
2206
+ 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, rowSelectionMode, selectedColumnKeys: selectedColumnKeySet, cellSpans: cellSpanLookup.covered, maxRowSpan: cellSpanLookup.maxRowSpan, highlightEditedCells: highlightsEditedCells, highlightInsertedRows: highlightsInsertedRows, insertedRowKeys: insertedRowKeySet, editedCellKeys, cellAnnotations, getRowKey, rowDragPreview, columnDropTarget, colors: themeColors });
2207
+ }, [bodyTop, bodyViewportHeight, bottomSummaryHeight, cellAnnotations, cellSpanLookup, columnDraggable, columnDropTarget, columns, editedCellHighlightColor, editedCellKeys, editing, renderHeight, filterValues, fixedHeader, getRowKey, hasStripedRows, hasVerticalBorders, headerDepth, headerHeight, headerLeafHeight, headerLeafTop, highlightsEditedCells, highlightsInsertedRows, insertedRowHighlightColor, insertedRowKeySet, isVirtualized, metrics, rowDragPreview, rowHeight, rowSelectionMode, rows, selectedColumnKeySet, selectedRowKeySet, selection, selectionRange, sortState, stripedColor, viewport.width, virtualOverscan]);
1967
2208
  const scheduleDraw = (0, import_react5.useCallback)(() => {
1968
2209
  if (frameRef.current === null) frameRef.current = requestAnimationFrame(draw);
1969
2210
  }, [draw]);
@@ -2145,11 +2386,7 @@ function Table({
2145
2386
  }
2146
2387
  };
2147
2388
  }, [scheduleDraw]);
2148
- (0, import_react5.useEffect)(() => {
2149
- setDisplaySortState(sortState);
2150
- }, [sortState]);
2151
2389
  (0, import_react5.useEffect)(() => () => {
2152
- if (sortDebounceRef.current !== null) clearTimeout(sortDebounceRef.current);
2153
2390
  if (textFrameRef.current !== null) cancelAnimationFrame(textFrameRef.current);
2154
2391
  if (toastTimerRef.current !== null) clearTimeout(toastTimerRef.current);
2155
2392
  if (verticalScrollbarVisibilityTimerRef.current !== null) clearTimeout(verticalScrollbarVisibilityTimerRef.current);
@@ -2524,12 +2761,12 @@ function Table({
2524
2761
  const localX = clientX - canvasRef.current.getBoundingClientRect().left;
2525
2762
  const columnRight = getDisplayedColumnLeft(columnIndex) + metrics[columnIndex].width;
2526
2763
  const visibleActions = getVisibleHeaderActions(column, metrics[columnIndex].width, columnDraggable, measureHeaderTitleWidth(columnIndex));
2527
- let right = columnRight - (visibleActions.drag ? 18 : 4);
2764
+ let right = columnRight - (visibleActions.drag ? HEADER_ACTION_SLOT_WIDTH : 2);
2528
2765
  if (visibleActions.sort) {
2529
- if (localX >= right - 18 && localX < right) return "sort";
2530
- right -= 18;
2766
+ if (localX >= right - HEADER_ACTION_SLOT_WIDTH && localX < right) return "sort";
2767
+ right -= HEADER_ACTION_SLOT_WIDTH;
2531
2768
  }
2532
- if (visibleActions.filter && localX >= right - 18 && localX < right) return "filter";
2769
+ if (visibleActions.filter && localX >= right - HEADER_ACTION_SLOT_WIDTH && localX < right) return "filter";
2533
2770
  return null;
2534
2771
  }, [columnDraggable, columns, getDisplayedColumnLeft, measureHeaderTitleWidth, metrics]);
2535
2772
  const isHeaderTitleTruncated = (0, import_react5.useCallback)((columnIndex) => {
@@ -2539,7 +2776,7 @@ function Table({
2539
2776
  const metric = metrics[columnIndex];
2540
2777
  const titleWidth = measureHeaderTitleWidth(columnIndex);
2541
2778
  const visibleActions = getVisibleHeaderActions(column, metric.width, columnDraggable, titleWidth);
2542
- const actionWidth = (Number(visibleActions.drag) + Number(visibleActions.filter) + Number(visibleActions.sort)) * 18;
2779
+ const actionWidth = (Number(visibleActions.drag) + Number(visibleActions.filter) + Number(visibleActions.sort)) * HEADER_ACTION_SLOT_WIDTH;
2543
2780
  return getHeaderTitleRequiredWidth(column, titleWidth) > Math.max(0, metric.width - actionWidth);
2544
2781
  }, [columnDraggable, columns, measureHeaderTitleWidth, metrics]);
2545
2782
  const locateHeaderDragHandle = (0, import_react5.useCallback)((clientX, columnIndex) => {
@@ -2695,6 +2932,7 @@ function Table({
2695
2932
  }
2696
2933
  const sourceIndex = headerDragCell.startIndex;
2697
2934
  columnDragPreviewRef.current = { sourceIndex, targetIndex: sourceIndex };
2935
+ validColumnDropRef.current = null;
2698
2936
  return {
2699
2937
  type: "table-column",
2700
2938
  sourceIndex,
@@ -2705,13 +2943,26 @@ function Table({
2705
2943
  fixed: columns[headerDragCell.startIndex]?.fixed
2706
2944
  };
2707
2945
  },
2708
- onGenerateDragPreview: ({ nativeSetDragImage, source }) => {
2946
+ onGenerateDragPreview: ({ nativeSetDragImage, source, location }) => {
2709
2947
  const sourceIndex = Number(source.data.sourceIndex);
2948
+ const canvasRect = canvas.getBoundingClientRect();
2949
+ const input = location.current.input;
2950
+ const previewOffset = source.data.type === "table-row" ? {
2951
+ x: Math.max(0, Math.min(canvasRect.width, input.clientX - canvasRect.left)) + 2,
2952
+ y: Math.max(0, Math.min(rowHeight, input.clientY - canvasRect.top - bodyTop - sourceIndex * rowHeight + scrollRef.current.top)) + 2
2953
+ } : {
2954
+ x: Math.max(0, Math.min(metrics[sourceIndex].width, input.clientX - canvasRect.left - getDisplayedColumnLeft(sourceIndex))),
2955
+ y: Math.max(0, Math.min(canvasRect.height, input.clientY - canvasRect.top))
2956
+ };
2710
2957
  (0, import_set_custom_native_drag_preview.setCustomNativeDragPreview)({
2711
2958
  nativeSetDragImage,
2959
+ getOffset: () => previewOffset,
2712
2960
  render: ({ container }) => {
2713
- const canvasRect = canvas.getBoundingClientRect();
2714
2961
  if (source.data.type === "table-row") {
2962
+ const frame = document.createElement("div");
2963
+ frame.className = "rvg-row-drag-preview-frame";
2964
+ frame.style.width = `${canvasRect.width + 4}px`;
2965
+ frame.style.height = `${rowHeight + 4}px`;
2715
2966
  const preview2 = document.createElement("div");
2716
2967
  preview2.className = "rvg-row-drag-preview";
2717
2968
  preview2.style.width = `${canvasRect.width}px`;
@@ -2737,10 +2988,14 @@ function Table({
2737
2988
  } else {
2738
2989
  cell.textContent = getCellLabel(sourceIndex, columnIndex);
2739
2990
  }
2991
+ if (selection?.rowKey === rowKey2 && selection.columnKey === column2.key) {
2992
+ cell.classList.add("is-selected");
2993
+ }
2740
2994
  preview2.appendChild(cell);
2741
2995
  });
2742
- container.appendChild(preview2);
2743
- return () => preview2.remove();
2996
+ frame.appendChild(preview2);
2997
+ container.appendChild(frame);
2998
+ return () => frame.remove();
2744
2999
  }
2745
3000
  const preview = document.createElement("div");
2746
3001
  preview.className = "rvg-column-drag-preview";
@@ -2754,6 +3009,29 @@ function Table({
2754
3009
  header.style.height = `${headerHeight}px`;
2755
3010
  header.style.justifyContent = column.align === "right" ? "flex-end" : column.align === "center" ? "center" : "flex-start";
2756
3011
  header.textContent = column.title;
3012
+ const visibleActions = getVisibleHeaderActions(column, columnWidth, columnDraggable, measureHeaderTitleWidth(sourceIndex));
3013
+ let actionRight = 2;
3014
+ if (visibleActions.drag) {
3015
+ const icon = createHeaderDragHandleSvg();
3016
+ icon.classList.add("rvg-preview-header-icon");
3017
+ icon.style.right = `${actionRight}px`;
3018
+ header.appendChild(icon);
3019
+ actionRight += HEADER_ACTION_SLOT_WIDTH;
3020
+ }
3021
+ if (visibleActions.sort) {
3022
+ const direction = sortState?.columnKey === column.key ? sortState.direction : null;
3023
+ const icon = createHeaderSortSvg(direction);
3024
+ icon.classList.add("rvg-preview-header-icon");
3025
+ icon.style.right = `${actionRight}px`;
3026
+ header.appendChild(icon);
3027
+ actionRight += HEADER_ACTION_SLOT_WIDTH;
3028
+ }
3029
+ if (visibleActions.filter) {
3030
+ const icon = createHeaderSearchSvg(Boolean(filterValues[column.key]));
3031
+ icon.classList.add("rvg-preview-header-icon", "rvg-preview-header-search-icon");
3032
+ icon.style.right = `${actionRight}px`;
3033
+ header.appendChild(icon);
3034
+ }
2757
3035
  preview.appendChild(header);
2758
3036
  for (let rowIndex = domRange.rowStart; rowIndex < domRange.rowEnd; rowIndex += 1) {
2759
3037
  const row = rows[rowIndex];
@@ -2782,6 +3060,8 @@ function Table({
2782
3060
  },
2783
3061
  onDragStart: ({ source }) => {
2784
3062
  suppressClickRef.current = true;
3063
+ hideDragTooltips();
3064
+ document.documentElement.classList.add("rvg-is-dragging");
2785
3065
  canvas.style.cursor = "move";
2786
3066
  if (source.data.type === "table-row") {
2787
3067
  const sourceIndex = Number(source.data.sourceIndex);
@@ -2807,15 +3087,46 @@ function Table({
2807
3087
  }
2808
3088
  const targetIndex = Number(target?.targetIndex);
2809
3089
  if (!Number.isInteger(targetIndex) || target?.headerKind !== source.data.headerKind || target?.parentKey !== source.data.parentKey || target?.fixed !== source.data.fixed) {
3090
+ validColumnDropRef.current = null;
3091
+ setColumnDropTarget(null);
2810
3092
  setDragGuide(null);
2811
3093
  return;
2812
3094
  }
2813
3095
  const edge = target?.columnEdge;
2814
3096
  const destinationIndex = getColumnDropIndex(sourceIndex, targetIndex, edge);
2815
3097
  columnDragPreviewRef.current = { sourceIndex, targetIndex: destinationIndex };
3098
+ if (destinationIndex === sourceIndex) {
3099
+ validColumnDropRef.current = null;
3100
+ setColumnDropTarget(null);
3101
+ setDragGuide(null);
3102
+ return;
3103
+ }
3104
+ validColumnDropRef.current = {
3105
+ sourceIndex,
3106
+ targetIndex: destinationIndex,
3107
+ rawTargetIndex: targetIndex,
3108
+ targetKey: String(target.targetKey),
3109
+ headerKind: source.data.headerKind === "group" ? "group" : "column",
3110
+ parentKey: typeof source.data.parentKey === "string" ? source.data.parentKey : void 0,
3111
+ fixed: source.data.fixed === "left" || source.data.fixed === "right" ? source.data.fixed : void 0,
3112
+ columnEdge: edge === "right" ? "right" : "left"
3113
+ };
3114
+ const siblingCells = headerCells.filter((cell) => cell.level === Number(source.data.headerLevel) && (cell.leaf ? "column" : "group") === source.data.headerKind && cell.parentKey === source.data.parentKey && columns[cell.startIndex]?.fixed === source.data.fixed);
3115
+ const targetEndIndex = Number.isInteger(target?.targetEndIndex) ? Number(target.targetEndIndex) : targetIndex;
3116
+ const hoveredTargetCell = siblingCells.find((cell) => cell.startIndex === targetIndex && cell.endIndex === targetEndIndex);
3117
+ if (!hoveredTargetCell) {
3118
+ validColumnDropRef.current = null;
3119
+ setColumnDropTarget(null);
3120
+ setDragGuide(null);
3121
+ return;
3122
+ }
3123
+ const highlightedCell = edge === "right" ? hoveredTargetCell : siblingCells.filter((cell) => cell.endIndex < hoveredTargetCell.startIndex).sort((left, right) => right.endIndex - left.endIndex)[0] ?? hoveredTargetCell;
3124
+ setColumnDropTarget({ startIndex: highlightedCell.startIndex, endIndex: highlightedCell.endIndex });
2816
3125
  const targetLeft = Number(target?.targetLeft);
2817
3126
  const targetWidth = Number(target?.targetWidth);
2818
3127
  if (!Number.isFinite(targetLeft) || !Number.isFinite(targetWidth)) {
3128
+ validColumnDropRef.current = null;
3129
+ setColumnDropTarget(null);
2819
3130
  setDragGuide(null);
2820
3131
  return;
2821
3132
  }
@@ -2823,18 +3134,19 @@ function Table({
2823
3134
  setDragGuide(edge === "right" ? targetLeft + targetWidth : targetLeft, guideTop);
2824
3135
  },
2825
3136
  onDrop: ({ source, location }) => {
3137
+ document.documentElement.classList.remove("rvg-is-dragging");
2826
3138
  const target = location.current.dropTargets[0]?.data;
2827
3139
  const sourceIndex = Number(source.data.sourceIndex);
2828
3140
  if (source.data.type === "table-row") {
2829
- const rawTargetIndex2 = Number(target?.targetIndex);
2830
- let targetIndex2 = rawTargetIndex2 + (target?.rowEdge === "bottom" ? 1 : 0);
2831
- if (sourceIndex < targetIndex2) targetIndex2 -= 1;
2832
- targetIndex2 = Math.max(0, Math.min(rows.length - 1, targetIndex2));
3141
+ const rawTargetIndex = Number(target?.targetIndex);
3142
+ let targetIndex = rawTargetIndex + (target?.rowEdge === "bottom" ? 1 : 0);
3143
+ if (sourceIndex < targetIndex) targetIndex -= 1;
3144
+ targetIndex = Math.max(0, Math.min(rows.length - 1, targetIndex));
2833
3145
  scrollerRef.current?.classList.add("rvg-row-drop-settling");
2834
3146
  setRowDragPreview(null);
2835
3147
  setRowDragGuide(null);
2836
3148
  canvas.style.cursor = "default";
2837
- if (Number.isInteger(rawTargetIndex2) && sourceIndex !== targetIndex2) onRowOrderChange?.(sourceIndex, targetIndex2);
3149
+ if (Number.isInteger(rawTargetIndex) && sourceIndex !== targetIndex) applyRowOrder(sourceIndex, targetIndex);
2838
3150
  requestAnimationFrame(() => {
2839
3151
  requestAnimationFrame(() => scrollerRef.current?.classList.remove("rvg-row-drop-settling"));
2840
3152
  });
@@ -2843,21 +3155,22 @@ function Table({
2843
3155
  }, 0);
2844
3156
  return;
2845
3157
  }
2846
- const rawTargetIndex = Number(target?.targetIndex);
2847
- const targetIndex = columnDragPreviewRef.current?.targetIndex ?? (Number.isInteger(rawTargetIndex) ? getColumnDropIndex(sourceIndex, rawTargetIndex, target?.columnEdge) : rawTargetIndex);
3158
+ const validDrop = validColumnDropRef.current;
2848
3159
  canvas.style.cursor = "default";
2849
3160
  setDragGuide(null);
3161
+ setColumnDropTarget(null);
2850
3162
  columnDragPreviewRef.current = null;
2851
- if (Number.isInteger(rawTargetIndex) && Number.isInteger(targetIndex) && sourceIndex !== targetIndex && target?.headerKind === source.data.headerKind && target?.parentKey === source.data.parentKey && target?.fixed === source.data.fixed) {
2852
- onColumnOrderChange?.(
2853
- sourceIndex - utilityColumnCount,
2854
- targetIndex - utilityColumnCount,
3163
+ validColumnDropRef.current = null;
3164
+ if (validDrop) {
3165
+ applyColumnOrder(
3166
+ validDrop.sourceIndex - utilityColumnCount,
3167
+ validDrop.targetIndex - utilityColumnCount,
2855
3168
  {
2856
- type: source.data.headerKind === "group" ? "group" : "column",
3169
+ type: validDrop.headerKind,
2857
3170
  sourceKey: String(source.data.sourceKey),
2858
- targetKey: String(target.targetKey),
2859
- parentKey: typeof source.data.parentKey === "string" ? source.data.parentKey : void 0,
2860
- placement: getColumnDropPlacement(sourceIndex, rawTargetIndex, target.columnEdge)
3171
+ targetKey: validDrop.targetKey,
3172
+ parentKey: validDrop.parentKey,
3173
+ placement: getColumnDropPlacement(validDrop.sourceIndex, validDrop.rawTargetIndex, validDrop.columnEdge)
2861
3174
  }
2862
3175
  );
2863
3176
  }
@@ -2869,6 +3182,7 @@ function Table({
2869
3182
  const cleanupDrop = (0, import_adapter.dropTargetForElements)({
2870
3183
  element: canvas,
2871
3184
  canDrop: ({ source }) => source.data.type === "table-column" || source.data.type === "table-row",
3185
+ getDropEffect: () => "move",
2872
3186
  getData: ({ input, element, source }) => {
2873
3187
  if (source.data.type === "table-row") {
2874
3188
  const targetIndex2 = locateRow(input.clientY);
@@ -2877,7 +3191,8 @@ function Table({
2877
3191
  const rowEdge = input.clientY - canvas.getBoundingClientRect().top < rowTop + rowHeight / 2 ? "top" : "bottom";
2878
3192
  return (0, import_closest_edge.attachClosestEdge)({ targetIndex: targetIndex2, rowEdge }, { element, input, allowedEdges: ["top", "bottom"] });
2879
3193
  }
2880
- const targetCell = locateHeaderCell(input.clientX, input.clientY);
3194
+ const hoveredColumnIndex = locateColumn(input.clientX);
3195
+ const targetCell = locateHeaderCell(input.clientX, input.clientY) ?? headerCells.find((cell) => hoveredColumnIndex >= cell.startIndex && hoveredColumnIndex <= cell.endIndex && cell.level === Number(source.data.headerLevel) && (cell.leaf ? "column" : "group") === source.data.headerKind && cell.parentKey === source.data.parentKey);
2881
3196
  if (!targetCell) return { targetIndex: -1 };
2882
3197
  let targetIndex = targetCell.startIndex;
2883
3198
  const targetFixed = columns[targetCell.startIndex]?.fixed;
@@ -2894,6 +3209,7 @@ function Table({
2894
3209
  const columnEdge = input.clientX - canvas.getBoundingClientRect().left < left + targetWidth / 2 ? "left" : "right";
2895
3210
  return (0, import_closest_edge.attachClosestEdge)({
2896
3211
  targetIndex,
3212
+ targetEndIndex: targetCell.endIndex,
2897
3213
  targetKey: targetCell.key,
2898
3214
  parentKey: targetCell.parentKey,
2899
3215
  headerKind,
@@ -2905,10 +3221,11 @@ function Table({
2905
3221
  }
2906
3222
  });
2907
3223
  return () => {
3224
+ document.documentElement.classList.remove("rvg-is-dragging");
2908
3225
  cleanupDrag();
2909
3226
  cleanupDrop();
2910
3227
  };
2911
- }, [bodyTop, columnDraggable, columns, fixedHeader, getCellLabel, getDisplayedColumnLeft, getHeaderCellWidth, getRowKey, headerHeight, locateColumn, locateHeaderCell, locateHeaderCellDragHandle, locateHeaderResizeCell, locateRow, metrics, onColumnOrderChange, onRowOrderChange, rowDraggable, rowHeight, rows, selectedRowKeySet, setDragGuide, setRowDragGuide, utilityColumnCount]);
3228
+ }, [applyColumnOrder, applyRowOrder, bodyTop, columnDraggable, columns, filterValues, fixedHeader, getCellLabel, getDisplayedColumnLeft, getHeaderCellWidth, getRowKey, headerCells, headerHeight, hideDragTooltips, locateColumn, locateHeaderCell, locateHeaderCellDragHandle, locateHeaderResizeCell, locateRow, measureHeaderTitleWidth, metrics, rowDraggable, rowHeight, rows, selectedRowKeySet, selection, setDragGuide, setRowDragGuide, sortState, utilityColumnCount]);
2912
3229
  const locateCell = (0, import_react5.useCallback)((clientX, clientY) => {
2913
3230
  const canvas = canvasRef.current;
2914
3231
  if (!canvas) return null;
@@ -3315,19 +3632,14 @@ function Table({
3315
3632
  setResizeGuideTop(0);
3316
3633
  }, []);
3317
3634
  const queueSortStateChange = (0, import_react5.useCallback)((next) => {
3318
- setDisplaySortState(next);
3319
- if (sortDebounceRef.current !== null) clearTimeout(sortDebounceRef.current);
3320
- sortDebounceRef.current = setTimeout(() => {
3321
- sortDebounceRef.current = null;
3322
- onSortStateChange?.(next);
3323
- }, 200);
3324
- }, [onSortStateChange]);
3635
+ setSortState(next);
3636
+ }, []);
3325
3637
  const renderHeaderIcons = (columnIndex, left) => {
3326
3638
  const column = columns[columnIndex];
3327
3639
  if (column.rowSelection || column.rowDragHandle || column.rowNumber) return null;
3328
3640
  const top = headerLeafTop + (headerLeafHeight - 14) / 2;
3329
3641
  const visibleActions = getVisibleHeaderActions(column, metrics[columnIndex].width, columnDraggable, measureHeaderTitleWidth(columnIndex));
3330
- let right = left + metrics[columnIndex].width - (visibleActions.drag ? 18 : 4);
3642
+ let right = left + metrics[columnIndex].width - (visibleActions.drag ? HEADER_ACTION_SLOT_WIDTH : 2);
3331
3643
  const icons = [];
3332
3644
  if (visibleActions.drag) {
3333
3645
  const hovered = hoveredHeaderAction?.columnIndex === columnIndex && hoveredHeaderAction.action === "drag";
@@ -3337,11 +3649,11 @@ function Table({
3337
3649
  }
3338
3650
  if (visibleActions.sort) {
3339
3651
  const hovered = hoveredHeaderAction?.columnIndex === columnIndex && hoveredHeaderAction.action === "sort";
3340
- const direction = displaySortState?.columnKey === column.key ? displaySortState.direction : null;
3652
+ const direction = sortState?.columnKey === column.key ? sortState.direction : null;
3341
3653
  icons.push(
3342
3654
  /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(HeaderSortIcon, { className: `rvg-header-icon${hovered ? " is-hovered" : ""}`, style: { left: right - 16, top }, direction }, "sort")
3343
3655
  );
3344
- right -= 18;
3656
+ right -= HEADER_ACTION_SLOT_WIDTH;
3345
3657
  }
3346
3658
  if (visibleActions.filter) {
3347
3659
  const hovered = hoveredHeaderAction?.columnIndex === columnIndex && hoveredHeaderAction.action === "filter";
@@ -3368,7 +3680,7 @@ function Table({
3368
3680
  if (context) context.font = "600 13px Inter, ui-sans-serif, system-ui, sans-serif";
3369
3681
  const titleWidth = context?.measureText(column.title).width ?? column.title.length * 13;
3370
3682
  const visibleActions = cell?.leaf || !cell ? getVisibleHeaderActions(column, cellWidth, columnDraggable, titleWidth) : null;
3371
- const actionWidth = visibleActions ? (Number(visibleActions.drag) + Number(visibleActions.filter) + Number(visibleActions.sort)) * 18 : columnDraggable ? 18 : 0;
3683
+ const actionWidth = visibleActions ? (Number(visibleActions.drag) + Number(visibleActions.filter) + Number(visibleActions.sort)) * HEADER_ACTION_SLOT_WIDTH : columnDraggable ? HEADER_ACTION_SLOT_WIDTH : 0;
3372
3684
  const contentWidth2 = Math.max(0, cellWidth - actionWidth);
3373
3685
  const maxTextWidth = Math.max(0, contentWidth2 - 20);
3374
3686
  const textWidth = Math.min(titleWidth, maxTextWidth);
@@ -3387,7 +3699,7 @@ function Table({
3387
3699
  anchorLeft = anchorX;
3388
3700
  label = labels.dragColumn;
3389
3701
  } else if (action === "sort") {
3390
- const direction = displaySortState?.columnKey === column.key ? displaySortState.direction : null;
3702
+ const direction = sortState?.columnKey === column.key ? sortState.direction : null;
3391
3703
  label = direction === "asc" ? labels.sortAsc : direction === "desc" ? labels.sortDesc : labels.sortBoth;
3392
3704
  anchorX = cellLeft + cellWidth - 22;
3393
3705
  anchorLeft = anchorX;
@@ -3407,7 +3719,7 @@ function Table({
3407
3719
  label,
3408
3720
  placement
3409
3721
  };
3410
- }, [baseHeaderHeight, columnDraggable, columns, displaySortState, filterValues, fixedHeader, getDisplayedColumnLeft, getHeaderCellLeft, getHeaderCellWidth, headerDepth, headerRowHeights, headerRowOffsets, labels, metrics, scrollPosition.top, viewport.width]);
3722
+ }, [baseHeaderHeight, columnDraggable, columns, filterValues, fixedHeader, getDisplayedColumnLeft, getHeaderCellLeft, getHeaderCellWidth, headerDepth, headerRowHeights, headerRowOffsets, labels, metrics, scrollPosition.top, sortState, viewport.width]);
3411
3723
  const isPointerOnHeaderTitle = (0, import_react5.useCallback)((clientX, clientY, cell) => {
3412
3724
  const canvas = canvasRef.current;
3413
3725
  if (!canvas) return false;
@@ -3422,7 +3734,7 @@ function Table({
3422
3734
  if (context) context.font = "600 13px Inter, ui-sans-serif, system-ui, sans-serif";
3423
3735
  const titleWidth = context?.measureText(column.title).width ?? column.title.length * 13;
3424
3736
  const visibleActions = cell.leaf ? getVisibleHeaderActions(column, cellWidth, columnDraggable, titleWidth) : null;
3425
- const actionWidth = visibleActions ? (Number(visibleActions.drag) + Number(visibleActions.filter) + Number(visibleActions.sort)) * 18 : columnDraggable ? 18 : 0;
3737
+ const actionWidth = visibleActions ? (Number(visibleActions.drag) + Number(visibleActions.filter) + Number(visibleActions.sort)) * HEADER_ACTION_SLOT_WIDTH : columnDraggable ? HEADER_ACTION_SLOT_WIDTH : 0;
3426
3738
  const contentWidth2 = Math.max(0, cellWidth - actionWidth);
3427
3739
  const textWidth = Math.min(titleWidth, Math.max(0, contentWidth2 - 20));
3428
3740
  const textLeft = column.align === "right" ? cellLeft + contentWidth2 - 10 - textWidth : column.align === "center" ? cellLeft + contentWidth2 / 2 - textWidth / 2 : cellLeft + 10;
@@ -3662,7 +3974,9 @@ function Table({
3662
3974
  });
3663
3975
  setPendingInserts((current) => [...current, pending]);
3664
3976
  try {
3665
- await Promise.resolve(onInsertRows({ rowIndex: sourceRowIndex >= 0 ? sourceRowIndex : cell.rowIndex, row, position, count, insertedRows }));
3977
+ const nextRows = sourceRows.slice();
3978
+ nextRows.splice(insertIndex, 0, ...insertedRows);
3979
+ await Promise.resolve(onInsertRows(nextRows, insertedRows));
3666
3980
  await waitForPaint();
3667
3981
  setInsertBusy(false);
3668
3982
  } catch (error) {
@@ -3689,28 +4003,10 @@ function Table({
3689
4003
  const currentScrollTop = scrollRef.current.top;
3690
4004
  const top = (fixedHeader ? 0 : bodyTop) + rowIndex * rowHeight - currentScrollTop;
3691
4005
  if (column.rowSelection) {
3692
- const rowKey2 = getRowKey(rows[rowIndex], rowIndex);
3693
- return /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
3694
- "div",
3695
- {
3696
- className: "rvg-row-selection-icon",
3697
- style: { left, top, width: cellWidth, height: cellHeight, opacity: isDraggedSource ? 0 : 1, transform: `translateY(${dragOffset}px)` },
3698
- children: /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(SelectionIcon, { checked: selectedRowKeySet.has(rowKey2), radio: rowSelectionMode === "single" })
3699
- },
3700
- `selection:${String(rowKey2)}`
3701
- );
4006
+ return null;
3702
4007
  }
3703
4008
  if (column.rowDragHandle) {
3704
- return /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
3705
- "div",
3706
- {
3707
- className: "rvg-row-drag-icon",
3708
- style: { left, top, width: cellWidth, height: cellHeight, opacity: isDraggedSource ? 0 : 1, transform: `translateY(${dragOffset}px)` },
3709
- "aria-hidden": "true",
3710
- children: /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(RowDragHandleIcon, {})
3711
- },
3712
- `drag:${String(getRowKey(rows[rowIndex], rowIndex))}`
3713
- );
4009
+ return null;
3714
4010
  }
3715
4011
  if (column.renderCell && !(editing?.rowIndex === rowIndex && editing.columnIndex === columnIndex)) {
3716
4012
  const row = rows[rowIndex];
@@ -3737,6 +4033,7 @@ function Table({
3737
4033
  };
3738
4034
  const renderHeaderTitle = (columnIndex, left) => {
3739
4035
  const column = columns[columnIndex];
4036
+ const isDropTarget = Boolean(columnDropTarget && columnIndex >= columnDropTarget.startIndex && columnIndex <= columnDropTarget.endIndex);
3740
4037
  const currentScrollTop = scrollRef.current.top;
3741
4038
  const headerTop = fixedHeader ? 0 : -currentScrollTop;
3742
4039
  const leafTop = headerTop + headerLeafTop;
@@ -3744,7 +4041,7 @@ function Table({
3744
4041
  return /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
3745
4042
  "div",
3746
4043
  {
3747
- className: "rvg-header-title",
4044
+ className: `rvg-header-title${isDropTarget ? " is-drop-target" : ""}`,
3748
4045
  style: { left, top: headerTop, width: metrics[columnIndex].width, height: headerHeight, justifyContent: "center", padding: 0, textAlign: "center" },
3749
4046
  children: /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("span", { children: column.title })
3750
4047
  },
@@ -3767,11 +4064,11 @@ function Table({
3767
4064
  }
3768
4065
  if (column.renderHeader) {
3769
4066
  const visibleActions = getVisibleHeaderActions(column, metrics[columnIndex].width, columnDraggable, measureHeaderTitleWidth(columnIndex));
3770
- const actionWidth = (Number(visibleActions.drag) + Number(visibleActions.filter) + Number(visibleActions.sort)) * 18;
4067
+ const actionWidth = (Number(visibleActions.drag) + Number(visibleActions.filter) + Number(visibleActions.sort)) * HEADER_ACTION_SLOT_WIDTH;
3771
4068
  return /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
3772
4069
  "div",
3773
4070
  {
3774
- className: "rvg-header-title is-custom",
4071
+ className: `rvg-header-title is-custom${isDropTarget ? " is-drop-target" : ""}`,
3775
4072
  "data-level": headerDepth - 1,
3776
4073
  "data-measure-key": column.key,
3777
4074
  style: {
@@ -3812,11 +4109,12 @@ function Table({
3812
4109
  const left = fixedSide === "left" ? leftFixedOffsets.get(cell.startIndex) ?? 0 : fixedSide === "right" ? viewport.width - (rightFixedOffsets.get(cell.startIndex) ?? 0) - startMetric.width : getScrollableColumnLeft(cell.startIndex);
3813
4110
  const width2 = cell.leaf ? startMetric.width : getCellDisplayWidth(cell.startIndex, cell.endIndex - cell.startIndex + 1);
3814
4111
  const visibleActions = cell.leaf ? getVisibleHeaderActions(column, startMetric.width, columnDraggable, measureHeaderTitleWidth(cell.startIndex)) : null;
3815
- const actionWidth = visibleActions ? (Number(visibleActions.drag) + Number(visibleActions.filter) + Number(visibleActions.sort)) * 18 : columnDraggable && !cell.leaf ? 18 : 0;
4112
+ const actionWidth = visibleActions ? (Number(visibleActions.drag) + Number(visibleActions.filter) + Number(visibleActions.sort)) * HEADER_ACTION_SLOT_WIDTH : columnDraggable && !cell.leaf ? HEADER_ACTION_SLOT_WIDTH : 0;
4113
+ const isDropTarget = Boolean(columnDropTarget && cell.endIndex >= columnDropTarget.startIndex && cell.startIndex <= columnDropTarget.endIndex);
3816
4114
  return /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)(
3817
4115
  "div",
3818
4116
  {
3819
- className: `rvg-header-title rvg-header-group${column.renderHeader ? " is-custom" : ""}${cell.leaf ? " is-leaf" : ""}`,
4117
+ className: `rvg-header-title rvg-header-group${column.renderHeader ? " is-custom" : ""}${cell.leaf ? " is-leaf" : ""}${isDropTarget ? " is-drop-target" : ""}`,
3820
4118
  "data-level": cell.level,
3821
4119
  "data-measure-key": `group:${cell.key}:${cell.level}`,
3822
4120
  style: {
@@ -3872,6 +4170,7 @@ function Table({
3872
4170
  })();
3873
4171
  const selectionOutlineStyle = (() => {
3874
4172
  if (!selection || selectionRange) return null;
4173
+ if (rowDragPreview?.sourceIndex === selection.rowIndex) return null;
3875
4174
  const row = rows[selection.rowIndex];
3876
4175
  const metric = metrics[selection.columnIndex];
3877
4176
  const column = columns[selection.columnIndex];
@@ -3881,10 +4180,11 @@ function Table({
3881
4180
  const rawRight = rawLeft + getCellDisplayWidth(selection.columnIndex, span?.colSpan ?? 1);
3882
4181
  const rawTop = bodyTop + selection.rowIndex * rowHeight - scrollPosition.top;
3883
4182
  const rawBottom = rawTop + rowHeight * (span?.rowSpan ?? 1);
4183
+ const dragOffset = getRowDragOffset(selection.rowIndex);
3884
4184
  const horizontalStart = column.fixed === void 0 ? fixedWidth : 0;
3885
4185
  const horizontalEnd = column.fixed === void 0 ? viewport.width - rightFixedWidth : viewport.width;
3886
4186
  const verticalStart = fixedHeader ? bodyTop : 0;
3887
- if (rawRight <= horizontalStart || rawLeft >= horizontalEnd || rawBottom <= verticalStart || rawTop >= renderHeight) return null;
4187
+ if (rawRight <= horizontalStart || rawLeft >= horizontalEnd || rawBottom + dragOffset <= verticalStart || rawTop + dragOffset >= renderHeight) return null;
3888
4188
  const borderWidth = 2;
3889
4189
  const left = Math.round(Math.max(rawLeft - borderWidth, horizontalStart));
3890
4190
  const right = Math.round(Math.min(rawRight + 1, horizontalEnd, viewport.width));
@@ -3894,7 +4194,8 @@ function Table({
3894
4194
  left,
3895
4195
  top,
3896
4196
  width: Math.max(0, right - left),
3897
- height: Math.max(0, bottom - top)
4197
+ height: Math.max(0, bottom - top),
4198
+ transform: `translateY(${dragOffset}px)`
3898
4199
  };
3899
4200
  })();
3900
4201
  const selectionEdgeIndicator = getSelectionEdgeIndicator(scrollPosition.left, scrollPosition.top);
@@ -4053,6 +4354,10 @@ function Table({
4053
4354
  className: "rvg-canvas",
4054
4355
  style: { width: viewport.width, height: renderHeight },
4055
4356
  onMouseMove: (event) => {
4357
+ if (document.documentElement.classList.contains("rvg-is-dragging")) {
4358
+ hideDragTooltips();
4359
+ return;
4360
+ }
4056
4361
  if (columnDragRef.current) return;
4057
4362
  const rect = event.currentTarget.getBoundingClientRect();
4058
4363
  const localY = event.clientY - rect.top;
@@ -4155,6 +4460,14 @@ function Table({
4155
4460
  }
4156
4461
  },
4157
4462
  onPointerDown: (event) => {
4463
+ const headerDragCell = columnDraggable ? locateHeaderCell(event.clientX, event.clientY) : null;
4464
+ const columnIndex = locateColumn(event.clientX);
4465
+ const rowDragHandle = rowDraggable && columnIndex >= 0 && Boolean(columns[columnIndex].rowDragHandle) && locateRow(event.clientY) >= 0;
4466
+ if (headerDragCell && locateHeaderCellDragHandle(event.clientX, headerDragCell) || rowDragHandle) {
4467
+ hideDragTooltips();
4468
+ document.documentElement.classList.add("rvg-is-dragging");
4469
+ event.currentTarget.style.cursor = "move";
4470
+ }
4158
4471
  handleColumnPointerDown(event);
4159
4472
  beginRangeDrag(event);
4160
4473
  },
@@ -4163,10 +4476,12 @@ function Table({
4163
4476
  updateRangeDrag(event);
4164
4477
  },
4165
4478
  onPointerUp: (event) => {
4479
+ document.documentElement.classList.remove("rvg-is-dragging");
4166
4480
  handleColumnPointerUp(event);
4167
4481
  endRangeDrag(event);
4168
4482
  },
4169
4483
  onPointerCancel: (event) => {
4484
+ document.documentElement.classList.remove("rvg-is-dragging");
4170
4485
  handleColumnPointerUp(event);
4171
4486
  endRangeDrag(event);
4172
4487
  },
@@ -4185,7 +4500,7 @@ function Table({
4185
4500
  const action = locateHeaderAction(event.clientX, columnIndex);
4186
4501
  if (action === "sort") {
4187
4502
  const columnKey = columns[columnIndex].key;
4188
- const next = displaySortState?.columnKey !== columnKey ? { columnKey, direction: "asc" } : displaySortState.direction === "asc" ? { columnKey, direction: "desc" } : null;
4503
+ const next = sortState?.columnKey !== columnKey ? { columnKey, direction: "asc" } : sortState.direction === "asc" ? { columnKey, direction: "desc" } : null;
4189
4504
  queueSortStateChange(next);
4190
4505
  return;
4191
4506
  }
@@ -4535,12 +4850,12 @@ function Table({
4535
4850
  /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("span", { children: labels.rowUnit })
4536
4851
  ] });
4537
4852
  if (item === "move-up") {
4538
- if (!rowDraggable || !onRowOrderChange) return null;
4539
- return renderContextMenuButton("cell-move-up", cellContext, labels.moveUp, () => onRowOrderChange(cell.rowIndex, cell.rowIndex - 1), override, { disabled: cell.rowIndex === 0, icon: /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(ContextMenuIcon, { type: "move-up" }) });
4853
+ if (!rowDraggable) return null;
4854
+ return renderContextMenuButton("cell-move-up", cellContext, labels.moveUp, () => applyRowOrder(cell.rowIndex, cell.rowIndex - 1), override, { disabled: cell.rowIndex === 0, icon: /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(ContextMenuIcon, { type: "move-up" }) });
4540
4855
  }
4541
4856
  if (item === "move-down") {
4542
- if (!rowDraggable || !onRowOrderChange) return null;
4543
- return renderContextMenuButton("cell-move-down", cellContext, labels.moveDown, () => onRowOrderChange(cell.rowIndex, cell.rowIndex + 1), override, { disabled: cell.rowIndex === rows.length - 1, icon: /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(ContextMenuIcon, { type: "move-down" }) });
4857
+ if (!rowDraggable) return null;
4858
+ return renderContextMenuButton("cell-move-down", cellContext, labels.moveDown, () => applyRowOrder(cell.rowIndex, cell.rowIndex + 1), override, { disabled: cell.rowIndex === rows.length - 1, icon: /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(ContextMenuIcon, { type: "move-down" }) });
4544
4859
  }
4545
4860
  if (item === "delete-row") return renderContextMenuButton("cell-delete-row", cellContext, labels.deleteRow, () => setConfirmAction({ type: "delete-row", rows: affectedRows.map((rowIndex) => ({ rowIndex, row: rows[rowIndex] })) }), override, { disabled: !onDeleteRows, danger: true, icon: /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(ContextMenuIcon, { type: "delete" }) });
4546
4861
  return null;
@@ -4572,21 +4887,30 @@ function Table({
4572
4887
  await Promise.resolve(onCellChange?.({ ...confirmAction.cell, row: confirmAction.row, previousValue: confirmAction.previousValue, value: "" }));
4573
4888
  } else {
4574
4889
  const deletingKeys = confirmAction.rows.map(({ row, rowIndex }) => getDataRowKey(row, rowIndex));
4890
+ const deletedRows = confirmAction.rows.map(({ row }) => row);
4891
+ const deletingKeySet = new Set(deletingKeys);
4892
+ const nextRows = sourceRows.filter((row, rowIndex) => !deletingKeySet.has(getDataRowKey(row, rowIndex)));
4575
4893
  setConfirmAction(null);
4576
4894
  setConfirmLoading(false);
4895
+ setPendingDeletedRowKeys((current) => {
4896
+ const next = new Set(current);
4897
+ deletingKeys.forEach((key) => next.add(key));
4898
+ return next;
4899
+ });
4577
4900
  if (onDeleteRows) {
4578
4901
  setDeleteBusy(true);
4579
4902
  try {
4580
- await Promise.resolve(onDeleteRows({ rows: confirmAction.rows, viewportRange: domRange }));
4903
+ await Promise.resolve(onDeleteRows(nextRows, deletedRows));
4904
+ } catch (error) {
4905
+ setPendingDeletedRowKeys((current) => {
4906
+ const next = new Set(current);
4907
+ deletingKeys.forEach((key) => next.delete(key));
4908
+ return next;
4909
+ });
4910
+ throw error;
4581
4911
  } finally {
4582
4912
  setDeleteBusy(false);
4583
4913
  }
4584
- } else {
4585
- setPendingDeletedRowKeys((current) => {
4586
- const next = new Set(current);
4587
- deletingKeys.forEach((key) => next.add(key));
4588
- return next;
4589
- });
4590
4914
  }
4591
4915
  return;
4592
4916
  }
@@ -4659,7 +4983,7 @@ function Table({
4659
4983
  const next = { ...filterValues };
4660
4984
  if (filterEditor.draft) next[key] = filterEditor.draft;
4661
4985
  else delete next[key];
4662
- onFilterValuesChange?.(next);
4986
+ setFilterValues(next);
4663
4987
  setFilterEditor(null);
4664
4988
  }
4665
4989
  }
@@ -4670,7 +4994,7 @@ function Table({
4670
4994
  const key = columns[filterEditor.columnIndex].key;
4671
4995
  const next = { ...filterValues };
4672
4996
  delete next[key];
4673
- onFilterValuesChange?.(next);
4997
+ setFilterValues(next);
4674
4998
  setFilterEditor(null);
4675
4999
  }, children: labels.reset }),
4676
5000
  /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("button", { type: "button", className: "rvg-filter-confirm", onClick: () => {
@@ -4678,7 +5002,7 @@ function Table({
4678
5002
  const next = { ...filterValues };
4679
5003
  if (filterEditor.draft) next[key] = filterEditor.draft;
4680
5004
  else delete next[key];
4681
- onFilterValuesChange?.(next);
5005
+ setFilterValues(next);
4682
5006
  setFilterEditor(null);
4683
5007
  }, children: labels.confirm })
4684
5008
  ] })