better UI
This commit is contained in:
parent
44fb8726ca
commit
1f9379df7f
16 changed files with 440 additions and 248 deletions
179
src/App/App.tsx
179
src/App/App.tsx
|
|
@ -1,4 +1,4 @@
|
|||
import { ReactElement, useEffect, useRef, useState } from "react";
|
||||
import { Dispatch, ReactElement, SetStateAction, useEffect, useRef, useState } from "react";
|
||||
|
||||
import { emptyStatechart, Statechart } from "../statecharts/abstract_syntax";
|
||||
import { handleInputEvent, initialize } from "../statecharts/interpreter";
|
||||
|
|
@ -9,26 +9,73 @@ import { getSimTime, getWallClkDelay, TimeMode } from "../statecharts/time";
|
|||
import "../index.css";
|
||||
import "./App.css";
|
||||
|
||||
import { Box, Stack } from "@mui/material";
|
||||
import Stack from "@mui/material/Stack";
|
||||
import Box from "@mui/material/Box";
|
||||
import { TopPanel } from "./TopPanel";
|
||||
import { RTHistory } from "./RTHistory";
|
||||
import { ShowAST, ShowOutputEvents } from "./ShowAST";
|
||||
import { ShowAST, ShowInputEvents, ShowOutputEvents } from "./ShowAST";
|
||||
import { TraceableError } from "../statecharts/parser";
|
||||
import { getKeyHandler } from "./shortcut_handler";
|
||||
import { BottomPanel } from "./BottomPanel";
|
||||
import { emptyState, VisualEditorState } from "@/statecharts/concrete_syntax";
|
||||
import { usePersistentState } from "@/util/persistent_state";
|
||||
|
||||
type EditHistory = {
|
||||
current: VisualEditorState,
|
||||
history: VisualEditorState[],
|
||||
future: VisualEditorState[],
|
||||
}
|
||||
|
||||
export function App() {
|
||||
const [mode, setMode] = useState<InsertMode>("and");
|
||||
const [historyState, setHistoryState] = useState<EditHistory>({current: emptyState, history: [], future: []});
|
||||
const [ast, setAST] = useState<Statechart>(emptyStatechart);
|
||||
const [errors, setErrors] = useState<TraceableError[]>([]);
|
||||
const [rt, setRT] = useState<BigStep[]>([]);
|
||||
const [rtIdx, setRTIdx] = useState<number|undefined>();
|
||||
const [time, setTime] = useState<TimeMode>({kind: "paused", simtime: 0});
|
||||
|
||||
const [modal, setModal] = useState<ReactElement|null>(null);
|
||||
|
||||
const editorState = historyState.current;
|
||||
const setEditorState = (cb: (value: VisualEditorState) => VisualEditorState) => {
|
||||
setHistoryState(historyState => ({...historyState, current: cb(historyState.current)}));
|
||||
}
|
||||
|
||||
const refRightSideBar = useRef<HTMLDivElement>(null);
|
||||
|
||||
|
||||
function makeCheckPoint() {
|
||||
setHistoryState(historyState => ({
|
||||
...historyState,
|
||||
history: [...historyState.history, historyState.current],
|
||||
future: [],
|
||||
}));
|
||||
}
|
||||
function onUndo() {
|
||||
setHistoryState(historyState => {
|
||||
if (historyState.history.length === 0) {
|
||||
return historyState; // no change
|
||||
}
|
||||
return {
|
||||
current: historyState.history.at(-1)!,
|
||||
history: historyState.history.slice(0,-1),
|
||||
future: [...historyState.future, historyState.current],
|
||||
}
|
||||
})
|
||||
}
|
||||
function onRedo() {
|
||||
setHistoryState(historyState => {
|
||||
if (historyState.future.length === 0) {
|
||||
return historyState; // no change
|
||||
}
|
||||
return {
|
||||
current: historyState.future.at(-1)!,
|
||||
history: [...historyState.history, historyState.current],
|
||||
future: historyState.future.slice(0,-1),
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function onInit() {
|
||||
const config = initialize(ast);
|
||||
setRT([{inputEvent: null, simtime: 0, ...config}]);
|
||||
|
|
@ -140,13 +187,16 @@ export function App() {
|
|||
// return state && state.parent?.kind !== "and";
|
||||
// })) || new Set();
|
||||
|
||||
const highlightActive = (rtIdx === undefined) ? new Set() : rt[rtIdx].mode;
|
||||
const highlightActive: Set<string> = (rtIdx === undefined) ? new Set() : rt[rtIdx].mode;
|
||||
|
||||
const highlightTransitions = (rtIdx === undefined) ? [] : rt[rtIdx].firedTransitions;
|
||||
|
||||
console.log(ast);
|
||||
const [showStateTree, setShowStateTree] = usePersistentState("showStateTree", true);
|
||||
const [showInputEvents, setShowInputEvents] = usePersistentState("showInputEvents", true);
|
||||
const [showOutputEvents, setShowOutputEvents] = usePersistentState("showOutputEvents", true);
|
||||
|
||||
return <>
|
||||
|
||||
{/* Modal dialog */}
|
||||
{modal && <div
|
||||
className="modalOuter"
|
||||
|
|
@ -157,56 +207,83 @@ export function App() {
|
|||
</span>
|
||||
</div>
|
||||
</div>}
|
||||
<Stack sx={{height:'100vh'}}>
|
||||
{/* Top bar */}
|
||||
<Box
|
||||
sx={{
|
||||
display: "flex",
|
||||
borderBottom: 1,
|
||||
borderColor: "divider",
|
||||
alignItems: 'center',
|
||||
flex: '0 0 content',
|
||||
}}>
|
||||
<TopPanel
|
||||
rt={rtIdx === undefined ? undefined : rt[rtIdx]}
|
||||
{...{rtIdx, ast, time, setTime, onInit, onClear, onRaise, onBack, mode, setMode, setModal}}
|
||||
/>
|
||||
</Box>
|
||||
|
||||
{/* Everything below the top bar */}
|
||||
<Stack direction="row" sx={{
|
||||
overflow: 'auto',
|
||||
}}>
|
||||
<Stack sx={{height:'100%'}}>
|
||||
<Stack direction="row" sx={{flexGrow:1, overflow: "auto"}}>
|
||||
|
||||
{/* main */}
|
||||
<Box sx={{
|
||||
flexGrow:1,
|
||||
overflow:'auto',
|
||||
}}>
|
||||
<VisualEditor {...{ast, setAST, rt: rt.at(rtIdx!), setRT, errors, setErrors, mode, highlightActive, highlightTransitions, setModal}}/>
|
||||
{/* Left: top bar and main editor */}
|
||||
<Box sx={{flexGrow:1, overflow: "auto"}}>
|
||||
<Stack sx={{height:'100%'}}>
|
||||
{/* Top bar */}
|
||||
<Box sx={{
|
||||
display: "flex",
|
||||
borderBottom: 1,
|
||||
borderColor: "divider",
|
||||
alignItems: 'center',
|
||||
flex: '0 0 content',
|
||||
}}>
|
||||
<TopPanel
|
||||
rt={rtIdx === undefined ? undefined : rt[rtIdx]}
|
||||
{...{rtIdx, ast, time, setTime, onUndo, onRedo, onInit, onClear, onRaise, onBack, mode, setMode, setModal}}
|
||||
/>
|
||||
</Box>
|
||||
{/* Below the top bar: Editor */}
|
||||
<Box sx={{flexGrow:1, overflow: "auto"}}>
|
||||
<VisualEditor {...{state: editorState, setState: setEditorState, ast, setAST, rt: rt.at(rtIdx!), setRT, errors, setErrors, mode, highlightActive, highlightTransitions, setModal, makeCheckPoint}}/>
|
||||
</Box>
|
||||
</Stack>
|
||||
</Box>
|
||||
|
||||
{/* right sidebar */}
|
||||
<Box
|
||||
sx={{
|
||||
borderLeft: 1,
|
||||
borderColor: "divider",
|
||||
flex: '0 0 content',
|
||||
overflowY: "auto",
|
||||
overflowX: "visible",
|
||||
maxWidth: 'min(300px, 30vw)',
|
||||
}}>
|
||||
<ShowAST {...{...ast, rt: rt.at(rtIdx!), highlightActive}}/>
|
||||
<ShowOutputEvents outputEvents={ast.outputEvents}/>
|
||||
<br/>
|
||||
<div ref={refRightSideBar}>
|
||||
<RTHistory {...{ast, rt, rtIdx, setTime, setRTIdx, refRightSideBar}}/>
|
||||
</div>
|
||||
</Box>
|
||||
{/* Right: sidebar */}
|
||||
<Box sx={{
|
||||
borderLeft: 1,
|
||||
borderColor: "divider",
|
||||
flex: '0 0 content',
|
||||
overflowY: "auto",
|
||||
overflowX: "visible",
|
||||
maxWidth: 'min(300px, 30vw)',
|
||||
}}>
|
||||
<Stack sx={{height:'100%'}}>
|
||||
<Box className="onTop" sx={{flex: '0 0 content', backgroundColor: ''}}>
|
||||
<details open={showStateTree}
|
||||
onToggle={e => setShowStateTree(e.newState === "open")}>
|
||||
<summary>state tree</summary>
|
||||
<ul>
|
||||
<ShowAST {...{...ast, rt: rt.at(rtIdx!), highlightActive}}/>
|
||||
</ul>
|
||||
</details>
|
||||
<hr/>
|
||||
<details open={showInputEvents}
|
||||
onToggle={e => setShowInputEvents(e.newState === "open")}>
|
||||
<summary>input events</summary>
|
||||
<ShowInputEvents inputEvents={ast.inputEvents} onRaise={onRaise} disabled={rtIdx===undefined}/>
|
||||
</details>
|
||||
<hr/>
|
||||
<details open={showOutputEvents}
|
||||
onToggle={e => setShowOutputEvents(e.newState === "open")}>
|
||||
<summary>output events</summary>
|
||||
<ShowOutputEvents outputEvents={ast.outputEvents}/>
|
||||
</details>
|
||||
</Box>
|
||||
<Box sx={{
|
||||
flexGrow:1,
|
||||
overflow:'auto',
|
||||
minHeight: '75%', // <-- allows us to always scroll down the sidebar far enough such that the execution history is enough in view
|
||||
}}>
|
||||
<Box sx={{ height: '100%'}}>
|
||||
<div ref={refRightSideBar}>
|
||||
<RTHistory {...{ast, rt, rtIdx, setTime, setRTIdx, refRightSideBar}}/>
|
||||
</div>
|
||||
</Box>
|
||||
</Box>
|
||||
</Stack>
|
||||
</Box>
|
||||
|
||||
|
||||
</Stack>
|
||||
<Box sx={{
|
||||
flex: '0 0 content',
|
||||
}}>
|
||||
|
||||
{/* Bottom panel */}
|
||||
<Box sx={{flex: '0 0 content'}}>
|
||||
<BottomPanel {...{errors}}/>
|
||||
</Box>
|
||||
</Stack>
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue