fired transitions run animation once
This commit is contained in:
parent
0ba4fd4cae
commit
29808a683c
6 changed files with 34 additions and 14 deletions
|
|
@ -6,9 +6,10 @@ import "./BottomPanel.css";
|
||||||
import head from "../head.svg" ;
|
import head from "../head.svg" ;
|
||||||
import { usePersistentState } from "@/util/persistent_state";
|
import { usePersistentState } from "@/util/persistent_state";
|
||||||
import { PersistentDetails } from "./PersistentDetails";
|
import { PersistentDetails } from "./PersistentDetails";
|
||||||
|
import { DigitalWatch } from "@/Plant/DigitalWatch/DigitalWatch";
|
||||||
|
|
||||||
export function BottomPanel(props: {errors: TraceableError[]}) {
|
export function BottomPanel(props: {errors: TraceableError[]}) {
|
||||||
const [greeting, setGreeting] = useState(<><b><img src={head} style={{transform: "scaleX(-1)"}}/> "Welcome to StateBuddy, buddy!"</b></>);
|
const [greeting, setGreeting] = useState(<><b><img src={head} style={{transform: "scaleX(-1)"}}/> "Welcome to StateBuddy, buddy!"</b><br/></>);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
|
|
@ -17,7 +18,8 @@ export function BottomPanel(props: {errors: TraceableError[]}) {
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
return <div className="toolbar bottom">
|
return <div className="toolbar bottom">
|
||||||
<>{greeting}</>
|
{greeting}
|
||||||
|
<DigitalWatch alarm={true} light={true} h={12} m={30} s={33}/>
|
||||||
{props.errors.length > 0 &&
|
{props.errors.length > 0 &&
|
||||||
<div className="errorStatus">
|
<div className="errorStatus">
|
||||||
<PersistentDetails initiallyOpen={false} localStorageKey="errorsExpanded">
|
<PersistentDetails initiallyOpen={false} localStorageKey="errorsExpanded">
|
||||||
|
|
|
||||||
|
|
@ -1,9 +1,10 @@
|
||||||
import { Dispatch, ReactElement, SetStateAction, useState, KeyboardEvent } from "react";
|
import { Dispatch, ReactElement, SetStateAction, useState, KeyboardEvent, useEffect, useRef } from "react";
|
||||||
|
|
||||||
import { parse as parseLabel } from "../statecharts/label_parser";
|
import { parse as parseLabel } from "../statecharts/label_parser";
|
||||||
|
|
||||||
export function TextDialog(props: {setModal: Dispatch<SetStateAction<ReactElement|null>>, text: string, done: (newText: string|undefined) => void}) {
|
export function TextDialog(props: {setModal: Dispatch<SetStateAction<ReactElement|null>>, text: string, done: (newText: string|undefined) => void}) {
|
||||||
const [text, setText] = useState(props.text);
|
const [text, setText] = useState(props.text);
|
||||||
|
|
||||||
function onKeyDown(e: KeyboardEvent) {
|
function onKeyDown(e: KeyboardEvent) {
|
||||||
if (e.key === "Enter") {
|
if (e.key === "Enter") {
|
||||||
if (!e.shiftKey) {
|
if (!e.shiftKey) {
|
||||||
|
|
@ -19,19 +20,19 @@ export function TextDialog(props: {setModal: Dispatch<SetStateAction<ReactElemen
|
||||||
e.stopPropagation();
|
e.stopPropagation();
|
||||||
}
|
}
|
||||||
|
|
||||||
let error = "";
|
let parseError = "";
|
||||||
try {
|
try {
|
||||||
const parsed = parseLabel(text);
|
parseLabel(text);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
error = e.message;
|
parseError = e.message;
|
||||||
}
|
}
|
||||||
|
|
||||||
return <div onKeyDown={onKeyDown} style={{padding: 4}}>
|
return <div onKeyDown={onKeyDown} style={{padding: 4}}>
|
||||||
Text label:<br/>
|
Text label:<br/>
|
||||||
<textarea autoFocus style={{fontFamily: 'Roboto', width:'calc(100%-10px)', height: 60}} onChange={e=>setText(e.target.value)} value={text}/>
|
<textarea autoFocus style={{fontFamily: 'Roboto', width: 400, height: 60}} onChange={e=>setText(e.target.value)} value={text} onFocus={e => e.target.select()}/>
|
||||||
<br/>
|
<br/>
|
||||||
<span style={{color: 'var(--error-color)'}}>{error}</span><br/>
|
<span style={{color: 'var(--error-color)'}}>{parseError}</span><br/>
|
||||||
<p><kbd>Enter</kbd> to confirm. <kbd>Esc</kbd> to cancel.
|
<p><kbd>Enter</kbd> to confirm. <kbd>Esc</kbd> to cancel.
|
||||||
</p>
|
</p>
|
||||||
(Tip: <kbd>Shift</kbd>+<kbd>Enter</kbd> to insert newline.)
|
(Tip: <kbd>Shift</kbd>+<kbd>Enter</kbd> to insert newline.)
|
||||||
|
|
|
||||||
|
|
@ -35,7 +35,7 @@ export function DigitalWatch({light, h, m, s, alarm, callbacks}: DigitalWatchPro
|
||||||
src: url(${digitalFont});
|
src: url(${digitalFont});
|
||||||
}
|
}
|
||||||
`}</style>
|
`}</style>
|
||||||
<svg version="1.1" width="222" height="236">
|
<svg version="1.1" width="222" height="236" style={{userSelect: 'none'}}>
|
||||||
{light ?
|
{light ?
|
||||||
<image width="222" height="236" xlinkHref={imgWatchLight}/>
|
<image width="222" height="236" xlinkHref={imgWatchLight}/>
|
||||||
: <image width="222" height="236" xlinkHref={imgWatch}/>
|
: <image width="222" height="236" xlinkHref={imgWatch}/>
|
||||||
|
|
|
||||||
|
|
@ -161,17 +161,18 @@ text.helper:hover {
|
||||||
}
|
}
|
||||||
.arrow.fired {
|
.arrow.fired {
|
||||||
stroke: rgb(160, 0, 168);
|
stroke: rgb(160, 0, 168);
|
||||||
stroke-width: 4px;
|
stroke-width: 3px;
|
||||||
/* animation: blinkTransition 1s infinite; */
|
animation: blinkTransition 1s;
|
||||||
}
|
}
|
||||||
|
|
||||||
@keyframes blinkTransition {
|
@keyframes blinkTransition {
|
||||||
100%,
|
|
||||||
0% {
|
0% {
|
||||||
stroke: rgb(231, 111, 0);
|
stroke: rgb(255, 128, 9);
|
||||||
|
stroke-width: 6px;
|
||||||
|
filter: drop-shadow(0 0 5px rgba(255, 128, 9, 1));
|
||||||
}
|
}
|
||||||
100% {
|
100% {
|
||||||
stroke: rgb(160, 0, 168);
|
stroke: rgb(160 0 168);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -119,6 +119,21 @@ export function VisualEditor({state, setState, ast, setAST, rt, errors, setError
|
||||||
}
|
}
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
// bit of a hacky way to force the animation on fired transitions to replay, if the new 'rt' contains the same fired transitions as the previous one
|
||||||
|
requestAnimationFrame(() => {
|
||||||
|
console.log('rt changed');
|
||||||
|
document.querySelectorAll(".arrow.fired").forEach(el => {
|
||||||
|
el.style.animation = 'none';
|
||||||
|
requestAnimationFrame(() => {
|
||||||
|
el.style.animation = '';
|
||||||
|
})
|
||||||
|
setTimeout(() => {
|
||||||
|
}, 10); // <- small timeout seems to be necessary or the animation won't restart
|
||||||
|
});
|
||||||
|
})
|
||||||
|
}, [rt]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const timeout = setTimeout(() => {
|
const timeout = setTimeout(() => {
|
||||||
const serializedState = JSON.stringify(state);
|
const serializedState = JSON.stringify(state);
|
||||||
|
|
|
||||||
1
todo.txt
1
todo.txt
|
|
@ -33,6 +33,7 @@ TODO
|
||||||
- regions in AND-state
|
- regions in AND-state
|
||||||
|
|
||||||
- usability stuff:
|
- usability stuff:
|
||||||
|
- show internal events
|
||||||
- highlight selected shapes while making a selection
|
- highlight selected shapes while making a selection
|
||||||
- comments sometimes snap to transitions even if they belong to a state
|
- comments sometimes snap to transitions even if they belong to a state
|
||||||
- highlight fired transitions
|
- highlight fired transitions
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue