@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.cjs CHANGED
@@ -57,6 +57,7 @@ var ZH_CN_GRID_LABELS = {
57
57
  sortDesc: "\u5F53\u524D\uFF1A\u964D\u5E8F",
58
58
  sortBoth: "\u5347\u5E8F / \u964D\u5E8F",
59
59
  dragColumn: "\u62D6\u52A8\u5217\u6392\u5E8F",
60
+ revealSelectedCell: "\u5B9A\u4F4D\u5230\u9009\u4E2D\u5355\u5143\u683C",
60
61
  previousYear: "\u4E0A\u4E00\u5E74",
61
62
  nextYear: "\u4E0B\u4E00\u5E74",
62
63
  previousMonth: "\u4E0A\u4E2A\u6708",
@@ -125,6 +126,7 @@ var EN_US_GRID_LABELS = {
125
126
  sortDesc: "Current: descending",
126
127
  sortBoth: "Ascending / descending",
127
128
  dragColumn: "Drag column to reorder",
129
+ revealSelectedCell: "Reveal selected cell",
128
130
  previousYear: "Previous year",
129
131
  nextYear: "Next year",
130
132
  previousMonth: "Previous month",
@@ -316,16 +318,6 @@ function ContextMenuIcon({ type }) {
316
318
  };
317
319
  return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("svg", { className: "rvg-context-menu-svg", viewBox: "0 0 16 16", "aria-hidden": "true", children: paths[type].map((path) => /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("path", { d: path }, path)) });
318
320
  }
319
- function RowDragHandleIcon() {
320
- return /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("svg", { viewBox: "0 0 16 16", focusable: "false", children: [
321
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("circle", { cx: "5", cy: "4", r: "1" }),
322
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("circle", { cx: "11", cy: "4", r: "1" }),
323
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("circle", { cx: "5", cy: "8", r: "1" }),
324
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("circle", { cx: "11", cy: "8", r: "1" }),
325
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("circle", { cx: "5", cy: "12", r: "1" }),
326
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("circle", { cx: "11", cy: "12", r: "1" })
327
- ] });
328
- }
329
321
  function LoadingSpinnerIcon() {
330
322
  return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("svg", { viewBox: "0 0 24 24", "aria-hidden": "true", children: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("path", { d: "M20 12a8 8 0 1 1-3.1-6.3" }) });
331
323
  }
@@ -333,7 +325,6 @@ function LoadingSpinnerIcon() {
333
325
  // src/components/editors/ChoiceEditor.tsx
334
326
  var import_jsx_runtime3 = require("react/jsx-runtime");
335
327
  function ChoiceEditor({ value, options, style, labels, onChange, onCommit, onCancel }) {
336
- const editorStyle = typeof style.width === "number" ? { ...style, width: Math.max(0, style.width - 1) } : style;
337
328
  const collection = (0, import_select.createListCollection)({
338
329
  items: options,
339
330
  itemToValue: (item) => item.value,
@@ -343,7 +334,7 @@ function ChoiceEditor({ value, options, style, labels, onChange, onCommit, onCan
343
334
  import_select.Select.Root,
344
335
  {
345
336
  className: "rvg-choice-editor",
346
- style: editorStyle,
337
+ style,
347
338
  collection,
348
339
  value: value ? [value] : [],
349
340
  defaultOpen: true,
@@ -754,6 +745,48 @@ function createRowDragHandleSvg() {
754
745
  }
755
746
  return svg;
756
747
  }
748
+ function createHeaderDragHandleSvg() {
749
+ const svg = document.createElementNS("http://www.w3.org/2000/svg", "svg");
750
+ svg.setAttribute("viewBox", "0 0 14 14");
751
+ svg.setAttribute("aria-hidden", "true");
752
+ for (const cy of [2.5, 7, 11.5]) {
753
+ const circle = document.createElementNS("http://www.w3.org/2000/svg", "circle");
754
+ circle.setAttribute("cx", "7");
755
+ circle.setAttribute("cy", String(cy));
756
+ circle.setAttribute("r", "1.25");
757
+ svg.appendChild(circle);
758
+ }
759
+ return svg;
760
+ }
761
+ function createHeaderSortSvg(direction) {
762
+ const svg = document.createElementNS("http://www.w3.org/2000/svg", "svg");
763
+ svg.setAttribute("viewBox", "0 0 14 14");
764
+ svg.setAttribute("aria-hidden", "true");
765
+ for (const [value, active] of [
766
+ ["M3 5.5 7 1.5l4 4Z", direction === "asc"],
767
+ ["M3 8.5 7 12.5l4-4Z", direction === "desc"]
768
+ ]) {
769
+ const path = document.createElementNS("http://www.w3.org/2000/svg", "path");
770
+ path.setAttribute("d", value);
771
+ if (active) path.classList.add("is-active");
772
+ svg.appendChild(path);
773
+ }
774
+ return svg;
775
+ }
776
+ function createHeaderSearchSvg(active) {
777
+ const svg = document.createElementNS("http://www.w3.org/2000/svg", "svg");
778
+ svg.setAttribute("viewBox", "0 0 14 14");
779
+ svg.setAttribute("aria-hidden", "true");
780
+ if (active) svg.classList.add("is-active");
781
+ const circle = document.createElementNS("http://www.w3.org/2000/svg", "circle");
782
+ circle.setAttribute("cx", "5.5");
783
+ circle.setAttribute("cy", "5.5");
784
+ circle.setAttribute("r", "4.25");
785
+ const path = document.createElementNS("http://www.w3.org/2000/svg", "path");
786
+ path.setAttribute("d", "m8.75 8.75 2.25 2.25");
787
+ svg.append(circle, path);
788
+ return svg;
789
+ }
757
790
 
758
791
  // src/Table.tsx
759
792
  var import_adapter = require("@atlaskit/pragmatic-drag-and-drop/element/adapter");
@@ -774,6 +807,7 @@ function getDisplayLabel(column, row, rowIndex) {
774
807
 
775
808
  // src/core/layout.ts
776
809
  var DEFAULT_COLUMN_WIDTH = 140;
810
+ var HEADER_ACTION_SLOT_WIDTH = 16;
777
811
  function isDataColumn(column) {
778
812
  return !column.rowSelection && !column.rowDragHandle && !column.rowNumber;
779
813
  }
@@ -788,7 +822,7 @@ function getHeaderTitleRequiredWidth(column, titleWidth) {
788
822
  return minimumReadableTitleWidth + (column.align === "center" ? 20 : 22);
789
823
  }
790
824
  function getVisibleHeaderActions(column, width, columnDraggable = false, titleWidth = 26) {
791
- const capacity = Math.max(0, Math.floor((width - getHeaderTitleRequiredWidth(column, titleWidth)) / 18));
825
+ const capacity = Math.max(0, Math.floor((width - getHeaderTitleRequiredWidth(column, titleWidth)) / HEADER_ACTION_SLOT_WIDTH));
792
826
  const candidates = [
793
827
  isColumnFilterable(column) ? "filter" : null,
794
828
  isColumnSortable(column) ? "sort" : null,
@@ -867,12 +901,15 @@ var COLORS = {
867
901
  grid: "#e3e6e8",
868
902
  text: "#202124",
869
903
  muted: "#5f6368",
904
+ icon: "#9aa0a6",
905
+ borderStrong: "#b8bec4",
870
906
  selection: "#1677ff",
871
907
  selectionFill: "#edf4ff",
872
908
  axisSelectionFill: "#e8f2ff",
873
909
  rowHoverFill: "#f6f9fc",
874
910
  editedFill: "#fff1b8",
875
911
  insertedFill: "#c8ead4",
912
+ columnDropTargetFill: "#eeeeee",
876
913
  stripe: "#fafbfc"
877
914
  };
878
915
  function ellipsizeText(context, text, maxWidth) {
@@ -892,7 +929,7 @@ function ellipsizeText(context, text, maxWidth) {
892
929
  return `${text.slice(0, start)}${ellipsis}`;
893
930
  }
894
931
  function paintGrid(options) {
895
- const { context: ctx, width, height, pixelRatio, scrollLeft, scrollTop, rowHeight, headerHeight, fixedHeader, verticalBorderless, striped, columnDraggable, sortState, filterValues, hoveredHeaderAction, rows, columns, metrics, range, selection, editing, hoveredRowIndex, selectionRange, selectedRowKeys, selectedColumnKeys, highlightEditedCells, highlightInsertedRows, insertedRowKeys, editedCellKeys, cellAnnotations, getRowKey, rowDragPreview } = options;
932
+ const { context: ctx, width, height, pixelRatio, scrollLeft, scrollTop, rowHeight, headerHeight, fixedHeader, verticalBorderless, striped, columnDraggable, sortState, filterValues, hoveredHeaderAction, rows, columns, metrics, range, selection, editing, hoveredRowIndex, selectionRange, selectedRowKeys, rowSelectionMode, selectedColumnKeys, highlightEditedCells, highlightInsertedRows, insertedRowKeys, editedCellKeys, cellAnnotations, getRowKey, rowDragPreview, columnDropTarget } = options;
896
933
  const headerLeafTop = options.headerLeafTop ?? 0;
897
934
  const headerLeafHeight = options.headerLeafHeight ?? headerHeight;
898
935
  const colors = { ...COLORS, ...options.colors };
@@ -914,6 +951,7 @@ function paintGrid(options) {
914
951
  const leftFixedWidth = columns.reduce((value, column, index) => column.fixed === "left" ? value + metrics[index].width : value, 0);
915
952
  const rightFixedWidth = columns.reduce((value, column, index) => column.fixed === "right" ? value + metrics[index].width : value, 0);
916
953
  const rightFixedLeft = width - rightFixedWidth;
954
+ const rightFixedBoundaryInset = rightFixedWidth > 0 && !verticalBorderless ? 1 : 0;
917
955
  const leftOffsets = /* @__PURE__ */ new Map();
918
956
  let leftOffset = 0;
919
957
  for (let index = 0; index < columns.length; index += 1) {
@@ -937,11 +975,19 @@ function paintGrid(options) {
937
975
  const getColumnX = (index) => {
938
976
  return columns[index].fixed === "left" ? leftOffsets.get(index) ?? 0 : columns[index].fixed === "right" ? width - (rightOffsets.get(index) ?? 0) - metrics[index].width : (scrollableLefts.get(index) ?? leftFixedWidth) - scrollLeft;
939
977
  };
978
+ const getRowDragOffset = (rowIndex) => {
979
+ if (!rowDragPreview || rowIndex === rowDragPreview.sourceIndex) return 0;
980
+ if (rowDragPreview.sourceIndex < rowDragPreview.targetIndex) {
981
+ return rowIndex > rowDragPreview.sourceIndex && rowIndex <= rowDragPreview.targetIndex ? -rowHeight : 0;
982
+ }
983
+ return rowIndex >= rowDragPreview.targetIndex && rowIndex < rowDragPreview.sourceIndex ? rowHeight : 0;
984
+ };
985
+ const rowBelowDragGap = rowDragPreview ? rowDragPreview.sourceIndex <= rowDragPreview.targetIndex ? rowDragPreview.targetIndex + 1 : rowDragPreview.targetIndex : -1;
940
986
  const paintBodyColumns = (layer) => {
941
987
  const rowStart = Math.max(0, range.rowStart - Math.max(0, options.maxRowSpan - 1));
942
988
  for (let rowIndex = rowStart; rowIndex < range.rowEnd; rowIndex += 1) {
943
989
  if (rowDragPreview?.sourceIndex === rowIndex) continue;
944
- const shift = rowDragPreview ? rowDragPreview.sourceIndex < rowDragPreview.targetIndex ? rowIndex > rowDragPreview.sourceIndex && rowIndex <= rowDragPreview.targetIndex ? -rowHeight : 0 : rowIndex >= rowDragPreview.targetIndex && rowIndex < rowDragPreview.sourceIndex ? rowHeight : 0 : 0;
990
+ const shift = getRowDragOffset(rowIndex);
945
991
  const y = bodyTop + rowIndex * rowHeight - scrollTop + shift;
946
992
  const row = rows[rowIndex];
947
993
  const rowKey = getRowKey(row, rowIndex);
@@ -974,6 +1020,10 @@ function paintGrid(options) {
974
1020
  ctx.fillStyle = colors.rowHoverFill;
975
1021
  ctx.fillRect(x, y, cellWidth, cellHeight);
976
1022
  }
1023
+ if (columnDropTarget && columnIndex >= columnDropTarget.startIndex && columnIndex <= columnDropTarget.endIndex) {
1024
+ ctx.fillStyle = colors.columnDropTargetFill;
1025
+ ctx.fillRect(x, y, cellWidth, cellHeight);
1026
+ }
977
1027
  if (highlightInsertedRows && insertedRowKeys.has(rowKey)) {
978
1028
  ctx.fillStyle = colors.insertedFill;
979
1029
  ctx.fillRect(x, y, cellWidth, cellHeight);
@@ -1000,6 +1050,7 @@ function paintGrid(options) {
1000
1050
  }
1001
1051
  ctx.fillStyle = colors.grid;
1002
1052
  if (!verticalBorderless) ctx.fillRect(x + cellWidth - 1, y, 1, cellHeight);
1053
+ if (rowIndex === rowBelowDragGap) ctx.fillRect(x, y, cellWidth, 1);
1003
1054
  if (!options.suppressLastRowBottomBorder || rowIndex !== rows.length - 1) {
1004
1055
  ctx.fillRect(x, y + cellHeight - 1, cellWidth, 1);
1005
1056
  }
@@ -1026,6 +1077,59 @@ function paintGrid(options) {
1026
1077
  if (columnIndex === rangeColumnStart) ctx.fillRect(x, y, 2, cellHeight);
1027
1078
  if (columnIndex === rangeColumnEnd) ctx.fillRect(x + cellWidth - 2, y, 2, cellHeight);
1028
1079
  }
1080
+ if (column.rowDragHandle) {
1081
+ const iconLeft = x + (cellWidth - 16) / 2;
1082
+ const iconTop = y + (cellHeight - 16) / 2;
1083
+ ctx.fillStyle = colors.icon;
1084
+ ctx.beginPath();
1085
+ for (const circleX of [5, 11]) {
1086
+ for (const circleY of [4, 8, 12]) {
1087
+ ctx.moveTo(iconLeft + circleX + 1, iconTop + circleY);
1088
+ ctx.arc(iconLeft + circleX, iconTop + circleY, 1, 0, Math.PI * 2);
1089
+ }
1090
+ }
1091
+ ctx.fill();
1092
+ }
1093
+ if (column.rowSelection) {
1094
+ const iconLeft = x + (cellWidth - 16) / 2;
1095
+ const iconTop = y + (cellHeight - 16) / 2;
1096
+ const checked = selectedRowKeys.has(rowKey);
1097
+ ctx.save();
1098
+ ctx.lineWidth = 1;
1099
+ if (rowSelectionMode === "single") {
1100
+ ctx.beginPath();
1101
+ ctx.arc(iconLeft + 8, iconTop + 8, 6.25, 0, Math.PI * 2);
1102
+ ctx.fillStyle = colors.background;
1103
+ ctx.fill();
1104
+ ctx.strokeStyle = checked ? colors.selection : colors.borderStrong;
1105
+ ctx.stroke();
1106
+ if (checked) {
1107
+ ctx.beginPath();
1108
+ ctx.arc(iconLeft + 8, iconTop + 8, 3.25, 0, Math.PI * 2);
1109
+ ctx.fillStyle = colors.selection;
1110
+ ctx.fill();
1111
+ }
1112
+ } else {
1113
+ ctx.beginPath();
1114
+ ctx.roundRect(iconLeft + 1.5, iconTop + 1.5, 13, 13, 3);
1115
+ ctx.fillStyle = checked ? colors.selection : colors.background;
1116
+ ctx.fill();
1117
+ ctx.strokeStyle = checked ? colors.selection : colors.borderStrong;
1118
+ ctx.stroke();
1119
+ if (checked) {
1120
+ ctx.beginPath();
1121
+ ctx.moveTo(iconLeft + 4.1, iconTop + 8);
1122
+ ctx.lineTo(iconLeft + 6.45, iconTop + 10.35);
1123
+ ctx.lineTo(iconLeft + 11.9, iconTop + 5.15);
1124
+ ctx.strokeStyle = colors.background;
1125
+ ctx.lineWidth = 1.75;
1126
+ ctx.lineCap = "round";
1127
+ ctx.lineJoin = "round";
1128
+ ctx.stroke();
1129
+ }
1130
+ }
1131
+ ctx.restore();
1132
+ }
1029
1133
  if (!column.rowDragHandle && !column.rowSelection && !column.renderCell && !(editing?.rowIndex === rowIndex && editing.columnIndex === columnIndex)) {
1030
1134
  let label = "";
1031
1135
  try {
@@ -1081,6 +1185,10 @@ function paintGrid(options) {
1081
1185
  ctx.fillStyle = colors.selectionFill;
1082
1186
  ctx.fillRect(x, headerY, metric.width, headerHeight);
1083
1187
  }
1188
+ if (columnDropTarget && columnIndex >= columnDropTarget.startIndex && columnIndex <= columnDropTarget.endIndex) {
1189
+ ctx.fillStyle = colors.columnDropTargetFill;
1190
+ ctx.fillRect(x, headerY, metric.width, headerHeight);
1191
+ }
1084
1192
  ctx.fillStyle = colors.grid;
1085
1193
  if (!verticalBorderless) ctx.fillRect(x + metric.width - 1, headerY, 1, headerHeight);
1086
1194
  ctx.fillRect(x, headerY + headerHeight - 1, metric.width, 1);
@@ -1089,7 +1197,7 @@ function paintGrid(options) {
1089
1197
  ctx.font = "600 13px Inter, ui-sans-serif, system-ui, sans-serif";
1090
1198
  const titleWidth = ctx.measureText(column.title).width;
1091
1199
  const visibleActions = getVisibleHeaderActions(column, metric.width, columnDraggable, titleWidth);
1092
- const actionWidth = (Number(visibleActions.drag) + Number(visibleActions.filter) + Number(visibleActions.sort)) * 18;
1200
+ const actionWidth = (Number(visibleActions.drag) + Number(visibleActions.filter) + Number(visibleActions.sort)) * HEADER_ACTION_SLOT_WIDTH;
1093
1201
  const contentWidth = Math.max(0, metric.width - actionWidth);
1094
1202
  const padding = 10;
1095
1203
  const clipWidth = contentWidth;
@@ -1137,28 +1245,28 @@ function paintGrid(options) {
1137
1245
  const markerBottom = Math.min(bottom, height);
1138
1246
  const startX = getColumnX(columnStart);
1139
1247
  const endX = getColumnX(columnEnd) + metrics[columnEnd].width;
1140
- const visibleLeft = columns[columnStart].fixed === "left" ? Math.max(0, startX) : Math.max(leftFixedWidth - 1, startX);
1248
+ const visibleLeft = columns[columnStart].fixed === "left" ? Math.max(0, startX) : Math.max(leftFixedWidth, startX);
1141
1249
  const visibleRight = columns[columnEnd].fixed === "right" ? Math.min(width, endX) : Math.min(rightFixedLeft, endX);
1142
1250
  ctx.fillStyle = colors.selection;
1143
1251
  if (markerBottom > markerTop) {
1144
1252
  if (columns[columnStart].fixed === void 0 && startX < leftFixedWidth) {
1145
- ctx.fillRect(Math.max(0, leftFixedWidth - 1), markerTop, 2, markerBottom - markerTop);
1253
+ ctx.fillRect(Math.max(0, leftFixedWidth), markerTop, 2, markerBottom - markerTop);
1146
1254
  }
1147
1255
  if (columns[columnEnd].fixed === void 0 && endX > rightFixedLeft) {
1148
- ctx.fillRect(Math.max(0, rightFixedLeft - 1), markerTop, 2, markerBottom - markerTop);
1256
+ ctx.fillRect(Math.max(0, rightFixedLeft - 2 - rightFixedBoundaryInset), markerTop, 2, markerBottom - markerTop);
1149
1257
  }
1150
1258
  }
1151
1259
  if (visibleRight > visibleLeft) {
1152
1260
  if (top < clipTop) ctx.fillRect(visibleLeft, clipTop, visibleRight - visibleLeft, 2);
1153
1261
  if (bottom > height) ctx.fillRect(visibleLeft, height - 2, visibleRight - visibleLeft, 2);
1154
1262
  } else if (top < clipTop || bottom > height) {
1155
- const markerX = endX <= leftFixedWidth ? Math.max(0, leftFixedWidth - 1) : Math.max(0, rightFixedLeft - 1);
1263
+ const markerX = endX <= leftFixedWidth ? Math.max(0, leftFixedWidth) : Math.max(0, rightFixedLeft - 2 - rightFixedBoundaryInset);
1156
1264
  ctx.fillRect(markerX, top < clipTop ? clipTop : height - 2, 2, 2);
1157
1265
  }
1158
1266
  }
1159
1267
  const selectedRow = selection ? rows[selection.rowIndex] : void 0;
1160
1268
  const hasValidSelection = Boolean(selection && selectedRow && getRowKey(selectedRow, selection.rowIndex) === selection.rowKey);
1161
- if (selection && hasValidSelection && !selectionRange) {
1269
+ if (selection && hasValidSelection && !selectionRange && !rowDragPreview) {
1162
1270
  const metric = metrics[selection.columnIndex];
1163
1271
  if (metric) {
1164
1272
  const fixedSide = columns[selection.columnIndex].fixed;
@@ -1167,22 +1275,22 @@ function paintGrid(options) {
1167
1275
  const selectionWidth = getCellWidth(selection.columnIndex, span?.colSpan ?? 1);
1168
1276
  const selectionHeight = rowHeight * (span?.rowSpan ?? 1);
1169
1277
  const x = Math.round(getColumnX(selection.columnIndex));
1170
- const y = Math.round(bodyTop + selection.rowIndex * rowHeight - scrollTop);
1278
+ const y = Math.round(bodyTop + selection.rowIndex * rowHeight - scrollTop + getRowDragOffset(selection.rowIndex));
1171
1279
  const borderWidth = 2;
1172
1280
  const visibleLeft = fixedSide === "left" ? 0 : leftFixedWidth;
1173
1281
  const selectionAreaRight = fixedSide === "right" ? width : width - rightFixedWidth;
1174
1282
  const visibleRight = x + selectionWidth;
1175
1283
  const visibleBottom = y + selectionHeight;
1176
1284
  const selectionTop = fixedHeader ? bodyTop : 0;
1177
- const selectionClipTop = fixedHeader ? Math.max(0, selectionTop - 1) : selectionTop;
1285
+ const selectionClipTop = selectionTop;
1178
1286
  const hiddenAboveHeader = visibleBottom <= selectionTop;
1179
1287
  const hiddenBelowViewport = y >= height;
1180
1288
  const hiddenBehindLeft = !isFixed && visibleRight <= visibleLeft;
1181
1289
  const hiddenBehindRight = !isFixed && x >= selectionAreaRight;
1182
- const leftMarkerX = Math.max(0, leftFixedWidth - 1);
1290
+ const leftMarkerX = Math.max(0, leftFixedWidth);
1183
1291
  if ((hiddenAboveHeader || hiddenBelowViewport) && (hiddenBehindLeft || hiddenBehindRight)) {
1184
1292
  const markerSize = 2;
1185
- const markerX = hiddenBehindLeft ? leftMarkerX : selectionAreaRight - borderWidth;
1293
+ const markerX = hiddenBehindLeft ? leftMarkerX : selectionAreaRight - borderWidth - rightFixedBoundaryInset;
1186
1294
  const markerY = hiddenAboveHeader ? selectionClipTop : height - markerSize;
1187
1295
  ctx.fillStyle = colors.selection;
1188
1296
  ctx.fillRect(markerX, markerY, markerSize, markerSize);
@@ -1190,7 +1298,7 @@ function paintGrid(options) {
1190
1298
  }
1191
1299
  ctx.save();
1192
1300
  ctx.beginPath();
1193
- const clipLeft = fixedSide === "left" ? 0 : Math.max(0, leftFixedWidth - 1);
1301
+ const clipLeft = fixedSide === "left" ? 0 : Math.max(0, leftFixedWidth);
1194
1302
  const clipRight = Math.min(width, selectionAreaRight + 1);
1195
1303
  ctx.rect(clipLeft, selectionClipTop, clipRight - clipLeft, height - selectionClipTop);
1196
1304
  ctx.clip();
@@ -1208,14 +1316,14 @@ function paintGrid(options) {
1208
1316
  if (hiddenBehindLeft || hiddenBehindRight) {
1209
1317
  const markerTop = Math.max(y - borderWidth, selectionTop);
1210
1318
  const markerBottom = Math.min(y + selectionHeight + 1, height);
1211
- const markerX = hiddenBehindLeft ? leftMarkerX : selectionAreaRight - borderWidth;
1319
+ const markerX = hiddenBehindLeft ? leftMarkerX : selectionAreaRight - borderWidth - rightFixedBoundaryInset;
1212
1320
  ctx.fillRect(markerX, markerTop, borderWidth, Math.max(0, markerBottom - markerTop));
1213
1321
  }
1214
1322
  if (hiddenAboveHeader || hiddenBelowViewport || hiddenBehindLeft || hiddenBehindRight) {
1215
1323
  ctx.restore();
1216
1324
  return;
1217
1325
  }
1218
- const borderLeft = x <= 0 ? 0 : Math.max(x - borderWidth, visibleLeft - 1);
1326
+ const borderLeft = x <= 0 ? 0 : Math.max(x - borderWidth, visibleLeft);
1219
1327
  const borderRight = Math.min(x + selectionWidth - 1, selectionAreaRight - borderWidth, width - borderWidth);
1220
1328
  const borderTop = Math.max(y - borderWidth, selectionClipTop);
1221
1329
  const borderBottom = Math.min(y + selectionHeight - 1, height - borderWidth);
@@ -1277,6 +1385,40 @@ function flattenDataColumns(columns, inheritedFixed, insideGroup = false) {
1277
1385
  return flattenDataColumns(column.children, column.fixed ?? inheritedFixed, true);
1278
1386
  });
1279
1387
  }
1388
+ function reconcileColumns(current, incoming) {
1389
+ const incomingByKey = new Map(incoming.map((column) => [column.key, column]));
1390
+ const ordered = current.flatMap((column) => {
1391
+ const replacement = incomingByKey.get(column.key);
1392
+ if (!replacement) return [];
1393
+ incomingByKey.delete(column.key);
1394
+ return [{
1395
+ ...replacement,
1396
+ children: replacement.children ? reconcileColumns(column.children ?? [], replacement.children) : void 0
1397
+ }];
1398
+ });
1399
+ incoming.forEach((column) => {
1400
+ if (incomingByKey.has(column.key)) ordered.push(column);
1401
+ });
1402
+ return ordered;
1403
+ }
1404
+ function reorderColumns(columns, detail) {
1405
+ const reorder = (items) => {
1406
+ const sourceIndex = items.findIndex((column2) => column2.key === detail.sourceKey);
1407
+ const targetIndex = items.findIndex((column2) => column2.key === detail.targetKey);
1408
+ if (sourceIndex < 0 || targetIndex < 0 || sourceIndex === targetIndex) return items;
1409
+ const next = items.slice();
1410
+ const [column] = next.splice(sourceIndex, 1);
1411
+ let insertIndex = targetIndex + (detail.placement === "after" ? 1 : 0);
1412
+ if (sourceIndex < insertIndex) insertIndex -= 1;
1413
+ next.splice(insertIndex, 0, column);
1414
+ return next;
1415
+ };
1416
+ if (!detail.parentKey) return reorder(columns);
1417
+ return columns.map((column) => {
1418
+ if (column.key === detail.parentKey && column.children) return { ...column, children: reorder(column.children) };
1419
+ return column.children ? { ...column, children: reorderColumns(column.children, detail) } : column;
1420
+ });
1421
+ }
1280
1422
  function buildHeaderCells(sourceColumns, utilityColumnCount, depth) {
1281
1423
  const cells = [];
1282
1424
  let leafIndex = utilityColumnCount;
@@ -1324,6 +1466,25 @@ var createEmptyCellSpanLookup = () => ({
1324
1466
  covered: /* @__PURE__ */ new Map(),
1325
1467
  maxRowSpan: 1
1326
1468
  });
1469
+ var summaryFunctionIds = /* @__PURE__ */ new WeakMap();
1470
+ var nextSummaryFunctionId = 1;
1471
+ function getSummaryFunctionId(fn) {
1472
+ if (!fn) return "";
1473
+ const existing = summaryFunctionIds.get(fn);
1474
+ if (existing !== void 0) return existing;
1475
+ const next = nextSummaryFunctionId;
1476
+ nextSummaryFunctionId += 1;
1477
+ summaryFunctionIds.set(fn, next);
1478
+ return next;
1479
+ }
1480
+ function getSummarySignature(columns) {
1481
+ return columns.filter((column) => Boolean(column.summary)).map((column) => [
1482
+ column.key,
1483
+ String(column.dataIndex ?? ""),
1484
+ typeof column.summary === "function" ? getSummaryFunctionId(column.summary) : "sum",
1485
+ getSummaryFunctionId(column.formatter)
1486
+ ].join("")).join("");
1487
+ }
1327
1488
  async function calculateSummaryValues(columns, rows) {
1328
1489
  const values = /* @__PURE__ */ new Map();
1329
1490
  for (const column of columns) {
@@ -1362,7 +1523,7 @@ function resolveContextMenuSection(section, defaults) {
1362
1523
  return defaults;
1363
1524
  }
1364
1525
  function Table({
1365
- columns: sourceColumns,
1526
+ columns: columnProps,
1366
1527
  rows: sourceRows,
1367
1528
  rowKey,
1368
1529
  width = "100%",
@@ -1391,23 +1552,21 @@ function Table({
1391
1552
  rowSelection,
1392
1553
  selectedRowKeys,
1393
1554
  defaultSelectedRowKeys,
1394
- onSelectedRowKeysChange,
1555
+ onSelectedRowChange,
1395
1556
  columnSelection,
1396
1557
  selectedColumnKeys,
1397
1558
  defaultSelectedColumnKeys,
1398
- onSelectedColumnKeysChange,
1559
+ onSelectedColumnChange,
1399
1560
  columnDraggable = true,
1400
- columnResizable = false,
1401
- onColumnOrderChange,
1561
+ columnResizable = true,
1562
+ onColumnsReorder,
1402
1563
  onColumnResize,
1403
1564
  rowDraggable = false,
1404
- onRowOrderChange,
1565
+ onRowsReorder,
1405
1566
  onInsertRows,
1406
1567
  onDeleteRows,
1407
- sortState = null,
1408
- onSortStateChange,
1409
- filterValues = {},
1410
- onFilterValuesChange,
1568
+ onSortChange,
1569
+ onFilterChange,
1411
1570
  onCellChange,
1412
1571
  onCellContextMenu,
1413
1572
  contextMenu: contextMenuConfig = true,
@@ -1416,6 +1575,10 @@ function Table({
1416
1575
  style,
1417
1576
  ariaLabel = "Data grid"
1418
1577
  }) {
1578
+ const [sourceColumns, setSourceColumns] = (0, import_react5.useState)(columnProps);
1579
+ (0, import_react5.useEffect)(() => {
1580
+ setSourceColumns((current) => reconcileColumns(current, columnProps));
1581
+ }, [columnProps]);
1419
1582
  const rowHeight = layout?.rowHeight ?? rowHeightProp ?? 36;
1420
1583
  const baseHeaderHeight = layout?.headerHeight ?? headerHeightProp ?? 40;
1421
1584
  const headerDepth = (0, import_react5.useMemo)(() => Math.max(1, ...sourceColumns.map(getColumnDepth)), [sourceColumns]);
@@ -1496,7 +1659,6 @@ function Table({
1496
1659
  const frameRef = (0, import_react5.useRef)(null);
1497
1660
  const textFrameRef = (0, import_react5.useRef)(null);
1498
1661
  const toastTimerRef = (0, import_react5.useRef)(null);
1499
- const sortDebounceRef = (0, import_react5.useRef)(null);
1500
1662
  const headerTooltipTimerRef = (0, import_react5.useRef)(null);
1501
1663
  const cellTooltipTimerRef = (0, import_react5.useRef)(null);
1502
1664
  const pendingHeaderTooltipKeyRef = (0, import_react5.useRef)("");
@@ -1508,6 +1670,7 @@ function Table({
1508
1670
  const dragGuideRef = (0, import_react5.useRef)(null);
1509
1671
  const rowDragGuideRef = (0, import_react5.useRef)(null);
1510
1672
  const selectionFocusRef = (0, import_react5.useRef)(null);
1673
+ const selectionEdgeTargetRef = (0, import_react5.useRef)(null);
1511
1674
  const verticalScrollbarRef = (0, import_react5.useRef)(null);
1512
1675
  const verticalScrollbarThumbRef = (0, import_react5.useRef)(null);
1513
1676
  const verticalScrollbarDragRef = (0, import_react5.useRef)(null);
@@ -1526,6 +1689,7 @@ function Table({
1526
1689
  const suppressClickRef = (0, import_react5.useRef)(false);
1527
1690
  const hasCompletedLoadRef = (0, import_react5.useRef)(!loading);
1528
1691
  const columnDragPreviewRef = (0, import_react5.useRef)(null);
1692
+ const validColumnDropRef = (0, import_react5.useRef)(null);
1529
1693
  const hoveredHeaderActionRef = (0, import_react5.useRef)(null);
1530
1694
  const hoveredRowIndexRef = (0, import_react5.useRef)(null);
1531
1695
  const [viewport, setViewport] = (0, import_react5.useState)({ width: 0, height: typeof height === "number" ? height : FALLBACK_HEIGHT });
@@ -1533,11 +1697,8 @@ function Table({
1533
1697
  const resolvedHeight = typeof height === "number" ? height : viewport.height || FALLBACK_HEIGHT;
1534
1698
  const [scrollPosition, setScrollPosition] = (0, import_react5.useState)({ left: 0, top: 0 });
1535
1699
  const scrollRef = (0, import_react5.useRef)({ left: 0, top: 0 });
1536
- const [selection, setSelection] = useControllableValue(selectedCell, defaultSelectedCell, onSelectedCellChange);
1537
- const [selectionRange, setSelectionRange] = (0, import_react5.useState)(null);
1538
- const rangeDragRef = (0, import_react5.useRef)(null);
1539
1700
  const [rowKeys, setRowKeys] = useControllableKeys(selectedRowKeys, defaultSelectedRowKeys, (keys) => {
1540
- if (!onSelectedRowKeysChange) return;
1701
+ if (!onSelectedRowChange) return;
1541
1702
  const missing = new Set(keys.filter((key) => !rowByKeyRef.current.has(key)));
1542
1703
  if (missing.size > 0) {
1543
1704
  for (let index = 0; index < rows.length && missing.size > 0; index += 1) {
@@ -1554,12 +1715,13 @@ function Table({
1554
1715
  const index = rowIndexByKeyRef.current.get(key);
1555
1716
  return row && index !== void 0 ? [{ row, index }] : [];
1556
1717
  });
1557
- onSelectedRowKeysChange(keys, entries.map(({ row }) => row), entries.map(({ index }) => index));
1718
+ onSelectedRowChange(keys, entries.map(({ row }) => row), entries.map(({ index }) => index));
1558
1719
  });
1559
- const [columnKeys, setColumnKeys] = useControllableKeys(selectedColumnKeys, defaultSelectedColumnKeys, onSelectedColumnKeysChange);
1720
+ const [columnKeys, setColumnKeys] = useControllableKeys(selectedColumnKeys, defaultSelectedColumnKeys, onSelectedColumnChange);
1560
1721
  const [editing, setEditing] = (0, import_react5.useState)(null);
1561
1722
  const [draft, setDraft] = (0, import_react5.useState)("");
1562
1723
  const [rowDragPreview, setRowDragPreview] = (0, import_react5.useState)(null);
1724
+ const [columnDropTarget, setColumnDropTarget] = (0, import_react5.useState)(null);
1563
1725
  (0, import_react5.useEffect)(() => {
1564
1726
  if (!loading) hasCompletedLoadRef.current = true;
1565
1727
  }, [loading]);
@@ -1628,6 +1790,8 @@ function Table({
1628
1790
  return () => observer.disconnect();
1629
1791
  }, [activeCustomHeaderLevels, measureCustomHeaderHeight, viewport.width]);
1630
1792
  const [filterEditor, setFilterEditor] = (0, import_react5.useState)(null);
1793
+ const [filterValues, setFilterValues] = (0, import_react5.useState)({});
1794
+ const [sortState, setSortState] = (0, import_react5.useState)(null);
1631
1795
  const [contextMenu, setContextMenu] = (0, import_react5.useState)(null);
1632
1796
  const [insertCounts, setInsertCounts] = (0, import_react5.useState)({ before: 1, after: 1 });
1633
1797
  const [confirmAction, setConfirmAction] = (0, import_react5.useState)(null);
@@ -1640,7 +1804,20 @@ function Table({
1640
1804
  const [hoveredHeaderAction, setHoveredHeaderAction] = (0, import_react5.useState)(null);
1641
1805
  const [visibleHeaderTooltip, setVisibleHeaderTooltip] = (0, import_react5.useState)(null);
1642
1806
  const [hoveredCellTooltip, setHoveredCellTooltip] = (0, import_react5.useState)(null);
1643
- const [displaySortState, setDisplaySortState] = (0, import_react5.useState)(sortState);
1807
+ const hideDragTooltips = (0, import_react5.useCallback)(() => {
1808
+ if (headerTooltipTimerRef.current !== null) {
1809
+ clearTimeout(headerTooltipTimerRef.current);
1810
+ headerTooltipTimerRef.current = null;
1811
+ }
1812
+ if (cellTooltipTimerRef.current !== null) {
1813
+ clearTimeout(cellTooltipTimerRef.current);
1814
+ cellTooltipTimerRef.current = null;
1815
+ }
1816
+ pendingHeaderTooltipKeyRef.current = "";
1817
+ pendingCellTooltipKeyRef.current = "";
1818
+ setVisibleHeaderTooltip(null);
1819
+ setHoveredCellTooltip(null);
1820
+ }, []);
1644
1821
  const [editedCellKeys, setEditedCellKeys] = (0, import_react5.useState)(() => /* @__PURE__ */ new Set());
1645
1822
  const [cellAnnotations, setCellAnnotations] = (0, import_react5.useState)(() => /* @__PURE__ */ new Map());
1646
1823
  const [annotationType, setAnnotationType] = (0, import_react5.useState)("corner");
@@ -1652,6 +1829,7 @@ function Table({
1652
1829
  const [pendingDeletedRowKeys, setPendingDeletedRowKeys] = (0, import_react5.useState)(() => /* @__PURE__ */ new Set());
1653
1830
  const [pendingInserts, setPendingInserts] = (0, import_react5.useState)(() => []);
1654
1831
  const [insertedRowKeys, setInsertedRowKeys] = (0, import_react5.useState)(() => /* @__PURE__ */ new Set());
1832
+ const [rowOrderKeys, setRowOrderKeys] = (0, import_react5.useState)(null);
1655
1833
  const settledPendingInsertIdsRef = (0, import_react5.useRef)(/* @__PURE__ */ new Set());
1656
1834
  const getDataRowKey = (0, import_react5.useCallback)((row, index) => {
1657
1835
  return typeof rowKey === "function" ? rowKey(row, index) : rowKey ? row[rowKey] : getDefaultRowKey(row, index);
@@ -1666,7 +1844,7 @@ function Table({
1666
1844
  }
1667
1845
  return sourceRows.findIndex((sourceRow, index) => getDataRowKey(sourceRow, index) === key);
1668
1846
  }, [getDataRowKey, sourceRows]);
1669
- const rows = (0, import_react5.useMemo)(() => {
1847
+ const orderedRows = (0, import_react5.useMemo)(() => {
1670
1848
  let next = sourceRows;
1671
1849
  if (pendingInserts.length > 0) {
1672
1850
  const activeInserts = pendingInserts.filter((insert) => {
@@ -1685,9 +1863,91 @@ function Table({
1685
1863
  insertedOffset += insert.rows.length;
1686
1864
  });
1687
1865
  }
1688
- if (pendingDeletedRowKeys.size === 0) return next;
1689
- return next.filter((row, index) => !pendingDeletedRowKeys.has(getDataRowKey(row, index)));
1690
- }, [getDataRowKey, pendingDeletedRowKeys, pendingInserts, sourceRows]);
1866
+ if (pendingDeletedRowKeys.size > 0) {
1867
+ next = next.filter((row, index) => !pendingDeletedRowKeys.has(getDataRowKey(row, index)));
1868
+ }
1869
+ if (!rowOrderKeys) return next;
1870
+ const rowsByKey = new Map(next.map((row, index) => [getDataRowKey(row, index), row]));
1871
+ const ordered = rowOrderKeys.flatMap((key) => {
1872
+ const row = rowsByKey.get(key);
1873
+ if (!row) return [];
1874
+ rowsByKey.delete(key);
1875
+ return [row];
1876
+ });
1877
+ next.forEach((row, index) => {
1878
+ const key = getDataRowKey(row, index);
1879
+ if (rowsByKey.has(key)) ordered.push(row);
1880
+ });
1881
+ return ordered;
1882
+ }, [getDataRowKey, pendingDeletedRowKeys, pendingInserts, rowOrderKeys, sourceRows]);
1883
+ const activeFilterValues = (0, import_react5.useMemo)(() => Object.fromEntries(
1884
+ Object.entries(filterValues).filter(([, value]) => value.trim() !== "")
1885
+ ), [filterValues]);
1886
+ const filteredRows = (0, import_react5.useMemo)(() => {
1887
+ if (Object.keys(activeFilterValues).length === 0) return orderedRows;
1888
+ if (onFilterChange) return onFilterChange(orderedRows, activeFilterValues);
1889
+ return orderedRows.filter((row, rowIndex) => Object.entries(activeFilterValues).every(([columnKey, query]) => {
1890
+ const column = columns.find((candidate) => candidate.key === columnKey);
1891
+ if (!column) return true;
1892
+ return getDisplayLabel(column, row, rowIndex).toLocaleLowerCase().includes(query.trim().toLocaleLowerCase());
1893
+ }));
1894
+ }, [activeFilterValues, columns, onFilterChange, orderedRows]);
1895
+ const rows = (0, import_react5.useMemo)(() => {
1896
+ if (!sortState) return filteredRows;
1897
+ if (onSortChange) return onSortChange(filteredRows, sortState);
1898
+ const column = columns.find((candidate) => candidate.key === sortState.columnKey);
1899
+ if (!column) return filteredRows;
1900
+ const direction = sortState.direction === "asc" ? 1 : -1;
1901
+ return filteredRows.slice().sort((left, right) => {
1902
+ const leftValue = column.dataIndex === void 0 ? void 0 : left[column.dataIndex];
1903
+ const rightValue = column.dataIndex === void 0 ? void 0 : right[column.dataIndex];
1904
+ const result = typeof leftValue === "number" && typeof rightValue === "number" ? leftValue - rightValue : getDisplayLabel(column, left, 0).localeCompare(getDisplayLabel(column, right, 0), language);
1905
+ return result * direction;
1906
+ });
1907
+ }, [columns, filteredRows, language, onSortChange, sortState]);
1908
+ const resolveSelectionTarget = (0, import_react5.useCallback)((target) => {
1909
+ if (target == null) return target;
1910
+ const columnIndex = columns.findIndex((column) => column.key === target.columnKey);
1911
+ const rowIndex = target.rowKey !== void 0 ? rows.findIndex((row2, index) => getDataRowKey(row2, index) === target.rowKey) : target.rowIndex ?? -1;
1912
+ const row = rows[rowIndex];
1913
+ if (columnIndex < 0 || !row) return null;
1914
+ return {
1915
+ rowIndex,
1916
+ columnIndex,
1917
+ rowKey: getDataRowKey(row, rowIndex),
1918
+ columnKey: target.columnKey
1919
+ };
1920
+ }, [columns, getDataRowKey, rows]);
1921
+ const resolvedSelectedCell = (0, import_react5.useMemo)(() => resolveSelectionTarget(selectedCell), [resolveSelectionTarget, selectedCell]);
1922
+ const resolvedDefaultSelectedCell = (0, import_react5.useMemo)(() => resolveSelectionTarget(defaultSelectedCell), [defaultSelectedCell, resolveSelectionTarget]);
1923
+ const handleSelectedCellChange = (0, import_react5.useCallback)((next) => {
1924
+ if (!onSelectedCellChange) return;
1925
+ if (!next) {
1926
+ onSelectedCellChange(null);
1927
+ return;
1928
+ }
1929
+ const row = rows[next.rowIndex];
1930
+ const column = columns[next.columnIndex];
1931
+ const value = row && column?.dataIndex !== void 0 ? row[column.dataIndex] : void 0;
1932
+ onSelectedCellChange({ ...next, value });
1933
+ }, [columns, onSelectedCellChange, rows]);
1934
+ const [selection, setSelection] = useControllableValue(resolvedSelectedCell, resolvedDefaultSelectedCell, handleSelectedCellChange);
1935
+ const [selectionRange, setSelectionRange] = (0, import_react5.useState)(null);
1936
+ const rangeDragRef = (0, import_react5.useRef)(null);
1937
+ const applyRowOrder = (0, import_react5.useCallback)((sourceIndex, targetIndex) => {
1938
+ if (sourceIndex === targetIndex || sourceIndex < 0 || targetIndex < 0 || sourceIndex >= rows.length || targetIndex >= rows.length) return;
1939
+ const nextEntries = rows.map((row, index) => ({ row, key: getDataRowKey(row, index) }));
1940
+ const [entry] = nextEntries.splice(sourceIndex, 1);
1941
+ nextEntries.splice(targetIndex, 0, entry);
1942
+ const nextRows = nextEntries.map(({ row }) => row);
1943
+ setRowOrderKeys(nextEntries.map(({ key }) => key));
1944
+ onRowsReorder?.(nextRows, { sourceIndex, targetIndex });
1945
+ }, [getDataRowKey, onRowsReorder, rows]);
1946
+ const applyColumnOrder = (0, import_react5.useCallback)((sourceIndex, targetIndex, detail) => {
1947
+ const nextColumns = reorderColumns(sourceColumns, detail);
1948
+ setSourceColumns(nextColumns);
1949
+ onColumnsReorder?.(nextColumns, { sourceIndex, targetIndex, ...detail });
1950
+ }, [onColumnsReorder, sourceColumns]);
1691
1951
  (0, import_react5.useEffect)(() => {
1692
1952
  if (insertBusy || settledPendingInsertIdsRef.current.size === 0) return;
1693
1953
  setPendingInserts((current) => {
@@ -1755,7 +2015,8 @@ function Table({
1755
2015
  const getScrollableColumnLeft = (0, import_react5.useCallback)((columnIndex) => scrollableColumnLefts[columnIndex] ?? fixedWidth, [fixedWidth, scrollableColumnLefts]);
1756
2016
  const hasRowSelectionColumn = (0, import_react5.useMemo)(() => columns.some((column) => column.rowSelection), [columns]);
1757
2017
  const rowSelectionMode = typeof rowSelection === "object" ? rowSelection.mode ?? "multiple" : "multiple";
1758
- const hasSummaryRow = (0, import_react5.useMemo)(() => rows.length > 0 && summaryEnabled && columns.some((column) => Boolean(column.summary)), [columns, rows.length, summaryEnabled]);
2018
+ const summarySignature = (0, import_react5.useMemo)(() => getSummarySignature(columns), [columns]);
2019
+ const hasSummaryRow = rows.length > 0 && summaryEnabled && summarySignature.length > 0;
1759
2020
  const topSummaryHeight = hasSummaryRow && summaryPosition === "top" ? rowHeight : 0;
1760
2021
  const bottomSummaryHeight = hasSummaryRow && summaryPosition === "bottom" ? rowHeight : 0;
1761
2022
  const bodyTop = headerHeight + topSummaryHeight;
@@ -1764,21 +2025,21 @@ function Table({
1764
2025
  const effectiveHeight = autoHeight ? Math.max(bodyTop + bottomSummaryHeight, Math.min(resolvedHeight, realContentHeight)) : resolvedHeight;
1765
2026
  const renderHeight = Math.max(0, effectiveHeight - bottomSummaryHeight);
1766
2027
  const bodyViewportHeight = Math.max(0, renderHeight - (fixedHeader ? headerHeight + topSummaryHeight : 0));
1767
- const [summaryState, setSummaryState] = (0, import_react5.useState)(() => ({ columns: null, rows: null, values: /* @__PURE__ */ new Map(), pending: false }));
1768
- const summaryPending = hasSummaryRow && (loading || summaryState.pending || summaryState.columns !== columns || summaryState.rows !== rows);
1769
- const summaryValues = hasSummaryRow && summaryState.columns === columns && summaryState.rows === rows ? summaryState.values : /* @__PURE__ */ new Map();
2028
+ const [summaryState, setSummaryState] = (0, import_react5.useState)(() => ({ signature: "", rows: null, values: /* @__PURE__ */ new Map(), pending: false }));
2029
+ const summaryPending = hasSummaryRow && (loading || summaryState.pending || summaryState.signature !== summarySignature || summaryState.rows !== rows);
2030
+ const summaryValues = hasSummaryRow && summaryState.signature === summarySignature && summaryState.rows === rows ? summaryState.values : /* @__PURE__ */ new Map();
1770
2031
  (0, import_react5.useEffect)(() => {
1771
2032
  let cancelled = false;
1772
2033
  if (!hasSummaryRow) {
1773
- 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 });
2034
+ setSummaryState((current) => current.signature === "" && current.rows === null && current.values.size === 0 && !current.pending ? current : { signature: "", rows: null, values: /* @__PURE__ */ new Map(), pending: false });
1774
2035
  return () => {
1775
2036
  cancelled = true;
1776
2037
  };
1777
2038
  }
1778
2039
  setSummaryState((current) => ({
1779
- columns,
2040
+ signature: summarySignature,
1780
2041
  rows,
1781
- values: current.columns === columns && current.rows === rows ? current.values : /* @__PURE__ */ new Map(),
2042
+ values: current.signature === summarySignature && current.rows === rows ? current.values : /* @__PURE__ */ new Map(),
1782
2043
  pending: true
1783
2044
  }));
1784
2045
  if (loading) {
@@ -1791,13 +2052,13 @@ function Table({
1791
2052
  if (cancelled) return;
1792
2053
  const values = await calculateSummaryValues(columns, rows);
1793
2054
  if (cancelled) return;
1794
- setSummaryState({ columns, rows, values, pending: false });
2055
+ setSummaryState({ signature: summarySignature, rows, values, pending: false });
1795
2056
  };
1796
2057
  void run();
1797
2058
  return () => {
1798
2059
  cancelled = true;
1799
2060
  };
1800
- }, [columns, hasSummaryRow, loading, rows]);
2061
+ }, [hasSummaryRow, loading, rows, summarySignature]);
1801
2062
  const domRange = (0, import_react5.useMemo)(() => {
1802
2063
  if (!isVirtualized) return { rowStart: 0, rowEnd: rows.length, columnStart: 0, columnEnd: columns.length };
1803
2064
  const currentScroll2 = scrollRef.current;
@@ -1928,22 +2189,177 @@ function Table({
1928
2189
  grid: readThemeColor(themeStyles, "--rvg-color-grid", "#e3e6e8"),
1929
2190
  text: readThemeColor(themeStyles, "--rvg-color-text", "#202124"),
1930
2191
  muted: readThemeColor(themeStyles, "--rvg-color-muted", "#5f6368"),
2192
+ icon: readThemeColor(themeStyles, "--rvg-color-icon", "#9aa0a6"),
2193
+ borderStrong: readThemeColor(themeStyles, "--rvg-color-border-strong", "#b8bec4"),
1931
2194
  selection: readThemeColor(themeStyles, "--rvg-color-primary", "#1677ff"),
1932
2195
  selectionFill: readThemeColor(themeStyles, "--rvg-color-selection-fill", "#edf4ff"),
1933
2196
  axisSelectionFill: readThemeColor(themeStyles, "--rvg-color-axis-selection-fill", "#e8f2ff"),
1934
2197
  rowHoverFill: readThemeColor(themeStyles, "--rvg-color-row-hover-fill", "#f6f9fc"),
1935
2198
  editedFill: editedCellHighlightColor ?? readThemeColor(themeStyles, "--rvg-color-edited-fill", "#fff1b8"),
1936
2199
  insertedFill: insertedRowHighlightColor ?? readThemeColor(themeStyles, "--rvg-color-inserted-fill", "#c8ead4"),
2200
+ columnDropTargetFill: readThemeColor(themeStyles, "--rvg-color-column-drop-target-fill", "#eeeeee"),
1937
2201
  stripe: stripedColor ?? readThemeColor(themeStyles, "--rvg-color-stripe", "#fafbfc")
1938
2202
  };
1939
2203
  const scroll = scrollRef.current;
1940
2204
  const rowScrollTop = fixedHeader ? scroll.top : Math.max(0, scroll.top - bodyTop);
1941
2205
  const range = isVirtualized ? getViewportRange(scroll.left, rowScrollTop, viewport.width, bodyViewportHeight, rows.length, rowHeight, metrics, virtualOverscan) : { rowStart: 0, rowEnd: rows.length, columnStart: 0, columnEnd: columns.length };
1942
- paintGrid({ context, width: viewport.width, height: renderHeight, pixelRatio: ratio, scrollLeft: scroll.left, scrollTop: scroll.top, rowHeight, headerHeight, headerLeafTop, headerLeafHeight, bodyTop, suppressLastRowBottomBorder: bottomSummaryHeight > 0, suppressFrameBottomBorder: bottomSummaryHeight > 0, fixedHeader, verticalBorderless: !hasVerticalBorders, striped: hasStripedRows, columnDraggable, sortState, filterValues, hoveredHeaderAction: hoveredHeaderActionRef.current, rows, columns, metrics, range, selection, editing, hoveredRowIndex: hoveredRowIndexRef.current, selectionRange, selectedRowKeys: selectedRowKeySet, selectedColumnKeys: selectedColumnKeySet, cellSpans: cellSpanLookup.covered, maxRowSpan: cellSpanLookup.maxRowSpan, highlightEditedCells: highlightsEditedCells, highlightInsertedRows: highlightsInsertedRows, insertedRowKeys: insertedRowKeySet, editedCellKeys, cellAnnotations, getRowKey, rowDragPreview, colors: themeColors });
1943
- }, [bodyTop, bodyViewportHeight, bottomSummaryHeight, cellAnnotations, cellSpanLookup, columnDraggable, columns, editedCellHighlightColor, editedCellKeys, editing, renderHeight, filterValues, fixedHeader, getRowKey, hasStripedRows, hasVerticalBorders, headerDepth, headerHeight, headerLeafHeight, headerLeafTop, highlightsEditedCells, highlightsInsertedRows, insertedRowHighlightColor, insertedRowKeySet, isVirtualized, metrics, rowDragPreview, rowHeight, rows, selectedColumnKeySet, selectedRowKeySet, selection, selectionRange, sortState, stripedColor, viewport.width, virtualOverscan]);
2206
+ paintGrid({ context, width: viewport.width, height: renderHeight, pixelRatio: ratio, scrollLeft: scroll.left, scrollTop: scroll.top, rowHeight, headerHeight, headerLeafTop, headerLeafHeight, bodyTop, suppressLastRowBottomBorder: bottomSummaryHeight > 0, suppressFrameBottomBorder: bottomSummaryHeight > 0, fixedHeader, verticalBorderless: !hasVerticalBorders, striped: hasStripedRows, columnDraggable, sortState, filterValues, hoveredHeaderAction: hoveredHeaderActionRef.current, rows, columns, metrics, range, selection, editing, hoveredRowIndex: hoveredRowIndexRef.current, selectionRange, selectedRowKeys: selectedRowKeySet, rowSelectionMode, selectedColumnKeys: selectedColumnKeySet, cellSpans: cellSpanLookup.covered, maxRowSpan: cellSpanLookup.maxRowSpan, highlightEditedCells: highlightsEditedCells, highlightInsertedRows: highlightsInsertedRows, insertedRowKeys: insertedRowKeySet, editedCellKeys, cellAnnotations, getRowKey, rowDragPreview, columnDropTarget, colors: themeColors });
2207
+ }, [bodyTop, bodyViewportHeight, bottomSummaryHeight, cellAnnotations, cellSpanLookup, columnDraggable, columnDropTarget, columns, editedCellHighlightColor, editedCellKeys, editing, renderHeight, filterValues, fixedHeader, getRowKey, hasStripedRows, hasVerticalBorders, headerDepth, headerHeight, headerLeafHeight, headerLeafTop, highlightsEditedCells, highlightsInsertedRows, insertedRowHighlightColor, insertedRowKeySet, isVirtualized, metrics, rowDragPreview, rowHeight, rowSelectionMode, rows, selectedColumnKeySet, selectedRowKeySet, selection, selectionRange, sortState, stripedColor, viewport.width, virtualOverscan]);
1944
2208
  const scheduleDraw = (0, import_react5.useCallback)(() => {
1945
2209
  if (frameRef.current === null) frameRef.current = requestAnimationFrame(draw);
1946
2210
  }, [draw]);
2211
+ const drawImmediately = (0, import_react5.useCallback)(() => {
2212
+ if (frameRef.current !== null) cancelAnimationFrame(frameRef.current);
2213
+ frameRef.current = null;
2214
+ draw();
2215
+ }, [draw]);
2216
+ const getSelectionHorizontalVisibility = (0, import_react5.useCallback)((scrollLeft) => {
2217
+ if (!selection || selectionRange) return "visible";
2218
+ const column = columns[selection.columnIndex];
2219
+ if (!column || column.fixed !== void 0) return "visible";
2220
+ const span = getCellSpan(selection.rowIndex, selection.columnIndex);
2221
+ const left = getScrollableColumnLeft(selection.columnIndex) - scrollLeft;
2222
+ const right = left + getCellDisplayWidth(selection.columnIndex, span?.colSpan ?? 1);
2223
+ if (right <= fixedWidth) return "hidden-left";
2224
+ if (left >= viewport.width - rightFixedWidth) return "hidden-right";
2225
+ return "visible";
2226
+ }, [columns, fixedWidth, getCellDisplayWidth, getCellSpan, getScrollableColumnLeft, rightFixedWidth, selection, selectionRange, viewport.width]);
2227
+ const getSelectionEdgeIndicator = (0, import_react5.useCallback)((scrollLeft, scrollTop) => {
2228
+ if (!selection || selectionRange) return null;
2229
+ const row = rows[selection.rowIndex];
2230
+ const metric = metrics[selection.columnIndex];
2231
+ const column = columns[selection.columnIndex];
2232
+ if (!row || !metric || !column || getRowKey(row, selection.rowIndex) !== selection.rowKey) return null;
2233
+ const span = getCellSpan(selection.rowIndex, selection.columnIndex);
2234
+ 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;
2235
+ const rawRight = rawLeft + getCellDisplayWidth(selection.columnIndex, span?.colSpan ?? 1);
2236
+ const rawTop = bodyTop + selection.rowIndex * rowHeight - scrollTop;
2237
+ const rawBottom = rawTop + rowHeight * (span?.rowSpan ?? 1);
2238
+ const horizontalStart = column.fixed === void 0 ? fixedWidth : 0;
2239
+ const horizontalEnd = column.fixed === void 0 ? viewport.width - rightFixedWidth : viewport.width;
2240
+ const verticalStart = fixedHeader ? bodyTop : 0;
2241
+ const hiddenLeft = column.fixed === void 0 && rawRight <= horizontalStart;
2242
+ const hiddenRight = column.fixed === void 0 && rawLeft >= horizontalEnd;
2243
+ const hiddenTop = rawBottom <= verticalStart;
2244
+ const hiddenBottom = rawTop >= renderHeight;
2245
+ if (!hiddenLeft && !hiddenRight && !hiddenTop && !hiddenBottom) return null;
2246
+ const borderWidth = 2;
2247
+ const cornerLength = 10;
2248
+ const hitSlop = 6;
2249
+ const targetSize = borderWidth + hitSlop * 2;
2250
+ const rightBoundaryInset = rightFixedWidth > 0 && hasVerticalBorders ? 1 : 0;
2251
+ const markerX = hiddenLeft ? horizontalStart : horizontalEnd - borderWidth - rightBoundaryInset;
2252
+ const markerY = hiddenTop ? verticalStart : renderHeight - borderWidth;
2253
+ if (hiddenLeft || hiddenRight) {
2254
+ let markerTop = Math.max(rawTop - borderWidth, verticalStart);
2255
+ let markerBottom = Math.min(rawBottom + 1, renderHeight);
2256
+ const topClip = Math.max(0, verticalStart - (rawTop - borderWidth));
2257
+ const bottomClip = Math.max(0, rawBottom + 1 - renderHeight);
2258
+ if (topClip > 0 || bottomClip > 0) {
2259
+ const foldAtTop = topClip > 0;
2260
+ const fold = Math.min(cornerLength, foldAtTop ? topClip : bottomClip);
2261
+ if (fold > borderWidth) {
2262
+ const cornerWidth = fold;
2263
+ const cornerHeight = Math.max(cornerLength, markerBottom - markerTop);
2264
+ const cornerOnLeft = hiddenLeft;
2265
+ const shapeLeft = cornerOnLeft ? markerX : markerX + borderWidth - cornerWidth;
2266
+ const shapeTop = foldAtTop ? verticalStart : renderHeight - cornerHeight;
2267
+ return {
2268
+ side: `${foldAtTop ? "top" : "bottom"}-${cornerOnLeft ? "left" : "right"}`,
2269
+ orientation: "corner",
2270
+ style: {
2271
+ left: shapeLeft - hitSlop,
2272
+ top: shapeTop - hitSlop,
2273
+ width: cornerWidth + hitSlop * 2,
2274
+ height: cornerHeight + hitSlop * 2
2275
+ },
2276
+ corner: { width: cornerWidth, height: cornerHeight }
2277
+ };
2278
+ }
2279
+ }
2280
+ if (markerBottom <= markerTop) return null;
2281
+ return {
2282
+ side: hiddenLeft ? "left" : "right",
2283
+ orientation: "vertical",
2284
+ viewportEdge: hiddenLeft && horizontalStart === 0 ? "left" : hiddenRight && horizontalEnd === viewport.width ? "right" : void 0,
2285
+ style: {
2286
+ left: markerX - hitSlop,
2287
+ top: markerTop,
2288
+ width: targetSize,
2289
+ height: markerBottom - markerTop
2290
+ }
2291
+ };
2292
+ }
2293
+ const horizontalMarkerEnd = horizontalEnd - rightBoundaryInset;
2294
+ let markerLeft = Math.max(rawLeft - borderWidth, horizontalStart);
2295
+ let markerRight = Math.min(rawRight + 1, horizontalMarkerEnd);
2296
+ const leftClip = Math.max(0, horizontalStart - (rawLeft - borderWidth));
2297
+ const rightClip = Math.max(0, rawRight + 1 - horizontalMarkerEnd);
2298
+ if (leftClip > 0 || rightClip > 0) {
2299
+ const foldAtLeft = leftClip > 0;
2300
+ const fold = Math.min(cornerLength, foldAtLeft ? leftClip : rightClip);
2301
+ if (fold > borderWidth) {
2302
+ const cornerWidth = Math.max(cornerLength, markerRight - markerLeft);
2303
+ const cornerHeight = fold;
2304
+ const shapeLeft = foldAtLeft ? horizontalStart : horizontalMarkerEnd - cornerWidth;
2305
+ const shapeTop = hiddenTop ? verticalStart : renderHeight - cornerHeight;
2306
+ return {
2307
+ side: `${hiddenTop ? "top" : "bottom"}-${foldAtLeft ? "left" : "right"}`,
2308
+ orientation: "corner",
2309
+ style: {
2310
+ left: shapeLeft - hitSlop,
2311
+ top: shapeTop - hitSlop,
2312
+ width: cornerWidth + hitSlop * 2,
2313
+ height: cornerHeight + hitSlop * 2
2314
+ },
2315
+ corner: { width: cornerWidth, height: cornerHeight }
2316
+ };
2317
+ }
2318
+ }
2319
+ if (markerRight <= markerLeft) return null;
2320
+ return {
2321
+ side: hiddenTop ? "top" : "bottom",
2322
+ orientation: "horizontal",
2323
+ viewportEdge: hiddenTop && verticalStart === 0 ? "top" : hiddenBottom ? "bottom" : void 0,
2324
+ style: {
2325
+ left: markerLeft,
2326
+ top: markerY - hitSlop,
2327
+ width: markerRight - markerLeft,
2328
+ height: targetSize
2329
+ }
2330
+ };
2331
+ }, [bodyTop, columns, fixedHeader, fixedWidth, getCellDisplayWidth, getCellSpan, getRowKey, getScrollableColumnLeft, hasVerticalBorders, leftFixedOffsets, metrics, renderHeight, rightFixedOffsets, rightFixedWidth, rowHeight, rows, selection, selectionRange, viewport.width]);
2332
+ const syncSelectionEdgeTarget = (0, import_react5.useCallback)((scrollLeft, scrollTop) => {
2333
+ const target = selectionEdgeTargetRef.current;
2334
+ if (!target) return;
2335
+ const indicator = getSelectionEdgeIndicator(scrollLeft, scrollTop);
2336
+ if (!indicator) {
2337
+ target.style.visibility = "hidden";
2338
+ return;
2339
+ }
2340
+ const orientationChanged = !target.classList.contains(`is-${indicator.orientation}`);
2341
+ target.className = `rvg-selection-edge-target is-${indicator.orientation} is-${indicator.side}${indicator.viewportEdge ? ` is-viewport-${indicator.viewportEdge}` : ""}${orientationChanged ? " is-shape-syncing" : ""}`;
2342
+ target.style.left = `${Number(indicator.style.left)}px`;
2343
+ target.style.top = `${Number(indicator.style.top)}px`;
2344
+ target.style.width = `${Number(indicator.style.width)}px`;
2345
+ target.style.height = `${Number(indicator.style.height)}px`;
2346
+ if (indicator.corner) {
2347
+ target.style.setProperty("--rvg-corner-width", `${indicator.corner.width}px`);
2348
+ target.style.setProperty("--rvg-corner-height", `${indicator.corner.height}px`);
2349
+ target.style.setProperty("--rvg-corner-hover-width-extension", "4px");
2350
+ target.style.setProperty("--rvg-corner-hover-height-extension", "4px");
2351
+ } else {
2352
+ target.style.removeProperty("--rvg-corner-width");
2353
+ target.style.removeProperty("--rvg-corner-height");
2354
+ target.style.removeProperty("--rvg-corner-hover-width-extension");
2355
+ target.style.removeProperty("--rvg-corner-hover-height-extension");
2356
+ }
2357
+ target.style.visibility = "visible";
2358
+ if (orientationChanged) {
2359
+ void target.offsetWidth;
2360
+ target.classList.remove("is-shape-syncing");
2361
+ }
2362
+ }, [getSelectionEdgeIndicator]);
1947
2363
  (0, import_react5.useLayoutEffect)(() => {
1948
2364
  const element = scrollerRef.current;
1949
2365
  if (!element) return;
@@ -1970,11 +2386,7 @@ function Table({
1970
2386
  }
1971
2387
  };
1972
2388
  }, [scheduleDraw]);
1973
- (0, import_react5.useEffect)(() => {
1974
- setDisplaySortState(sortState);
1975
- }, [sortState]);
1976
2389
  (0, import_react5.useEffect)(() => () => {
1977
- if (sortDebounceRef.current !== null) clearTimeout(sortDebounceRef.current);
1978
2390
  if (textFrameRef.current !== null) cancelAnimationFrame(textFrameRef.current);
1979
2391
  if (toastTimerRef.current !== null) clearTimeout(toastTimerRef.current);
1980
2392
  if (verticalScrollbarVisibilityTimerRef.current !== null) clearTimeout(verticalScrollbarVisibilityTimerRef.current);
@@ -2194,7 +2606,13 @@ function Table({
2194
2606
  }
2195
2607
  const nextThumbLeft = Math.max(0, Math.min(trackWidth - thumbWidth, event.clientX - trackRect.left - thumbWidth / 2));
2196
2608
  const maxThumbLeft = Math.max(1, trackWidth - thumbWidth);
2197
- scroller.scrollLeft = nextThumbLeft / maxThumbLeft * maxScrollLeft;
2609
+ const previousScrollLeft = scroller.scrollLeft;
2610
+ const nextScrollLeft = nextThumbLeft / maxThumbLeft * maxScrollLeft;
2611
+ const visibilityChanged = getSelectionHorizontalVisibility(previousScrollLeft) !== getSelectionHorizontalVisibility(nextScrollLeft);
2612
+ scroller.scrollLeft = nextScrollLeft;
2613
+ scrollRef.current = { left: scroller.scrollLeft, top: scroller.scrollTop };
2614
+ syncSelectionEdgeTarget(scroller.scrollLeft, scroller.scrollTop);
2615
+ if (visibilityChanged) drawImmediately();
2198
2616
  updateCustomScrollbars();
2199
2617
  horizontalScrollbarDragRef.current = {
2200
2618
  pointerId: event.pointerId,
@@ -2204,7 +2622,7 @@ function Table({
2204
2622
  thumbWidth,
2205
2623
  maxScrollLeft
2206
2624
  };
2207
- }, [showCustomScrollbar, updateCustomScrollbars]);
2625
+ }, [drawImmediately, getSelectionHorizontalVisibility, showCustomScrollbar, syncSelectionEdgeTarget, updateCustomScrollbars]);
2208
2626
  const handleHorizontalScrollbarPointerMove = (0, import_react5.useCallback)((event) => {
2209
2627
  const drag = horizontalScrollbarDragRef.current;
2210
2628
  const scroller = scrollerRef.current;
@@ -2212,9 +2630,15 @@ function Table({
2212
2630
  event.preventDefault();
2213
2631
  const maxThumbLeft = Math.max(1, drag.trackWidth - drag.thumbWidth);
2214
2632
  const nextThumbLeft = Math.max(0, Math.min(maxThumbLeft, drag.startLeft + event.clientX - drag.startX));
2215
- scroller.scrollLeft = nextThumbLeft / maxThumbLeft * drag.maxScrollLeft;
2633
+ const previousScrollLeft = scroller.scrollLeft;
2634
+ const nextScrollLeft = nextThumbLeft / maxThumbLeft * drag.maxScrollLeft;
2635
+ const visibilityChanged = getSelectionHorizontalVisibility(previousScrollLeft) !== getSelectionHorizontalVisibility(nextScrollLeft);
2636
+ scroller.scrollLeft = nextScrollLeft;
2637
+ scrollRef.current = { left: scroller.scrollLeft, top: scroller.scrollTop };
2638
+ syncSelectionEdgeTarget(scroller.scrollLeft, scroller.scrollTop);
2639
+ if (visibilityChanged) drawImmediately();
2216
2640
  updateCustomScrollbars();
2217
- }, [updateCustomScrollbars]);
2641
+ }, [drawImmediately, getSelectionHorizontalVisibility, syncSelectionEdgeTarget, updateCustomScrollbars]);
2218
2642
  const handleHorizontalScrollbarPointerUp = (0, import_react5.useCallback)((event) => {
2219
2643
  const drag = horizontalScrollbarDragRef.current;
2220
2644
  if (!drag || drag.pointerId !== event.pointerId) return;
@@ -2229,6 +2653,7 @@ function Table({
2229
2653
  const scrollDeltaX = element.scrollLeft - previousScroll.left;
2230
2654
  const scrollDeltaY = element.scrollTop - previousScroll.top;
2231
2655
  scrollRef.current = { left: element.scrollLeft, top: element.scrollTop };
2656
+ syncSelectionEdgeTarget(element.scrollLeft, element.scrollTop);
2232
2657
  const focus = selectionFocusRef.current;
2233
2658
  if (focus && selection && !selectionRange) {
2234
2659
  const row = rows[selection.rowIndex];
@@ -2250,9 +2675,9 @@ function Table({
2250
2675
  focus.style.display = "none";
2251
2676
  } else {
2252
2677
  const borderWidth = 2;
2253
- const left = Math.round(Math.max(rawLeft - borderWidth, horizontalStart - 1));
2678
+ const left = Math.round(Math.max(rawLeft - borderWidth, horizontalStart));
2254
2679
  const right = Math.round(Math.min(rawRight + 1, horizontalEnd, viewport.width));
2255
- const top = Math.round(Math.max(rawTop - borderWidth, verticalStart - 1));
2680
+ const top = Math.round(Math.max(rawTop - borderWidth, verticalStart));
2256
2681
  const bottom = Math.round(Math.min(rawBottom + 1, renderHeight));
2257
2682
  focus.style.display = "block";
2258
2683
  focus.style.left = `${left}px`;
@@ -2282,7 +2707,7 @@ function Table({
2282
2707
  setHoveredCellTooltip(null);
2283
2708
  scheduleDraw();
2284
2709
  setEditing(null);
2285
- }, [bodyTop, columns, renderHeight, fixedHeader, fixedWidth, getCellDisplayWidth, getCellSpan, getRowKey, getScrollableColumnLeft, metrics, rightFixedOffsets, rightFixedWidth, rowHeight, rows, scheduleDraw, selection, selectionRange, showCustomScrollbar, updateCustomScrollbars, viewport.width]);
2710
+ }, [bodyTop, columns, renderHeight, fixedHeader, fixedWidth, getCellDisplayWidth, getCellSpan, getRowKey, getScrollableColumnLeft, metrics, rightFixedOffsets, rightFixedWidth, rowHeight, rows, scheduleDraw, selection, selectionRange, showCustomScrollbar, syncSelectionEdgeTarget, updateCustomScrollbars, viewport.width]);
2286
2711
  const locateColumn = (0, import_react5.useCallback)((clientX) => {
2287
2712
  const canvas = canvasRef.current;
2288
2713
  if (!canvas) return -1;
@@ -2336,12 +2761,12 @@ function Table({
2336
2761
  const localX = clientX - canvasRef.current.getBoundingClientRect().left;
2337
2762
  const columnRight = getDisplayedColumnLeft(columnIndex) + metrics[columnIndex].width;
2338
2763
  const visibleActions = getVisibleHeaderActions(column, metrics[columnIndex].width, columnDraggable, measureHeaderTitleWidth(columnIndex));
2339
- let right = columnRight - (visibleActions.drag ? 18 : 4);
2764
+ let right = columnRight - (visibleActions.drag ? HEADER_ACTION_SLOT_WIDTH : 2);
2340
2765
  if (visibleActions.sort) {
2341
- if (localX >= right - 18 && localX < right) return "sort";
2342
- right -= 18;
2766
+ if (localX >= right - HEADER_ACTION_SLOT_WIDTH && localX < right) return "sort";
2767
+ right -= HEADER_ACTION_SLOT_WIDTH;
2343
2768
  }
2344
- if (visibleActions.filter && localX >= right - 18 && localX < right) return "filter";
2769
+ if (visibleActions.filter && localX >= right - HEADER_ACTION_SLOT_WIDTH && localX < right) return "filter";
2345
2770
  return null;
2346
2771
  }, [columnDraggable, columns, getDisplayedColumnLeft, measureHeaderTitleWidth, metrics]);
2347
2772
  const isHeaderTitleTruncated = (0, import_react5.useCallback)((columnIndex) => {
@@ -2351,7 +2776,7 @@ function Table({
2351
2776
  const metric = metrics[columnIndex];
2352
2777
  const titleWidth = measureHeaderTitleWidth(columnIndex);
2353
2778
  const visibleActions = getVisibleHeaderActions(column, metric.width, columnDraggable, titleWidth);
2354
- const actionWidth = (Number(visibleActions.drag) + Number(visibleActions.filter) + Number(visibleActions.sort)) * 18;
2779
+ const actionWidth = (Number(visibleActions.drag) + Number(visibleActions.filter) + Number(visibleActions.sort)) * HEADER_ACTION_SLOT_WIDTH;
2355
2780
  return getHeaderTitleRequiredWidth(column, titleWidth) > Math.max(0, metric.width - actionWidth);
2356
2781
  }, [columnDraggable, columns, measureHeaderTitleWidth, metrics]);
2357
2782
  const locateHeaderDragHandle = (0, import_react5.useCallback)((clientX, columnIndex) => {
@@ -2370,6 +2795,30 @@ function Table({
2370
2795
  return fixedSide === "left" ? leftFixedOffsets.get(cell.startIndex) ?? 0 : fixedSide === "right" ? viewport.width - (rightFixedOffsets.get(cell.startIndex) ?? 0) - startMetric.width : getScrollableColumnLeft(cell.startIndex) - scrollRef.current.left;
2371
2796
  }, [columns, getScrollableColumnLeft, leftFixedOffsets, metrics, rightFixedOffsets, viewport.width]);
2372
2797
  const getHeaderCellWidth = (0, import_react5.useCallback)((cell) => cell.leaf ? metrics[cell.startIndex]?.width ?? 0 : getCellDisplayWidth(cell.startIndex, cell.endIndex - cell.startIndex + 1), [getCellDisplayWidth, metrics]);
2798
+ const getHeaderCellEdgeFromMetrics = (0, import_react5.useCallback)((startIndex, endIndex, edge, nextMetrics) => {
2799
+ const fixedSide = columns[startIndex]?.fixed;
2800
+ const width2 = nextMetrics.slice(startIndex, endIndex + 1).reduce((total, metric) => total + metric.width, 0);
2801
+ let left;
2802
+ if (fixedSide === "left") {
2803
+ left = 0;
2804
+ for (let index = 0; index < startIndex; index += 1) {
2805
+ if (columns[index].fixed === "left") left += nextMetrics[index]?.width ?? 0;
2806
+ }
2807
+ } else if (fixedSide === "right") {
2808
+ let rightOffset = 0;
2809
+ for (let index = columns.length - 1; index > startIndex; index -= 1) {
2810
+ if (columns[index].fixed === "right") rightOffset += nextMetrics[index]?.width ?? 0;
2811
+ }
2812
+ left = viewport.width - rightOffset - width2;
2813
+ } else {
2814
+ left = 0;
2815
+ for (let index = 0; index < startIndex; index += 1) {
2816
+ if (columns[index].fixed === void 0) left += nextMetrics[index]?.width ?? 0;
2817
+ }
2818
+ left += fixedWidth - scrollRef.current.left;
2819
+ }
2820
+ return left + (edge === "right" ? width2 : 0);
2821
+ }, [columns, fixedWidth, viewport.width]);
2373
2822
  const locateHeaderResizeHit = (0, import_react5.useCallback)((clientX, clientY) => {
2374
2823
  if (!columnResizable || !canvasRef.current) return null;
2375
2824
  const rect = canvasRef.current.getBoundingClientRect();
@@ -2380,6 +2829,7 @@ function Table({
2380
2829
  const level = getHeaderLevelAtY(localY, headerY);
2381
2830
  let closest = null;
2382
2831
  let distance = 6;
2832
+ let priority = -1;
2383
2833
  for (const cell of headerCells) {
2384
2834
  if (cell.level !== level) continue;
2385
2835
  if (columns[cell.startIndex]?.rowSelection || columns[cell.startIndex]?.rowDragHandle || columns[cell.startIndex]?.rowNumber) continue;
@@ -2387,8 +2837,10 @@ function Table({
2387
2837
  const edge = fixedSide === "right" && cell.leaf ? "left" : "right";
2388
2838
  const edgeX = getHeaderCellLeft(cell) + (edge === "right" ? getHeaderCellWidth(cell) : 0);
2389
2839
  const nextDistance = Math.abs(localX - edgeX);
2390
- if (nextDistance < distance) {
2840
+ const nextPriority = fixedSide === "right" ? 2 : fixedSide === "left" ? 1 : 0;
2841
+ if (nextDistance < distance || nextDistance === distance && nextPriority > priority) {
2391
2842
  distance = nextDistance;
2843
+ priority = nextPriority;
2392
2844
  closest = { cell, edge };
2393
2845
  }
2394
2846
  }
@@ -2480,6 +2932,7 @@ function Table({
2480
2932
  }
2481
2933
  const sourceIndex = headerDragCell.startIndex;
2482
2934
  columnDragPreviewRef.current = { sourceIndex, targetIndex: sourceIndex };
2935
+ validColumnDropRef.current = null;
2483
2936
  return {
2484
2937
  type: "table-column",
2485
2938
  sourceIndex,
@@ -2490,13 +2943,26 @@ function Table({
2490
2943
  fixed: columns[headerDragCell.startIndex]?.fixed
2491
2944
  };
2492
2945
  },
2493
- onGenerateDragPreview: ({ nativeSetDragImage, source }) => {
2946
+ onGenerateDragPreview: ({ nativeSetDragImage, source, location }) => {
2494
2947
  const sourceIndex = Number(source.data.sourceIndex);
2948
+ const canvasRect = canvas.getBoundingClientRect();
2949
+ const input = location.current.input;
2950
+ const previewOffset = source.data.type === "table-row" ? {
2951
+ x: Math.max(0, Math.min(canvasRect.width, input.clientX - canvasRect.left)) + 2,
2952
+ y: Math.max(0, Math.min(rowHeight, input.clientY - canvasRect.top - bodyTop - sourceIndex * rowHeight + scrollRef.current.top)) + 2
2953
+ } : {
2954
+ x: Math.max(0, Math.min(metrics[sourceIndex].width, input.clientX - canvasRect.left - getDisplayedColumnLeft(sourceIndex))),
2955
+ y: Math.max(0, Math.min(canvasRect.height, input.clientY - canvasRect.top))
2956
+ };
2495
2957
  (0, import_set_custom_native_drag_preview.setCustomNativeDragPreview)({
2496
2958
  nativeSetDragImage,
2959
+ getOffset: () => previewOffset,
2497
2960
  render: ({ container }) => {
2498
- const canvasRect = canvas.getBoundingClientRect();
2499
2961
  if (source.data.type === "table-row") {
2962
+ const frame = document.createElement("div");
2963
+ frame.className = "rvg-row-drag-preview-frame";
2964
+ frame.style.width = `${canvasRect.width + 4}px`;
2965
+ frame.style.height = `${rowHeight + 4}px`;
2500
2966
  const preview2 = document.createElement("div");
2501
2967
  preview2.className = "rvg-row-drag-preview";
2502
2968
  preview2.style.width = `${canvasRect.width}px`;
@@ -2522,10 +2988,14 @@ function Table({
2522
2988
  } else {
2523
2989
  cell.textContent = getCellLabel(sourceIndex, columnIndex);
2524
2990
  }
2991
+ if (selection?.rowKey === rowKey2 && selection.columnKey === column2.key) {
2992
+ cell.classList.add("is-selected");
2993
+ }
2525
2994
  preview2.appendChild(cell);
2526
2995
  });
2527
- container.appendChild(preview2);
2528
- return () => preview2.remove();
2996
+ frame.appendChild(preview2);
2997
+ container.appendChild(frame);
2998
+ return () => frame.remove();
2529
2999
  }
2530
3000
  const preview = document.createElement("div");
2531
3001
  preview.className = "rvg-column-drag-preview";
@@ -2539,6 +3009,29 @@ function Table({
2539
3009
  header.style.height = `${headerHeight}px`;
2540
3010
  header.style.justifyContent = column.align === "right" ? "flex-end" : column.align === "center" ? "center" : "flex-start";
2541
3011
  header.textContent = column.title;
3012
+ const visibleActions = getVisibleHeaderActions(column, columnWidth, columnDraggable, measureHeaderTitleWidth(sourceIndex));
3013
+ let actionRight = 2;
3014
+ if (visibleActions.drag) {
3015
+ const icon = createHeaderDragHandleSvg();
3016
+ icon.classList.add("rvg-preview-header-icon");
3017
+ icon.style.right = `${actionRight}px`;
3018
+ header.appendChild(icon);
3019
+ actionRight += HEADER_ACTION_SLOT_WIDTH;
3020
+ }
3021
+ if (visibleActions.sort) {
3022
+ const direction = sortState?.columnKey === column.key ? sortState.direction : null;
3023
+ const icon = createHeaderSortSvg(direction);
3024
+ icon.classList.add("rvg-preview-header-icon");
3025
+ icon.style.right = `${actionRight}px`;
3026
+ header.appendChild(icon);
3027
+ actionRight += HEADER_ACTION_SLOT_WIDTH;
3028
+ }
3029
+ if (visibleActions.filter) {
3030
+ const icon = createHeaderSearchSvg(Boolean(filterValues[column.key]));
3031
+ icon.classList.add("rvg-preview-header-icon", "rvg-preview-header-search-icon");
3032
+ icon.style.right = `${actionRight}px`;
3033
+ header.appendChild(icon);
3034
+ }
2542
3035
  preview.appendChild(header);
2543
3036
  for (let rowIndex = domRange.rowStart; rowIndex < domRange.rowEnd; rowIndex += 1) {
2544
3037
  const row = rows[rowIndex];
@@ -2567,6 +3060,8 @@ function Table({
2567
3060
  },
2568
3061
  onDragStart: ({ source }) => {
2569
3062
  suppressClickRef.current = true;
3063
+ hideDragTooltips();
3064
+ document.documentElement.classList.add("rvg-is-dragging");
2570
3065
  canvas.style.cursor = "move";
2571
3066
  if (source.data.type === "table-row") {
2572
3067
  const sourceIndex = Number(source.data.sourceIndex);
@@ -2592,30 +3087,66 @@ function Table({
2592
3087
  }
2593
3088
  const targetIndex = Number(target?.targetIndex);
2594
3089
  if (!Number.isInteger(targetIndex) || target?.headerKind !== source.data.headerKind || target?.parentKey !== source.data.parentKey || target?.fixed !== source.data.fixed) {
3090
+ validColumnDropRef.current = null;
3091
+ setColumnDropTarget(null);
2595
3092
  setDragGuide(null);
2596
3093
  return;
2597
3094
  }
2598
3095
  const edge = target?.columnEdge;
2599
3096
  const destinationIndex = getColumnDropIndex(sourceIndex, targetIndex, edge);
2600
3097
  columnDragPreviewRef.current = { sourceIndex, targetIndex: destinationIndex };
3098
+ if (destinationIndex === sourceIndex) {
3099
+ validColumnDropRef.current = null;
3100
+ setColumnDropTarget(null);
3101
+ setDragGuide(null);
3102
+ return;
3103
+ }
3104
+ validColumnDropRef.current = {
3105
+ sourceIndex,
3106
+ targetIndex: destinationIndex,
3107
+ rawTargetIndex: targetIndex,
3108
+ targetKey: String(target.targetKey),
3109
+ headerKind: source.data.headerKind === "group" ? "group" : "column",
3110
+ parentKey: typeof source.data.parentKey === "string" ? source.data.parentKey : void 0,
3111
+ fixed: source.data.fixed === "left" || source.data.fixed === "right" ? source.data.fixed : void 0,
3112
+ columnEdge: edge === "right" ? "right" : "left"
3113
+ };
3114
+ const siblingCells = headerCells.filter((cell) => cell.level === Number(source.data.headerLevel) && (cell.leaf ? "column" : "group") === source.data.headerKind && cell.parentKey === source.data.parentKey && columns[cell.startIndex]?.fixed === source.data.fixed);
3115
+ const targetEndIndex = Number.isInteger(target?.targetEndIndex) ? Number(target.targetEndIndex) : targetIndex;
3116
+ const hoveredTargetCell = siblingCells.find((cell) => cell.startIndex === targetIndex && cell.endIndex === targetEndIndex);
3117
+ if (!hoveredTargetCell) {
3118
+ validColumnDropRef.current = null;
3119
+ setColumnDropTarget(null);
3120
+ setDragGuide(null);
3121
+ return;
3122
+ }
3123
+ const highlightedCell = edge === "right" ? hoveredTargetCell : siblingCells.filter((cell) => cell.endIndex < hoveredTargetCell.startIndex).sort((left, right) => right.endIndex - left.endIndex)[0] ?? hoveredTargetCell;
3124
+ setColumnDropTarget({ startIndex: highlightedCell.startIndex, endIndex: highlightedCell.endIndex });
2601
3125
  const targetLeft = Number(target?.targetLeft);
2602
3126
  const targetWidth = Number(target?.targetWidth);
3127
+ if (!Number.isFinite(targetLeft) || !Number.isFinite(targetWidth)) {
3128
+ validColumnDropRef.current = null;
3129
+ setColumnDropTarget(null);
3130
+ setDragGuide(null);
3131
+ return;
3132
+ }
2603
3133
  const guideTop = headerRowOffsets[Number(source.data.headerLevel)] ?? 0;
2604
3134
  setDragGuide(edge === "right" ? targetLeft + targetWidth : targetLeft, guideTop);
2605
3135
  },
2606
3136
  onDrop: ({ source, location }) => {
3137
+ document.documentElement.classList.remove("rvg-is-dragging");
2607
3138
  const target = location.current.dropTargets[0]?.data;
2608
3139
  const sourceIndex = Number(source.data.sourceIndex);
2609
3140
  if (source.data.type === "table-row") {
2610
- const rawTargetIndex2 = Number(target?.targetIndex);
2611
- let targetIndex2 = rawTargetIndex2 + (target?.rowEdge === "bottom" ? 1 : 0);
2612
- if (sourceIndex < targetIndex2) targetIndex2 -= 1;
2613
- targetIndex2 = Math.max(0, Math.min(rows.length - 1, targetIndex2));
3141
+ const rawTargetIndex = Number(target?.targetIndex);
3142
+ let targetIndex = rawTargetIndex + (target?.rowEdge === "bottom" ? 1 : 0);
3143
+ if (sourceIndex < targetIndex) targetIndex -= 1;
3144
+ targetIndex = Math.max(0, Math.min(rows.length - 1, targetIndex));
2614
3145
  scrollerRef.current?.classList.add("rvg-row-drop-settling");
2615
3146
  setRowDragPreview(null);
2616
3147
  setRowDragGuide(null);
2617
3148
  canvas.style.cursor = "default";
2618
- if (Number.isInteger(rawTargetIndex2) && sourceIndex !== targetIndex2) onRowOrderChange?.(sourceIndex, targetIndex2);
3149
+ if (Number.isInteger(rawTargetIndex) && sourceIndex !== targetIndex) applyRowOrder(sourceIndex, targetIndex);
2619
3150
  requestAnimationFrame(() => {
2620
3151
  requestAnimationFrame(() => scrollerRef.current?.classList.remove("rvg-row-drop-settling"));
2621
3152
  });
@@ -2624,21 +3155,22 @@ function Table({
2624
3155
  }, 0);
2625
3156
  return;
2626
3157
  }
2627
- const rawTargetIndex = Number(target?.targetIndex);
2628
- const targetIndex = columnDragPreviewRef.current?.targetIndex ?? (Number.isInteger(rawTargetIndex) ? getColumnDropIndex(sourceIndex, rawTargetIndex, target?.columnEdge) : rawTargetIndex);
3158
+ const validDrop = validColumnDropRef.current;
2629
3159
  canvas.style.cursor = "default";
2630
3160
  setDragGuide(null);
3161
+ setColumnDropTarget(null);
2631
3162
  columnDragPreviewRef.current = null;
2632
- if (Number.isInteger(rawTargetIndex) && Number.isInteger(targetIndex) && sourceIndex !== targetIndex && target?.headerKind === source.data.headerKind && target?.parentKey === source.data.parentKey && target?.fixed === source.data.fixed) {
2633
- onColumnOrderChange?.(
2634
- sourceIndex - utilityColumnCount,
2635
- targetIndex - utilityColumnCount,
3163
+ validColumnDropRef.current = null;
3164
+ if (validDrop) {
3165
+ applyColumnOrder(
3166
+ validDrop.sourceIndex - utilityColumnCount,
3167
+ validDrop.targetIndex - utilityColumnCount,
2636
3168
  {
2637
- type: source.data.headerKind === "group" ? "group" : "column",
3169
+ type: validDrop.headerKind,
2638
3170
  sourceKey: String(source.data.sourceKey),
2639
- targetKey: String(target.targetKey),
2640
- parentKey: typeof source.data.parentKey === "string" ? source.data.parentKey : void 0,
2641
- placement: getColumnDropPlacement(sourceIndex, rawTargetIndex, target.columnEdge)
3171
+ targetKey: validDrop.targetKey,
3172
+ parentKey: validDrop.parentKey,
3173
+ placement: getColumnDropPlacement(validDrop.sourceIndex, validDrop.rawTargetIndex, validDrop.columnEdge)
2642
3174
  }
2643
3175
  );
2644
3176
  }
@@ -2650,6 +3182,7 @@ function Table({
2650
3182
  const cleanupDrop = (0, import_adapter.dropTargetForElements)({
2651
3183
  element: canvas,
2652
3184
  canDrop: ({ source }) => source.data.type === "table-column" || source.data.type === "table-row",
3185
+ getDropEffect: () => "move",
2653
3186
  getData: ({ input, element, source }) => {
2654
3187
  if (source.data.type === "table-row") {
2655
3188
  const targetIndex2 = locateRow(input.clientY);
@@ -2658,7 +3191,8 @@ function Table({
2658
3191
  const rowEdge = input.clientY - canvas.getBoundingClientRect().top < rowTop + rowHeight / 2 ? "top" : "bottom";
2659
3192
  return (0, import_closest_edge.attachClosestEdge)({ targetIndex: targetIndex2, rowEdge }, { element, input, allowedEdges: ["top", "bottom"] });
2660
3193
  }
2661
- const targetCell = locateHeaderCell(input.clientX, input.clientY);
3194
+ const hoveredColumnIndex = locateColumn(input.clientX);
3195
+ const targetCell = locateHeaderCell(input.clientX, input.clientY) ?? headerCells.find((cell) => hoveredColumnIndex >= cell.startIndex && hoveredColumnIndex <= cell.endIndex && cell.level === Number(source.data.headerLevel) && (cell.leaf ? "column" : "group") === source.data.headerKind && cell.parentKey === source.data.parentKey);
2662
3196
  if (!targetCell) return { targetIndex: -1 };
2663
3197
  let targetIndex = targetCell.startIndex;
2664
3198
  const targetFixed = columns[targetCell.startIndex]?.fixed;
@@ -2675,6 +3209,7 @@ function Table({
2675
3209
  const columnEdge = input.clientX - canvas.getBoundingClientRect().left < left + targetWidth / 2 ? "left" : "right";
2676
3210
  return (0, import_closest_edge.attachClosestEdge)({
2677
3211
  targetIndex,
3212
+ targetEndIndex: targetCell.endIndex,
2678
3213
  targetKey: targetCell.key,
2679
3214
  parentKey: targetCell.parentKey,
2680
3215
  headerKind,
@@ -2686,10 +3221,11 @@ function Table({
2686
3221
  }
2687
3222
  });
2688
3223
  return () => {
3224
+ document.documentElement.classList.remove("rvg-is-dragging");
2689
3225
  cleanupDrag();
2690
3226
  cleanupDrop();
2691
3227
  };
2692
- }, [bodyTop, columnDraggable, columns, fixedHeader, getCellLabel, getDisplayedColumnLeft, getHeaderCellWidth, getRowKey, headerHeight, locateColumn, locateHeaderCell, locateHeaderCellDragHandle, locateHeaderResizeCell, locateRow, metrics, onColumnOrderChange, onRowOrderChange, rowDraggable, rowHeight, rows, selectedRowKeySet, setDragGuide, setRowDragGuide, utilityColumnCount]);
3228
+ }, [applyColumnOrder, applyRowOrder, bodyTop, columnDraggable, columns, filterValues, fixedHeader, getCellLabel, getDisplayedColumnLeft, getHeaderCellWidth, getRowKey, headerCells, headerHeight, hideDragTooltips, locateColumn, locateHeaderCell, locateHeaderCellDragHandle, locateHeaderResizeCell, locateRow, measureHeaderTitleWidth, metrics, rowDraggable, rowHeight, rows, selectedRowKeySet, selection, setDragGuide, setRowDragGuide, sortState, utilityColumnCount]);
2693
3229
  const locateCell = (0, import_react5.useCallback)((clientX, clientY) => {
2694
3230
  const canvas = canvasRef.current;
2695
3231
  if (!canvas) return null;
@@ -2997,18 +3533,28 @@ function Table({
2997
3533
  const editorStyle = (0, import_react5.useMemo)(() => {
2998
3534
  if (!editing) return void 0;
2999
3535
  const metric = metrics[editing.columnIndex];
3000
- const fixedSide = columns[editing.columnIndex].fixed;
3536
+ const column = columns[editing.columnIndex];
3537
+ if (!metric || !column) return void 0;
3538
+ const fixedSide = column.fixed;
3001
3539
  const span = getCellSpan(editing.rowIndex, editing.columnIndex);
3002
3540
  const cellWidth = getCellDisplayWidth(editing.columnIndex, span?.colSpan ?? 1);
3003
3541
  const cellHeight = rowHeight * (span?.rowSpan ?? 1);
3004
3542
  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;
3543
+ const cellTop = bodyTop + editing.rowIndex * rowHeight - scrollRef.current.top;
3544
+ const horizontalStart = fixedSide === void 0 ? fixedWidth : 0;
3545
+ const horizontalEnd = fixedSide === void 0 ? viewport.width - rightFixedWidth : viewport.width;
3546
+ const verticalStart = fixedHeader ? bodyTop : 0;
3547
+ const visibleLeft = Math.max(cellLeft, horizontalStart);
3548
+ const visibleRight = Math.min(cellLeft + cellWidth, horizontalEnd, viewport.width);
3549
+ const visibleTop = Math.max(cellTop, verticalStart);
3550
+ const visibleBottom = Math.min(cellTop + cellHeight, renderHeight);
3005
3551
  return {
3006
- left: (fixedSide ? cellLeft : Math.max(cellLeft, fixedWidth)) + 1,
3007
- top: Math.max(fixedHeader ? bodyTop : 0, bodyTop + editing.rowIndex * rowHeight - scrollRef.current.top) + 1,
3008
- width: cellWidth - 2,
3009
- height: cellHeight - 2
3552
+ left: visibleLeft,
3553
+ top: visibleTop,
3554
+ width: Math.max(0, visibleRight - visibleLeft),
3555
+ height: Math.max(0, visibleBottom - visibleTop)
3010
3556
  };
3011
- }, [bodyTop, columns, editing, fixedHeader, fixedWidth, getCellDisplayWidth, getCellSpan, getScrollableColumnLeft, leftFixedOffsets, metrics, rightFixedOffsets, rowHeight, viewport.width]);
3557
+ }, [bodyTop, columns, editing, fixedHeader, fixedWidth, getCellDisplayWidth, getCellSpan, getScrollableColumnLeft, leftFixedOffsets, metrics, renderHeight, rightFixedOffsets, rightFixedWidth, rowHeight, viewport.width]);
3012
3558
  const handleColumnPointerDown = (0, import_react5.useCallback)((event) => {
3013
3559
  const rect = event.currentTarget.getBoundingClientRect();
3014
3560
  const localY = event.clientY - rect.top;
@@ -3028,9 +3574,10 @@ function Table({
3028
3574
  columnIndex: resizeIndex,
3029
3575
  startX: event.clientX,
3030
3576
  startWidth: startWidths.reduce((total, width2) => total + width2, 0),
3031
- startGuideX,
3032
3577
  guideTop: headerRowOffsets[resizeCell.level] ?? 0,
3033
3578
  resizeEdge,
3579
+ resizeStartIndex: resizeCell.startIndex,
3580
+ resizeEndIndex: resizeCell.endIndex,
3034
3581
  resizeIndices,
3035
3582
  startWidths
3036
3583
  };
@@ -3047,6 +3594,12 @@ function Table({
3047
3594
  const minWidth = getMinimumColumnWidth(column, columnDraggable);
3048
3595
  const pointerDelta = Math.round(event.clientX - drag.startX);
3049
3596
  const delta = drag.resizeEdge === "left" ? -pointerDelta : pointerDelta;
3597
+ const setGuideFromWidths = (nextWidths) => {
3598
+ const widthByIndex = new Map(drag.resizeIndices.map((columnIndex, index) => [columnIndex, nextWidths[index]]));
3599
+ const nextColumns = columns.map((column2, index) => widthByIndex.has(index) ? { ...column2, width: widthByIndex.get(index) } : column2);
3600
+ const nextMetrics = buildColumnMetrics(nextColumns, { columnDraggable, viewportWidth: viewport.width });
3601
+ setResizeGuideX(getHeaderCellEdgeFromMetrics(drag.resizeStartIndex, drag.resizeEndIndex, drag.resizeEdge, nextMetrics));
3602
+ };
3050
3603
  if (drag.resizeIndices.length > 1) {
3051
3604
  const minWidths = drag.resizeIndices.map((index) => getMinimumColumnWidth(columns[index], columnDraggable));
3052
3605
  const minTotalWidth = minWidths.reduce((total, width3) => total + width3, 0);
@@ -3063,13 +3616,13 @@ function Table({
3063
3616
  drag.resizeIndices.forEach((columnIndex, index) => {
3064
3617
  resizeColumn(columns[columnIndex].key, nextWidths[index]);
3065
3618
  });
3066
- setResizeGuideX(drag.startGuideX + (drag.resizeEdge === "left" ? drag.startWidth - targetTotalWidth : targetTotalWidth - drag.startWidth));
3619
+ setGuideFromWidths(nextWidths);
3067
3620
  return;
3068
3621
  }
3069
3622
  const width2 = Math.max(minWidth, Math.round(drag.startWidth + delta));
3070
3623
  resizeColumn(column.key, width2);
3071
- setResizeGuideX(drag.startGuideX + (drag.resizeEdge === "left" ? drag.startWidth - width2 : width2 - drag.startWidth));
3072
- }, [columnDraggable, columns, resizeColumn]);
3624
+ setGuideFromWidths([width2]);
3625
+ }, [columnDraggable, columns, getHeaderCellEdgeFromMetrics, resizeColumn, viewport.width]);
3073
3626
  const handleColumnPointerUp = (0, import_react5.useCallback)((event) => {
3074
3627
  const drag = columnDragRef.current;
3075
3628
  if (!drag) return;
@@ -3079,19 +3632,14 @@ function Table({
3079
3632
  setResizeGuideTop(0);
3080
3633
  }, []);
3081
3634
  const queueSortStateChange = (0, import_react5.useCallback)((next) => {
3082
- setDisplaySortState(next);
3083
- if (sortDebounceRef.current !== null) clearTimeout(sortDebounceRef.current);
3084
- sortDebounceRef.current = setTimeout(() => {
3085
- sortDebounceRef.current = null;
3086
- onSortStateChange?.(next);
3087
- }, 200);
3088
- }, [onSortStateChange]);
3635
+ setSortState(next);
3636
+ }, []);
3089
3637
  const renderHeaderIcons = (columnIndex, left) => {
3090
3638
  const column = columns[columnIndex];
3091
3639
  if (column.rowSelection || column.rowDragHandle || column.rowNumber) return null;
3092
3640
  const top = headerLeafTop + (headerLeafHeight - 14) / 2;
3093
3641
  const visibleActions = getVisibleHeaderActions(column, metrics[columnIndex].width, columnDraggable, measureHeaderTitleWidth(columnIndex));
3094
- let right = left + metrics[columnIndex].width - (visibleActions.drag ? 18 : 4);
3642
+ let right = left + metrics[columnIndex].width - (visibleActions.drag ? HEADER_ACTION_SLOT_WIDTH : 2);
3095
3643
  const icons = [];
3096
3644
  if (visibleActions.drag) {
3097
3645
  const hovered = hoveredHeaderAction?.columnIndex === columnIndex && hoveredHeaderAction.action === "drag";
@@ -3101,11 +3649,11 @@ function Table({
3101
3649
  }
3102
3650
  if (visibleActions.sort) {
3103
3651
  const hovered = hoveredHeaderAction?.columnIndex === columnIndex && hoveredHeaderAction.action === "sort";
3104
- const direction = displaySortState?.columnKey === column.key ? displaySortState.direction : null;
3652
+ const direction = sortState?.columnKey === column.key ? sortState.direction : null;
3105
3653
  icons.push(
3106
3654
  /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(HeaderSortIcon, { className: `rvg-header-icon${hovered ? " is-hovered" : ""}`, style: { left: right - 16, top }, direction }, "sort")
3107
3655
  );
3108
- right -= 18;
3656
+ right -= HEADER_ACTION_SLOT_WIDTH;
3109
3657
  }
3110
3658
  if (visibleActions.filter) {
3111
3659
  const hovered = hoveredHeaderAction?.columnIndex === columnIndex && hoveredHeaderAction.action === "filter";
@@ -3132,7 +3680,7 @@ function Table({
3132
3680
  if (context) context.font = "600 13px Inter, ui-sans-serif, system-ui, sans-serif";
3133
3681
  const titleWidth = context?.measureText(column.title).width ?? column.title.length * 13;
3134
3682
  const visibleActions = cell?.leaf || !cell ? getVisibleHeaderActions(column, cellWidth, columnDraggable, titleWidth) : null;
3135
- const actionWidth = visibleActions ? (Number(visibleActions.drag) + Number(visibleActions.filter) + Number(visibleActions.sort)) * 18 : columnDraggable ? 18 : 0;
3683
+ const actionWidth = visibleActions ? (Number(visibleActions.drag) + Number(visibleActions.filter) + Number(visibleActions.sort)) * HEADER_ACTION_SLOT_WIDTH : columnDraggable ? HEADER_ACTION_SLOT_WIDTH : 0;
3136
3684
  const contentWidth2 = Math.max(0, cellWidth - actionWidth);
3137
3685
  const maxTextWidth = Math.max(0, contentWidth2 - 20);
3138
3686
  const textWidth = Math.min(titleWidth, maxTextWidth);
@@ -3151,7 +3699,7 @@ function Table({
3151
3699
  anchorLeft = anchorX;
3152
3700
  label = labels.dragColumn;
3153
3701
  } else if (action === "sort") {
3154
- const direction = displaySortState?.columnKey === column.key ? displaySortState.direction : null;
3702
+ const direction = sortState?.columnKey === column.key ? sortState.direction : null;
3155
3703
  label = direction === "asc" ? labels.sortAsc : direction === "desc" ? labels.sortDesc : labels.sortBoth;
3156
3704
  anchorX = cellLeft + cellWidth - 22;
3157
3705
  anchorLeft = anchorX;
@@ -3171,7 +3719,7 @@ function Table({
3171
3719
  label,
3172
3720
  placement
3173
3721
  };
3174
- }, [baseHeaderHeight, columnDraggable, columns, displaySortState, filterValues, fixedHeader, getDisplayedColumnLeft, getHeaderCellLeft, getHeaderCellWidth, headerDepth, headerRowHeights, headerRowOffsets, labels, metrics, scrollPosition.top, viewport.width]);
3722
+ }, [baseHeaderHeight, columnDraggable, columns, filterValues, fixedHeader, getDisplayedColumnLeft, getHeaderCellLeft, getHeaderCellWidth, headerDepth, headerRowHeights, headerRowOffsets, labels, metrics, scrollPosition.top, sortState, viewport.width]);
3175
3723
  const isPointerOnHeaderTitle = (0, import_react5.useCallback)((clientX, clientY, cell) => {
3176
3724
  const canvas = canvasRef.current;
3177
3725
  if (!canvas) return false;
@@ -3186,7 +3734,7 @@ function Table({
3186
3734
  if (context) context.font = "600 13px Inter, ui-sans-serif, system-ui, sans-serif";
3187
3735
  const titleWidth = context?.measureText(column.title).width ?? column.title.length * 13;
3188
3736
  const visibleActions = cell.leaf ? getVisibleHeaderActions(column, cellWidth, columnDraggable, titleWidth) : null;
3189
- const actionWidth = visibleActions ? (Number(visibleActions.drag) + Number(visibleActions.filter) + Number(visibleActions.sort)) * 18 : columnDraggable ? 18 : 0;
3737
+ const actionWidth = visibleActions ? (Number(visibleActions.drag) + Number(visibleActions.filter) + Number(visibleActions.sort)) * HEADER_ACTION_SLOT_WIDTH : columnDraggable ? HEADER_ACTION_SLOT_WIDTH : 0;
3190
3738
  const contentWidth2 = Math.max(0, cellWidth - actionWidth);
3191
3739
  const textWidth = Math.min(titleWidth, Math.max(0, contentWidth2 - 20));
3192
3740
  const textLeft = column.align === "right" ? cellLeft + contentWidth2 - 10 - textWidth : column.align === "center" ? cellLeft + contentWidth2 / 2 - textWidth / 2 : cellLeft + 10;
@@ -3426,7 +3974,9 @@ function Table({
3426
3974
  });
3427
3975
  setPendingInserts((current) => [...current, pending]);
3428
3976
  try {
3429
- await Promise.resolve(onInsertRows({ rowIndex: sourceRowIndex >= 0 ? sourceRowIndex : cell.rowIndex, row, position, count, insertedRows }));
3977
+ const nextRows = sourceRows.slice();
3978
+ nextRows.splice(insertIndex, 0, ...insertedRows);
3979
+ await Promise.resolve(onInsertRows(nextRows, insertedRows));
3430
3980
  await waitForPaint();
3431
3981
  setInsertBusy(false);
3432
3982
  } catch (error) {
@@ -3453,28 +4003,10 @@ function Table({
3453
4003
  const currentScrollTop = scrollRef.current.top;
3454
4004
  const top = (fixedHeader ? 0 : bodyTop) + rowIndex * rowHeight - currentScrollTop;
3455
4005
  if (column.rowSelection) {
3456
- const rowKey2 = getRowKey(rows[rowIndex], rowIndex);
3457
- return /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
3458
- "div",
3459
- {
3460
- className: "rvg-row-selection-icon",
3461
- style: { left, top, width: cellWidth, height: cellHeight, opacity: isDraggedSource ? 0 : 1, transform: `translateY(${dragOffset}px)` },
3462
- children: /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(SelectionIcon, { checked: selectedRowKeySet.has(rowKey2), radio: rowSelectionMode === "single" })
3463
- },
3464
- `selection:${String(rowKey2)}`
3465
- );
4006
+ return null;
3466
4007
  }
3467
4008
  if (column.rowDragHandle) {
3468
- return /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
3469
- "div",
3470
- {
3471
- className: "rvg-row-drag-icon",
3472
- style: { left, top, width: cellWidth, height: cellHeight, opacity: isDraggedSource ? 0 : 1, transform: `translateY(${dragOffset}px)` },
3473
- "aria-hidden": "true",
3474
- children: /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(RowDragHandleIcon, {})
3475
- },
3476
- `drag:${String(getRowKey(rows[rowIndex], rowIndex))}`
3477
- );
4009
+ return null;
3478
4010
  }
3479
4011
  if (column.renderCell && !(editing?.rowIndex === rowIndex && editing.columnIndex === columnIndex)) {
3480
4012
  const row = rows[rowIndex];
@@ -3501,6 +4033,7 @@ function Table({
3501
4033
  };
3502
4034
  const renderHeaderTitle = (columnIndex, left) => {
3503
4035
  const column = columns[columnIndex];
4036
+ const isDropTarget = Boolean(columnDropTarget && columnIndex >= columnDropTarget.startIndex && columnIndex <= columnDropTarget.endIndex);
3504
4037
  const currentScrollTop = scrollRef.current.top;
3505
4038
  const headerTop = fixedHeader ? 0 : -currentScrollTop;
3506
4039
  const leafTop = headerTop + headerLeafTop;
@@ -3508,7 +4041,7 @@ function Table({
3508
4041
  return /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
3509
4042
  "div",
3510
4043
  {
3511
- className: "rvg-header-title",
4044
+ className: `rvg-header-title${isDropTarget ? " is-drop-target" : ""}`,
3512
4045
  style: { left, top: headerTop, width: metrics[columnIndex].width, height: headerHeight, justifyContent: "center", padding: 0, textAlign: "center" },
3513
4046
  children: /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("span", { children: column.title })
3514
4047
  },
@@ -3531,11 +4064,11 @@ function Table({
3531
4064
  }
3532
4065
  if (column.renderHeader) {
3533
4066
  const visibleActions = getVisibleHeaderActions(column, metrics[columnIndex].width, columnDraggable, measureHeaderTitleWidth(columnIndex));
3534
- const actionWidth = (Number(visibleActions.drag) + Number(visibleActions.filter) + Number(visibleActions.sort)) * 18;
4067
+ const actionWidth = (Number(visibleActions.drag) + Number(visibleActions.filter) + Number(visibleActions.sort)) * HEADER_ACTION_SLOT_WIDTH;
3535
4068
  return /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
3536
4069
  "div",
3537
4070
  {
3538
- className: "rvg-header-title is-custom",
4071
+ className: `rvg-header-title is-custom${isDropTarget ? " is-drop-target" : ""}`,
3539
4072
  "data-level": headerDepth - 1,
3540
4073
  "data-measure-key": column.key,
3541
4074
  style: {
@@ -3576,11 +4109,12 @@ function Table({
3576
4109
  const left = fixedSide === "left" ? leftFixedOffsets.get(cell.startIndex) ?? 0 : fixedSide === "right" ? viewport.width - (rightFixedOffsets.get(cell.startIndex) ?? 0) - startMetric.width : getScrollableColumnLeft(cell.startIndex);
3577
4110
  const width2 = cell.leaf ? startMetric.width : getCellDisplayWidth(cell.startIndex, cell.endIndex - cell.startIndex + 1);
3578
4111
  const visibleActions = cell.leaf ? getVisibleHeaderActions(column, startMetric.width, columnDraggable, measureHeaderTitleWidth(cell.startIndex)) : null;
3579
- const actionWidth = visibleActions ? (Number(visibleActions.drag) + Number(visibleActions.filter) + Number(visibleActions.sort)) * 18 : columnDraggable && !cell.leaf ? 18 : 0;
4112
+ const actionWidth = visibleActions ? (Number(visibleActions.drag) + Number(visibleActions.filter) + Number(visibleActions.sort)) * HEADER_ACTION_SLOT_WIDTH : columnDraggable && !cell.leaf ? HEADER_ACTION_SLOT_WIDTH : 0;
4113
+ const isDropTarget = Boolean(columnDropTarget && cell.endIndex >= columnDropTarget.startIndex && cell.startIndex <= columnDropTarget.endIndex);
3580
4114
  return /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)(
3581
4115
  "div",
3582
4116
  {
3583
- className: `rvg-header-title rvg-header-group${column.renderHeader ? " is-custom" : ""}${cell.leaf ? " is-leaf" : ""}`,
4117
+ className: `rvg-header-title rvg-header-group${column.renderHeader ? " is-custom" : ""}${cell.leaf ? " is-leaf" : ""}${isDropTarget ? " is-drop-target" : ""}`,
3584
4118
  "data-level": cell.level,
3585
4119
  "data-measure-key": `group:${cell.key}:${cell.level}`,
3586
4120
  style: {
@@ -3636,6 +4170,7 @@ function Table({
3636
4170
  })();
3637
4171
  const selectionOutlineStyle = (() => {
3638
4172
  if (!selection || selectionRange) return null;
4173
+ if (rowDragPreview?.sourceIndex === selection.rowIndex) return null;
3639
4174
  const row = rows[selection.rowIndex];
3640
4175
  const metric = metrics[selection.columnIndex];
3641
4176
  const column = columns[selection.columnIndex];
@@ -3645,22 +4180,37 @@ function Table({
3645
4180
  const rawRight = rawLeft + getCellDisplayWidth(selection.columnIndex, span?.colSpan ?? 1);
3646
4181
  const rawTop = bodyTop + selection.rowIndex * rowHeight - scrollPosition.top;
3647
4182
  const rawBottom = rawTop + rowHeight * (span?.rowSpan ?? 1);
4183
+ const dragOffset = getRowDragOffset(selection.rowIndex);
3648
4184
  const horizontalStart = column.fixed === void 0 ? fixedWidth : 0;
3649
4185
  const horizontalEnd = column.fixed === void 0 ? viewport.width - rightFixedWidth : viewport.width;
3650
4186
  const verticalStart = fixedHeader ? bodyTop : 0;
3651
- if (rawRight <= horizontalStart || rawLeft >= horizontalEnd || rawBottom <= verticalStart || rawTop >= renderHeight) return null;
4187
+ if (rawRight <= horizontalStart || rawLeft >= horizontalEnd || rawBottom + dragOffset <= verticalStart || rawTop + dragOffset >= renderHeight) return null;
3652
4188
  const borderWidth = 2;
3653
- const left = Math.round(Math.max(rawLeft - borderWidth, horizontalStart - 1));
4189
+ const left = Math.round(Math.max(rawLeft - borderWidth, horizontalStart));
3654
4190
  const right = Math.round(Math.min(rawRight + 1, horizontalEnd, viewport.width));
3655
- const top = Math.round(Math.max(rawTop - borderWidth, verticalStart - 1));
4191
+ const top = Math.round(Math.max(rawTop - borderWidth, verticalStart));
3656
4192
  const bottom = Math.round(Math.min(rawBottom + 1, renderHeight));
3657
4193
  return {
3658
4194
  left,
3659
4195
  top,
3660
4196
  width: Math.max(0, right - left),
3661
- height: Math.max(0, bottom - top)
4197
+ height: Math.max(0, bottom - top),
4198
+ transform: `translateY(${dragOffset}px)`
3662
4199
  };
3663
4200
  })();
4201
+ const selectionEdgeIndicator = getSelectionEdgeIndicator(scrollPosition.left, scrollPosition.top);
4202
+ const selectionEdgeStyle = selectionEdgeIndicator ? {
4203
+ ...selectionEdgeIndicator.style,
4204
+ ...selectionEdgeIndicator.corner ? {
4205
+ "--rvg-corner-width": `${selectionEdgeIndicator.corner.width}px`,
4206
+ "--rvg-corner-height": `${selectionEdgeIndicator.corner.height}px`,
4207
+ "--rvg-corner-hover-width-extension": "4px",
4208
+ "--rvg-corner-hover-height-extension": "4px"
4209
+ } : null
4210
+ } : void 0;
4211
+ (0, import_react5.useLayoutEffect)(() => {
4212
+ syncSelectionEdgeTarget(scrollPosition.left, scrollPosition.top);
4213
+ }, [scrollPosition.left, scrollPosition.top, syncSelectionEdgeTarget]);
3664
4214
  const handleContextMenu = (event) => {
3665
4215
  event.preventDefault();
3666
4216
  event.stopPropagation();
@@ -3804,6 +4354,10 @@ function Table({
3804
4354
  className: "rvg-canvas",
3805
4355
  style: { width: viewport.width, height: renderHeight },
3806
4356
  onMouseMove: (event) => {
4357
+ if (document.documentElement.classList.contains("rvg-is-dragging")) {
4358
+ hideDragTooltips();
4359
+ return;
4360
+ }
3807
4361
  if (columnDragRef.current) return;
3808
4362
  const rect = event.currentTarget.getBoundingClientRect();
3809
4363
  const localY = event.clientY - rect.top;
@@ -3906,6 +4460,14 @@ function Table({
3906
4460
  }
3907
4461
  },
3908
4462
  onPointerDown: (event) => {
4463
+ const headerDragCell = columnDraggable ? locateHeaderCell(event.clientX, event.clientY) : null;
4464
+ const columnIndex = locateColumn(event.clientX);
4465
+ const rowDragHandle = rowDraggable && columnIndex >= 0 && Boolean(columns[columnIndex].rowDragHandle) && locateRow(event.clientY) >= 0;
4466
+ if (headerDragCell && locateHeaderCellDragHandle(event.clientX, headerDragCell) || rowDragHandle) {
4467
+ hideDragTooltips();
4468
+ document.documentElement.classList.add("rvg-is-dragging");
4469
+ event.currentTarget.style.cursor = "move";
4470
+ }
3909
4471
  handleColumnPointerDown(event);
3910
4472
  beginRangeDrag(event);
3911
4473
  },
@@ -3914,10 +4476,12 @@ function Table({
3914
4476
  updateRangeDrag(event);
3915
4477
  },
3916
4478
  onPointerUp: (event) => {
4479
+ document.documentElement.classList.remove("rvg-is-dragging");
3917
4480
  handleColumnPointerUp(event);
3918
4481
  endRangeDrag(event);
3919
4482
  },
3920
4483
  onPointerCancel: (event) => {
4484
+ document.documentElement.classList.remove("rvg-is-dragging");
3921
4485
  handleColumnPointerUp(event);
3922
4486
  endRangeDrag(event);
3923
4487
  },
@@ -3936,7 +4500,7 @@ function Table({
3936
4500
  const action = locateHeaderAction(event.clientX, columnIndex);
3937
4501
  if (action === "sort") {
3938
4502
  const columnKey = columns[columnIndex].key;
3939
- const next = displaySortState?.columnKey !== columnKey ? { columnKey, direction: "asc" } : displaySortState.direction === "asc" ? { columnKey, direction: "desc" } : null;
4503
+ const next = sortState?.columnKey !== columnKey ? { columnKey, direction: "asc" } : sortState.direction === "asc" ? { columnKey, direction: "desc" } : null;
3940
4504
  queueSortStateChange(next);
3941
4505
  return;
3942
4506
  }
@@ -4108,6 +4672,27 @@ function Table({
4108
4672
  }
4109
4673
  ),
4110
4674
  selectionOutlineStyle && /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("div", { ref: selectionFocusRef, className: "rvg-selection-focus", style: selectionOutlineStyle, "aria-hidden": "true" }),
4675
+ selectionEdgeIndicator && selection && /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
4676
+ "button",
4677
+ {
4678
+ ref: selectionEdgeTargetRef,
4679
+ type: "button",
4680
+ className: `rvg-selection-edge-target is-${selectionEdgeIndicator.orientation} is-${selectionEdgeIndicator.side}${selectionEdgeIndicator.viewportEdge ? ` is-viewport-${selectionEdgeIndicator.viewportEdge}` : ""}`,
4681
+ style: selectionEdgeStyle,
4682
+ "aria-label": labels.revealSelectedCell,
4683
+ title: labels.revealSelectedCell,
4684
+ onPointerDown: (event) => {
4685
+ event.preventDefault();
4686
+ event.stopPropagation();
4687
+ },
4688
+ onClick: (event) => {
4689
+ event.stopPropagation();
4690
+ scrollerRef.current?.focus({ preventScroll: true });
4691
+ revealCell(selection);
4692
+ },
4693
+ children: /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("span", { className: "rvg-selection-edge-halo", "aria-hidden": "true", children: /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("svg", { viewBox: "0 0 100 100", preserveAspectRatio: "none", children: /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("path", { d: "M 0 100 L 0 0 L 100 0" }) }) })
4694
+ }
4695
+ ),
4111
4696
  contextMenu && /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("div", { ref: contextMenuRef, className: "rvg-context-menu", role: "menu", style: { left: contextMenu.left, top: contextMenu.top }, children: contextMenu.type === "header" ? (() => {
4112
4697
  const column = columns[contextMenu.columnIndex];
4113
4698
  const headerContext = { column, columnIndex: contextMenu.columnIndex };
@@ -4265,12 +4850,12 @@ function Table({
4265
4850
  /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("span", { children: labels.rowUnit })
4266
4851
  ] });
4267
4852
  if (item === "move-up") {
4268
- if (!rowDraggable || !onRowOrderChange) return null;
4269
- return renderContextMenuButton("cell-move-up", cellContext, labels.moveUp, () => onRowOrderChange(cell.rowIndex, cell.rowIndex - 1), override, { disabled: cell.rowIndex === 0, icon: /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(ContextMenuIcon, { type: "move-up" }) });
4853
+ if (!rowDraggable) return null;
4854
+ return renderContextMenuButton("cell-move-up", cellContext, labels.moveUp, () => applyRowOrder(cell.rowIndex, cell.rowIndex - 1), override, { disabled: cell.rowIndex === 0, icon: /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(ContextMenuIcon, { type: "move-up" }) });
4270
4855
  }
4271
4856
  if (item === "move-down") {
4272
- if (!rowDraggable || !onRowOrderChange) return null;
4273
- return renderContextMenuButton("cell-move-down", cellContext, labels.moveDown, () => onRowOrderChange(cell.rowIndex, cell.rowIndex + 1), override, { disabled: cell.rowIndex === rows.length - 1, icon: /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(ContextMenuIcon, { type: "move-down" }) });
4857
+ if (!rowDraggable) return null;
4858
+ return renderContextMenuButton("cell-move-down", cellContext, labels.moveDown, () => applyRowOrder(cell.rowIndex, cell.rowIndex + 1), override, { disabled: cell.rowIndex === rows.length - 1, icon: /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(ContextMenuIcon, { type: "move-down" }) });
4274
4859
  }
4275
4860
  if (item === "delete-row") return renderContextMenuButton("cell-delete-row", cellContext, labels.deleteRow, () => setConfirmAction({ type: "delete-row", rows: affectedRows.map((rowIndex) => ({ rowIndex, row: rows[rowIndex] })) }), override, { disabled: !onDeleteRows, danger: true, icon: /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(ContextMenuIcon, { type: "delete" }) });
4276
4861
  return null;
@@ -4302,21 +4887,30 @@ function Table({
4302
4887
  await Promise.resolve(onCellChange?.({ ...confirmAction.cell, row: confirmAction.row, previousValue: confirmAction.previousValue, value: "" }));
4303
4888
  } else {
4304
4889
  const deletingKeys = confirmAction.rows.map(({ row, rowIndex }) => getDataRowKey(row, rowIndex));
4890
+ const deletedRows = confirmAction.rows.map(({ row }) => row);
4891
+ const deletingKeySet = new Set(deletingKeys);
4892
+ const nextRows = sourceRows.filter((row, rowIndex) => !deletingKeySet.has(getDataRowKey(row, rowIndex)));
4305
4893
  setConfirmAction(null);
4306
4894
  setConfirmLoading(false);
4895
+ setPendingDeletedRowKeys((current) => {
4896
+ const next = new Set(current);
4897
+ deletingKeys.forEach((key) => next.add(key));
4898
+ return next;
4899
+ });
4307
4900
  if (onDeleteRows) {
4308
4901
  setDeleteBusy(true);
4309
4902
  try {
4310
- await Promise.resolve(onDeleteRows({ rows: confirmAction.rows, viewportRange: domRange }));
4903
+ await Promise.resolve(onDeleteRows(nextRows, deletedRows));
4904
+ } catch (error) {
4905
+ setPendingDeletedRowKeys((current) => {
4906
+ const next = new Set(current);
4907
+ deletingKeys.forEach((key) => next.delete(key));
4908
+ return next;
4909
+ });
4910
+ throw error;
4311
4911
  } finally {
4312
4912
  setDeleteBusy(false);
4313
4913
  }
4314
- } else {
4315
- setPendingDeletedRowKeys((current) => {
4316
- const next = new Set(current);
4317
- deletingKeys.forEach((key) => next.add(key));
4318
- return next;
4319
- });
4320
4914
  }
4321
4915
  return;
4322
4916
  }
@@ -4389,7 +4983,7 @@ function Table({
4389
4983
  const next = { ...filterValues };
4390
4984
  if (filterEditor.draft) next[key] = filterEditor.draft;
4391
4985
  else delete next[key];
4392
- onFilterValuesChange?.(next);
4986
+ setFilterValues(next);
4393
4987
  setFilterEditor(null);
4394
4988
  }
4395
4989
  }
@@ -4400,7 +4994,7 @@ function Table({
4400
4994
  const key = columns[filterEditor.columnIndex].key;
4401
4995
  const next = { ...filterValues };
4402
4996
  delete next[key];
4403
- onFilterValuesChange?.(next);
4997
+ setFilterValues(next);
4404
4998
  setFilterEditor(null);
4405
4999
  }, children: labels.reset }),
4406
5000
  /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("button", { type: "button", className: "rvg-filter-confirm", onClick: () => {
@@ -4408,7 +5002,7 @@ function Table({
4408
5002
  const next = { ...filterValues };
4409
5003
  if (filterEditor.draft) next[key] = filterEditor.draft;
4410
5004
  else delete next[key];
4411
- onFilterValuesChange?.(next);
5005
+ setFilterValues(next);
4412
5006
  setFilterEditor(null);
4413
5007
  }, children: labels.confirm })
4414
5008
  ] })
@@ -4432,8 +5026,8 @@ function Table({
4432
5026
  column.key
4433
5027
  );
4434
5028
  }) }, rowIndex)),
4435
- fixedWidth > 0 && /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("div", { className: "rvg-loading-fixed-shadow is-left", style: { left: fixedWidth } }),
4436
- rightFixedWidth > 0 && /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("div", { className: "rvg-loading-fixed-shadow is-right", style: { right: rightFixedWidth } })
5029
+ fixedWidth > 0 && currentScroll.left > 0 && /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("div", { className: "rvg-loading-fixed-shadow is-left", style: { left: fixedWidth } }),
5030
+ rightFixedWidth > 0 && currentScroll.left < contentWidth - viewport.width && /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("div", { className: "rvg-loading-fixed-shadow is-right", style: { right: rightFixedWidth } })
4437
5031
  ] }),
4438
5032
  loading && !hasCustomLoading && hasCompletedLoadRef.current && /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("div", { className: "rvg-refresh-loading", "aria-label": labels.refreshing, children: /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(LoadingSpinnerIcon, {}) })
4439
5033
  ] });