@lensui/lens-table 0.1.5 → 0.1.7

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
@@ -1,5 +1,5 @@
1
1
  // src/Table.tsx
2
- import { Fragment as Fragment2, useCallback as useCallback2, useEffect as useEffect2, useLayoutEffect as useLayoutEffect2, useMemo, useRef as useRef2, useState as useState4 } from "react";
2
+ import { forwardRef, useImperativeHandle, Fragment as Fragment2, useCallback as useCallback2, useEffect as useEffect2, useLayoutEffect as useLayoutEffect2, useMemo, useRef as useRef2, useState as useState4 } from "react";
3
3
  import { flushSync } from "react-dom";
4
4
 
5
5
  // src/components/EmptyState.tsx
@@ -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;
@@ -1352,8 +1492,8 @@ function resolveContextMenuSection(section, defaults) {
1352
1492
  }
1353
1493
  return defaults;
1354
1494
  }
1355
- function Table({
1356
- columns: sourceColumns,
1495
+ function TableInner({
1496
+ columns: columnProps,
1357
1497
  rows: sourceRows,
1358
1498
  rowKey,
1359
1499
  width = "100%",
@@ -1382,31 +1522,35 @@ 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,
1541
+ onCellClick,
1542
+ onCellDoubleClick,
1403
1543
  onCellContextMenu,
1404
1544
  contextMenu: contextMenuConfig = true,
1405
1545
  emptyContent,
1406
1546
  className = "",
1407
1547
  style,
1408
1548
  ariaLabel = "Data grid"
1409
- }) {
1549
+ }, ref) {
1550
+ const [sourceColumns, setSourceColumns] = useState4(columnProps);
1551
+ useEffect2(() => {
1552
+ setSourceColumns((current) => reconcileColumns(current, columnProps));
1553
+ }, [columnProps]);
1410
1554
  const rowHeight = layout?.rowHeight ?? rowHeightProp ?? 36;
1411
1555
  const baseHeaderHeight = layout?.headerHeight ?? headerHeightProp ?? 40;
1412
1556
  const headerDepth = useMemo(() => Math.max(1, ...sourceColumns.map(getColumnDepth)), [sourceColumns]);
@@ -1448,6 +1592,7 @@ function Table({
1448
1592
  const customContextMenuConfig = typeof contextMenuConfig === "object" ? contextMenuConfig : void 0;
1449
1593
  const [resizedColumnWidths, setResizedColumnWidths] = useState4({});
1450
1594
  const leafSourceColumns = useMemo(() => flattenDataColumns(sourceColumns), [sourceColumns]);
1595
+ const showRowCheckbox = Boolean(rowSelection) && (typeof rowSelection !== "object" || rowSelection.showCheckbox !== false);
1451
1596
  useEffect2(() => {
1452
1597
  if (!isProductionRuntime() && hasNestedFixedColumns(sourceColumns)) {
1453
1598
  console.warn("[Table] Multi-level headers only support fixed columns on top-level columns. Nested fixed values are ignored.");
@@ -1458,19 +1603,19 @@ function Table({
1458
1603
  if (rowDraggable) {
1459
1604
  utilityColumns.push({ key: "__rvg_row_drag__", title: "", width: 36, align: "center", fixed: "left", rowDragHandle: true });
1460
1605
  }
1461
- if (rowSelection) {
1606
+ if (showRowCheckbox) {
1462
1607
  utilityColumns.push({ key: "__rvg_row_selection__", title: "", width: 44, align: "center", fixed: "left", rowSelection: true });
1463
1608
  }
1464
1609
  if (rowNumber) {
1465
- utilityColumns.push({ key: "__rvg_row_number__", title: "#", width: getRowNumberColumnWidth(sourceRows.length), align: "center", fixed: "left", rowNumber: true });
1610
+ utilityColumns.push({ key: "__rvg_row_number__", title: "#", width: getRowNumberColumnWidth(sourceRows.length), align: "center", fixed: typeof rowNumber === "object" && rowNumber.fixed === false ? void 0 : "left", rowNumber: true });
1466
1611
  }
1467
1612
  const dataColumns = leafSourceColumns.map((column) => {
1468
1613
  const resizedWidth = resizedColumnWidths[column.key];
1469
1614
  return resizedWidth === void 0 ? column : { ...column, width: resizedWidth };
1470
1615
  });
1471
1616
  return [...utilityColumns, ...dataColumns];
1472
- }, [leafSourceColumns, resizedColumnWidths, rowDraggable, rowNumber, rowSelection, sourceRows.length]);
1473
- const utilityColumnCount = (rowDraggable ? 1 : 0) + (rowSelection ? 1 : 0) + (rowNumber ? 1 : 0);
1617
+ }, [leafSourceColumns, resizedColumnWidths, rowDraggable, rowNumber, showRowCheckbox, sourceRows.length]);
1618
+ const utilityColumnCount = (rowDraggable ? 1 : 0) + (showRowCheckbox ? 1 : 0) + (rowNumber ? 1 : 0);
1474
1619
  const headerCells = useMemo(() => buildHeaderCells(sourceColumns, utilityColumnCount, headerDepth), [headerDepth, sourceColumns, utilityColumnCount]);
1475
1620
  const activeCustomHeaderLevels = useMemo(() => {
1476
1621
  const levels = /* @__PURE__ */ new Map();
@@ -1487,7 +1632,6 @@ function Table({
1487
1632
  const frameRef = useRef2(null);
1488
1633
  const textFrameRef = useRef2(null);
1489
1634
  const toastTimerRef = useRef2(null);
1490
- const sortDebounceRef = useRef2(null);
1491
1635
  const headerTooltipTimerRef = useRef2(null);
1492
1636
  const cellTooltipTimerRef = useRef2(null);
1493
1637
  const pendingHeaderTooltipKeyRef = useRef2("");
@@ -1518,6 +1662,7 @@ function Table({
1518
1662
  const suppressClickRef = useRef2(false);
1519
1663
  const hasCompletedLoadRef = useRef2(!loading);
1520
1664
  const columnDragPreviewRef = useRef2(null);
1665
+ const validColumnDropRef = useRef2(null);
1521
1666
  const hoveredHeaderActionRef = useRef2(null);
1522
1667
  const hoveredRowIndexRef = useRef2(null);
1523
1668
  const [viewport, setViewport] = useState4({ width: 0, height: typeof height === "number" ? height : FALLBACK_HEIGHT });
@@ -1525,11 +1670,8 @@ function Table({
1525
1670
  const resolvedHeight = typeof height === "number" ? height : viewport.height || FALLBACK_HEIGHT;
1526
1671
  const [scrollPosition, setScrollPosition] = useState4({ left: 0, top: 0 });
1527
1672
  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
1673
  const [rowKeys, setRowKeys] = useControllableKeys(selectedRowKeys, defaultSelectedRowKeys, (keys) => {
1532
- if (!onSelectedRowKeysChange) return;
1674
+ if (!onSelectedRowChange) return;
1533
1675
  const missing = new Set(keys.filter((key) => !rowByKeyRef.current.has(key)));
1534
1676
  if (missing.size > 0) {
1535
1677
  for (let index = 0; index < rows.length && missing.size > 0; index += 1) {
@@ -1546,12 +1688,13 @@ function Table({
1546
1688
  const index = rowIndexByKeyRef.current.get(key);
1547
1689
  return row && index !== void 0 ? [{ row, index }] : [];
1548
1690
  });
1549
- onSelectedRowKeysChange(keys, entries.map(({ row }) => row), entries.map(({ index }) => index));
1691
+ onSelectedRowChange(keys, entries.map(({ row }) => row), entries.map(({ index }) => index));
1550
1692
  });
1551
- const [columnKeys, setColumnKeys] = useControllableKeys(selectedColumnKeys, defaultSelectedColumnKeys, onSelectedColumnKeysChange);
1693
+ const [columnKeys, setColumnKeys] = useControllableKeys(selectedColumnKeys, defaultSelectedColumnKeys, onSelectedColumnChange);
1552
1694
  const [editing, setEditing] = useState4(null);
1553
1695
  const [draft, setDraft] = useState4("");
1554
1696
  const [rowDragPreview, setRowDragPreview] = useState4(null);
1697
+ const [columnDropTarget, setColumnDropTarget] = useState4(null);
1555
1698
  useEffect2(() => {
1556
1699
  if (!loading) hasCompletedLoadRef.current = true;
1557
1700
  }, [loading]);
@@ -1620,6 +1763,8 @@ function Table({
1620
1763
  return () => observer.disconnect();
1621
1764
  }, [activeCustomHeaderLevels, measureCustomHeaderHeight, viewport.width]);
1622
1765
  const [filterEditor, setFilterEditor] = useState4(null);
1766
+ const [filterValues, setFilterValues] = useState4({});
1767
+ const [sortState, setSortState] = useState4(null);
1623
1768
  const [contextMenu, setContextMenu] = useState4(null);
1624
1769
  const [insertCounts, setInsertCounts] = useState4({ before: 1, after: 1 });
1625
1770
  const [confirmAction, setConfirmAction] = useState4(null);
@@ -1632,7 +1777,20 @@ function Table({
1632
1777
  const [hoveredHeaderAction, setHoveredHeaderAction] = useState4(null);
1633
1778
  const [visibleHeaderTooltip, setVisibleHeaderTooltip] = useState4(null);
1634
1779
  const [hoveredCellTooltip, setHoveredCellTooltip] = useState4(null);
1635
- const [displaySortState, setDisplaySortState] = useState4(sortState);
1780
+ const hideDragTooltips = useCallback2(() => {
1781
+ if (headerTooltipTimerRef.current !== null) {
1782
+ clearTimeout(headerTooltipTimerRef.current);
1783
+ headerTooltipTimerRef.current = null;
1784
+ }
1785
+ if (cellTooltipTimerRef.current !== null) {
1786
+ clearTimeout(cellTooltipTimerRef.current);
1787
+ cellTooltipTimerRef.current = null;
1788
+ }
1789
+ pendingHeaderTooltipKeyRef.current = "";
1790
+ pendingCellTooltipKeyRef.current = "";
1791
+ setVisibleHeaderTooltip(null);
1792
+ setHoveredCellTooltip(null);
1793
+ }, []);
1636
1794
  const [editedCellKeys, setEditedCellKeys] = useState4(() => /* @__PURE__ */ new Set());
1637
1795
  const [cellAnnotations, setCellAnnotations] = useState4(() => /* @__PURE__ */ new Map());
1638
1796
  const [annotationType, setAnnotationType] = useState4("corner");
@@ -1644,6 +1802,7 @@ function Table({
1644
1802
  const [pendingDeletedRowKeys, setPendingDeletedRowKeys] = useState4(() => /* @__PURE__ */ new Set());
1645
1803
  const [pendingInserts, setPendingInserts] = useState4(() => []);
1646
1804
  const [insertedRowKeys, setInsertedRowKeys] = useState4(() => /* @__PURE__ */ new Set());
1805
+ const [rowOrderKeys, setRowOrderKeys] = useState4(null);
1647
1806
  const settledPendingInsertIdsRef = useRef2(/* @__PURE__ */ new Set());
1648
1807
  const getDataRowKey = useCallback2((row, index) => {
1649
1808
  return typeof rowKey === "function" ? rowKey(row, index) : rowKey ? row[rowKey] : getDefaultRowKey(row, index);
@@ -1658,7 +1817,7 @@ function Table({
1658
1817
  }
1659
1818
  return sourceRows.findIndex((sourceRow, index) => getDataRowKey(sourceRow, index) === key);
1660
1819
  }, [getDataRowKey, sourceRows]);
1661
- const rows = useMemo(() => {
1820
+ const orderedRows = useMemo(() => {
1662
1821
  let next = sourceRows;
1663
1822
  if (pendingInserts.length > 0) {
1664
1823
  const activeInserts = pendingInserts.filter((insert) => {
@@ -1677,9 +1836,91 @@ function Table({
1677
1836
  insertedOffset += insert.rows.length;
1678
1837
  });
1679
1838
  }
1680
- if (pendingDeletedRowKeys.size === 0) return next;
1681
- return next.filter((row, index) => !pendingDeletedRowKeys.has(getDataRowKey(row, index)));
1682
- }, [getDataRowKey, pendingDeletedRowKeys, pendingInserts, sourceRows]);
1839
+ if (pendingDeletedRowKeys.size > 0) {
1840
+ next = next.filter((row, index) => !pendingDeletedRowKeys.has(getDataRowKey(row, index)));
1841
+ }
1842
+ if (!rowOrderKeys) return next;
1843
+ const rowsByKey = new Map(next.map((row, index) => [getDataRowKey(row, index), row]));
1844
+ const ordered = rowOrderKeys.flatMap((key) => {
1845
+ const row = rowsByKey.get(key);
1846
+ if (!row) return [];
1847
+ rowsByKey.delete(key);
1848
+ return [row];
1849
+ });
1850
+ next.forEach((row, index) => {
1851
+ const key = getDataRowKey(row, index);
1852
+ if (rowsByKey.has(key)) ordered.push(row);
1853
+ });
1854
+ return ordered;
1855
+ }, [getDataRowKey, pendingDeletedRowKeys, pendingInserts, rowOrderKeys, sourceRows]);
1856
+ const activeFilterValues = useMemo(() => Object.fromEntries(
1857
+ Object.entries(filterValues).filter(([, value]) => value.trim() !== "")
1858
+ ), [filterValues]);
1859
+ const filteredRows = useMemo(() => {
1860
+ if (Object.keys(activeFilterValues).length === 0) return orderedRows;
1861
+ if (onFilterChange) return onFilterChange(orderedRows, activeFilterValues);
1862
+ return orderedRows.filter((row, rowIndex) => Object.entries(activeFilterValues).every(([columnKey, query]) => {
1863
+ const column = columns.find((candidate) => candidate.key === columnKey);
1864
+ if (!column) return true;
1865
+ return getDisplayLabel(column, row, rowIndex).toLocaleLowerCase().includes(query.trim().toLocaleLowerCase());
1866
+ }));
1867
+ }, [activeFilterValues, columns, onFilterChange, orderedRows]);
1868
+ const rows = useMemo(() => {
1869
+ if (!sortState) return filteredRows;
1870
+ if (onSortChange) return onSortChange(filteredRows, sortState);
1871
+ const column = columns.find((candidate) => candidate.key === sortState.columnKey);
1872
+ if (!column) return filteredRows;
1873
+ const direction = sortState.direction === "asc" ? 1 : -1;
1874
+ return filteredRows.slice().sort((left, right) => {
1875
+ const leftValue = column.dataIndex === void 0 ? void 0 : left[column.dataIndex];
1876
+ const rightValue = column.dataIndex === void 0 ? void 0 : right[column.dataIndex];
1877
+ const result = typeof leftValue === "number" && typeof rightValue === "number" ? leftValue - rightValue : getDisplayLabel(column, left, 0).localeCompare(getDisplayLabel(column, right, 0), language);
1878
+ return result * direction;
1879
+ });
1880
+ }, [columns, filteredRows, language, onSortChange, sortState]);
1881
+ const resolveSelectionTarget = useCallback2((target) => {
1882
+ if (target == null) return target;
1883
+ const columnIndex = columns.findIndex((column) => column.key === target.columnKey);
1884
+ const rowIndex = target.rowKey !== void 0 ? rows.findIndex((row2, index) => getDataRowKey(row2, index) === target.rowKey) : target.rowIndex ?? -1;
1885
+ const row = rows[rowIndex];
1886
+ if (columnIndex < 0 || !row) return null;
1887
+ return {
1888
+ rowIndex,
1889
+ columnIndex,
1890
+ rowKey: getDataRowKey(row, rowIndex),
1891
+ columnKey: target.columnKey
1892
+ };
1893
+ }, [columns, getDataRowKey, rows]);
1894
+ const resolvedSelectedCell = useMemo(() => resolveSelectionTarget(selectedCell), [resolveSelectionTarget, selectedCell]);
1895
+ const resolvedDefaultSelectedCell = useMemo(() => resolveSelectionTarget(defaultSelectedCell), [defaultSelectedCell, resolveSelectionTarget]);
1896
+ const handleSelectedCellChange = useCallback2((next) => {
1897
+ if (!onSelectedCellChange) return;
1898
+ if (!next) {
1899
+ onSelectedCellChange(null);
1900
+ return;
1901
+ }
1902
+ const row = rows[next.rowIndex];
1903
+ const column = columns[next.columnIndex];
1904
+ const value = row && column?.dataIndex !== void 0 ? row[column.dataIndex] : void 0;
1905
+ onSelectedCellChange({ ...next, value });
1906
+ }, [columns, onSelectedCellChange, rows]);
1907
+ const [selection, setSelection] = useControllableValue(resolvedSelectedCell, resolvedDefaultSelectedCell, handleSelectedCellChange);
1908
+ const [selectionRange, setSelectionRange] = useState4(null);
1909
+ const rangeDragRef = useRef2(null);
1910
+ const applyRowOrder = useCallback2((sourceIndex, targetIndex) => {
1911
+ if (sourceIndex === targetIndex || sourceIndex < 0 || targetIndex < 0 || sourceIndex >= rows.length || targetIndex >= rows.length) return;
1912
+ const nextEntries = rows.map((row, index) => ({ row, key: getDataRowKey(row, index) }));
1913
+ const [entry] = nextEntries.splice(sourceIndex, 1);
1914
+ nextEntries.splice(targetIndex, 0, entry);
1915
+ const nextRows = nextEntries.map(({ row }) => row);
1916
+ setRowOrderKeys(nextEntries.map(({ key }) => key));
1917
+ onRowsReorder?.(nextRows, { sourceIndex, targetIndex });
1918
+ }, [getDataRowKey, onRowsReorder, rows]);
1919
+ const applyColumnOrder = useCallback2((sourceIndex, targetIndex, detail) => {
1920
+ const nextColumns = reorderColumns(sourceColumns, detail);
1921
+ setSourceColumns(nextColumns);
1922
+ onColumnsReorder?.(nextColumns, { sourceIndex, targetIndex, ...detail });
1923
+ }, [onColumnsReorder, sourceColumns]);
1683
1924
  useEffect2(() => {
1684
1925
  if (insertBusy || settledPendingInsertIdsRef.current.size === 0) return;
1685
1926
  setPendingInserts((current) => {
@@ -1921,19 +2162,22 @@ function Table({
1921
2162
  grid: readThemeColor(themeStyles, "--rvg-color-grid", "#e3e6e8"),
1922
2163
  text: readThemeColor(themeStyles, "--rvg-color-text", "#202124"),
1923
2164
  muted: readThemeColor(themeStyles, "--rvg-color-muted", "#5f6368"),
2165
+ icon: readThemeColor(themeStyles, "--rvg-color-icon", "#9aa0a6"),
2166
+ borderStrong: readThemeColor(themeStyles, "--rvg-color-border-strong", "#b8bec4"),
1924
2167
  selection: readThemeColor(themeStyles, "--rvg-color-primary", "#1677ff"),
1925
2168
  selectionFill: readThemeColor(themeStyles, "--rvg-color-selection-fill", "#edf4ff"),
1926
2169
  axisSelectionFill: readThemeColor(themeStyles, "--rvg-color-axis-selection-fill", "#e8f2ff"),
1927
2170
  rowHoverFill: readThemeColor(themeStyles, "--rvg-color-row-hover-fill", "#f6f9fc"),
1928
2171
  editedFill: editedCellHighlightColor ?? readThemeColor(themeStyles, "--rvg-color-edited-fill", "#fff1b8"),
1929
2172
  insertedFill: insertedRowHighlightColor ?? readThemeColor(themeStyles, "--rvg-color-inserted-fill", "#c8ead4"),
2173
+ columnDropTargetFill: readThemeColor(themeStyles, "--rvg-color-column-drop-target-fill", "#eeeeee"),
1930
2174
  stripe: stripedColor ?? readThemeColor(themeStyles, "--rvg-color-stripe", "#fafbfc")
1931
2175
  };
1932
2176
  const scroll = scrollRef.current;
1933
2177
  const rowScrollTop = fixedHeader ? scroll.top : Math.max(0, scroll.top - bodyTop);
1934
2178
  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]);
2179
+ 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 });
2180
+ }, [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
2181
  const scheduleDraw = useCallback2(() => {
1938
2182
  if (frameRef.current === null) frameRef.current = requestAnimationFrame(draw);
1939
2183
  }, [draw]);
@@ -2115,11 +2359,7 @@ function Table({
2115
2359
  }
2116
2360
  };
2117
2361
  }, [scheduleDraw]);
2118
- useEffect2(() => {
2119
- setDisplaySortState(sortState);
2120
- }, [sortState]);
2121
2362
  useEffect2(() => () => {
2122
- if (sortDebounceRef.current !== null) clearTimeout(sortDebounceRef.current);
2123
2363
  if (textFrameRef.current !== null) cancelAnimationFrame(textFrameRef.current);
2124
2364
  if (toastTimerRef.current !== null) clearTimeout(toastTimerRef.current);
2125
2365
  if (verticalScrollbarVisibilityTimerRef.current !== null) clearTimeout(verticalScrollbarVisibilityTimerRef.current);
@@ -2494,12 +2734,12 @@ function Table({
2494
2734
  const localX = clientX - canvasRef.current.getBoundingClientRect().left;
2495
2735
  const columnRight = getDisplayedColumnLeft(columnIndex) + metrics[columnIndex].width;
2496
2736
  const visibleActions = getVisibleHeaderActions(column, metrics[columnIndex].width, columnDraggable, measureHeaderTitleWidth(columnIndex));
2497
- let right = columnRight - (visibleActions.drag ? 18 : 4);
2737
+ let right = columnRight - (visibleActions.drag ? HEADER_ACTION_SLOT_WIDTH : 2);
2498
2738
  if (visibleActions.sort) {
2499
- if (localX >= right - 18 && localX < right) return "sort";
2500
- right -= 18;
2739
+ if (localX >= right - HEADER_ACTION_SLOT_WIDTH && localX < right) return "sort";
2740
+ right -= HEADER_ACTION_SLOT_WIDTH;
2501
2741
  }
2502
- if (visibleActions.filter && localX >= right - 18 && localX < right) return "filter";
2742
+ if (visibleActions.filter && localX >= right - HEADER_ACTION_SLOT_WIDTH && localX < right) return "filter";
2503
2743
  return null;
2504
2744
  }, [columnDraggable, columns, getDisplayedColumnLeft, measureHeaderTitleWidth, metrics]);
2505
2745
  const isHeaderTitleTruncated = useCallback2((columnIndex) => {
@@ -2509,7 +2749,7 @@ function Table({
2509
2749
  const metric = metrics[columnIndex];
2510
2750
  const titleWidth = measureHeaderTitleWidth(columnIndex);
2511
2751
  const visibleActions = getVisibleHeaderActions(column, metric.width, columnDraggable, titleWidth);
2512
- const actionWidth = (Number(visibleActions.drag) + Number(visibleActions.filter) + Number(visibleActions.sort)) * 18;
2752
+ const actionWidth = (Number(visibleActions.drag) + Number(visibleActions.filter) + Number(visibleActions.sort)) * HEADER_ACTION_SLOT_WIDTH;
2513
2753
  return getHeaderTitleRequiredWidth(column, titleWidth) > Math.max(0, metric.width - actionWidth);
2514
2754
  }, [columnDraggable, columns, measureHeaderTitleWidth, metrics]);
2515
2755
  const locateHeaderDragHandle = useCallback2((clientX, columnIndex) => {
@@ -2665,6 +2905,7 @@ function Table({
2665
2905
  }
2666
2906
  const sourceIndex = headerDragCell.startIndex;
2667
2907
  columnDragPreviewRef.current = { sourceIndex, targetIndex: sourceIndex };
2908
+ validColumnDropRef.current = null;
2668
2909
  return {
2669
2910
  type: "table-column",
2670
2911
  sourceIndex,
@@ -2675,13 +2916,26 @@ function Table({
2675
2916
  fixed: columns[headerDragCell.startIndex]?.fixed
2676
2917
  };
2677
2918
  },
2678
- onGenerateDragPreview: ({ nativeSetDragImage, source }) => {
2919
+ onGenerateDragPreview: ({ nativeSetDragImage, source, location }) => {
2679
2920
  const sourceIndex = Number(source.data.sourceIndex);
2921
+ const canvasRect = canvas.getBoundingClientRect();
2922
+ const input = location.current.input;
2923
+ const previewOffset = source.data.type === "table-row" ? {
2924
+ x: Math.max(0, Math.min(canvasRect.width, input.clientX - canvasRect.left)) + 2,
2925
+ y: Math.max(0, Math.min(rowHeight, input.clientY - canvasRect.top - bodyTop - sourceIndex * rowHeight + scrollRef.current.top)) + 2
2926
+ } : {
2927
+ x: Math.max(0, Math.min(metrics[sourceIndex].width, input.clientX - canvasRect.left - getDisplayedColumnLeft(sourceIndex))),
2928
+ y: Math.max(0, Math.min(canvasRect.height, input.clientY - canvasRect.top))
2929
+ };
2680
2930
  setCustomNativeDragPreview({
2681
2931
  nativeSetDragImage,
2932
+ getOffset: () => previewOffset,
2682
2933
  render: ({ container }) => {
2683
- const canvasRect = canvas.getBoundingClientRect();
2684
2934
  if (source.data.type === "table-row") {
2935
+ const frame = document.createElement("div");
2936
+ frame.className = "rvg-row-drag-preview-frame";
2937
+ frame.style.width = `${canvasRect.width + 4}px`;
2938
+ frame.style.height = `${rowHeight + 4}px`;
2685
2939
  const preview2 = document.createElement("div");
2686
2940
  preview2.className = "rvg-row-drag-preview";
2687
2941
  preview2.style.width = `${canvasRect.width}px`;
@@ -2707,10 +2961,14 @@ function Table({
2707
2961
  } else {
2708
2962
  cell.textContent = getCellLabel(sourceIndex, columnIndex);
2709
2963
  }
2964
+ if (selection?.rowKey === rowKey2 && selection.columnKey === column2.key) {
2965
+ cell.classList.add("is-selected");
2966
+ }
2710
2967
  preview2.appendChild(cell);
2711
2968
  });
2712
- container.appendChild(preview2);
2713
- return () => preview2.remove();
2969
+ frame.appendChild(preview2);
2970
+ container.appendChild(frame);
2971
+ return () => frame.remove();
2714
2972
  }
2715
2973
  const preview = document.createElement("div");
2716
2974
  preview.className = "rvg-column-drag-preview";
@@ -2724,6 +2982,29 @@ function Table({
2724
2982
  header.style.height = `${headerHeight}px`;
2725
2983
  header.style.justifyContent = column.align === "right" ? "flex-end" : column.align === "center" ? "center" : "flex-start";
2726
2984
  header.textContent = column.title;
2985
+ const visibleActions = getVisibleHeaderActions(column, columnWidth, columnDraggable, measureHeaderTitleWidth(sourceIndex));
2986
+ let actionRight = 2;
2987
+ if (visibleActions.drag) {
2988
+ const icon = createHeaderDragHandleSvg();
2989
+ icon.classList.add("rvg-preview-header-icon");
2990
+ icon.style.right = `${actionRight}px`;
2991
+ header.appendChild(icon);
2992
+ actionRight += HEADER_ACTION_SLOT_WIDTH;
2993
+ }
2994
+ if (visibleActions.sort) {
2995
+ const direction = sortState?.columnKey === column.key ? sortState.direction : null;
2996
+ const icon = createHeaderSortSvg(direction);
2997
+ icon.classList.add("rvg-preview-header-icon");
2998
+ icon.style.right = `${actionRight}px`;
2999
+ header.appendChild(icon);
3000
+ actionRight += HEADER_ACTION_SLOT_WIDTH;
3001
+ }
3002
+ if (visibleActions.filter) {
3003
+ const icon = createHeaderSearchSvg(Boolean(filterValues[column.key]));
3004
+ icon.classList.add("rvg-preview-header-icon", "rvg-preview-header-search-icon");
3005
+ icon.style.right = `${actionRight}px`;
3006
+ header.appendChild(icon);
3007
+ }
2727
3008
  preview.appendChild(header);
2728
3009
  for (let rowIndex = domRange.rowStart; rowIndex < domRange.rowEnd; rowIndex += 1) {
2729
3010
  const row = rows[rowIndex];
@@ -2752,6 +3033,8 @@ function Table({
2752
3033
  },
2753
3034
  onDragStart: ({ source }) => {
2754
3035
  suppressClickRef.current = true;
3036
+ hideDragTooltips();
3037
+ document.documentElement.classList.add("rvg-is-dragging");
2755
3038
  canvas.style.cursor = "move";
2756
3039
  if (source.data.type === "table-row") {
2757
3040
  const sourceIndex = Number(source.data.sourceIndex);
@@ -2777,15 +3060,46 @@ function Table({
2777
3060
  }
2778
3061
  const targetIndex = Number(target?.targetIndex);
2779
3062
  if (!Number.isInteger(targetIndex) || target?.headerKind !== source.data.headerKind || target?.parentKey !== source.data.parentKey || target?.fixed !== source.data.fixed) {
3063
+ validColumnDropRef.current = null;
3064
+ setColumnDropTarget(null);
2780
3065
  setDragGuide(null);
2781
3066
  return;
2782
3067
  }
2783
3068
  const edge = target?.columnEdge;
2784
3069
  const destinationIndex = getColumnDropIndex(sourceIndex, targetIndex, edge);
2785
3070
  columnDragPreviewRef.current = { sourceIndex, targetIndex: destinationIndex };
3071
+ if (destinationIndex === sourceIndex) {
3072
+ validColumnDropRef.current = null;
3073
+ setColumnDropTarget(null);
3074
+ setDragGuide(null);
3075
+ return;
3076
+ }
3077
+ validColumnDropRef.current = {
3078
+ sourceIndex,
3079
+ targetIndex: destinationIndex,
3080
+ rawTargetIndex: targetIndex,
3081
+ targetKey: String(target.targetKey),
3082
+ headerKind: source.data.headerKind === "group" ? "group" : "column",
3083
+ parentKey: typeof source.data.parentKey === "string" ? source.data.parentKey : void 0,
3084
+ fixed: source.data.fixed === "left" || source.data.fixed === "right" ? source.data.fixed : void 0,
3085
+ columnEdge: edge === "right" ? "right" : "left"
3086
+ };
3087
+ 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);
3088
+ const targetEndIndex = Number.isInteger(target?.targetEndIndex) ? Number(target.targetEndIndex) : targetIndex;
3089
+ const hoveredTargetCell = siblingCells.find((cell) => cell.startIndex === targetIndex && cell.endIndex === targetEndIndex);
3090
+ if (!hoveredTargetCell) {
3091
+ validColumnDropRef.current = null;
3092
+ setColumnDropTarget(null);
3093
+ setDragGuide(null);
3094
+ return;
3095
+ }
3096
+ const highlightedCell = edge === "right" ? hoveredTargetCell : siblingCells.filter((cell) => cell.endIndex < hoveredTargetCell.startIndex).sort((left, right) => right.endIndex - left.endIndex)[0] ?? hoveredTargetCell;
3097
+ setColumnDropTarget({ startIndex: highlightedCell.startIndex, endIndex: highlightedCell.endIndex });
2786
3098
  const targetLeft = Number(target?.targetLeft);
2787
3099
  const targetWidth = Number(target?.targetWidth);
2788
3100
  if (!Number.isFinite(targetLeft) || !Number.isFinite(targetWidth)) {
3101
+ validColumnDropRef.current = null;
3102
+ setColumnDropTarget(null);
2789
3103
  setDragGuide(null);
2790
3104
  return;
2791
3105
  }
@@ -2793,18 +3107,19 @@ function Table({
2793
3107
  setDragGuide(edge === "right" ? targetLeft + targetWidth : targetLeft, guideTop);
2794
3108
  },
2795
3109
  onDrop: ({ source, location }) => {
3110
+ document.documentElement.classList.remove("rvg-is-dragging");
2796
3111
  const target = location.current.dropTargets[0]?.data;
2797
3112
  const sourceIndex = Number(source.data.sourceIndex);
2798
3113
  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));
3114
+ const rawTargetIndex = Number(target?.targetIndex);
3115
+ let targetIndex = rawTargetIndex + (target?.rowEdge === "bottom" ? 1 : 0);
3116
+ if (sourceIndex < targetIndex) targetIndex -= 1;
3117
+ targetIndex = Math.max(0, Math.min(rows.length - 1, targetIndex));
2803
3118
  scrollerRef.current?.classList.add("rvg-row-drop-settling");
2804
3119
  setRowDragPreview(null);
2805
3120
  setRowDragGuide(null);
2806
3121
  canvas.style.cursor = "default";
2807
- if (Number.isInteger(rawTargetIndex2) && sourceIndex !== targetIndex2) onRowOrderChange?.(sourceIndex, targetIndex2);
3122
+ if (Number.isInteger(rawTargetIndex) && sourceIndex !== targetIndex) applyRowOrder(sourceIndex, targetIndex);
2808
3123
  requestAnimationFrame(() => {
2809
3124
  requestAnimationFrame(() => scrollerRef.current?.classList.remove("rvg-row-drop-settling"));
2810
3125
  });
@@ -2813,21 +3128,22 @@ function Table({
2813
3128
  }, 0);
2814
3129
  return;
2815
3130
  }
2816
- const rawTargetIndex = Number(target?.targetIndex);
2817
- const targetIndex = columnDragPreviewRef.current?.targetIndex ?? (Number.isInteger(rawTargetIndex) ? getColumnDropIndex(sourceIndex, rawTargetIndex, target?.columnEdge) : rawTargetIndex);
3131
+ const validDrop = validColumnDropRef.current;
2818
3132
  canvas.style.cursor = "default";
2819
3133
  setDragGuide(null);
3134
+ setColumnDropTarget(null);
2820
3135
  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,
3136
+ validColumnDropRef.current = null;
3137
+ if (validDrop) {
3138
+ applyColumnOrder(
3139
+ validDrop.sourceIndex - utilityColumnCount,
3140
+ validDrop.targetIndex - utilityColumnCount,
2825
3141
  {
2826
- type: source.data.headerKind === "group" ? "group" : "column",
3142
+ type: validDrop.headerKind,
2827
3143
  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)
3144
+ targetKey: validDrop.targetKey,
3145
+ parentKey: validDrop.parentKey,
3146
+ placement: getColumnDropPlacement(validDrop.sourceIndex, validDrop.rawTargetIndex, validDrop.columnEdge)
2831
3147
  }
2832
3148
  );
2833
3149
  }
@@ -2839,6 +3155,7 @@ function Table({
2839
3155
  const cleanupDrop = dropTargetForElements({
2840
3156
  element: canvas,
2841
3157
  canDrop: ({ source }) => source.data.type === "table-column" || source.data.type === "table-row",
3158
+ getDropEffect: () => "move",
2842
3159
  getData: ({ input, element, source }) => {
2843
3160
  if (source.data.type === "table-row") {
2844
3161
  const targetIndex2 = locateRow(input.clientY);
@@ -2847,7 +3164,8 @@ function Table({
2847
3164
  const rowEdge = input.clientY - canvas.getBoundingClientRect().top < rowTop + rowHeight / 2 ? "top" : "bottom";
2848
3165
  return attachClosestEdge({ targetIndex: targetIndex2, rowEdge }, { element, input, allowedEdges: ["top", "bottom"] });
2849
3166
  }
2850
- const targetCell = locateHeaderCell(input.clientX, input.clientY);
3167
+ const hoveredColumnIndex = locateColumn(input.clientX);
3168
+ 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
3169
  if (!targetCell) return { targetIndex: -1 };
2852
3170
  let targetIndex = targetCell.startIndex;
2853
3171
  const targetFixed = columns[targetCell.startIndex]?.fixed;
@@ -2864,6 +3182,7 @@ function Table({
2864
3182
  const columnEdge = input.clientX - canvas.getBoundingClientRect().left < left + targetWidth / 2 ? "left" : "right";
2865
3183
  return attachClosestEdge({
2866
3184
  targetIndex,
3185
+ targetEndIndex: targetCell.endIndex,
2867
3186
  targetKey: targetCell.key,
2868
3187
  parentKey: targetCell.parentKey,
2869
3188
  headerKind,
@@ -2875,10 +3194,11 @@ function Table({
2875
3194
  }
2876
3195
  });
2877
3196
  return () => {
3197
+ document.documentElement.classList.remove("rvg-is-dragging");
2878
3198
  cleanupDrag();
2879
3199
  cleanupDrop();
2880
3200
  };
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]);
3201
+ }, [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
3202
  const locateCell = useCallback2((clientX, clientY) => {
2883
3203
  const canvas = canvasRef.current;
2884
3204
  if (!canvas) return null;
@@ -3001,9 +3321,15 @@ function Table({
3001
3321
  scrollRef.current = { left: scroller.scrollLeft, top: scroller.scrollTop };
3002
3322
  scheduleDraw();
3003
3323
  }, [bodyTop, columns, fixedHeader, fixedWidth, getCellDisplayWidth, getCellSpan, getScrollableColumnLeft, metrics, rightFixedWidth, rowHeight, scheduleDraw, viewport.width]);
3324
+ const isCellEditable = useCallback2((cell) => {
3325
+ const column = columns[cell.columnIndex];
3326
+ const row = rows[cell.rowIndex];
3327
+ if (!column || !row || column.dataIndex === void 0 || column.rowNumber || column.rowSelection || column.rowDragHandle) return false;
3328
+ return typeof column.editable === "function" ? column.editable(row[column.dataIndex], row, cell.rowIndex) : column.editable === true;
3329
+ }, [columns, rows]);
3004
3330
  const beginEdit = useCallback2((cell) => {
3005
3331
  const column = columns[cell.columnIndex];
3006
- if (!column?.editable || column.dataIndex === void 0) return;
3332
+ if (!isCellEditable(cell) || column.dataIndex === void 0) return;
3007
3333
  const value = rows[cell.rowIndex]?.[column.dataIndex];
3008
3334
  setDraft(value == null ? "" : String(value));
3009
3335
  const scroller = scrollerRef.current;
@@ -3045,7 +3371,7 @@ function Table({
3045
3371
  return;
3046
3372
  }
3047
3373
  setEditing(cell);
3048
- }, [columns, fixedHeader, fixedWidth, getCellDisplayWidth, getCellSpan, getScrollableColumnLeft, metrics, rightFixedWidth, rowHeight, rows, scheduleDraw, viewport.width]);
3374
+ }, [columns, fixedHeader, fixedWidth, getCellDisplayWidth, getCellSpan, getScrollableColumnLeft, isCellEditable, metrics, rightFixedWidth, rowHeight, rows, scheduleDraw, viewport.width]);
3049
3375
  const getCellEditKey = (cell) => `${typeof cell.rowKey}:${String(cell.rowKey)}\0${cell.columnKey}`;
3050
3376
  const markCellEdited = (cell, previousValue, nextValue) => {
3051
3377
  const key = getCellEditKey(cell);
@@ -3073,6 +3399,10 @@ function Table({
3073
3399
  };
3074
3400
  const commitEdit = useCallback2((nextDraft, close = true) => {
3075
3401
  if (!editing) return;
3402
+ if (!isCellEditable(editing)) {
3403
+ setEditing(null);
3404
+ return;
3405
+ }
3076
3406
  const committedDraft = nextDraft ?? draft;
3077
3407
  const column = columns[editing.columnIndex];
3078
3408
  const row = rows[editing.rowIndex];
@@ -3087,7 +3417,7 @@ function Table({
3087
3417
  onCellChange?.({ ...editing, row, value: committedValue, previousValue });
3088
3418
  }
3089
3419
  if (close) setEditing(null);
3090
- }, [columns, draft, editing, onCellChange, rows]);
3420
+ }, [columns, draft, editing, isCellEditable, onCellChange, rows]);
3091
3421
  const renderEditor = () => {
3092
3422
  if (!editing || !editorStyle) return null;
3093
3423
  const column = columns[editing.columnIndex];
@@ -3285,19 +3615,14 @@ function Table({
3285
3615
  setResizeGuideTop(0);
3286
3616
  }, []);
3287
3617
  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]);
3618
+ setSortState(next);
3619
+ }, []);
3295
3620
  const renderHeaderIcons = (columnIndex, left) => {
3296
3621
  const column = columns[columnIndex];
3297
3622
  if (column.rowSelection || column.rowDragHandle || column.rowNumber) return null;
3298
3623
  const top = headerLeafTop + (headerLeafHeight - 14) / 2;
3299
3624
  const visibleActions = getVisibleHeaderActions(column, metrics[columnIndex].width, columnDraggable, measureHeaderTitleWidth(columnIndex));
3300
- let right = left + metrics[columnIndex].width - (visibleActions.drag ? 18 : 4);
3625
+ let right = left + metrics[columnIndex].width - (visibleActions.drag ? HEADER_ACTION_SLOT_WIDTH : 2);
3301
3626
  const icons = [];
3302
3627
  if (visibleActions.drag) {
3303
3628
  const hovered = hoveredHeaderAction?.columnIndex === columnIndex && hoveredHeaderAction.action === "drag";
@@ -3307,11 +3632,11 @@ function Table({
3307
3632
  }
3308
3633
  if (visibleActions.sort) {
3309
3634
  const hovered = hoveredHeaderAction?.columnIndex === columnIndex && hoveredHeaderAction.action === "sort";
3310
- const direction = displaySortState?.columnKey === column.key ? displaySortState.direction : null;
3635
+ const direction = sortState?.columnKey === column.key ? sortState.direction : null;
3311
3636
  icons.push(
3312
3637
  /* @__PURE__ */ jsx7(HeaderSortIcon, { className: `rvg-header-icon${hovered ? " is-hovered" : ""}`, style: { left: right - 16, top }, direction }, "sort")
3313
3638
  );
3314
- right -= 18;
3639
+ right -= HEADER_ACTION_SLOT_WIDTH;
3315
3640
  }
3316
3641
  if (visibleActions.filter) {
3317
3642
  const hovered = hoveredHeaderAction?.columnIndex === columnIndex && hoveredHeaderAction.action === "filter";
@@ -3338,7 +3663,7 @@ function Table({
3338
3663
  if (context) context.font = "600 13px Inter, ui-sans-serif, system-ui, sans-serif";
3339
3664
  const titleWidth = context?.measureText(column.title).width ?? column.title.length * 13;
3340
3665
  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;
3666
+ const actionWidth = visibleActions ? (Number(visibleActions.drag) + Number(visibleActions.filter) + Number(visibleActions.sort)) * HEADER_ACTION_SLOT_WIDTH : columnDraggable ? HEADER_ACTION_SLOT_WIDTH : 0;
3342
3667
  const contentWidth2 = Math.max(0, cellWidth - actionWidth);
3343
3668
  const maxTextWidth = Math.max(0, contentWidth2 - 20);
3344
3669
  const textWidth = Math.min(titleWidth, maxTextWidth);
@@ -3357,7 +3682,7 @@ function Table({
3357
3682
  anchorLeft = anchorX;
3358
3683
  label = labels.dragColumn;
3359
3684
  } else if (action === "sort") {
3360
- const direction = displaySortState?.columnKey === column.key ? displaySortState.direction : null;
3685
+ const direction = sortState?.columnKey === column.key ? sortState.direction : null;
3361
3686
  label = direction === "asc" ? labels.sortAsc : direction === "desc" ? labels.sortDesc : labels.sortBoth;
3362
3687
  anchorX = cellLeft + cellWidth - 22;
3363
3688
  anchorLeft = anchorX;
@@ -3377,7 +3702,7 @@ function Table({
3377
3702
  label,
3378
3703
  placement
3379
3704
  };
3380
- }, [baseHeaderHeight, columnDraggable, columns, displaySortState, filterValues, fixedHeader, getDisplayedColumnLeft, getHeaderCellLeft, getHeaderCellWidth, headerDepth, headerRowHeights, headerRowOffsets, labels, metrics, scrollPosition.top, viewport.width]);
3705
+ }, [baseHeaderHeight, columnDraggable, columns, filterValues, fixedHeader, getDisplayedColumnLeft, getHeaderCellLeft, getHeaderCellWidth, headerDepth, headerRowHeights, headerRowOffsets, labels, metrics, scrollPosition.top, sortState, viewport.width]);
3381
3706
  const isPointerOnHeaderTitle = useCallback2((clientX, clientY, cell) => {
3382
3707
  const canvas = canvasRef.current;
3383
3708
  if (!canvas) return false;
@@ -3392,7 +3717,7 @@ function Table({
3392
3717
  if (context) context.font = "600 13px Inter, ui-sans-serif, system-ui, sans-serif";
3393
3718
  const titleWidth = context?.measureText(column.title).width ?? column.title.length * 13;
3394
3719
  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;
3720
+ const actionWidth = visibleActions ? (Number(visibleActions.drag) + Number(visibleActions.filter) + Number(visibleActions.sort)) * HEADER_ACTION_SLOT_WIDTH : columnDraggable ? HEADER_ACTION_SLOT_WIDTH : 0;
3396
3721
  const contentWidth2 = Math.max(0, cellWidth - actionWidth);
3397
3722
  const textWidth = Math.min(titleWidth, Math.max(0, contentWidth2 - 20));
3398
3723
  const textLeft = column.align === "right" ? cellLeft + contentWidth2 - 10 - textWidth : column.align === "center" ? cellLeft + contentWidth2 / 2 - textWidth / 2 : cellLeft + 10;
@@ -3632,7 +3957,9 @@ function Table({
3632
3957
  });
3633
3958
  setPendingInserts((current) => [...current, pending]);
3634
3959
  try {
3635
- await Promise.resolve(onInsertRows({ rowIndex: sourceRowIndex >= 0 ? sourceRowIndex : cell.rowIndex, row, position, count, insertedRows }));
3960
+ const nextRows = sourceRows.slice();
3961
+ nextRows.splice(insertIndex, 0, ...insertedRows);
3962
+ await Promise.resolve(onInsertRows(nextRows, insertedRows));
3636
3963
  await waitForPaint();
3637
3964
  setInsertBusy(false);
3638
3965
  } catch (error) {
@@ -3659,28 +3986,10 @@ function Table({
3659
3986
  const currentScrollTop = scrollRef.current.top;
3660
3987
  const top = (fixedHeader ? 0 : bodyTop) + rowIndex * rowHeight - currentScrollTop;
3661
3988
  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
- );
3989
+ return null;
3672
3990
  }
3673
3991
  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
- );
3992
+ return null;
3684
3993
  }
3685
3994
  if (column.renderCell && !(editing?.rowIndex === rowIndex && editing.columnIndex === columnIndex)) {
3686
3995
  const row = rows[rowIndex];
@@ -3707,6 +4016,7 @@ function Table({
3707
4016
  };
3708
4017
  const renderHeaderTitle = (columnIndex, left) => {
3709
4018
  const column = columns[columnIndex];
4019
+ const isDropTarget = Boolean(columnDropTarget && columnIndex >= columnDropTarget.startIndex && columnIndex <= columnDropTarget.endIndex);
3710
4020
  const currentScrollTop = scrollRef.current.top;
3711
4021
  const headerTop = fixedHeader ? 0 : -currentScrollTop;
3712
4022
  const leafTop = headerTop + headerLeafTop;
@@ -3714,7 +4024,7 @@ function Table({
3714
4024
  return /* @__PURE__ */ jsx7(
3715
4025
  "div",
3716
4026
  {
3717
- className: "rvg-header-title",
4027
+ className: `rvg-header-title${isDropTarget ? " is-drop-target" : ""}`,
3718
4028
  style: { left, top: headerTop, width: metrics[columnIndex].width, height: headerHeight, justifyContent: "center", padding: 0, textAlign: "center" },
3719
4029
  children: /* @__PURE__ */ jsx7("span", { children: column.title })
3720
4030
  },
@@ -3737,11 +4047,11 @@ function Table({
3737
4047
  }
3738
4048
  if (column.renderHeader) {
3739
4049
  const visibleActions = getVisibleHeaderActions(column, metrics[columnIndex].width, columnDraggable, measureHeaderTitleWidth(columnIndex));
3740
- const actionWidth = (Number(visibleActions.drag) + Number(visibleActions.filter) + Number(visibleActions.sort)) * 18;
4050
+ const actionWidth = (Number(visibleActions.drag) + Number(visibleActions.filter) + Number(visibleActions.sort)) * HEADER_ACTION_SLOT_WIDTH;
3741
4051
  return /* @__PURE__ */ jsx7(
3742
4052
  "div",
3743
4053
  {
3744
- className: "rvg-header-title is-custom",
4054
+ className: `rvg-header-title is-custom${isDropTarget ? " is-drop-target" : ""}`,
3745
4055
  "data-level": headerDepth - 1,
3746
4056
  "data-measure-key": column.key,
3747
4057
  style: {
@@ -3782,11 +4092,12 @@ function Table({
3782
4092
  const left = fixedSide === "left" ? leftFixedOffsets.get(cell.startIndex) ?? 0 : fixedSide === "right" ? viewport.width - (rightFixedOffsets.get(cell.startIndex) ?? 0) - startMetric.width : getScrollableColumnLeft(cell.startIndex);
3783
4093
  const width2 = cell.leaf ? startMetric.width : getCellDisplayWidth(cell.startIndex, cell.endIndex - cell.startIndex + 1);
3784
4094
  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;
4095
+ const actionWidth = visibleActions ? (Number(visibleActions.drag) + Number(visibleActions.filter) + Number(visibleActions.sort)) * HEADER_ACTION_SLOT_WIDTH : columnDraggable && !cell.leaf ? HEADER_ACTION_SLOT_WIDTH : 0;
4096
+ const isDropTarget = Boolean(columnDropTarget && cell.endIndex >= columnDropTarget.startIndex && cell.startIndex <= columnDropTarget.endIndex);
3786
4097
  return /* @__PURE__ */ jsxs7(
3787
4098
  "div",
3788
4099
  {
3789
- className: `rvg-header-title rvg-header-group${column.renderHeader ? " is-custom" : ""}${cell.leaf ? " is-leaf" : ""}`,
4100
+ className: `rvg-header-title rvg-header-group${column.renderHeader ? " is-custom" : ""}${cell.leaf ? " is-leaf" : ""}${isDropTarget ? " is-drop-target" : ""}`,
3790
4101
  "data-level": cell.level,
3791
4102
  "data-measure-key": `group:${cell.key}:${cell.level}`,
3792
4103
  style: {
@@ -3842,6 +4153,7 @@ function Table({
3842
4153
  })();
3843
4154
  const selectionOutlineStyle = (() => {
3844
4155
  if (!selection || selectionRange) return null;
4156
+ if (rowDragPreview?.sourceIndex === selection.rowIndex) return null;
3845
4157
  const row = rows[selection.rowIndex];
3846
4158
  const metric = metrics[selection.columnIndex];
3847
4159
  const column = columns[selection.columnIndex];
@@ -3851,10 +4163,11 @@ function Table({
3851
4163
  const rawRight = rawLeft + getCellDisplayWidth(selection.columnIndex, span?.colSpan ?? 1);
3852
4164
  const rawTop = bodyTop + selection.rowIndex * rowHeight - scrollPosition.top;
3853
4165
  const rawBottom = rawTop + rowHeight * (span?.rowSpan ?? 1);
4166
+ const dragOffset = getRowDragOffset(selection.rowIndex);
3854
4167
  const horizontalStart = column.fixed === void 0 ? fixedWidth : 0;
3855
4168
  const horizontalEnd = column.fixed === void 0 ? viewport.width - rightFixedWidth : viewport.width;
3856
4169
  const verticalStart = fixedHeader ? bodyTop : 0;
3857
- if (rawRight <= horizontalStart || rawLeft >= horizontalEnd || rawBottom <= verticalStart || rawTop >= renderHeight) return null;
4170
+ if (rawRight <= horizontalStart || rawLeft >= horizontalEnd || rawBottom + dragOffset <= verticalStart || rawTop + dragOffset >= renderHeight) return null;
3858
4171
  const borderWidth = 2;
3859
4172
  const left = Math.round(Math.max(rawLeft - borderWidth, horizontalStart));
3860
4173
  const right = Math.round(Math.min(rawRight + 1, horizontalEnd, viewport.width));
@@ -3864,7 +4177,8 @@ function Table({
3864
4177
  left,
3865
4178
  top,
3866
4179
  width: Math.max(0, right - left),
3867
- height: Math.max(0, bottom - top)
4180
+ height: Math.max(0, bottom - top),
4181
+ transform: `translateY(${dragOffset}px)`
3868
4182
  };
3869
4183
  })();
3870
4184
  const selectionEdgeIndicator = getSelectionEdgeIndicator(scrollPosition.left, scrollPosition.top);
@@ -4005,6 +4319,77 @@ function Table({
4005
4319
  `summary:${column.key}`
4006
4320
  );
4007
4321
  };
4322
+ useImperativeHandle(ref, () => {
4323
+ const resolveDataCell = (target) => {
4324
+ const cell = resolveSelectionTarget(target);
4325
+ if (!cell) return null;
4326
+ const column = columns[cell.columnIndex];
4327
+ if (column.rowNumber || column.rowSelection || column.rowDragHandle) return null;
4328
+ const span = getCellSpan(cell.rowIndex, cell.columnIndex);
4329
+ if (!span) return cell;
4330
+ return {
4331
+ rowIndex: span.rowIndex,
4332
+ columnIndex: span.columnIndex,
4333
+ rowKey: getRowKey(rows[span.rowIndex], span.rowIndex),
4334
+ columnKey: columns[span.columnIndex].key
4335
+ };
4336
+ };
4337
+ return {
4338
+ focus: () => scrollerRef.current?.focus({ preventScroll: true }),
4339
+ scrollToCell: (target) => {
4340
+ const cell = resolveDataCell(target);
4341
+ if (!cell) return false;
4342
+ revealCell(cell);
4343
+ return true;
4344
+ },
4345
+ getSelectedCell: () => {
4346
+ if (!selection) return null;
4347
+ const cell = resolveDataCell(selection);
4348
+ if (!cell) return null;
4349
+ const column = columns[cell.columnIndex];
4350
+ const row = rows[cell.rowIndex];
4351
+ return { ...cell, row, column, value: column.dataIndex === void 0 ? void 0 : row[column.dataIndex] };
4352
+ },
4353
+ setSelectedCell: (target) => {
4354
+ const cell = target === null ? null : resolveDataCell(target);
4355
+ if (target !== null && !cell) return false;
4356
+ setSelectionRange(null);
4357
+ setSelection(cell);
4358
+ return true;
4359
+ },
4360
+ getSelectedRowKeys: () => [...rowKeys],
4361
+ getSelectedRows: () => rows.filter((row, index) => selectedRowKeySet.has(getRowKey(row, index))),
4362
+ setSelectedRowKeys: (keys) => {
4363
+ if (!rowSelection) return;
4364
+ const next = [...new Set(keys)];
4365
+ setRowKeys(rowSelectionMode === "single" ? next.slice(0, 1) : next);
4366
+ },
4367
+ getSelectedColumnKeys: () => [...columnKeys],
4368
+ setSelectedColumnKeys: (keys) => {
4369
+ if (!columnSelection) return;
4370
+ const next = [...new Set(keys)].filter((key) => columns.some((column) => column.key === key && !column.rowNumber && !column.rowSelection && !column.rowDragHandle));
4371
+ setColumnKeys(typeof columnSelection === "object" && columnSelection.mode === "single" ? next.slice(0, 1) : next);
4372
+ },
4373
+ clearSelection: () => {
4374
+ setSelectionRange(null);
4375
+ setSelection(null);
4376
+ setRowKeys([]);
4377
+ setColumnKeys([]);
4378
+ rowAnchorRef.current = null;
4379
+ setEditing(null);
4380
+ },
4381
+ startEdit: (target) => {
4382
+ const cell = target ? resolveDataCell(target) : selection ? resolveDataCell(selection) : null;
4383
+ if (!cell || !isCellEditable(cell)) return false;
4384
+ setSelectionRange(null);
4385
+ setSelection(cell);
4386
+ beginEdit(cell);
4387
+ return true;
4388
+ },
4389
+ commitEdit: () => commitEdit(),
4390
+ cancelEdit: () => setEditing(null)
4391
+ };
4392
+ });
4008
4393
  const rootStyle = {
4009
4394
  width,
4010
4395
  height: autoHeight ? effectiveHeight : height,
@@ -4023,6 +4408,10 @@ function Table({
4023
4408
  className: "rvg-canvas",
4024
4409
  style: { width: viewport.width, height: renderHeight },
4025
4410
  onMouseMove: (event) => {
4411
+ if (document.documentElement.classList.contains("rvg-is-dragging")) {
4412
+ hideDragTooltips();
4413
+ return;
4414
+ }
4026
4415
  if (columnDragRef.current) return;
4027
4416
  const rect = event.currentTarget.getBoundingClientRect();
4028
4417
  const localY = event.clientY - rect.top;
@@ -4125,6 +4514,14 @@ function Table({
4125
4514
  }
4126
4515
  },
4127
4516
  onPointerDown: (event) => {
4517
+ const headerDragCell = columnDraggable ? locateHeaderCell(event.clientX, event.clientY) : null;
4518
+ const columnIndex = locateColumn(event.clientX);
4519
+ const rowDragHandle = rowDraggable && columnIndex >= 0 && Boolean(columns[columnIndex].rowDragHandle) && locateRow(event.clientY) >= 0;
4520
+ if (headerDragCell && locateHeaderCellDragHandle(event.clientX, headerDragCell) || rowDragHandle) {
4521
+ hideDragTooltips();
4522
+ document.documentElement.classList.add("rvg-is-dragging");
4523
+ event.currentTarget.style.cursor = "move";
4524
+ }
4128
4525
  handleColumnPointerDown(event);
4129
4526
  beginRangeDrag(event);
4130
4527
  },
@@ -4133,10 +4530,12 @@ function Table({
4133
4530
  updateRangeDrag(event);
4134
4531
  },
4135
4532
  onPointerUp: (event) => {
4533
+ document.documentElement.classList.remove("rvg-is-dragging");
4136
4534
  handleColumnPointerUp(event);
4137
4535
  endRangeDrag(event);
4138
4536
  },
4139
4537
  onPointerCancel: (event) => {
4538
+ document.documentElement.classList.remove("rvg-is-dragging");
4140
4539
  handleColumnPointerUp(event);
4141
4540
  endRangeDrag(event);
4142
4541
  },
@@ -4155,7 +4554,7 @@ function Table({
4155
4554
  const action = locateHeaderAction(event.clientX, columnIndex);
4156
4555
  if (action === "sort") {
4157
4556
  const columnKey = columns[columnIndex].key;
4158
- const next = displaySortState?.columnKey !== columnKey ? { columnKey, direction: "asc" } : displaySortState.direction === "asc" ? { columnKey, direction: "desc" } : null;
4557
+ const next = sortState?.columnKey !== columnKey ? { columnKey, direction: "asc" } : sortState.direction === "asc" ? { columnKey, direction: "desc" } : null;
4159
4558
  queueSortStateChange(next);
4160
4559
  return;
4161
4560
  }
@@ -4189,6 +4588,10 @@ function Table({
4189
4588
  }
4190
4589
  if (event.detail > 1) return;
4191
4590
  clickedCellRef.current = cell;
4591
+ const column = columns[cell.columnIndex];
4592
+ const row = rows[cell.rowIndex];
4593
+ onCellClick?.({ ...cell, row, column, value: column.dataIndex === void 0 ? void 0 : row[column.dataIndex] }, event);
4594
+ if (event.defaultPrevented) return;
4192
4595
  setSelectionRange(null);
4193
4596
  scrollerRef.current?.focus({ preventScroll: true });
4194
4597
  setSelection(cell);
@@ -4199,7 +4602,12 @@ function Table({
4199
4602
  },
4200
4603
  onDoubleClick: (event) => {
4201
4604
  const cell = clickedCellRef.current ?? locateCell(event.clientX, event.clientY);
4202
- if (cell) beginEdit(cell);
4605
+ if (!cell) return;
4606
+ const column = columns[cell.columnIndex];
4607
+ if (column.rowNumber || column.rowSelection || column.rowDragHandle) return;
4608
+ const row = rows[cell.rowIndex];
4609
+ onCellDoubleClick?.({ ...cell, row, column, value: column.dataIndex === void 0 ? void 0 : row[column.dataIndex] }, event);
4610
+ if (!event.defaultPrevented) beginEdit(cell);
4203
4611
  },
4204
4612
  onContextMenu: handleContextMenu
4205
4613
  }
@@ -4212,7 +4620,7 @@ function Table({
4212
4620
  className: "rvg-text-scroll-content",
4213
4621
  style: { left: -fixedWidth, width: contentWidth, transform: `translateX(${-currentScroll.left}px)` },
4214
4622
  children: [
4215
- columns.map((column, columnIndex) => column.fixed || columnIndex < utilityColumnCount || headerDepth > 1 ? null : columnIndex >= domRange.columnStart && columnIndex < domRange.columnEnd ? /* @__PURE__ */ jsx7("span", { children: renderHeaderTitle(columnIndex, getScrollableColumnLeft(columnIndex)) }, column.key) : null),
4623
+ columns.map((column, columnIndex) => column.fixed || columnIndex >= utilityColumnCount && headerDepth > 1 ? null : columnIndex >= domRange.columnStart && columnIndex < domRange.columnEnd ? /* @__PURE__ */ jsx7("span", { children: renderHeaderTitle(columnIndex, getScrollableColumnLeft(columnIndex)) }, column.key) : null),
4216
4624
  headerCells.map((cell) => {
4217
4625
  if (columns[cell.startIndex]?.fixed) return null;
4218
4626
  if (cell.endIndex < domRange.columnStart || cell.startIndex >= domRange.columnEnd) return null;
@@ -4230,7 +4638,7 @@ function Table({
4230
4638
  const left = column.fixed === "left" ? leftFixedOffsets.get(columnIndex) ?? 0 : viewport.width - (rightFixedOffsets.get(columnIndex) ?? 0) - metrics[columnIndex].width;
4231
4639
  return renderCellText(rowIndex, columnIndex, left);
4232
4640
  })) }),
4233
- columns.map((column, columnIndex) => columnIndex < utilityColumnCount || headerDepth === 1 && (column.fixed === "left" || column.fixed === "right") ? renderHeaderTitle(columnIndex, column.fixed === "right" ? viewport.width - (rightFixedOffsets.get(columnIndex) ?? 0) - metrics[columnIndex].width : column.fixed === "left" ? leftFixedOffsets.get(columnIndex) ?? 0 : getScrollableColumnLeft(columnIndex)) : null),
4641
+ columns.map((column, columnIndex) => column.fixed && (columnIndex < utilityColumnCount || headerDepth === 1) ? renderHeaderTitle(columnIndex, column.fixed === "right" ? viewport.width - (rightFixedOffsets.get(columnIndex) ?? 0) - metrics[columnIndex].width : column.fixed === "left" ? leftFixedOffsets.get(columnIndex) ?? 0 : getScrollableColumnLeft(columnIndex)) : null),
4234
4642
  headerCells.map((cell) => columns[cell.startIndex]?.fixed ? renderGroupedHeaderCell(cell) : null),
4235
4643
  fixedWidth > 0 && currentScroll.left > 0 && /* @__PURE__ */ jsx7("div", { className: "rvg-text-fixed-shadow is-left", style: { left: fixedWidth } }),
4236
4644
  rightFixedWidth > 0 && currentScroll.left < contentWidth - viewport.width && /* @__PURE__ */ jsx7("div", { className: "rvg-text-fixed-shadow is-right", style: { right: rightFixedWidth } }),
@@ -4447,7 +4855,7 @@ function Table({
4447
4855
  const affectedRows = selectedIndices.includes(cell.rowIndex) ? selectedIndices : [cell.rowIndex];
4448
4856
  const cellContext = { ...cell, row, column, value: dataIndex === void 0 ? void 0 : row[dataIndex] };
4449
4857
  return renderConfiguredContextMenu(resolveContextMenuSection(customContextMenuConfig?.cell, DEFAULT_CELL_CONTEXT_MENU), cellContext, (item, override) => {
4450
- if (item === "edit") return renderContextMenuButton("cell-edit", cellContext, labels.edit, () => beginEdit(cell), override, { disabled: !column.editable, icon: /* @__PURE__ */ jsx7(ContextMenuIcon, { type: "edit" }) });
4858
+ if (item === "edit") return renderContextMenuButton("cell-edit", cellContext, labels.edit, () => beginEdit(cell), override, { disabled: !isCellEditable(cell), icon: /* @__PURE__ */ jsx7(ContextMenuIcon, { type: "edit" }) });
4451
4859
  if (item === "copy") return renderContextMenuButton("cell-copy", cellContext, labels.copyContent, async () => {
4452
4860
  try {
4453
4861
  await navigator.clipboard.writeText(getCellLabel(cell.rowIndex, cell.columnIndex));
@@ -4505,12 +4913,12 @@ function Table({
4505
4913
  /* @__PURE__ */ jsx7("span", { children: labels.rowUnit })
4506
4914
  ] });
4507
4915
  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" }) });
4916
+ if (!rowDraggable) return null;
4917
+ 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
4918
  }
4511
4919
  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" }) });
4920
+ if (!rowDraggable) return null;
4921
+ 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
4922
  }
4515
4923
  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
4924
  return null;
@@ -4542,21 +4950,30 @@ function Table({
4542
4950
  await Promise.resolve(onCellChange?.({ ...confirmAction.cell, row: confirmAction.row, previousValue: confirmAction.previousValue, value: "" }));
4543
4951
  } else {
4544
4952
  const deletingKeys = confirmAction.rows.map(({ row, rowIndex }) => getDataRowKey(row, rowIndex));
4953
+ const deletedRows = confirmAction.rows.map(({ row }) => row);
4954
+ const deletingKeySet = new Set(deletingKeys);
4955
+ const nextRows = sourceRows.filter((row, rowIndex) => !deletingKeySet.has(getDataRowKey(row, rowIndex)));
4545
4956
  setConfirmAction(null);
4546
4957
  setConfirmLoading(false);
4958
+ setPendingDeletedRowKeys((current) => {
4959
+ const next = new Set(current);
4960
+ deletingKeys.forEach((key) => next.add(key));
4961
+ return next;
4962
+ });
4547
4963
  if (onDeleteRows) {
4548
4964
  setDeleteBusy(true);
4549
4965
  try {
4550
- await Promise.resolve(onDeleteRows({ rows: confirmAction.rows, viewportRange: domRange }));
4966
+ await Promise.resolve(onDeleteRows(nextRows, deletedRows));
4967
+ } catch (error) {
4968
+ setPendingDeletedRowKeys((current) => {
4969
+ const next = new Set(current);
4970
+ deletingKeys.forEach((key) => next.delete(key));
4971
+ return next;
4972
+ });
4973
+ throw error;
4551
4974
  } finally {
4552
4975
  setDeleteBusy(false);
4553
4976
  }
4554
- } else {
4555
- setPendingDeletedRowKeys((current) => {
4556
- const next = new Set(current);
4557
- deletingKeys.forEach((key) => next.add(key));
4558
- return next;
4559
- });
4560
4977
  }
4561
4978
  return;
4562
4979
  }
@@ -4629,7 +5046,7 @@ function Table({
4629
5046
  const next = { ...filterValues };
4630
5047
  if (filterEditor.draft) next[key] = filterEditor.draft;
4631
5048
  else delete next[key];
4632
- onFilterValuesChange?.(next);
5049
+ setFilterValues(next);
4633
5050
  setFilterEditor(null);
4634
5051
  }
4635
5052
  }
@@ -4640,7 +5057,7 @@ function Table({
4640
5057
  const key = columns[filterEditor.columnIndex].key;
4641
5058
  const next = { ...filterValues };
4642
5059
  delete next[key];
4643
- onFilterValuesChange?.(next);
5060
+ setFilterValues(next);
4644
5061
  setFilterEditor(null);
4645
5062
  }, children: labels.reset }),
4646
5063
  /* @__PURE__ */ jsx7("button", { type: "button", className: "rvg-filter-confirm", onClick: () => {
@@ -4648,7 +5065,7 @@ function Table({
4648
5065
  const next = { ...filterValues };
4649
5066
  if (filterEditor.draft) next[key] = filterEditor.draft;
4650
5067
  else delete next[key];
4651
- onFilterValuesChange?.(next);
5068
+ setFilterValues(next);
4652
5069
  setFilterEditor(null);
4653
5070
  }, children: labels.confirm })
4654
5071
  ] })
@@ -4678,6 +5095,7 @@ function Table({
4678
5095
  loading && !hasCustomLoading && hasCompletedLoadRef.current && /* @__PURE__ */ jsx7("div", { className: "rvg-refresh-loading", "aria-label": labels.refreshing, children: /* @__PURE__ */ jsx7(LoadingSpinnerIcon, {}) })
4679
5096
  ] });
4680
5097
  }
5098
+ var Table = forwardRef(TableInner);
4681
5099
  export {
4682
5100
  Table,
4683
5101
  Table as VelocityGrid,