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.
This commit is contained in:
Felix Zedén Yverås 2024-07-17 21:28:05 +02:00
parent 47fce123d3
commit 3659ba9143
5 changed files with 34 additions and 1 deletions

View File

@ -85,6 +85,11 @@ export default function Note({ data, onPointerDown }) {
<g <g
onPointerEnter={(e) => e.isPrimary && setHovered(true)} onPointerEnter={(e) => e.isPrimary && setHovered(true)}
onPointerLeave={(e) => e.isPrimary && setHovered(false)} 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);
}}
> >
<path <path
d={`M${data.x + fold} ${data.y} L${data.x + w - r} ${ d={`M${data.x + fold} ${data.y} L${data.x + w - r} ${

View File

@ -280,6 +280,11 @@ export default function Table(props) {
setHoveredField(-1); 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);
}}
> >
<div <div
className={`${ className={`${

View File

@ -1540,6 +1540,11 @@ export default function ControlPanel({
className="text-xl me-1" className="text-xl me-1"
onPointerEnter={(e) => e.isPrimary && setShowEditName(true)} onPointerEnter={(e) => e.isPrimary && setShowEditName(true)}
onPointerLeave={(e) => e.isPrimary && setShowEditName(false)} 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)} onClick={() => setModal(MODAL.RENAME)}
> >
{window.name.split(" ")[0] === "t" ? "Templates/" : "Diagrams/"} {window.name.split(" ")[0] === "t" ? "Templates/" : "Diagrams/"}

View File

@ -24,7 +24,15 @@ function Table({ table, grab }) {
width={tableWidth} width={tableWidth}
height={height} height={height}
className="drop-shadow-lg rounded-md cursor-move" 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)} onPointerEnter={(e) => e.isPrimary && setIsHovered(true)}
onPointerLeave={(e) => e.isPrimary && setIsHovered(false)} onPointerLeave={(e) => e.isPrimary && setIsHovered(false)}
> >
@ -48,6 +56,11 @@ function Table({ table, grab }) {
} h-[36px] px-2 py-1 flex justify-between`} } h-[36px] px-2 py-1 flex justify-between`}
onPointerEnter={(e) => e.isPrimary && setHoveredField(i)} onPointerEnter={(e) => e.isPrimary && setHoveredField(i)}
onPointerLeave={(e) => e.isPrimary && setHoveredField(-1)} 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);
}}
> >
<div className={hoveredField === i ? "text-zinc-500" : ""}> <div className={hoveredField === i ? "text-zinc-500" : ""}>
<button <button

View File

@ -352,6 +352,11 @@ export default function WorkSpace() {
onPointerUp={(e) => e.isPrimary && setResize(false)} onPointerUp={(e) => e.isPrimary && setResize(false)}
onPointerLeave={(e) => e.isPrimary && setResize(false)} onPointerLeave={(e) => e.isPrimary && setResize(false)}
onPointerMove={(e) => e.isPrimary && handleResize(e)} onPointerMove={(e) => e.isPrimary && handleResize(e)}
onPointerDown={(e) => {
// Required for onPointerLeave to trigger when a touch pointer leaves
// https://stackoverflow.com/a/70976017/1137077
e.target.releasePointerCapture(e.pointerId);
}}
> >
{layout.sidebar && ( {layout.sidebar && (
<SidePanel resize={resize} setResize={setResize} width={width} /> <SidePanel resize={resize} setResize={setResize} width={width} />