mirror of
https://github.com/storytold/artcraft.git
synced 2026-11-04 01:18:34 +00:00
Revert "Modal: drop transform from open animation so clicks don't shift targets"
This reverts commit 4167bf22ce.
This commit is contained in:
1 parent
4167bf22ce
commit
989d77f520
2 files changed
+86
-13
No files matched your search
@@ -123,6 +123,76 @@ export const PageEditor = () => {
|
||||
}
|
||||
};
|
||||
|
||||
// TEMP debug: trace click / mousedown / pointerdown events at every
|
||||
// listenable phase so we can locate whatever is silencing the
|
||||
// upload-modal file-picker click. Remove once the bug is fixed.
|
||||
useEffect(() => {
|
||||
const describe = (t: EventTarget | null): string => {
|
||||
if (!(t instanceof HTMLElement)) return String(t);
|
||||
const id = t.id ? `#${t.id}` : "";
|
||||
const cls =
|
||||
typeof t.className === "string" && t.className
|
||||
? `.${t.className.split(/\s+/).slice(0, 2).join(".")}`
|
||||
: "";
|
||||
const label =
|
||||
t.closest("label")?.getAttribute("for") ??
|
||||
(t.closest("label") ? "(unnamed)" : null);
|
||||
return `${t.tagName.toLowerCase()}${id}${cls}${label ? ` [in label for=${label}]` : ""}`;
|
||||
};
|
||||
|
||||
const trace = (phase: string, type: string) => (e: Event) => {
|
||||
// eslint-disable-next-line no-console
|
||||
console.debug(
|
||||
`[click-trace] ${type} ${phase}`,
|
||||
{
|
||||
target: describe(e.target),
|
||||
currentTarget: describe(e.currentTarget),
|
||||
defaultPrevented: e.defaultPrevented,
|
||||
bubbles: e.bubbles,
|
||||
composedPath: (e as any).composedPath?.()?.slice(0, 6).map(describe),
|
||||
},
|
||||
);
|
||||
};
|
||||
|
||||
const types: Array<
|
||||
"click" | "mousedown" | "mouseup" | "pointerdown" | "pointerup"
|
||||
> = ["click", "mousedown", "mouseup", "pointerdown", "pointerup"];
|
||||
const handlers = types.flatMap((type) => {
|
||||
const cap = trace("CAPTURE", type);
|
||||
const bub = trace("BUBBLE", type);
|
||||
document.addEventListener(type, cap, true);
|
||||
document.addEventListener(type, bub, false);
|
||||
return [
|
||||
() => document.removeEventListener(type, cap, true),
|
||||
() => document.removeEventListener(type, bub, false),
|
||||
];
|
||||
});
|
||||
|
||||
// Monkey-patch setPointerCapture to see who's grabbing the pointer
|
||||
// (a mid-click capture explains the missing click — mouseup fires
|
||||
// on the captured element instead of the original mousedown target).
|
||||
const proto = Element.prototype as any;
|
||||
const originalSet = proto.setPointerCapture;
|
||||
proto.setPointerCapture = function (id: number) {
|
||||
// eslint-disable-next-line no-console
|
||||
console.debug("[click-trace] setPointerCapture", {
|
||||
on: describe(this),
|
||||
pointerId: id,
|
||||
stack: new Error().stack?.split("\n").slice(1, 6).join("\n"),
|
||||
});
|
||||
return originalSet.call(this, id);
|
||||
};
|
||||
|
||||
// eslint-disable-next-line no-console
|
||||
console.debug("[click-trace] installed");
|
||||
return () => {
|
||||
handlers.forEach((off) => off());
|
||||
proto.setPointerCapture = originalSet;
|
||||
// eslint-disable-next-line no-console
|
||||
console.debug("[click-trace] removed");
|
||||
};
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
window.onbeforeunload = () => {
|
||||
return "You may have unsaved changes.";
|
||||
|
||||
@@ -229,19 +229,20 @@ export const Modal = ({
|
||||
// Track last non-expanded position
|
||||
const lastNonExpandedPosition = useRef<{ x: number; y: number } | null>(null);
|
||||
|
||||
// Animation transitions. Transform (translate / scale) is
|
||||
// intentionally omitted — when the React tree under the modal is
|
||||
// busy (e.g. the 3D scene is rendering), the spring takes long
|
||||
// enough that a quick click on a deep child (like the file
|
||||
// uploader's "Upload a file" link) lands its mousedown on one
|
||||
// element and its mouseup on another after the modal shifts under
|
||||
// the cursor, breaking WebView2's strict same-target rule for
|
||||
// synthesizing `click`. The file picker then never opens. Opacity
|
||||
// alone gives the fade without moving any clickable surface.
|
||||
// Animation transitions
|
||||
const transitions = useTransition(isOpen, {
|
||||
from: { opacity: 0 },
|
||||
enter: { opacity: 1 },
|
||||
leave: { opacity: 0 },
|
||||
from: {
|
||||
opacity: 0,
|
||||
transform: "scale(0.95) translateY(-10px)",
|
||||
},
|
||||
enter: {
|
||||
opacity: 1,
|
||||
transform: "scale(1) translateY(0px)",
|
||||
},
|
||||
leave: {
|
||||
opacity: 0,
|
||||
transform: "scale(0.95) translateY(10px)",
|
||||
},
|
||||
config: {
|
||||
tension: 300,
|
||||
friction: 30,
|
||||
@@ -985,7 +986,9 @@ export const Modal = ({
|
||||
style={{
|
||||
...getModalStyle(),
|
||||
opacity: styles.opacity,
|
||||
willChange: "opacity",
|
||||
transform: styles.transform,
|
||||
transformOrigin: "center center",
|
||||
willChange: "transform, opacity", // Optimize for animations
|
||||
outline: "none",
|
||||
}}
|
||||
>
|
||||
|
||||
Reference in new issue
Block a user