1
0
mirror of https://github.com/excalidraw/excalidraw.git synced 2024-11-02 03:25:53 +01:00

feat: enable panning/zoom while in wysiwyg (#8437)

This commit is contained in:
David Luzar 2024-08-29 00:42:46 +02:00 committed by GitHub
parent ea7c702cfc
commit 00af35c692
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
4 changed files with 67 additions and 25 deletions

@ -49,6 +49,7 @@ import {
} from "./icons";
import { KEYS } from "../keys";
import { useTunnels } from "../context/tunnels";
import { CLASSES } from "../constants";
export const canChangeStrokeColor = (
appState: UIAppState,
@ -426,7 +427,7 @@ export const ZoomActions = ({
renderAction: ActionManager["renderAction"];
zoom: Zoom;
}) => (
<Stack.Col gap={1} className="zoom-actions">
<Stack.Col gap={1} className={CLASSES.ZOOM_ACTIONS}>
<Stack.Row align="center">
{renderAction("zoomOut")}
{renderAction("resetZoom")}

@ -2578,6 +2578,11 @@ class App extends React.Component<AppProps, AppState> {
addEventListener(window, EVENT.RESIZE, this.onResize, false),
addEventListener(window, EVENT.UNLOAD, this.onUnload, false),
addEventListener(window, EVENT.BLUR, this.onBlur, false),
addEventListener(
this.excalidrawContainerRef.current,
EVENT.WHEEL,
this.handleWheel,
),
addEventListener(
this.excalidrawContainerRef.current,
EVENT.DRAG_OVER,
@ -6384,8 +6389,8 @@ class App extends React.Component<AppProps, AppState> {
};
// Returns whether the event is a panning
private handleCanvasPanUsingWheelOrSpaceDrag = (
event: React.PointerEvent<HTMLElement>,
public handleCanvasPanUsingWheelOrSpaceDrag = (
event: React.PointerEvent<HTMLElement> | MouseEvent,
): boolean => {
if (
!(
@ -6394,13 +6399,16 @@ class App extends React.Component<AppProps, AppState> {
(event.button === POINTER_BUTTON.MAIN && isHoldingSpace) ||
isHandToolActive(this.state) ||
this.state.viewModeEnabled)
) ||
this.state.editingTextElement
)
) {
return false;
}
isPanning = true;
event.preventDefault();
if (!this.state.editingTextElement) {
// preventing defualt while text editing messes with cursor/focus
event.preventDefault();
}
let nextPastePrevented = false;
const isLinux =
@ -9472,7 +9480,6 @@ class App extends React.Component<AppProps, AppState> {
// NOTE wheel, touchstart, touchend events must be registered outside
// of react because react binds them them passively (so we can't prevent
// default on them)
this.interactiveCanvas.addEventListener(EVENT.WHEEL, this.handleWheel);
this.interactiveCanvas.addEventListener(
EVENT.TOUCH_START,
this.onTouchStart,
@ -9480,10 +9487,6 @@ class App extends React.Component<AppProps, AppState> {
this.interactiveCanvas.addEventListener(EVENT.TOUCH_END, this.onTouchEnd);
// -----------------------------------------------------------------------
} else {
this.interactiveCanvas?.removeEventListener(
EVENT.WHEEL,
this.handleWheel,
);
this.interactiveCanvas?.removeEventListener(
EVENT.TOUCH_START,
this.onTouchStart,
@ -10078,7 +10081,19 @@ class App extends React.Component<AppProps, AppState> {
(
event: WheelEvent | React.WheelEvent<HTMLDivElement | HTMLCanvasElement>,
) => {
// if not scrolling on canvas/wysiwyg, ignore
if (
!(
event.target instanceof HTMLCanvasElement ||
event.target instanceof HTMLTextAreaElement ||
event.target instanceof HTMLIFrameElement
)
) {
return;
}
event.preventDefault();
if (isPanning) {
return;
}

@ -112,6 +112,7 @@ export const ENV = {
export const CLASSES = {
SHAPE_ACTIONS_MENU: "App-menu__left",
ZOOM_ACTIONS: "zoom-actions",
};
/**

@ -11,7 +11,7 @@ import {
isBoundToContainer,
isTextElement,
} from "./typeChecks";
import { CLASSES, isSafari } from "../constants";
import { CLASSES, isSafari, POINTER_BUTTON } from "../constants";
import type {
ExcalidrawElement,
ExcalidrawLinearElement,
@ -38,7 +38,11 @@ import {
actionDecreaseFontSize,
actionIncreaseFontSize,
} from "../actions/actionProperties";
import { actionZoomIn, actionZoomOut } from "../actions/actionCanvas";
import {
actionResetZoom,
actionZoomIn,
actionZoomOut,
} from "../actions/actionCanvas";
import type App from "../components/App";
import { LinearElementEditor } from "./linearElementEditor";
import { parseClipboard } from "../clipboard";
@ -379,6 +383,10 @@ export const textWysiwyg = ({
event.preventDefault();
app.actionManager.executeAction(actionZoomOut);
updateWysiwygStyle();
} else if (!event.shiftKey && actionResetZoom.keyTest(event)) {
event.preventDefault();
app.actionManager.executeAction(actionResetZoom);
updateWysiwygStyle();
} else if (actionDecreaseFontSize.keyTest(event)) {
app.actionManager.executeAction(actionDecreaseFontSize);
} else if (actionIncreaseFontSize.keyTest(event)) {
@ -593,6 +601,7 @@ export const textWysiwyg = ({
window.removeEventListener("blur", handleSubmit);
window.removeEventListener("beforeunload", handleSubmit);
unbindUpdate();
unbindOnScroll();
editable.remove();
};
@ -619,10 +628,29 @@ export const textWysiwyg = ({
});
};
const temporarilyDisableSubmit = () => {
editable.onblur = null;
window.addEventListener("pointerup", bindBlurEvent);
// handle edge-case where pointerup doesn't fire e.g. due to user
// alt-tabbing away
window.addEventListener("blur", handleSubmit);
};
// prevent blur when changing properties from the menu
const onPointerDown = (event: MouseEvent) => {
const target = event?.target;
// panning canvas
if (event.button === POINTER_BUTTON.WHEEL) {
// trying to pan by clicking inside text area itself -> handle here
if (target instanceof HTMLTextAreaElement) {
event.preventDefault();
app.handleCanvasPanUsingWheelOrSpaceDrag(event);
}
temporarilyDisableSubmit();
return;
}
const isPropertiesTrigger =
target instanceof HTMLElement &&
target.classList.contains("properties-trigger");
@ -630,17 +658,14 @@ export const textWysiwyg = ({
if (
((event.target instanceof HTMLElement ||
event.target instanceof SVGElement) &&
event.target.closest(`.${CLASSES.SHAPE_ACTIONS_MENU}`) &&
event.target.closest(
`.${CLASSES.SHAPE_ACTIONS_MENU}, .${CLASSES.ZOOM_ACTIONS}`,
) &&
!isWritableElement(event.target)) ||
isPropertiesTrigger
) {
editable.onblur = null;
window.addEventListener("pointerup", bindBlurEvent);
// handle edge-case where pointerup doesn't fire e.g. due to user
// alt-tabbing away
window.addEventListener("blur", handleSubmit);
temporarilyDisableSubmit();
} else if (
event.target instanceof HTMLElement &&
event.target instanceof HTMLCanvasElement &&
// Vitest simply ignores stopPropagation, capture-mode, or rAF
// so without introducing crazier hacks, nothing we can do
@ -659,7 +684,7 @@ export const textWysiwyg = ({
};
// handle updates of textElement properties of editing element
const unbindUpdate = Scene.getScene(element)!.onUpdate(() => {
const unbindUpdate = app.scene.onUpdate(() => {
updateWysiwygStyle();
const isPopupOpened = !!document.activeElement?.closest(
".properties-content",
@ -669,6 +694,10 @@ export const textWysiwyg = ({
}
});
const unbindOnScroll = app.onScrollChangeEmitter.on(() => {
updateWysiwygStyle();
});
// ---------------------------------------------------------------------------
let isDestroyed = false;
@ -699,10 +728,6 @@ export const textWysiwyg = ({
requestAnimationFrame(() => {
window.addEventListener("pointerdown", onPointerDown, { capture: true });
});
window.addEventListener("wheel", stopEvent, {
passive: false,
capture: true,
});
window.addEventListener("beforeunload", handleSubmit);
excalidrawContainer
?.querySelector(".excalidraw-textEditorContainer")!