@lensui/lens-table 0.1.4 → 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
@@ -27,6 +27,7 @@ var ZH_CN_GRID_LABELS = {
27
27
  sortDesc: "\u5F53\u524D\uFF1A\u964D\u5E8F",
28
28
  sortBoth: "\u5347\u5E8F / \u964D\u5E8F",
29
29
  dragColumn: "\u62D6\u52A8\u5217\u6392\u5E8F",
30
+ revealSelectedCell: "\u5B9A\u4F4D\u5230\u9009\u4E2D\u5355\u5143\u683C",
30
31
  previousYear: "\u4E0A\u4E00\u5E74",
31
32
  nextYear: "\u4E0B\u4E00\u5E74",
32
33
  previousMonth: "\u4E0A\u4E2A\u6708",
@@ -95,6 +96,7 @@ var EN_US_GRID_LABELS = {
95
96
  sortDesc: "Current: descending",
96
97
  sortBoth: "Ascending / descending",
97
98
  dragColumn: "Drag column to reorder",
99
+ revealSelectedCell: "Reveal selected cell",
98
100
  previousYear: "Previous year",
99
101
  nextYear: "Next year",
100
102
  previousMonth: "Previous month",
@@ -286,16 +288,6 @@ function ContextMenuIcon({ type }) {
286
288
  };
287
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)) });
288
290
  }
289
- function RowDragHandleIcon() {
290
- return /* @__PURE__ */ jsxs2("svg", { viewBox: "0 0 16 16", focusable: "false", children: [
291
- /* @__PURE__ */ jsx2("circle", { cx: "5", cy: "4", r: "1" }),
292
- /* @__PURE__ */ jsx2("circle", { cx: "11", cy: "4", r: "1" }),
293
- /* @__PURE__ */ jsx2("circle", { cx: "5", cy: "8", r: "1" }),
294
- /* @__PURE__ */ jsx2("circle", { cx: "11", cy: "8", r: "1" }),
295
- /* @__PURE__ */ jsx2("circle", { cx: "5", cy: "12", r: "1" }),
296
- /* @__PURE__ */ jsx2("circle", { cx: "11", cy: "12", r: "1" })
297
- ] });
298
- }
299
291
  function LoadingSpinnerIcon() {
300
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" }) });
301
293
  }
@@ -303,7 +295,6 @@ function LoadingSpinnerIcon() {
303
295
  // src/components/editors/ChoiceEditor.tsx
304
296
  import { jsx as jsx3, jsxs as jsxs3 } from "react/jsx-runtime";
305
297
  function ChoiceEditor({ value, options, style, labels, onChange, onCommit, onCancel }) {
306
- const editorStyle = typeof style.width === "number" ? { ...style, width: Math.max(0, style.width - 1) } : style;
307
298
  const collection = createListCollection({
308
299
  items: options,
309
300
  itemToValue: (item) => item.value,
@@ -313,7 +304,7 @@ function ChoiceEditor({ value, options, style, labels, onChange, onCommit, onCan
313
304
  Select.Root,
314
305
  {
315
306
  className: "rvg-choice-editor",
316
- style: editorStyle,
307
+ style,
317
308
  collection,
318
309
  value: value ? [value] : [],
319
310
  defaultOpen: true,
@@ -724,6 +715,48 @@ function createRowDragHandleSvg() {
724
715
  }
725
716
  return svg;
726
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
+ }
727
760
 
728
761
  // src/Table.tsx
729
762
  import { draggable, dropTargetForElements } from "@atlaskit/pragmatic-drag-and-drop/element/adapter";
@@ -744,6 +777,7 @@ function getDisplayLabel(column, row, rowIndex) {
744
777
 
745
778
  // src/core/layout.ts
746
779
  var DEFAULT_COLUMN_WIDTH = 140;
780
+ var HEADER_ACTION_SLOT_WIDTH = 16;
747
781
  function isDataColumn(column) {
748
782
  return !column.rowSelection && !column.rowDragHandle && !column.rowNumber;
749
783
  }
@@ -758,7 +792,7 @@ function getHeaderTitleRequiredWidth(column, titleWidth) {
758
792
  return minimumReadableTitleWidth + (column.align === "center" ? 20 : 22);
759
793
  }
760
794
  function getVisibleHeaderActions(column, width, columnDraggable = false, titleWidth = 26) {
761
- 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));
762
796
  const candidates = [
763
797
  isColumnFilterable(column) ? "filter" : null,
764
798
  isColumnSortable(column) ? "sort" : null,
@@ -837,12 +871,15 @@ var COLORS = {
837
871
  grid: "#e3e6e8",
838
872
  text: "#202124",
839
873
  muted: "#5f6368",
874
+ icon: "#9aa0a6",
875
+ borderStrong: "#b8bec4",
840
876
  selection: "#1677ff",
841
877
  selectionFill: "#edf4ff",
842
878
  axisSelectionFill: "#e8f2ff",
843
879
  rowHoverFill: "#f6f9fc",
844
880
  editedFill: "#fff1b8",
845
881
  insertedFill: "#c8ead4",
882
+ columnDropTargetFill: "#eeeeee",
846
883
  stripe: "#fafbfc"
847
884
  };
848
885
  function ellipsizeText(context, text, maxWidth) {
@@ -862,7 +899,7 @@ function ellipsizeText(context, text, maxWidth) {
862
899
  return `${text.slice(0, start)}${ellipsis}`;
863
900
  }
864
901
  function paintGrid(options) {
865
- 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;
866
903
  const headerLeafTop = options.headerLeafTop ?? 0;
867
904
  const headerLeafHeight = options.headerLeafHeight ?? headerHeight;
868
905
  const colors = { ...COLORS, ...options.colors };
@@ -884,6 +921,7 @@ function paintGrid(options) {
884
921
  const leftFixedWidth = columns.reduce((value, column, index) => column.fixed === "left" ? value + metrics[index].width : value, 0);
885
922
  const rightFixedWidth = columns.reduce((value, column, index) => column.fixed === "right" ? value + metrics[index].width : value, 0);
886
923
  const rightFixedLeft = width - rightFixedWidth;
924
+ const rightFixedBoundaryInset = rightFixedWidth > 0 && !verticalBorderless ? 1 : 0;
887
925
  const leftOffsets = /* @__PURE__ */ new Map();
888
926
  let leftOffset = 0;
889
927
  for (let index = 0; index < columns.length; index += 1) {
@@ -907,11 +945,19 @@ function paintGrid(options) {
907
945
  const getColumnX = (index) => {
908
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;
909
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;
910
956
  const paintBodyColumns = (layer) => {
911
957
  const rowStart = Math.max(0, range.rowStart - Math.max(0, options.maxRowSpan - 1));
912
958
  for (let rowIndex = rowStart; rowIndex < range.rowEnd; rowIndex += 1) {
913
959
  if (rowDragPreview?.sourceIndex === rowIndex) continue;
914
- 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);
915
961
  const y = bodyTop + rowIndex * rowHeight - scrollTop + shift;
916
962
  const row = rows[rowIndex];
917
963
  const rowKey = getRowKey(row, rowIndex);
@@ -944,6 +990,10 @@ function paintGrid(options) {
944
990
  ctx.fillStyle = colors.rowHoverFill;
945
991
  ctx.fillRect(x, y, cellWidth, cellHeight);
946
992
  }
993
+ if (columnDropTarget && columnIndex >= columnDropTarget.startIndex && columnIndex <= columnDropTarget.endIndex) {
994
+ ctx.fillStyle = colors.columnDropTargetFill;
995
+ ctx.fillRect(x, y, cellWidth, cellHeight);
996
+ }
947
997
  if (highlightInsertedRows && insertedRowKeys.has(rowKey)) {
948
998
  ctx.fillStyle = colors.insertedFill;
949
999
  ctx.fillRect(x, y, cellWidth, cellHeight);
@@ -970,6 +1020,7 @@ function paintGrid(options) {
970
1020
  }
971
1021
  ctx.fillStyle = colors.grid;
972
1022
  if (!verticalBorderless) ctx.fillRect(x + cellWidth - 1, y, 1, cellHeight);
1023
+ if (rowIndex === rowBelowDragGap) ctx.fillRect(x, y, cellWidth, 1);
973
1024
  if (!options.suppressLastRowBottomBorder || rowIndex !== rows.length - 1) {
974
1025
  ctx.fillRect(x, y + cellHeight - 1, cellWidth, 1);
975
1026
  }
@@ -996,6 +1047,59 @@ function paintGrid(options) {
996
1047
  if (columnIndex === rangeColumnStart) ctx.fillRect(x, y, 2, cellHeight);
997
1048
  if (columnIndex === rangeColumnEnd) ctx.fillRect(x + cellWidth - 2, y, 2, cellHeight);
998
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
+ }
999
1103
  if (!column.rowDragHandle && !column.rowSelection && !column.renderCell && !(editing?.rowIndex === rowIndex && editing.columnIndex === columnIndex)) {
1000
1104
  let label = "";
1001
1105
  try {
@@ -1051,6 +1155,10 @@ function paintGrid(options) {
1051
1155
  ctx.fillStyle = colors.selectionFill;
1052
1156
  ctx.fillRect(x, headerY, metric.width, headerHeight);
1053
1157
  }
1158
+ if (columnDropTarget && columnIndex >= columnDropTarget.startIndex && columnIndex <= columnDropTarget.endIndex) {
1159
+ ctx.fillStyle = colors.columnDropTargetFill;
1160
+ ctx.fillRect(x, headerY, metric.width, headerHeight);
1161
+ }
1054
1162
  ctx.fillStyle = colors.grid;
1055
1163
  if (!verticalBorderless) ctx.fillRect(x + metric.width - 1, headerY, 1, headerHeight);
1056
1164
  ctx.fillRect(x, headerY + headerHeight - 1, metric.width, 1);
@@ -1059,7 +1167,7 @@ function paintGrid(options) {
1059
1167
  ctx.font = "600 13px Inter, ui-sans-serif, system-ui, sans-serif";
1060
1168
  const titleWidth = ctx.measureText(column.title).width;
1061
1169
  const visibleActions = getVisibleHeaderActions(column, metric.width, columnDraggable, titleWidth);
1062
- 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;
1063
1171
  const contentWidth = Math.max(0, metric.width - actionWidth);
1064
1172
  const padding = 10;
1065
1173
  const clipWidth = contentWidth;
@@ -1107,28 +1215,28 @@ function paintGrid(options) {
1107
1215
  const markerBottom = Math.min(bottom, height);
1108
1216
  const startX = getColumnX(columnStart);
1109
1217
  const endX = getColumnX(columnEnd) + metrics[columnEnd].width;
1110
- const visibleLeft = columns[columnStart].fixed === "left" ? Math.max(0, startX) : Math.max(leftFixedWidth - 1, startX);
1218
+ const visibleLeft = columns[columnStart].fixed === "left" ? Math.max(0, startX) : Math.max(leftFixedWidth, startX);
1111
1219
  const visibleRight = columns[columnEnd].fixed === "right" ? Math.min(width, endX) : Math.min(rightFixedLeft, endX);
1112
1220
  ctx.fillStyle = colors.selection;
1113
1221
  if (markerBottom > markerTop) {
1114
1222
  if (columns[columnStart].fixed === void 0 && startX < leftFixedWidth) {
1115
- ctx.fillRect(Math.max(0, leftFixedWidth - 1), markerTop, 2, markerBottom - markerTop);
1223
+ ctx.fillRect(Math.max(0, leftFixedWidth), markerTop, 2, markerBottom - markerTop);
1116
1224
  }
1117
1225
  if (columns[columnEnd].fixed === void 0 && endX > rightFixedLeft) {
1118
- ctx.fillRect(Math.max(0, rightFixedLeft - 1), markerTop, 2, markerBottom - markerTop);
1226
+ ctx.fillRect(Math.max(0, rightFixedLeft - 2 - rightFixedBoundaryInset), markerTop, 2, markerBottom - markerTop);
1119
1227
  }
1120
1228
  }
1121
1229
  if (visibleRight > visibleLeft) {
1122
1230
  if (top < clipTop) ctx.fillRect(visibleLeft, clipTop, visibleRight - visibleLeft, 2);
1123
1231
  if (bottom > height) ctx.fillRect(visibleLeft, height - 2, visibleRight - visibleLeft, 2);
1124
1232
  } else if (top < clipTop || bottom > height) {
1125
- const markerX = endX <= leftFixedWidth ? Math.max(0, leftFixedWidth - 1) : Math.max(0, rightFixedLeft - 1);
1233
+ const markerX = endX <= leftFixedWidth ? Math.max(0, leftFixedWidth) : Math.max(0, rightFixedLeft - 2 - rightFixedBoundaryInset);
1126
1234
  ctx.fillRect(markerX, top < clipTop ? clipTop : height - 2, 2, 2);
1127
1235
  }
1128
1236
  }
1129
1237
  const selectedRow = selection ? rows[selection.rowIndex] : void 0;
1130
1238
  const hasValidSelection = Boolean(selection && selectedRow && getRowKey(selectedRow, selection.rowIndex) === selection.rowKey);
1131
- if (selection && hasValidSelection && !selectionRange) {
1239
+ if (selection && hasValidSelection && !selectionRange && !rowDragPreview) {
1132
1240
  const metric = metrics[selection.columnIndex];
1133
1241
  if (metric) {
1134
1242
  const fixedSide = columns[selection.columnIndex].fixed;
@@ -1137,22 +1245,22 @@ function paintGrid(options) {
1137
1245
  const selectionWidth = getCellWidth(selection.columnIndex, span?.colSpan ?? 1);
1138
1246
  const selectionHeight = rowHeight * (span?.rowSpan ?? 1);
1139
1247
  const x = Math.round(getColumnX(selection.columnIndex));
1140
- const y = Math.round(bodyTop + selection.rowIndex * rowHeight - scrollTop);
1248
+ const y = Math.round(bodyTop + selection.rowIndex * rowHeight - scrollTop + getRowDragOffset(selection.rowIndex));
1141
1249
  const borderWidth = 2;
1142
1250
  const visibleLeft = fixedSide === "left" ? 0 : leftFixedWidth;
1143
1251
  const selectionAreaRight = fixedSide === "right" ? width : width - rightFixedWidth;
1144
1252
  const visibleRight = x + selectionWidth;
1145
1253
  const visibleBottom = y + selectionHeight;
1146
1254
  const selectionTop = fixedHeader ? bodyTop : 0;
1147
- const selectionClipTop = fixedHeader ? Math.max(0, selectionTop - 1) : selectionTop;
1255
+ const selectionClipTop = selectionTop;
1148
1256
  const hiddenAboveHeader = visibleBottom <= selectionTop;
1149
1257
  const hiddenBelowViewport = y >= height;
1150
1258
  const hiddenBehindLeft = !isFixed && visibleRight <= visibleLeft;
1151
1259
  const hiddenBehindRight = !isFixed && x >= selectionAreaRight;
1152
- const leftMarkerX = Math.max(0, leftFixedWidth - 1);
1260
+ const leftMarkerX = Math.max(0, leftFixedWidth);
1153
1261
  if ((hiddenAboveHeader || hiddenBelowViewport) && (hiddenBehindLeft || hiddenBehindRight)) {
1154
1262
  const markerSize = 2;
1155
- const markerX = hiddenBehindLeft ? leftMarkerX : selectionAreaRight - borderWidth;
1263
+ const markerX = hiddenBehindLeft ? leftMarkerX : selectionAreaRight - borderWidth - rightFixedBoundaryInset;
1156
1264
  const markerY = hiddenAboveHeader ? selectionClipTop : height - markerSize;
1157
1265
  ctx.fillStyle = colors.selection;
1158
1266
  ctx.fillRect(markerX, markerY, markerSize, markerSize);
@@ -1160,7 +1268,7 @@ function paintGrid(options) {
1160
1268
  }
1161
1269
  ctx.save();
1162
1270
  ctx.beginPath();
1163
- const clipLeft = fixedSide === "left" ? 0 : Math.max(0, leftFixedWidth - 1);
1271
+ const clipLeft = fixedSide === "left" ? 0 : Math.max(0, leftFixedWidth);
1164
1272
  const clipRight = Math.min(width, selectionAreaRight + 1);
1165
1273
  ctx.rect(clipLeft, selectionClipTop, clipRight - clipLeft, height - selectionClipTop);
1166
1274
  ctx.clip();
@@ -1178,14 +1286,14 @@ function paintGrid(options) {
1178
1286
  if (hiddenBehindLeft || hiddenBehindRight) {
1179
1287
  const markerTop = Math.max(y - borderWidth, selectionTop);
1180
1288
  const markerBottom = Math.min(y + selectionHeight + 1, height);
1181
- const markerX = hiddenBehindLeft ? leftMarkerX : selectionAreaRight - borderWidth;
1289
+ const markerX = hiddenBehindLeft ? leftMarkerX : selectionAreaRight - borderWidth - rightFixedBoundaryInset;
1182
1290
  ctx.fillRect(markerX, markerTop, borderWidth, Math.max(0, markerBottom - markerTop));
1183
1291
  }
1184
1292
  if (hiddenAboveHeader || hiddenBelowViewport || hiddenBehindLeft || hiddenBehindRight) {
1185
1293
  ctx.restore();
1186
1294
  return;
1187
1295
  }
1188
- const borderLeft = x <= 0 ? 0 : Math.max(x - borderWidth, visibleLeft - 1);
1296
+ const borderLeft = x <= 0 ? 0 : Math.max(x - borderWidth, visibleLeft);
1189
1297
  const borderRight = Math.min(x + selectionWidth - 1, selectionAreaRight - borderWidth, width - borderWidth);
1190
1298
  const borderTop = Math.max(y - borderWidth, selectionClipTop);
1191
1299
  const borderBottom = Math.min(y + selectionHeight - 1, height - borderWidth);
@@ -1247,6 +1355,40 @@ function flattenDataColumns(columns, inheritedFixed, insideGroup = false) {
1247
1355
  return flattenDataColumns(column.children, column.fixed ?? inheritedFixed, true);
1248
1356
  });
1249
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
+ }
1250
1392
  function buildHeaderCells(sourceColumns, utilityColumnCount, depth) {
1251
1393
  const cells = [];
1252
1394
  let leafIndex = utilityColumnCount;
@@ -1294,6 +1436,25 @@ var createEmptyCellSpanLookup = () => ({
1294
1436
  covered: /* @__PURE__ */ new Map(),
1295
1437
  maxRowSpan: 1
1296
1438
  });
1439
+ var summaryFunctionIds = /* @__PURE__ */ new WeakMap();
1440
+ var nextSummaryFunctionId = 1;
1441
+ function getSummaryFunctionId(fn) {
1442
+ if (!fn) return "";
1443
+ const existing = summaryFunctionIds.get(fn);
1444
+ if (existing !== void 0) return existing;
1445
+ const next = nextSummaryFunctionId;
1446
+ nextSummaryFunctionId += 1;
1447
+ summaryFunctionIds.set(fn, next);
1448
+ return next;
1449
+ }
1450
+ function getSummarySignature(columns) {
1451
+ return columns.filter((column) => Boolean(column.summary)).map((column) => [
1452
+ column.key,
1453
+ String(column.dataIndex ?? ""),
1454
+ typeof column.summary === "function" ? getSummaryFunctionId(column.summary) : "sum",
1455
+ getSummaryFunctionId(column.formatter)
1456
+ ].join("")).join("");
1457
+ }
1297
1458
  async function calculateSummaryValues(columns, rows) {
1298
1459
  const values = /* @__PURE__ */ new Map();
1299
1460
  for (const column of columns) {
@@ -1332,7 +1493,7 @@ function resolveContextMenuSection(section, defaults) {
1332
1493
  return defaults;
1333
1494
  }
1334
1495
  function Table({
1335
- columns: sourceColumns,
1496
+ columns: columnProps,
1336
1497
  rows: sourceRows,
1337
1498
  rowKey,
1338
1499
  width = "100%",
@@ -1361,23 +1522,21 @@ function Table({
1361
1522
  rowSelection,
1362
1523
  selectedRowKeys,
1363
1524
  defaultSelectedRowKeys,
1364
- onSelectedRowKeysChange,
1525
+ onSelectedRowChange,
1365
1526
  columnSelection,
1366
1527
  selectedColumnKeys,
1367
1528
  defaultSelectedColumnKeys,
1368
- onSelectedColumnKeysChange,
1529
+ onSelectedColumnChange,
1369
1530
  columnDraggable = true,
1370
- columnResizable = false,
1371
- onColumnOrderChange,
1531
+ columnResizable = true,
1532
+ onColumnsReorder,
1372
1533
  onColumnResize,
1373
1534
  rowDraggable = false,
1374
- onRowOrderChange,
1535
+ onRowsReorder,
1375
1536
  onInsertRows,
1376
1537
  onDeleteRows,
1377
- sortState = null,
1378
- onSortStateChange,
1379
- filterValues = {},
1380
- onFilterValuesChange,
1538
+ onSortChange,
1539
+ onFilterChange,
1381
1540
  onCellChange,
1382
1541
  onCellContextMenu,
1383
1542
  contextMenu: contextMenuConfig = true,
@@ -1386,6 +1545,10 @@ function Table({
1386
1545
  style,
1387
1546
  ariaLabel = "Data grid"
1388
1547
  }) {
1548
+ const [sourceColumns, setSourceColumns] = useState4(columnProps);
1549
+ useEffect2(() => {
1550
+ setSourceColumns((current) => reconcileColumns(current, columnProps));
1551
+ }, [columnProps]);
1389
1552
  const rowHeight = layout?.rowHeight ?? rowHeightProp ?? 36;
1390
1553
  const baseHeaderHeight = layout?.headerHeight ?? headerHeightProp ?? 40;
1391
1554
  const headerDepth = useMemo(() => Math.max(1, ...sourceColumns.map(getColumnDepth)), [sourceColumns]);
@@ -1466,7 +1629,6 @@ function Table({
1466
1629
  const frameRef = useRef2(null);
1467
1630
  const textFrameRef = useRef2(null);
1468
1631
  const toastTimerRef = useRef2(null);
1469
- const sortDebounceRef = useRef2(null);
1470
1632
  const headerTooltipTimerRef = useRef2(null);
1471
1633
  const cellTooltipTimerRef = useRef2(null);
1472
1634
  const pendingHeaderTooltipKeyRef = useRef2("");
@@ -1478,6 +1640,7 @@ function Table({
1478
1640
  const dragGuideRef = useRef2(null);
1479
1641
  const rowDragGuideRef = useRef2(null);
1480
1642
  const selectionFocusRef = useRef2(null);
1643
+ const selectionEdgeTargetRef = useRef2(null);
1481
1644
  const verticalScrollbarRef = useRef2(null);
1482
1645
  const verticalScrollbarThumbRef = useRef2(null);
1483
1646
  const verticalScrollbarDragRef = useRef2(null);
@@ -1496,6 +1659,7 @@ function Table({
1496
1659
  const suppressClickRef = useRef2(false);
1497
1660
  const hasCompletedLoadRef = useRef2(!loading);
1498
1661
  const columnDragPreviewRef = useRef2(null);
1662
+ const validColumnDropRef = useRef2(null);
1499
1663
  const hoveredHeaderActionRef = useRef2(null);
1500
1664
  const hoveredRowIndexRef = useRef2(null);
1501
1665
  const [viewport, setViewport] = useState4({ width: 0, height: typeof height === "number" ? height : FALLBACK_HEIGHT });
@@ -1503,11 +1667,8 @@ function Table({
1503
1667
  const resolvedHeight = typeof height === "number" ? height : viewport.height || FALLBACK_HEIGHT;
1504
1668
  const [scrollPosition, setScrollPosition] = useState4({ left: 0, top: 0 });
1505
1669
  const scrollRef = useRef2({ left: 0, top: 0 });
1506
- const [selection, setSelection] = useControllableValue(selectedCell, defaultSelectedCell, onSelectedCellChange);
1507
- const [selectionRange, setSelectionRange] = useState4(null);
1508
- const rangeDragRef = useRef2(null);
1509
1670
  const [rowKeys, setRowKeys] = useControllableKeys(selectedRowKeys, defaultSelectedRowKeys, (keys) => {
1510
- if (!onSelectedRowKeysChange) return;
1671
+ if (!onSelectedRowChange) return;
1511
1672
  const missing = new Set(keys.filter((key) => !rowByKeyRef.current.has(key)));
1512
1673
  if (missing.size > 0) {
1513
1674
  for (let index = 0; index < rows.length && missing.size > 0; index += 1) {
@@ -1524,12 +1685,13 @@ function Table({
1524
1685
  const index = rowIndexByKeyRef.current.get(key);
1525
1686
  return row && index !== void 0 ? [{ row, index }] : [];
1526
1687
  });
1527
- onSelectedRowKeysChange(keys, entries.map(({ row }) => row), entries.map(({ index }) => index));
1688
+ onSelectedRowChange(keys, entries.map(({ row }) => row), entries.map(({ index }) => index));
1528
1689
  });
1529
- const [columnKeys, setColumnKeys] = useControllableKeys(selectedColumnKeys, defaultSelectedColumnKeys, onSelectedColumnKeysChange);
1690
+ const [columnKeys, setColumnKeys] = useControllableKeys(selectedColumnKeys, defaultSelectedColumnKeys, onSelectedColumnChange);
1530
1691
  const [editing, setEditing] = useState4(null);
1531
1692
  const [draft, setDraft] = useState4("");
1532
1693
  const [rowDragPreview, setRowDragPreview] = useState4(null);
1694
+ const [columnDropTarget, setColumnDropTarget] = useState4(null);
1533
1695
  useEffect2(() => {
1534
1696
  if (!loading) hasCompletedLoadRef.current = true;
1535
1697
  }, [loading]);
@@ -1598,6 +1760,8 @@ function Table({
1598
1760
  return () => observer.disconnect();
1599
1761
  }, [activeCustomHeaderLevels, measureCustomHeaderHeight, viewport.width]);
1600
1762
  const [filterEditor, setFilterEditor] = useState4(null);
1763
+ const [filterValues, setFilterValues] = useState4({});
1764
+ const [sortState, setSortState] = useState4(null);
1601
1765
  const [contextMenu, setContextMenu] = useState4(null);
1602
1766
  const [insertCounts, setInsertCounts] = useState4({ before: 1, after: 1 });
1603
1767
  const [confirmAction, setConfirmAction] = useState4(null);
@@ -1610,7 +1774,20 @@ function Table({
1610
1774
  const [hoveredHeaderAction, setHoveredHeaderAction] = useState4(null);
1611
1775
  const [visibleHeaderTooltip, setVisibleHeaderTooltip] = useState4(null);
1612
1776
  const [hoveredCellTooltip, setHoveredCellTooltip] = useState4(null);
1613
- 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
+ }, []);
1614
1791
  const [editedCellKeys, setEditedCellKeys] = useState4(() => /* @__PURE__ */ new Set());
1615
1792
  const [cellAnnotations, setCellAnnotations] = useState4(() => /* @__PURE__ */ new Map());
1616
1793
  const [annotationType, setAnnotationType] = useState4("corner");
@@ -1622,6 +1799,7 @@ function Table({
1622
1799
  const [pendingDeletedRowKeys, setPendingDeletedRowKeys] = useState4(() => /* @__PURE__ */ new Set());
1623
1800
  const [pendingInserts, setPendingInserts] = useState4(() => []);
1624
1801
  const [insertedRowKeys, setInsertedRowKeys] = useState4(() => /* @__PURE__ */ new Set());
1802
+ const [rowOrderKeys, setRowOrderKeys] = useState4(null);
1625
1803
  const settledPendingInsertIdsRef = useRef2(/* @__PURE__ */ new Set());
1626
1804
  const getDataRowKey = useCallback2((row, index) => {
1627
1805
  return typeof rowKey === "function" ? rowKey(row, index) : rowKey ? row[rowKey] : getDefaultRowKey(row, index);
@@ -1636,7 +1814,7 @@ function Table({
1636
1814
  }
1637
1815
  return sourceRows.findIndex((sourceRow, index) => getDataRowKey(sourceRow, index) === key);
1638
1816
  }, [getDataRowKey, sourceRows]);
1639
- const rows = useMemo(() => {
1817
+ const orderedRows = useMemo(() => {
1640
1818
  let next = sourceRows;
1641
1819
  if (pendingInserts.length > 0) {
1642
1820
  const activeInserts = pendingInserts.filter((insert) => {
@@ -1655,9 +1833,91 @@ function Table({
1655
1833
  insertedOffset += insert.rows.length;
1656
1834
  });
1657
1835
  }
1658
- if (pendingDeletedRowKeys.size === 0) return next;
1659
- return next.filter((row, index) => !pendingDeletedRowKeys.has(getDataRowKey(row, index)));
1660
- }, [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]);
1661
1921
  useEffect2(() => {
1662
1922
  if (insertBusy || settledPendingInsertIdsRef.current.size === 0) return;
1663
1923
  setPendingInserts((current) => {
@@ -1725,7 +1985,8 @@ function Table({
1725
1985
  const getScrollableColumnLeft = useCallback2((columnIndex) => scrollableColumnLefts[columnIndex] ?? fixedWidth, [fixedWidth, scrollableColumnLefts]);
1726
1986
  const hasRowSelectionColumn = useMemo(() => columns.some((column) => column.rowSelection), [columns]);
1727
1987
  const rowSelectionMode = typeof rowSelection === "object" ? rowSelection.mode ?? "multiple" : "multiple";
1728
- const hasSummaryRow = useMemo(() => rows.length > 0 && summaryEnabled && columns.some((column) => Boolean(column.summary)), [columns, rows.length, summaryEnabled]);
1988
+ const summarySignature = useMemo(() => getSummarySignature(columns), [columns]);
1989
+ const hasSummaryRow = rows.length > 0 && summaryEnabled && summarySignature.length > 0;
1729
1990
  const topSummaryHeight = hasSummaryRow && summaryPosition === "top" ? rowHeight : 0;
1730
1991
  const bottomSummaryHeight = hasSummaryRow && summaryPosition === "bottom" ? rowHeight : 0;
1731
1992
  const bodyTop = headerHeight + topSummaryHeight;
@@ -1734,21 +1995,21 @@ function Table({
1734
1995
  const effectiveHeight = autoHeight ? Math.max(bodyTop + bottomSummaryHeight, Math.min(resolvedHeight, realContentHeight)) : resolvedHeight;
1735
1996
  const renderHeight = Math.max(0, effectiveHeight - bottomSummaryHeight);
1736
1997
  const bodyViewportHeight = Math.max(0, renderHeight - (fixedHeader ? headerHeight + topSummaryHeight : 0));
1737
- const [summaryState, setSummaryState] = useState4(() => ({ columns: null, rows: null, values: /* @__PURE__ */ new Map(), pending: false }));
1738
- const summaryPending = hasSummaryRow && (loading || summaryState.pending || summaryState.columns !== columns || summaryState.rows !== rows);
1739
- const summaryValues = hasSummaryRow && summaryState.columns === columns && summaryState.rows === rows ? summaryState.values : /* @__PURE__ */ new Map();
1998
+ const [summaryState, setSummaryState] = useState4(() => ({ signature: "", rows: null, values: /* @__PURE__ */ new Map(), pending: false }));
1999
+ const summaryPending = hasSummaryRow && (loading || summaryState.pending || summaryState.signature !== summarySignature || summaryState.rows !== rows);
2000
+ const summaryValues = hasSummaryRow && summaryState.signature === summarySignature && summaryState.rows === rows ? summaryState.values : /* @__PURE__ */ new Map();
1740
2001
  useEffect2(() => {
1741
2002
  let cancelled = false;
1742
2003
  if (!hasSummaryRow) {
1743
- setSummaryState((current) => current.columns === null && current.rows === null && current.values.size === 0 && !current.pending ? current : { columns: null, rows: null, values: /* @__PURE__ */ new Map(), pending: false });
2004
+ setSummaryState((current) => current.signature === "" && current.rows === null && current.values.size === 0 && !current.pending ? current : { signature: "", rows: null, values: /* @__PURE__ */ new Map(), pending: false });
1744
2005
  return () => {
1745
2006
  cancelled = true;
1746
2007
  };
1747
2008
  }
1748
2009
  setSummaryState((current) => ({
1749
- columns,
2010
+ signature: summarySignature,
1750
2011
  rows,
1751
- values: current.columns === columns && current.rows === rows ? current.values : /* @__PURE__ */ new Map(),
2012
+ values: current.signature === summarySignature && current.rows === rows ? current.values : /* @__PURE__ */ new Map(),
1752
2013
  pending: true
1753
2014
  }));
1754
2015
  if (loading) {
@@ -1761,13 +2022,13 @@ function Table({
1761
2022
  if (cancelled) return;
1762
2023
  const values = await calculateSummaryValues(columns, rows);
1763
2024
  if (cancelled) return;
1764
- setSummaryState({ columns, rows, values, pending: false });
2025
+ setSummaryState({ signature: summarySignature, rows, values, pending: false });
1765
2026
  };
1766
2027
  void run();
1767
2028
  return () => {
1768
2029
  cancelled = true;
1769
2030
  };
1770
- }, [columns, hasSummaryRow, loading, rows]);
2031
+ }, [hasSummaryRow, loading, rows, summarySignature]);
1771
2032
  const domRange = useMemo(() => {
1772
2033
  if (!isVirtualized) return { rowStart: 0, rowEnd: rows.length, columnStart: 0, columnEnd: columns.length };
1773
2034
  const currentScroll2 = scrollRef.current;
@@ -1898,22 +2159,177 @@ function Table({
1898
2159
  grid: readThemeColor(themeStyles, "--rvg-color-grid", "#e3e6e8"),
1899
2160
  text: readThemeColor(themeStyles, "--rvg-color-text", "#202124"),
1900
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"),
1901
2164
  selection: readThemeColor(themeStyles, "--rvg-color-primary", "#1677ff"),
1902
2165
  selectionFill: readThemeColor(themeStyles, "--rvg-color-selection-fill", "#edf4ff"),
1903
2166
  axisSelectionFill: readThemeColor(themeStyles, "--rvg-color-axis-selection-fill", "#e8f2ff"),
1904
2167
  rowHoverFill: readThemeColor(themeStyles, "--rvg-color-row-hover-fill", "#f6f9fc"),
1905
2168
  editedFill: editedCellHighlightColor ?? readThemeColor(themeStyles, "--rvg-color-edited-fill", "#fff1b8"),
1906
2169
  insertedFill: insertedRowHighlightColor ?? readThemeColor(themeStyles, "--rvg-color-inserted-fill", "#c8ead4"),
2170
+ columnDropTargetFill: readThemeColor(themeStyles, "--rvg-color-column-drop-target-fill", "#eeeeee"),
1907
2171
  stripe: stripedColor ?? readThemeColor(themeStyles, "--rvg-color-stripe", "#fafbfc")
1908
2172
  };
1909
2173
  const scroll = scrollRef.current;
1910
2174
  const rowScrollTop = fixedHeader ? scroll.top : Math.max(0, scroll.top - bodyTop);
1911
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 };
1912
- 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 });
1913
- }, [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]);
1914
2178
  const scheduleDraw = useCallback2(() => {
1915
2179
  if (frameRef.current === null) frameRef.current = requestAnimationFrame(draw);
1916
2180
  }, [draw]);
2181
+ const drawImmediately = useCallback2(() => {
2182
+ if (frameRef.current !== null) cancelAnimationFrame(frameRef.current);
2183
+ frameRef.current = null;
2184
+ draw();
2185
+ }, [draw]);
2186
+ const getSelectionHorizontalVisibility = useCallback2((scrollLeft) => {
2187
+ if (!selection || selectionRange) return "visible";
2188
+ const column = columns[selection.columnIndex];
2189
+ if (!column || column.fixed !== void 0) return "visible";
2190
+ const span = getCellSpan(selection.rowIndex, selection.columnIndex);
2191
+ const left = getScrollableColumnLeft(selection.columnIndex) - scrollLeft;
2192
+ const right = left + getCellDisplayWidth(selection.columnIndex, span?.colSpan ?? 1);
2193
+ if (right <= fixedWidth) return "hidden-left";
2194
+ if (left >= viewport.width - rightFixedWidth) return "hidden-right";
2195
+ return "visible";
2196
+ }, [columns, fixedWidth, getCellDisplayWidth, getCellSpan, getScrollableColumnLeft, rightFixedWidth, selection, selectionRange, viewport.width]);
2197
+ const getSelectionEdgeIndicator = useCallback2((scrollLeft, scrollTop) => {
2198
+ if (!selection || selectionRange) return null;
2199
+ const row = rows[selection.rowIndex];
2200
+ const metric = metrics[selection.columnIndex];
2201
+ const column = columns[selection.columnIndex];
2202
+ if (!row || !metric || !column || getRowKey(row, selection.rowIndex) !== selection.rowKey) return null;
2203
+ const span = getCellSpan(selection.rowIndex, selection.columnIndex);
2204
+ const rawLeft = column.fixed === "left" ? leftFixedOffsets.get(selection.columnIndex) ?? 0 : column.fixed === "right" ? viewport.width - (rightFixedOffsets.get(selection.columnIndex) ?? 0) - metric.width : getScrollableColumnLeft(selection.columnIndex) - scrollLeft;
2205
+ const rawRight = rawLeft + getCellDisplayWidth(selection.columnIndex, span?.colSpan ?? 1);
2206
+ const rawTop = bodyTop + selection.rowIndex * rowHeight - scrollTop;
2207
+ const rawBottom = rawTop + rowHeight * (span?.rowSpan ?? 1);
2208
+ const horizontalStart = column.fixed === void 0 ? fixedWidth : 0;
2209
+ const horizontalEnd = column.fixed === void 0 ? viewport.width - rightFixedWidth : viewport.width;
2210
+ const verticalStart = fixedHeader ? bodyTop : 0;
2211
+ const hiddenLeft = column.fixed === void 0 && rawRight <= horizontalStart;
2212
+ const hiddenRight = column.fixed === void 0 && rawLeft >= horizontalEnd;
2213
+ const hiddenTop = rawBottom <= verticalStart;
2214
+ const hiddenBottom = rawTop >= renderHeight;
2215
+ if (!hiddenLeft && !hiddenRight && !hiddenTop && !hiddenBottom) return null;
2216
+ const borderWidth = 2;
2217
+ const cornerLength = 10;
2218
+ const hitSlop = 6;
2219
+ const targetSize = borderWidth + hitSlop * 2;
2220
+ const rightBoundaryInset = rightFixedWidth > 0 && hasVerticalBorders ? 1 : 0;
2221
+ const markerX = hiddenLeft ? horizontalStart : horizontalEnd - borderWidth - rightBoundaryInset;
2222
+ const markerY = hiddenTop ? verticalStart : renderHeight - borderWidth;
2223
+ if (hiddenLeft || hiddenRight) {
2224
+ let markerTop = Math.max(rawTop - borderWidth, verticalStart);
2225
+ let markerBottom = Math.min(rawBottom + 1, renderHeight);
2226
+ const topClip = Math.max(0, verticalStart - (rawTop - borderWidth));
2227
+ const bottomClip = Math.max(0, rawBottom + 1 - renderHeight);
2228
+ if (topClip > 0 || bottomClip > 0) {
2229
+ const foldAtTop = topClip > 0;
2230
+ const fold = Math.min(cornerLength, foldAtTop ? topClip : bottomClip);
2231
+ if (fold > borderWidth) {
2232
+ const cornerWidth = fold;
2233
+ const cornerHeight = Math.max(cornerLength, markerBottom - markerTop);
2234
+ const cornerOnLeft = hiddenLeft;
2235
+ const shapeLeft = cornerOnLeft ? markerX : markerX + borderWidth - cornerWidth;
2236
+ const shapeTop = foldAtTop ? verticalStart : renderHeight - cornerHeight;
2237
+ return {
2238
+ side: `${foldAtTop ? "top" : "bottom"}-${cornerOnLeft ? "left" : "right"}`,
2239
+ orientation: "corner",
2240
+ style: {
2241
+ left: shapeLeft - hitSlop,
2242
+ top: shapeTop - hitSlop,
2243
+ width: cornerWidth + hitSlop * 2,
2244
+ height: cornerHeight + hitSlop * 2
2245
+ },
2246
+ corner: { width: cornerWidth, height: cornerHeight }
2247
+ };
2248
+ }
2249
+ }
2250
+ if (markerBottom <= markerTop) return null;
2251
+ return {
2252
+ side: hiddenLeft ? "left" : "right",
2253
+ orientation: "vertical",
2254
+ viewportEdge: hiddenLeft && horizontalStart === 0 ? "left" : hiddenRight && horizontalEnd === viewport.width ? "right" : void 0,
2255
+ style: {
2256
+ left: markerX - hitSlop,
2257
+ top: markerTop,
2258
+ width: targetSize,
2259
+ height: markerBottom - markerTop
2260
+ }
2261
+ };
2262
+ }
2263
+ const horizontalMarkerEnd = horizontalEnd - rightBoundaryInset;
2264
+ let markerLeft = Math.max(rawLeft - borderWidth, horizontalStart);
2265
+ let markerRight = Math.min(rawRight + 1, horizontalMarkerEnd);
2266
+ const leftClip = Math.max(0, horizontalStart - (rawLeft - borderWidth));
2267
+ const rightClip = Math.max(0, rawRight + 1 - horizontalMarkerEnd);
2268
+ if (leftClip > 0 || rightClip > 0) {
2269
+ const foldAtLeft = leftClip > 0;
2270
+ const fold = Math.min(cornerLength, foldAtLeft ? leftClip : rightClip);
2271
+ if (fold > borderWidth) {
2272
+ const cornerWidth = Math.max(cornerLength, markerRight - markerLeft);
2273
+ const cornerHeight = fold;
2274
+ const shapeLeft = foldAtLeft ? horizontalStart : horizontalMarkerEnd - cornerWidth;
2275
+ const shapeTop = hiddenTop ? verticalStart : renderHeight - cornerHeight;
2276
+ return {
2277
+ side: `${hiddenTop ? "top" : "bottom"}-${foldAtLeft ? "left" : "right"}`,
2278
+ orientation: "corner",
2279
+ style: {
2280
+ left: shapeLeft - hitSlop,
2281
+ top: shapeTop - hitSlop,
2282
+ width: cornerWidth + hitSlop * 2,
2283
+ height: cornerHeight + hitSlop * 2
2284
+ },
2285
+ corner: { width: cornerWidth, height: cornerHeight }
2286
+ };
2287
+ }
2288
+ }
2289
+ if (markerRight <= markerLeft) return null;
2290
+ return {
2291
+ side: hiddenTop ? "top" : "bottom",
2292
+ orientation: "horizontal",
2293
+ viewportEdge: hiddenTop && verticalStart === 0 ? "top" : hiddenBottom ? "bottom" : void 0,
2294
+ style: {
2295
+ left: markerLeft,
2296
+ top: markerY - hitSlop,
2297
+ width: markerRight - markerLeft,
2298
+ height: targetSize
2299
+ }
2300
+ };
2301
+ }, [bodyTop, columns, fixedHeader, fixedWidth, getCellDisplayWidth, getCellSpan, getRowKey, getScrollableColumnLeft, hasVerticalBorders, leftFixedOffsets, metrics, renderHeight, rightFixedOffsets, rightFixedWidth, rowHeight, rows, selection, selectionRange, viewport.width]);
2302
+ const syncSelectionEdgeTarget = useCallback2((scrollLeft, scrollTop) => {
2303
+ const target = selectionEdgeTargetRef.current;
2304
+ if (!target) return;
2305
+ const indicator = getSelectionEdgeIndicator(scrollLeft, scrollTop);
2306
+ if (!indicator) {
2307
+ target.style.visibility = "hidden";
2308
+ return;
2309
+ }
2310
+ const orientationChanged = !target.classList.contains(`is-${indicator.orientation}`);
2311
+ target.className = `rvg-selection-edge-target is-${indicator.orientation} is-${indicator.side}${indicator.viewportEdge ? ` is-viewport-${indicator.viewportEdge}` : ""}${orientationChanged ? " is-shape-syncing" : ""}`;
2312
+ target.style.left = `${Number(indicator.style.left)}px`;
2313
+ target.style.top = `${Number(indicator.style.top)}px`;
2314
+ target.style.width = `${Number(indicator.style.width)}px`;
2315
+ target.style.height = `${Number(indicator.style.height)}px`;
2316
+ if (indicator.corner) {
2317
+ target.style.setProperty("--rvg-corner-width", `${indicator.corner.width}px`);
2318
+ target.style.setProperty("--rvg-corner-height", `${indicator.corner.height}px`);
2319
+ target.style.setProperty("--rvg-corner-hover-width-extension", "4px");
2320
+ target.style.setProperty("--rvg-corner-hover-height-extension", "4px");
2321
+ } else {
2322
+ target.style.removeProperty("--rvg-corner-width");
2323
+ target.style.removeProperty("--rvg-corner-height");
2324
+ target.style.removeProperty("--rvg-corner-hover-width-extension");
2325
+ target.style.removeProperty("--rvg-corner-hover-height-extension");
2326
+ }
2327
+ target.style.visibility = "visible";
2328
+ if (orientationChanged) {
2329
+ void target.offsetWidth;
2330
+ target.classList.remove("is-shape-syncing");
2331
+ }
2332
+ }, [getSelectionEdgeIndicator]);
1917
2333
  useLayoutEffect2(() => {
1918
2334
  const element = scrollerRef.current;
1919
2335
  if (!element) return;
@@ -1940,11 +2356,7 @@ function Table({
1940
2356
  }
1941
2357
  };
1942
2358
  }, [scheduleDraw]);
1943
- useEffect2(() => {
1944
- setDisplaySortState(sortState);
1945
- }, [sortState]);
1946
2359
  useEffect2(() => () => {
1947
- if (sortDebounceRef.current !== null) clearTimeout(sortDebounceRef.current);
1948
2360
  if (textFrameRef.current !== null) cancelAnimationFrame(textFrameRef.current);
1949
2361
  if (toastTimerRef.current !== null) clearTimeout(toastTimerRef.current);
1950
2362
  if (verticalScrollbarVisibilityTimerRef.current !== null) clearTimeout(verticalScrollbarVisibilityTimerRef.current);
@@ -2164,7 +2576,13 @@ function Table({
2164
2576
  }
2165
2577
  const nextThumbLeft = Math.max(0, Math.min(trackWidth - thumbWidth, event.clientX - trackRect.left - thumbWidth / 2));
2166
2578
  const maxThumbLeft = Math.max(1, trackWidth - thumbWidth);
2167
- scroller.scrollLeft = nextThumbLeft / maxThumbLeft * maxScrollLeft;
2579
+ const previousScrollLeft = scroller.scrollLeft;
2580
+ const nextScrollLeft = nextThumbLeft / maxThumbLeft * maxScrollLeft;
2581
+ const visibilityChanged = getSelectionHorizontalVisibility(previousScrollLeft) !== getSelectionHorizontalVisibility(nextScrollLeft);
2582
+ scroller.scrollLeft = nextScrollLeft;
2583
+ scrollRef.current = { left: scroller.scrollLeft, top: scroller.scrollTop };
2584
+ syncSelectionEdgeTarget(scroller.scrollLeft, scroller.scrollTop);
2585
+ if (visibilityChanged) drawImmediately();
2168
2586
  updateCustomScrollbars();
2169
2587
  horizontalScrollbarDragRef.current = {
2170
2588
  pointerId: event.pointerId,
@@ -2174,7 +2592,7 @@ function Table({
2174
2592
  thumbWidth,
2175
2593
  maxScrollLeft
2176
2594
  };
2177
- }, [showCustomScrollbar, updateCustomScrollbars]);
2595
+ }, [drawImmediately, getSelectionHorizontalVisibility, showCustomScrollbar, syncSelectionEdgeTarget, updateCustomScrollbars]);
2178
2596
  const handleHorizontalScrollbarPointerMove = useCallback2((event) => {
2179
2597
  const drag = horizontalScrollbarDragRef.current;
2180
2598
  const scroller = scrollerRef.current;
@@ -2182,9 +2600,15 @@ function Table({
2182
2600
  event.preventDefault();
2183
2601
  const maxThumbLeft = Math.max(1, drag.trackWidth - drag.thumbWidth);
2184
2602
  const nextThumbLeft = Math.max(0, Math.min(maxThumbLeft, drag.startLeft + event.clientX - drag.startX));
2185
- scroller.scrollLeft = nextThumbLeft / maxThumbLeft * drag.maxScrollLeft;
2603
+ const previousScrollLeft = scroller.scrollLeft;
2604
+ const nextScrollLeft = nextThumbLeft / maxThumbLeft * drag.maxScrollLeft;
2605
+ const visibilityChanged = getSelectionHorizontalVisibility(previousScrollLeft) !== getSelectionHorizontalVisibility(nextScrollLeft);
2606
+ scroller.scrollLeft = nextScrollLeft;
2607
+ scrollRef.current = { left: scroller.scrollLeft, top: scroller.scrollTop };
2608
+ syncSelectionEdgeTarget(scroller.scrollLeft, scroller.scrollTop);
2609
+ if (visibilityChanged) drawImmediately();
2186
2610
  updateCustomScrollbars();
2187
- }, [updateCustomScrollbars]);
2611
+ }, [drawImmediately, getSelectionHorizontalVisibility, syncSelectionEdgeTarget, updateCustomScrollbars]);
2188
2612
  const handleHorizontalScrollbarPointerUp = useCallback2((event) => {
2189
2613
  const drag = horizontalScrollbarDragRef.current;
2190
2614
  if (!drag || drag.pointerId !== event.pointerId) return;
@@ -2199,6 +2623,7 @@ function Table({
2199
2623
  const scrollDeltaX = element.scrollLeft - previousScroll.left;
2200
2624
  const scrollDeltaY = element.scrollTop - previousScroll.top;
2201
2625
  scrollRef.current = { left: element.scrollLeft, top: element.scrollTop };
2626
+ syncSelectionEdgeTarget(element.scrollLeft, element.scrollTop);
2202
2627
  const focus = selectionFocusRef.current;
2203
2628
  if (focus && selection && !selectionRange) {
2204
2629
  const row = rows[selection.rowIndex];
@@ -2220,9 +2645,9 @@ function Table({
2220
2645
  focus.style.display = "none";
2221
2646
  } else {
2222
2647
  const borderWidth = 2;
2223
- const left = Math.round(Math.max(rawLeft - borderWidth, horizontalStart - 1));
2648
+ const left = Math.round(Math.max(rawLeft - borderWidth, horizontalStart));
2224
2649
  const right = Math.round(Math.min(rawRight + 1, horizontalEnd, viewport.width));
2225
- const top = Math.round(Math.max(rawTop - borderWidth, verticalStart - 1));
2650
+ const top = Math.round(Math.max(rawTop - borderWidth, verticalStart));
2226
2651
  const bottom = Math.round(Math.min(rawBottom + 1, renderHeight));
2227
2652
  focus.style.display = "block";
2228
2653
  focus.style.left = `${left}px`;
@@ -2252,7 +2677,7 @@ function Table({
2252
2677
  setHoveredCellTooltip(null);
2253
2678
  scheduleDraw();
2254
2679
  setEditing(null);
2255
- }, [bodyTop, columns, renderHeight, fixedHeader, fixedWidth, getCellDisplayWidth, getCellSpan, getRowKey, getScrollableColumnLeft, metrics, rightFixedOffsets, rightFixedWidth, rowHeight, rows, scheduleDraw, selection, selectionRange, showCustomScrollbar, updateCustomScrollbars, viewport.width]);
2680
+ }, [bodyTop, columns, renderHeight, fixedHeader, fixedWidth, getCellDisplayWidth, getCellSpan, getRowKey, getScrollableColumnLeft, metrics, rightFixedOffsets, rightFixedWidth, rowHeight, rows, scheduleDraw, selection, selectionRange, showCustomScrollbar, syncSelectionEdgeTarget, updateCustomScrollbars, viewport.width]);
2256
2681
  const locateColumn = useCallback2((clientX) => {
2257
2682
  const canvas = canvasRef.current;
2258
2683
  if (!canvas) return -1;
@@ -2306,12 +2731,12 @@ function Table({
2306
2731
  const localX = clientX - canvasRef.current.getBoundingClientRect().left;
2307
2732
  const columnRight = getDisplayedColumnLeft(columnIndex) + metrics[columnIndex].width;
2308
2733
  const visibleActions = getVisibleHeaderActions(column, metrics[columnIndex].width, columnDraggable, measureHeaderTitleWidth(columnIndex));
2309
- let right = columnRight - (visibleActions.drag ? 18 : 4);
2734
+ let right = columnRight - (visibleActions.drag ? HEADER_ACTION_SLOT_WIDTH : 2);
2310
2735
  if (visibleActions.sort) {
2311
- if (localX >= right - 18 && localX < right) return "sort";
2312
- right -= 18;
2736
+ if (localX >= right - HEADER_ACTION_SLOT_WIDTH && localX < right) return "sort";
2737
+ right -= HEADER_ACTION_SLOT_WIDTH;
2313
2738
  }
2314
- if (visibleActions.filter && localX >= right - 18 && localX < right) return "filter";
2739
+ if (visibleActions.filter && localX >= right - HEADER_ACTION_SLOT_WIDTH && localX < right) return "filter";
2315
2740
  return null;
2316
2741
  }, [columnDraggable, columns, getDisplayedColumnLeft, measureHeaderTitleWidth, metrics]);
2317
2742
  const isHeaderTitleTruncated = useCallback2((columnIndex) => {
@@ -2321,7 +2746,7 @@ function Table({
2321
2746
  const metric = metrics[columnIndex];
2322
2747
  const titleWidth = measureHeaderTitleWidth(columnIndex);
2323
2748
  const visibleActions = getVisibleHeaderActions(column, metric.width, columnDraggable, titleWidth);
2324
- 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;
2325
2750
  return getHeaderTitleRequiredWidth(column, titleWidth) > Math.max(0, metric.width - actionWidth);
2326
2751
  }, [columnDraggable, columns, measureHeaderTitleWidth, metrics]);
2327
2752
  const locateHeaderDragHandle = useCallback2((clientX, columnIndex) => {
@@ -2340,6 +2765,30 @@ function Table({
2340
2765
  return fixedSide === "left" ? leftFixedOffsets.get(cell.startIndex) ?? 0 : fixedSide === "right" ? viewport.width - (rightFixedOffsets.get(cell.startIndex) ?? 0) - startMetric.width : getScrollableColumnLeft(cell.startIndex) - scrollRef.current.left;
2341
2766
  }, [columns, getScrollableColumnLeft, leftFixedOffsets, metrics, rightFixedOffsets, viewport.width]);
2342
2767
  const getHeaderCellWidth = useCallback2((cell) => cell.leaf ? metrics[cell.startIndex]?.width ?? 0 : getCellDisplayWidth(cell.startIndex, cell.endIndex - cell.startIndex + 1), [getCellDisplayWidth, metrics]);
2768
+ const getHeaderCellEdgeFromMetrics = useCallback2((startIndex, endIndex, edge, nextMetrics) => {
2769
+ const fixedSide = columns[startIndex]?.fixed;
2770
+ const width2 = nextMetrics.slice(startIndex, endIndex + 1).reduce((total, metric) => total + metric.width, 0);
2771
+ let left;
2772
+ if (fixedSide === "left") {
2773
+ left = 0;
2774
+ for (let index = 0; index < startIndex; index += 1) {
2775
+ if (columns[index].fixed === "left") left += nextMetrics[index]?.width ?? 0;
2776
+ }
2777
+ } else if (fixedSide === "right") {
2778
+ let rightOffset = 0;
2779
+ for (let index = columns.length - 1; index > startIndex; index -= 1) {
2780
+ if (columns[index].fixed === "right") rightOffset += nextMetrics[index]?.width ?? 0;
2781
+ }
2782
+ left = viewport.width - rightOffset - width2;
2783
+ } else {
2784
+ left = 0;
2785
+ for (let index = 0; index < startIndex; index += 1) {
2786
+ if (columns[index].fixed === void 0) left += nextMetrics[index]?.width ?? 0;
2787
+ }
2788
+ left += fixedWidth - scrollRef.current.left;
2789
+ }
2790
+ return left + (edge === "right" ? width2 : 0);
2791
+ }, [columns, fixedWidth, viewport.width]);
2343
2792
  const locateHeaderResizeHit = useCallback2((clientX, clientY) => {
2344
2793
  if (!columnResizable || !canvasRef.current) return null;
2345
2794
  const rect = canvasRef.current.getBoundingClientRect();
@@ -2350,6 +2799,7 @@ function Table({
2350
2799
  const level = getHeaderLevelAtY(localY, headerY);
2351
2800
  let closest = null;
2352
2801
  let distance = 6;
2802
+ let priority = -1;
2353
2803
  for (const cell of headerCells) {
2354
2804
  if (cell.level !== level) continue;
2355
2805
  if (columns[cell.startIndex]?.rowSelection || columns[cell.startIndex]?.rowDragHandle || columns[cell.startIndex]?.rowNumber) continue;
@@ -2357,8 +2807,10 @@ function Table({
2357
2807
  const edge = fixedSide === "right" && cell.leaf ? "left" : "right";
2358
2808
  const edgeX = getHeaderCellLeft(cell) + (edge === "right" ? getHeaderCellWidth(cell) : 0);
2359
2809
  const nextDistance = Math.abs(localX - edgeX);
2360
- if (nextDistance < distance) {
2810
+ const nextPriority = fixedSide === "right" ? 2 : fixedSide === "left" ? 1 : 0;
2811
+ if (nextDistance < distance || nextDistance === distance && nextPriority > priority) {
2361
2812
  distance = nextDistance;
2813
+ priority = nextPriority;
2362
2814
  closest = { cell, edge };
2363
2815
  }
2364
2816
  }
@@ -2450,6 +2902,7 @@ function Table({
2450
2902
  }
2451
2903
  const sourceIndex = headerDragCell.startIndex;
2452
2904
  columnDragPreviewRef.current = { sourceIndex, targetIndex: sourceIndex };
2905
+ validColumnDropRef.current = null;
2453
2906
  return {
2454
2907
  type: "table-column",
2455
2908
  sourceIndex,
@@ -2460,13 +2913,26 @@ function Table({
2460
2913
  fixed: columns[headerDragCell.startIndex]?.fixed
2461
2914
  };
2462
2915
  },
2463
- onGenerateDragPreview: ({ nativeSetDragImage, source }) => {
2916
+ onGenerateDragPreview: ({ nativeSetDragImage, source, location }) => {
2464
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
+ };
2465
2927
  setCustomNativeDragPreview({
2466
2928
  nativeSetDragImage,
2929
+ getOffset: () => previewOffset,
2467
2930
  render: ({ container }) => {
2468
- const canvasRect = canvas.getBoundingClientRect();
2469
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`;
2470
2936
  const preview2 = document.createElement("div");
2471
2937
  preview2.className = "rvg-row-drag-preview";
2472
2938
  preview2.style.width = `${canvasRect.width}px`;
@@ -2492,10 +2958,14 @@ function Table({
2492
2958
  } else {
2493
2959
  cell.textContent = getCellLabel(sourceIndex, columnIndex);
2494
2960
  }
2961
+ if (selection?.rowKey === rowKey2 && selection.columnKey === column2.key) {
2962
+ cell.classList.add("is-selected");
2963
+ }
2495
2964
  preview2.appendChild(cell);
2496
2965
  });
2497
- container.appendChild(preview2);
2498
- return () => preview2.remove();
2966
+ frame.appendChild(preview2);
2967
+ container.appendChild(frame);
2968
+ return () => frame.remove();
2499
2969
  }
2500
2970
  const preview = document.createElement("div");
2501
2971
  preview.className = "rvg-column-drag-preview";
@@ -2509,6 +2979,29 @@ function Table({
2509
2979
  header.style.height = `${headerHeight}px`;
2510
2980
  header.style.justifyContent = column.align === "right" ? "flex-end" : column.align === "center" ? "center" : "flex-start";
2511
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
+ }
2512
3005
  preview.appendChild(header);
2513
3006
  for (let rowIndex = domRange.rowStart; rowIndex < domRange.rowEnd; rowIndex += 1) {
2514
3007
  const row = rows[rowIndex];
@@ -2537,6 +3030,8 @@ function Table({
2537
3030
  },
2538
3031
  onDragStart: ({ source }) => {
2539
3032
  suppressClickRef.current = true;
3033
+ hideDragTooltips();
3034
+ document.documentElement.classList.add("rvg-is-dragging");
2540
3035
  canvas.style.cursor = "move";
2541
3036
  if (source.data.type === "table-row") {
2542
3037
  const sourceIndex = Number(source.data.sourceIndex);
@@ -2562,30 +3057,66 @@ function Table({
2562
3057
  }
2563
3058
  const targetIndex = Number(target?.targetIndex);
2564
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);
2565
3062
  setDragGuide(null);
2566
3063
  return;
2567
3064
  }
2568
3065
  const edge = target?.columnEdge;
2569
3066
  const destinationIndex = getColumnDropIndex(sourceIndex, targetIndex, edge);
2570
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 });
2571
3095
  const targetLeft = Number(target?.targetLeft);
2572
3096
  const targetWidth = Number(target?.targetWidth);
3097
+ if (!Number.isFinite(targetLeft) || !Number.isFinite(targetWidth)) {
3098
+ validColumnDropRef.current = null;
3099
+ setColumnDropTarget(null);
3100
+ setDragGuide(null);
3101
+ return;
3102
+ }
2573
3103
  const guideTop = headerRowOffsets[Number(source.data.headerLevel)] ?? 0;
2574
3104
  setDragGuide(edge === "right" ? targetLeft + targetWidth : targetLeft, guideTop);
2575
3105
  },
2576
3106
  onDrop: ({ source, location }) => {
3107
+ document.documentElement.classList.remove("rvg-is-dragging");
2577
3108
  const target = location.current.dropTargets[0]?.data;
2578
3109
  const sourceIndex = Number(source.data.sourceIndex);
2579
3110
  if (source.data.type === "table-row") {
2580
- const rawTargetIndex2 = Number(target?.targetIndex);
2581
- let targetIndex2 = rawTargetIndex2 + (target?.rowEdge === "bottom" ? 1 : 0);
2582
- if (sourceIndex < targetIndex2) targetIndex2 -= 1;
2583
- 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));
2584
3115
  scrollerRef.current?.classList.add("rvg-row-drop-settling");
2585
3116
  setRowDragPreview(null);
2586
3117
  setRowDragGuide(null);
2587
3118
  canvas.style.cursor = "default";
2588
- if (Number.isInteger(rawTargetIndex2) && sourceIndex !== targetIndex2) onRowOrderChange?.(sourceIndex, targetIndex2);
3119
+ if (Number.isInteger(rawTargetIndex) && sourceIndex !== targetIndex) applyRowOrder(sourceIndex, targetIndex);
2589
3120
  requestAnimationFrame(() => {
2590
3121
  requestAnimationFrame(() => scrollerRef.current?.classList.remove("rvg-row-drop-settling"));
2591
3122
  });
@@ -2594,21 +3125,22 @@ function Table({
2594
3125
  }, 0);
2595
3126
  return;
2596
3127
  }
2597
- const rawTargetIndex = Number(target?.targetIndex);
2598
- const targetIndex = columnDragPreviewRef.current?.targetIndex ?? (Number.isInteger(rawTargetIndex) ? getColumnDropIndex(sourceIndex, rawTargetIndex, target?.columnEdge) : rawTargetIndex);
3128
+ const validDrop = validColumnDropRef.current;
2599
3129
  canvas.style.cursor = "default";
2600
3130
  setDragGuide(null);
3131
+ setColumnDropTarget(null);
2601
3132
  columnDragPreviewRef.current = null;
2602
- if (Number.isInteger(rawTargetIndex) && Number.isInteger(targetIndex) && sourceIndex !== targetIndex && target?.headerKind === source.data.headerKind && target?.parentKey === source.data.parentKey && target?.fixed === source.data.fixed) {
2603
- onColumnOrderChange?.(
2604
- sourceIndex - utilityColumnCount,
2605
- targetIndex - utilityColumnCount,
3133
+ validColumnDropRef.current = null;
3134
+ if (validDrop) {
3135
+ applyColumnOrder(
3136
+ validDrop.sourceIndex - utilityColumnCount,
3137
+ validDrop.targetIndex - utilityColumnCount,
2606
3138
  {
2607
- type: source.data.headerKind === "group" ? "group" : "column",
3139
+ type: validDrop.headerKind,
2608
3140
  sourceKey: String(source.data.sourceKey),
2609
- targetKey: String(target.targetKey),
2610
- parentKey: typeof source.data.parentKey === "string" ? source.data.parentKey : void 0,
2611
- placement: getColumnDropPlacement(sourceIndex, rawTargetIndex, target.columnEdge)
3141
+ targetKey: validDrop.targetKey,
3142
+ parentKey: validDrop.parentKey,
3143
+ placement: getColumnDropPlacement(validDrop.sourceIndex, validDrop.rawTargetIndex, validDrop.columnEdge)
2612
3144
  }
2613
3145
  );
2614
3146
  }
@@ -2620,6 +3152,7 @@ function Table({
2620
3152
  const cleanupDrop = dropTargetForElements({
2621
3153
  element: canvas,
2622
3154
  canDrop: ({ source }) => source.data.type === "table-column" || source.data.type === "table-row",
3155
+ getDropEffect: () => "move",
2623
3156
  getData: ({ input, element, source }) => {
2624
3157
  if (source.data.type === "table-row") {
2625
3158
  const targetIndex2 = locateRow(input.clientY);
@@ -2628,7 +3161,8 @@ function Table({
2628
3161
  const rowEdge = input.clientY - canvas.getBoundingClientRect().top < rowTop + rowHeight / 2 ? "top" : "bottom";
2629
3162
  return attachClosestEdge({ targetIndex: targetIndex2, rowEdge }, { element, input, allowedEdges: ["top", "bottom"] });
2630
3163
  }
2631
- 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);
2632
3166
  if (!targetCell) return { targetIndex: -1 };
2633
3167
  let targetIndex = targetCell.startIndex;
2634
3168
  const targetFixed = columns[targetCell.startIndex]?.fixed;
@@ -2645,6 +3179,7 @@ function Table({
2645
3179
  const columnEdge = input.clientX - canvas.getBoundingClientRect().left < left + targetWidth / 2 ? "left" : "right";
2646
3180
  return attachClosestEdge({
2647
3181
  targetIndex,
3182
+ targetEndIndex: targetCell.endIndex,
2648
3183
  targetKey: targetCell.key,
2649
3184
  parentKey: targetCell.parentKey,
2650
3185
  headerKind,
@@ -2656,10 +3191,11 @@ function Table({
2656
3191
  }
2657
3192
  });
2658
3193
  return () => {
3194
+ document.documentElement.classList.remove("rvg-is-dragging");
2659
3195
  cleanupDrag();
2660
3196
  cleanupDrop();
2661
3197
  };
2662
- }, [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]);
2663
3199
  const locateCell = useCallback2((clientX, clientY) => {
2664
3200
  const canvas = canvasRef.current;
2665
3201
  if (!canvas) return null;
@@ -2967,18 +3503,28 @@ function Table({
2967
3503
  const editorStyle = useMemo(() => {
2968
3504
  if (!editing) return void 0;
2969
3505
  const metric = metrics[editing.columnIndex];
2970
- const fixedSide = columns[editing.columnIndex].fixed;
3506
+ const column = columns[editing.columnIndex];
3507
+ if (!metric || !column) return void 0;
3508
+ const fixedSide = column.fixed;
2971
3509
  const span = getCellSpan(editing.rowIndex, editing.columnIndex);
2972
3510
  const cellWidth = getCellDisplayWidth(editing.columnIndex, span?.colSpan ?? 1);
2973
3511
  const cellHeight = rowHeight * (span?.rowSpan ?? 1);
2974
3512
  const cellLeft = fixedSide === "left" ? leftFixedOffsets.get(editing.columnIndex) ?? 0 : fixedSide === "right" ? viewport.width - (rightFixedOffsets.get(editing.columnIndex) ?? 0) - metric.width : getScrollableColumnLeft(editing.columnIndex) - scrollRef.current.left;
3513
+ const cellTop = bodyTop + editing.rowIndex * rowHeight - scrollRef.current.top;
3514
+ const horizontalStart = fixedSide === void 0 ? fixedWidth : 0;
3515
+ const horizontalEnd = fixedSide === void 0 ? viewport.width - rightFixedWidth : viewport.width;
3516
+ const verticalStart = fixedHeader ? bodyTop : 0;
3517
+ const visibleLeft = Math.max(cellLeft, horizontalStart);
3518
+ const visibleRight = Math.min(cellLeft + cellWidth, horizontalEnd, viewport.width);
3519
+ const visibleTop = Math.max(cellTop, verticalStart);
3520
+ const visibleBottom = Math.min(cellTop + cellHeight, renderHeight);
2975
3521
  return {
2976
- left: (fixedSide ? cellLeft : Math.max(cellLeft, fixedWidth)) + 1,
2977
- top: Math.max(fixedHeader ? bodyTop : 0, bodyTop + editing.rowIndex * rowHeight - scrollRef.current.top) + 1,
2978
- width: cellWidth - 2,
2979
- height: cellHeight - 2
3522
+ left: visibleLeft,
3523
+ top: visibleTop,
3524
+ width: Math.max(0, visibleRight - visibleLeft),
3525
+ height: Math.max(0, visibleBottom - visibleTop)
2980
3526
  };
2981
- }, [bodyTop, columns, editing, fixedHeader, fixedWidth, getCellDisplayWidth, getCellSpan, getScrollableColumnLeft, leftFixedOffsets, metrics, rightFixedOffsets, rowHeight, viewport.width]);
3527
+ }, [bodyTop, columns, editing, fixedHeader, fixedWidth, getCellDisplayWidth, getCellSpan, getScrollableColumnLeft, leftFixedOffsets, metrics, renderHeight, rightFixedOffsets, rightFixedWidth, rowHeight, viewport.width]);
2982
3528
  const handleColumnPointerDown = useCallback2((event) => {
2983
3529
  const rect = event.currentTarget.getBoundingClientRect();
2984
3530
  const localY = event.clientY - rect.top;
@@ -2998,9 +3544,10 @@ function Table({
2998
3544
  columnIndex: resizeIndex,
2999
3545
  startX: event.clientX,
3000
3546
  startWidth: startWidths.reduce((total, width2) => total + width2, 0),
3001
- startGuideX,
3002
3547
  guideTop: headerRowOffsets[resizeCell.level] ?? 0,
3003
3548
  resizeEdge,
3549
+ resizeStartIndex: resizeCell.startIndex,
3550
+ resizeEndIndex: resizeCell.endIndex,
3004
3551
  resizeIndices,
3005
3552
  startWidths
3006
3553
  };
@@ -3017,6 +3564,12 @@ function Table({
3017
3564
  const minWidth = getMinimumColumnWidth(column, columnDraggable);
3018
3565
  const pointerDelta = Math.round(event.clientX - drag.startX);
3019
3566
  const delta = drag.resizeEdge === "left" ? -pointerDelta : pointerDelta;
3567
+ const setGuideFromWidths = (nextWidths) => {
3568
+ const widthByIndex = new Map(drag.resizeIndices.map((columnIndex, index) => [columnIndex, nextWidths[index]]));
3569
+ const nextColumns = columns.map((column2, index) => widthByIndex.has(index) ? { ...column2, width: widthByIndex.get(index) } : column2);
3570
+ const nextMetrics = buildColumnMetrics(nextColumns, { columnDraggable, viewportWidth: viewport.width });
3571
+ setResizeGuideX(getHeaderCellEdgeFromMetrics(drag.resizeStartIndex, drag.resizeEndIndex, drag.resizeEdge, nextMetrics));
3572
+ };
3020
3573
  if (drag.resizeIndices.length > 1) {
3021
3574
  const minWidths = drag.resizeIndices.map((index) => getMinimumColumnWidth(columns[index], columnDraggable));
3022
3575
  const minTotalWidth = minWidths.reduce((total, width3) => total + width3, 0);
@@ -3033,13 +3586,13 @@ function Table({
3033
3586
  drag.resizeIndices.forEach((columnIndex, index) => {
3034
3587
  resizeColumn(columns[columnIndex].key, nextWidths[index]);
3035
3588
  });
3036
- setResizeGuideX(drag.startGuideX + (drag.resizeEdge === "left" ? drag.startWidth - targetTotalWidth : targetTotalWidth - drag.startWidth));
3589
+ setGuideFromWidths(nextWidths);
3037
3590
  return;
3038
3591
  }
3039
3592
  const width2 = Math.max(minWidth, Math.round(drag.startWidth + delta));
3040
3593
  resizeColumn(column.key, width2);
3041
- setResizeGuideX(drag.startGuideX + (drag.resizeEdge === "left" ? drag.startWidth - width2 : width2 - drag.startWidth));
3042
- }, [columnDraggable, columns, resizeColumn]);
3594
+ setGuideFromWidths([width2]);
3595
+ }, [columnDraggable, columns, getHeaderCellEdgeFromMetrics, resizeColumn, viewport.width]);
3043
3596
  const handleColumnPointerUp = useCallback2((event) => {
3044
3597
  const drag = columnDragRef.current;
3045
3598
  if (!drag) return;
@@ -3049,19 +3602,14 @@ function Table({
3049
3602
  setResizeGuideTop(0);
3050
3603
  }, []);
3051
3604
  const queueSortStateChange = useCallback2((next) => {
3052
- setDisplaySortState(next);
3053
- if (sortDebounceRef.current !== null) clearTimeout(sortDebounceRef.current);
3054
- sortDebounceRef.current = setTimeout(() => {
3055
- sortDebounceRef.current = null;
3056
- onSortStateChange?.(next);
3057
- }, 200);
3058
- }, [onSortStateChange]);
3605
+ setSortState(next);
3606
+ }, []);
3059
3607
  const renderHeaderIcons = (columnIndex, left) => {
3060
3608
  const column = columns[columnIndex];
3061
3609
  if (column.rowSelection || column.rowDragHandle || column.rowNumber) return null;
3062
3610
  const top = headerLeafTop + (headerLeafHeight - 14) / 2;
3063
3611
  const visibleActions = getVisibleHeaderActions(column, metrics[columnIndex].width, columnDraggable, measureHeaderTitleWidth(columnIndex));
3064
- let right = left + metrics[columnIndex].width - (visibleActions.drag ? 18 : 4);
3612
+ let right = left + metrics[columnIndex].width - (visibleActions.drag ? HEADER_ACTION_SLOT_WIDTH : 2);
3065
3613
  const icons = [];
3066
3614
  if (visibleActions.drag) {
3067
3615
  const hovered = hoveredHeaderAction?.columnIndex === columnIndex && hoveredHeaderAction.action === "drag";
@@ -3071,11 +3619,11 @@ function Table({
3071
3619
  }
3072
3620
  if (visibleActions.sort) {
3073
3621
  const hovered = hoveredHeaderAction?.columnIndex === columnIndex && hoveredHeaderAction.action === "sort";
3074
- const direction = displaySortState?.columnKey === column.key ? displaySortState.direction : null;
3622
+ const direction = sortState?.columnKey === column.key ? sortState.direction : null;
3075
3623
  icons.push(
3076
3624
  /* @__PURE__ */ jsx7(HeaderSortIcon, { className: `rvg-header-icon${hovered ? " is-hovered" : ""}`, style: { left: right - 16, top }, direction }, "sort")
3077
3625
  );
3078
- right -= 18;
3626
+ right -= HEADER_ACTION_SLOT_WIDTH;
3079
3627
  }
3080
3628
  if (visibleActions.filter) {
3081
3629
  const hovered = hoveredHeaderAction?.columnIndex === columnIndex && hoveredHeaderAction.action === "filter";
@@ -3102,7 +3650,7 @@ function Table({
3102
3650
  if (context) context.font = "600 13px Inter, ui-sans-serif, system-ui, sans-serif";
3103
3651
  const titleWidth = context?.measureText(column.title).width ?? column.title.length * 13;
3104
3652
  const visibleActions = cell?.leaf || !cell ? getVisibleHeaderActions(column, cellWidth, columnDraggable, titleWidth) : null;
3105
- 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;
3106
3654
  const contentWidth2 = Math.max(0, cellWidth - actionWidth);
3107
3655
  const maxTextWidth = Math.max(0, contentWidth2 - 20);
3108
3656
  const textWidth = Math.min(titleWidth, maxTextWidth);
@@ -3121,7 +3669,7 @@ function Table({
3121
3669
  anchorLeft = anchorX;
3122
3670
  label = labels.dragColumn;
3123
3671
  } else if (action === "sort") {
3124
- const direction = displaySortState?.columnKey === column.key ? displaySortState.direction : null;
3672
+ const direction = sortState?.columnKey === column.key ? sortState.direction : null;
3125
3673
  label = direction === "asc" ? labels.sortAsc : direction === "desc" ? labels.sortDesc : labels.sortBoth;
3126
3674
  anchorX = cellLeft + cellWidth - 22;
3127
3675
  anchorLeft = anchorX;
@@ -3141,7 +3689,7 @@ function Table({
3141
3689
  label,
3142
3690
  placement
3143
3691
  };
3144
- }, [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]);
3145
3693
  const isPointerOnHeaderTitle = useCallback2((clientX, clientY, cell) => {
3146
3694
  const canvas = canvasRef.current;
3147
3695
  if (!canvas) return false;
@@ -3156,7 +3704,7 @@ function Table({
3156
3704
  if (context) context.font = "600 13px Inter, ui-sans-serif, system-ui, sans-serif";
3157
3705
  const titleWidth = context?.measureText(column.title).width ?? column.title.length * 13;
3158
3706
  const visibleActions = cell.leaf ? getVisibleHeaderActions(column, cellWidth, columnDraggable, titleWidth) : null;
3159
- 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;
3160
3708
  const contentWidth2 = Math.max(0, cellWidth - actionWidth);
3161
3709
  const textWidth = Math.min(titleWidth, Math.max(0, contentWidth2 - 20));
3162
3710
  const textLeft = column.align === "right" ? cellLeft + contentWidth2 - 10 - textWidth : column.align === "center" ? cellLeft + contentWidth2 / 2 - textWidth / 2 : cellLeft + 10;
@@ -3396,7 +3944,9 @@ function Table({
3396
3944
  });
3397
3945
  setPendingInserts((current) => [...current, pending]);
3398
3946
  try {
3399
- 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));
3400
3950
  await waitForPaint();
3401
3951
  setInsertBusy(false);
3402
3952
  } catch (error) {
@@ -3423,28 +3973,10 @@ function Table({
3423
3973
  const currentScrollTop = scrollRef.current.top;
3424
3974
  const top = (fixedHeader ? 0 : bodyTop) + rowIndex * rowHeight - currentScrollTop;
3425
3975
  if (column.rowSelection) {
3426
- const rowKey2 = getRowKey(rows[rowIndex], rowIndex);
3427
- return /* @__PURE__ */ jsx7(
3428
- "div",
3429
- {
3430
- className: "rvg-row-selection-icon",
3431
- style: { left, top, width: cellWidth, height: cellHeight, opacity: isDraggedSource ? 0 : 1, transform: `translateY(${dragOffset}px)` },
3432
- children: /* @__PURE__ */ jsx7(SelectionIcon, { checked: selectedRowKeySet.has(rowKey2), radio: rowSelectionMode === "single" })
3433
- },
3434
- `selection:${String(rowKey2)}`
3435
- );
3976
+ return null;
3436
3977
  }
3437
3978
  if (column.rowDragHandle) {
3438
- return /* @__PURE__ */ jsx7(
3439
- "div",
3440
- {
3441
- className: "rvg-row-drag-icon",
3442
- style: { left, top, width: cellWidth, height: cellHeight, opacity: isDraggedSource ? 0 : 1, transform: `translateY(${dragOffset}px)` },
3443
- "aria-hidden": "true",
3444
- children: /* @__PURE__ */ jsx7(RowDragHandleIcon, {})
3445
- },
3446
- `drag:${String(getRowKey(rows[rowIndex], rowIndex))}`
3447
- );
3979
+ return null;
3448
3980
  }
3449
3981
  if (column.renderCell && !(editing?.rowIndex === rowIndex && editing.columnIndex === columnIndex)) {
3450
3982
  const row = rows[rowIndex];
@@ -3471,6 +4003,7 @@ function Table({
3471
4003
  };
3472
4004
  const renderHeaderTitle = (columnIndex, left) => {
3473
4005
  const column = columns[columnIndex];
4006
+ const isDropTarget = Boolean(columnDropTarget && columnIndex >= columnDropTarget.startIndex && columnIndex <= columnDropTarget.endIndex);
3474
4007
  const currentScrollTop = scrollRef.current.top;
3475
4008
  const headerTop = fixedHeader ? 0 : -currentScrollTop;
3476
4009
  const leafTop = headerTop + headerLeafTop;
@@ -3478,7 +4011,7 @@ function Table({
3478
4011
  return /* @__PURE__ */ jsx7(
3479
4012
  "div",
3480
4013
  {
3481
- className: "rvg-header-title",
4014
+ className: `rvg-header-title${isDropTarget ? " is-drop-target" : ""}`,
3482
4015
  style: { left, top: headerTop, width: metrics[columnIndex].width, height: headerHeight, justifyContent: "center", padding: 0, textAlign: "center" },
3483
4016
  children: /* @__PURE__ */ jsx7("span", { children: column.title })
3484
4017
  },
@@ -3501,11 +4034,11 @@ function Table({
3501
4034
  }
3502
4035
  if (column.renderHeader) {
3503
4036
  const visibleActions = getVisibleHeaderActions(column, metrics[columnIndex].width, columnDraggable, measureHeaderTitleWidth(columnIndex));
3504
- 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;
3505
4038
  return /* @__PURE__ */ jsx7(
3506
4039
  "div",
3507
4040
  {
3508
- className: "rvg-header-title is-custom",
4041
+ className: `rvg-header-title is-custom${isDropTarget ? " is-drop-target" : ""}`,
3509
4042
  "data-level": headerDepth - 1,
3510
4043
  "data-measure-key": column.key,
3511
4044
  style: {
@@ -3546,11 +4079,12 @@ function Table({
3546
4079
  const left = fixedSide === "left" ? leftFixedOffsets.get(cell.startIndex) ?? 0 : fixedSide === "right" ? viewport.width - (rightFixedOffsets.get(cell.startIndex) ?? 0) - startMetric.width : getScrollableColumnLeft(cell.startIndex);
3547
4080
  const width2 = cell.leaf ? startMetric.width : getCellDisplayWidth(cell.startIndex, cell.endIndex - cell.startIndex + 1);
3548
4081
  const visibleActions = cell.leaf ? getVisibleHeaderActions(column, startMetric.width, columnDraggable, measureHeaderTitleWidth(cell.startIndex)) : null;
3549
- 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);
3550
4084
  return /* @__PURE__ */ jsxs7(
3551
4085
  "div",
3552
4086
  {
3553
- 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" : ""}`,
3554
4088
  "data-level": cell.level,
3555
4089
  "data-measure-key": `group:${cell.key}:${cell.level}`,
3556
4090
  style: {
@@ -3606,6 +4140,7 @@ function Table({
3606
4140
  })();
3607
4141
  const selectionOutlineStyle = (() => {
3608
4142
  if (!selection || selectionRange) return null;
4143
+ if (rowDragPreview?.sourceIndex === selection.rowIndex) return null;
3609
4144
  const row = rows[selection.rowIndex];
3610
4145
  const metric = metrics[selection.columnIndex];
3611
4146
  const column = columns[selection.columnIndex];
@@ -3615,22 +4150,37 @@ function Table({
3615
4150
  const rawRight = rawLeft + getCellDisplayWidth(selection.columnIndex, span?.colSpan ?? 1);
3616
4151
  const rawTop = bodyTop + selection.rowIndex * rowHeight - scrollPosition.top;
3617
4152
  const rawBottom = rawTop + rowHeight * (span?.rowSpan ?? 1);
4153
+ const dragOffset = getRowDragOffset(selection.rowIndex);
3618
4154
  const horizontalStart = column.fixed === void 0 ? fixedWidth : 0;
3619
4155
  const horizontalEnd = column.fixed === void 0 ? viewport.width - rightFixedWidth : viewport.width;
3620
4156
  const verticalStart = fixedHeader ? bodyTop : 0;
3621
- 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;
3622
4158
  const borderWidth = 2;
3623
- const left = Math.round(Math.max(rawLeft - borderWidth, horizontalStart - 1));
4159
+ const left = Math.round(Math.max(rawLeft - borderWidth, horizontalStart));
3624
4160
  const right = Math.round(Math.min(rawRight + 1, horizontalEnd, viewport.width));
3625
- const top = Math.round(Math.max(rawTop - borderWidth, verticalStart - 1));
4161
+ const top = Math.round(Math.max(rawTop - borderWidth, verticalStart));
3626
4162
  const bottom = Math.round(Math.min(rawBottom + 1, renderHeight));
3627
4163
  return {
3628
4164
  left,
3629
4165
  top,
3630
4166
  width: Math.max(0, right - left),
3631
- height: Math.max(0, bottom - top)
4167
+ height: Math.max(0, bottom - top),
4168
+ transform: `translateY(${dragOffset}px)`
3632
4169
  };
3633
4170
  })();
4171
+ const selectionEdgeIndicator = getSelectionEdgeIndicator(scrollPosition.left, scrollPosition.top);
4172
+ const selectionEdgeStyle = selectionEdgeIndicator ? {
4173
+ ...selectionEdgeIndicator.style,
4174
+ ...selectionEdgeIndicator.corner ? {
4175
+ "--rvg-corner-width": `${selectionEdgeIndicator.corner.width}px`,
4176
+ "--rvg-corner-height": `${selectionEdgeIndicator.corner.height}px`,
4177
+ "--rvg-corner-hover-width-extension": "4px",
4178
+ "--rvg-corner-hover-height-extension": "4px"
4179
+ } : null
4180
+ } : void 0;
4181
+ useLayoutEffect2(() => {
4182
+ syncSelectionEdgeTarget(scrollPosition.left, scrollPosition.top);
4183
+ }, [scrollPosition.left, scrollPosition.top, syncSelectionEdgeTarget]);
3634
4184
  const handleContextMenu = (event) => {
3635
4185
  event.preventDefault();
3636
4186
  event.stopPropagation();
@@ -3774,6 +4324,10 @@ function Table({
3774
4324
  className: "rvg-canvas",
3775
4325
  style: { width: viewport.width, height: renderHeight },
3776
4326
  onMouseMove: (event) => {
4327
+ if (document.documentElement.classList.contains("rvg-is-dragging")) {
4328
+ hideDragTooltips();
4329
+ return;
4330
+ }
3777
4331
  if (columnDragRef.current) return;
3778
4332
  const rect = event.currentTarget.getBoundingClientRect();
3779
4333
  const localY = event.clientY - rect.top;
@@ -3876,6 +4430,14 @@ function Table({
3876
4430
  }
3877
4431
  },
3878
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
+ }
3879
4441
  handleColumnPointerDown(event);
3880
4442
  beginRangeDrag(event);
3881
4443
  },
@@ -3884,10 +4446,12 @@ function Table({
3884
4446
  updateRangeDrag(event);
3885
4447
  },
3886
4448
  onPointerUp: (event) => {
4449
+ document.documentElement.classList.remove("rvg-is-dragging");
3887
4450
  handleColumnPointerUp(event);
3888
4451
  endRangeDrag(event);
3889
4452
  },
3890
4453
  onPointerCancel: (event) => {
4454
+ document.documentElement.classList.remove("rvg-is-dragging");
3891
4455
  handleColumnPointerUp(event);
3892
4456
  endRangeDrag(event);
3893
4457
  },
@@ -3906,7 +4470,7 @@ function Table({
3906
4470
  const action = locateHeaderAction(event.clientX, columnIndex);
3907
4471
  if (action === "sort") {
3908
4472
  const columnKey = columns[columnIndex].key;
3909
- 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;
3910
4474
  queueSortStateChange(next);
3911
4475
  return;
3912
4476
  }
@@ -4078,6 +4642,27 @@ function Table({
4078
4642
  }
4079
4643
  ),
4080
4644
  selectionOutlineStyle && /* @__PURE__ */ jsx7("div", { ref: selectionFocusRef, className: "rvg-selection-focus", style: selectionOutlineStyle, "aria-hidden": "true" }),
4645
+ selectionEdgeIndicator && selection && /* @__PURE__ */ jsx7(
4646
+ "button",
4647
+ {
4648
+ ref: selectionEdgeTargetRef,
4649
+ type: "button",
4650
+ className: `rvg-selection-edge-target is-${selectionEdgeIndicator.orientation} is-${selectionEdgeIndicator.side}${selectionEdgeIndicator.viewportEdge ? ` is-viewport-${selectionEdgeIndicator.viewportEdge}` : ""}`,
4651
+ style: selectionEdgeStyle,
4652
+ "aria-label": labels.revealSelectedCell,
4653
+ title: labels.revealSelectedCell,
4654
+ onPointerDown: (event) => {
4655
+ event.preventDefault();
4656
+ event.stopPropagation();
4657
+ },
4658
+ onClick: (event) => {
4659
+ event.stopPropagation();
4660
+ scrollerRef.current?.focus({ preventScroll: true });
4661
+ revealCell(selection);
4662
+ },
4663
+ children: /* @__PURE__ */ jsx7("span", { className: "rvg-selection-edge-halo", "aria-hidden": "true", children: /* @__PURE__ */ jsx7("svg", { viewBox: "0 0 100 100", preserveAspectRatio: "none", children: /* @__PURE__ */ jsx7("path", { d: "M 0 100 L 0 0 L 100 0" }) }) })
4664
+ }
4665
+ ),
4081
4666
  contextMenu && /* @__PURE__ */ jsx7("div", { ref: contextMenuRef, className: "rvg-context-menu", role: "menu", style: { left: contextMenu.left, top: contextMenu.top }, children: contextMenu.type === "header" ? (() => {
4082
4667
  const column = columns[contextMenu.columnIndex];
4083
4668
  const headerContext = { column, columnIndex: contextMenu.columnIndex };
@@ -4235,12 +4820,12 @@ function Table({
4235
4820
  /* @__PURE__ */ jsx7("span", { children: labels.rowUnit })
4236
4821
  ] });
4237
4822
  if (item === "move-up") {
4238
- if (!rowDraggable || !onRowOrderChange) return null;
4239
- 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" }) });
4240
4825
  }
4241
4826
  if (item === "move-down") {
4242
- if (!rowDraggable || !onRowOrderChange) return null;
4243
- 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" }) });
4244
4829
  }
4245
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" }) });
4246
4831
  return null;
@@ -4272,21 +4857,30 @@ function Table({
4272
4857
  await Promise.resolve(onCellChange?.({ ...confirmAction.cell, row: confirmAction.row, previousValue: confirmAction.previousValue, value: "" }));
4273
4858
  } else {
4274
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)));
4275
4863
  setConfirmAction(null);
4276
4864
  setConfirmLoading(false);
4865
+ setPendingDeletedRowKeys((current) => {
4866
+ const next = new Set(current);
4867
+ deletingKeys.forEach((key) => next.add(key));
4868
+ return next;
4869
+ });
4277
4870
  if (onDeleteRows) {
4278
4871
  setDeleteBusy(true);
4279
4872
  try {
4280
- 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;
4281
4881
  } finally {
4282
4882
  setDeleteBusy(false);
4283
4883
  }
4284
- } else {
4285
- setPendingDeletedRowKeys((current) => {
4286
- const next = new Set(current);
4287
- deletingKeys.forEach((key) => next.add(key));
4288
- return next;
4289
- });
4290
4884
  }
4291
4885
  return;
4292
4886
  }
@@ -4359,7 +4953,7 @@ function Table({
4359
4953
  const next = { ...filterValues };
4360
4954
  if (filterEditor.draft) next[key] = filterEditor.draft;
4361
4955
  else delete next[key];
4362
- onFilterValuesChange?.(next);
4956
+ setFilterValues(next);
4363
4957
  setFilterEditor(null);
4364
4958
  }
4365
4959
  }
@@ -4370,7 +4964,7 @@ function Table({
4370
4964
  const key = columns[filterEditor.columnIndex].key;
4371
4965
  const next = { ...filterValues };
4372
4966
  delete next[key];
4373
- onFilterValuesChange?.(next);
4967
+ setFilterValues(next);
4374
4968
  setFilterEditor(null);
4375
4969
  }, children: labels.reset }),
4376
4970
  /* @__PURE__ */ jsx7("button", { type: "button", className: "rvg-filter-confirm", onClick: () => {
@@ -4378,7 +4972,7 @@ function Table({
4378
4972
  const next = { ...filterValues };
4379
4973
  if (filterEditor.draft) next[key] = filterEditor.draft;
4380
4974
  else delete next[key];
4381
- onFilterValuesChange?.(next);
4975
+ setFilterValues(next);
4382
4976
  setFilterEditor(null);
4383
4977
  }, children: labels.confirm })
4384
4978
  ] })
@@ -4402,8 +4996,8 @@ function Table({
4402
4996
  column.key
4403
4997
  );
4404
4998
  }) }, rowIndex)),
4405
- fixedWidth > 0 && /* @__PURE__ */ jsx7("div", { className: "rvg-loading-fixed-shadow is-left", style: { left: fixedWidth } }),
4406
- rightFixedWidth > 0 && /* @__PURE__ */ jsx7("div", { className: "rvg-loading-fixed-shadow is-right", style: { right: rightFixedWidth } })
4999
+ fixedWidth > 0 && currentScroll.left > 0 && /* @__PURE__ */ jsx7("div", { className: "rvg-loading-fixed-shadow is-left", style: { left: fixedWidth } }),
5000
+ rightFixedWidth > 0 && currentScroll.left < contentWidth - viewport.width && /* @__PURE__ */ jsx7("div", { className: "rvg-loading-fixed-shadow is-right", style: { right: rightFixedWidth } })
4407
5001
  ] }),
4408
5002
  loading && !hasCustomLoading && hasCompletedLoadRef.current && /* @__PURE__ */ jsx7("div", { className: "rvg-refresh-loading", "aria-label": labels.refreshing, children: /* @__PURE__ */ jsx7(LoadingSpinnerIcon, {}) })
4409
5003
  ] });