first of many performance improvements: no unnecessary TextSVG re-renders - more to follow
This commit is contained in:
parent
af60e811fc
commit
0fc3775a11
14 changed files with 116 additions and 74 deletions
|
|
@ -1,4 +1,4 @@
|
|||
import { ReactElement, useEffect, useMemo, useRef, useState } from "react";
|
||||
import { ReactElement, useCallback, useEffect, useMemo, useRef, useState } from "react";
|
||||
|
||||
import { emptyStatechart, Statechart, Transition } from "../statecharts/abstract_syntax";
|
||||
import { handleInputEvent, initialize, RuntimeError } from "../statecharts/interpreter";
|
||||
|
|
@ -85,9 +85,9 @@ export function App() {
|
|||
const plant = plants.find(([pn, p]) => pn === plantName)![1];
|
||||
|
||||
const editorState = historyState.current;
|
||||
const setEditorState = (cb: (value: VisualEditorState) => VisualEditorState) => {
|
||||
const setEditorState = useCallback((cb: (value: VisualEditorState) => VisualEditorState) => {
|
||||
setHistoryState(historyState => ({...historyState, current: cb(historyState.current)}));
|
||||
}
|
||||
}, [setHistoryState]);
|
||||
|
||||
const refRightSideBar = useRef<HTMLDivElement>(null);
|
||||
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
import { Box, Stack } from "@mui/material";
|
||||
import { memo, PropsWithChildren, ReactElement } from "react";
|
||||
|
||||
export function KeyInfoVisible(props: {keyInfo, children, horizontal?: boolean}) {
|
||||
export const KeyInfoVisible = memo(function KeyInfoVisible(props: PropsWithChildren<{keyInfo: ReactElement, horizontal?: boolean}>) {
|
||||
return <div style={{display: 'inline-block'}}>
|
||||
{/* <Stack direction={props.horizontal ? "row" : "column"}> */}
|
||||
<div style={{display: props.horizontal ? 'inline-block' : '', fontSize:11, height: 18, textAlign:"center", paddingLeft: 3, paddingRight: 3}}>
|
||||
|
|
@ -11,8 +11,8 @@ export function KeyInfoVisible(props: {keyInfo, children, horizontal?: boolean})
|
|||
</div>
|
||||
{/* </Stack> */}
|
||||
</div>
|
||||
}
|
||||
});
|
||||
|
||||
export function KeyInfoHidden(props: {children}) {
|
||||
export const KeyInfoHidden = memo(function KeyInfoHidden(props: PropsWithChildren<{}>) {
|
||||
return <>{props.children}</>;
|
||||
}
|
||||
});
|
||||
|
|
|
|||
|
|
@ -32,7 +32,7 @@ export function ShowAction(props: {action: Action}) {
|
|||
}
|
||||
}
|
||||
|
||||
export function ShowAST(props: {root: ConcreteState | PseudoState, transitions: Map<string, Transition[]>, trace: TraceState | null, highlightActive: Set<string>}) {
|
||||
export const ShowAST = memo(function ShowAST(props: {root: ConcreteState | PseudoState, transitions: Map<string, Transition[]>, trace: TraceState | null, highlightActive: Set<string>}) {
|
||||
const description = stateDescription(props.root);
|
||||
// const outgoing = props.transitions.get(props.root.uid) || [];
|
||||
|
||||
|
|
@ -69,11 +69,11 @@ export function ShowAST(props: {root: ConcreteState | PseudoState, transitions:
|
|||
// outgoing.map(transition => <> <ShowTransition transition={transition}/><br/></>)
|
||||
// } */}
|
||||
// </details>;
|
||||
}
|
||||
});
|
||||
|
||||
import BoltIcon from '@mui/icons-material/Bolt';
|
||||
import { KeyInfoHidden, KeyInfoVisible } from "./KeyInfo";
|
||||
import { useEffect } from "react";
|
||||
import { memo, useEffect } from "react";
|
||||
import { TraceState } from "./App";
|
||||
|
||||
export function ShowInputEvents({inputEvents, onRaise, disabled, showKeys}: {inputEvents: EventTrigger[], onRaise: (e: string, p: any) => void, disabled: boolean, showKeys: boolean}) {
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
import { Dispatch, ReactElement, SetStateAction, useEffect, useState } from "react";
|
||||
import { Dispatch, memo, ReactElement, SetStateAction, useEffect, useState } from "react";
|
||||
import { BigStep, TimerElapseEvent, Timers } from "../statecharts/runtime_types";
|
||||
import { getSimTime, setPaused, setRealtime, TimeMode } from "../statecharts/time";
|
||||
import { Statechart } from "../statecharts/abstract_syntax";
|
||||
|
|
@ -11,12 +11,8 @@ import SkipNextIcon from '@mui/icons-material/SkipNext';
|
|||
import SkipPreviousIcon from '@mui/icons-material/SkipPrevious';import TrendingFlatIcon from '@mui/icons-material/TrendingFlat';
|
||||
import AccessAlarmIcon from '@mui/icons-material/AccessAlarm';
|
||||
import StopIcon from '@mui/icons-material/Stop';
|
||||
import UndoIcon from '@mui/icons-material/Undo';
|
||||
import RedoIcon from '@mui/icons-material/Redo';
|
||||
import InfoOutlineIcon from '@mui/icons-material/InfoOutline';
|
||||
import KeyboardIcon from '@mui/icons-material/Keyboard';
|
||||
import ZoomInIcon from '@mui/icons-material/ZoomIn';
|
||||
import ZoomOutIcon from '@mui/icons-material/ZoomOut';
|
||||
|
||||
import { formatTime } from "./util";
|
||||
import { InsertMode } from "../VisualEditor/VisualEditor";
|
||||
|
|
@ -26,20 +22,20 @@ import { usePersistentState } from "@/util/persistent_state";
|
|||
import { RountangleIcon, PseudoStateIcon, HistoryIcon } from "./Icons";
|
||||
import { ZOOM_MAX, ZOOM_MIN, ZOOM_STEP } from "@/VisualEditor/parameters";
|
||||
import { EditHistory, TraceState } from "./App";
|
||||
import { ZoomButtons } from "./TopPanel/ZoomButtons";
|
||||
import { UndoRedoButtons } from "./TopPanel/UndoRedoButtons";
|
||||
|
||||
export type TopPanelProps = {
|
||||
trace: TraceState | null,
|
||||
// rt?: BigStep,
|
||||
// rtIdx?: number,
|
||||
time: TimeMode,
|
||||
setTime: Dispatch<SetStateAction<TimeMode>>,
|
||||
onUndo: () => void,
|
||||
onRedo: () => void,
|
||||
onInit: () => void,
|
||||
onClear: () => void,
|
||||
onRaise: (e: string, p: any) => void,
|
||||
// onRaise: (e: string, p: any) => void,
|
||||
onBack: () => void,
|
||||
ast: Statechart,
|
||||
// ast: Statechart,
|
||||
mode: InsertMode,
|
||||
setMode: Dispatch<SetStateAction<InsertMode>>,
|
||||
setModal: Dispatch<SetStateAction<ReactElement|null>>,
|
||||
|
|
@ -50,7 +46,7 @@ export type TopPanelProps = {
|
|||
history: EditHistory,
|
||||
}
|
||||
|
||||
export function TopPanel({trace, time, setTime, onUndo, onRedo, onInit, onClear, onRaise, onBack, ast, mode, setMode, setModal, zoom, setZoom, showKeys, setShowKeys, history}: TopPanelProps) {
|
||||
export const TopPanel = memo(function TopPanel({trace, time, setTime, onUndo, onRedo, onInit, onClear, onBack, mode, setMode, setModal, zoom, setZoom, showKeys, setShowKeys, history}: TopPanelProps) {
|
||||
const [displayTime, setDisplayTime] = useState("0.000");
|
||||
const [timescale, setTimescale] = useState(1);
|
||||
|
||||
|
|
@ -111,14 +107,6 @@ export function TopPanel({trace, time, setTime, onUndo, onRedo, onInit, onClear,
|
|||
e.preventDefault();
|
||||
onRedo();
|
||||
}
|
||||
if (e.key === "+") {
|
||||
e.preventDefault();
|
||||
onZoomIn();
|
||||
}
|
||||
if (e.key === "-") {
|
||||
e.preventDefault();
|
||||
onZoomOut();
|
||||
}
|
||||
}
|
||||
};
|
||||
window.addEventListener("keydown", onKeyDown);
|
||||
|
|
@ -143,12 +131,6 @@ export function TopPanel({trace, time, setTime, onUndo, onRedo, onInit, onClear,
|
|||
}
|
||||
}, [time]);
|
||||
|
||||
function onZoomIn() {
|
||||
setZoom(zoom => Math.min(zoom * ZOOM_STEP, ZOOM_MAX));
|
||||
}
|
||||
function onZoomOut() {
|
||||
setZoom(zoom => Math.max(zoom / ZOOM_STEP, ZOOM_MIN));
|
||||
}
|
||||
|
||||
function onChangePaused(paused: boolean, wallclktime: number) {
|
||||
setTime(time => {
|
||||
|
|
@ -218,24 +200,13 @@ export function TopPanel({trace, time, setTime, onUndo, onRedo, onInit, onClear,
|
|||
|
||||
{/* zoom */}
|
||||
<div className="toolbarGroup">
|
||||
<KeyInfo keyInfo={<><kbd>Ctrl</kbd>+<kbd>-</kbd></>}>
|
||||
<button title="zoom out" onClick={onZoomOut} disabled={zoom <= ZOOM_MIN}><ZoomOutIcon fontSize="small"/></button>
|
||||
</KeyInfo>
|
||||
<input title="current zoom level" value={zoom.toFixed(3)} style={{width:40}} readOnly/>
|
||||
<KeyInfo keyInfo={<><kbd>Ctrl</kbd>+<kbd>+</kbd></>}>
|
||||
<button title="zoom in" onClick={onZoomIn} disabled={zoom >= ZOOM_MAX}><ZoomInIcon fontSize="small"/></button>
|
||||
</KeyInfo>
|
||||
<ZoomButtons showKeys={showKeys} zoom={zoom} setZoom={setZoom}/>
|
||||
 
|
||||
</div>
|
||||
|
||||
{/* undo / redo */}
|
||||
<div className="toolbarGroup">
|
||||
<KeyInfo keyInfo={<><kbd>Ctrl</kbd>+<kbd>Z</kbd></>}>
|
||||
<button title="undo" onClick={onUndo} disabled={history.history.length === 0}><UndoIcon fontSize="small"/> ({history.history.length})</button>
|
||||
</KeyInfo>
|
||||
<KeyInfo keyInfo={<><kbd>Ctrl</kbd>+<kbd>Shift</kbd>+<kbd>Z</kbd></>}>
|
||||
<button title="redo" onClick={onRedo} disabled={history.future.length === 0}><RedoIcon fontSize="small"/> ({history.future.length})</button>
|
||||
</KeyInfo>
|
||||
<UndoRedoButtons showKeys={showKeys} onUndo={onUndo} onRedo={onRedo} historyLength={history.history.length} futureLength={history.future.length}/>
|
||||
 
|
||||
</div>
|
||||
|
||||
|
|
@ -349,4 +320,4 @@ export function TopPanel({trace, time, setTime, onUndo, onRedo, onInit, onClear,
|
|||
</div> */}
|
||||
|
||||
</div>;
|
||||
}
|
||||
});
|
||||
|
|
|
|||
17
src/App/TopPanel/UndoRedoButtons.tsx
Normal file
17
src/App/TopPanel/UndoRedoButtons.tsx
Normal file
|
|
@ -0,0 +1,17 @@
|
|||
import { memo } from "react";
|
||||
import { KeyInfoHidden, KeyInfoVisible } from "../KeyInfo";
|
||||
|
||||
import UndoIcon from '@mui/icons-material/Undo';
|
||||
import RedoIcon from '@mui/icons-material/Redo';
|
||||
|
||||
export const UndoRedoButtons = memo(function UndoRedoButtons({showKeys, onUndo, onRedo, historyLength, futureLength}: {showKeys: boolean, onUndo: () => void, onRedo: () => void, historyLength: number, futureLength: number}) {
|
||||
const KeyInfo = showKeys ? KeyInfoVisible : KeyInfoHidden;
|
||||
return <>
|
||||
<KeyInfo keyInfo={<><kbd>Ctrl</kbd>+<kbd>Z</kbd></>}>
|
||||
<button title="undo" onClick={onUndo} disabled={historyLength === 0}><UndoIcon fontSize="small"/> ({historyLength})</button>
|
||||
</KeyInfo>
|
||||
<KeyInfo keyInfo={<><kbd>Ctrl</kbd>+<kbd>Shift</kbd>+<kbd>Z</kbd></>}>
|
||||
<button title="redo" onClick={onRedo} disabled={futureLength === 0}><RedoIcon fontSize="small"/> ({futureLength})</button>
|
||||
</KeyInfo>
|
||||
</>;
|
||||
});
|
||||
47
src/App/TopPanel/ZoomButtons.tsx
Normal file
47
src/App/TopPanel/ZoomButtons.tsx
Normal file
|
|
@ -0,0 +1,47 @@
|
|||
import { ZOOM_MAX, ZOOM_MIN, ZOOM_STEP } from "@/VisualEditor/parameters";
|
||||
import { Dispatch, memo, SetStateAction, useEffect } from "react";
|
||||
import { KeyInfoHidden, KeyInfoVisible } from "../KeyInfo";
|
||||
|
||||
import ZoomInIcon from '@mui/icons-material/ZoomIn';
|
||||
import ZoomOutIcon from '@mui/icons-material/ZoomOut';
|
||||
|
||||
export const ZoomButtons = memo(function ZoomButtons({showKeys, zoom, setZoom}: {showKeys: boolean, zoom: number, setZoom: Dispatch<SetStateAction<number>>}) {
|
||||
|
||||
const KeyInfo = showKeys ? KeyInfoVisible : KeyInfoHidden;
|
||||
|
||||
function onZoomIn() {
|
||||
setZoom(zoom => Math.min(zoom * ZOOM_STEP, ZOOM_MAX));
|
||||
}
|
||||
function onZoomOut() {
|
||||
setZoom(zoom => Math.max(zoom / ZOOM_STEP, ZOOM_MIN));
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
const onKeyDown = (e: KeyboardEvent) => {
|
||||
if (e.ctrlKey) {
|
||||
if (e.key === "+") {
|
||||
e.preventDefault();
|
||||
onZoomIn();
|
||||
}
|
||||
if (e.key === "-") {
|
||||
e.preventDefault();
|
||||
onZoomOut();
|
||||
}
|
||||
}
|
||||
};
|
||||
window.addEventListener("keydown", onKeyDown);
|
||||
return () => {
|
||||
window.removeEventListener("keydown", onKeyDown);
|
||||
};
|
||||
}, []);
|
||||
|
||||
return <>
|
||||
<KeyInfo keyInfo={<><kbd>Ctrl</kbd>+<kbd>-</kbd></>}>
|
||||
<button title="zoom out" onClick={onZoomOut} disabled={zoom <= ZOOM_MIN}><ZoomOutIcon fontSize="small"/></button>
|
||||
</KeyInfo>
|
||||
<input title="current zoom level" value={zoom.toFixed(3)} style={{width:40}} readOnly/>
|
||||
<KeyInfo keyInfo={<><kbd>Ctrl</kbd>+<kbd>+</kbd></>}>
|
||||
<button title="zoom in" onClick={onZoomIn} disabled={zoom >= ZOOM_MAX}><ZoomInIcon fontSize="small"/></button>
|
||||
</KeyInfo>
|
||||
</>;
|
||||
});
|
||||
Loading…
Add table
Add a link
Reference in a new issue