feat: allows all interactions to be disabled

This commit is contained in:
Mark Mankarious
2023-08-08 09:32:00 +01:00
parent ad347817ff
commit 0e5ca5a442
5 changed files with 65 additions and 28 deletions

View File

@@ -14,7 +14,6 @@ import {
import { useSceneStore, SceneProvider } from 'src/stores/sceneStore';
import { GlobalStyles } from 'src/styles/GlobalStyles';
import { Renderer } from 'src/components/Renderer/Renderer';
import { sceneToSceneInput } from 'src/utils';
import { LabelContainer } from 'src/components/Node/LabelContainer';
import { useWindowUtils } from 'src/hooks/useWindowUtils';
import { ItemControlsManager } from './components/ItemControls/ItemControlsManager';
@@ -23,14 +22,20 @@ import { UiStateProvider, useUiStateStore } from './stores/uiStateStore';
interface Props {
initialScene: SceneInput & {
zoom?: number;
isToolbarVisible?: boolean;
};
interactionsEnabled?: boolean;
onSceneUpdated?: (scene: SceneInput, prevScene: SceneInput) => void;
width?: number | string;
height?: number | string;
}
const App = ({ initialScene, width, height = 500, onSceneUpdated }: Props) => {
const App = ({
initialScene,
width,
height = 500,
interactionsEnabled: interactionsEnabledProp = true,
onSceneUpdated
}: Props) => {
useWindowUtils();
const sceneActions = useSceneStore((state) => {
return state.actions;
@@ -38,19 +43,14 @@ const App = ({ initialScene, width, height = 500, onSceneUpdated }: Props) => {
const uiActions = useUiStateStore((state) => {
return state.actions;
});
const isToolbarVisible = useUiStateStore((state) => {
return state.isToolbarVisible;
const interactionsEnabled = useUiStateStore((state) => {
return state.interactionsEnabled;
});
useEffect(() => {
uiActions.setZoom(initialScene.zoom ?? 1);
uiActions.setToolbarVisibility(initialScene.isToolbarVisible ?? true);
}, [
initialScene.zoom,
initialScene.isToolbarVisible,
sceneActions,
uiActions
]);
uiActions.setInteractionsEnabled(interactionsEnabledProp);
}, [initialScene.zoom, interactionsEnabledProp, sceneActions, uiActions]);
useEffect(() => {
sceneActions.setScene(initialScene);
@@ -68,8 +68,8 @@ const App = ({ initialScene, width, height = 500, onSceneUpdated }: Props) => {
}}
>
<Renderer />
{isToolbarVisible && <ItemControlsManager />}
<ToolMenu />
<ItemControlsManager />
{interactionsEnabled && <ToolMenu />}
</Box>
</>
);

View File

@@ -18,6 +18,9 @@ export const Renderer = () => {
const scene = useSceneStore(({ nodes, connectors, groups }) => {
return { nodes, connectors, groups };
});
const interactionsEnabled = useUiStateStore((state) => {
return state.interactionsEnabled;
});
const icons = useSceneStore((state) => {
return state.icons;
});
@@ -36,7 +39,10 @@ export const Renderer = () => {
const { setRendererSize } = useUiStateStore((state) => {
return state.actions;
});
const { setElement } = useInteractionManager();
const {
setElement: setInteractionsElement,
setIsEnabled: setInteractionsEnabled
} = useInteractionManager();
const { observe, disconnect, size: rendererSize } = useResizeObserver();
const getNodesFromIds = useCallback(
@@ -58,12 +64,16 @@ export const Renderer = () => {
if (!containerRef.current) return;
observe(containerRef.current);
setElement(containerRef.current);
setInteractionsElement(containerRef.current);
return () => {
disconnect();
};
}, [setElement, observe, disconnect]);
}, [setInteractionsElement, observe, disconnect]);
useEffect(() => {
setInteractionsEnabled(interactionsEnabled);
}, [interactionsEnabled, setInteractionsEnabled]);
useEffect(() => {
setRendererSize(rendererSize);

View File

@@ -1,4 +1,4 @@
import { useCallback, useEffect, useRef } from 'react';
import { useCallback, useEffect, useRef, useState } from 'react';
import { produce } from 'immer';
import { useSceneStore } from 'src/stores/sceneStore';
import { useUiStateStore } from 'src/stores/uiStateStore';
@@ -18,6 +18,8 @@ const reducers: { [k in string]: InteractionReducer } = {
export const useInteractionManager = () => {
const rendererRef = useRef<HTMLElement>();
const [isEnabled, setIsEnabled] = useState(true);
const destroyListeners = useRef<() => void>();
const mode = useUiStateStore((state) => {
return state.mode;
});
@@ -140,8 +142,12 @@ export const useInteractionManager = () => {
]
);
// TODO: Needs optimisation, listeners are added / removed every time the mouse position changes. Very intensive.
useEffect(() => {
if (!rendererRef.current) return;
if (!rendererRef.current || !isEnabled) {
destroyListeners.current?.();
return;
}
const el = rendererRef.current;
@@ -149,18 +155,21 @@ export const useInteractionManager = () => {
el.addEventListener('mousedown', onMouseEvent);
el.addEventListener('mouseup', onMouseEvent);
return () => {
destroyListeners.current = () => {
el.removeEventListener('mousemove', onMouseEvent);
el.removeEventListener('mousedown', onMouseEvent);
el.removeEventListener('mouseup', onMouseEvent);
};
}, [onMouseEvent]);
return destroyListeners.current;
}, [onMouseEvent, isEnabled]);
const setElement = useCallback((element: HTMLElement) => {
rendererRef.current = element;
}, []);
return {
setElement
setElement,
setIsEnabled
};
};

View File

@@ -12,7 +12,7 @@ type UiStateStore = UiState & Actions;
const initialState = () => {
return createStore<UiStateStore>((set, get) => {
return {
isToolbarVisible: true,
interactionsEnabled: true,
mode: {
type: 'CURSOR',
showCursor: true,
@@ -61,8 +61,16 @@ const initialState = () => {
setRendererSize: (rendererSize) => {
set({ rendererSize });
},
setToolbarVisibility: (visible) => {
set({ isToolbarVisible: visible });
setInteractionsEnabled: (enabled) => {
set({ interactionsEnabled: enabled });
if (!enabled) {
set({ mode: { type: 'INTERACTIONS_DISABLED', showCursor: false } });
} else {
set({
mode: { type: 'CURSOR', showCursor: true, mousedown: null }
});
}
}
}
};

View File

@@ -32,6 +32,11 @@ export interface Mouse {
}
// Begin mode types
export interface InteractionsDisabled {
type: 'INTERACTIONS_DISABLED';
showCursor: boolean;
}
export interface CursorMode {
type: 'CURSOR';
showCursor: boolean;
@@ -63,7 +68,12 @@ export interface DragItemsMode {
items: SceneItem[];
}
export type Mode = CursorMode | PanMode | DragItemsMode | LassoMode;
export type Mode =
| InteractionsDisabled
| CursorMode
| PanMode
| DragItemsMode
| LassoMode;
// End mode types
export type ContextMenu =
@@ -80,7 +90,7 @@ export interface Scroll {
}
export interface UiState {
isToolbarVisible: boolean;
interactionsEnabled: boolean;
mode: Mode;
itemControls: ItemControls;
contextMenu: ContextMenu;
@@ -92,7 +102,6 @@ export interface UiState {
export interface UiStateActions {
setMode: (mode: Mode) => void;
setToolbarVisibility: (visible: boolean) => void;
incrementZoom: () => void;
decrementZoom: () => void;
setZoom: (zoom: number) => void;
@@ -101,4 +110,5 @@ export interface UiStateActions {
setContextMenu: (contextMenu: ContextMenu) => void;
setMouse: (mouse: Mouse) => void;
setRendererSize: (rendererSize: Size) => void;
setInteractionsEnabled: (enabled: boolean) => void;
}