fix: cursor being leaked outside of canvas (#3161)

pull/3165/head
David Luzar 4 years ago committed by GitHub
parent f295ba98c5
commit c77c9ce65a
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

@ -18,7 +18,7 @@ import { isBindingElement } from "../element/typeChecks";
export const actionFinalize = register({
name: "finalize",
perform: (elements, appState) => {
perform: (elements, appState, _, { canvas }) => {
if (appState.editingLinearElement) {
const {
elementId,
@ -126,7 +126,7 @@ export const actionFinalize = register({
(!appState.elementLocked && appState.elementType !== "draw") ||
!multiPointElement
) {
resetCursor();
resetCursor(canvas);
}
return {
elements: newElements,

@ -151,10 +151,12 @@ const LIBRARY_ICON = (
);
export const ShapesSwitcher = ({
canvas,
elementType,
setAppState,
isLibraryOpen,
}: {
canvas: HTMLCanvasElement | null;
elementType: ExcalidrawElement["type"];
setAppState: React.Component<any, AppState>["setState"];
isLibraryOpen: boolean;
@ -185,7 +187,7 @@ export const ShapesSwitcher = ({
multiElement: null,
selectedElementIds: {},
});
setCursorForShape(value);
setCursorForShape(canvas, value);
setAppState({});
}}
/>

@ -172,6 +172,7 @@ import {
ResolvablePromise,
resolvablePromise,
sceneCoordsToViewportCoords,
setCursor,
setCursorForShape,
tupleToCoors,
viewportCoordsToSceneCoords,
@ -1440,16 +1441,16 @@ class App extends React.Component<ExcalidrawProps, AppState> {
}
if (event.key === KEYS.SPACE && gesture.pointers.size === 0) {
isHoldingSpace = true;
document.documentElement.style.cursor = CURSOR_TYPE.GRABBING;
setCursor(this.canvas, CURSOR_TYPE.GRABBING);
}
});
private onKeyUp = withBatchedUpdates((event: KeyboardEvent) => {
if (event.key === KEYS.SPACE) {
if (this.state.elementType === "selection") {
resetCursor();
resetCursor(this.canvas);
} else {
setCursorForShape(this.state.elementType);
setCursorForShape(this.canvas, this.state.elementType);
this.setState({
selectedElementIds: {},
selectedGroupIds: {},
@ -1475,7 +1476,7 @@ class App extends React.Component<ExcalidrawProps, AppState> {
private selectShapeTool(elementType: AppState["elementType"]) {
if (!isHoldingSpace) {
setCursorForShape(elementType);
setCursorForShape(this.canvas, elementType);
}
if (isToolIcon(document.activeElement)) {
document.activeElement.blur();
@ -1601,7 +1602,7 @@ class App extends React.Component<ExcalidrawProps, AppState> {
editingElement: null,
});
if (this.state.elementLocked) {
setCursorForShape(this.state.elementType);
setCursorForShape(this.canvas, this.state.elementType);
}
}),
element,
@ -1782,7 +1783,7 @@ class App extends React.Component<ExcalidrawProps, AppState> {
return;
}
resetCursor();
resetCursor(this.canvas);
const { x: sceneX, y: sceneY } = viewportCoordsToSceneCoords(
event,
@ -1814,7 +1815,7 @@ class App extends React.Component<ExcalidrawProps, AppState> {
}
}
resetCursor();
resetCursor(this.canvas);
if (!event[KEYS.CTRL_OR_CMD]) {
this.startTextEditing({
@ -1880,9 +1881,9 @@ class App extends React.Component<ExcalidrawProps, AppState> {
const isOverScrollBar = isPointerOverScrollBars.isOverEither;
if (!this.state.draggingElement && !this.state.multiElement) {
if (isOverScrollBar) {
resetCursor();
resetCursor(this.canvas);
} else {
setCursorForShape(this.state.elementType);
setCursorForShape(this.canvas, this.state.elementType);
}
}
@ -1933,7 +1934,7 @@ class App extends React.Component<ExcalidrawProps, AppState> {
const { points, lastCommittedPoint } = multiElement;
const lastPoint = points[points.length - 1];
setCursorForShape(this.state.elementType);
setCursorForShape(this.canvas, this.state.elementType);
if (lastPoint === lastCommittedPoint) {
// if we haven't yet created a temp point and we're beyond commit-zone
@ -1950,7 +1951,7 @@ class App extends React.Component<ExcalidrawProps, AppState> {
points: [...points, [scenePointerX - rx, scenePointerY - ry]],
});
} else {
document.documentElement.style.cursor = CURSOR_TYPE.POINTER;
setCursor(this.canvas, CURSOR_TYPE.POINTER);
// in this branch, we're inside the commit zone, and no uncommitted
// point exists. Thus do nothing (don't add/remove points).
}
@ -1964,13 +1965,13 @@ class App extends React.Component<ExcalidrawProps, AppState> {
lastCommittedPoint[1],
) < LINE_CONFIRM_THRESHOLD
) {
document.documentElement.style.cursor = CURSOR_TYPE.POINTER;
setCursor(this.canvas, CURSOR_TYPE.POINTER);
mutateElement(multiElement, {
points: points.slice(0, -1),
});
} else {
if (isPathALoop(points, this.state.zoom.value)) {
document.documentElement.style.cursor = CURSOR_TYPE.POINTER;
setCursor(this.canvas, CURSOR_TYPE.POINTER);
}
// update last uncommitted point
mutateElement(multiElement, {
@ -2013,8 +2014,9 @@ class App extends React.Component<ExcalidrawProps, AppState> {
elementWithTransformHandleType &&
elementWithTransformHandleType.transformHandleType
) {
document.documentElement.style.cursor = getCursorForResizingElement(
elementWithTransformHandleType,
setCursor(
this.canvas,
getCursorForResizingElement(elementWithTransformHandleType),
);
return;
}
@ -2027,9 +2029,12 @@ class App extends React.Component<ExcalidrawProps, AppState> {
event.pointerType,
);
if (transformHandleType) {
document.documentElement.style.cursor = getCursorForResizingElement({
transformHandleType,
});
setCursor(
this.canvas,
getCursorForResizingElement({
transformHandleType,
}),
);
return;
}
}
@ -2039,11 +2044,12 @@ class App extends React.Component<ExcalidrawProps, AppState> {
scenePointer.y,
);
if (this.state.elementType === "text") {
document.documentElement.style.cursor = isTextElement(hitElement)
? CURSOR_TYPE.TEXT
: CURSOR_TYPE.CROSSHAIR;
setCursor(
this.canvas,
isTextElement(hitElement) ? CURSOR_TYPE.TEXT : CURSOR_TYPE.CROSSHAIR,
);
} else if (isOverScrollBar) {
document.documentElement.style.cursor = CURSOR_TYPE.AUTO;
setCursor(this.canvas, CURSOR_TYPE.AUTO);
} else if (
hitElement ||
this.isHittingCommonBoundingBoxOfSelectedElements(
@ -2051,9 +2057,9 @@ class App extends React.Component<ExcalidrawProps, AppState> {
selectedElements,
)
) {
document.documentElement.style.cursor = CURSOR_TYPE.MOVE;
setCursor(this.canvas, CURSOR_TYPE.MOVE);
} else {
document.documentElement.style.cursor = CURSOR_TYPE.AUTO;
setCursor(this.canvas, CURSOR_TYPE.AUTO);
}
};
@ -2226,7 +2232,7 @@ class App extends React.Component<ExcalidrawProps, AppState> {
let nextPastePrevented = false;
const isLinux = /Linux/.test(window.navigator.platform);
document.documentElement.style.cursor = CURSOR_TYPE.GRABBING;
setCursor(this.canvas, CURSOR_TYPE.GRABBING);
let { clientX: lastX, clientY: lastY } = event;
const onPointerMove = withBatchedUpdates((event: PointerEvent) => {
const deltaX = lastX - event.clientX;
@ -2278,7 +2284,7 @@ class App extends React.Component<ExcalidrawProps, AppState> {
lastPointerUp = null;
isPanning = false;
if (!isHoldingSpace) {
setCursorForShape(this.state.elementType);
setCursorForShape(this.canvas, this.state.elementType);
}
this.setState({
cursorButton: "up",
@ -2394,7 +2400,7 @@ class App extends React.Component<ExcalidrawProps, AppState> {
const onPointerUp = withBatchedUpdates(() => {
isDraggingScrollBar = false;
setCursorForShape(this.state.elementType);
setCursorForShape(this.canvas, this.state.elementType);
lastPointerUp = null;
this.setState({
cursorButton: "up",
@ -2457,9 +2463,12 @@ class App extends React.Component<ExcalidrawProps, AppState> {
);
}
if (pointerDownState.resize.handleType) {
document.documentElement.style.cursor = getCursorForResizingElement({
transformHandleType: pointerDownState.resize.handleType,
});
setCursor(
this.canvas,
getCursorForResizingElement({
transformHandleType: pointerDownState.resize.handleType,
}),
);
pointerDownState.resize.isResizing = true;
pointerDownState.resize.offset = tupleToCoors(
getResizeOffsetXY(
@ -2624,7 +2633,7 @@ class App extends React.Component<ExcalidrawProps, AppState> {
insertAtParentCenter: !event.altKey,
});
resetCursor();
resetCursor(this.canvas);
if (!this.state.elementLocked) {
this.setState({
elementType: "selection",
@ -2681,7 +2690,7 @@ class App extends React.Component<ExcalidrawProps, AppState> {
mutateElement(multiElement, {
lastCommittedPoint: multiElement.points[multiElement.points.length - 1],
});
document.documentElement.style.cursor = CURSOR_TYPE.POINTER;
setCursor(this.canvas, CURSOR_TYPE.POINTER);
} else {
const [gridX, gridY] = getGridPoint(
pointerDownState.origin.x,
@ -3216,7 +3225,7 @@ class App extends React.Component<ExcalidrawProps, AppState> {
}
this.setState({ suggestedBindings: [], startBoundElement: null });
if (!elementLocked && elementType !== "draw") {
resetCursor();
resetCursor(this.canvas);
this.setState((prevState) => ({
draggingElement: null,
elementType: "selection",
@ -3387,7 +3396,7 @@ class App extends React.Component<ExcalidrawProps, AppState> {
}
if (!elementLocked && elementType !== "draw") {
resetCursor();
resetCursor(this.canvas);
this.setState({
draggingElement: null,
suggestedBindings: [],

@ -516,6 +516,7 @@ const LayerUI = ({
{heading}
<Stack.Row gap={1}>
<ShapesSwitcher
canvas={canvas}
elementType={appState.elementType}
setAppState={setAppState}
isLibraryOpen={appState.isLibraryOpen}

@ -57,6 +57,7 @@ export const MobileMenu = ({
{heading}
<Stack.Row gap={1}>
<ShapesSwitcher
canvas={canvas}
elementType={appState.elementType}
setAppState={setAppState}
isLibraryOpen={appState.isLibraryOpen}

@ -1,6 +1,5 @@
import React from "react";
import * as Sentry from "@sentry/browser";
import { resetCursor } from "../utils";
import { t } from "../i18n";
interface TopErrorBoundaryState {
@ -24,7 +23,6 @@ export class TopErrorBoundary extends React.Component<
}
componentDidCatch(error: Error, errorInfo: any) {
resetCursor();
const _localStorage: any = {};
for (const [key, value] of Object.entries({ ...localStorage })) {
try {

@ -21,7 +21,6 @@ import {
import { isLinearElement, isTextElement } from "./typeChecks";
import { mutateElement } from "./mutateElement";
import { getPerfectElementSize } from "./sizeHelpers";
import { getCursorForResizingElement } from "./resizeTest";
import { measureText, getFontString } from "../utils";
import { updateBoundElements } from "./binding";
import {
@ -105,13 +104,6 @@ export const transformElements = (
);
}
// update cursor
// FIXME it is not very nice to have this here
document.documentElement.style.cursor = getCursorForResizingElement({
element,
transformHandleType,
});
return true;
} else if (selectedElements.length > 1) {
if (transformHandleType === "rotation") {

@ -160,15 +160,29 @@ export const removeSelection = () => {
export const distance = (x: number, y: number) => Math.abs(x - y);
export const resetCursor = () => {
document.documentElement.style.cursor = "";
export const resetCursor = (canvas: HTMLCanvasElement | null) => {
if (canvas) {
canvas.style.cursor = "";
}
};
export const setCursor = (canvas: HTMLCanvasElement | null, cursor: string) => {
if (canvas) {
canvas.style.cursor = cursor;
}
};
export const setCursorForShape = (shape: string) => {
export const setCursorForShape = (
canvas: HTMLCanvasElement | null,
shape: string,
) => {
if (!canvas) {
return;
}
if (shape === "selection") {
resetCursor();
resetCursor(canvas);
} else {
document.documentElement.style.cursor = CURSOR_TYPE.CROSSHAIR;
canvas.style.cursor = CURSOR_TYPE.CROSSHAIR;
}
};

Loading…
Cancel
Save