@lensui/lens-table 0.1.29 → 0.1.30

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
@@ -765,7 +765,7 @@ function getDisplayLabel(column, row, rowIndex) {
765
765
  }
766
766
 
767
767
  // src/core/layout.ts
768
- var DEFAULT_COLUMN_WIDTH = 140;
768
+ var DEFAULT_COLUMN_WIDTH = 100;
769
769
  var HEADER_ACTION_SLOT_WIDTH = 16;
770
770
  function isDataColumn(column) {
771
771
  return !column.rowSelection && !column.rowDragHandle && !column.rowNumber;
@@ -1788,6 +1788,7 @@ function TableInner({
1788
1788
  const columnAnchorRef = (0, import_react5.useRef)(null);
1789
1789
  const columnDragRef = (0, import_react5.useRef)(null);
1790
1790
  const columnOrderDragRef = (0, import_react5.useRef)(null);
1791
+ const columnOrderLongPressTimerRef = (0, import_react5.useRef)(null);
1791
1792
  const dragGuideRef = (0, import_react5.useRef)(null);
1792
1793
  const rowDragGuideRef = (0, import_react5.useRef)(null);
1793
1794
  const selectionFocusRef = (0, import_react5.useRef)(null);
@@ -2337,8 +2338,8 @@ function TableInner({
2337
2338
  borderStrong: readThemeColor(themeStyles, "--rvg-color-border-strong", "#b8bec4"),
2338
2339
  selection: readThemeColor(themeStyles, "--rvg-color-primary", "#1677ff"),
2339
2340
  selectionFill: readThemeColor(themeStyles, "--rvg-color-selection-fill", "#edf4ff"),
2340
- axisSelectionFill: readThemeColor(themeStyles, "--rvg-color-axis-selection-fill", "#e8f2ff"),
2341
- axisSelectionText: readThemeColor(themeStyles, "--rvg-color-axis-selection-text", readThemeColor(themeStyles, "--rvg-color-text", "#202124")),
2341
+ axisSelectionFill: columnDragPreview && selectedColumnKeySet.has(columns[columnDragPreview.sourceIndex]?.key) ? readThemeColor(themeStyles, "--rvg-color-axis-selection-drag-fill", "#d8e8f8") : readThemeColor(themeStyles, "--rvg-color-axis-selection-fill", "#e8f2ff"),
2342
+ axisSelectionText: columnDragPreview && selectedColumnKeySet.has(columns[columnDragPreview.sourceIndex]?.key) ? readThemeColor(themeStyles, "--rvg-color-axis-selection-drag-text", "#325b88") : readThemeColor(themeStyles, "--rvg-color-axis-selection-text", readThemeColor(themeStyles, "--rvg-color-text", "#202124")),
2342
2343
  rowHoverFill: readThemeColor(themeStyles, "--rvg-color-row-hover-fill", "#f6f9fc"),
2343
2344
  editedFill: editedCellHighlightColor ?? readThemeColor(themeStyles, "--rvg-color-edited-fill", "#fff1b8"),
2344
2345
  insertedFill: insertedRowHighlightColor ?? readThemeColor(themeStyles, "--rvg-color-inserted-fill", "#c8ead4"),
@@ -2349,7 +2350,7 @@ function TableInner({
2349
2350
  const rowScrollTop = fixedHeader ? scroll.top : Math.max(0, scroll.top - bodyTop);
2350
2351
  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 };
2351
2352
  paintGrid({ context, width: viewport.width, height: renderHeight, pixelRatio: ratio, scrollLeft: scroll.left, scrollTop: scroll.top, rowHeight, bodyFontSize, headerActionSlotWidth, headerHeight, headerLeafTop, headerLeafHeight, bodyTop, suppressLastRowBottomBorder: bottomSummaryHeight > 0, suppressFrameBottomBorder: bottomSummaryHeight > 0, fixedHeader, verticalBorderless: !hasVerticalBorders, horizontalBorderless, frameBorderless, extendVerticalGridLines: !autoHeight, striped: hasStripedRows, columnDraggable, sortState, filterValues, hoveredHeaderAction: hoveredHeaderActionRef.current, rows, rowStyle, 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 });
2352
- }, [autoHeight, bodyTop, bodyViewportHeight, bottomSummaryHeight, cellAnnotations, cellSpanLookup, columnDraggable, columnDropTarget, columns, editedCellHighlightColor, editedCellKeys, editing, renderHeight, filterValues, fixedHeader, frameBorderless, getRowKey, hasStripedRows, hasVerticalBorders, headerActionSlotWidth, headerDepth, headerHeight, headerLeafHeight, headerLeafTop, horizontalBorderless, highlightsEditedCells, highlightsInsertedRows, insertedRowHighlightColor, insertedRowKeySet, isVirtualized, metrics, rowDragPreview, rowHeight, rowSelectionMode, rowStyle, rows, selectedColumnKeySet, selectedRowKeySet, selection, selectionRange, sortState, stripedColor, viewport.width, virtualOverscan]);
2353
+ }, [autoHeight, bodyTop, bodyViewportHeight, bottomSummaryHeight, cellAnnotations, cellSpanLookup, columnDragPreview, columnDraggable, columnDropTarget, columns, editedCellHighlightColor, editedCellKeys, editing, renderHeight, filterValues, fixedHeader, frameBorderless, getRowKey, hasStripedRows, hasVerticalBorders, headerActionSlotWidth, headerDepth, headerHeight, headerLeafHeight, headerLeafTop, horizontalBorderless, highlightsEditedCells, highlightsInsertedRows, insertedRowHighlightColor, insertedRowKeySet, isVirtualized, metrics, rowDragPreview, rowHeight, rowSelectionMode, rowStyle, rows, selectedColumnKeySet, selectedRowKeySet, selection, selectionRange, sortState, stripedColor, viewport.width, virtualOverscan]);
2353
2354
  const scheduleDraw = (0, import_react5.useCallback)(() => {
2354
2355
  if (frameRef.current === null) frameRef.current = requestAnimationFrame(draw);
2355
2356
  }, [draw]);
@@ -3624,6 +3625,10 @@ function TableInner({
3624
3625
  event.currentTarget.style.cursor = "default";
3625
3626
  }, []);
3626
3627
  const clearColumnOrderPointerDrag = (0, import_react5.useCallback)((canvas) => {
3628
+ if (columnOrderLongPressTimerRef.current) {
3629
+ clearTimeout(columnOrderLongPressTimerRef.current);
3630
+ columnOrderLongPressTimerRef.current = null;
3631
+ }
3627
3632
  columnOrderDragRef.current = null;
3628
3633
  setColumnDragPreview(null);
3629
3634
  validColumnDropRef.current = null;
@@ -3632,6 +3637,43 @@ function TableInner({
3632
3637
  document.documentElement.classList.remove("rvg-is-dragging");
3633
3638
  canvas.style.cursor = "default";
3634
3639
  }, [setDragGuide]);
3640
+ const updateColumnDragPreview = (0, import_react5.useCallback)((sourceCell, clientX, canvas) => {
3641
+ const rootRect = rootRef.current?.getBoundingClientRect() ?? canvas.getBoundingClientRect();
3642
+ const sourceWidth = metrics[sourceCell.startIndex]?.width ?? 100;
3643
+ const sourceColumn = columns[sourceCell.startIndex];
3644
+ const previewContext = canvas.getContext("2d");
3645
+ const previewStyles = rootRef.current ? getComputedStyle(rootRef.current) : null;
3646
+ const previewFontSize = previewStyles ? readThemePixel(previewStyles, "--rvg-font-size-body", 13) : 13;
3647
+ if (previewContext) previewContext.font = `${previewFontSize}px Inter, ui-sans-serif, system-ui, sans-serif`;
3648
+ const measurePreviewText = (value) => previewContext?.measureText(value).width ?? value.length * previewFontSize * 0.58;
3649
+ const previewContentWidth = Math.max(
3650
+ measurePreviewText(sourceColumn?.title ?? ""),
3651
+ ...rows.slice(0, 100).map((_row, rowIndex) => measurePreviewText(getCellLabel(rowIndex, sourceCell.startIndex)))
3652
+ ) + 24;
3653
+ const previewWidth = Math.min(
3654
+ Math.max(sourceWidth, Math.ceil(previewContentWidth)),
3655
+ Math.max(60, Math.min(360, viewport.width - 16))
3656
+ );
3657
+ setColumnDragPreview({
3658
+ sourceIndex: sourceCell.startIndex,
3659
+ left: Math.max(4, Math.min(viewport.width - previewWidth - 4, clientX - rootRect.left - previewWidth / 2)),
3660
+ width: previewWidth
3661
+ });
3662
+ }, [columns, getCellLabel, metrics, rows, viewport.width]);
3663
+ const activateColumnOrderPointerDrag = (0, import_react5.useCallback)((canvas, clientX) => {
3664
+ const drag = columnOrderDragRef.current;
3665
+ if (!drag || drag.active) return;
3666
+ if (columnOrderLongPressTimerRef.current) {
3667
+ clearTimeout(columnOrderLongPressTimerRef.current);
3668
+ columnOrderLongPressTimerRef.current = null;
3669
+ }
3670
+ drag.active = true;
3671
+ suppressClickRef.current = true;
3672
+ hideDragTooltips();
3673
+ document.documentElement.classList.add("rvg-is-dragging");
3674
+ canvas.style.cursor = "move";
3675
+ updateColumnDragPreview(drag.sourceCell, clientX, canvas);
3676
+ }, [hideDragTooltips, updateColumnDragPreview]);
3635
3677
  const handleColumnOrderPointerDown = (0, import_react5.useCallback)((event) => {
3636
3678
  if (!columnDraggable || event.button !== 0 || columnDragRef.current) return;
3637
3679
  const sourceCell = locateHeaderCell(event.clientX, event.clientY);
@@ -3647,41 +3689,25 @@ function TableInner({
3647
3689
  active: false,
3648
3690
  sourceCell
3649
3691
  };
3650
- }, [columnDraggable, columns, locateHeaderAction, locateHeaderCell, locateHeaderResizeCell]);
3692
+ if (columnOrderLongPressTimerRef.current) clearTimeout(columnOrderLongPressTimerRef.current);
3693
+ const canvas = event.currentTarget;
3694
+ const pointerId = event.pointerId;
3695
+ columnOrderLongPressTimerRef.current = setTimeout(() => {
3696
+ const drag = columnOrderDragRef.current;
3697
+ if (!drag || drag.pointerId !== pointerId || drag.active) return;
3698
+ activateColumnOrderPointerDrag(canvas, drag.startX);
3699
+ }, 320);
3700
+ }, [activateColumnOrderPointerDrag, columnDraggable, columns, locateHeaderAction, locateHeaderCell, locateHeaderResizeCell]);
3651
3701
  const handleColumnOrderPointerMove = (0, import_react5.useCallback)((event) => {
3652
3702
  const drag = columnOrderDragRef.current;
3653
3703
  if (!drag || drag.pointerId !== event.pointerId || columnDragRef.current) return;
3654
3704
  if (!drag.active) {
3655
3705
  if (Math.hypot(event.clientX - drag.startX, event.clientY - drag.startY) < 4) return;
3656
- drag.active = true;
3657
- suppressClickRef.current = true;
3658
- hideDragTooltips();
3659
- document.documentElement.classList.add("rvg-is-dragging");
3660
- event.currentTarget.style.cursor = "move";
3706
+ activateColumnOrderPointerDrag(event.currentTarget, event.clientX);
3661
3707
  }
3662
3708
  event.preventDefault();
3663
3709
  const sourceCell = drag.sourceCell;
3664
- const rootRect = rootRef.current?.getBoundingClientRect() ?? event.currentTarget.getBoundingClientRect();
3665
- const sourceWidth = metrics[sourceCell.startIndex]?.width ?? 160;
3666
- const sourceColumn = columns[sourceCell.startIndex];
3667
- const previewContext = event.currentTarget.getContext("2d");
3668
- const previewStyles = rootRef.current ? getComputedStyle(rootRef.current) : null;
3669
- const previewFontSize = previewStyles ? readThemePixel(previewStyles, "--rvg-font-size-body", 13) : 13;
3670
- if (previewContext) previewContext.font = `${previewFontSize}px Inter, ui-sans-serif, system-ui, sans-serif`;
3671
- const measurePreviewText = (value) => previewContext?.measureText(value).width ?? value.length * previewFontSize * 0.58;
3672
- const previewContentWidth = Math.max(
3673
- measurePreviewText(sourceColumn?.title ?? ""),
3674
- ...rows.slice(0, 100).map((_row, rowIndex) => measurePreviewText(getCellLabel(rowIndex, sourceCell.startIndex)))
3675
- ) + 24;
3676
- const previewWidth = Math.min(
3677
- Math.max(sourceWidth, Math.ceil(previewContentWidth)),
3678
- Math.max(60, Math.min(360, viewport.width - 16))
3679
- );
3680
- setColumnDragPreview({
3681
- sourceIndex: sourceCell.startIndex,
3682
- left: Math.max(4, Math.min(viewport.width - previewWidth - 4, event.clientX - rootRect.left - previewWidth / 2)),
3683
- width: previewWidth
3684
- });
3710
+ updateColumnDragPreview(sourceCell, event.clientX, event.currentTarget);
3685
3711
  const hoveredColumnIndex = locateColumn(event.clientX);
3686
3712
  const targetCell = locateHeaderCell(event.clientX, event.clientY) ?? headerCells.find((cell) => hoveredColumnIndex >= cell.startIndex && hoveredColumnIndex <= cell.endIndex && cell.level === sourceCell.level && cell.leaf === sourceCell.leaf && cell.parentKey === sourceCell.parentKey && columns[cell.startIndex]?.fixed === columns[sourceCell.startIndex]?.fixed);
3687
3713
  if (!targetCell) {
@@ -3697,16 +3723,31 @@ function TableInner({
3697
3723
  setDragGuide(null);
3698
3724
  return;
3699
3725
  }
3726
+ if (targetCell.startIndex === sourceCell.startIndex && targetCell.endIndex === sourceCell.endIndex) {
3727
+ validColumnDropRef.current = null;
3728
+ setColumnDropTarget(null);
3729
+ setDragGuide(null);
3730
+ return;
3731
+ }
3700
3732
  const targetLeft = getHeaderCellLeft(targetCell);
3701
3733
  const targetWidth = getHeaderCellWidth(targetCell);
3702
3734
  const localX = event.clientX - event.currentTarget.getBoundingClientRect().left;
3703
- const columnEdge = localX < targetLeft + targetWidth / 2 ? "left" : "right";
3704
- const destinationIndex = resolveColumnDropIndex(
3735
+ let columnEdge = localX < targetLeft + targetWidth / 2 ? "left" : "right";
3736
+ let destinationIndex = resolveColumnDropIndex(
3705
3737
  sourceCell.startIndex,
3706
3738
  targetCell.startIndex,
3707
3739
  columnEdge,
3708
3740
  columns.length
3709
3741
  );
3742
+ if (destinationIndex === sourceCell.startIndex && targetCell.startIndex !== sourceCell.startIndex) {
3743
+ columnEdge = targetCell.startIndex < sourceCell.startIndex ? "left" : "right";
3744
+ destinationIndex = resolveColumnDropIndex(
3745
+ sourceCell.startIndex,
3746
+ targetCell.startIndex,
3747
+ columnEdge,
3748
+ columns.length
3749
+ );
3750
+ }
3710
3751
  const compatibleHighlightCells = headerCells.filter((cell) => {
3711
3752
  const cellColumn = columns[cell.startIndex];
3712
3753
  return cellColumn && !cellColumn.rowSelection && !cellColumn.rowDragHandle && !cellColumn.rowNumber && cellColumn.fixed === targetColumn.fixed;
@@ -3727,7 +3768,7 @@ function TableInner({
3727
3768
  fixed: targetColumn.fixed,
3728
3769
  columnEdge
3729
3770
  };
3730
- }, [columns, getCellLabel, getHeaderCellLeft, getHeaderCellWidth, headerCells, headerRowOffsets, hideDragTooltips, locateColumn, locateHeaderCell, metrics, rows, setDragGuide, viewport.width]);
3771
+ }, [activateColumnOrderPointerDrag, columns, getHeaderCellLeft, getHeaderCellWidth, headerCells, headerRowOffsets, locateColumn, locateHeaderCell, setDragGuide, updateColumnDragPreview]);
3731
3772
  const finishColumnOrderPointerDrag = (0, import_react5.useCallback)((event, apply) => {
3732
3773
  const drag = columnOrderDragRef.current;
3733
3774
  if (!drag || drag.pointerId !== event.pointerId) return;
@@ -4583,7 +4624,10 @@ function TableInner({
4583
4624
  };
4584
4625
  const currentScroll = scrollRef.current;
4585
4626
  const scrollerBottom = Math.max(0, bottomSummaryHeight - horizontalScrollbarHeight);
4586
- return /* @__PURE__ */ (0, import_jsx_runtime7.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" : ""} ${loading && !hasCompletedLoadRef.current && !hasCustomLoading ? "is-initial-loading" : ""} ${className}`, style: rootStyle, children: [
4627
+ const draggingSelectedColumn = Boolean(
4628
+ columnDragPreview && selectedColumnKeySet.has(columns[columnDragPreview.sourceIndex]?.key)
4629
+ );
4630
+ return /* @__PURE__ */ (0, import_jsx_runtime7.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: [
4587
4631
  /* @__PURE__ */ (0, import_jsx_runtime7.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: [
4588
4632
  /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
4589
4633
  "canvas",
package/dist/index.css CHANGED
@@ -17,6 +17,8 @@
17
17
  --rvg-color-selection-fill: #edf4ff;
18
18
  --rvg-color-axis-selection-fill: #e8f2ff;
19
19
  --rvg-color-axis-selection-text: var(--rvg-color-text);
20
+ --rvg-color-axis-selection-drag-fill: #d8e8f8;
21
+ --rvg-color-axis-selection-drag-text: #325b88;
20
22
  --rvg-color-column-drop-target-fill: #e2edf9;
21
23
  --rvg-color-range-fill: #e8f1ff;
22
24
  --rvg-color-row-hover-fill: #f6f9fc;
@@ -777,6 +779,10 @@
777
779
  background: var(--rvg-color-axis-selection-fill);
778
780
  color: var(--rvg-color-axis-selection-text);
779
781
  }
782
+ .rvg-root.is-dragging-selected-column .rvg-header-title.is-axis-selected {
783
+ background: var(--rvg-color-axis-selection-drag-fill);
784
+ color: var(--rvg-color-axis-selection-drag-text);
785
+ }
780
786
  .rvg-text-fixed-shadow {
781
787
  position: absolute;
782
788
  z-index: 4;
package/dist/index.js CHANGED
@@ -735,7 +735,7 @@ function getDisplayLabel(column, row, rowIndex) {
735
735
  }
736
736
 
737
737
  // src/core/layout.ts
738
- var DEFAULT_COLUMN_WIDTH = 140;
738
+ var DEFAULT_COLUMN_WIDTH = 100;
739
739
  var HEADER_ACTION_SLOT_WIDTH = 16;
740
740
  function isDataColumn(column) {
741
741
  return !column.rowSelection && !column.rowDragHandle && !column.rowNumber;
@@ -1758,6 +1758,7 @@ function TableInner({
1758
1758
  const columnAnchorRef = useRef2(null);
1759
1759
  const columnDragRef = useRef2(null);
1760
1760
  const columnOrderDragRef = useRef2(null);
1761
+ const columnOrderLongPressTimerRef = useRef2(null);
1761
1762
  const dragGuideRef = useRef2(null);
1762
1763
  const rowDragGuideRef = useRef2(null);
1763
1764
  const selectionFocusRef = useRef2(null);
@@ -2307,8 +2308,8 @@ function TableInner({
2307
2308
  borderStrong: readThemeColor(themeStyles, "--rvg-color-border-strong", "#b8bec4"),
2308
2309
  selection: readThemeColor(themeStyles, "--rvg-color-primary", "#1677ff"),
2309
2310
  selectionFill: readThemeColor(themeStyles, "--rvg-color-selection-fill", "#edf4ff"),
2310
- axisSelectionFill: readThemeColor(themeStyles, "--rvg-color-axis-selection-fill", "#e8f2ff"),
2311
- axisSelectionText: readThemeColor(themeStyles, "--rvg-color-axis-selection-text", readThemeColor(themeStyles, "--rvg-color-text", "#202124")),
2311
+ axisSelectionFill: columnDragPreview && selectedColumnKeySet.has(columns[columnDragPreview.sourceIndex]?.key) ? readThemeColor(themeStyles, "--rvg-color-axis-selection-drag-fill", "#d8e8f8") : readThemeColor(themeStyles, "--rvg-color-axis-selection-fill", "#e8f2ff"),
2312
+ axisSelectionText: columnDragPreview && selectedColumnKeySet.has(columns[columnDragPreview.sourceIndex]?.key) ? readThemeColor(themeStyles, "--rvg-color-axis-selection-drag-text", "#325b88") : readThemeColor(themeStyles, "--rvg-color-axis-selection-text", readThemeColor(themeStyles, "--rvg-color-text", "#202124")),
2312
2313
  rowHoverFill: readThemeColor(themeStyles, "--rvg-color-row-hover-fill", "#f6f9fc"),
2313
2314
  editedFill: editedCellHighlightColor ?? readThemeColor(themeStyles, "--rvg-color-edited-fill", "#fff1b8"),
2314
2315
  insertedFill: insertedRowHighlightColor ?? readThemeColor(themeStyles, "--rvg-color-inserted-fill", "#c8ead4"),
@@ -2319,7 +2320,7 @@ function TableInner({
2319
2320
  const rowScrollTop = fixedHeader ? scroll.top : Math.max(0, scroll.top - bodyTop);
2320
2321
  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 };
2321
2322
  paintGrid({ context, width: viewport.width, height: renderHeight, pixelRatio: ratio, scrollLeft: scroll.left, scrollTop: scroll.top, rowHeight, bodyFontSize, headerActionSlotWidth, headerHeight, headerLeafTop, headerLeafHeight, bodyTop, suppressLastRowBottomBorder: bottomSummaryHeight > 0, suppressFrameBottomBorder: bottomSummaryHeight > 0, fixedHeader, verticalBorderless: !hasVerticalBorders, horizontalBorderless, frameBorderless, extendVerticalGridLines: !autoHeight, striped: hasStripedRows, columnDraggable, sortState, filterValues, hoveredHeaderAction: hoveredHeaderActionRef.current, rows, rowStyle, 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 });
2322
- }, [autoHeight, bodyTop, bodyViewportHeight, bottomSummaryHeight, cellAnnotations, cellSpanLookup, columnDraggable, columnDropTarget, columns, editedCellHighlightColor, editedCellKeys, editing, renderHeight, filterValues, fixedHeader, frameBorderless, getRowKey, hasStripedRows, hasVerticalBorders, headerActionSlotWidth, headerDepth, headerHeight, headerLeafHeight, headerLeafTop, horizontalBorderless, highlightsEditedCells, highlightsInsertedRows, insertedRowHighlightColor, insertedRowKeySet, isVirtualized, metrics, rowDragPreview, rowHeight, rowSelectionMode, rowStyle, rows, selectedColumnKeySet, selectedRowKeySet, selection, selectionRange, sortState, stripedColor, viewport.width, virtualOverscan]);
2323
+ }, [autoHeight, bodyTop, bodyViewportHeight, bottomSummaryHeight, cellAnnotations, cellSpanLookup, columnDragPreview, columnDraggable, columnDropTarget, columns, editedCellHighlightColor, editedCellKeys, editing, renderHeight, filterValues, fixedHeader, frameBorderless, getRowKey, hasStripedRows, hasVerticalBorders, headerActionSlotWidth, headerDepth, headerHeight, headerLeafHeight, headerLeafTop, horizontalBorderless, highlightsEditedCells, highlightsInsertedRows, insertedRowHighlightColor, insertedRowKeySet, isVirtualized, metrics, rowDragPreview, rowHeight, rowSelectionMode, rowStyle, rows, selectedColumnKeySet, selectedRowKeySet, selection, selectionRange, sortState, stripedColor, viewport.width, virtualOverscan]);
2323
2324
  const scheduleDraw = useCallback2(() => {
2324
2325
  if (frameRef.current === null) frameRef.current = requestAnimationFrame(draw);
2325
2326
  }, [draw]);
@@ -3594,6 +3595,10 @@ function TableInner({
3594
3595
  event.currentTarget.style.cursor = "default";
3595
3596
  }, []);
3596
3597
  const clearColumnOrderPointerDrag = useCallback2((canvas) => {
3598
+ if (columnOrderLongPressTimerRef.current) {
3599
+ clearTimeout(columnOrderLongPressTimerRef.current);
3600
+ columnOrderLongPressTimerRef.current = null;
3601
+ }
3597
3602
  columnOrderDragRef.current = null;
3598
3603
  setColumnDragPreview(null);
3599
3604
  validColumnDropRef.current = null;
@@ -3602,6 +3607,43 @@ function TableInner({
3602
3607
  document.documentElement.classList.remove("rvg-is-dragging");
3603
3608
  canvas.style.cursor = "default";
3604
3609
  }, [setDragGuide]);
3610
+ const updateColumnDragPreview = useCallback2((sourceCell, clientX, canvas) => {
3611
+ const rootRect = rootRef.current?.getBoundingClientRect() ?? canvas.getBoundingClientRect();
3612
+ const sourceWidth = metrics[sourceCell.startIndex]?.width ?? 100;
3613
+ const sourceColumn = columns[sourceCell.startIndex];
3614
+ const previewContext = canvas.getContext("2d");
3615
+ const previewStyles = rootRef.current ? getComputedStyle(rootRef.current) : null;
3616
+ const previewFontSize = previewStyles ? readThemePixel(previewStyles, "--rvg-font-size-body", 13) : 13;
3617
+ if (previewContext) previewContext.font = `${previewFontSize}px Inter, ui-sans-serif, system-ui, sans-serif`;
3618
+ const measurePreviewText = (value) => previewContext?.measureText(value).width ?? value.length * previewFontSize * 0.58;
3619
+ const previewContentWidth = Math.max(
3620
+ measurePreviewText(sourceColumn?.title ?? ""),
3621
+ ...rows.slice(0, 100).map((_row, rowIndex) => measurePreviewText(getCellLabel(rowIndex, sourceCell.startIndex)))
3622
+ ) + 24;
3623
+ const previewWidth = Math.min(
3624
+ Math.max(sourceWidth, Math.ceil(previewContentWidth)),
3625
+ Math.max(60, Math.min(360, viewport.width - 16))
3626
+ );
3627
+ setColumnDragPreview({
3628
+ sourceIndex: sourceCell.startIndex,
3629
+ left: Math.max(4, Math.min(viewport.width - previewWidth - 4, clientX - rootRect.left - previewWidth / 2)),
3630
+ width: previewWidth
3631
+ });
3632
+ }, [columns, getCellLabel, metrics, rows, viewport.width]);
3633
+ const activateColumnOrderPointerDrag = useCallback2((canvas, clientX) => {
3634
+ const drag = columnOrderDragRef.current;
3635
+ if (!drag || drag.active) return;
3636
+ if (columnOrderLongPressTimerRef.current) {
3637
+ clearTimeout(columnOrderLongPressTimerRef.current);
3638
+ columnOrderLongPressTimerRef.current = null;
3639
+ }
3640
+ drag.active = true;
3641
+ suppressClickRef.current = true;
3642
+ hideDragTooltips();
3643
+ document.documentElement.classList.add("rvg-is-dragging");
3644
+ canvas.style.cursor = "move";
3645
+ updateColumnDragPreview(drag.sourceCell, clientX, canvas);
3646
+ }, [hideDragTooltips, updateColumnDragPreview]);
3605
3647
  const handleColumnOrderPointerDown = useCallback2((event) => {
3606
3648
  if (!columnDraggable || event.button !== 0 || columnDragRef.current) return;
3607
3649
  const sourceCell = locateHeaderCell(event.clientX, event.clientY);
@@ -3617,41 +3659,25 @@ function TableInner({
3617
3659
  active: false,
3618
3660
  sourceCell
3619
3661
  };
3620
- }, [columnDraggable, columns, locateHeaderAction, locateHeaderCell, locateHeaderResizeCell]);
3662
+ if (columnOrderLongPressTimerRef.current) clearTimeout(columnOrderLongPressTimerRef.current);
3663
+ const canvas = event.currentTarget;
3664
+ const pointerId = event.pointerId;
3665
+ columnOrderLongPressTimerRef.current = setTimeout(() => {
3666
+ const drag = columnOrderDragRef.current;
3667
+ if (!drag || drag.pointerId !== pointerId || drag.active) return;
3668
+ activateColumnOrderPointerDrag(canvas, drag.startX);
3669
+ }, 320);
3670
+ }, [activateColumnOrderPointerDrag, columnDraggable, columns, locateHeaderAction, locateHeaderCell, locateHeaderResizeCell]);
3621
3671
  const handleColumnOrderPointerMove = useCallback2((event) => {
3622
3672
  const drag = columnOrderDragRef.current;
3623
3673
  if (!drag || drag.pointerId !== event.pointerId || columnDragRef.current) return;
3624
3674
  if (!drag.active) {
3625
3675
  if (Math.hypot(event.clientX - drag.startX, event.clientY - drag.startY) < 4) return;
3626
- drag.active = true;
3627
- suppressClickRef.current = true;
3628
- hideDragTooltips();
3629
- document.documentElement.classList.add("rvg-is-dragging");
3630
- event.currentTarget.style.cursor = "move";
3676
+ activateColumnOrderPointerDrag(event.currentTarget, event.clientX);
3631
3677
  }
3632
3678
  event.preventDefault();
3633
3679
  const sourceCell = drag.sourceCell;
3634
- const rootRect = rootRef.current?.getBoundingClientRect() ?? event.currentTarget.getBoundingClientRect();
3635
- const sourceWidth = metrics[sourceCell.startIndex]?.width ?? 160;
3636
- const sourceColumn = columns[sourceCell.startIndex];
3637
- const previewContext = event.currentTarget.getContext("2d");
3638
- const previewStyles = rootRef.current ? getComputedStyle(rootRef.current) : null;
3639
- const previewFontSize = previewStyles ? readThemePixel(previewStyles, "--rvg-font-size-body", 13) : 13;
3640
- if (previewContext) previewContext.font = `${previewFontSize}px Inter, ui-sans-serif, system-ui, sans-serif`;
3641
- const measurePreviewText = (value) => previewContext?.measureText(value).width ?? value.length * previewFontSize * 0.58;
3642
- const previewContentWidth = Math.max(
3643
- measurePreviewText(sourceColumn?.title ?? ""),
3644
- ...rows.slice(0, 100).map((_row, rowIndex) => measurePreviewText(getCellLabel(rowIndex, sourceCell.startIndex)))
3645
- ) + 24;
3646
- const previewWidth = Math.min(
3647
- Math.max(sourceWidth, Math.ceil(previewContentWidth)),
3648
- Math.max(60, Math.min(360, viewport.width - 16))
3649
- );
3650
- setColumnDragPreview({
3651
- sourceIndex: sourceCell.startIndex,
3652
- left: Math.max(4, Math.min(viewport.width - previewWidth - 4, event.clientX - rootRect.left - previewWidth / 2)),
3653
- width: previewWidth
3654
- });
3680
+ updateColumnDragPreview(sourceCell, event.clientX, event.currentTarget);
3655
3681
  const hoveredColumnIndex = locateColumn(event.clientX);
3656
3682
  const targetCell = locateHeaderCell(event.clientX, event.clientY) ?? headerCells.find((cell) => hoveredColumnIndex >= cell.startIndex && hoveredColumnIndex <= cell.endIndex && cell.level === sourceCell.level && cell.leaf === sourceCell.leaf && cell.parentKey === sourceCell.parentKey && columns[cell.startIndex]?.fixed === columns[sourceCell.startIndex]?.fixed);
3657
3683
  if (!targetCell) {
@@ -3667,16 +3693,31 @@ function TableInner({
3667
3693
  setDragGuide(null);
3668
3694
  return;
3669
3695
  }
3696
+ if (targetCell.startIndex === sourceCell.startIndex && targetCell.endIndex === sourceCell.endIndex) {
3697
+ validColumnDropRef.current = null;
3698
+ setColumnDropTarget(null);
3699
+ setDragGuide(null);
3700
+ return;
3701
+ }
3670
3702
  const targetLeft = getHeaderCellLeft(targetCell);
3671
3703
  const targetWidth = getHeaderCellWidth(targetCell);
3672
3704
  const localX = event.clientX - event.currentTarget.getBoundingClientRect().left;
3673
- const columnEdge = localX < targetLeft + targetWidth / 2 ? "left" : "right";
3674
- const destinationIndex = resolveColumnDropIndex(
3705
+ let columnEdge = localX < targetLeft + targetWidth / 2 ? "left" : "right";
3706
+ let destinationIndex = resolveColumnDropIndex(
3675
3707
  sourceCell.startIndex,
3676
3708
  targetCell.startIndex,
3677
3709
  columnEdge,
3678
3710
  columns.length
3679
3711
  );
3712
+ if (destinationIndex === sourceCell.startIndex && targetCell.startIndex !== sourceCell.startIndex) {
3713
+ columnEdge = targetCell.startIndex < sourceCell.startIndex ? "left" : "right";
3714
+ destinationIndex = resolveColumnDropIndex(
3715
+ sourceCell.startIndex,
3716
+ targetCell.startIndex,
3717
+ columnEdge,
3718
+ columns.length
3719
+ );
3720
+ }
3680
3721
  const compatibleHighlightCells = headerCells.filter((cell) => {
3681
3722
  const cellColumn = columns[cell.startIndex];
3682
3723
  return cellColumn && !cellColumn.rowSelection && !cellColumn.rowDragHandle && !cellColumn.rowNumber && cellColumn.fixed === targetColumn.fixed;
@@ -3697,7 +3738,7 @@ function TableInner({
3697
3738
  fixed: targetColumn.fixed,
3698
3739
  columnEdge
3699
3740
  };
3700
- }, [columns, getCellLabel, getHeaderCellLeft, getHeaderCellWidth, headerCells, headerRowOffsets, hideDragTooltips, locateColumn, locateHeaderCell, metrics, rows, setDragGuide, viewport.width]);
3741
+ }, [activateColumnOrderPointerDrag, columns, getHeaderCellLeft, getHeaderCellWidth, headerCells, headerRowOffsets, locateColumn, locateHeaderCell, setDragGuide, updateColumnDragPreview]);
3701
3742
  const finishColumnOrderPointerDrag = useCallback2((event, apply) => {
3702
3743
  const drag = columnOrderDragRef.current;
3703
3744
  if (!drag || drag.pointerId !== event.pointerId) return;
@@ -4553,7 +4594,10 @@ function TableInner({
4553
4594
  };
4554
4595
  const currentScroll = scrollRef.current;
4555
4596
  const scrollerBottom = Math.max(0, bottomSummaryHeight - horizontalScrollbarHeight);
4556
- return /* @__PURE__ */ jsxs7("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" : ""} ${loading && !hasCompletedLoadRef.current && !hasCustomLoading ? "is-initial-loading" : ""} ${className}`, style: rootStyle, children: [
4597
+ const draggingSelectedColumn = Boolean(
4598
+ columnDragPreview && selectedColumnKeySet.has(columns[columnDragPreview.sourceIndex]?.key)
4599
+ );
4600
+ return /* @__PURE__ */ jsxs7("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: [
4557
4601
  /* @__PURE__ */ jsxs7("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: [
4558
4602
  /* @__PURE__ */ jsx7(
4559
4603
  "canvas",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lensui/lens-table",
3
- "version": "0.1.29",
3
+ "version": "0.1.30",
4
4
  "description": "A lightweight, canvas-rendered React table component for LensUI.",
5
5
  "type": "module",
6
6
  "main": "./dist/index.cjs",