further reduce unnecessary re-renders
This commit is contained in:
parent
2ca2ba5d1b
commit
87ceaa1220
5 changed files with 40 additions and 26 deletions
|
|
@ -18,9 +18,7 @@ import { formatTime } from "./util";
|
|||
import { InsertMode } from "../VisualEditor/VisualEditor";
|
||||
import { KeyInfoHidden, KeyInfoVisible } from "./KeyInfo";
|
||||
import { About } from "./About";
|
||||
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";
|
||||
|
|
@ -46,6 +44,18 @@ export type TopPanelProps = {
|
|||
history: EditHistory,
|
||||
}
|
||||
|
||||
const ShortCutShowKeys = <kbd>~</kbd>;
|
||||
|
||||
const insertModes: [InsertMode, string, ReactElement, ReactElement][] = [
|
||||
["and", "AND-states", <RountangleIcon kind="and"/>, <kbd>A</kbd>],
|
||||
["or", "OR-states", <RountangleIcon kind="or"/>, <kbd>O</kbd>],
|
||||
["pseudo", "pseudo-states", <PseudoStateIcon/>, <kbd>P</kbd>],
|
||||
["shallow", "shallow history", <HistoryIcon kind="shallow"/>, <kbd>H</kbd>],
|
||||
["deep", "deep history", <HistoryIcon kind="deep"/>, <></>],
|
||||
["transition", "transitions", <TrendingFlatIcon fontSize="small"/>, <kbd>T</kbd>],
|
||||
["text", "text", <> T </>, <kbd>X</kbd>],
|
||||
];
|
||||
|
||||
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);
|
||||
|
|
@ -191,7 +201,7 @@ export const TopPanel = memo(function TopPanel({trace, time, setTime, onUndo, on
|
|||
|
||||
{/* shortcuts / about */}
|
||||
<div className="toolbarGroup">
|
||||
<KeyInfo keyInfo={<kbd>~</kbd>}>
|
||||
<KeyInfo keyInfo={ShortCutShowKeys}>
|
||||
<button title="show/hide keyboard shortcuts" className={showKeys?"active":""} onClick={() => setShowKeys(s => !s)}><KeyboardIcon fontSize="small"/></button>
|
||||
</KeyInfo>
|
||||
<button title="about StateBuddy" onClick={() => setModal(<About setModal={setModal}/>)}><InfoOutlineIcon fontSize="small"/></button>
|
||||
|
|
@ -212,15 +222,7 @@ export const TopPanel = memo(function TopPanel({trace, time, setTime, onUndo, on
|
|||
|
||||
{/* insert rountangle / arrow / ... */}
|
||||
<div className="toolbarGroup">
|
||||
{([
|
||||
["and", "AND-states", <RountangleIcon kind="and"/>, <kbd>A</kbd>],
|
||||
["or", "OR-states", <RountangleIcon kind="or"/>, <kbd>O</kbd>],
|
||||
["pseudo", "pseudo-states", <PseudoStateIcon/>, <kbd>P</kbd>],
|
||||
["shallow", "shallow history", <HistoryIcon kind="shallow"/>, <kbd>H</kbd>],
|
||||
["deep", "deep history", <HistoryIcon kind="deep"/>, <></>],
|
||||
["transition", "transitions", <TrendingFlatIcon fontSize="small"/>, <kbd>T</kbd>],
|
||||
["text", "text", <> T </>, <kbd>X</kbd>],
|
||||
] as [InsertMode, string, ReactElement, ReactElement][]).map(([m, hint, buttonTxt, keyInfo]) =>
|
||||
{insertModes.map(([m, hint, buttonTxt, keyInfo]) =>
|
||||
<KeyInfo key={m} keyInfo={keyInfo}>
|
||||
<button
|
||||
title={"insert "+hint}
|
||||
|
|
|
|||
|
|
@ -5,6 +5,9 @@ import { KeyInfoHidden, KeyInfoVisible } from "../KeyInfo";
|
|||
import ZoomInIcon from '@mui/icons-material/ZoomIn';
|
||||
import ZoomOutIcon from '@mui/icons-material/ZoomOut';
|
||||
|
||||
const shortcutZoomIn = <><kbd>Ctrl</kbd>+<kbd>-</kbd></>;
|
||||
const shortcutZoomOut = <><kbd>Ctrl</kbd>+<kbd>+</kbd></>;
|
||||
|
||||
export const ZoomButtons = memo(function ZoomButtons({showKeys, zoom, setZoom}: {showKeys: boolean, zoom: number, setZoom: Dispatch<SetStateAction<number>>}) {
|
||||
|
||||
const KeyInfo = showKeys ? KeyInfoVisible : KeyInfoHidden;
|
||||
|
|
@ -36,11 +39,11 @@ export const ZoomButtons = memo(function ZoomButtons({showKeys, zoom, setZoom}:
|
|||
}, []);
|
||||
|
||||
return <>
|
||||
<KeyInfo keyInfo={<><kbd>Ctrl</kbd>+<kbd>-</kbd></>}>
|
||||
<KeyInfo keyInfo={shortcutZoomOut}>
|
||||
<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></>}>
|
||||
<KeyInfo keyInfo={shortcutZoomIn}>
|
||||
<button title="zoom in" onClick={onZoomIn} disabled={zoom >= ZOOM_MAX}><ZoomInIcon fontSize="small"/></button>
|
||||
</KeyInfo>
|
||||
</>;
|
||||
|
|
|
|||
|
|
@ -1,10 +1,11 @@
|
|||
import { memo } from "react";
|
||||
import { Arrow } from "../statecharts/concrete_syntax";
|
||||
import { Arrow, ArrowPart } from "../statecharts/concrete_syntax";
|
||||
import { ArcDirection, euclideanDistance } from "./geometry";
|
||||
import { CORNER_HELPER_RADIUS } from "./parameters";
|
||||
import { arraysEqual } from "@/App/util";
|
||||
|
||||
|
||||
export const ArrowSVG = memo(function(props: { arrow: Arrow; selected: string[]; errors: string[]; highlight: boolean; fired: boolean; arc: ArcDirection; initialMarker: boolean }) {
|
||||
export const ArrowSVG = memo(function(props: { arrow: Arrow; selected: ArrowPart[]; error: string; highlight: boolean; fired: boolean; arc: ArcDirection; initialMarker: boolean }) {
|
||||
const { start, end, uid } = props.arrow;
|
||||
const radius = euclideanDistance(start, end) / 1.6;
|
||||
let largeArc = "1";
|
||||
|
|
@ -18,7 +19,7 @@ export const ArrowSVG = memo(function(props: { arrow: Arrow; selected: string[];
|
|||
<path
|
||||
className={"arrow"
|
||||
+ (props.selected.length === 2 ? " selected" : "")
|
||||
+ (props.errors.length > 0 ? " error" : "")
|
||||
+ (props.error ? " error" : "")
|
||||
+ (props.highlight ? " highlight" : "")
|
||||
+ (props.fired ? " fired" : "")
|
||||
}
|
||||
|
|
@ -30,13 +31,13 @@ export const ArrowSVG = memo(function(props: { arrow: Arrow; selected: string[];
|
|||
data-uid={uid}
|
||||
data-parts="start end" />
|
||||
|
||||
{props.errors.length > 0 && <text
|
||||
{props.error && <text
|
||||
className="error"
|
||||
x={(start.x + end.x) / 2 + 5}
|
||||
y={(start.y + end.y) / 2}
|
||||
textAnchor="middle"
|
||||
data-uid={uid}
|
||||
data-parts="start end">{props.errors.join(', ')}</text>}
|
||||
data-parts="start end">{props.error}</text>}
|
||||
|
||||
<path
|
||||
className="helper"
|
||||
|
|
@ -79,4 +80,12 @@ export const ArrowSVG = memo(function(props: { arrow: Arrow; selected: string[];
|
|||
data-parts="end" />}
|
||||
|
||||
</g>;
|
||||
});
|
||||
}, (prevProps, nextProps) => {
|
||||
return prevProps.arrow === nextProps.arrow
|
||||
&& arraysEqual(prevProps.selected, nextProps.selected)
|
||||
&& prevProps.highlight === nextProps.highlight
|
||||
&& prevProps.error === nextProps.error
|
||||
&& prevProps.fired === nextProps.fired
|
||||
&& prevProps.arc === nextProps.arc
|
||||
&& prevProps.initialMarker === nextProps.initialMarker
|
||||
})
|
||||
|
|
|
|||
|
|
@ -755,10 +755,10 @@ export const VisualEditor = memo(function VisualEditor({state, setState, setAST,
|
|||
return <ArrowSVG
|
||||
key={arrow.uid}
|
||||
arrow={arrow}
|
||||
selected={selection.find(a => a.uid === arrow.uid)?.parts || []}
|
||||
errors={errors
|
||||
selected={selection.find(a => a.uid === arrow.uid)?.parts as ArrowPart[] || []}
|
||||
error={errors
|
||||
.filter(({shapeUid}) => shapeUid === arrow.uid)
|
||||
.map(({message}) => message)}
|
||||
.map(({message}) => message).join(', ')}
|
||||
highlight={arrowsToHighlight.hasOwnProperty(arrow.uid)}
|
||||
fired={highlightTransitions.includes(arrow.uid)}
|
||||
arc={arc}
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
import { Dispatch, SetStateAction, useState } from "react";
|
||||
import { Dispatch, SetStateAction, useCallback, useState } from "react";
|
||||
|
||||
// like useState, but it is persisted in localStorage
|
||||
// important: values must be JSON-(de-)serializable
|
||||
|
|
@ -18,7 +18,7 @@ export function usePersistentState<T>(key: string, initial: T): [T, Dispatch<Set
|
|||
return initial;
|
||||
});
|
||||
|
||||
function setStateWrapped(val: SetStateAction<T>) {
|
||||
const setStateWrapped = useCallback((val: SetStateAction<T>) => {
|
||||
setState((oldState: T) => {
|
||||
let newVal;
|
||||
if (typeof val === 'function') {
|
||||
|
|
@ -32,7 +32,7 @@ export function usePersistentState<T>(key: string, initial: T): [T, Dispatch<Set
|
|||
localStorage.setItem(key, serialized);
|
||||
return newVal;
|
||||
});
|
||||
}
|
||||
}, [setState]);
|
||||
|
||||
return [state, setStateWrapped];
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue