@lensui/lens-table 0.1.4 → 0.1.5

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",
@@ -333,7 +335,6 @@ function LoadingSpinnerIcon() {
333
335
  // src/components/editors/ChoiceEditor.tsx
334
336
  var import_jsx_runtime3 = require("react/jsx-runtime");
335
337
  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
338
  const collection = (0, import_select.createListCollection)({
338
339
  items: options,
339
340
  itemToValue: (item) => item.value,
@@ -343,7 +344,7 @@ function ChoiceEditor({ value, options, style, labels, onChange, onCommit, onCan
343
344
  import_select.Select.Root,
344
345
  {
345
346
  className: "rvg-choice-editor",
346
- style: editorStyle,
347
+ style,
347
348
  collection,
348
349
  value: value ? [value] : [],
349
350
  defaultOpen: true,
@@ -914,6 +915,7 @@ function paintGrid(options) {
914
915
  const leftFixedWidth = columns.reduce((value, column, index) => column.fixed === "left" ? value + metrics[index].width : value, 0);
915
916
  const rightFixedWidth = columns.reduce((value, column, index) => column.fixed === "right" ? value + metrics[index].width : value, 0);
916
917
  const rightFixedLeft = width - rightFixedWidth;
918
+ const rightFixedBoundaryInset = rightFixedWidth > 0 && !verticalBorderless ? 1 : 0;
917
919
  const leftOffsets = /* @__PURE__ */ new Map();
918
920
  let leftOffset = 0;
919
921
  for (let index = 0; index < columns.length; index += 1) {
@@ -1137,22 +1139,22 @@ function paintGrid(options) {
1137
1139
  const markerBottom = Math.min(bottom, height);
1138
1140
  const startX = getColumnX(columnStart);
1139
1141
  const endX = getColumnX(columnEnd) + metrics[columnEnd].width;
1140
- const visibleLeft = columns[columnStart].fixed === "left" ? Math.max(0, startX) : Math.max(leftFixedWidth - 1, startX);
1142
+ const visibleLeft = columns[columnStart].fixed === "left" ? Math.max(0, startX) : Math.max(leftFixedWidth, startX);
1141
1143
  const visibleRight = columns[columnEnd].fixed === "right" ? Math.min(width, endX) : Math.min(rightFixedLeft, endX);
1142
1144
  ctx.fillStyle = colors.selection;
1143
1145
  if (markerBottom > markerTop) {
1144
1146
  if (columns[columnStart].fixed === void 0 && startX < leftFixedWidth) {
1145
- ctx.fillRect(Math.max(0, leftFixedWidth - 1), markerTop, 2, markerBottom - markerTop);
1147
+ ctx.fillRect(Math.max(0, leftFixedWidth), markerTop, 2, markerBottom - markerTop);
1146
1148
  }
1147
1149
  if (columns[columnEnd].fixed === void 0 && endX > rightFixedLeft) {
1148
- ctx.fillRect(Math.max(0, rightFixedLeft - 1), markerTop, 2, markerBottom - markerTop);
1150
+ ctx.fillRect(Math.max(0, rightFixedLeft - 2 - rightFixedBoundaryInset), markerTop, 2, markerBottom - markerTop);
1149
1151
  }
1150
1152
  }
1151
1153
  if (visibleRight > visibleLeft) {
1152
1154
  if (top < clipTop) ctx.fillRect(visibleLeft, clipTop, visibleRight - visibleLeft, 2);
1153
1155
  if (bottom > height) ctx.fillRect(visibleLeft, height - 2, visibleRight - visibleLeft, 2);
1154
1156
  } else if (top < clipTop || bottom > height) {
1155
- const markerX = endX <= leftFixedWidth ? Math.max(0, leftFixedWidth - 1) : Math.max(0, rightFixedLeft - 1);
1157
+ const markerX = endX <= leftFixedWidth ? Math.max(0, leftFixedWidth) : Math.max(0, rightFixedLeft - 2 - rightFixedBoundaryInset);
1156
1158
  ctx.fillRect(markerX, top < clipTop ? clipTop : height - 2, 2, 2);
1157
1159
  }
1158
1160
  }
@@ -1174,15 +1176,15 @@ function paintGrid(options) {
1174
1176
  const visibleRight = x + selectionWidth;
1175
1177
  const visibleBottom = y + selectionHeight;
1176
1178
  const selectionTop = fixedHeader ? bodyTop : 0;
1177
- const selectionClipTop = fixedHeader ? Math.max(0, selectionTop - 1) : selectionTop;
1179
+ const selectionClipTop = selectionTop;
1178
1180
  const hiddenAboveHeader = visibleBottom <= selectionTop;
1179
1181
  const hiddenBelowViewport = y >= height;
1180
1182
  const hiddenBehindLeft = !isFixed && visibleRight <= visibleLeft;
1181
1183
  const hiddenBehindRight = !isFixed && x >= selectionAreaRight;
1182
- const leftMarkerX = Math.max(0, leftFixedWidth - 1);
1184
+ const leftMarkerX = Math.max(0, leftFixedWidth);
1183
1185
  if ((hiddenAboveHeader || hiddenBelowViewport) && (hiddenBehindLeft || hiddenBehindRight)) {
1184
1186
  const markerSize = 2;
1185
- const markerX = hiddenBehindLeft ? leftMarkerX : selectionAreaRight - borderWidth;
1187
+ const markerX = hiddenBehindLeft ? leftMarkerX : selectionAreaRight - borderWidth - rightFixedBoundaryInset;
1186
1188
  const markerY = hiddenAboveHeader ? selectionClipTop : height - markerSize;
1187
1189
  ctx.fillStyle = colors.selection;
1188
1190
  ctx.fillRect(markerX, markerY, markerSize, markerSize);
@@ -1190,7 +1192,7 @@ function paintGrid(options) {
1190
1192
  }
1191
1193
  ctx.save();
1192
1194
  ctx.beginPath();
1193
- const clipLeft = fixedSide === "left" ? 0 : Math.max(0, leftFixedWidth - 1);
1195
+ const clipLeft = fixedSide === "left" ? 0 : Math.max(0, leftFixedWidth);
1194
1196
  const clipRight = Math.min(width, selectionAreaRight + 1);
1195
1197
  ctx.rect(clipLeft, selectionClipTop, clipRight - clipLeft, height - selectionClipTop);
1196
1198
  ctx.clip();
@@ -1208,14 +1210,14 @@ function paintGrid(options) {
1208
1210
  if (hiddenBehindLeft || hiddenBehindRight) {
1209
1211
  const markerTop = Math.max(y - borderWidth, selectionTop);
1210
1212
  const markerBottom = Math.min(y + selectionHeight + 1, height);
1211
- const markerX = hiddenBehindLeft ? leftMarkerX : selectionAreaRight - borderWidth;
1213
+ const markerX = hiddenBehindLeft ? leftMarkerX : selectionAreaRight - borderWidth - rightFixedBoundaryInset;
1212
1214
  ctx.fillRect(markerX, markerTop, borderWidth, Math.max(0, markerBottom - markerTop));
1213
1215
  }
1214
1216
  if (hiddenAboveHeader || hiddenBelowViewport || hiddenBehindLeft || hiddenBehindRight) {
1215
1217
  ctx.restore();
1216
1218
  return;
1217
1219
  }
1218
- const borderLeft = x <= 0 ? 0 : Math.max(x - borderWidth, visibleLeft - 1);
1220
+ const borderLeft = x <= 0 ? 0 : Math.max(x - borderWidth, visibleLeft);
1219
1221
  const borderRight = Math.min(x + selectionWidth - 1, selectionAreaRight - borderWidth, width - borderWidth);
1220
1222
  const borderTop = Math.max(y - borderWidth, selectionClipTop);
1221
1223
  const borderBottom = Math.min(y + selectionHeight - 1, height - borderWidth);
@@ -1324,6 +1326,25 @@ var createEmptyCellSpanLookup = () => ({
1324
1326
  covered: /* @__PURE__ */ new Map(),
1325
1327
  maxRowSpan: 1
1326
1328
  });
1329
+ var summaryFunctionIds = /* @__PURE__ */ new WeakMap();
1330
+ var nextSummaryFunctionId = 1;
1331
+ function getSummaryFunctionId(fn) {
1332
+ if (!fn) return "";
1333
+ const existing = summaryFunctionIds.get(fn);
1334
+ if (existing !== void 0) return existing;
1335
+ const next = nextSummaryFunctionId;
1336
+ nextSummaryFunctionId += 1;
1337
+ summaryFunctionIds.set(fn, next);
1338
+ return next;
1339
+ }
1340
+ function getSummarySignature(columns) {
1341
+ return columns.filter((column) => Boolean(column.summary)).map((column) => [
1342
+ column.key,
1343
+ String(column.dataIndex ?? ""),
1344
+ typeof column.summary === "function" ? getSummaryFunctionId(column.summary) : "sum",
1345
+ getSummaryFunctionId(column.formatter)
1346
+ ].join("")).join("");
1347
+ }
1327
1348
  async function calculateSummaryValues(columns, rows) {
1328
1349
  const values = /* @__PURE__ */ new Map();
1329
1350
  for (const column of columns) {
@@ -1508,6 +1529,7 @@ function Table({
1508
1529
  const dragGuideRef = (0, import_react5.useRef)(null);
1509
1530
  const rowDragGuideRef = (0, import_react5.useRef)(null);
1510
1531
  const selectionFocusRef = (0, import_react5.useRef)(null);
1532
+ const selectionEdgeTargetRef = (0, import_react5.useRef)(null);
1511
1533
  const verticalScrollbarRef = (0, import_react5.useRef)(null);
1512
1534
  const verticalScrollbarThumbRef = (0, import_react5.useRef)(null);
1513
1535
  const verticalScrollbarDragRef = (0, import_react5.useRef)(null);
@@ -1755,7 +1777,8 @@ function Table({
1755
1777
  const getScrollableColumnLeft = (0, import_react5.useCallback)((columnIndex) => scrollableColumnLefts[columnIndex] ?? fixedWidth, [fixedWidth, scrollableColumnLefts]);
1756
1778
  const hasRowSelectionColumn = (0, import_react5.useMemo)(() => columns.some((column) => column.rowSelection), [columns]);
1757
1779
  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]);
1780
+ const summarySignature = (0, import_react5.useMemo)(() => getSummarySignature(columns), [columns]);
1781
+ const hasSummaryRow = rows.length > 0 && summaryEnabled && summarySignature.length > 0;
1759
1782
  const topSummaryHeight = hasSummaryRow && summaryPosition === "top" ? rowHeight : 0;
1760
1783
  const bottomSummaryHeight = hasSummaryRow && summaryPosition === "bottom" ? rowHeight : 0;
1761
1784
  const bodyTop = headerHeight + topSummaryHeight;
@@ -1764,21 +1787,21 @@ function Table({
1764
1787
  const effectiveHeight = autoHeight ? Math.max(bodyTop + bottomSummaryHeight, Math.min(resolvedHeight, realContentHeight)) : resolvedHeight;
1765
1788
  const renderHeight = Math.max(0, effectiveHeight - bottomSummaryHeight);
1766
1789
  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();
1790
+ const [summaryState, setSummaryState] = (0, import_react5.useState)(() => ({ signature: "", rows: null, values: /* @__PURE__ */ new Map(), pending: false }));
1791
+ const summaryPending = hasSummaryRow && (loading || summaryState.pending || summaryState.signature !== summarySignature || summaryState.rows !== rows);
1792
+ const summaryValues = hasSummaryRow && summaryState.signature === summarySignature && summaryState.rows === rows ? summaryState.values : /* @__PURE__ */ new Map();
1770
1793
  (0, import_react5.useEffect)(() => {
1771
1794
  let cancelled = false;
1772
1795
  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 });
1796
+ setSummaryState((current) => current.signature === "" && current.rows === null && current.values.size === 0 && !current.pending ? current : { signature: "", rows: null, values: /* @__PURE__ */ new Map(), pending: false });
1774
1797
  return () => {
1775
1798
  cancelled = true;
1776
1799
  };
1777
1800
  }
1778
1801
  setSummaryState((current) => ({
1779
- columns,
1802
+ signature: summarySignature,
1780
1803
  rows,
1781
- values: current.columns === columns && current.rows === rows ? current.values : /* @__PURE__ */ new Map(),
1804
+ values: current.signature === summarySignature && current.rows === rows ? current.values : /* @__PURE__ */ new Map(),
1782
1805
  pending: true
1783
1806
  }));
1784
1807
  if (loading) {
@@ -1791,13 +1814,13 @@ function Table({
1791
1814
  if (cancelled) return;
1792
1815
  const values = await calculateSummaryValues(columns, rows);
1793
1816
  if (cancelled) return;
1794
- setSummaryState({ columns, rows, values, pending: false });
1817
+ setSummaryState({ signature: summarySignature, rows, values, pending: false });
1795
1818
  };
1796
1819
  void run();
1797
1820
  return () => {
1798
1821
  cancelled = true;
1799
1822
  };
1800
- }, [columns, hasSummaryRow, loading, rows]);
1823
+ }, [hasSummaryRow, loading, rows, summarySignature]);
1801
1824
  const domRange = (0, import_react5.useMemo)(() => {
1802
1825
  if (!isVirtualized) return { rowStart: 0, rowEnd: rows.length, columnStart: 0, columnEnd: columns.length };
1803
1826
  const currentScroll2 = scrollRef.current;
@@ -1944,6 +1967,158 @@ function Table({
1944
1967
  const scheduleDraw = (0, import_react5.useCallback)(() => {
1945
1968
  if (frameRef.current === null) frameRef.current = requestAnimationFrame(draw);
1946
1969
  }, [draw]);
1970
+ const drawImmediately = (0, import_react5.useCallback)(() => {
1971
+ if (frameRef.current !== null) cancelAnimationFrame(frameRef.current);
1972
+ frameRef.current = null;
1973
+ draw();
1974
+ }, [draw]);
1975
+ const getSelectionHorizontalVisibility = (0, import_react5.useCallback)((scrollLeft) => {
1976
+ if (!selection || selectionRange) return "visible";
1977
+ const column = columns[selection.columnIndex];
1978
+ if (!column || column.fixed !== void 0) return "visible";
1979
+ const span = getCellSpan(selection.rowIndex, selection.columnIndex);
1980
+ const left = getScrollableColumnLeft(selection.columnIndex) - scrollLeft;
1981
+ const right = left + getCellDisplayWidth(selection.columnIndex, span?.colSpan ?? 1);
1982
+ if (right <= fixedWidth) return "hidden-left";
1983
+ if (left >= viewport.width - rightFixedWidth) return "hidden-right";
1984
+ return "visible";
1985
+ }, [columns, fixedWidth, getCellDisplayWidth, getCellSpan, getScrollableColumnLeft, rightFixedWidth, selection, selectionRange, viewport.width]);
1986
+ const getSelectionEdgeIndicator = (0, import_react5.useCallback)((scrollLeft, scrollTop) => {
1987
+ if (!selection || selectionRange) return null;
1988
+ const row = rows[selection.rowIndex];
1989
+ const metric = metrics[selection.columnIndex];
1990
+ const column = columns[selection.columnIndex];
1991
+ if (!row || !metric || !column || getRowKey(row, selection.rowIndex) !== selection.rowKey) return null;
1992
+ const span = getCellSpan(selection.rowIndex, selection.columnIndex);
1993
+ 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;
1994
+ const rawRight = rawLeft + getCellDisplayWidth(selection.columnIndex, span?.colSpan ?? 1);
1995
+ const rawTop = bodyTop + selection.rowIndex * rowHeight - scrollTop;
1996
+ const rawBottom = rawTop + rowHeight * (span?.rowSpan ?? 1);
1997
+ const horizontalStart = column.fixed === void 0 ? fixedWidth : 0;
1998
+ const horizontalEnd = column.fixed === void 0 ? viewport.width - rightFixedWidth : viewport.width;
1999
+ const verticalStart = fixedHeader ? bodyTop : 0;
2000
+ const hiddenLeft = column.fixed === void 0 && rawRight <= horizontalStart;
2001
+ const hiddenRight = column.fixed === void 0 && rawLeft >= horizontalEnd;
2002
+ const hiddenTop = rawBottom <= verticalStart;
2003
+ const hiddenBottom = rawTop >= renderHeight;
2004
+ if (!hiddenLeft && !hiddenRight && !hiddenTop && !hiddenBottom) return null;
2005
+ const borderWidth = 2;
2006
+ const cornerLength = 10;
2007
+ const hitSlop = 6;
2008
+ const targetSize = borderWidth + hitSlop * 2;
2009
+ const rightBoundaryInset = rightFixedWidth > 0 && hasVerticalBorders ? 1 : 0;
2010
+ const markerX = hiddenLeft ? horizontalStart : horizontalEnd - borderWidth - rightBoundaryInset;
2011
+ const markerY = hiddenTop ? verticalStart : renderHeight - borderWidth;
2012
+ if (hiddenLeft || hiddenRight) {
2013
+ let markerTop = Math.max(rawTop - borderWidth, verticalStart);
2014
+ let markerBottom = Math.min(rawBottom + 1, renderHeight);
2015
+ const topClip = Math.max(0, verticalStart - (rawTop - borderWidth));
2016
+ const bottomClip = Math.max(0, rawBottom + 1 - renderHeight);
2017
+ if (topClip > 0 || bottomClip > 0) {
2018
+ const foldAtTop = topClip > 0;
2019
+ const fold = Math.min(cornerLength, foldAtTop ? topClip : bottomClip);
2020
+ if (fold > borderWidth) {
2021
+ const cornerWidth = fold;
2022
+ const cornerHeight = Math.max(cornerLength, markerBottom - markerTop);
2023
+ const cornerOnLeft = hiddenLeft;
2024
+ const shapeLeft = cornerOnLeft ? markerX : markerX + borderWidth - cornerWidth;
2025
+ const shapeTop = foldAtTop ? verticalStart : renderHeight - cornerHeight;
2026
+ return {
2027
+ side: `${foldAtTop ? "top" : "bottom"}-${cornerOnLeft ? "left" : "right"}`,
2028
+ orientation: "corner",
2029
+ style: {
2030
+ left: shapeLeft - hitSlop,
2031
+ top: shapeTop - hitSlop,
2032
+ width: cornerWidth + hitSlop * 2,
2033
+ height: cornerHeight + hitSlop * 2
2034
+ },
2035
+ corner: { width: cornerWidth, height: cornerHeight }
2036
+ };
2037
+ }
2038
+ }
2039
+ if (markerBottom <= markerTop) return null;
2040
+ return {
2041
+ side: hiddenLeft ? "left" : "right",
2042
+ orientation: "vertical",
2043
+ viewportEdge: hiddenLeft && horizontalStart === 0 ? "left" : hiddenRight && horizontalEnd === viewport.width ? "right" : void 0,
2044
+ style: {
2045
+ left: markerX - hitSlop,
2046
+ top: markerTop,
2047
+ width: targetSize,
2048
+ height: markerBottom - markerTop
2049
+ }
2050
+ };
2051
+ }
2052
+ const horizontalMarkerEnd = horizontalEnd - rightBoundaryInset;
2053
+ let markerLeft = Math.max(rawLeft - borderWidth, horizontalStart);
2054
+ let markerRight = Math.min(rawRight + 1, horizontalMarkerEnd);
2055
+ const leftClip = Math.max(0, horizontalStart - (rawLeft - borderWidth));
2056
+ const rightClip = Math.max(0, rawRight + 1 - horizontalMarkerEnd);
2057
+ if (leftClip > 0 || rightClip > 0) {
2058
+ const foldAtLeft = leftClip > 0;
2059
+ const fold = Math.min(cornerLength, foldAtLeft ? leftClip : rightClip);
2060
+ if (fold > borderWidth) {
2061
+ const cornerWidth = Math.max(cornerLength, markerRight - markerLeft);
2062
+ const cornerHeight = fold;
2063
+ const shapeLeft = foldAtLeft ? horizontalStart : horizontalMarkerEnd - cornerWidth;
2064
+ const shapeTop = hiddenTop ? verticalStart : renderHeight - cornerHeight;
2065
+ return {
2066
+ side: `${hiddenTop ? "top" : "bottom"}-${foldAtLeft ? "left" : "right"}`,
2067
+ orientation: "corner",
2068
+ style: {
2069
+ left: shapeLeft - hitSlop,
2070
+ top: shapeTop - hitSlop,
2071
+ width: cornerWidth + hitSlop * 2,
2072
+ height: cornerHeight + hitSlop * 2
2073
+ },
2074
+ corner: { width: cornerWidth, height: cornerHeight }
2075
+ };
2076
+ }
2077
+ }
2078
+ if (markerRight <= markerLeft) return null;
2079
+ return {
2080
+ side: hiddenTop ? "top" : "bottom",
2081
+ orientation: "horizontal",
2082
+ viewportEdge: hiddenTop && verticalStart === 0 ? "top" : hiddenBottom ? "bottom" : void 0,
2083
+ style: {
2084
+ left: markerLeft,
2085
+ top: markerY - hitSlop,
2086
+ width: markerRight - markerLeft,
2087
+ height: targetSize
2088
+ }
2089
+ };
2090
+ }, [bodyTop, columns, fixedHeader, fixedWidth, getCellDisplayWidth, getCellSpan, getRowKey, getScrollableColumnLeft, hasVerticalBorders, leftFixedOffsets, metrics, renderHeight, rightFixedOffsets, rightFixedWidth, rowHeight, rows, selection, selectionRange, viewport.width]);
2091
+ const syncSelectionEdgeTarget = (0, import_react5.useCallback)((scrollLeft, scrollTop) => {
2092
+ const target = selectionEdgeTargetRef.current;
2093
+ if (!target) return;
2094
+ const indicator = getSelectionEdgeIndicator(scrollLeft, scrollTop);
2095
+ if (!indicator) {
2096
+ target.style.visibility = "hidden";
2097
+ return;
2098
+ }
2099
+ const orientationChanged = !target.classList.contains(`is-${indicator.orientation}`);
2100
+ target.className = `rvg-selection-edge-target is-${indicator.orientation} is-${indicator.side}${indicator.viewportEdge ? ` is-viewport-${indicator.viewportEdge}` : ""}${orientationChanged ? " is-shape-syncing" : ""}`;
2101
+ target.style.left = `${Number(indicator.style.left)}px`;
2102
+ target.style.top = `${Number(indicator.style.top)}px`;
2103
+ target.style.width = `${Number(indicator.style.width)}px`;
2104
+ target.style.height = `${Number(indicator.style.height)}px`;
2105
+ if (indicator.corner) {
2106
+ target.style.setProperty("--rvg-corner-width", `${indicator.corner.width}px`);
2107
+ target.style.setProperty("--rvg-corner-height", `${indicator.corner.height}px`);
2108
+ target.style.setProperty("--rvg-corner-hover-width-extension", "4px");
2109
+ target.style.setProperty("--rvg-corner-hover-height-extension", "4px");
2110
+ } else {
2111
+ target.style.removeProperty("--rvg-corner-width");
2112
+ target.style.removeProperty("--rvg-corner-height");
2113
+ target.style.removeProperty("--rvg-corner-hover-width-extension");
2114
+ target.style.removeProperty("--rvg-corner-hover-height-extension");
2115
+ }
2116
+ target.style.visibility = "visible";
2117
+ if (orientationChanged) {
2118
+ void target.offsetWidth;
2119
+ target.classList.remove("is-shape-syncing");
2120
+ }
2121
+ }, [getSelectionEdgeIndicator]);
1947
2122
  (0, import_react5.useLayoutEffect)(() => {
1948
2123
  const element = scrollerRef.current;
1949
2124
  if (!element) return;
@@ -2194,7 +2369,13 @@ function Table({
2194
2369
  }
2195
2370
  const nextThumbLeft = Math.max(0, Math.min(trackWidth - thumbWidth, event.clientX - trackRect.left - thumbWidth / 2));
2196
2371
  const maxThumbLeft = Math.max(1, trackWidth - thumbWidth);
2197
- scroller.scrollLeft = nextThumbLeft / maxThumbLeft * maxScrollLeft;
2372
+ const previousScrollLeft = scroller.scrollLeft;
2373
+ const nextScrollLeft = nextThumbLeft / maxThumbLeft * maxScrollLeft;
2374
+ const visibilityChanged = getSelectionHorizontalVisibility(previousScrollLeft) !== getSelectionHorizontalVisibility(nextScrollLeft);
2375
+ scroller.scrollLeft = nextScrollLeft;
2376
+ scrollRef.current = { left: scroller.scrollLeft, top: scroller.scrollTop };
2377
+ syncSelectionEdgeTarget(scroller.scrollLeft, scroller.scrollTop);
2378
+ if (visibilityChanged) drawImmediately();
2198
2379
  updateCustomScrollbars();
2199
2380
  horizontalScrollbarDragRef.current = {
2200
2381
  pointerId: event.pointerId,
@@ -2204,7 +2385,7 @@ function Table({
2204
2385
  thumbWidth,
2205
2386
  maxScrollLeft
2206
2387
  };
2207
- }, [showCustomScrollbar, updateCustomScrollbars]);
2388
+ }, [drawImmediately, getSelectionHorizontalVisibility, showCustomScrollbar, syncSelectionEdgeTarget, updateCustomScrollbars]);
2208
2389
  const handleHorizontalScrollbarPointerMove = (0, import_react5.useCallback)((event) => {
2209
2390
  const drag = horizontalScrollbarDragRef.current;
2210
2391
  const scroller = scrollerRef.current;
@@ -2212,9 +2393,15 @@ function Table({
2212
2393
  event.preventDefault();
2213
2394
  const maxThumbLeft = Math.max(1, drag.trackWidth - drag.thumbWidth);
2214
2395
  const nextThumbLeft = Math.max(0, Math.min(maxThumbLeft, drag.startLeft + event.clientX - drag.startX));
2215
- scroller.scrollLeft = nextThumbLeft / maxThumbLeft * drag.maxScrollLeft;
2396
+ const previousScrollLeft = scroller.scrollLeft;
2397
+ const nextScrollLeft = nextThumbLeft / maxThumbLeft * drag.maxScrollLeft;
2398
+ const visibilityChanged = getSelectionHorizontalVisibility(previousScrollLeft) !== getSelectionHorizontalVisibility(nextScrollLeft);
2399
+ scroller.scrollLeft = nextScrollLeft;
2400
+ scrollRef.current = { left: scroller.scrollLeft, top: scroller.scrollTop };
2401
+ syncSelectionEdgeTarget(scroller.scrollLeft, scroller.scrollTop);
2402
+ if (visibilityChanged) drawImmediately();
2216
2403
  updateCustomScrollbars();
2217
- }, [updateCustomScrollbars]);
2404
+ }, [drawImmediately, getSelectionHorizontalVisibility, syncSelectionEdgeTarget, updateCustomScrollbars]);
2218
2405
  const handleHorizontalScrollbarPointerUp = (0, import_react5.useCallback)((event) => {
2219
2406
  const drag = horizontalScrollbarDragRef.current;
2220
2407
  if (!drag || drag.pointerId !== event.pointerId) return;
@@ -2229,6 +2416,7 @@ function Table({
2229
2416
  const scrollDeltaX = element.scrollLeft - previousScroll.left;
2230
2417
  const scrollDeltaY = element.scrollTop - previousScroll.top;
2231
2418
  scrollRef.current = { left: element.scrollLeft, top: element.scrollTop };
2419
+ syncSelectionEdgeTarget(element.scrollLeft, element.scrollTop);
2232
2420
  const focus = selectionFocusRef.current;
2233
2421
  if (focus && selection && !selectionRange) {
2234
2422
  const row = rows[selection.rowIndex];
@@ -2250,9 +2438,9 @@ function Table({
2250
2438
  focus.style.display = "none";
2251
2439
  } else {
2252
2440
  const borderWidth = 2;
2253
- const left = Math.round(Math.max(rawLeft - borderWidth, horizontalStart - 1));
2441
+ const left = Math.round(Math.max(rawLeft - borderWidth, horizontalStart));
2254
2442
  const right = Math.round(Math.min(rawRight + 1, horizontalEnd, viewport.width));
2255
- const top = Math.round(Math.max(rawTop - borderWidth, verticalStart - 1));
2443
+ const top = Math.round(Math.max(rawTop - borderWidth, verticalStart));
2256
2444
  const bottom = Math.round(Math.min(rawBottom + 1, renderHeight));
2257
2445
  focus.style.display = "block";
2258
2446
  focus.style.left = `${left}px`;
@@ -2282,7 +2470,7 @@ function Table({
2282
2470
  setHoveredCellTooltip(null);
2283
2471
  scheduleDraw();
2284
2472
  setEditing(null);
2285
- }, [bodyTop, columns, renderHeight, fixedHeader, fixedWidth, getCellDisplayWidth, getCellSpan, getRowKey, getScrollableColumnLeft, metrics, rightFixedOffsets, rightFixedWidth, rowHeight, rows, scheduleDraw, selection, selectionRange, showCustomScrollbar, updateCustomScrollbars, viewport.width]);
2473
+ }, [bodyTop, columns, renderHeight, fixedHeader, fixedWidth, getCellDisplayWidth, getCellSpan, getRowKey, getScrollableColumnLeft, metrics, rightFixedOffsets, rightFixedWidth, rowHeight, rows, scheduleDraw, selection, selectionRange, showCustomScrollbar, syncSelectionEdgeTarget, updateCustomScrollbars, viewport.width]);
2286
2474
  const locateColumn = (0, import_react5.useCallback)((clientX) => {
2287
2475
  const canvas = canvasRef.current;
2288
2476
  if (!canvas) return -1;
@@ -2370,6 +2558,30 @@ function Table({
2370
2558
  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
2559
  }, [columns, getScrollableColumnLeft, leftFixedOffsets, metrics, rightFixedOffsets, viewport.width]);
2372
2560
  const getHeaderCellWidth = (0, import_react5.useCallback)((cell) => cell.leaf ? metrics[cell.startIndex]?.width ?? 0 : getCellDisplayWidth(cell.startIndex, cell.endIndex - cell.startIndex + 1), [getCellDisplayWidth, metrics]);
2561
+ const getHeaderCellEdgeFromMetrics = (0, import_react5.useCallback)((startIndex, endIndex, edge, nextMetrics) => {
2562
+ const fixedSide = columns[startIndex]?.fixed;
2563
+ const width2 = nextMetrics.slice(startIndex, endIndex + 1).reduce((total, metric) => total + metric.width, 0);
2564
+ let left;
2565
+ if (fixedSide === "left") {
2566
+ left = 0;
2567
+ for (let index = 0; index < startIndex; index += 1) {
2568
+ if (columns[index].fixed === "left") left += nextMetrics[index]?.width ?? 0;
2569
+ }
2570
+ } else if (fixedSide === "right") {
2571
+ let rightOffset = 0;
2572
+ for (let index = columns.length - 1; index > startIndex; index -= 1) {
2573
+ if (columns[index].fixed === "right") rightOffset += nextMetrics[index]?.width ?? 0;
2574
+ }
2575
+ left = viewport.width - rightOffset - width2;
2576
+ } else {
2577
+ left = 0;
2578
+ for (let index = 0; index < startIndex; index += 1) {
2579
+ if (columns[index].fixed === void 0) left += nextMetrics[index]?.width ?? 0;
2580
+ }
2581
+ left += fixedWidth - scrollRef.current.left;
2582
+ }
2583
+ return left + (edge === "right" ? width2 : 0);
2584
+ }, [columns, fixedWidth, viewport.width]);
2373
2585
  const locateHeaderResizeHit = (0, import_react5.useCallback)((clientX, clientY) => {
2374
2586
  if (!columnResizable || !canvasRef.current) return null;
2375
2587
  const rect = canvasRef.current.getBoundingClientRect();
@@ -2380,6 +2592,7 @@ function Table({
2380
2592
  const level = getHeaderLevelAtY(localY, headerY);
2381
2593
  let closest = null;
2382
2594
  let distance = 6;
2595
+ let priority = -1;
2383
2596
  for (const cell of headerCells) {
2384
2597
  if (cell.level !== level) continue;
2385
2598
  if (columns[cell.startIndex]?.rowSelection || columns[cell.startIndex]?.rowDragHandle || columns[cell.startIndex]?.rowNumber) continue;
@@ -2387,8 +2600,10 @@ function Table({
2387
2600
  const edge = fixedSide === "right" && cell.leaf ? "left" : "right";
2388
2601
  const edgeX = getHeaderCellLeft(cell) + (edge === "right" ? getHeaderCellWidth(cell) : 0);
2389
2602
  const nextDistance = Math.abs(localX - edgeX);
2390
- if (nextDistance < distance) {
2603
+ const nextPriority = fixedSide === "right" ? 2 : fixedSide === "left" ? 1 : 0;
2604
+ if (nextDistance < distance || nextDistance === distance && nextPriority > priority) {
2391
2605
  distance = nextDistance;
2606
+ priority = nextPriority;
2392
2607
  closest = { cell, edge };
2393
2608
  }
2394
2609
  }
@@ -2600,6 +2815,10 @@ function Table({
2600
2815
  columnDragPreviewRef.current = { sourceIndex, targetIndex: destinationIndex };
2601
2816
  const targetLeft = Number(target?.targetLeft);
2602
2817
  const targetWidth = Number(target?.targetWidth);
2818
+ if (!Number.isFinite(targetLeft) || !Number.isFinite(targetWidth)) {
2819
+ setDragGuide(null);
2820
+ return;
2821
+ }
2603
2822
  const guideTop = headerRowOffsets[Number(source.data.headerLevel)] ?? 0;
2604
2823
  setDragGuide(edge === "right" ? targetLeft + targetWidth : targetLeft, guideTop);
2605
2824
  },
@@ -2997,18 +3216,28 @@ function Table({
2997
3216
  const editorStyle = (0, import_react5.useMemo)(() => {
2998
3217
  if (!editing) return void 0;
2999
3218
  const metric = metrics[editing.columnIndex];
3000
- const fixedSide = columns[editing.columnIndex].fixed;
3219
+ const column = columns[editing.columnIndex];
3220
+ if (!metric || !column) return void 0;
3221
+ const fixedSide = column.fixed;
3001
3222
  const span = getCellSpan(editing.rowIndex, editing.columnIndex);
3002
3223
  const cellWidth = getCellDisplayWidth(editing.columnIndex, span?.colSpan ?? 1);
3003
3224
  const cellHeight = rowHeight * (span?.rowSpan ?? 1);
3004
3225
  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;
3226
+ const cellTop = bodyTop + editing.rowIndex * rowHeight - scrollRef.current.top;
3227
+ const horizontalStart = fixedSide === void 0 ? fixedWidth : 0;
3228
+ const horizontalEnd = fixedSide === void 0 ? viewport.width - rightFixedWidth : viewport.width;
3229
+ const verticalStart = fixedHeader ? bodyTop : 0;
3230
+ const visibleLeft = Math.max(cellLeft, horizontalStart);
3231
+ const visibleRight = Math.min(cellLeft + cellWidth, horizontalEnd, viewport.width);
3232
+ const visibleTop = Math.max(cellTop, verticalStart);
3233
+ const visibleBottom = Math.min(cellTop + cellHeight, renderHeight);
3005
3234
  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
3235
+ left: visibleLeft,
3236
+ top: visibleTop,
3237
+ width: Math.max(0, visibleRight - visibleLeft),
3238
+ height: Math.max(0, visibleBottom - visibleTop)
3010
3239
  };
3011
- }, [bodyTop, columns, editing, fixedHeader, fixedWidth, getCellDisplayWidth, getCellSpan, getScrollableColumnLeft, leftFixedOffsets, metrics, rightFixedOffsets, rowHeight, viewport.width]);
3240
+ }, [bodyTop, columns, editing, fixedHeader, fixedWidth, getCellDisplayWidth, getCellSpan, getScrollableColumnLeft, leftFixedOffsets, metrics, renderHeight, rightFixedOffsets, rightFixedWidth, rowHeight, viewport.width]);
3012
3241
  const handleColumnPointerDown = (0, import_react5.useCallback)((event) => {
3013
3242
  const rect = event.currentTarget.getBoundingClientRect();
3014
3243
  const localY = event.clientY - rect.top;
@@ -3028,9 +3257,10 @@ function Table({
3028
3257
  columnIndex: resizeIndex,
3029
3258
  startX: event.clientX,
3030
3259
  startWidth: startWidths.reduce((total, width2) => total + width2, 0),
3031
- startGuideX,
3032
3260
  guideTop: headerRowOffsets[resizeCell.level] ?? 0,
3033
3261
  resizeEdge,
3262
+ resizeStartIndex: resizeCell.startIndex,
3263
+ resizeEndIndex: resizeCell.endIndex,
3034
3264
  resizeIndices,
3035
3265
  startWidths
3036
3266
  };
@@ -3047,6 +3277,12 @@ function Table({
3047
3277
  const minWidth = getMinimumColumnWidth(column, columnDraggable);
3048
3278
  const pointerDelta = Math.round(event.clientX - drag.startX);
3049
3279
  const delta = drag.resizeEdge === "left" ? -pointerDelta : pointerDelta;
3280
+ const setGuideFromWidths = (nextWidths) => {
3281
+ const widthByIndex = new Map(drag.resizeIndices.map((columnIndex, index) => [columnIndex, nextWidths[index]]));
3282
+ const nextColumns = columns.map((column2, index) => widthByIndex.has(index) ? { ...column2, width: widthByIndex.get(index) } : column2);
3283
+ const nextMetrics = buildColumnMetrics(nextColumns, { columnDraggable, viewportWidth: viewport.width });
3284
+ setResizeGuideX(getHeaderCellEdgeFromMetrics(drag.resizeStartIndex, drag.resizeEndIndex, drag.resizeEdge, nextMetrics));
3285
+ };
3050
3286
  if (drag.resizeIndices.length > 1) {
3051
3287
  const minWidths = drag.resizeIndices.map((index) => getMinimumColumnWidth(columns[index], columnDraggable));
3052
3288
  const minTotalWidth = minWidths.reduce((total, width3) => total + width3, 0);
@@ -3063,13 +3299,13 @@ function Table({
3063
3299
  drag.resizeIndices.forEach((columnIndex, index) => {
3064
3300
  resizeColumn(columns[columnIndex].key, nextWidths[index]);
3065
3301
  });
3066
- setResizeGuideX(drag.startGuideX + (drag.resizeEdge === "left" ? drag.startWidth - targetTotalWidth : targetTotalWidth - drag.startWidth));
3302
+ setGuideFromWidths(nextWidths);
3067
3303
  return;
3068
3304
  }
3069
3305
  const width2 = Math.max(minWidth, Math.round(drag.startWidth + delta));
3070
3306
  resizeColumn(column.key, width2);
3071
- setResizeGuideX(drag.startGuideX + (drag.resizeEdge === "left" ? drag.startWidth - width2 : width2 - drag.startWidth));
3072
- }, [columnDraggable, columns, resizeColumn]);
3307
+ setGuideFromWidths([width2]);
3308
+ }, [columnDraggable, columns, getHeaderCellEdgeFromMetrics, resizeColumn, viewport.width]);
3073
3309
  const handleColumnPointerUp = (0, import_react5.useCallback)((event) => {
3074
3310
  const drag = columnDragRef.current;
3075
3311
  if (!drag) return;
@@ -3650,9 +3886,9 @@ function Table({
3650
3886
  const verticalStart = fixedHeader ? bodyTop : 0;
3651
3887
  if (rawRight <= horizontalStart || rawLeft >= horizontalEnd || rawBottom <= verticalStart || rawTop >= renderHeight) return null;
3652
3888
  const borderWidth = 2;
3653
- const left = Math.round(Math.max(rawLeft - borderWidth, horizontalStart - 1));
3889
+ const left = Math.round(Math.max(rawLeft - borderWidth, horizontalStart));
3654
3890
  const right = Math.round(Math.min(rawRight + 1, horizontalEnd, viewport.width));
3655
- const top = Math.round(Math.max(rawTop - borderWidth, verticalStart - 1));
3891
+ const top = Math.round(Math.max(rawTop - borderWidth, verticalStart));
3656
3892
  const bottom = Math.round(Math.min(rawBottom + 1, renderHeight));
3657
3893
  return {
3658
3894
  left,
@@ -3661,6 +3897,19 @@ function Table({
3661
3897
  height: Math.max(0, bottom - top)
3662
3898
  };
3663
3899
  })();
3900
+ const selectionEdgeIndicator = getSelectionEdgeIndicator(scrollPosition.left, scrollPosition.top);
3901
+ const selectionEdgeStyle = selectionEdgeIndicator ? {
3902
+ ...selectionEdgeIndicator.style,
3903
+ ...selectionEdgeIndicator.corner ? {
3904
+ "--rvg-corner-width": `${selectionEdgeIndicator.corner.width}px`,
3905
+ "--rvg-corner-height": `${selectionEdgeIndicator.corner.height}px`,
3906
+ "--rvg-corner-hover-width-extension": "4px",
3907
+ "--rvg-corner-hover-height-extension": "4px"
3908
+ } : null
3909
+ } : void 0;
3910
+ (0, import_react5.useLayoutEffect)(() => {
3911
+ syncSelectionEdgeTarget(scrollPosition.left, scrollPosition.top);
3912
+ }, [scrollPosition.left, scrollPosition.top, syncSelectionEdgeTarget]);
3664
3913
  const handleContextMenu = (event) => {
3665
3914
  event.preventDefault();
3666
3915
  event.stopPropagation();
@@ -4108,6 +4357,27 @@ function Table({
4108
4357
  }
4109
4358
  ),
4110
4359
  selectionOutlineStyle && /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("div", { ref: selectionFocusRef, className: "rvg-selection-focus", style: selectionOutlineStyle, "aria-hidden": "true" }),
4360
+ selectionEdgeIndicator && selection && /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
4361
+ "button",
4362
+ {
4363
+ ref: selectionEdgeTargetRef,
4364
+ type: "button",
4365
+ className: `rvg-selection-edge-target is-${selectionEdgeIndicator.orientation} is-${selectionEdgeIndicator.side}${selectionEdgeIndicator.viewportEdge ? ` is-viewport-${selectionEdgeIndicator.viewportEdge}` : ""}`,
4366
+ style: selectionEdgeStyle,
4367
+ "aria-label": labels.revealSelectedCell,
4368
+ title: labels.revealSelectedCell,
4369
+ onPointerDown: (event) => {
4370
+ event.preventDefault();
4371
+ event.stopPropagation();
4372
+ },
4373
+ onClick: (event) => {
4374
+ event.stopPropagation();
4375
+ scrollerRef.current?.focus({ preventScroll: true });
4376
+ revealCell(selection);
4377
+ },
4378
+ 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" }) }) })
4379
+ }
4380
+ ),
4111
4381
  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
4382
  const column = columns[contextMenu.columnIndex];
4113
4383
  const headerContext = { column, columnIndex: contextMenu.columnIndex };
@@ -4432,8 +4702,8 @@ function Table({
4432
4702
  column.key
4433
4703
  );
4434
4704
  }) }, 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 } })
4705
+ fixedWidth > 0 && currentScroll.left > 0 && /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("div", { className: "rvg-loading-fixed-shadow is-left", style: { left: fixedWidth } }),
4706
+ 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
4707
  ] }),
4438
4708
  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
4709
  ] });