@lensui/lens-table 0.1.87 → 0.1.89

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
@@ -1176,6 +1176,41 @@ function getSelectionOutlineBounds({
1176
1176
  bottom: Math.min(bottom + borderWidth, verticalEnd + borderWidth)
1177
1177
  };
1178
1178
  }
1179
+ function getAnimatedRowDragOffset(preview, rowIndex, rowHeight) {
1180
+ if (!preview || rowIndex === preview.sourceIndex) return 0;
1181
+ const targetOffset = preview.sourceIndex < preview.targetIndex ? rowIndex > preview.sourceIndex && rowIndex <= preview.targetIndex ? -rowHeight : 0 : rowIndex >= preview.targetIndex && rowIndex < preview.sourceIndex ? rowHeight : 0;
1182
+ const startOffset = preview.fromOffsets?.[rowIndex] ?? 0;
1183
+ const progress = Math.max(0, Math.min(1, preview.progress ?? 1));
1184
+ return startOffset + (targetOffset - startOffset) * progress;
1185
+ }
1186
+ function getAnimatedRowDragGapRanges(preview, rowCount, rowHeight, bodyTop, scrollTop) {
1187
+ if (!preview || rowCount <= 0 || rowHeight <= 0) return [];
1188
+ const affectedIndices = [preview.sourceIndex, preview.targetIndex];
1189
+ preview.fromOffsets?.forEach((offset, rowIndex) => {
1190
+ if (Math.abs(offset) > 0.01) affectedIndices.push(rowIndex);
1191
+ });
1192
+ const startIndex = Math.max(0, Math.min(...affectedIndices));
1193
+ const endIndex = Math.min(rowCount - 1, Math.max(...affectedIndices));
1194
+ const regionTop = bodyTop + startIndex * rowHeight - scrollTop;
1195
+ const regionBottom = bodyTop + (endIndex + 1) * rowHeight - scrollTop;
1196
+ const occupied = [];
1197
+ for (let rowIndex = startIndex; rowIndex <= endIndex; rowIndex += 1) {
1198
+ if (rowIndex === preview.sourceIndex) continue;
1199
+ const top = bodyTop + rowIndex * rowHeight - scrollTop + getAnimatedRowDragOffset(preview, rowIndex, rowHeight);
1200
+ occupied.push({ top, bottom: top + rowHeight });
1201
+ }
1202
+ occupied.sort((left, right) => left.top - right.top);
1203
+ const gaps = [];
1204
+ let cursor = regionTop;
1205
+ for (const interval of occupied) {
1206
+ const top = Math.max(regionTop, interval.top);
1207
+ const bottom = Math.min(regionBottom, interval.bottom);
1208
+ if (top > cursor + 0.01) gaps.push({ top: cursor, bottom: top });
1209
+ cursor = Math.max(cursor, bottom);
1210
+ }
1211
+ if (cursor < regionBottom - 0.01) gaps.push({ top: cursor, bottom: regionBottom });
1212
+ return gaps;
1213
+ }
1179
1214
  function paintGrid(options) {
1180
1215
  const { context: ctx, width, height, pixelRatio, scrollLeft, scrollTop, rowHeight, bodyFontSize, headerActionSlotWidth, headerHeight, fixedHeader, verticalBorderless, horizontalBorderless, frameBorderless, extendVerticalGridLines, 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;
1181
1216
  const headerLeafTop = options.headerLeafTop ?? 0;
@@ -1228,13 +1263,7 @@ function paintGrid(options) {
1228
1263
  };
1229
1264
  const displayedContentRight = columns.reduce((right, _column, index) => Math.max(right, getColumnX(index) + metrics[index].width), 0);
1230
1265
  const trailingBlankStart = displayedContentRight > 0 && displayedContentRight < width ? displayedContentRight : null;
1231
- const getRowDragOffset = (rowIndex) => {
1232
- if (!rowDragPreview || rowIndex === rowDragPreview.sourceIndex) return 0;
1233
- if (rowDragPreview.sourceIndex < rowDragPreview.targetIndex) {
1234
- return rowIndex > rowDragPreview.sourceIndex && rowIndex <= rowDragPreview.targetIndex ? -rowHeight : 0;
1235
- }
1236
- return rowIndex >= rowDragPreview.targetIndex && rowIndex < rowDragPreview.sourceIndex ? rowHeight : 0;
1237
- };
1266
+ const getRowDragOffset = (rowIndex) => getAnimatedRowDragOffset(rowDragPreview, rowIndex, rowHeight);
1238
1267
  const rowBelowDragGap = rowDragPreview ? rowDragPreview.sourceIndex <= rowDragPreview.targetIndex ? rowDragPreview.targetIndex + 1 : rowDragPreview.targetIndex : -1;
1239
1268
  if (trailingBlankStart !== null) {
1240
1269
  const rowStart = Math.max(0, range.rowStart);
@@ -1343,7 +1372,7 @@ function paintGrid(options) {
1343
1372
  if (!verticalBorderless && !(frameBorderless && columnIndex + colSpan >= columns.length)) {
1344
1373
  ctx.fillRect(x + cellWidth - 1, y, 1, cellHeight);
1345
1374
  }
1346
- if (rowIndex === rowBelowDragGap) ctx.fillRect(x, y, cellWidth, 1);
1375
+ if (!horizontalBorderless && rowIndex === rowBelowDragGap) ctx.fillRect(x, y, cellWidth, 1);
1347
1376
  if (!horizontalBorderless && (!options.suppressLastRowBottomBorder || rowIndex !== rows.length - 1)) {
1348
1377
  ctx.fillRect(x, y + cellHeight - 1, cellWidth, 1);
1349
1378
  }
@@ -1477,6 +1506,26 @@ function paintGrid(options) {
1477
1506
  paintBodyColumns("scroll");
1478
1507
  paintBodyColumns("left");
1479
1508
  paintBodyColumns("right");
1509
+ if (rowDragPreview && !verticalBorderless) {
1510
+ const gapRanges = getAnimatedRowDragGapRanges(
1511
+ rowDragPreview,
1512
+ rows.length,
1513
+ rowHeight,
1514
+ bodyTop,
1515
+ scrollTop
1516
+ );
1517
+ ctx.fillStyle = colors.grid;
1518
+ for (let columnIndex = 0; columnIndex < columns.length; columnIndex += 1) {
1519
+ if (frameBorderless && columnIndex === columns.length - 1) continue;
1520
+ const x = getColumnX(columnIndex) + metrics[columnIndex].width - 1;
1521
+ if (columns[columnIndex].fixed === void 0 && (x < leftFixedWidth || x >= rightFixedLeft)) continue;
1522
+ for (const gap of gapRanges) {
1523
+ const top = Math.max(bodyTop, gap.top);
1524
+ const bottom = Math.min(height, gap.bottom);
1525
+ if (bottom > top) ctx.fillRect(x, top, 1, bottom - top);
1526
+ }
1527
+ }
1528
+ }
1480
1529
  if (extendVerticalGridLines) {
1481
1530
  const extensionTop = Math.max(bodyTop, Math.min(height, bodyTop + rows.length * rowHeight - scrollTop));
1482
1531
  const extensionHeight = Math.max(0, height - extensionTop);
@@ -2181,6 +2230,24 @@ function TableInner({
2181
2230
  const [liftedRowPreview, setLiftedRowPreview] = (0, import_react8.useState)(null);
2182
2231
  const [columnDragPreview, setColumnDragPreview] = (0, import_react8.useState)(null);
2183
2232
  const [columnDropTarget, setColumnDropTarget] = (0, import_react8.useState)(null);
2233
+ (0, import_react8.useEffect)(() => {
2234
+ if (!rowDragPreview || (rowDragPreview.progress ?? 1) >= 1) return;
2235
+ const sourceIndex = rowDragPreview.sourceIndex;
2236
+ const targetIndex = rowDragPreview.targetIndex;
2237
+ if (window.matchMedia?.("(prefers-reduced-motion: reduce)").matches) {
2238
+ setRowDragPreview((current) => current?.sourceIndex === sourceIndex && current.targetIndex === targetIndex ? { ...current, progress: 1 } : current);
2239
+ return;
2240
+ }
2241
+ const startedAt = performance.now();
2242
+ let frame = 0;
2243
+ const animate = (now) => {
2244
+ const progress = Math.min(1, (now - startedAt) / 140);
2245
+ setRowDragPreview((current) => current?.sourceIndex === sourceIndex && current.targetIndex === targetIndex ? { ...current, progress } : current);
2246
+ if (progress < 1) frame = requestAnimationFrame(animate);
2247
+ };
2248
+ frame = requestAnimationFrame(animate);
2249
+ return () => cancelAnimationFrame(frame);
2250
+ }, [rowDragPreview?.sourceIndex, rowDragPreview?.targetIndex]);
2184
2251
  (0, import_react8.useEffect)(() => {
2185
2252
  if (!loading) hasCompletedLoadRef.current = true;
2186
2253
  }, [loading]);
@@ -3427,9 +3494,10 @@ function TableInner({
3427
3494
  const setRowDragGuide = (0, import_react8.useCallback)((y) => {
3428
3495
  const guide = rowDragGuideRef.current;
3429
3496
  if (!guide) return;
3430
- guide.style.display = y === null ? "none" : "block";
3431
- if (y !== null) guide.style.transform = `translateY(${Math.round(y)}px)`;
3432
- }, []);
3497
+ const visibleY = horizontalBorderless ? null : y;
3498
+ guide.style.display = visibleY === null ? "none" : "block";
3499
+ if (visibleY !== null) guide.style.transform = `translateY(${Math.round(visibleY)}px)`;
3500
+ }, [horizontalBorderless]);
3433
3501
  const locateRow = (0, import_react8.useCallback)((clientY) => {
3434
3502
  const canvas = canvasRef.current;
3435
3503
  if (!canvas) return -1;
@@ -3476,7 +3544,15 @@ function TableInner({
3476
3544
  if (drag.sourceIndex < targetIndex) targetIndex -= 1;
3477
3545
  targetIndex = Math.max(0, Math.min(rows.length - 1, targetIndex));
3478
3546
  drag.targetIndex = targetIndex;
3479
- setRowDragPreview(null);
3547
+ setRowDragPreview((current) => current?.sourceIndex === drag.sourceIndex && current.targetIndex === targetIndex ? current : {
3548
+ sourceIndex: drag.sourceIndex,
3549
+ targetIndex,
3550
+ fromOffsets: Array.from(
3551
+ { length: rows.length },
3552
+ (_, rowIndex) => getAnimatedRowDragOffset(current, rowIndex, rowHeight)
3553
+ ),
3554
+ progress: 0
3555
+ });
3480
3556
  setRowDragGuide(rowTop + (edge === "bottom" ? rowHeight : 0));
3481
3557
  }, [bodyTop, hideDragTooltips, locateRow, renderHeight, rowHeight, rows.length, setRowDragGuide]);
3482
3558
  const finishRowOrderPointerDrag = (0, import_react8.useCallback)((event, apply) => {
@@ -4415,13 +4491,7 @@ function TableInner({
4415
4491
  top: Math.min(Math.max(0, clientY - root.top), Math.max(0, renderHeight - 68))
4416
4492
  });
4417
4493
  };
4418
- const getRowDragOffset = (rowIndex) => {
4419
- if (!rowDragPreview || rowIndex === rowDragPreview.sourceIndex) return 0;
4420
- if (rowDragPreview.sourceIndex < rowDragPreview.targetIndex) {
4421
- return rowIndex > rowDragPreview.sourceIndex && rowIndex <= rowDragPreview.targetIndex ? -rowHeight : 0;
4422
- }
4423
- return rowIndex >= rowDragPreview.targetIndex && rowIndex < rowDragPreview.sourceIndex ? rowHeight : 0;
4424
- };
4494
+ const getRowDragOffset = (rowIndex) => getAnimatedRowDragOffset(rowDragPreview, rowIndex, rowHeight);
4425
4495
  const renderAnnotationControls = (targetCells) => {
4426
4496
  const getKey = (cell) => `${typeof cell.rowKey}:${String(cell.rowKey)}\0${cell.columnKey}`;
4427
4497
  const hasAnnotation = targetCells.some((cell) => cellAnnotations.has(getKey(cell)));
@@ -4731,7 +4801,7 @@ function TableInner({
4731
4801
  })();
4732
4802
  const selectionOutlineStyle = (() => {
4733
4803
  if (!selection || selectionRange) return null;
4734
- if (rowDragPreview?.sourceIndex === selection.rowIndex) return null;
4804
+ if (liftedRowPreview?.sourceIndex === selection.rowIndex) return null;
4735
4805
  const row = rows[selection.rowIndex];
4736
4806
  const metric = metrics[selection.columnIndex];
4737
4807
  const column = columns[selection.columnIndex];
@@ -5011,7 +5081,12 @@ function TableInner({
5011
5081
  const draggingSelectedColumn = Boolean(
5012
5082
  columnDragPreview && selectedColumnKeySet.has(columns[columnDragPreview.sourceIndex]?.key)
5013
5083
  );
5014
- return /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("div", { ref: rootRef, className: `rvg-root${!hasVerticalBorders ? " is-vertical-borderless" : ""}${horizontalBorderless ? " is-horizontal-borderless" : ""}${frameBorderless ? " is-frame-borderless" : ""}${hasStripedRows ? " is-striped" : ""}${insertBusy ? " is-inserting" : ""}${deleteBusy ? " is-deleting" : ""}${draggingSelectedColumn ? " is-dragging-selected-column" : ""} ${loading && !hasCompletedLoadRef.current && !hasCustomLoading ? "is-initial-loading" : ""} ${className}`, style: rootStyle, children: [
5084
+ const liftedRow = liftedRowPreview ? rows[liftedRowPreview.sourceIndex] : void 0;
5085
+ const liftedRowKey = liftedRow && liftedRowPreview ? getRowKey(liftedRow, liftedRowPreview.sourceIndex) : void 0;
5086
+ const liftedRowAxisSelected = liftedRowKey !== void 0 && selectedRowKeySet.has(liftedRowKey);
5087
+ const liftedRowCellFocused = liftedRowKey !== void 0 && selection?.rowKey === liftedRowKey;
5088
+ const liftedRowStyle = liftedRow && liftedRowPreview && !liftedRowAxisSelected ? rowStyle?.(liftedRow, liftedRowPreview.sourceIndex) : void 0;
5089
+ return /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("div", { ref: rootRef, className: `rvg-root${!hasVerticalBorders ? " is-vertical-borderless" : ""}${horizontalBorderless ? " is-horizontal-borderless" : ""}${frameBorderless ? " is-frame-borderless" : ""}${hasStripedRows ? " is-striped" : ""}${insertBusy ? " is-inserting" : ""}${deleteBusy ? " is-deleting" : ""}${draggingSelectedColumn ? " is-dragging-selected-column" : ""}${rowDragPreview ? " is-row-dragging" : ""} ${loading && !hasCompletedLoadRef.current && !hasCustomLoading ? "is-initial-loading" : ""} ${className}`, style: rootStyle, children: [
5015
5090
  /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("div", { ref: scrollerRef, className: `rvg-scroller${contextMenu ? " is-context-menu-open" : ""}`, style: { bottom: scrollerBottom }, onScroll: handleScroll, onCopy: handleCopy, onContextMenu: handleContextMenu, tabIndex: 0, role: "grid", "aria-label": ariaLabel, "aria-rowcount": rows.length, "aria-colcount": columns.length, onKeyDown: handleKeyDown, children: [
5016
5091
  /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
5017
5092
  "canvas",
@@ -5724,24 +5799,31 @@ function TableInner({
5724
5799
  ]
5725
5800
  }
5726
5801
  ),
5727
- liftedRowPreview && rows[liftedRowPreview.sourceIndex] && /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
5802
+ liftedRowPreview && liftedRow && /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
5728
5803
  "div",
5729
5804
  {
5730
5805
  "aria-hidden": "true",
5731
- className: `rvg-row-drag-preview${selectedRowKeySet.has(getRowKey(rows[liftedRowPreview.sourceIndex], liftedRowPreview.sourceIndex)) ? " is-selected" : ""}`,
5732
- style: { top: liftedRowPreview.top, width: viewport.width, height: rowHeight },
5733
- children: columns.map((column, columnIndex) => /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
5734
- "div",
5735
- {
5736
- className: "rvg-row-drag-preview-cell",
5737
- style: {
5738
- left: getDisplayedColumnLeft(columnIndex),
5739
- width: metrics[columnIndex]?.width ?? 0
5806
+ className: `rvg-row-drag-preview${liftedRowAxisSelected ? " is-selected is-axis-selected" : ""}${hasStripedRows && liftedRowPreview.sourceIndex % 2 === 1 ? " is-striped" : ""}`,
5807
+ style: { ...liftedRowStyle, top: liftedRowPreview.top, width: viewport.width, height: rowHeight },
5808
+ children: columns.map((column, columnIndex) => {
5809
+ const value = column.dataIndex === void 0 ? void 0 : liftedRow[column.dataIndex];
5810
+ const cellStyle = liftedRowAxisSelected ? void 0 : column.cellStyle?.(value, liftedRow, liftedRowPreview.sourceIndex);
5811
+ return /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
5812
+ "div",
5813
+ {
5814
+ className: `rvg-row-drag-preview-cell${column.renderCell ? " is-custom" : ""}${liftedRowAxisSelected ? " is-axis-selected" : ""}${liftedRowCellFocused && selection?.columnKey === column.key ? " is-selected" : ""}`,
5815
+ style: {
5816
+ ...cellStyle,
5817
+ left: getDisplayedColumnLeft(columnIndex),
5818
+ width: metrics[columnIndex]?.width ?? 0,
5819
+ justifyContent: column.align === "right" ? "flex-end" : column.align === "center" ? "center" : "flex-start",
5820
+ color: cellStyle?.color ?? liftedRowStyle?.color
5821
+ },
5822
+ children: column.renderCell ? column.renderCell(value, liftedRow, liftedRowPreview.sourceIndex) : getCellLabel(liftedRowPreview.sourceIndex, columnIndex)
5740
5823
  },
5741
- children: getCellLabel(liftedRowPreview.sourceIndex, columnIndex)
5742
- },
5743
- `row-preview:${column.key}`
5744
- ))
5824
+ `row-preview:${column.key}`
5825
+ );
5826
+ })
5745
5827
  }
5746
5828
  ),
5747
5829
  resizeGuideX !== null && /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
package/dist/index.css CHANGED
@@ -230,7 +230,12 @@
230
230
  font-size: var(--rvg-font-size-body);
231
231
  transition: transform 140ms ease, opacity 100ms ease;
232
232
  }
233
- .rvg-row-drop-settling .rvg-cell-text {
233
+ .rvg-root.is-row-dragging .rvg-cell-text,
234
+ .rvg-root.is-row-dragging .rvg-cell-render,
235
+ .rvg-root.is-row-dragging .rvg-selection-focus,
236
+ .rvg-row-drop-settling .rvg-cell-text,
237
+ .rvg-row-drop-settling .rvg-cell-render,
238
+ .rvg-row-drop-settling .rvg-selection-focus {
234
239
  transition: none;
235
240
  }
236
241
  .rvg-cell-text > span,
@@ -482,7 +487,6 @@
482
487
  border: 2px solid var(--rvg-color-primary);
483
488
  box-sizing: border-box;
484
489
  pointer-events: none;
485
- transition: transform 140ms ease;
486
490
  }
487
491
  .rvg-selection-edge-target {
488
492
  position: absolute;
@@ -1638,17 +1642,28 @@
1638
1642
  }
1639
1643
  .rvg-row-drag-preview {
1640
1644
  position: absolute;
1641
- z-index: 6;
1645
+ z-index: 8;
1642
1646
  left: 0;
1643
- overflow: hidden;
1647
+ overflow: visible;
1644
1648
  border: 1px solid #8db8f3;
1645
1649
  background: var(--rvg-color-bg);
1646
1650
  box-shadow: 0 5px 14px rgba(32, 75, 135, 0.2);
1647
1651
  box-sizing: border-box;
1648
- opacity: 0.94;
1652
+ opacity: 1;
1649
1653
  pointer-events: none;
1650
1654
  transform: translateY(-1px);
1651
1655
  }
1656
+ .rvg-root.is-horizontal-borderless .rvg-row-drag-preview {
1657
+ border-top: 0;
1658
+ border-bottom: 0;
1659
+ }
1660
+ .rvg-row-drag-preview.is-striped {
1661
+ background: var(--rvg-color-stripe);
1662
+ }
1663
+ .rvg-row-drag-preview *,
1664
+ .rvg-row-drag-preview-cell > * {
1665
+ pointer-events: none !important;
1666
+ }
1652
1667
  .rvg-row-drag-preview-cell {
1653
1668
  position: absolute;
1654
1669
  top: 0;
@@ -1670,7 +1685,7 @@
1670
1685
  }
1671
1686
  .rvg-row-drag-preview.is-selected {
1672
1687
  border-color: var(--rvg-color-primary);
1673
- background: var(--rvg-color-selection-fill);
1688
+ background: var(--rvg-color-axis-selection-fill);
1674
1689
  }
1675
1690
  .rvg-row-drag-preview.is-selected .rvg-row-drag-preview-cell {
1676
1691
  border-right-color: color-mix(in srgb, var(--rvg-color-axis-selection-text) 34%, transparent);
@@ -1686,6 +1701,12 @@
1686
1701
  background: var(--rvg-color-selection-fill);
1687
1702
  box-shadow: 0 0 0 2px var(--rvg-color-primary);
1688
1703
  }
1704
+ @media (prefers-reduced-motion: reduce) {
1705
+ .rvg-cell-text,
1706
+ .rvg-cell-render {
1707
+ transition: none;
1708
+ }
1709
+ }
1689
1710
  .rvg-preview-checkbox {
1690
1711
  display: grid;
1691
1712
  width: 14px;
package/dist/index.js CHANGED
@@ -1146,6 +1146,41 @@ function getSelectionOutlineBounds({
1146
1146
  bottom: Math.min(bottom + borderWidth, verticalEnd + borderWidth)
1147
1147
  };
1148
1148
  }
1149
+ function getAnimatedRowDragOffset(preview, rowIndex, rowHeight) {
1150
+ if (!preview || rowIndex === preview.sourceIndex) return 0;
1151
+ const targetOffset = preview.sourceIndex < preview.targetIndex ? rowIndex > preview.sourceIndex && rowIndex <= preview.targetIndex ? -rowHeight : 0 : rowIndex >= preview.targetIndex && rowIndex < preview.sourceIndex ? rowHeight : 0;
1152
+ const startOffset = preview.fromOffsets?.[rowIndex] ?? 0;
1153
+ const progress = Math.max(0, Math.min(1, preview.progress ?? 1));
1154
+ return startOffset + (targetOffset - startOffset) * progress;
1155
+ }
1156
+ function getAnimatedRowDragGapRanges(preview, rowCount, rowHeight, bodyTop, scrollTop) {
1157
+ if (!preview || rowCount <= 0 || rowHeight <= 0) return [];
1158
+ const affectedIndices = [preview.sourceIndex, preview.targetIndex];
1159
+ preview.fromOffsets?.forEach((offset, rowIndex) => {
1160
+ if (Math.abs(offset) > 0.01) affectedIndices.push(rowIndex);
1161
+ });
1162
+ const startIndex = Math.max(0, Math.min(...affectedIndices));
1163
+ const endIndex = Math.min(rowCount - 1, Math.max(...affectedIndices));
1164
+ const regionTop = bodyTop + startIndex * rowHeight - scrollTop;
1165
+ const regionBottom = bodyTop + (endIndex + 1) * rowHeight - scrollTop;
1166
+ const occupied = [];
1167
+ for (let rowIndex = startIndex; rowIndex <= endIndex; rowIndex += 1) {
1168
+ if (rowIndex === preview.sourceIndex) continue;
1169
+ const top = bodyTop + rowIndex * rowHeight - scrollTop + getAnimatedRowDragOffset(preview, rowIndex, rowHeight);
1170
+ occupied.push({ top, bottom: top + rowHeight });
1171
+ }
1172
+ occupied.sort((left, right) => left.top - right.top);
1173
+ const gaps = [];
1174
+ let cursor = regionTop;
1175
+ for (const interval of occupied) {
1176
+ const top = Math.max(regionTop, interval.top);
1177
+ const bottom = Math.min(regionBottom, interval.bottom);
1178
+ if (top > cursor + 0.01) gaps.push({ top: cursor, bottom: top });
1179
+ cursor = Math.max(cursor, bottom);
1180
+ }
1181
+ if (cursor < regionBottom - 0.01) gaps.push({ top: cursor, bottom: regionBottom });
1182
+ return gaps;
1183
+ }
1149
1184
  function paintGrid(options) {
1150
1185
  const { context: ctx, width, height, pixelRatio, scrollLeft, scrollTop, rowHeight, bodyFontSize, headerActionSlotWidth, headerHeight, fixedHeader, verticalBorderless, horizontalBorderless, frameBorderless, extendVerticalGridLines, 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;
1151
1186
  const headerLeafTop = options.headerLeafTop ?? 0;
@@ -1198,13 +1233,7 @@ function paintGrid(options) {
1198
1233
  };
1199
1234
  const displayedContentRight = columns.reduce((right, _column, index) => Math.max(right, getColumnX(index) + metrics[index].width), 0);
1200
1235
  const trailingBlankStart = displayedContentRight > 0 && displayedContentRight < width ? displayedContentRight : null;
1201
- const getRowDragOffset = (rowIndex) => {
1202
- if (!rowDragPreview || rowIndex === rowDragPreview.sourceIndex) return 0;
1203
- if (rowDragPreview.sourceIndex < rowDragPreview.targetIndex) {
1204
- return rowIndex > rowDragPreview.sourceIndex && rowIndex <= rowDragPreview.targetIndex ? -rowHeight : 0;
1205
- }
1206
- return rowIndex >= rowDragPreview.targetIndex && rowIndex < rowDragPreview.sourceIndex ? rowHeight : 0;
1207
- };
1236
+ const getRowDragOffset = (rowIndex) => getAnimatedRowDragOffset(rowDragPreview, rowIndex, rowHeight);
1208
1237
  const rowBelowDragGap = rowDragPreview ? rowDragPreview.sourceIndex <= rowDragPreview.targetIndex ? rowDragPreview.targetIndex + 1 : rowDragPreview.targetIndex : -1;
1209
1238
  if (trailingBlankStart !== null) {
1210
1239
  const rowStart = Math.max(0, range.rowStart);
@@ -1313,7 +1342,7 @@ function paintGrid(options) {
1313
1342
  if (!verticalBorderless && !(frameBorderless && columnIndex + colSpan >= columns.length)) {
1314
1343
  ctx.fillRect(x + cellWidth - 1, y, 1, cellHeight);
1315
1344
  }
1316
- if (rowIndex === rowBelowDragGap) ctx.fillRect(x, y, cellWidth, 1);
1345
+ if (!horizontalBorderless && rowIndex === rowBelowDragGap) ctx.fillRect(x, y, cellWidth, 1);
1317
1346
  if (!horizontalBorderless && (!options.suppressLastRowBottomBorder || rowIndex !== rows.length - 1)) {
1318
1347
  ctx.fillRect(x, y + cellHeight - 1, cellWidth, 1);
1319
1348
  }
@@ -1447,6 +1476,26 @@ function paintGrid(options) {
1447
1476
  paintBodyColumns("scroll");
1448
1477
  paintBodyColumns("left");
1449
1478
  paintBodyColumns("right");
1479
+ if (rowDragPreview && !verticalBorderless) {
1480
+ const gapRanges = getAnimatedRowDragGapRanges(
1481
+ rowDragPreview,
1482
+ rows.length,
1483
+ rowHeight,
1484
+ bodyTop,
1485
+ scrollTop
1486
+ );
1487
+ ctx.fillStyle = colors.grid;
1488
+ for (let columnIndex = 0; columnIndex < columns.length; columnIndex += 1) {
1489
+ if (frameBorderless && columnIndex === columns.length - 1) continue;
1490
+ const x = getColumnX(columnIndex) + metrics[columnIndex].width - 1;
1491
+ if (columns[columnIndex].fixed === void 0 && (x < leftFixedWidth || x >= rightFixedLeft)) continue;
1492
+ for (const gap of gapRanges) {
1493
+ const top = Math.max(bodyTop, gap.top);
1494
+ const bottom = Math.min(height, gap.bottom);
1495
+ if (bottom > top) ctx.fillRect(x, top, 1, bottom - top);
1496
+ }
1497
+ }
1498
+ }
1450
1499
  if (extendVerticalGridLines) {
1451
1500
  const extensionTop = Math.max(bodyTop, Math.min(height, bodyTop + rows.length * rowHeight - scrollTop));
1452
1501
  const extensionHeight = Math.max(0, height - extensionTop);
@@ -2151,6 +2200,24 @@ function TableInner({
2151
2200
  const [liftedRowPreview, setLiftedRowPreview] = useState6(null);
2152
2201
  const [columnDragPreview, setColumnDragPreview] = useState6(null);
2153
2202
  const [columnDropTarget, setColumnDropTarget] = useState6(null);
2203
+ useEffect3(() => {
2204
+ if (!rowDragPreview || (rowDragPreview.progress ?? 1) >= 1) return;
2205
+ const sourceIndex = rowDragPreview.sourceIndex;
2206
+ const targetIndex = rowDragPreview.targetIndex;
2207
+ if (window.matchMedia?.("(prefers-reduced-motion: reduce)").matches) {
2208
+ setRowDragPreview((current) => current?.sourceIndex === sourceIndex && current.targetIndex === targetIndex ? { ...current, progress: 1 } : current);
2209
+ return;
2210
+ }
2211
+ const startedAt = performance.now();
2212
+ let frame = 0;
2213
+ const animate = (now) => {
2214
+ const progress = Math.min(1, (now - startedAt) / 140);
2215
+ setRowDragPreview((current) => current?.sourceIndex === sourceIndex && current.targetIndex === targetIndex ? { ...current, progress } : current);
2216
+ if (progress < 1) frame = requestAnimationFrame(animate);
2217
+ };
2218
+ frame = requestAnimationFrame(animate);
2219
+ return () => cancelAnimationFrame(frame);
2220
+ }, [rowDragPreview?.sourceIndex, rowDragPreview?.targetIndex]);
2154
2221
  useEffect3(() => {
2155
2222
  if (!loading) hasCompletedLoadRef.current = true;
2156
2223
  }, [loading]);
@@ -3397,9 +3464,10 @@ function TableInner({
3397
3464
  const setRowDragGuide = useCallback3((y) => {
3398
3465
  const guide = rowDragGuideRef.current;
3399
3466
  if (!guide) return;
3400
- guide.style.display = y === null ? "none" : "block";
3401
- if (y !== null) guide.style.transform = `translateY(${Math.round(y)}px)`;
3402
- }, []);
3467
+ const visibleY = horizontalBorderless ? null : y;
3468
+ guide.style.display = visibleY === null ? "none" : "block";
3469
+ if (visibleY !== null) guide.style.transform = `translateY(${Math.round(visibleY)}px)`;
3470
+ }, [horizontalBorderless]);
3403
3471
  const locateRow = useCallback3((clientY) => {
3404
3472
  const canvas = canvasRef.current;
3405
3473
  if (!canvas) return -1;
@@ -3446,7 +3514,15 @@ function TableInner({
3446
3514
  if (drag.sourceIndex < targetIndex) targetIndex -= 1;
3447
3515
  targetIndex = Math.max(0, Math.min(rows.length - 1, targetIndex));
3448
3516
  drag.targetIndex = targetIndex;
3449
- setRowDragPreview(null);
3517
+ setRowDragPreview((current) => current?.sourceIndex === drag.sourceIndex && current.targetIndex === targetIndex ? current : {
3518
+ sourceIndex: drag.sourceIndex,
3519
+ targetIndex,
3520
+ fromOffsets: Array.from(
3521
+ { length: rows.length },
3522
+ (_, rowIndex) => getAnimatedRowDragOffset(current, rowIndex, rowHeight)
3523
+ ),
3524
+ progress: 0
3525
+ });
3450
3526
  setRowDragGuide(rowTop + (edge === "bottom" ? rowHeight : 0));
3451
3527
  }, [bodyTop, hideDragTooltips, locateRow, renderHeight, rowHeight, rows.length, setRowDragGuide]);
3452
3528
  const finishRowOrderPointerDrag = useCallback3((event, apply) => {
@@ -4385,13 +4461,7 @@ function TableInner({
4385
4461
  top: Math.min(Math.max(0, clientY - root.top), Math.max(0, renderHeight - 68))
4386
4462
  });
4387
4463
  };
4388
- const getRowDragOffset = (rowIndex) => {
4389
- if (!rowDragPreview || rowIndex === rowDragPreview.sourceIndex) return 0;
4390
- if (rowDragPreview.sourceIndex < rowDragPreview.targetIndex) {
4391
- return rowIndex > rowDragPreview.sourceIndex && rowIndex <= rowDragPreview.targetIndex ? -rowHeight : 0;
4392
- }
4393
- return rowIndex >= rowDragPreview.targetIndex && rowIndex < rowDragPreview.sourceIndex ? rowHeight : 0;
4394
- };
4464
+ const getRowDragOffset = (rowIndex) => getAnimatedRowDragOffset(rowDragPreview, rowIndex, rowHeight);
4395
4465
  const renderAnnotationControls = (targetCells) => {
4396
4466
  const getKey = (cell) => `${typeof cell.rowKey}:${String(cell.rowKey)}\0${cell.columnKey}`;
4397
4467
  const hasAnnotation = targetCells.some((cell) => cellAnnotations.has(getKey(cell)));
@@ -4701,7 +4771,7 @@ function TableInner({
4701
4771
  })();
4702
4772
  const selectionOutlineStyle = (() => {
4703
4773
  if (!selection || selectionRange) return null;
4704
- if (rowDragPreview?.sourceIndex === selection.rowIndex) return null;
4774
+ if (liftedRowPreview?.sourceIndex === selection.rowIndex) return null;
4705
4775
  const row = rows[selection.rowIndex];
4706
4776
  const metric = metrics[selection.columnIndex];
4707
4777
  const column = columns[selection.columnIndex];
@@ -4981,7 +5051,12 @@ function TableInner({
4981
5051
  const draggingSelectedColumn = Boolean(
4982
5052
  columnDragPreview && selectedColumnKeySet.has(columns[columnDragPreview.sourceIndex]?.key)
4983
5053
  );
4984
- return /* @__PURE__ */ jsxs8("div", { ref: rootRef, className: `rvg-root${!hasVerticalBorders ? " is-vertical-borderless" : ""}${horizontalBorderless ? " is-horizontal-borderless" : ""}${frameBorderless ? " is-frame-borderless" : ""}${hasStripedRows ? " is-striped" : ""}${insertBusy ? " is-inserting" : ""}${deleteBusy ? " is-deleting" : ""}${draggingSelectedColumn ? " is-dragging-selected-column" : ""} ${loading && !hasCompletedLoadRef.current && !hasCustomLoading ? "is-initial-loading" : ""} ${className}`, style: rootStyle, children: [
5054
+ const liftedRow = liftedRowPreview ? rows[liftedRowPreview.sourceIndex] : void 0;
5055
+ const liftedRowKey = liftedRow && liftedRowPreview ? getRowKey(liftedRow, liftedRowPreview.sourceIndex) : void 0;
5056
+ const liftedRowAxisSelected = liftedRowKey !== void 0 && selectedRowKeySet.has(liftedRowKey);
5057
+ const liftedRowCellFocused = liftedRowKey !== void 0 && selection?.rowKey === liftedRowKey;
5058
+ const liftedRowStyle = liftedRow && liftedRowPreview && !liftedRowAxisSelected ? rowStyle?.(liftedRow, liftedRowPreview.sourceIndex) : void 0;
5059
+ return /* @__PURE__ */ jsxs8("div", { ref: rootRef, className: `rvg-root${!hasVerticalBorders ? " is-vertical-borderless" : ""}${horizontalBorderless ? " is-horizontal-borderless" : ""}${frameBorderless ? " is-frame-borderless" : ""}${hasStripedRows ? " is-striped" : ""}${insertBusy ? " is-inserting" : ""}${deleteBusy ? " is-deleting" : ""}${draggingSelectedColumn ? " is-dragging-selected-column" : ""}${rowDragPreview ? " is-row-dragging" : ""} ${loading && !hasCompletedLoadRef.current && !hasCustomLoading ? "is-initial-loading" : ""} ${className}`, style: rootStyle, children: [
4985
5060
  /* @__PURE__ */ jsxs8("div", { ref: scrollerRef, className: `rvg-scroller${contextMenu ? " is-context-menu-open" : ""}`, style: { bottom: scrollerBottom }, onScroll: handleScroll, onCopy: handleCopy, onContextMenu: handleContextMenu, tabIndex: 0, role: "grid", "aria-label": ariaLabel, "aria-rowcount": rows.length, "aria-colcount": columns.length, onKeyDown: handleKeyDown, children: [
4986
5061
  /* @__PURE__ */ jsx8(
4987
5062
  "canvas",
@@ -5694,24 +5769,31 @@ function TableInner({
5694
5769
  ]
5695
5770
  }
5696
5771
  ),
5697
- liftedRowPreview && rows[liftedRowPreview.sourceIndex] && /* @__PURE__ */ jsx8(
5772
+ liftedRowPreview && liftedRow && /* @__PURE__ */ jsx8(
5698
5773
  "div",
5699
5774
  {
5700
5775
  "aria-hidden": "true",
5701
- className: `rvg-row-drag-preview${selectedRowKeySet.has(getRowKey(rows[liftedRowPreview.sourceIndex], liftedRowPreview.sourceIndex)) ? " is-selected" : ""}`,
5702
- style: { top: liftedRowPreview.top, width: viewport.width, height: rowHeight },
5703
- children: columns.map((column, columnIndex) => /* @__PURE__ */ jsx8(
5704
- "div",
5705
- {
5706
- className: "rvg-row-drag-preview-cell",
5707
- style: {
5708
- left: getDisplayedColumnLeft(columnIndex),
5709
- width: metrics[columnIndex]?.width ?? 0
5776
+ className: `rvg-row-drag-preview${liftedRowAxisSelected ? " is-selected is-axis-selected" : ""}${hasStripedRows && liftedRowPreview.sourceIndex % 2 === 1 ? " is-striped" : ""}`,
5777
+ style: { ...liftedRowStyle, top: liftedRowPreview.top, width: viewport.width, height: rowHeight },
5778
+ children: columns.map((column, columnIndex) => {
5779
+ const value = column.dataIndex === void 0 ? void 0 : liftedRow[column.dataIndex];
5780
+ const cellStyle = liftedRowAxisSelected ? void 0 : column.cellStyle?.(value, liftedRow, liftedRowPreview.sourceIndex);
5781
+ return /* @__PURE__ */ jsx8(
5782
+ "div",
5783
+ {
5784
+ className: `rvg-row-drag-preview-cell${column.renderCell ? " is-custom" : ""}${liftedRowAxisSelected ? " is-axis-selected" : ""}${liftedRowCellFocused && selection?.columnKey === column.key ? " is-selected" : ""}`,
5785
+ style: {
5786
+ ...cellStyle,
5787
+ left: getDisplayedColumnLeft(columnIndex),
5788
+ width: metrics[columnIndex]?.width ?? 0,
5789
+ justifyContent: column.align === "right" ? "flex-end" : column.align === "center" ? "center" : "flex-start",
5790
+ color: cellStyle?.color ?? liftedRowStyle?.color
5791
+ },
5792
+ children: column.renderCell ? column.renderCell(value, liftedRow, liftedRowPreview.sourceIndex) : getCellLabel(liftedRowPreview.sourceIndex, columnIndex)
5710
5793
  },
5711
- children: getCellLabel(liftedRowPreview.sourceIndex, columnIndex)
5712
- },
5713
- `row-preview:${column.key}`
5714
- ))
5794
+ `row-preview:${column.key}`
5795
+ );
5796
+ })
5715
5797
  }
5716
5798
  ),
5717
5799
  resizeGuideX !== null && /* @__PURE__ */ jsx8(
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lensui/lens-table",
3
- "version": "0.1.87",
3
+ "version": "0.1.89",
4
4
  "description": "A lightweight, canvas-rendered React table component for LensUI.",
5
5
  "type": "module",
6
6
  "main": "./dist/index.cjs",