Compare commits

...

1 Commits

Author SHA1 Message Date
dwelle 28cbe82a86 wip 2026-03-19 10:30:55 +01:00
3 changed files with 95 additions and 3 deletions
+60 -3
View File
@@ -692,6 +692,7 @@ class App extends React.Component<AppProps, AppState> {
lastPointerMoveEvent: PointerEvent | null = null;
/** current frame pointer cords */
lastPointerMoveCoords: { x: number; y: number } | null = null;
private lastCompletedCanvasClicks: { x: number; y: number }[] = [];
/** previous frame pointer coords */
previousPointerMoveCoords: { x: number; y: number } | null = null;
lastViewportPosition = { x: 0, y: 0 };
@@ -2341,6 +2342,7 @@ class App extends React.Component<AppProps, AppState> {
}
handleCanvasRef={this.handleInteractiveCanvasRef}
onContextMenu={this.handleCanvasContextMenu}
onClick={this.handleCanvasClick}
onPointerMove={this.handleCanvasPointerMove}
onPointerUp={this.handleCanvasPointerUp}
onPointerCancel={this.removePointer}
@@ -3594,10 +3596,14 @@ class App extends React.Component<AppProps, AppState> {
this.lassoTrail.endPath();
this.deselectElements();
// @ts-ignore
this.handleCanvasDoubleClick({
clientX: touch.clientX,
clientY: touch.clientY,
type: "touch",
altKey: false,
ctrlKey: false,
metaKey: false,
shiftKey: false,
});
}
didTapTwice = false;
@@ -6220,10 +6226,46 @@ class App extends React.Component<AppProps, AppState> {
}
};
private shouldHandleBrowserCanvasDoubleClick = (type: string) => {
// TODO remove this once we consolidate double-click logic and handle
// ourselves for all event types together
if (type === "touch") {
return true;
}
if (this.lastCompletedCanvasClicks.length === 0) {
return true;
}
if (this.lastCompletedCanvasClicks.length < 2) {
return false;
}
const [firstClick, secondClick] = this.lastCompletedCanvasClicks;
return (
pointDistance(
pointFrom(firstClick.x, firstClick.y),
pointFrom(secondClick.x, secondClick.y),
) <= DOUBLE_TAP_POSITION_THRESHOLD
);
};
private handleCanvasDoubleClick = (
event: React.MouseEvent<HTMLCanvasElement>,
event: Pick<
React.MouseEvent<HTMLCanvasElement>,
| "type"
| "clientX"
| "clientY"
| "altKey"
| "ctrlKey"
| "metaKey"
| "shiftKey"
>,
) => {
if (this.state.editingTextElement) {
if (
this.state.editingTextElement ||
!this.shouldHandleBrowserCanvasDoubleClick(event.type)
) {
return;
}
// case: double-clicking with arrow/line tool selected would both create
@@ -6413,6 +6455,21 @@ class App extends React.Component<AppProps, AppState> {
}
};
private handleCanvasClick = (event: React.MouseEvent<HTMLCanvasElement>) => {
if (event.button !== POINTER_BUTTON.MAIN) {
this.lastCompletedCanvasClicks = [];
return;
}
this.lastCompletedCanvasClicks = [
...this.lastCompletedCanvasClicks.slice(-1),
{
x: event.clientX,
y: event.clientY,
},
];
};
private getElementLinkAtPosition = (
scenePointer: Readonly<{ x: number; y: number }>,
hitElementMightBeLocked: NonDeletedExcalidrawElement | null,
@@ -54,6 +54,7 @@ type InteractiveCanvasProps = {
DOMAttributes<HTMLCanvasElement | HTMLDivElement>["onContextMenu"],
undefined
>;
onClick: Exclude<DOMAttributes<HTMLCanvasElement>["onClick"], undefined>;
onPointerMove: Exclude<
DOMAttributes<HTMLCanvasElement>["onPointerMove"],
undefined
@@ -213,6 +214,7 @@ const InteractiveCanvas = (props: InteractiveCanvasProps) => {
height={props.appState.height * props.scale}
ref={props.handleCanvasRef}
onContextMenu={props.onContextMenu}
onClick={props.onClick}
onPointerMove={props.onPointerMove}
onPointerUp={props.onPointerUp}
onPointerCancel={props.onPointerCancel}
@@ -293,6 +293,39 @@ describe("textWysiwyg", () => {
expect(await getTextEditor({ waitForEditor: false })).toBe(null);
});
it("should ignore double-click when the second click ends away from where it started", async () => {
UI.clickTool("selection");
mouse.downAt(40, 40);
mouse.upAt(40, 40);
fireEvent.click(GlobalTestState.interactiveCanvas, {
button: 0,
clientX: 40,
clientY: 40,
});
mouse.downAt(40, 40);
mouse.moveTo(200, 200);
mouse.upAt(200, 200);
fireEvent.click(GlobalTestState.interactiveCanvas, {
button: 0,
clientX: 200,
clientY: 200,
});
fireEvent.doubleClick(GlobalTestState.interactiveCanvas, {
button: 0,
clientX: 200,
clientY: 200,
});
const editor = await getTextEditor({ waitForEditor: false });
expect(editor).toBe(null);
expect(h.state.editingTextElement).toBe(null);
expect(h.elements.length).toBe(0);
});
// FIXME too flaky. No one knows why.
it.skip("should bump the version of a labeled arrow when the label is updated", async () => {
const arrow = UI.createElement("arrow", {