mirror of
https://github.com/wavetermdev/backup.git
synced 2026-08-05 13:57:07 -07:00
Make the debounced node inner rect hook more reliable (#350)
Rather than try to track the transition state, which was proving unreliable, I am just directly tracking the node state and determining whether to debounce the inner rect based on whether the user has the prefers-reduced-motion setting or query, whether the node is resizing, and whether it's currently magnified. I'm then using the actual animation time setting to determine how long to debounce.
This commit is contained in:
+2
-11
@@ -252,20 +252,11 @@ const AppKeyHandlers = () => {
|
||||
};
|
||||
|
||||
const AppInner = () => {
|
||||
const [prefersReducedMotion, setPrefersReducedMotion] = React.useState(false);
|
||||
const prefersReducedMotionSetting = jotai.useAtomValue(atoms.reducedMotionPreferenceAtom);
|
||||
const prefersReducedMotion = jotai.useAtomValue(atoms.prefersReducedMotionAtom);
|
||||
const client = jotai.useAtomValue(atoms.client);
|
||||
const windowData = jotai.useAtomValue(atoms.waveWindow);
|
||||
const isFullScreen = jotai.useAtomValue(atoms.isFullScreen);
|
||||
|
||||
React.useEffect(() => {
|
||||
const reducedMotionQuery = window.matchMedia("(prefers-reduced-motion: reduce)");
|
||||
setPrefersReducedMotion(!reducedMotionQuery || reducedMotionQuery.matches);
|
||||
reducedMotionQuery.addEventListener("change", () => {
|
||||
setPrefersReducedMotion(reducedMotionQuery.matches);
|
||||
});
|
||||
}, []);
|
||||
|
||||
if (client == null || windowData == null) {
|
||||
return (
|
||||
<div className="mainapp">
|
||||
@@ -279,7 +270,7 @@ const AppInner = () => {
|
||||
<div
|
||||
className={clsx("mainapp", PLATFORM, {
|
||||
fullscreen: isFullScreen,
|
||||
"prefers-reduced-motion": prefersReducedMotion || prefersReducedMotionSetting,
|
||||
"prefers-reduced-motion": prefersReducedMotion,
|
||||
})}
|
||||
onContextMenu={handleContextMenu}
|
||||
>
|
||||
|
||||
@@ -124,7 +124,26 @@ function initGlobalAtoms(initOpts: GlobalInitOptions) {
|
||||
} catch (_) {
|
||||
// do nothing
|
||||
}
|
||||
const reducedMotionPreferenceAtom = jotai.atom((get) => get(settingsAtom)?.["window:reducedmotion"]);
|
||||
|
||||
const reducedMotionSettingAtom = jotai.atom((get) => get(settingsAtom)?.["window:reducedmotion"]);
|
||||
const reducedMotionSystemPreferenceAtom = jotai.atom(false);
|
||||
|
||||
// Composite of the prefers-reduced-motion media query and the window:reducedmotion user setting.
|
||||
const prefersReducedMotionAtom = jotai.atom((get) => {
|
||||
const reducedMotionSetting = get(reducedMotionSettingAtom);
|
||||
const reducedMotionSystemPreference = get(reducedMotionSystemPreferenceAtom);
|
||||
return reducedMotionSetting || reducedMotionSystemPreference;
|
||||
});
|
||||
|
||||
// Set up a handler for changes to the prefers-reduced-motion media query.
|
||||
if (globalThis.window != null) {
|
||||
const reducedMotionQuery = window.matchMedia("(prefers-reduced-motion: reduce)");
|
||||
globalStore.set(reducedMotionSystemPreferenceAtom, !reducedMotionQuery || reducedMotionQuery.matches);
|
||||
reducedMotionQuery?.addEventListener("change", () => {
|
||||
globalStore.set(reducedMotionSystemPreferenceAtom, reducedMotionQuery.matches);
|
||||
});
|
||||
}
|
||||
|
||||
const typeAheadModalAtom = jotai.atom({});
|
||||
const modalOpen = jotai.atom(false);
|
||||
const allConnStatusAtom = jotai.atom<ConnStatus[]>((get) => {
|
||||
@@ -146,7 +165,7 @@ function initGlobalAtoms(initOpts: GlobalInitOptions) {
|
||||
isFullScreen: isFullScreenAtom,
|
||||
controlShiftDelayAtom,
|
||||
updaterStatusAtom,
|
||||
reducedMotionPreferenceAtom,
|
||||
prefersReducedMotionAtom,
|
||||
typeAheadModalAtom,
|
||||
modalOpen,
|
||||
allConnStatus: allConnStatusAtom,
|
||||
|
||||
@@ -773,10 +773,12 @@ export class LayoutModel {
|
||||
return isFocused;
|
||||
}),
|
||||
numLeafs: this.numLeafs,
|
||||
isResizing: this.isResizing,
|
||||
isMagnified: atom((get) => {
|
||||
const treeState = get(this.treeStateAtom);
|
||||
return treeState.magnifiedNodeId === nodeid;
|
||||
}),
|
||||
animationTimeS: this.animationTimeS,
|
||||
ready: this.ready,
|
||||
disablePointerEvents: this.activeDrag,
|
||||
onClose: async () => await this.closeNode(nodeid),
|
||||
|
||||
@@ -5,7 +5,8 @@ import { atoms, globalStore, WOS } from "@/app/store/global";
|
||||
import { fireAndForget } from "@/util/util";
|
||||
import useResizeObserver from "@react-hook/resize-observer";
|
||||
import { Atom, useAtomValue } from "jotai";
|
||||
import { CSSProperties, useEffect, useLayoutEffect, useState } from "react";
|
||||
import { CSSProperties, useCallback, useEffect, useLayoutEffect, useState } from "react";
|
||||
import { debounce } from "throttle-debounce";
|
||||
import { withLayoutTreeStateAtomFromTab } from "./layoutAtom";
|
||||
import { LayoutModel } from "./layoutModel";
|
||||
import { LayoutNode, NodeModel, TileLayoutContents } from "./types";
|
||||
@@ -67,32 +68,26 @@ export function useNodeModel(layoutModel: LayoutModel, layoutNode: LayoutNode):
|
||||
|
||||
export function useDebouncedNodeInnerRect(nodeModel: NodeModel): CSSProperties {
|
||||
const nodeInnerRect = useAtomValue(nodeModel.innerRect);
|
||||
const animationTimeS = useAtomValue(nodeModel.animationTimeS);
|
||||
const isMagnified = useAtomValue(nodeModel.isMagnified);
|
||||
const isResizing = useAtomValue(nodeModel.isResizing);
|
||||
const prefersReducedMotion = useAtomValue(atoms.prefersReducedMotionAtom);
|
||||
const [innerRect, setInnerRect] = useState<CSSProperties>();
|
||||
const [isTransitioning, setIsTransitioning] = useState(false);
|
||||
|
||||
useEffect(() => {
|
||||
const onTransitionStart = () => {
|
||||
setIsTransitioning(true);
|
||||
};
|
||||
const onTransitionEnd = () => {
|
||||
setIsTransitioning(false);
|
||||
};
|
||||
if (nodeModel.displayContainerRef.current) {
|
||||
nodeModel.displayContainerRef.current.addEventListener("transitionstart", onTransitionStart);
|
||||
nodeModel.displayContainerRef.current.addEventListener("transitionend", onTransitionEnd);
|
||||
}
|
||||
|
||||
return () => {
|
||||
nodeModel.displayContainerRef.current?.removeEventListener("transitionstart", onTransitionStart);
|
||||
nodeModel.displayContainerRef.current?.removeEventListener("transitionend", onTransitionEnd);
|
||||
};
|
||||
}, [nodeModel]);
|
||||
const setInnerRectDebounced = useCallback(
|
||||
debounce(animationTimeS * 1000, (nodeInnerRect) => {
|
||||
setInnerRect(nodeInnerRect);
|
||||
}),
|
||||
[animationTimeS]
|
||||
);
|
||||
|
||||
useLayoutEffect(() => {
|
||||
if (!isTransitioning) {
|
||||
if (prefersReducedMotion || isMagnified || isResizing) {
|
||||
setInnerRect(nodeInnerRect);
|
||||
} else {
|
||||
setInnerRectDebounced(nodeInnerRect);
|
||||
}
|
||||
}, [nodeInnerRect, isTransitioning]);
|
||||
}, [nodeInnerRect]);
|
||||
|
||||
return innerRect;
|
||||
}
|
||||
|
||||
@@ -335,6 +335,8 @@ export interface NodeModel {
|
||||
numLeafs: Atom<number>;
|
||||
nodeId: string;
|
||||
blockId: string;
|
||||
animationTimeS: Atom<number>;
|
||||
isResizing: Atom<boolean>;
|
||||
isFocused: Atom<boolean>;
|
||||
isMagnified: Atom<boolean>;
|
||||
ready: Atom<boolean>;
|
||||
|
||||
Vendored
+1
-1
@@ -18,7 +18,7 @@ declare global {
|
||||
activeTabId: jotai.Atom<string>; // derrived from windowDataAtom
|
||||
isFullScreen: jotai.PrimitiveAtom<boolean>;
|
||||
controlShiftDelayAtom: jotai.PrimitiveAtom<boolean>;
|
||||
reducedMotionPreferenceAtom: jotai.Atom<boolean>;
|
||||
prefersReducedMotionAtom: jotai.Atom<boolean>;
|
||||
updaterStatusAtom: jotai.PrimitiveAtom<UpdaterStatus>;
|
||||
typeAheadModalAtom: jotai.PrimitiveAtom<TypeAheadModalType>;
|
||||
modalOpen: jotai.PrimitiveAtom<boolean>;
|
||||
|
||||
Reference in New Issue
Block a user