custom dialog for editing text labels (no more window.prompt)
This commit is contained in:
parent
20f28d8382
commit
88dee7e3b9
6 changed files with 98 additions and 62 deletions
|
|
@ -184,7 +184,7 @@ export function App() {
|
||||||
flexGrow:1,
|
flexGrow:1,
|
||||||
overflow:'auto',
|
overflow:'auto',
|
||||||
}}>
|
}}>
|
||||||
<VisualEditor {...{ast, setAST, rt: rt.at(rtIdx!), setRT, errors, setErrors, mode, highlightActive, highlightTransitions}}/>
|
<VisualEditor {...{ast, setAST, rt: rt.at(rtIdx!), setRT, errors, setErrors, mode, highlightActive, highlightTransitions, setModal}}/>
|
||||||
</Box>
|
</Box>
|
||||||
|
|
||||||
{/* right sidebar */}
|
{/* right sidebar */}
|
||||||
|
|
|
||||||
26
src/App/TextDialog.tsx
Normal file
26
src/App/TextDialog.tsx
Normal file
|
|
@ -0,0 +1,26 @@
|
||||||
|
import { Dispatch, ReactElement, SetStateAction, useState } from "react";
|
||||||
|
|
||||||
|
export function TextDialog(props: {setModal: Dispatch<SetStateAction<ReactElement|null>>, text: string, done: (newText: string|undefined) => void}) {
|
||||||
|
const [text, setText] = useState(props.text);
|
||||||
|
function onKeyDown(e: KeyboardEvent) {
|
||||||
|
if (e.key === "Enter") {
|
||||||
|
if (!e.shiftKey) {
|
||||||
|
e.preventDefault();
|
||||||
|
props.done(text);
|
||||||
|
props.setModal(null);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (e.key === "Escape") {
|
||||||
|
props.setModal(null);
|
||||||
|
e.stopPropagation();
|
||||||
|
}
|
||||||
|
e.stopPropagation();
|
||||||
|
}
|
||||||
|
return <span onKeyDown={onKeyDown} style={{backgroundColor:'white'}}>
|
||||||
|
<textarea style={{fontFamily: 'Roboto', width:500, height: 100}} onChange={e=>setText(e.target.value)}>{text}</textarea>
|
||||||
|
<br/>
|
||||||
|
<span style={{backgroundColor:'lightyellow'}}>
|
||||||
|
Tip: <kbd>Shift</kbd>+<kbd>Enter</kbd> to insert newline.
|
||||||
|
</span>
|
||||||
|
</span>;
|
||||||
|
}
|
||||||
|
|
@ -75,38 +75,40 @@ export function TopPanel({rt, rtIdx, time, setTime, onInit, onClear, onRaise, on
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const onKeyDown = (e: KeyboardEvent) => {
|
const onKeyDown = (e: KeyboardEvent) => {
|
||||||
if (e.key === " ") {
|
if (!e.ctrlKey) {
|
||||||
e.preventDefault();
|
if (e.key === " ") {
|
||||||
if (rt)
|
e.preventDefault();
|
||||||
onChangePaused(time.kind !== "paused", Math.round(performance.now()));
|
if (rt)
|
||||||
};
|
onChangePaused(time.kind !== "paused", Math.round(performance.now()));
|
||||||
if (e.key === "i") {
|
};
|
||||||
e.preventDefault();
|
if (e.key === "i") {
|
||||||
onInit();
|
e.preventDefault();
|
||||||
}
|
onInit();
|
||||||
if (e.key === "c") {
|
}
|
||||||
e.preventDefault();
|
if (e.key === "c") {
|
||||||
onClear();
|
e.preventDefault();
|
||||||
}
|
onClear();
|
||||||
if (e.key === "Tab") {
|
}
|
||||||
e.preventDefault();
|
if (e.key === "Tab") {
|
||||||
onSkip();
|
e.preventDefault();
|
||||||
}
|
onSkip();
|
||||||
if (e.key === "s") {
|
}
|
||||||
e.preventDefault();
|
if (e.key === "s") {
|
||||||
onSlower();
|
e.preventDefault();
|
||||||
}
|
onSlower();
|
||||||
if (e.key === "f") {
|
}
|
||||||
e.preventDefault();
|
if (e.key === "f") {
|
||||||
onFaster();
|
e.preventDefault();
|
||||||
}
|
onFaster();
|
||||||
if (e.key === "`") {
|
}
|
||||||
e.preventDefault();
|
if (e.key === "`") {
|
||||||
setShowKeys(show => !show);
|
e.preventDefault();
|
||||||
}
|
setShowKeys(show => !show);
|
||||||
if (e.key === "Backspace") {
|
}
|
||||||
e.preventDefault();
|
if (e.key === "Backspace") {
|
||||||
onBack();
|
e.preventDefault();
|
||||||
|
onBack();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
window.addEventListener("keydown", onKeyDown);
|
window.addEventListener("keydown", onKeyDown);
|
||||||
|
|
|
||||||
|
|
@ -3,26 +3,28 @@ import { InsertMode } from "../VisualEditor/VisualEditor";
|
||||||
|
|
||||||
export function getKeyHandler(setMode: Dispatch<SetStateAction<InsertMode>>) {
|
export function getKeyHandler(setMode: Dispatch<SetStateAction<InsertMode>>) {
|
||||||
return function onKeyDown(e: KeyboardEvent) {
|
return function onKeyDown(e: KeyboardEvent) {
|
||||||
if (e.key === "a") {
|
if (!e.ctrlKey) {
|
||||||
setMode("and");
|
if (e.key === "a") {
|
||||||
}
|
setMode("and");
|
||||||
if (e.key === "o") {
|
}
|
||||||
setMode("or");
|
if (e.key === "o") {
|
||||||
}
|
setMode("or");
|
||||||
if (e.key === "p") {
|
}
|
||||||
setMode("pseudo");
|
if (e.key === "p") {
|
||||||
}
|
setMode("pseudo");
|
||||||
if (e.key === "t") {
|
}
|
||||||
setMode("transition");
|
if (e.key === "t") {
|
||||||
}
|
setMode("transition");
|
||||||
if (e.key === "x") {
|
}
|
||||||
setMode("text");
|
if (e.key === "x") {
|
||||||
}
|
setMode("text");
|
||||||
if (e.key === "h") {
|
}
|
||||||
setMode(oldMode => {
|
if (e.key === "h") {
|
||||||
if (oldMode === "shallow") return "deep";
|
setMode(oldMode => {
|
||||||
return "shallow";
|
if (oldMode === "shallow") return "deep";
|
||||||
})
|
return "shallow";
|
||||||
|
})
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,9 @@
|
||||||
|
import { TextDialog } from "@/App/TextDialog";
|
||||||
import { TraceableError } from "..//statecharts/parser";
|
import { TraceableError } from "..//statecharts/parser";
|
||||||
import {Text} from "../statecharts/concrete_syntax";
|
import {Text} from "../statecharts/concrete_syntax";
|
||||||
|
import { Dispatch, ReactElement, SetStateAction } from "react";
|
||||||
|
|
||||||
export function TextSVG(props: {text: Text, error: TraceableError|undefined, selected: boolean, highlight: boolean, onEdit: (newText: string) => void}) {
|
export function TextSVG(props: {text: Text, error: TraceableError|undefined, selected: boolean, highlight: boolean, onEdit: (newText: string) => void, setModal: Dispatch<SetStateAction<ReactElement|null>>}) {
|
||||||
const commonProps = {
|
const commonProps = {
|
||||||
"data-uid": props.text.uid,
|
"data-uid": props.text.uid,
|
||||||
"data-parts": "text",
|
"data-parts": "text",
|
||||||
|
|
@ -9,6 +11,7 @@ export function TextSVG(props: {text: Text, error: TraceableError|undefined, sel
|
||||||
className: "draggableText"
|
className: "draggableText"
|
||||||
+ (props.selected ? " selected":"")
|
+ (props.selected ? " selected":"")
|
||||||
+ (props.highlight ? " highlight":""),
|
+ (props.highlight ? " highlight":""),
|
||||||
|
style: {whiteSpace: "preserve"},
|
||||||
}
|
}
|
||||||
|
|
||||||
let textNode;
|
let textNode;
|
||||||
|
|
@ -32,12 +35,13 @@ export function TextSVG(props: {text: Text, error: TraceableError|undefined, sel
|
||||||
key={props.text.uid}
|
key={props.text.uid}
|
||||||
transform={`translate(${props.text.topLeft.x} ${props.text.topLeft.y})`}
|
transform={`translate(${props.text.topLeft.x} ${props.text.topLeft.y})`}
|
||||||
onDoubleClick={() => {
|
onDoubleClick={() => {
|
||||||
const newText = prompt("", props.text.text);
|
props.setModal(<TextDialog setModal={props.setModal} text={props.text.text} done={newText => {
|
||||||
if (newText) {
|
if (newText) {
|
||||||
props.onEdit(newText);
|
props.onEdit(newText);
|
||||||
}
|
}
|
||||||
|
}} />)
|
||||||
}}>
|
}}>
|
||||||
{textNode}
|
{textNode}
|
||||||
<text className="draggableText helper" textAnchor="middle" data-uid={props.text.uid} data-parts="text">{props.text.text}</text>
|
<text className="draggableText helper" textAnchor="middle" data-uid={props.text.uid} data-parts="text" style={{whiteSpace: "preserve"}}>{props.text.text}</text>
|
||||||
</g>;
|
</g>;
|
||||||
}
|
}
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
import { Dispatch, SetStateAction, useEffect, useMemo, useRef, useState } from "react";
|
import { Dispatch, ReactElement, SetStateAction, useEffect, useMemo, useRef, useState } from "react";
|
||||||
|
|
||||||
import { Statechart } from "../statecharts/abstract_syntax";
|
import { Statechart } from "../statecharts/abstract_syntax";
|
||||||
import { Arrow, ArrowPart, Diamond, History, Rountangle, RountanglePart, Text, VisualEditorState, emptyState } from "../statecharts/concrete_syntax";
|
import { Arrow, ArrowPart, Diamond, History, Rountangle, RountanglePart, Text, VisualEditorState, emptyState } from "../statecharts/concrete_syntax";
|
||||||
|
|
@ -68,9 +68,10 @@ type VisualEditorProps = {
|
||||||
mode: InsertMode,
|
mode: InsertMode,
|
||||||
highlightActive: Set<string>,
|
highlightActive: Set<string>,
|
||||||
highlightTransitions: string[],
|
highlightTransitions: string[],
|
||||||
|
setModal: Dispatch<SetStateAction<ReactElement|null>>,
|
||||||
};
|
};
|
||||||
|
|
||||||
export function VisualEditor({ast, setAST, rt, errors, setErrors, mode, highlightActive, highlightTransitions}: VisualEditorProps) {
|
export function VisualEditor({ast, setAST, rt, errors, setErrors, mode, highlightActive, highlightTransitions, setModal}: VisualEditorProps) {
|
||||||
const [historyState, setHistoryState] = useState<HistoryState>({current: emptyState, history: [], future: []});
|
const [historyState, setHistoryState] = useState<HistoryState>({current: emptyState, history: [], future: []});
|
||||||
|
|
||||||
const state = historyState.current;
|
const state = historyState.current;
|
||||||
|
|
@ -324,7 +325,7 @@ export function VisualEditor({ast, setAST, rt, errors, setErrors, mode, highligh
|
||||||
setSelection([]);
|
setSelection([]);
|
||||||
};
|
};
|
||||||
|
|
||||||
const onMouseMove = (e: {pageX: number, pageY: number}) => {
|
const onMouseMove = (e: {pageX: number, pageY: number, movementX: number, movementY: number}) => {
|
||||||
const currentPointer = getCurrentPointer(e);
|
const currentPointer = getCurrentPointer(e);
|
||||||
if (dragging) {
|
if (dragging) {
|
||||||
// const pointerDelta = subtractV2D(currentPointer, dragging.lastMousePos);
|
// const pointerDelta = subtractV2D(currentPointer, dragging.lastMousePos);
|
||||||
|
|
@ -812,6 +813,7 @@ export function VisualEditor({ast, setAST, rt, errors, setErrors, mode, highligh
|
||||||
selected={Boolean(selection.find(s => s.uid === txt.uid)?.parts?.length)}
|
selected={Boolean(selection.find(s => s.uid === txt.uid)?.parts?.length)}
|
||||||
highlight={textsToHighlight.hasOwnProperty(txt.uid)}
|
highlight={textsToHighlight.hasOwnProperty(txt.uid)}
|
||||||
onEdit={newText => onEditText(txt, newText)}
|
onEdit={newText => onEditText(txt, newText)}
|
||||||
|
setModal={setModal}
|
||||||
/>
|
/>
|
||||||
})}
|
})}
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue