Skip to content

Commit 0497fed

Browse files
committed
fix: address performance and logic issues in drag selection
- Add throttling to mouse move handler (16ms/~60fps) to prevent performance issues - Fix mouse leave handler to only terminate drag when no button is pressed - Fix logic error that was clearing UI on right/middle clicks - Add proper cleanup for throttled function Addresses review feedback from Graphite
1 parent 3c0f9bf commit 0497fed

File tree

1 file changed

+25
-14
lines changed
  • apps/web/client/src/app/project/[id]/_components/canvas

1 file changed

+25
-14
lines changed

apps/web/client/src/app/project/[id]/_components/canvas/index.tsx

Lines changed: 25 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
import { useEditorEngine } from '@/components/store/editor';
44
import { EditorAttributes } from '@onlook/constants';
55
import { EditorMode } from '@onlook/models';
6+
import { throttle } from 'lodash';
67
import { observer } from 'mobx-react-lite';
78
import { useCallback, useEffect, useMemo, useRef, useState } from 'react';
89
import { Frames } from './frames';
@@ -35,7 +36,7 @@ export const Canvas = observer(() => {
3536
return;
3637
}
3738

38-
// Start drag selection only in design mode and when not holding middle mouse button
39+
// Start drag selection only in design mode and left mouse button
3940
if (editorEngine.state.editorMode === EditorMode.DESIGN && event.button === 0) {
4041
const rect = containerRef.current.getBoundingClientRect();
4142
const x = event.clientX - rect.left;
@@ -50,21 +51,25 @@ export const Canvas = observer(() => {
5051
editorEngine.clearUI();
5152
editorEngine.frames.deselectAll();
5253
}
53-
} else {
54+
} else if (event.button === 0) {
55+
// Only clear UI for left clicks that don't start drag selection
5456
editorEngine.clearUI();
5557
}
5658
};
5759

58-
const handleCanvasMouseMove = (event: React.MouseEvent<HTMLDivElement>) => {
59-
if (!isDragSelecting || !containerRef.current) {
60-
return;
61-
}
62-
63-
const rect = containerRef.current.getBoundingClientRect();
64-
const x = event.clientX - rect.left;
65-
const y = event.clientY - rect.top;
66-
setDragSelectEnd({ x, y });
67-
};
60+
const handleCanvasMouseMove = useCallback(
61+
throttle((event: React.MouseEvent<HTMLDivElement>) => {
62+
if (!isDragSelecting || !containerRef.current) {
63+
return;
64+
}
65+
66+
const rect = containerRef.current.getBoundingClientRect();
67+
const x = event.clientX - rect.left;
68+
const y = event.clientY - rect.top;
69+
setDragSelectEnd({ x, y });
70+
}, 16), // ~60fps
71+
[isDragSelecting]
72+
);
6873

6974
const handleCanvasMouseUp = (event: React.MouseEvent<HTMLDivElement>) => {
7075
if (!isDragSelecting) {
@@ -239,9 +244,10 @@ export const Canvas = observer(() => {
239244
div.removeEventListener('wheel', handleWheel);
240245
div.removeEventListener('mousedown', middleMouseButtonDown);
241246
div.removeEventListener('mouseup', middleMouseButtonUp);
247+
handleCanvasMouseMove.cancel?.(); // Clean up throttled function
242248
};
243249
}
244-
}, [handleWheel, middleMouseButtonDown, middleMouseButtonUp]);
250+
}, [handleWheel, middleMouseButtonDown, middleMouseButtonUp, handleCanvasMouseMove]);
245251

246252
return (
247253
<HotkeysArea>
@@ -251,7 +257,12 @@ export const Canvas = observer(() => {
251257
onMouseDown={handleCanvasMouseDown}
252258
onMouseMove={handleCanvasMouseMove}
253259
onMouseUp={handleCanvasMouseUp}
254-
onMouseLeave={handleCanvasMouseUp}
260+
onMouseLeave={(e) => {
261+
// Only terminate drag if no mouse button is pressed
262+
if (e.buttons === 0) {
263+
handleCanvasMouseUp(e);
264+
}
265+
}}
255266
>
256267
<div id={EditorAttributes.CANVAS_CONTAINER_ID} style={transformStyle}>
257268
<Frames />

0 commit comments

Comments
 (0)