errors in bottom panel are collapsible

This commit is contained in:
Joeri Exelmans 2025-10-21 10:51:17 +02:00
parent 297905a4af
commit 60a7d12857
6 changed files with 74 additions and 69 deletions

View file

@ -18,7 +18,11 @@ details:open {
details > summary:hover {
background-color: #eee;
cursor: pointer;
cursor: default;
}
.errorStatus details > summary:hover {
background-color: rgb(102, 0, 0);
}
.stateTree > * {
@ -35,9 +39,9 @@ details > summary:hover {
}
/* if <details> has no children (besides the obvious <summary> child), then hide the marker */
details:not(:has(:not(summary))) > summary::marker {
/* details:not(:has(:not(summary))) > summary::marker {
content: " ";
}
} */
.outputEvent {
border: 1px black solid;

View file

@ -19,6 +19,7 @@ import { getKeyHandler } from "./shortcut_handler";
import { BottomPanel } from "./BottomPanel";
import { emptyState } from "@/statecharts/concrete_syntax";
import { usePersistentState } from "@/util/persistent_state";
import { PersistentDetails } from "./PersistentDetails";
type EditHistory = {
current: VisualEditorState,
@ -191,10 +192,6 @@ export function App() {
const highlightTransitions = (rtIdx === undefined) ? [] : rt[rtIdx].firedTransitions;
const [showStateTree, setShowStateTree] = usePersistentState("showStateTree", true);
const [showInputEvents, setShowInputEvents] = usePersistentState("showInputEvents", true);
const [showOutputEvents, setShowOutputEvents] = usePersistentState("showOutputEvents", true);
return <>
{/* Modal dialog */}
@ -245,25 +242,22 @@ export function App() {
}}>
<Stack sx={{height:'100%'}}>
<Box className="onTop" sx={{flex: '0 0 content', backgroundColor: ''}}>
<details open={showStateTree}
onToggle={e => setShowStateTree(e.newState === "open")}>
<PersistentDetails localStorageKey="showStateTree" initiallyOpen={true}>
<summary>state tree</summary>
<ul>
<ShowAST {...{...ast, rt: rt.at(rtIdx!), highlightActive}}/>
</ul>
</details>
</PersistentDetails>
<hr/>
<details open={showInputEvents}
onToggle={e => setShowInputEvents(e.newState === "open")}>
<PersistentDetails localStorageKey="showInputEvents" initiallyOpen={true}>
<summary>input events</summary>
<ShowInputEvents inputEvents={ast.inputEvents} onRaise={onRaise} disabled={rtIdx===undefined}/>
</details>
</PersistentDetails>
<hr/>
<details open={showOutputEvents}
onToggle={e => setShowOutputEvents(e.newState === "open")}>
<PersistentDetails localStorageKey="showOutputEvents" initiallyOpen={true}>
<summary>output events</summary>
<ShowOutputEvents outputEvents={ast.outputEvents}/>
</details>
</PersistentDetails>
</Box>
<Box sx={{
flexGrow:1,

View file

@ -4,6 +4,8 @@ import { TraceableError } from "../statecharts/parser";
import "./BottomPanel.css";
import head from "../head.svg" ;
import { usePersistentState } from "@/util/persistent_state";
import { PersistentDetails } from "./PersistentDetails";
export function BottomPanel(props: {errors: TraceableError[]}) {
const [greeting, setGreeting] = useState(<><b><img src={head} style={{transform: "scaleX(-1)"}}/>&emsp;"Welcome to StateBuddy, buddy!"</b></>);
@ -18,7 +20,14 @@ export function BottomPanel(props: {errors: TraceableError[]}) {
<>{greeting}</>
{props.errors.length > 0 &&
<div className="errorStatus">
{props.errors.length>0 && <>{props.errors.length} errors: {props.errors.map(({message})=>message).join(', ')}</>}
</div>}
<PersistentDetails initiallyOpen={false} localStorageKey="errorsExpanded">
<summary>{props.errors.length} errors</summary>
{props.errors.map(({message})=>
<div>
{message}
</div>)}
</PersistentDetails>
</div>
}
</div>;
}

View file

@ -0,0 +1,15 @@
import { usePersistentState } from "@/util/persistent_state"
import { DetailsHTMLAttributes, PropsWithChildren } from "react";
type Props = {
localStorageKey: string,
initiallyOpen?: boolean,
} & DetailsHTMLAttributes<HTMLDetailsElement>;
// A <details> node that remembers whether it was open or closed by storing that state in localStorage.
export function PersistentDetails({localStorageKey, initiallyOpen, children, ...rest}: PropsWithChildren<Props>) {
const [open, setOpen] = usePersistentState(localStorageKey, initiallyOpen);
return <details open={open} onToggle={e => setOpen(e.newState === "open")} {...rest}>
{children}
</details>;
}