From 08b13f971d51dcd875a81ed55d760585c892be2a Mon Sep 17 00:00:00 2001 From: David Luzar <5153846+dwelle@users.noreply.github.com> Date: Tue, 28 May 2024 16:18:02 +0200 Subject: [PATCH] fix: wysiwyg blur-submit on mobile (#8075) --- packages/excalidraw/element/textWysiwyg.tsx | 33 +++++++++++++++++---- 1 file changed, 28 insertions(+), 5 deletions(-) diff --git a/packages/excalidraw/element/textWysiwyg.tsx b/packages/excalidraw/element/textWysiwyg.tsx index 5dd438d66f..632759330d 100644 --- a/packages/excalidraw/element/textWysiwyg.tsx +++ b/packages/excalidraw/element/textWysiwyg.tsx @@ -493,6 +493,11 @@ export const textWysiwyg = ({ // so that we don't need to create separate a callback for event handlers let submittedViaKeyboard = false; const handleSubmit = () => { + // prevent double submit + if (isDestroyed) { + return; + } + isDestroyed = true; // cleanup must be run before onSubmit otherwise when app blurs the wysiwyg // it'd get stuck in an infinite loop of blur→onSubmit after we re-focus the // wysiwyg on update @@ -546,10 +551,6 @@ export const textWysiwyg = ({ }; const cleanup = () => { - if (isDestroyed) { - return; - } - isDestroyed = true; // remove events to ensure they don't late-fire editable.onblur = null; editable.oninput = null; @@ -641,6 +642,22 @@ export const textWysiwyg = ({ // handle edge-case where pointerup doesn't fire e.g. due to user // alt-tabbing away window.addEventListener("blur", handleSubmit); + } else if ( + event.target instanceof HTMLElement && + !event.target.contains(editable) && + // Vitest simply ignores stopPropagation, capture-mode, or rAF + // so without introducing crazier hacks, nothing we can do + !isTestEnv() + ) { + // On mobile, blur event doesn't seem to always fire correctly, + // so we want to also submit on pointerdown outside the wysiwyg. + // Done in the next frame to prevent pointerdown from creating a new text + // immediately (if tools locked) so that users on mobile have chance + // to submit first (to hide virtual keyboard). + // Note: revisit if we want to differ this behavior on Desktop + requestAnimationFrame(() => { + handleSubmit(); + }); } }; @@ -678,7 +695,13 @@ export const textWysiwyg = ({ window.addEventListener("resize", updateWysiwygStyle); } - window.addEventListener("pointerdown", onPointerDown); + editable.onpointerdown = (event) => event.stopPropagation(); + + // rAF (+ capture to by doubly sure) so we don't catch te pointerdown that + // triggered the wysiwyg + requestAnimationFrame(() => { + window.addEventListener("pointerdown", onPointerDown, { capture: true }); + }); window.addEventListener("wheel", stopEvent, { passive: false, capture: true,