1 parent
de99931c04
commit
53b7be3c6f
4 files changed
+287
-31
No files matched your search
@@ -195,6 +195,7 @@ import {
|
||||
type ResultScopedRowCache,
|
||||
} from "@/lib/dataGrid/dataGridLargeValues";
|
||||
import { dataGridBottomScrollTop, dataGridScrollPosition, isDataGridAtScrollBottom, isDataGridNearScrollBottom, isDataGridPrefixAppend, shouldCheckInfiniteScrollAfterScroll, type DataGridScrollPosition } from "@/lib/dataGrid/dataGridInfiniteScroll";
|
||||
import { resolveDataGridWheelScroll } from "@/lib/dataGrid/dataGridWheel";
|
||||
import { CANVAS_DATA_GRID_ROW_HEIGHT, MAX_CANVAS_DATA_GRID_PIXEL_RATIO, canvasDataGridActionOverlayWidth, canvasDataGridActionReservedWidth, dataGridSearchMatchKey, drawCanvasDataGrid, type CanvasDevicePixelSize } from "@/lib/dataGrid/canvasDataGridRenderer";
|
||||
import { DATA_GRID_DARK_STRIPED_ROW_BG, DATA_GRID_LIGHT_STRIPED_ROW_BG, dataGridActiveRowBackground } from "@/lib/dataGrid/dataGridPaintTheme";
|
||||
import { createRowLowerTextCache } from "@/lib/dataGrid/dataGridRowLowerText";
|
||||
@@ -6721,14 +6722,9 @@ function onCanvasScroll(event: Event) {
|
||||
scheduleCanvasDraw();
|
||||
}
|
||||
|
||||
function canvasWheelDeltaToPixels(delta: number, deltaMode: number, pageSize: number): number {
|
||||
if (deltaMode === WheelEvent.DOM_DELTA_LINE) return delta * CANVAS_DATA_GRID_ROW_HEIGHT;
|
||||
if (deltaMode === WheelEvent.DOM_DELTA_PAGE) return delta * pageSize;
|
||||
return delta;
|
||||
}
|
||||
|
||||
function shouldAccelerateCanvasWheel(event: WheelEvent): boolean {
|
||||
if (event.ctrlKey || event.metaKey) return false;
|
||||
if (event.deltaX !== 0) return true;
|
||||
if (event.deltaMode !== WheelEvent.DOM_DELTA_PIXEL) return true;
|
||||
return event.shiftKey && Math.abs(event.deltaY) > Math.abs(event.deltaX) && Math.abs(event.deltaY) >= CANVAS_TRACKPAD_DELTA_THRESHOLD;
|
||||
}
|
||||
@@ -6738,42 +6734,45 @@ function onCanvasWheel(event: WheelEvent) {
|
||||
const scroller = canvasScrollerElement();
|
||||
if (!scroller) return;
|
||||
|
||||
const verticalDelta = canvasWheelDeltaToPixels(event.deltaY, event.deltaMode, scroller.clientHeight);
|
||||
const horizontalDelta = canvasWheelDeltaToPixels(event.deltaX, event.deltaMode, scroller.clientWidth);
|
||||
const shiftedHorizontalDelta = event.shiftKey && Math.abs(verticalDelta) > Math.abs(horizontalDelta) ? verticalDelta : 0;
|
||||
const nextTop = shiftedHorizontalDelta === 0 ? Math.max(0, Math.min(scroller.scrollHeight - scroller.clientHeight, scroller.scrollTop + verticalDelta * CANVAS_MOUSE_WHEEL_SCROLL_MULTIPLIER)) : scroller.scrollTop;
|
||||
const nextLeft = Math.max(0, Math.min(scroller.scrollWidth - scroller.clientWidth, scroller.scrollLeft + (horizontalDelta + shiftedHorizontalDelta) * CANVAS_MOUSE_WHEEL_SCROLL_MULTIPLIER));
|
||||
|
||||
if (nextTop === scroller.scrollTop && nextLeft === scroller.scrollLeft) return;
|
||||
const wheelScroll = resolveDataGridWheelScroll({
|
||||
deltaX: event.deltaX,
|
||||
deltaY: event.deltaY,
|
||||
deltaMode: event.deltaMode,
|
||||
shiftKey: event.shiftKey,
|
||||
ctrlKey: event.ctrlKey,
|
||||
metaKey: event.metaKey,
|
||||
lineSize: CANVAS_DATA_GRID_ROW_HEIGHT,
|
||||
metrics: scroller,
|
||||
accelerationFactor: CANVAS_MOUSE_WHEEL_SCROLL_MULTIPLIER,
|
||||
});
|
||||
if (!wheelScroll.moved) return;
|
||||
event.preventDefault();
|
||||
scroller.scrollTop = nextTop;
|
||||
scroller.scrollLeft = nextLeft;
|
||||
event.stopPropagation();
|
||||
scroller.scrollTop = wheelScroll.nextScrollTop;
|
||||
scroller.scrollLeft = wheelScroll.nextScrollLeft;
|
||||
onCanvasScroll({ target: scroller } as unknown as Event);
|
||||
}
|
||||
|
||||
function onDomGridWheel(event: WheelEvent) {
|
||||
if (event.ctrlKey || event.metaKey) return;
|
||||
const scroller = event.currentTarget instanceof HTMLElement ? event.currentTarget : gridScrollerElement();
|
||||
if (!scroller) return;
|
||||
|
||||
const verticalDelta = canvasWheelDeltaToPixels(event.deltaY, event.deltaMode, scroller.clientHeight);
|
||||
const horizontalDelta = canvasWheelDeltaToPixels(event.deltaX, event.deltaMode, scroller.clientWidth);
|
||||
const shiftedHorizontalDelta = event.shiftKey && Math.abs(verticalDelta) > Math.abs(horizontalDelta) ? verticalDelta : 0;
|
||||
const effectiveVerticalDelta = shiftedHorizontalDelta === 0 ? verticalDelta : 0;
|
||||
const effectiveHorizontalDelta = horizontalDelta + shiftedHorizontalDelta;
|
||||
if (effectiveVerticalDelta === 0 && effectiveHorizontalDelta === 0) return;
|
||||
|
||||
const maxTop = Math.max(0, scroller.scrollHeight - scroller.clientHeight);
|
||||
const maxLeft = Math.max(0, scroller.scrollWidth - scroller.clientWidth);
|
||||
const nextTop = Math.max(0, Math.min(maxTop, scroller.scrollTop + effectiveVerticalDelta));
|
||||
const nextLeft = Math.max(0, Math.min(maxLeft, scroller.scrollLeft + effectiveHorizontalDelta));
|
||||
// Let an outer scroll container handle wheel input once the grid reaches its boundary.
|
||||
if (nextTop === scroller.scrollTop && nextLeft === scroller.scrollLeft) return;
|
||||
const wheelScroll = resolveDataGridWheelScroll({
|
||||
deltaX: event.deltaX,
|
||||
deltaY: event.deltaY,
|
||||
deltaMode: event.deltaMode,
|
||||
shiftKey: event.shiftKey,
|
||||
ctrlKey: event.ctrlKey,
|
||||
metaKey: event.metaKey,
|
||||
lineSize: CANVAS_DATA_GRID_ROW_HEIGHT,
|
||||
metrics: scroller,
|
||||
});
|
||||
if (!wheelScroll.moved) return;
|
||||
event.preventDefault();
|
||||
event.stopPropagation();
|
||||
|
||||
scroller.scrollTop = nextTop;
|
||||
scroller.scrollLeft = nextLeft;
|
||||
scroller.scrollTop = wheelScroll.nextScrollTop;
|
||||
scroller.scrollLeft = wheelScroll.nextScrollLeft;
|
||||
onScrollerScroll({ target: scroller } as unknown as Event);
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,36 @@
|
||||
import { readFileSync } from "node:fs";
|
||||
import { describe, expect, it } from "vitest";
|
||||
|
||||
const dataGridSource = readFileSync(new URL("../DataGrid.vue", import.meta.url), "utf8");
|
||||
|
||||
function sourceBetween(start: string, end: string): string {
|
||||
const startIndex = dataGridSource.indexOf(start);
|
||||
const endIndex = dataGridSource.indexOf(end, startIndex + start.length);
|
||||
expect(startIndex).toBeGreaterThanOrEqual(0);
|
||||
expect(endIndex).toBeGreaterThan(startIndex);
|
||||
return dataGridSource.slice(startIndex, endIndex);
|
||||
}
|
||||
|
||||
describe("data grid wheel bindings", () => {
|
||||
it("routes Canvas and DOM wheel calculations through the shared helper", () => {
|
||||
const canvasHandler = sourceBetween("function onCanvasWheel", "function onDomGridWheel");
|
||||
const domHandler = sourceBetween("function onDomGridWheel", "function onCanvasMouseMove");
|
||||
|
||||
for (const handler of [canvasHandler, domHandler]) {
|
||||
expect(handler).toContain("resolveDataGridWheelScroll({");
|
||||
expect(handler).toContain("if (!wheelScroll.moved) return;");
|
||||
expect(handler.indexOf("if (!wheelScroll.moved) return;")).toBeLessThan(handler.indexOf("event.preventDefault();"));
|
||||
expect(handler).toContain("event.stopPropagation();");
|
||||
}
|
||||
});
|
||||
|
||||
it("accepts genuine pixel deltaX in the Canvas handler", () => {
|
||||
const predicate = sourceBetween("function shouldAccelerateCanvasWheel", "function onCanvasWheel");
|
||||
expect(predicate).toContain("if (event.deltaX !== 0) return true;");
|
||||
});
|
||||
|
||||
it("keeps wheel handlers bound in both render modes", () => {
|
||||
expect(dataGridSource).toContain('@wheel="onCanvasWheel"');
|
||||
expect(dataGridSource.match(/@wheel="onDomGridWheel"/g)).toHaveLength(2);
|
||||
});
|
||||
});
|
||||
Reference in new issue
Block a user