diff --git a/src/App/TopPanel.tsx b/src/App/TopPanel.tsx index 3d0f076..cf4223d 100644 --- a/src/App/TopPanel.tsx +++ b/src/App/TopPanel.tsx @@ -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 = ~; + +const insertModes: [InsertMode, string, ReactElement, ReactElement][] = [ + ["and", "AND-states", , A], + ["or", "OR-states", , O], + ["pseudo", "pseudo-states", , P], + ["shallow", "shallow history", , H], + ["deep", "deep history", , <>], + ["transition", "transitions", , T], + ["text", "text", <> T , X], +]; + 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 */}
- ~}> + @@ -212,15 +222,7 @@ export const TopPanel = memo(function TopPanel({trace, time, setTime, onUndo, on {/* insert rountangle / arrow / ... */}
- {([ - ["and", "AND-states", , A], - ["or", "OR-states", , O], - ["pseudo", "pseudo-states", , P], - ["shallow", "shallow history", , H], - ["deep", "deep history", , <>], - ["transition", "transitions", , T], - ["text", "text", <> T , X], - ] as [InsertMode, string, ReactElement, ReactElement][]).map(([m, hint, buttonTxt, keyInfo]) => + {insertModes.map(([m, hint, buttonTxt, keyInfo]) => - Ctrl++}> + ; diff --git a/src/VisualEditor/ArrowSVG.tsx b/src/VisualEditor/ArrowSVG.tsx index a998a99..709ca76 100644 --- a/src/VisualEditor/ArrowSVG.tsx +++ b/src/VisualEditor/ArrowSVG.tsx @@ -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[]; 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 && {props.errors.join(', ')}} + data-parts="start end">{props.error}} } ; -}); +}, (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 +}) diff --git a/src/VisualEditor/VisualEditor.tsx b/src/VisualEditor/VisualEditor.tsx index f41abf2..56c8cd6 100644 --- a/src/VisualEditor/VisualEditor.tsx +++ b/src/VisualEditor/VisualEditor.tsx @@ -755,10 +755,10 @@ export const VisualEditor = memo(function VisualEditor({state, setState, setAST, return 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} diff --git a/src/util/persistent_state.ts b/src/util/persistent_state.ts index 8046c56..d59681e 100644 --- a/src/util/persistent_state.ts +++ b/src/util/persistent_state.ts @@ -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(key: string, initial: T): [T, Dispatch) { + const setStateWrapped = useCallback((val: SetStateAction) => { setState((oldState: T) => { let newVal; if (typeof val === 'function') { @@ -32,7 +32,7 @@ export function usePersistentState(key: string, initial: T): [T, Dispatch