fix(grid): stabilize row drag selection gestures

This commit is contained in:
hanxuanyu authored and GitHub committed 2026-08-19 00:30:40 +08:00
1 parent b7338518d1
commit fd41268432
4 files changed
+228 -4

No files matched your search

+23 -3
View File
@@ -4601,6 +4601,10 @@ const selection = useDataGridSelection({
cellFromClientPoint: dataGridCellFromClientPoint,
rowFromClientPoint: dataGridRowFromClientPoint,
onUserCellSelection: invalidateSyntheticContextSelection,
// Canvas schedules its draw before the document-level mousemove handler runs,
// so its row state must be current before that frame is painted.
shouldUpdateDraggedRowsImmediately: () => useCanvasGridRows.value,
onDraggedRowSelectionChange: scheduleCanvasDraw,
runtimeScope: dataGridRuntimeScope,
});
@@ -4915,7 +4919,22 @@ const domSelectionDragOverlayStyle = computed((): CSSProperties | undefined => {
};
});
function onCellMouseenter(rowIndex: number, visibleColIdx: number, actualColIdx: number) {
function stopReleasedSelectionGesture(event: MouseEvent): boolean {
if ((event.buttons & 1) !== 0) return false;
let stopped = false;
if (isSelectingCells.value) {
finishCellSelection();
stopped = true;
}
if (selection.isSelectingRows.value) {
selection.finishRowSelection();
stopped = true;
}
return stopped;
}
function onCellMouseenter(rowIndex: number, visibleColIdx: number, actualColIdx: number, event: MouseEvent) {
stopReleasedSelectionGesture(event);
if (!isSelectingCells.value) {
quickDownloadMenuCell.value = retainBinaryCellDownloadMenuForHover(quickDownloadMenuCell.value, { rowIndex, col: actualColIdx });
if (!isScrolling.value) hoveredDetailCell.value = { rowIndex, col: actualColIdx };
@@ -6261,6 +6280,7 @@ function onDomGridWheel(event: WheelEvent) {
}
function onCanvasMouseMove(event: MouseEvent) {
stopReleasedSelectionGesture(event);
if (columnHeaderPointerInteractionActive()) {
if (canvasRef.value) canvasRef.value.style.cursor = "default";
onCanvasMouseLeave();
@@ -6290,7 +6310,7 @@ function onCanvasMouseMove(event: MouseEvent) {
if (previousActualColIdx !== undefined) onCellMouseleave(previous.rowIndex, previousActualColIdx);
}
canvasHoverCell.value = next;
if (next && actualColIdx !== undefined) onCellMouseenter(next.rowIndex, next.visibleColIdx, actualColIdx);
if (next && actualColIdx !== undefined) onCellMouseenter(next.rowIndex, next.visibleColIdx, actualColIdx, event);
scheduleCanvasDraw();
}
@@ -11758,7 +11778,7 @@ function currentGridContextMenuItems(): ContextMenuItem[] {
prepareDataCellMouseDown(item, col.actualColIdx);
handleDataCellMousedown(item.displayIndex, col.visibleColIdx, item.id, $event);
"
@mouseenter="onCellMouseenter(item.displayIndex, col.visibleColIdx, col.actualColIdx)"
@mouseenter="onCellMouseenter(item.displayIndex, col.visibleColIdx, col.actualColIdx, $event)"
@mouseleave="onCellMouseleave(item.displayIndex, col.actualColIdx)"
@dblclick="onDomCellDblClick(item, col.actualColIdx, $event)"
:data-visible-col-index="col.visibleColIdx"
@@ -0,0 +1,16 @@
import { readFileSync } from "node:fs";
import { describe, expect, it } from "vitest";
const source = readFileSync(new URL("../DataGrid.vue", import.meta.url), "utf8");
describe("DataGrid selection gesture ownership", () => {
it("stops released gestures before DOM hover or canvas movement extends them", () => {
expect(source).toContain("function stopReleasedSelectionGesture(event: MouseEvent)");
expect(source).toContain("if ((event.buttons & 1) !== 0) return false;");
expect(source).toContain("function onCellMouseenter(rowIndex: number, visibleColIdx: number, actualColIdx: number, event: MouseEvent)");
expect(source).toContain('@mouseenter="onCellMouseenter(item.displayIndex, col.visibleColIdx, col.actualColIdx, $event)"');
const canvasMove = source.slice(source.indexOf("function onCanvasMouseMove"), source.indexOf("function onCanvasMouseLeave"));
expect(canvasMove).toContain("stopReleasedSelectionGesture(event);");
});
});