Skip to content

Commit 8484a04

Browse files
authored
Edge case bug fix: Account for pointer resize events near edge of window/iframe (#530)
Resolves #368
1 parent abb0812 commit 8484a04

File tree

13 files changed

+285
-179
lines changed

13 files changed

+285
-179
lines changed

integrations/vite/src/index.css

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,3 +17,7 @@
1717
@apply text-blue-600 hover:text-pink-400 visited:text-blue-900;
1818
}
1919
}
20+
21+
#root {
22+
height: 100vh;
23+
}

integrations/vite/src/main.tsx

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,20 @@
11
import { StrictMode } from "react";
22
import { createRoot } from "react-dom/client";
3-
import { BrowserRouter, Routes, Route } from "react-router";
4-
import { Home } from "./routes/Home";
5-
import { Encoder } from "./routes/Encoder";
3+
import { BrowserRouter, Route, Routes } from "react-router";
64
import "./index.css";
75
import { Decoder } from "./routes/Decoder";
6+
import { Edges } from "./routes/Edges";
7+
import { Encoder } from "./routes/Encoder";
8+
import { Home } from "./routes/Home";
89

910
createRoot(document.getElementById("root")!).render(
1011
<StrictMode>
1112
<BrowserRouter>
1213
<Routes>
1314
<Route path="/" element={<Home />} />
14-
<Route path="/e2e/encoder" element={<Encoder />} />
1515
<Route path="/e2e/decoder/:encoded" element={<Decoder />} />
16+
<Route path="/e2e/edges" element={<Edges />} />
17+
<Route path="/e2e/encoder" element={<Encoder />} />
1618
</Routes>
1719
</BrowserRouter>
1820
</StrictMode>
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
import { useSearchParams } from "react-router";
2+
import { Group } from "../components/Group";
3+
import { Panel } from "../components/Panel";
4+
import { Separator } from "../components/Separator";
5+
import { Box } from "../../../../src/components/Box";
6+
7+
export function Edges() {
8+
const [params] = useSearchParams();
9+
10+
if (params.has("iframe")) {
11+
return (
12+
<Group className="h-full bg-slate-900">
13+
<Panel />
14+
<Separator />
15+
<Panel />
16+
</Group>
17+
);
18+
}
19+
20+
return (
21+
<Box className="h-full" direction="row">
22+
<div className="w-[50%]">Left</div>
23+
<iframe
24+
className="w-[50%]"
25+
sandbox="allow-scripts"
26+
src="http://localhost:3012/e2e/edges?iframe"
27+
/>
28+
</Box>
29+
);
30+
}

integrations/vite/src/routes/Home.tsx

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,9 @@ export function Home() {
99
<li>
1010
<Link to="/e2e/dynamic">e2e: dynamic</Link>
1111
</li>
12+
<li>
13+
<Link to="/e2e/edges">e2e: edges</Link>
14+
</li>
1215
</ul>
1316
);
1417
}

integrations/vite/vite.config.ts

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,5 +4,8 @@ import react from "@vitejs/plugin-react";
44

55
// https://vite.dev/config/
66
export default defineConfig({
7-
plugins: [react(), tailwindcss()]
7+
plugins: [react(), tailwindcss()],
8+
server: {
9+
cors: true
10+
}
811
});
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
import { read } from "../mutableState";
2+
import { updateActiveHitRegions } from "../utils/updateActiveHitRegion";
3+
4+
export function onGroupPointerLeave(event: PointerEvent) {
5+
if (event.defaultPrevented) {
6+
return;
7+
} else if (event.relatedTarget !== null) {
8+
return;
9+
}
10+
11+
const { interactionState, mountedGroups } = read();
12+
13+
// The "pointerleave" event is not reliably triggered when the pointer exits a window or iframe
14+
// To account for this, we listen for "pointerleave" events on the Group element itself
15+
switch (interactionState.state) {
16+
case "active": {
17+
interactionState.hitRegions.forEach((hitRegion) => {
18+
if (event.currentTarget === hitRegion.group.element) {
19+
updateActiveHitRegions({
20+
event,
21+
hitRegions: interactionState.hitRegions,
22+
initialLayoutMap: interactionState.initialLayoutMap,
23+
mountedGroups
24+
});
25+
}
26+
});
27+
}
28+
}
29+
}

lib/global/event-handlers/onPointerMove.ts

Lines changed: 0 additions & 157 deletions
This file was deleted.

lib/global/event-handlers/onKeyDown.ts renamed to lib/global/event-handlers/onWindowKeyDown.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ import { adjustLayoutForSeparator } from "../utils/adjustLayoutForSeparator";
33
import { findSeparatorGroup } from "../utils/findSeparatorGroup";
44
import { getMountedGroup } from "../utils/getMountedGroup";
55

6-
export function onKeyDown(event: KeyboardEvent) {
6+
export function onWindowKeyDown(event: KeyboardEvent) {
77
if (event.defaultPrevented) {
88
return;
99
}

lib/global/event-handlers/onPointerDown.ts renamed to lib/global/event-handlers/onWindowPointerDown.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ import type { RegisteredSeparator } from "../../components/separator/types";
44
import { read, update } from "../mutableState";
55
import { findMatchingHitRegions } from "../utils/findMatchingHitRegions";
66

7-
export function onPointerDown(event: PointerEvent) {
7+
export function onWindowPointerDown(event: PointerEvent) {
88
if (event.defaultPrevented) {
99
return;
1010
}
Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
import { updateCursorStyle } from "../cursor/updateCursorStyle";
2+
import { read, update } from "../mutableState";
3+
import { findMatchingHitRegions } from "../utils/findMatchingHitRegions";
4+
import { updateActiveHitRegions } from "../utils/updateActiveHitRegion";
5+
6+
export function onWindowPointerMove(event: PointerEvent) {
7+
if (event.defaultPrevented) {
8+
return;
9+
}
10+
11+
const { interactionState, mountedGroups } = read();
12+
13+
switch (interactionState.state) {
14+
case "active": {
15+
// Edge case (see #340)
16+
// Detect when the pointer has been released outside an iframe on a different domain
17+
if (
18+
// Skip this check for "pointerleave" events, else Firefox triggers a false positive (see #514)
19+
event.type !== "pointerleave" &&
20+
event.buttons === 0
21+
) {
22+
update((prevState) =>
23+
prevState.interactionState.state === "inactive"
24+
? prevState
25+
: {
26+
cursorFlags: 0,
27+
interactionState: {
28+
state: "inactive"
29+
}
30+
}
31+
);
32+
33+
return;
34+
}
35+
36+
updateActiveHitRegions({
37+
event,
38+
hitRegions: interactionState.hitRegions,
39+
initialLayoutMap: interactionState.initialLayoutMap,
40+
mountedGroups,
41+
pointerDownAtPoint: interactionState.pointerDownAtPoint
42+
});
43+
break;
44+
}
45+
default: {
46+
// Update HitRegions if a drag has not been started
47+
const hitRegions = findMatchingHitRegions(event, mountedGroups);
48+
49+
if (hitRegions.length === 0) {
50+
if (interactionState.state !== "inactive") {
51+
update({
52+
interactionState: { state: "inactive" }
53+
});
54+
}
55+
} else {
56+
update({
57+
interactionState: {
58+
hitRegions,
59+
state: "hover"
60+
}
61+
});
62+
}
63+
64+
updateCursorStyle();
65+
break;
66+
}
67+
}
68+
}

0 commit comments

Comments
 (0)