mirror of
https://github.com/GraphiteEditor/Graphite.git
synced 2026-09-29 08:08:12 +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,6 @@
|
||||
import type { LayoutTarget, Widget, WidgetInstance } from "@graphite/../wasm/pkg/graphite_wasm";
|
||||
import type { Editor } from "@graphite/editor";
|
||||
import { parseFillChoice } from "@graphite/utility-functions/colors";
|
||||
import { debouncer } from "@graphite/utility-functions/debounce";
|
||||
|
||||
import NodeCatalog from "@graphite/components/floating-menus/NodeCatalog.svelte";
|
||||
import BreadcrumbTrailButtons from "@graphite/components/widgets/buttons/BreadcrumbTrailButtons.svelte";
|
||||
@@ -137,7 +136,7 @@
|
||||
getProps: (props, index) => ({
|
||||
...props,
|
||||
$$events: {
|
||||
value: (e: CustomEvent) => debouncer((value: unknown) => widgetValueCommitAndUpdate(index, value, false), { debounceTime: 120 }).debounceUpdateValue(e.detail),
|
||||
value: (e: CustomEvent) => widgetValueCommitAndUpdate(index, e.detail, false),
|
||||
},
|
||||
}),
|
||||
},
|
||||
@@ -202,7 +201,7 @@
|
||||
incrementCallbackIncrease: () => widgetValueCommitAndUpdate(index, "Increment", false),
|
||||
incrementCallbackDecrease: () => widgetValueCommitAndUpdate(index, "Decrement", false),
|
||||
$$events: {
|
||||
value: (e: CustomEvent) => debouncer((value: unknown) => widgetValueUpdate(index, value, true)).debounceUpdateValue(e.detail),
|
||||
value: (e: CustomEvent) => widgetValueUpdate(index, e.detail, true),
|
||||
startHistoryTransaction: () => widgetValueCommit(index, props.value),
|
||||
},
|
||||
}),
|
||||
|
||||
@@ -2,7 +2,6 @@
|
||||
import { createEventDispatcher } from "svelte";
|
||||
|
||||
import type { Curve, CurveManipulatorGroup, ActionShortcut } from "@graphite/../wasm/pkg/graphite_wasm";
|
||||
import { clamp } from "@graphite/utility-functions/math";
|
||||
|
||||
import LayoutRow from "@graphite/components/layout/LayoutRow.svelte";
|
||||
|
||||
@@ -185,6 +184,10 @@
|
||||
dAttribute = recalculateSvgPath();
|
||||
updateCurve();
|
||||
}
|
||||
|
||||
function clamp(value: number, min = 0, max = 1): number {
|
||||
return Math.max(min, Math.min(value, max));
|
||||
}
|
||||
</script>
|
||||
|
||||
<LayoutRow class="curve-input" {tooltipLabel} {tooltipDescription} {tooltipShortcut}>
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
<script lang="ts">
|
||||
import { createEventDispatcher } from "svelte";
|
||||
import { createEventDispatcher, onDestroy } from "svelte";
|
||||
|
||||
import { PRESS_REPEAT_DELAY_MS, PRESS_REPEAT_INTERVAL_MS, PRESS_REPEAT_INTERVAL_RAPID_MS } from "@graphite/io-managers/input";
|
||||
import { PRESS_REPEAT_DELAY_MS, PRESS_REPEAT_INTERVAL_MS, PRESS_REPEAT_INTERVAL_RAPID_MS } from "@graphite/managers/input";
|
||||
|
||||
const ARROW_CLICK_DISTANCE = 0.05;
|
||||
const ARROW_REPEAT_DISTANCE = 0.01;
|
||||
@@ -187,6 +187,10 @@
|
||||
if (e.key === "Escape") abortInteraction();
|
||||
}
|
||||
|
||||
onDestroy(() => {
|
||||
removeEvents();
|
||||
});
|
||||
|
||||
function addEvents() {
|
||||
window.addEventListener("pointerup", onPointerUp);
|
||||
window.addEventListener("pointermove", onPointerMove);
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
</script>
|
||||
|
||||
<script lang="ts">
|
||||
import { createEventDispatcher, onDestroy } from "svelte";
|
||||
import { createEventDispatcher, onMount, onDestroy } from "svelte";
|
||||
|
||||
import { evaluateGradientAtPosition } from "@graphite/../wasm/pkg/graphite_wasm";
|
||||
import type { Color, GradientStops } from "@graphite/../wasm/pkg/graphite_wasm";
|
||||
@@ -338,7 +338,9 @@
|
||||
document.removeEventListener("keydown", onKeyDown);
|
||||
}
|
||||
|
||||
document.addEventListener("keydown", deleteStop);
|
||||
onMount(() => {
|
||||
document.addEventListener("keydown", deleteStop);
|
||||
});
|
||||
onDestroy(() => {
|
||||
removeEvents();
|
||||
document.removeEventListener("keydown", deleteStop);
|
||||
|
||||
@@ -62,7 +62,10 @@
|
||||
}
|
||||
|
||||
onMount(() => watchForCheckbox(forCheckbox));
|
||||
onDestroy(() => watchForCheckbox(undefined));
|
||||
onDestroy(() => {
|
||||
handlePointerLeave();
|
||||
watchForCheckbox(undefined);
|
||||
});
|
||||
</script>
|
||||
|
||||
<label
|
||||
|
||||
Reference in New Issue
Block a user