mirror of
https://github.com/GraphiteEditor/Graphite.git
synced 2026-09-30 02:38:11 +08:00
Fix frontend HMR so the page doesn't break upon saving TS files and restructure frontend architecture (#3871)
* Clean up component setup/tear-down side effects * Clean up more component setup/tear-down side effects * Remove nonfunctional debouncer * Clean up even more component setup/tear-down side effects * Reuse backend state * Fix HMR for IO Managers and for State Providers * Rename IO Managers -> Managers and State Providers -> Stores * Restructure and partially flatten managers/stores * Code review fixes * Review fixes
This commit is contained in:
@@ -4,7 +4,7 @@
|
||||
import { evaluateMathExpression, isPlatformNative } from "@graphite/../wasm/pkg/graphite_wasm";
|
||||
import type { NumberInputMode, NumberInputIncrementBehavior, ActionShortcut } from "@graphite/../wasm/pkg/graphite_wasm";
|
||||
import type { Editor } from "@graphite/editor";
|
||||
import { PRESS_REPEAT_DELAY_MS, PRESS_REPEAT_INTERVAL_MS } from "@graphite/io-managers/input";
|
||||
import { PRESS_REPEAT_DELAY_MS, PRESS_REPEAT_INTERVAL_MS } from "@graphite/managers/input";
|
||||
import { browserVersion } from "@graphite/utility-functions/platform";
|
||||
|
||||
import { preventEscapeClosingParentFloatingMenu } from "@graphite/components/layout/FloatingMenu.svelte";
|
||||
@@ -87,6 +87,12 @@
|
||||
let shiftKeyDown = false;
|
||||
// Track whether the Ctrl key is currently held down.
|
||||
let ctrlKeyDown = false;
|
||||
// Cleanup function for active drag interactions, called on destroy to prevent leaked listeners
|
||||
let activeDragCleanup: (() => void) | undefined;
|
||||
// Track the slider abort state for cleanup on destroy
|
||||
let sliderResetAbortHandler: (() => void) | undefined;
|
||||
let sliderAbortTimeout1: ReturnType<typeof setTimeout> | undefined;
|
||||
let sliderAbortTimeout2: ReturnType<typeof setTimeout> | undefined;
|
||||
|
||||
$: watchValue(value, unit);
|
||||
$: sliderStepValue = isInteger ? (step === undefined ? 1 : step) : "any";
|
||||
@@ -107,10 +113,31 @@
|
||||
addEventListener("mousemove", trackShiftAndCtrl);
|
||||
});
|
||||
onDestroy(() => {
|
||||
clearTimeout(repeatTimeout);
|
||||
clearTimeout(sliderAbortTimeout1);
|
||||
clearTimeout(sliderAbortTimeout2);
|
||||
|
||||
activeDragCleanup?.();
|
||||
|
||||
// Exit pointer lock if active (non-Safari path)
|
||||
if (document.pointerLockElement) document.exitPointerLock();
|
||||
|
||||
// Remove Safari cursor-hidden workaround class if present
|
||||
const isSafari = browserVersion().toLowerCase().includes("safari");
|
||||
if (isSafari) document.body.classList.remove("cursor-hidden");
|
||||
|
||||
// Clean up any listeners related to tracking the Shift and Ctrl keys
|
||||
removeEventListener("keydown", trackShiftAndCtrl);
|
||||
removeEventListener("keyup", trackShiftAndCtrl);
|
||||
removeEventListener("mousemove", trackShiftAndCtrl);
|
||||
clearTimeout(repeatTimeout);
|
||||
|
||||
// Clean up any slider-related listeners that may be active
|
||||
removeEventListener("mousedown", sliderAbortFromMousedown);
|
||||
removeEventListener("keydown", sliderAbortFromMousedown);
|
||||
removeEventListener("pointermove", sliderAbortFromDragging);
|
||||
removeEventListener("keydown", sliderAbortFromDragging);
|
||||
removeEventListener("keydown", incrementPressAbort);
|
||||
if (sliderResetAbortHandler) removeEventListener("pointerup", sliderResetAbortHandler);
|
||||
});
|
||||
|
||||
// ===============================
|
||||
@@ -297,7 +324,7 @@
|
||||
pressingArrow = false;
|
||||
clearTimeout(repeatTimeout);
|
||||
updateValue(initialValueBeforeDragging);
|
||||
removeEventListener("keydown", onIncrementPointerUp);
|
||||
removeEventListener("keydown", incrementPressAbort);
|
||||
}
|
||||
|
||||
// =======================================
|
||||
@@ -333,10 +360,9 @@
|
||||
alreadyActedGuard = true;
|
||||
|
||||
isDragging = true;
|
||||
beginDrag(e);
|
||||
|
||||
removeEventListener("pointermove", onMove);
|
||||
removeEventListener("pointerup", onUp);
|
||||
activeDragCleanup?.();
|
||||
beginDrag(e);
|
||||
};
|
||||
// If it's a mouseup, we'll begin editing the text field.
|
||||
const onUp = () => {
|
||||
@@ -346,11 +372,15 @@
|
||||
isDragging = false;
|
||||
self?.focus();
|
||||
|
||||
removeEventListener("pointermove", onMove);
|
||||
removeEventListener("pointerup", onUp);
|
||||
activeDragCleanup?.();
|
||||
};
|
||||
addEventListener("pointermove", onMove);
|
||||
addEventListener("pointerup", onUp);
|
||||
activeDragCleanup = () => {
|
||||
removeEventListener("pointermove", onMove);
|
||||
removeEventListener("pointerup", onUp);
|
||||
activeDragCleanup = undefined;
|
||||
};
|
||||
}
|
||||
|
||||
function beginDrag(e: PointerEvent) {
|
||||
@@ -449,16 +479,20 @@
|
||||
cumulativeDragDelta = 0;
|
||||
|
||||
// Clean up the event listeners.
|
||||
removeEventListener("pointerup", pointerUp);
|
||||
removeEventListener("pointermove", pointerMove);
|
||||
removeEventListener("pointerlockmove", pointerLockMove);
|
||||
if (usePointerLock) document.removeEventListener("pointerlockchange", pointerLockChange);
|
||||
activeDragCleanup?.();
|
||||
};
|
||||
|
||||
addEventListener("pointerup", pointerUp);
|
||||
addEventListener("pointermove", pointerMove);
|
||||
addEventListener("pointerlockmove", pointerLockMove);
|
||||
if (usePointerLock) document.addEventListener("pointerlockchange", pointerLockChange);
|
||||
activeDragCleanup = () => {
|
||||
removeEventListener("pointerup", pointerUp);
|
||||
removeEventListener("pointermove", pointerMove);
|
||||
removeEventListener("pointerlockmove", pointerLockMove);
|
||||
if (usePointerLock) document.removeEventListener("pointerlockchange", pointerLockChange);
|
||||
activeDragCleanup = undefined;
|
||||
};
|
||||
}
|
||||
|
||||
function pointerLockMoveUpdate(delta: number, slow: boolean, snapping: boolean, initialValue: number) {
|
||||
@@ -657,7 +691,7 @@
|
||||
|
||||
// End the user's drag by instantaneously disabling and re-enabling the range input element
|
||||
if (inputRangeElement) inputRangeElement.disabled = true;
|
||||
setTimeout(() => {
|
||||
sliderAbortTimeout1 = setTimeout(() => {
|
||||
if (inputRangeElement) inputRangeElement.disabled = false;
|
||||
}, 0);
|
||||
|
||||
@@ -680,11 +714,13 @@
|
||||
// dragging the slider, hitting Escape, then releasing the mouse button. This results in being transferred by `onSliderInput()` to the
|
||||
// "Deciding" state when we should remain in the "Ready" state as set here. (For debugging, this can be visualized in CSS by
|
||||
// recoloring the fake slider handle, which is shown in the "Deciding" state.)
|
||||
setTimeout(() => (rangeSliderClickDragState = "Ready"), 0);
|
||||
sliderAbortTimeout2 = setTimeout(() => (rangeSliderClickDragState = "Ready"), 0);
|
||||
|
||||
// Clean up the event listener that was used to call this function.
|
||||
removeEventListener("pointerup", sliderResetAbort);
|
||||
sliderResetAbortHandler = undefined;
|
||||
};
|
||||
sliderResetAbortHandler = sliderResetAbort;
|
||||
addEventListener("pointerup", sliderResetAbort);
|
||||
|
||||
// Clean up the event listeners that were for tracking an abort while dragging the slider, now that we're no longer dragging it.
|
||||
|
||||
Reference in New Issue
Block a user