@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.js CHANGED
@@ -288,16 +288,6 @@ function ContextMenuIcon({ type }) {
288
288
  };
289
289
  return /* @__PURE__ */ jsx2("svg", { className: "rvg-context-menu-svg", viewBox: "0 0 16 16", "aria-hidden": "true", children: paths[type].map((path) => /* @__PURE__ */ jsx2("path", { d: path }, path)) });
290
290
  }
291
- function RowDragHandleIcon() {
292
- return /* @__PURE__ */ jsxs2("svg", { viewBox: "0 0 16 16", focusable: "false", children: [
293
- /* @__PURE__ */ jsx2("circle", { cx: "5", cy: "4", r: "1" }),
294
- /* @__PURE__ */ jsx2("circle", { cx: "11", cy: "4", r: "1" }),
295
- /* @__PURE__ */ jsx2("circle", { cx: "5", cy: "8", r: "1" }),
296
- /* @__PURE__ */ jsx2("circle", { cx: "11", cy: "8", r: "1" }),
297
- /* @__PURE__ */ jsx2("circle", { cx: "5", cy: "12", r: "1" }),
298
- /* @__PURE__ */ jsx2("circle", { cx: "11", cy: "12", r: "1" })
299
- ] });
300
- }
301
291
  function LoadingSpinnerIcon() {
302
292
  return /* @__PURE__ */ jsx2("svg", { viewBox: "0 0 24 24", "aria-hidden": "true", children: /* @__PURE__ */ jsx2("path", { d: "M20 12a8 8 0 1 1-3.1-6.3" }) });
303
293
  }
@@ -725,6 +715,48 @@ function createRowDragHandleSvg() {
725
715
  }
726
716
  return svg;
727
717
  }
718
+ function createHeaderDragHandleSvg() {
719
+ const svg = document.createElementNS("http://www.w3.org/2000/svg", "svg");
720
+ svg.setAttribute("viewBox", "0 0 14 14");
721
+ svg.setAttribute("aria-hidden", "true");
722
+ for (const cy of [2.5, 7, 11.5]) {
723
+ const circle = document.createElementNS("http://www.w3.org/2000/svg", "circle");
724
+ circle.setAttribute("cx", "7");
725
+ circle.setAttribute("cy", String(cy));
726
+ circle.setAttribute("r", "1.25");
727
+ svg.appendChild(circle);
728
+ }
729
+ return svg;
730
+ }
731
+ function createHeaderSortSvg(direction) {
732
+ const svg = document.createElementNS("http://www.w3.org/2000/svg", "svg");
733
+ svg.setAttribute("viewBox", "0 0 14 14");
734
+ svg.setAttribute("aria-hidden", "true");
735
+ for (const [value, active] of [
736
+ ["M3 5.5 7 1.5l4 4Z", direction === "asc"],
737
+ ["M3 8.5 7 12.5l4-4Z", direction === "desc"]
738
+ ]) {
739
+ const path = document.createElementNS("http://www.w3.org/2000/svg", "path");
740
+ path.setAttribute("d", value);
741
+ if (active) path.classList.add("is-active");
742
+ svg.appendChild(path);
743
+ }
744
+ return svg;
745
+ }
746
+ function createHeaderSearchSvg(active) {
747
+ const svg = document.createElementNS("http://www.w3.org/2000/svg", "svg");
748
+ svg.setAttribute("viewBox", "0 0 14 14");
749
+ svg.setAttribute("aria-hidden", "true");
750
+ if (active) svg.classList.add("is-active");
751
+ const circle = document.createElementNS("http://www.w3.org/2000/svg", "circle");
752
+ circle.setAttribute("cx", "5.5");
753
+ circle.setAttribute("cy", "5.5");
754
+ circle.setAttribute("r", "4.25");
755
+ const path = document.createElementNS("http://www.w3.org/2000/svg", "path");
756
+ path.setAttribute("d", "m8.75 8.75 2.25 2.25");
757
+ svg.append(circle, path);
758
+ return svg;
759
+ }
728
760
 
729
761
  // src/Table.tsx
730
762
  import { draggable, dropTargetForElements } from "@atlaskit/pragmatic-drag-and-drop/element/adapter";
@@ -745,6 +777,7 @@ function getDisplayLabel(column, row, rowIndex) {
745
777
 
746
778
  // src/core/layout.ts
747
779
  var DEFAULT_COLUMN_WIDTH = 140;
780
+ var HEADER_ACTION_SLOT_WIDTH = 16;
748
781
  function isDataColumn(column) {
749
782
  return !column.rowSelection && !column.rowDragHandle && !column.rowNumber;
750
783
  }
@@ -759,7 +792,7 @@ function getHeaderTitleRequiredWidth(column, titleWidth) {
759
792
  return minimumReadableTitleWidth + (column.align === "center" ? 20 : 22);
760
793
  }
761
794
  function getVisibleHeaderActions(column, width, columnDraggable = false, titleWidth = 26) {
762
- const capacity = Math.max(0, Math.floor((width - getHeaderTitleRequiredWidth(column, titleWidth)) / 18));
795
+ const capacity = Math.max(0, Math.floor((width - getHeaderTitleRequiredWidth(column, titleWidth)) / HEADER_ACTION_SLOT_WIDTH));
763
796
  const candidates = [
764
797
  isColumnFilterable(column) ? "filter" : null,
765
798
  isColumnSortable(column) ? "sort" : null,
@@ -838,12 +871,15 @@ var COLORS = {
838
871
  grid: "#e3e6e8",
839
872
  text: "#202124",
840
873
  muted: "#5f6368",
874
+ icon: "#9aa0a6",
875
+ borderStrong: "#b8bec4",
841
876
  selection: "#1677ff",
842
877
  selectionFill: "#edf4ff",
843
878
  axisSelectionFill: "#e8f2ff",
844
879
  rowHoverFill: "#f6f9fc",
845
880
  editedFill: "#fff1b8",
846
881
  insertedFill: "#c8ead4",
882
+ columnDropTargetFill: "#eeeeee",
847
883
  stripe: "#fafbfc"
848
884
  };
849
885
  function ellipsizeText(context, text, maxWidth) {
@@ -863,7 +899,7 @@ function ellipsizeText(context, text, maxWidth) {
863
899
  return `${text.slice(0, start)}${ellipsis}`;
864
900
  }
865
901
  function paintGrid(options) {
866
- const { context: ctx, width, height, pixelRatio, scrollLeft, scrollTop, rowHeight, headerHeight, fixedHeader, verticalBorderless, striped, columnDraggable, sortState, filterValues, hoveredHeaderAction, rows, columns, metrics, range, selection, editing, hoveredRowIndex, selectionRange, selectedRowKeys, selectedColumnKeys, highlightEditedCells, highlightInsertedRows, insertedRowKeys, editedCellKeys, cellAnnotations, getRowKey, rowDragPreview } = options;
902
+ 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;
867
903
  const headerLeafTop = options.headerLeafTop ?? 0;
868
904
  const headerLeafHeight = options.headerLeafHeight ?? headerHeight;
869
905
  const colors = { ...COLORS, ...options.colors };
@@ -909,11 +945,19 @@ function paintGrid(options) {
909
945
  const getColumnX = (index) => {
910
946
  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;
911
947
  };
948
+ const getRowDragOffset = (rowIndex) => {
949
+ if (!rowDragPreview || rowIndex === rowDragPreview.sourceIndex) return 0;
950
+ if (rowDragPreview.sourceIndex < rowDragPreview.targetIndex) {
951
+ return rowIndex > rowDragPreview.sourceIndex && rowIndex <= rowDragPreview.targetIndex ? -rowHeight : 0;
952
+ }
953
+ return rowIndex >= rowDragPreview.targetIndex && rowIndex < rowDragPreview.sourceIndex ? rowHeight : 0;
954
+ };
955
+ const rowBelowDragGap = rowDragPreview ? rowDragPreview.sourceIndex <= rowDragPreview.targetIndex ? rowDragPreview.targetIndex + 1 : rowDragPreview.targetIndex : -1;
912
956
  const paintBodyColumns = (layer) => {
913
957
  const rowStart = Math.max(0, range.rowStart - Math.max(0, options.maxRowSpan - 1));
914
958
  for (let rowIndex = rowStart; rowIndex < range.rowEnd; rowIndex += 1) {
915
959
  if (rowDragPreview?.sourceIndex === rowIndex) continue;
916
- const shift = rowDragPreview ? rowDragPreview.sourceIndex < rowDragPreview.targetIndex ? rowIndex > rowDragPreview.sourceIndex && rowIndex <= rowDragPreview.targetIndex ? -rowHeight : 0 : rowIndex >= rowDragPreview.targetIndex && rowIndex < rowDragPreview.sourceIndex ? rowHeight : 0 : 0;
960
+ const shift = getRowDragOffset(rowIndex);
917
961
  const y = bodyTop + rowIndex * rowHeight - scrollTop + shift;
918
962
  const row = rows[rowIndex];
919
963
  const rowKey = getRowKey(row, rowIndex);
@@ -946,6 +990,10 @@ function paintGrid(options) {
946
990
  ctx.fillStyle = colors.rowHoverFill;
947
991
  ctx.fillRect(x, y, cellWidth, cellHeight);
948
992
  }
993
+ if (columnDropTarget && columnIndex >= columnDropTarget.startIndex && columnIndex <= columnDropTarget.endIndex) {
994
+ ctx.fillStyle = colors.columnDropTargetFill;
995
+ ctx.fillRect(x, y, cellWidth, cellHeight);
996
+ }
949
997
  if (highlightInsertedRows && insertedRowKeys.has(rowKey)) {
950
998
  ctx.fillStyle = colors.insertedFill;
951
999
  ctx.fillRect(x, y, cellWidth, cellHeight);
@@ -972,6 +1020,7 @@ function paintGrid(options) {
972
1020
  }
973
1021
  ctx.fillStyle = colors.grid;
974
1022
  if (!verticalBorderless) ctx.fillRect(x + cellWidth - 1, y, 1, cellHeight);
1023
+ if (rowIndex === rowBelowDragGap) ctx.fillRect(x, y, cellWidth, 1);
975
1024
  if (!options.suppressLastRowBottomBorder || rowIndex !== rows.length - 1) {
976
1025
  ctx.fillRect(x, y + cellHeight - 1, cellWidth, 1);
977
1026
  }
@@ -998,6 +1047,59 @@ function paintGrid(options) {
998
1047
  if (columnIndex === rangeColumnStart) ctx.fillRect(x, y, 2, cellHeight);
999
1048
  if (columnIndex === rangeColumnEnd) ctx.fillRect(x + cellWidth - 2, y, 2, cellHeight);
1000
1049
  }
1050
+ if (column.rowDragHandle) {
1051
+ const iconLeft = x + (cellWidth - 16) / 2;
1052
+ const iconTop = y + (cellHeight - 16) / 2;
1053
+ ctx.fillStyle = colors.icon;
1054
+ ctx.beginPath();
1055
+ for (const circleX of [5, 11]) {
1056
+ for (const circleY of [4, 8, 12]) {
1057
+ ctx.moveTo(iconLeft + circleX + 1, iconTop + circleY);
1058
+ ctx.arc(iconLeft + circleX, iconTop + circleY, 1, 0, Math.PI * 2);
1059
+ }
1060
+ }
1061
+ ctx.fill();
1062
+ }
1063
+ if (column.rowSelection) {
1064
+ const iconLeft = x + (cellWidth - 16) / 2;
1065
+ const iconTop = y + (cellHeight - 16) / 2;
1066
+ const checked = selectedRowKeys.has(rowKey);
1067
+ ctx.save();
1068
+ ctx.lineWidth = 1;
1069
+ if (rowSelectionMode === "single") {
1070
+ ctx.beginPath();
1071
+ ctx.arc(iconLeft + 8, iconTop + 8, 6.25, 0, Math.PI * 2);
1072
+ ctx.fillStyle = colors.background;
1073
+ ctx.fill();
1074
+ ctx.strokeStyle = checked ? colors.selection : colors.borderStrong;
1075
+ ctx.stroke();
1076
+ if (checked) {
1077
+ ctx.beginPath();
1078
+ ctx.arc(iconLeft + 8, iconTop + 8, 3.25, 0, Math.PI * 2);
1079
+ ctx.fillStyle = colors.selection;
1080
+ ctx.fill();
1081
+ }
1082
+ } else {
1083
+ ctx.beginPath();
1084
+ ctx.roundRect(iconLeft + 1.5, iconTop + 1.5, 13, 13, 3);
1085
+ ctx.fillStyle = checked ? colors.selection : colors.background;
1086
+ ctx.fill();
1087
+ ctx.strokeStyle = checked ? colors.selection : colors.borderStrong;
1088
+ ctx.stroke();
1089
+ if (checked) {
1090
+ ctx.beginPath();
1091
+ ctx.moveTo(iconLeft + 4.1, iconTop + 8);
1092
+ ctx.lineTo(iconLeft + 6.45, iconTop + 10.35);
1093
+ ctx.lineTo(iconLeft + 11.9, iconTop + 5.15);
1094
+ ctx.strokeStyle = colors.background;
1095
+ ctx.lineWidth = 1.75;
1096
+ ctx.lineCap = "round";
1097
+ ctx.lineJoin = "round";
1098
+ ctx.stroke();
1099
+ }
1100
+ }
1101
+ ctx.restore();
1102
+ }
1001
1103
  if (!column.rowDragHandle && !column.rowSelection && !column.renderCell && !(editing?.rowIndex === rowIndex && editing.columnIndex === columnIndex)) {
1002
1104
  let label = "";
1003
1105
  try {
@@ -1053,6 +1155,10 @@ function paintGrid(options) {
1053
1155
  ctx.fillStyle = colors.selectionFill;
1054
1156
  ctx.fillRect(x, headerY, metric.width, headerHeight);
1055
1157
  }
1158
+ if (columnDropTarget && columnIndex >= columnDropTarget.startIndex && columnIndex <= columnDropTarget.endIndex) {
1159
+ ctx.fillStyle = colors.columnDropTargetFill;
1160
+ ctx.fillRect(x, headerY, metric.width, headerHeight);
1161
+ }
1056
1162
  ctx.fillStyle = colors.grid;
1057
1163
  if (!verticalBorderless) ctx.fillRect(x + metric.width - 1, headerY, 1, headerHeight);
1058
1164
  ctx.fillRect(x, headerY + headerHeight - 1, metric.width, 1);
@@ -1061,7 +1167,7 @@ function paintGrid(options) {
1061
1167
  ctx.font = "600 13px Inter, ui-sans-serif, system-ui, sans-serif";
1062
1168
  const titleWidth = ctx.measureText(column.title).width;
1063
1169
  const visibleActions = getVisibleHeaderActions(column, metric.width, columnDraggable, titleWidth);
1064
- const actionWidth = (Number(visibleActions.drag) + Number(visibleActions.filter) + Number(visibleActions.sort)) * 18;
1170
+ const actionWidth = (Number(visibleActions.drag) + Number(visibleActions.filter) + Number(visibleActions.sort)) * HEADER_ACTION_SLOT_WIDTH;
1065
1171
  const contentWidth = Math.max(0, metric.width - actionWidth);
1066
1172
  const padding = 10;
1067
1173
  const clipWidth = contentWidth;
@@ -1130,7 +1236,7 @@ function paintGrid(options) {
1130
1236
  }
1131
1237
  const selectedRow = selection ? rows[selection.rowIndex] : void 0;
1132
1238
  const hasValidSelection = Boolean(selection && selectedRow && getRowKey(selectedRow, selection.rowIndex) === selection.rowKey);
1133
- if (selection && hasValidSelection && !selectionRange) {
1239
+ if (selection && hasValidSelection && !selectionRange && !rowDragPreview) {
1134
1240
  const metric = metrics[selection.columnIndex];
1135
1241
  if (metric) {
1136
1242
  const fixedSide = columns[selection.columnIndex].fixed;
@@ -1139,7 +1245,7 @@ function paintGrid(options) {
1139
1245
  const selectionWidth = getCellWidth(selection.columnIndex, span?.colSpan ?? 1);
1140
1246
  const selectionHeight = rowHeight * (span?.rowSpan ?? 1);
1141
1247
  const x = Math.round(getColumnX(selection.columnIndex));
1142
- const y = Math.round(bodyTop + selection.rowIndex * rowHeight - scrollTop);
1248
+ const y = Math.round(bodyTop + selection.rowIndex * rowHeight - scrollTop + getRowDragOffset(selection.rowIndex));
1143
1249
  const borderWidth = 2;
1144
1250
  const visibleLeft = fixedSide === "left" ? 0 : leftFixedWidth;
1145
1251
  const selectionAreaRight = fixedSide === "right" ? width : width - rightFixedWidth;
@@ -1249,6 +1355,40 @@ function flattenDataColumns(columns, inheritedFixed, insideGroup = false) {
1249
1355
  return flattenDataColumns(column.children, column.fixed ?? inheritedFixed, true);
1250
1356
  });
1251
1357
  }
1358
+ function reconcileColumns(current, incoming) {
1359
+ const incomingByKey = new Map(incoming.map((column) => [column.key, column]));
1360
+ const ordered = current.flatMap((column) => {
1361
+ const replacement = incomingByKey.get(column.key);
1362
+ if (!replacement) return [];
1363
+ incomingByKey.delete(column.key);
1364
+ return [{
1365
+ ...replacement,
1366
+ children: replacement.children ? reconcileColumns(column.children ?? [], replacement.children) : void 0
1367
+ }];
1368
+ });
1369
+ incoming.forEach((column) => {
1370
+ if (incomingByKey.has(column.key)) ordered.push(column);
1371
+ });
1372
+ return ordered;
1373
+ }
1374
+ function reorderColumns(columns, detail) {
1375
+ const reorder = (items) => {
1376
+ const sourceIndex = items.findIndex((column2) => column2.key === detail.sourceKey);
1377
+ const targetIndex = items.findIndex((column2) => column2.key === detail.targetKey);
1378
+ if (sourceIndex < 0 || targetIndex < 0 || sourceIndex === targetIndex) return items;
1379
+ const next = items.slice();
1380
+ const [column] = next.splice(sourceIndex, 1);
1381
+ let insertIndex = targetIndex + (detail.placement === "after" ? 1 : 0);
1382
+ if (sourceIndex < insertIndex) insertIndex -= 1;
1383
+ next.splice(insertIndex, 0, column);
1384
+ return next;
1385
+ };
1386
+ if (!detail.parentKey) return reorder(columns);
1387
+ return columns.map((column) => {
1388
+ if (column.key === detail.parentKey && column.children) return { ...column, children: reorder(column.children) };
1389
+ return column.children ? { ...column, children: reorderColumns(column.children, detail) } : column;
1390
+ });
1391
+ }
1252
1392
  function buildHeaderCells(sourceColumns, utilityColumnCount, depth) {
1253
1393
  const cells = [];
1254
1394
  let leafIndex = utilityColumnCount;
@@ -1353,7 +1493,7 @@ function resolveContextMenuSection(section, defaults) {
1353
1493
  return defaults;
1354
1494
  }
1355
1495
  function Table({
1356
- columns: sourceColumns,
1496
+ columns: columnProps,
1357
1497
  rows: sourceRows,
1358
1498
  rowKey,
1359
1499
  width = "100%",
@@ -1382,23 +1522,21 @@ function Table({
1382
1522
  rowSelection,
1383
1523
  selectedRowKeys,
1384
1524
  defaultSelectedRowKeys,
1385
- onSelectedRowKeysChange,
1525
+ onSelectedRowChange,
1386
1526
  columnSelection,
1387
1527
  selectedColumnKeys,
1388
1528
  defaultSelectedColumnKeys,
1389
- onSelectedColumnKeysChange,
1529
+ onSelectedColumnChange,
1390
1530
  columnDraggable = true,
1391
- columnResizable = false,
1392
- onColumnOrderChange,
1531
+ columnResizable = true,
1532
+ onColumnsReorder,
1393
1533
  onColumnResize,
1394
1534
  rowDraggable = false,
1395
- onRowOrderChange,
1535
+ onRowsReorder,
1396
1536
  onInsertRows,
1397
1537
  onDeleteRows,
1398
- sortState = null,
1399
- onSortStateChange,
1400
- filterValues = {},
1401
- onFilterValuesChange,
1538
+ onSortChange,
1539
+ onFilterChange,
1402
1540
  onCellChange,
1403
1541
  onCellContextMenu,
1404
1542
  contextMenu: contextMenuConfig = true,
@@ -1407,6 +1545,10 @@ function Table({
1407
1545
  style,
1408
1546
  ariaLabel = "Data grid"
1409
1547
  }) {
1548
+ const [sourceColumns, setSourceColumns] = useState4(columnProps);
1549
+ useEffect2(() => {
1550
+ setSourceColumns((current) => reconcileColumns(current, columnProps));
1551
+ }, [columnProps]);
1410
1552
  const rowHeight = layout?.rowHeight ?? rowHeightProp ?? 36;
1411
1553
  const baseHeaderHeight = layout?.headerHeight ?? headerHeightProp ?? 40;
1412
1554
  const headerDepth = useMemo(() => Math.max(1, ...sourceColumns.map(getColumnDepth)), [sourceColumns]);
@@ -1487,7 +1629,6 @@ function Table({
1487
1629
  const frameRef = useRef2(null);
1488
1630
  const textFrameRef = useRef2(null);
1489
1631
  const toastTimerRef = useRef2(null);
1490
- const sortDebounceRef = useRef2(null);
1491
1632
  const headerTooltipTimerRef = useRef2(null);
1492
1633
  const cellTooltipTimerRef = useRef2(null);
1493
1634
  const pendingHeaderTooltipKeyRef = useRef2("");
@@ -1518,6 +1659,7 @@ function Table({
1518
1659
  const suppressClickRef = useRef2(false);
1519
1660
  const hasCompletedLoadRef = useRef2(!loading);
1520
1661
  const columnDragPreviewRef = useRef2(null);
1662
+ const validColumnDropRef = useRef2(null);
1521
1663
  const hoveredHeaderActionRef = useRef2(null);
1522
1664
  const hoveredRowIndexRef = useRef2(null);
1523
1665
  const [viewport, setViewport] = useState4({ width: 0, height: typeof height === "number" ? height : FALLBACK_HEIGHT });
@@ -1525,11 +1667,8 @@ function Table({
1525
1667
  const resolvedHeight = typeof height === "number" ? height : viewport.height || FALLBACK_HEIGHT;
1526
1668
  const [scrollPosition, setScrollPosition] = useState4({ left: 0, top: 0 });
1527
1669
  const scrollRef = useRef2({ left: 0, top: 0 });
1528
- const [selection, setSelection] = useControllableValue(selectedCell, defaultSelectedCell, onSelectedCellChange);
1529
- const [selectionRange, setSelectionRange] = useState4(null);
1530
- const rangeDragRef = useRef2(null);
1531
1670
  const [rowKeys, setRowKeys] = useControllableKeys(selectedRowKeys, defaultSelectedRowKeys, (keys) => {
1532
- if (!onSelectedRowKeysChange) return;
1671
+ if (!onSelectedRowChange) return;
1533
1672
  const missing = new Set(keys.filter((key) => !rowByKeyRef.current.has(key)));
1534
1673
  if (missing.size > 0) {
1535
1674
  for (let index = 0; index < rows.length && missing.size > 0; index += 1) {
@@ -1546,12 +1685,13 @@ function Table({
1546
1685
  const index = rowIndexByKeyRef.current.get(key);
1547
1686
  return row && index !== void 0 ? [{ row, index }] : [];
1548
1687
  });
1549
- onSelectedRowKeysChange(keys, entries.map(({ row }) => row), entries.map(({ index }) => index));
1688
+ onSelectedRowChange(keys, entries.map(({ row }) => row), entries.map(({ index }) => index));
1550
1689
  });
1551
- const [columnKeys, setColumnKeys] = useControllableKeys(selectedColumnKeys, defaultSelectedColumnKeys, onSelectedColumnKeysChange);
1690
+ const [columnKeys, setColumnKeys] = useControllableKeys(selectedColumnKeys, defaultSelectedColumnKeys, onSelectedColumnChange);
1552
1691
  const [editing, setEditing] = useState4(null);
1553
1692
  const [draft, setDraft] = useState4("");
1554
1693
  const [rowDragPreview, setRowDragPreview] = useState4(null);
1694
+ const [columnDropTarget, setColumnDropTarget] = useState4(null);
1555
1695
  useEffect2(() => {
1556
1696
  if (!loading) hasCompletedLoadRef.current = true;
1557
1697
  }, [loading]);
@@ -1620,6 +1760,8 @@ function Table({
1620
1760
  return () => observer.disconnect();
1621
1761
  }, [activeCustomHeaderLevels, measureCustomHeaderHeight, viewport.width]);
1622
1762
  const [filterEditor, setFilterEditor] = useState4(null);
1763
+ const [filterValues, setFilterValues] = useState4({});
1764
+ const [sortState, setSortState] = useState4(null);
1623
1765
  const [contextMenu, setContextMenu] = useState4(null);
1624
1766
  const [insertCounts, setInsertCounts] = useState4({ before: 1, after: 1 });
1625
1767
  const [confirmAction, setConfirmAction] = useState4(null);
@@ -1632,7 +1774,20 @@ function Table({
1632
1774
  const [hoveredHeaderAction, setHoveredHeaderAction] = useState4(null);
1633
1775
  const [visibleHeaderTooltip, setVisibleHeaderTooltip] = useState4(null);
1634
1776
  const [hoveredCellTooltip, setHoveredCellTooltip] = useState4(null);
1635
- const [displaySortState, setDisplaySortState] = useState4(sortState);
1777
+ const hideDragTooltips = useCallback2(() => {
1778
+ if (headerTooltipTimerRef.current !== null) {
1779
+ clearTimeout(headerTooltipTimerRef.current);
1780
+ headerTooltipTimerRef.current = null;
1781
+ }
1782
+ if (cellTooltipTimerRef.current !== null) {
1783
+ clearTimeout(cellTooltipTimerRef.current);
1784
+ cellTooltipTimerRef.current = null;
1785
+ }
1786
+ pendingHeaderTooltipKeyRef.current = "";
1787
+ pendingCellTooltipKeyRef.current = "";
1788
+ setVisibleHeaderTooltip(null);
1789
+ setHoveredCellTooltip(null);
1790
+ }, []);
1636
1791
  const [editedCellKeys, setEditedCellKeys] = useState4(() => /* @__PURE__ */ new Set());
1637
1792
  const [cellAnnotations, setCellAnnotations] = useState4(() => /* @__PURE__ */ new Map());
1638
1793
  const [annotationType, setAnnotationType] = useState4("corner");
@@ -1644,6 +1799,7 @@ function Table({
1644
1799
  const [pendingDeletedRowKeys, setPendingDeletedRowKeys] = useState4(() => /* @__PURE__ */ new Set());
1645
1800
  const [pendingInserts, setPendingInserts] = useState4(() => []);
1646
1801
  const [insertedRowKeys, setInsertedRowKeys] = useState4(() => /* @__PURE__ */ new Set());
1802
+ const [rowOrderKeys, setRowOrderKeys] = useState4(null);
1647
1803
  const settledPendingInsertIdsRef = useRef2(/* @__PURE__ */ new Set());
1648
1804
  const getDataRowKey = useCallback2((row, index) => {
1649
1805
  return typeof rowKey === "function" ? rowKey(row, index) : rowKey ? row[rowKey] : getDefaultRowKey(row, index);
@@ -1658,7 +1814,7 @@ function Table({
1658
1814
  }
1659
1815
  return sourceRows.findIndex((sourceRow, index) => getDataRowKey(sourceRow, index) === key);
1660
1816
  }, [getDataRowKey, sourceRows]);
1661
- const rows = useMemo(() => {
1817
+ const orderedRows = useMemo(() => {
1662
1818
  let next = sourceRows;
1663
1819
  if (pendingInserts.length > 0) {
1664
1820
  const activeInserts = pendingInserts.filter((insert) => {
@@ -1677,9 +1833,91 @@ function Table({
1677
1833
  insertedOffset += insert.rows.length;
1678
1834
  });
1679
1835
  }
1680
- if (pendingDeletedRowKeys.size === 0) return next;
1681
- return next.filter((row, index) => !pendingDeletedRowKeys.has(getDataRowKey(row, index)));
1682
- }, [getDataRowKey, pendingDeletedRowKeys, pendingInserts, sourceRows]);
1836
+ if (pendingDeletedRowKeys.size > 0) {
1837
+ next = next.filter((row, index) => !pendingDeletedRowKeys.has(getDataRowKey(row, index)));
1838
+ }
1839
+ if (!rowOrderKeys) return next;
1840
+ const rowsByKey = new Map(next.map((row, index) => [getDataRowKey(row, index), row]));
1841
+ const ordered = rowOrderKeys.flatMap((key) => {
1842
+ const row = rowsByKey.get(key);
1843
+ if (!row) return [];
1844
+ rowsByKey.delete(key);
1845
+ return [row];
1846
+ });
1847
+ next.forEach((row, index) => {
1848
+ const key = getDataRowKey(row, index);
1849
+ if (rowsByKey.has(key)) ordered.push(row);
1850
+ });
1851
+ return ordered;
1852
+ }, [getDataRowKey, pendingDeletedRowKeys, pendingInserts, rowOrderKeys, sourceRows]);
1853
+ const activeFilterValues = useMemo(() => Object.fromEntries(
1854
+ Object.entries(filterValues).filter(([, value]) => value.trim() !== "")
1855
+ ), [filterValues]);
1856
+ const filteredRows = useMemo(() => {
1857
+ if (Object.keys(activeFilterValues).length === 0) return orderedRows;
1858
+ if (onFilterChange) return onFilterChange(orderedRows, activeFilterValues);
1859
+ return orderedRows.filter((row, rowIndex) => Object.entries(activeFilterValues).every(([columnKey, query]) => {
1860
+ const column = columns.find((candidate) => candidate.key === columnKey);
1861
+ if (!column) return true;
1862
+ return getDisplayLabel(column, row, rowIndex).toLocaleLowerCase().includes(query.trim().toLocaleLowerCase());
1863
+ }));
1864
+ }, [activeFilterValues, columns, onFilterChange, orderedRows]);
1865
+ const rows = useMemo(() => {
1866
+ if (!sortState) return filteredRows;
1867
+ if (onSortChange) return onSortChange(filteredRows, sortState);
1868
+ const column = columns.find((candidate) => candidate.key === sortState.columnKey);
1869
+ if (!column) return filteredRows;
1870
+ const direction = sortState.direction === "asc" ? 1 : -1;
1871
+ return filteredRows.slice().sort((left, right) => {
1872
+ const leftValue = column.dataIndex === void 0 ? void 0 : left[column.dataIndex];
1873
+ const rightValue = column.dataIndex === void 0 ? void 0 : right[column.dataIndex];
1874
+ const result = typeof leftValue === "number" && typeof rightValue === "number" ? leftValue - rightValue : getDisplayLabel(column, left, 0).localeCompare(getDisplayLabel(column, right, 0), language);
1875
+ return result * direction;
1876
+ });
1877
+ }, [columns, filteredRows, language, onSortChange, sortState]);
1878
+ const resolveSelectionTarget = useCallback2((target) => {
1879
+ if (target == null) return target;
1880
+ const columnIndex = columns.findIndex((column) => column.key === target.columnKey);
1881
+ const rowIndex = target.rowKey !== void 0 ? rows.findIndex((row2, index) => getDataRowKey(row2, index) === target.rowKey) : target.rowIndex ?? -1;
1882
+ const row = rows[rowIndex];
1883
+ if (columnIndex < 0 || !row) return null;
1884
+ return {
1885
+ rowIndex,
1886
+ columnIndex,
1887
+ rowKey: getDataRowKey(row, rowIndex),
1888
+ columnKey: target.columnKey
1889
+ };
1890
+ }, [columns, getDataRowKey, rows]);
1891
+ const resolvedSelectedCell = useMemo(() => resolveSelectionTarget(selectedCell), [resolveSelectionTarget, selectedCell]);
1892
+ const resolvedDefaultSelectedCell = useMemo(() => resolveSelectionTarget(defaultSelectedCell), [defaultSelectedCell, resolveSelectionTarget]);
1893
+ const handleSelectedCellChange = useCallback2((next) => {
1894
+ if (!onSelectedCellChange) return;
1895
+ if (!next) {
1896
+ onSelectedCellChange(null);
1897
+ return;
1898
+ }
1899
+ const row = rows[next.rowIndex];
1900
+ const column = columns[next.columnIndex];
1901
+ const value = row && column?.dataIndex !== void 0 ? row[column.dataIndex] : void 0;
1902
+ onSelectedCellChange({ ...next, value });
1903
+ }, [columns, onSelectedCellChange, rows]);
1904
+ const [selection, setSelection] = useControllableValue(resolvedSelectedCell, resolvedDefaultSelectedCell, handleSelectedCellChange);
1905
+ const [selectionRange, setSelectionRange] = useState4(null);
1906
+ const rangeDragRef = useRef2(null);
1907
+ const applyRowOrder = useCallback2((sourceIndex, targetIndex) => {
1908
+ if (sourceIndex === targetIndex || sourceIndex < 0 || targetIndex < 0 || sourceIndex >= rows.length || targetIndex >= rows.length) return;
1909
+ const nextEntries = rows.map((row, index) => ({ row, key: getDataRowKey(row, index) }));
1910
+ const [entry] = nextEntries.splice(sourceIndex, 1);
1911
+ nextEntries.splice(targetIndex, 0, entry);
1912
+ const nextRows = nextEntries.map(({ row }) => row);
1913
+ setRowOrderKeys(nextEntries.map(({ key }) => key));
1914
+ onRowsReorder?.(nextRows, { sourceIndex, targetIndex });
1915
+ }, [getDataRowKey, onRowsReorder, rows]);
1916
+ const applyColumnOrder = useCallback2((sourceIndex, targetIndex, detail) => {
1917
+ const nextColumns = reorderColumns(sourceColumns, detail);
1918
+ setSourceColumns(nextColumns);
1919
+ onColumnsReorder?.(nextColumns, { sourceIndex, targetIndex, ...detail });
1920
+ }, [onColumnsReorder, sourceColumns]);
1683
1921
  useEffect2(() => {
1684
1922
  if (insertBusy || settledPendingInsertIdsRef.current.size === 0) return;
1685
1923
  setPendingInserts((current) => {
@@ -1921,19 +2159,22 @@ function Table({
1921
2159
  grid: readThemeColor(themeStyles, "--rvg-color-grid", "#e3e6e8"),
1922
2160
  text: readThemeColor(themeStyles, "--rvg-color-text", "#202124"),
1923
2161
  muted: readThemeColor(themeStyles, "--rvg-color-muted", "#5f6368"),
2162
+ icon: readThemeColor(themeStyles, "--rvg-color-icon", "#9aa0a6"),
2163
+ borderStrong: readThemeColor(themeStyles, "--rvg-color-border-strong", "#b8bec4"),
1924
2164
  selection: readThemeColor(themeStyles, "--rvg-color-primary", "#1677ff"),
1925
2165
  selectionFill: readThemeColor(themeStyles, "--rvg-color-selection-fill", "#edf4ff"),
1926
2166
  axisSelectionFill: readThemeColor(themeStyles, "--rvg-color-axis-selection-fill", "#e8f2ff"),
1927
2167
  rowHoverFill: readThemeColor(themeStyles, "--rvg-color-row-hover-fill", "#f6f9fc"),
1928
2168
  editedFill: editedCellHighlightColor ?? readThemeColor(themeStyles, "--rvg-color-edited-fill", "#fff1b8"),
1929
2169
  insertedFill: insertedRowHighlightColor ?? readThemeColor(themeStyles, "--rvg-color-inserted-fill", "#c8ead4"),
2170
+ columnDropTargetFill: readThemeColor(themeStyles, "--rvg-color-column-drop-target-fill", "#eeeeee"),
1930
2171
  stripe: stripedColor ?? readThemeColor(themeStyles, "--rvg-color-stripe", "#fafbfc")
1931
2172
  };
1932
2173
  const scroll = scrollRef.current;
1933
2174
  const rowScrollTop = fixedHeader ? scroll.top : Math.max(0, scroll.top - bodyTop);
1934
2175
  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 };
1935
- paintGrid({ context, width: viewport.width, height: renderHeight, pixelRatio: ratio, scrollLeft: scroll.left, scrollTop: scroll.top, rowHeight, headerHeight, headerLeafTop, headerLeafHeight, bodyTop, suppressLastRowBottomBorder: bottomSummaryHeight > 0, suppressFrameBottomBorder: bottomSummaryHeight > 0, fixedHeader, verticalBorderless: !hasVerticalBorders, striped: hasStripedRows, columnDraggable, sortState, filterValues, hoveredHeaderAction: hoveredHeaderActionRef.current, rows, columns, metrics, range, selection, editing, hoveredRowIndex: hoveredRowIndexRef.current, selectionRange, selectedRowKeys: selectedRowKeySet, selectedColumnKeys: selectedColumnKeySet, cellSpans: cellSpanLookup.covered, maxRowSpan: cellSpanLookup.maxRowSpan, highlightEditedCells: highlightsEditedCells, highlightInsertedRows: highlightsInsertedRows, insertedRowKeys: insertedRowKeySet, editedCellKeys, cellAnnotations, getRowKey, rowDragPreview, colors: themeColors });
1936
- }, [bodyTop, bodyViewportHeight, bottomSummaryHeight, cellAnnotations, cellSpanLookup, columnDraggable, columns, editedCellHighlightColor, editedCellKeys, editing, renderHeight, filterValues, fixedHeader, getRowKey, hasStripedRows, hasVerticalBorders, headerDepth, headerHeight, headerLeafHeight, headerLeafTop, highlightsEditedCells, highlightsInsertedRows, insertedRowHighlightColor, insertedRowKeySet, isVirtualized, metrics, rowDragPreview, rowHeight, rows, selectedColumnKeySet, selectedRowKeySet, selection, selectionRange, sortState, stripedColor, viewport.width, virtualOverscan]);
2176
+ 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 });
2177
+ }, [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]);
1937
2178
  const scheduleDraw = useCallback2(() => {
1938
2179
  if (frameRef.current === null) frameRef.current = requestAnimationFrame(draw);
1939
2180
  }, [draw]);
@@ -2115,11 +2356,7 @@ function Table({
2115
2356
  }
2116
2357
  };
2117
2358
  }, [scheduleDraw]);
2118
- useEffect2(() => {
2119
- setDisplaySortState(sortState);
2120
- }, [sortState]);
2121
2359
  useEffect2(() => () => {
2122
- if (sortDebounceRef.current !== null) clearTimeout(sortDebounceRef.current);
2123
2360
  if (textFrameRef.current !== null) cancelAnimationFrame(textFrameRef.current);
2124
2361
  if (toastTimerRef.current !== null) clearTimeout(toastTimerRef.current);
2125
2362
  if (verticalScrollbarVisibilityTimerRef.current !== null) clearTimeout(verticalScrollbarVisibilityTimerRef.current);
@@ -2494,12 +2731,12 @@ function Table({
2494
2731
  const localX = clientX - canvasRef.current.getBoundingClientRect().left;
2495
2732
  const columnRight = getDisplayedColumnLeft(columnIndex) + metrics[columnIndex].width;
2496
2733
  const visibleActions = getVisibleHeaderActions(column, metrics[columnIndex].width, columnDraggable, measureHeaderTitleWidth(columnIndex));
2497
- let right = columnRight - (visibleActions.drag ? 18 : 4);
2734
+ let right = columnRight - (visibleActions.drag ? HEADER_ACTION_SLOT_WIDTH : 2);
2498
2735
  if (visibleActions.sort) {
2499
- if (localX >= right - 18 && localX < right) return "sort";
2500
- right -= 18;
2736
+ if (localX >= right - HEADER_ACTION_SLOT_WIDTH && localX < right) return "sort";
2737
+ right -= HEADER_ACTION_SLOT_WIDTH;
2501
2738
  }
2502
- if (visibleActions.filter && localX >= right - 18 && localX < right) return "filter";
2739
+ if (visibleActions.filter && localX >= right - HEADER_ACTION_SLOT_WIDTH && localX < right) return "filter";
2503
2740
  return null;
2504
2741
  }, [columnDraggable, columns, getDisplayedColumnLeft, measureHeaderTitleWidth, metrics]);
2505
2742
  const isHeaderTitleTruncated = useCallback2((columnIndex) => {
@@ -2509,7 +2746,7 @@ function Table({
2509
2746
  const metric = metrics[columnIndex];
2510
2747
  const titleWidth = measureHeaderTitleWidth(columnIndex);
2511
2748
  const visibleActions = getVisibleHeaderActions(column, metric.width, columnDraggable, titleWidth);
2512
- const actionWidth = (Number(visibleActions.drag) + Number(visibleActions.filter) + Number(visibleActions.sort)) * 18;
2749
+ const actionWidth = (Number(visibleActions.drag) + Number(visibleActions.filter) + Number(visibleActions.sort)) * HEADER_ACTION_SLOT_WIDTH;
2513
2750
  return getHeaderTitleRequiredWidth(column, titleWidth) > Math.max(0, metric.width - actionWidth);
2514
2751
  }, [columnDraggable, columns, measureHeaderTitleWidth, metrics]);
2515
2752
  const locateHeaderDragHandle = useCallback2((clientX, columnIndex) => {
@@ -2665,6 +2902,7 @@ function Table({
2665
2902
  }
2666
2903
  const sourceIndex = headerDragCell.startIndex;
2667
2904
  columnDragPreviewRef.current = { sourceIndex, targetIndex: sourceIndex };
2905
+ validColumnDropRef.current = null;
2668
2906
  return {
2669
2907
  type: "table-column",
2670
2908
  sourceIndex,
@@ -2675,13 +2913,26 @@ function Table({
2675
2913
  fixed: columns[headerDragCell.startIndex]?.fixed
2676
2914
  };
2677
2915
  },
2678
- onGenerateDragPreview: ({ nativeSetDragImage, source }) => {
2916
+ onGenerateDragPreview: ({ nativeSetDragImage, source, location }) => {
2679
2917
  const sourceIndex = Number(source.data.sourceIndex);
2918
+ const canvasRect = canvas.getBoundingClientRect();
2919
+ const input = location.current.input;
2920
+ const previewOffset = source.data.type === "table-row" ? {
2921
+ x: Math.max(0, Math.min(canvasRect.width, input.clientX - canvasRect.left)) + 2,
2922
+ y: Math.max(0, Math.min(rowHeight, input.clientY - canvasRect.top - bodyTop - sourceIndex * rowHeight + scrollRef.current.top)) + 2
2923
+ } : {
2924
+ x: Math.max(0, Math.min(metrics[sourceIndex].width, input.clientX - canvasRect.left - getDisplayedColumnLeft(sourceIndex))),
2925
+ y: Math.max(0, Math.min(canvasRect.height, input.clientY - canvasRect.top))
2926
+ };
2680
2927
  setCustomNativeDragPreview({
2681
2928
  nativeSetDragImage,
2929
+ getOffset: () => previewOffset,
2682
2930
  render: ({ container }) => {
2683
- const canvasRect = canvas.getBoundingClientRect();
2684
2931
  if (source.data.type === "table-row") {
2932
+ const frame = document.createElement("div");
2933
+ frame.className = "rvg-row-drag-preview-frame";
2934
+ frame.style.width = `${canvasRect.width + 4}px`;
2935
+ frame.style.height = `${rowHeight + 4}px`;
2685
2936
  const preview2 = document.createElement("div");
2686
2937
  preview2.className = "rvg-row-drag-preview";
2687
2938
  preview2.style.width = `${canvasRect.width}px`;
@@ -2707,10 +2958,14 @@ function Table({
2707
2958
  } else {
2708
2959
  cell.textContent = getCellLabel(sourceIndex, columnIndex);
2709
2960
  }
2961
+ if (selection?.rowKey === rowKey2 && selection.columnKey === column2.key) {
2962
+ cell.classList.add("is-selected");
2963
+ }
2710
2964
  preview2.appendChild(cell);
2711
2965
  });
2712
- container.appendChild(preview2);
2713
- return () => preview2.remove();
2966
+ frame.appendChild(preview2);
2967
+ container.appendChild(frame);
2968
+ return () => frame.remove();
2714
2969
  }
2715
2970
  const preview = document.createElement("div");
2716
2971
  preview.className = "rvg-column-drag-preview";
@@ -2724,6 +2979,29 @@ function Table({
2724
2979
  header.style.height = `${headerHeight}px`;
2725
2980
  header.style.justifyContent = column.align === "right" ? "flex-end" : column.align === "center" ? "center" : "flex-start";
2726
2981
  header.textContent = column.title;
2982
+ const visibleActions = getVisibleHeaderActions(column, columnWidth, columnDraggable, measureHeaderTitleWidth(sourceIndex));
2983
+ let actionRight = 2;
2984
+ if (visibleActions.drag) {
2985
+ const icon = createHeaderDragHandleSvg();
2986
+ icon.classList.add("rvg-preview-header-icon");
2987
+ icon.style.right = `${actionRight}px`;
2988
+ header.appendChild(icon);
2989
+ actionRight += HEADER_ACTION_SLOT_WIDTH;
2990
+ }
2991
+ if (visibleActions.sort) {
2992
+ const direction = sortState?.columnKey === column.key ? sortState.direction : null;
2993
+ const icon = createHeaderSortSvg(direction);
2994
+ icon.classList.add("rvg-preview-header-icon");
2995
+ icon.style.right = `${actionRight}px`;
2996
+ header.appendChild(icon);
2997
+ actionRight += HEADER_ACTION_SLOT_WIDTH;
2998
+ }
2999
+ if (visibleActions.filter) {
3000
+ const icon = createHeaderSearchSvg(Boolean(filterValues[column.key]));
3001
+ icon.classList.add("rvg-preview-header-icon", "rvg-preview-header-search-icon");
3002
+ icon.style.right = `${actionRight}px`;
3003
+ header.appendChild(icon);
3004
+ }
2727
3005
  preview.appendChild(header);
2728
3006
  for (let rowIndex = domRange.rowStart; rowIndex < domRange.rowEnd; rowIndex += 1) {
2729
3007
  const row = rows[rowIndex];
@@ -2752,6 +3030,8 @@ function Table({
2752
3030
  },
2753
3031
  onDragStart: ({ source }) => {
2754
3032
  suppressClickRef.current = true;
3033
+ hideDragTooltips();
3034
+ document.documentElement.classList.add("rvg-is-dragging");
2755
3035
  canvas.style.cursor = "move";
2756
3036
  if (source.data.type === "table-row") {
2757
3037
  const sourceIndex = Number(source.data.sourceIndex);
@@ -2777,15 +3057,46 @@ function Table({
2777
3057
  }
2778
3058
  const targetIndex = Number(target?.targetIndex);
2779
3059
  if (!Number.isInteger(targetIndex) || target?.headerKind !== source.data.headerKind || target?.parentKey !== source.data.parentKey || target?.fixed !== source.data.fixed) {
3060
+ validColumnDropRef.current = null;
3061
+ setColumnDropTarget(null);
2780
3062
  setDragGuide(null);
2781
3063
  return;
2782
3064
  }
2783
3065
  const edge = target?.columnEdge;
2784
3066
  const destinationIndex = getColumnDropIndex(sourceIndex, targetIndex, edge);
2785
3067
  columnDragPreviewRef.current = { sourceIndex, targetIndex: destinationIndex };
3068
+ if (destinationIndex === sourceIndex) {
3069
+ validColumnDropRef.current = null;
3070
+ setColumnDropTarget(null);
3071
+ setDragGuide(null);
3072
+ return;
3073
+ }
3074
+ validColumnDropRef.current = {
3075
+ sourceIndex,
3076
+ targetIndex: destinationIndex,
3077
+ rawTargetIndex: targetIndex,
3078
+ targetKey: String(target.targetKey),
3079
+ headerKind: source.data.headerKind === "group" ? "group" : "column",
3080
+ parentKey: typeof source.data.parentKey === "string" ? source.data.parentKey : void 0,
3081
+ fixed: source.data.fixed === "left" || source.data.fixed === "right" ? source.data.fixed : void 0,
3082
+ columnEdge: edge === "right" ? "right" : "left"
3083
+ };
3084
+ 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);
3085
+ const targetEndIndex = Number.isInteger(target?.targetEndIndex) ? Number(target.targetEndIndex) : targetIndex;
3086
+ const hoveredTargetCell = siblingCells.find((cell) => cell.startIndex === targetIndex && cell.endIndex === targetEndIndex);
3087
+ if (!hoveredTargetCell) {
3088
+ validColumnDropRef.current = null;
3089
+ setColumnDropTarget(null);
3090
+ setDragGuide(null);
3091
+ return;
3092
+ }
3093
+ const highlightedCell = edge === "right" ? hoveredTargetCell : siblingCells.filter((cell) => cell.endIndex < hoveredTargetCell.startIndex).sort((left, right) => right.endIndex - left.endIndex)[0] ?? hoveredTargetCell;
3094
+ setColumnDropTarget({ startIndex: highlightedCell.startIndex, endIndex: highlightedCell.endIndex });
2786
3095
  const targetLeft = Number(target?.targetLeft);
2787
3096
  const targetWidth = Number(target?.targetWidth);
2788
3097
  if (!Number.isFinite(targetLeft) || !Number.isFinite(targetWidth)) {
3098
+ validColumnDropRef.current = null;
3099
+ setColumnDropTarget(null);
2789
3100
  setDragGuide(null);
2790
3101
  return;
2791
3102
  }
@@ -2793,18 +3104,19 @@ function Table({
2793
3104
  setDragGuide(edge === "right" ? targetLeft + targetWidth : targetLeft, guideTop);
2794
3105
  },
2795
3106
  onDrop: ({ source, location }) => {
3107
+ document.documentElement.classList.remove("rvg-is-dragging");
2796
3108
  const target = location.current.dropTargets[0]?.data;
2797
3109
  const sourceIndex = Number(source.data.sourceIndex);
2798
3110
  if (source.data.type === "table-row") {
2799
- const rawTargetIndex2 = Number(target?.targetIndex);
2800
- let targetIndex2 = rawTargetIndex2 + (target?.rowEdge === "bottom" ? 1 : 0);
2801
- if (sourceIndex < targetIndex2) targetIndex2 -= 1;
2802
- targetIndex2 = Math.max(0, Math.min(rows.length - 1, targetIndex2));
3111
+ const rawTargetIndex = Number(target?.targetIndex);
3112
+ let targetIndex = rawTargetIndex + (target?.rowEdge === "bottom" ? 1 : 0);
3113
+ if (sourceIndex < targetIndex) targetIndex -= 1;
3114
+ targetIndex = Math.max(0, Math.min(rows.length - 1, targetIndex));
2803
3115
  scrollerRef.current?.classList.add("rvg-row-drop-settling");
2804
3116
  setRowDragPreview(null);
2805
3117
  setRowDragGuide(null);
2806
3118
  canvas.style.cursor = "default";
2807
- if (Number.isInteger(rawTargetIndex2) && sourceIndex !== targetIndex2) onRowOrderChange?.(sourceIndex, targetIndex2);
3119
+ if (Number.isInteger(rawTargetIndex) && sourceIndex !== targetIndex) applyRowOrder(sourceIndex, targetIndex);
2808
3120
  requestAnimationFrame(() => {
2809
3121
  requestAnimationFrame(() => scrollerRef.current?.classList.remove("rvg-row-drop-settling"));
2810
3122
  });
@@ -2813,21 +3125,22 @@ function Table({
2813
3125
  }, 0);
2814
3126
  return;
2815
3127
  }
2816
- const rawTargetIndex = Number(target?.targetIndex);
2817
- const targetIndex = columnDragPreviewRef.current?.targetIndex ?? (Number.isInteger(rawTargetIndex) ? getColumnDropIndex(sourceIndex, rawTargetIndex, target?.columnEdge) : rawTargetIndex);
3128
+ const validDrop = validColumnDropRef.current;
2818
3129
  canvas.style.cursor = "default";
2819
3130
  setDragGuide(null);
3131
+ setColumnDropTarget(null);
2820
3132
  columnDragPreviewRef.current = null;
2821
- if (Number.isInteger(rawTargetIndex) && Number.isInteger(targetIndex) && sourceIndex !== targetIndex && target?.headerKind === source.data.headerKind && target?.parentKey === source.data.parentKey && target?.fixed === source.data.fixed) {
2822
- onColumnOrderChange?.(
2823
- sourceIndex - utilityColumnCount,
2824
- targetIndex - utilityColumnCount,
3133
+ validColumnDropRef.current = null;
3134
+ if (validDrop) {
3135
+ applyColumnOrder(
3136
+ validDrop.sourceIndex - utilityColumnCount,
3137
+ validDrop.targetIndex - utilityColumnCount,
2825
3138
  {
2826
- type: source.data.headerKind === "group" ? "group" : "column",
3139
+ type: validDrop.headerKind,
2827
3140
  sourceKey: String(source.data.sourceKey),
2828
- targetKey: String(target.targetKey),
2829
- parentKey: typeof source.data.parentKey === "string" ? source.data.parentKey : void 0,
2830
- placement: getColumnDropPlacement(sourceIndex, rawTargetIndex, target.columnEdge)
3141
+ targetKey: validDrop.targetKey,
3142
+ parentKey: validDrop.parentKey,
3143
+ placement: getColumnDropPlacement(validDrop.sourceIndex, validDrop.rawTargetIndex, validDrop.columnEdge)
2831
3144
  }
2832
3145
  );
2833
3146
  }
@@ -2839,6 +3152,7 @@ function Table({
2839
3152
  const cleanupDrop = dropTargetForElements({
2840
3153
  element: canvas,
2841
3154
  canDrop: ({ source }) => source.data.type === "table-column" || source.data.type === "table-row",
3155
+ getDropEffect: () => "move",
2842
3156
  getData: ({ input, element, source }) => {
2843
3157
  if (source.data.type === "table-row") {
2844
3158
  const targetIndex2 = locateRow(input.clientY);
@@ -2847,7 +3161,8 @@ function Table({
2847
3161
  const rowEdge = input.clientY - canvas.getBoundingClientRect().top < rowTop + rowHeight / 2 ? "top" : "bottom";
2848
3162
  return attachClosestEdge({ targetIndex: targetIndex2, rowEdge }, { element, input, allowedEdges: ["top", "bottom"] });
2849
3163
  }
2850
- const targetCell = locateHeaderCell(input.clientX, input.clientY);
3164
+ const hoveredColumnIndex = locateColumn(input.clientX);
3165
+ 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);
2851
3166
  if (!targetCell) return { targetIndex: -1 };
2852
3167
  let targetIndex = targetCell.startIndex;
2853
3168
  const targetFixed = columns[targetCell.startIndex]?.fixed;
@@ -2864,6 +3179,7 @@ function Table({
2864
3179
  const columnEdge = input.clientX - canvas.getBoundingClientRect().left < left + targetWidth / 2 ? "left" : "right";
2865
3180
  return attachClosestEdge({
2866
3181
  targetIndex,
3182
+ targetEndIndex: targetCell.endIndex,
2867
3183
  targetKey: targetCell.key,
2868
3184
  parentKey: targetCell.parentKey,
2869
3185
  headerKind,
@@ -2875,10 +3191,11 @@ function Table({
2875
3191
  }
2876
3192
  });
2877
3193
  return () => {
3194
+ document.documentElement.classList.remove("rvg-is-dragging");
2878
3195
  cleanupDrag();
2879
3196
  cleanupDrop();
2880
3197
  };
2881
- }, [bodyTop, columnDraggable, columns, fixedHeader, getCellLabel, getDisplayedColumnLeft, getHeaderCellWidth, getRowKey, headerHeight, locateColumn, locateHeaderCell, locateHeaderCellDragHandle, locateHeaderResizeCell, locateRow, metrics, onColumnOrderChange, onRowOrderChange, rowDraggable, rowHeight, rows, selectedRowKeySet, setDragGuide, setRowDragGuide, utilityColumnCount]);
3198
+ }, [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]);
2882
3199
  const locateCell = useCallback2((clientX, clientY) => {
2883
3200
  const canvas = canvasRef.current;
2884
3201
  if (!canvas) return null;
@@ -3285,19 +3602,14 @@ function Table({
3285
3602
  setResizeGuideTop(0);
3286
3603
  }, []);
3287
3604
  const queueSortStateChange = useCallback2((next) => {
3288
- setDisplaySortState(next);
3289
- if (sortDebounceRef.current !== null) clearTimeout(sortDebounceRef.current);
3290
- sortDebounceRef.current = setTimeout(() => {
3291
- sortDebounceRef.current = null;
3292
- onSortStateChange?.(next);
3293
- }, 200);
3294
- }, [onSortStateChange]);
3605
+ setSortState(next);
3606
+ }, []);
3295
3607
  const renderHeaderIcons = (columnIndex, left) => {
3296
3608
  const column = columns[columnIndex];
3297
3609
  if (column.rowSelection || column.rowDragHandle || column.rowNumber) return null;
3298
3610
  const top = headerLeafTop + (headerLeafHeight - 14) / 2;
3299
3611
  const visibleActions = getVisibleHeaderActions(column, metrics[columnIndex].width, columnDraggable, measureHeaderTitleWidth(columnIndex));
3300
- let right = left + metrics[columnIndex].width - (visibleActions.drag ? 18 : 4);
3612
+ let right = left + metrics[columnIndex].width - (visibleActions.drag ? HEADER_ACTION_SLOT_WIDTH : 2);
3301
3613
  const icons = [];
3302
3614
  if (visibleActions.drag) {
3303
3615
  const hovered = hoveredHeaderAction?.columnIndex === columnIndex && hoveredHeaderAction.action === "drag";
@@ -3307,11 +3619,11 @@ function Table({
3307
3619
  }
3308
3620
  if (visibleActions.sort) {
3309
3621
  const hovered = hoveredHeaderAction?.columnIndex === columnIndex && hoveredHeaderAction.action === "sort";
3310
- const direction = displaySortState?.columnKey === column.key ? displaySortState.direction : null;
3622
+ const direction = sortState?.columnKey === column.key ? sortState.direction : null;
3311
3623
  icons.push(
3312
3624
  /* @__PURE__ */ jsx7(HeaderSortIcon, { className: `rvg-header-icon${hovered ? " is-hovered" : ""}`, style: { left: right - 16, top }, direction }, "sort")
3313
3625
  );
3314
- right -= 18;
3626
+ right -= HEADER_ACTION_SLOT_WIDTH;
3315
3627
  }
3316
3628
  if (visibleActions.filter) {
3317
3629
  const hovered = hoveredHeaderAction?.columnIndex === columnIndex && hoveredHeaderAction.action === "filter";
@@ -3338,7 +3650,7 @@ function Table({
3338
3650
  if (context) context.font = "600 13px Inter, ui-sans-serif, system-ui, sans-serif";
3339
3651
  const titleWidth = context?.measureText(column.title).width ?? column.title.length * 13;
3340
3652
  const visibleActions = cell?.leaf || !cell ? getVisibleHeaderActions(column, cellWidth, columnDraggable, titleWidth) : null;
3341
- const actionWidth = visibleActions ? (Number(visibleActions.drag) + Number(visibleActions.filter) + Number(visibleActions.sort)) * 18 : columnDraggable ? 18 : 0;
3653
+ const actionWidth = visibleActions ? (Number(visibleActions.drag) + Number(visibleActions.filter) + Number(visibleActions.sort)) * HEADER_ACTION_SLOT_WIDTH : columnDraggable ? HEADER_ACTION_SLOT_WIDTH : 0;
3342
3654
  const contentWidth2 = Math.max(0, cellWidth - actionWidth);
3343
3655
  const maxTextWidth = Math.max(0, contentWidth2 - 20);
3344
3656
  const textWidth = Math.min(titleWidth, maxTextWidth);
@@ -3357,7 +3669,7 @@ function Table({
3357
3669
  anchorLeft = anchorX;
3358
3670
  label = labels.dragColumn;
3359
3671
  } else if (action === "sort") {
3360
- const direction = displaySortState?.columnKey === column.key ? displaySortState.direction : null;
3672
+ const direction = sortState?.columnKey === column.key ? sortState.direction : null;
3361
3673
  label = direction === "asc" ? labels.sortAsc : direction === "desc" ? labels.sortDesc : labels.sortBoth;
3362
3674
  anchorX = cellLeft + cellWidth - 22;
3363
3675
  anchorLeft = anchorX;
@@ -3377,7 +3689,7 @@ function Table({
3377
3689
  label,
3378
3690
  placement
3379
3691
  };
3380
- }, [baseHeaderHeight, columnDraggable, columns, displaySortState, filterValues, fixedHeader, getDisplayedColumnLeft, getHeaderCellLeft, getHeaderCellWidth, headerDepth, headerRowHeights, headerRowOffsets, labels, metrics, scrollPosition.top, viewport.width]);
3692
+ }, [baseHeaderHeight, columnDraggable, columns, filterValues, fixedHeader, getDisplayedColumnLeft, getHeaderCellLeft, getHeaderCellWidth, headerDepth, headerRowHeights, headerRowOffsets, labels, metrics, scrollPosition.top, sortState, viewport.width]);
3381
3693
  const isPointerOnHeaderTitle = useCallback2((clientX, clientY, cell) => {
3382
3694
  const canvas = canvasRef.current;
3383
3695
  if (!canvas) return false;
@@ -3392,7 +3704,7 @@ function Table({
3392
3704
  if (context) context.font = "600 13px Inter, ui-sans-serif, system-ui, sans-serif";
3393
3705
  const titleWidth = context?.measureText(column.title).width ?? column.title.length * 13;
3394
3706
  const visibleActions = cell.leaf ? getVisibleHeaderActions(column, cellWidth, columnDraggable, titleWidth) : null;
3395
- const actionWidth = visibleActions ? (Number(visibleActions.drag) + Number(visibleActions.filter) + Number(visibleActions.sort)) * 18 : columnDraggable ? 18 : 0;
3707
+ const actionWidth = visibleActions ? (Number(visibleActions.drag) + Number(visibleActions.filter) + Number(visibleActions.sort)) * HEADER_ACTION_SLOT_WIDTH : columnDraggable ? HEADER_ACTION_SLOT_WIDTH : 0;
3396
3708
  const contentWidth2 = Math.max(0, cellWidth - actionWidth);
3397
3709
  const textWidth = Math.min(titleWidth, Math.max(0, contentWidth2 - 20));
3398
3710
  const textLeft = column.align === "right" ? cellLeft + contentWidth2 - 10 - textWidth : column.align === "center" ? cellLeft + contentWidth2 / 2 - textWidth / 2 : cellLeft + 10;
@@ -3632,7 +3944,9 @@ function Table({
3632
3944
  });
3633
3945
  setPendingInserts((current) => [...current, pending]);
3634
3946
  try {
3635
- await Promise.resolve(onInsertRows({ rowIndex: sourceRowIndex >= 0 ? sourceRowIndex : cell.rowIndex, row, position, count, insertedRows }));
3947
+ const nextRows = sourceRows.slice();
3948
+ nextRows.splice(insertIndex, 0, ...insertedRows);
3949
+ await Promise.resolve(onInsertRows(nextRows, insertedRows));
3636
3950
  await waitForPaint();
3637
3951
  setInsertBusy(false);
3638
3952
  } catch (error) {
@@ -3659,28 +3973,10 @@ function Table({
3659
3973
  const currentScrollTop = scrollRef.current.top;
3660
3974
  const top = (fixedHeader ? 0 : bodyTop) + rowIndex * rowHeight - currentScrollTop;
3661
3975
  if (column.rowSelection) {
3662
- const rowKey2 = getRowKey(rows[rowIndex], rowIndex);
3663
- return /* @__PURE__ */ jsx7(
3664
- "div",
3665
- {
3666
- className: "rvg-row-selection-icon",
3667
- style: { left, top, width: cellWidth, height: cellHeight, opacity: isDraggedSource ? 0 : 1, transform: `translateY(${dragOffset}px)` },
3668
- children: /* @__PURE__ */ jsx7(SelectionIcon, { checked: selectedRowKeySet.has(rowKey2), radio: rowSelectionMode === "single" })
3669
- },
3670
- `selection:${String(rowKey2)}`
3671
- );
3976
+ return null;
3672
3977
  }
3673
3978
  if (column.rowDragHandle) {
3674
- return /* @__PURE__ */ jsx7(
3675
- "div",
3676
- {
3677
- className: "rvg-row-drag-icon",
3678
- style: { left, top, width: cellWidth, height: cellHeight, opacity: isDraggedSource ? 0 : 1, transform: `translateY(${dragOffset}px)` },
3679
- "aria-hidden": "true",
3680
- children: /* @__PURE__ */ jsx7(RowDragHandleIcon, {})
3681
- },
3682
- `drag:${String(getRowKey(rows[rowIndex], rowIndex))}`
3683
- );
3979
+ return null;
3684
3980
  }
3685
3981
  if (column.renderCell && !(editing?.rowIndex === rowIndex && editing.columnIndex === columnIndex)) {
3686
3982
  const row = rows[rowIndex];
@@ -3707,6 +4003,7 @@ function Table({
3707
4003
  };
3708
4004
  const renderHeaderTitle = (columnIndex, left) => {
3709
4005
  const column = columns[columnIndex];
4006
+ const isDropTarget = Boolean(columnDropTarget && columnIndex >= columnDropTarget.startIndex && columnIndex <= columnDropTarget.endIndex);
3710
4007
  const currentScrollTop = scrollRef.current.top;
3711
4008
  const headerTop = fixedHeader ? 0 : -currentScrollTop;
3712
4009
  const leafTop = headerTop + headerLeafTop;
@@ -3714,7 +4011,7 @@ function Table({
3714
4011
  return /* @__PURE__ */ jsx7(
3715
4012
  "div",
3716
4013
  {
3717
- className: "rvg-header-title",
4014
+ className: `rvg-header-title${isDropTarget ? " is-drop-target" : ""}`,
3718
4015
  style: { left, top: headerTop, width: metrics[columnIndex].width, height: headerHeight, justifyContent: "center", padding: 0, textAlign: "center" },
3719
4016
  children: /* @__PURE__ */ jsx7("span", { children: column.title })
3720
4017
  },
@@ -3737,11 +4034,11 @@ function Table({
3737
4034
  }
3738
4035
  if (column.renderHeader) {
3739
4036
  const visibleActions = getVisibleHeaderActions(column, metrics[columnIndex].width, columnDraggable, measureHeaderTitleWidth(columnIndex));
3740
- const actionWidth = (Number(visibleActions.drag) + Number(visibleActions.filter) + Number(visibleActions.sort)) * 18;
4037
+ const actionWidth = (Number(visibleActions.drag) + Number(visibleActions.filter) + Number(visibleActions.sort)) * HEADER_ACTION_SLOT_WIDTH;
3741
4038
  return /* @__PURE__ */ jsx7(
3742
4039
  "div",
3743
4040
  {
3744
- className: "rvg-header-title is-custom",
4041
+ className: `rvg-header-title is-custom${isDropTarget ? " is-drop-target" : ""}`,
3745
4042
  "data-level": headerDepth - 1,
3746
4043
  "data-measure-key": column.key,
3747
4044
  style: {
@@ -3782,11 +4079,12 @@ function Table({
3782
4079
  const left = fixedSide === "left" ? leftFixedOffsets.get(cell.startIndex) ?? 0 : fixedSide === "right" ? viewport.width - (rightFixedOffsets.get(cell.startIndex) ?? 0) - startMetric.width : getScrollableColumnLeft(cell.startIndex);
3783
4080
  const width2 = cell.leaf ? startMetric.width : getCellDisplayWidth(cell.startIndex, cell.endIndex - cell.startIndex + 1);
3784
4081
  const visibleActions = cell.leaf ? getVisibleHeaderActions(column, startMetric.width, columnDraggable, measureHeaderTitleWidth(cell.startIndex)) : null;
3785
- const actionWidth = visibleActions ? (Number(visibleActions.drag) + Number(visibleActions.filter) + Number(visibleActions.sort)) * 18 : columnDraggable && !cell.leaf ? 18 : 0;
4082
+ const actionWidth = visibleActions ? (Number(visibleActions.drag) + Number(visibleActions.filter) + Number(visibleActions.sort)) * HEADER_ACTION_SLOT_WIDTH : columnDraggable && !cell.leaf ? HEADER_ACTION_SLOT_WIDTH : 0;
4083
+ const isDropTarget = Boolean(columnDropTarget && cell.endIndex >= columnDropTarget.startIndex && cell.startIndex <= columnDropTarget.endIndex);
3786
4084
  return /* @__PURE__ */ jsxs7(
3787
4085
  "div",
3788
4086
  {
3789
- className: `rvg-header-title rvg-header-group${column.renderHeader ? " is-custom" : ""}${cell.leaf ? " is-leaf" : ""}`,
4087
+ className: `rvg-header-title rvg-header-group${column.renderHeader ? " is-custom" : ""}${cell.leaf ? " is-leaf" : ""}${isDropTarget ? " is-drop-target" : ""}`,
3790
4088
  "data-level": cell.level,
3791
4089
  "data-measure-key": `group:${cell.key}:${cell.level}`,
3792
4090
  style: {
@@ -3842,6 +4140,7 @@ function Table({
3842
4140
  })();
3843
4141
  const selectionOutlineStyle = (() => {
3844
4142
  if (!selection || selectionRange) return null;
4143
+ if (rowDragPreview?.sourceIndex === selection.rowIndex) return null;
3845
4144
  const row = rows[selection.rowIndex];
3846
4145
  const metric = metrics[selection.columnIndex];
3847
4146
  const column = columns[selection.columnIndex];
@@ -3851,10 +4150,11 @@ function Table({
3851
4150
  const rawRight = rawLeft + getCellDisplayWidth(selection.columnIndex, span?.colSpan ?? 1);
3852
4151
  const rawTop = bodyTop + selection.rowIndex * rowHeight - scrollPosition.top;
3853
4152
  const rawBottom = rawTop + rowHeight * (span?.rowSpan ?? 1);
4153
+ const dragOffset = getRowDragOffset(selection.rowIndex);
3854
4154
  const horizontalStart = column.fixed === void 0 ? fixedWidth : 0;
3855
4155
  const horizontalEnd = column.fixed === void 0 ? viewport.width - rightFixedWidth : viewport.width;
3856
4156
  const verticalStart = fixedHeader ? bodyTop : 0;
3857
- if (rawRight <= horizontalStart || rawLeft >= horizontalEnd || rawBottom <= verticalStart || rawTop >= renderHeight) return null;
4157
+ if (rawRight <= horizontalStart || rawLeft >= horizontalEnd || rawBottom + dragOffset <= verticalStart || rawTop + dragOffset >= renderHeight) return null;
3858
4158
  const borderWidth = 2;
3859
4159
  const left = Math.round(Math.max(rawLeft - borderWidth, horizontalStart));
3860
4160
  const right = Math.round(Math.min(rawRight + 1, horizontalEnd, viewport.width));
@@ -3864,7 +4164,8 @@ function Table({
3864
4164
  left,
3865
4165
  top,
3866
4166
  width: Math.max(0, right - left),
3867
- height: Math.max(0, bottom - top)
4167
+ height: Math.max(0, bottom - top),
4168
+ transform: `translateY(${dragOffset}px)`
3868
4169
  };
3869
4170
  })();
3870
4171
  const selectionEdgeIndicator = getSelectionEdgeIndicator(scrollPosition.left, scrollPosition.top);
@@ -4023,6 +4324,10 @@ function Table({
4023
4324
  className: "rvg-canvas",
4024
4325
  style: { width: viewport.width, height: renderHeight },
4025
4326
  onMouseMove: (event) => {
4327
+ if (document.documentElement.classList.contains("rvg-is-dragging")) {
4328
+ hideDragTooltips();
4329
+ return;
4330
+ }
4026
4331
  if (columnDragRef.current) return;
4027
4332
  const rect = event.currentTarget.getBoundingClientRect();
4028
4333
  const localY = event.clientY - rect.top;
@@ -4125,6 +4430,14 @@ function Table({
4125
4430
  }
4126
4431
  },
4127
4432
  onPointerDown: (event) => {
4433
+ const headerDragCell = columnDraggable ? locateHeaderCell(event.clientX, event.clientY) : null;
4434
+ const columnIndex = locateColumn(event.clientX);
4435
+ const rowDragHandle = rowDraggable && columnIndex >= 0 && Boolean(columns[columnIndex].rowDragHandle) && locateRow(event.clientY) >= 0;
4436
+ if (headerDragCell && locateHeaderCellDragHandle(event.clientX, headerDragCell) || rowDragHandle) {
4437
+ hideDragTooltips();
4438
+ document.documentElement.classList.add("rvg-is-dragging");
4439
+ event.currentTarget.style.cursor = "move";
4440
+ }
4128
4441
  handleColumnPointerDown(event);
4129
4442
  beginRangeDrag(event);
4130
4443
  },
@@ -4133,10 +4446,12 @@ function Table({
4133
4446
  updateRangeDrag(event);
4134
4447
  },
4135
4448
  onPointerUp: (event) => {
4449
+ document.documentElement.classList.remove("rvg-is-dragging");
4136
4450
  handleColumnPointerUp(event);
4137
4451
  endRangeDrag(event);
4138
4452
  },
4139
4453
  onPointerCancel: (event) => {
4454
+ document.documentElement.classList.remove("rvg-is-dragging");
4140
4455
  handleColumnPointerUp(event);
4141
4456
  endRangeDrag(event);
4142
4457
  },
@@ -4155,7 +4470,7 @@ function Table({
4155
4470
  const action = locateHeaderAction(event.clientX, columnIndex);
4156
4471
  if (action === "sort") {
4157
4472
  const columnKey = columns[columnIndex].key;
4158
- const next = displaySortState?.columnKey !== columnKey ? { columnKey, direction: "asc" } : displaySortState.direction === "asc" ? { columnKey, direction: "desc" } : null;
4473
+ const next = sortState?.columnKey !== columnKey ? { columnKey, direction: "asc" } : sortState.direction === "asc" ? { columnKey, direction: "desc" } : null;
4159
4474
  queueSortStateChange(next);
4160
4475
  return;
4161
4476
  }
@@ -4505,12 +4820,12 @@ function Table({
4505
4820
  /* @__PURE__ */ jsx7("span", { children: labels.rowUnit })
4506
4821
  ] });
4507
4822
  if (item === "move-up") {
4508
- if (!rowDraggable || !onRowOrderChange) return null;
4509
- return renderContextMenuButton("cell-move-up", cellContext, labels.moveUp, () => onRowOrderChange(cell.rowIndex, cell.rowIndex - 1), override, { disabled: cell.rowIndex === 0, icon: /* @__PURE__ */ jsx7(ContextMenuIcon, { type: "move-up" }) });
4823
+ if (!rowDraggable) return null;
4824
+ return renderContextMenuButton("cell-move-up", cellContext, labels.moveUp, () => applyRowOrder(cell.rowIndex, cell.rowIndex - 1), override, { disabled: cell.rowIndex === 0, icon: /* @__PURE__ */ jsx7(ContextMenuIcon, { type: "move-up" }) });
4510
4825
  }
4511
4826
  if (item === "move-down") {
4512
- if (!rowDraggable || !onRowOrderChange) return null;
4513
- return renderContextMenuButton("cell-move-down", cellContext, labels.moveDown, () => onRowOrderChange(cell.rowIndex, cell.rowIndex + 1), override, { disabled: cell.rowIndex === rows.length - 1, icon: /* @__PURE__ */ jsx7(ContextMenuIcon, { type: "move-down" }) });
4827
+ if (!rowDraggable) return null;
4828
+ return renderContextMenuButton("cell-move-down", cellContext, labels.moveDown, () => applyRowOrder(cell.rowIndex, cell.rowIndex + 1), override, { disabled: cell.rowIndex === rows.length - 1, icon: /* @__PURE__ */ jsx7(ContextMenuIcon, { type: "move-down" }) });
4514
4829
  }
4515
4830
  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__ */ jsx7(ContextMenuIcon, { type: "delete" }) });
4516
4831
  return null;
@@ -4542,21 +4857,30 @@ function Table({
4542
4857
  await Promise.resolve(onCellChange?.({ ...confirmAction.cell, row: confirmAction.row, previousValue: confirmAction.previousValue, value: "" }));
4543
4858
  } else {
4544
4859
  const deletingKeys = confirmAction.rows.map(({ row, rowIndex }) => getDataRowKey(row, rowIndex));
4860
+ const deletedRows = confirmAction.rows.map(({ row }) => row);
4861
+ const deletingKeySet = new Set(deletingKeys);
4862
+ const nextRows = sourceRows.filter((row, rowIndex) => !deletingKeySet.has(getDataRowKey(row, rowIndex)));
4545
4863
  setConfirmAction(null);
4546
4864
  setConfirmLoading(false);
4865
+ setPendingDeletedRowKeys((current) => {
4866
+ const next = new Set(current);
4867
+ deletingKeys.forEach((key) => next.add(key));
4868
+ return next;
4869
+ });
4547
4870
  if (onDeleteRows) {
4548
4871
  setDeleteBusy(true);
4549
4872
  try {
4550
- await Promise.resolve(onDeleteRows({ rows: confirmAction.rows, viewportRange: domRange }));
4873
+ await Promise.resolve(onDeleteRows(nextRows, deletedRows));
4874
+ } catch (error) {
4875
+ setPendingDeletedRowKeys((current) => {
4876
+ const next = new Set(current);
4877
+ deletingKeys.forEach((key) => next.delete(key));
4878
+ return next;
4879
+ });
4880
+ throw error;
4551
4881
  } finally {
4552
4882
  setDeleteBusy(false);
4553
4883
  }
4554
- } else {
4555
- setPendingDeletedRowKeys((current) => {
4556
- const next = new Set(current);
4557
- deletingKeys.forEach((key) => next.add(key));
4558
- return next;
4559
- });
4560
4884
  }
4561
4885
  return;
4562
4886
  }
@@ -4629,7 +4953,7 @@ function Table({
4629
4953
  const next = { ...filterValues };
4630
4954
  if (filterEditor.draft) next[key] = filterEditor.draft;
4631
4955
  else delete next[key];
4632
- onFilterValuesChange?.(next);
4956
+ setFilterValues(next);
4633
4957
  setFilterEditor(null);
4634
4958
  }
4635
4959
  }
@@ -4640,7 +4964,7 @@ function Table({
4640
4964
  const key = columns[filterEditor.columnIndex].key;
4641
4965
  const next = { ...filterValues };
4642
4966
  delete next[key];
4643
- onFilterValuesChange?.(next);
4967
+ setFilterValues(next);
4644
4968
  setFilterEditor(null);
4645
4969
  }, children: labels.reset }),
4646
4970
  /* @__PURE__ */ jsx7("button", { type: "button", className: "rvg-filter-confirm", onClick: () => {
@@ -4648,7 +4972,7 @@ function Table({
4648
4972
  const next = { ...filterValues };
4649
4973
  if (filterEditor.draft) next[key] = filterEditor.draft;
4650
4974
  else delete next[key];
4651
- onFilterValuesChange?.(next);
4975
+ setFilterValues(next);
4652
4976
  setFilterEditor(null);
4653
4977
  }, children: labels.confirm })
4654
4978
  ] })