From 3659ba914345f17d5e7ed0e5465c3b10e64637cb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Felix=20Zed=C3=A9n=20Yver=C3=A5s?= Date: Wed, 17 Jul 2024 21:28:05 +0200 Subject: [PATCH] fix: release pointer capture when using `onPointerLeave` events Certain input sources (such as touch) are "captured" when they press an element. This means the pointer is always considered "inside" the element by the browser, even when they visually are not. This caused some issues on mobile browsers where touch and stylus events could not connect table columns with each other. Just to be safe, I've added the required `releasePointerCapture` call everywhere `onPointerEnter` or `onPointerLeave` is used. --- src/components/EditorCanvas/Note.jsx | 5 +++++ src/components/EditorCanvas/Table.jsx | 5 +++++ src/components/EditorHeader/ControlPanel.jsx | 5 +++++ src/components/SimpleCanvas.jsx | 15 ++++++++++++++- src/components/Workspace.jsx | 5 +++++ 5 files changed, 34 insertions(+), 1 deletion(-) diff --git a/src/components/EditorCanvas/Note.jsx b/src/components/EditorCanvas/Note.jsx index 381682e..cff89ba 100644 --- a/src/components/EditorCanvas/Note.jsx +++ b/src/components/EditorCanvas/Note.jsx @@ -85,6 +85,11 @@ export default function Note({ data, onPointerDown }) { e.isPrimary && setHovered(true)} onPointerLeave={(e) => e.isPrimary && setHovered(false)} + onPointerDown={(e) => { + // Required for onPointerLeave to trigger when a touch pointer leaves + // https://stackoverflow.com/a/70976017/1137077 + e.target.releasePointerCapture(e.pointerId); + }} > { + // Required for onPointerLeave to trigger when a touch pointer leaves + // https://stackoverflow.com/a/70976017/1137077 + e.target.releasePointerCapture(e.pointerId); + }} >
e.isPrimary && setShowEditName(true)} onPointerLeave={(e) => e.isPrimary && setShowEditName(false)} + onPointerDown={(e) => { + // Required for onPointerLeave to trigger when a touch pointer leaves + // https://stackoverflow.com/a/70976017/1137077 + e.target.releasePointerCapture(e.pointerId); + }} onClick={() => setModal(MODAL.RENAME)} > {window.name.split(" ")[0] === "t" ? "Templates/" : "Diagrams/"} diff --git a/src/components/SimpleCanvas.jsx b/src/components/SimpleCanvas.jsx index 91a4be8..a274255 100644 --- a/src/components/SimpleCanvas.jsx +++ b/src/components/SimpleCanvas.jsx @@ -24,7 +24,15 @@ function Table({ table, grab }) { width={tableWidth} height={height} className="drop-shadow-lg rounded-md cursor-move" - onPointerDown={(e) => e.isPrimary && grab(e)} + onPointerDown={(e) => { + // Required for onPointerLeave to trigger when a touch pointer leaves + // https://stackoverflow.com/a/70976017/1137077 + e.target.releasePointerCapture(e.pointerId); + + if (!e.isPrimary) return; + + grab(e); + }} onPointerEnter={(e) => e.isPrimary && setIsHovered(true)} onPointerLeave={(e) => e.isPrimary && setIsHovered(false)} > @@ -48,6 +56,11 @@ function Table({ table, grab }) { } h-[36px] px-2 py-1 flex justify-between`} onPointerEnter={(e) => e.isPrimary && setHoveredField(i)} onPointerLeave={(e) => e.isPrimary && setHoveredField(-1)} + onPointerDown={(e) => { + // Required for onPointerLeave to trigger when a touch pointer leaves + // https://stackoverflow.com/a/70976017/1137077 + e.target.releasePointerCapture(e.pointerId); + }} >