editable connections sc <-> plant
This commit is contained in:
parent
e27d3c4c88
commit
8ac5a730cc
28 changed files with 1191 additions and 1016 deletions
135
src/App/VisualEditor/useCopyPaste.ts
Normal file
135
src/App/VisualEditor/useCopyPaste.ts
Normal file
|
|
@ -0,0 +1,135 @@
|
|||
import { Arrow, Diamond, Rountangle, Text, History } from "@/statecharts/concrete_syntax";
|
||||
import { ClipboardEvent, Dispatch, SetStateAction, useCallback, useEffect } from "react";
|
||||
import { Selection, VisualEditorState } from "./VisualEditor";
|
||||
import { addV2D } from "@/util/geometry";
|
||||
|
||||
// const offset = {x: 40, y: 40};
|
||||
const offset = {x: 0, y: 0};
|
||||
|
||||
export function useCopyPaste(makeCheckPoint: () => void, state: VisualEditorState, setState: Dispatch<(v:VisualEditorState) => VisualEditorState>, selection: Selection) {
|
||||
const onPaste = useCallback((e: ClipboardEvent) => {
|
||||
const data = e.clipboardData?.getData("text/plain");
|
||||
if (data) {
|
||||
try {
|
||||
const parsed = JSON.parse(data);
|
||||
setState(state => {
|
||||
try {
|
||||
let nextID = state.nextID;
|
||||
const copiedRountangles: Rountangle[] = parsed.rountangles.map((r: Rountangle) => ({
|
||||
...r,
|
||||
uid: (nextID++).toString(),
|
||||
topLeft: addV2D(r.topLeft, offset),
|
||||
} as Rountangle));
|
||||
const copiedDiamonds: Diamond[] = parsed.diamonds.map((r: Diamond) => ({
|
||||
...r,
|
||||
uid: (nextID++).toString(),
|
||||
topLeft: addV2D(r.topLeft, offset),
|
||||
} as Diamond));
|
||||
const copiedArrows: Arrow[] = parsed.arrows.map((a: Arrow) => ({
|
||||
...a,
|
||||
uid: (nextID++).toString(),
|
||||
start: addV2D(a.start, offset),
|
||||
end: addV2D(a.end, offset),
|
||||
} as Arrow));
|
||||
const copiedTexts: Text[] = parsed.texts.map((t: Text) => ({
|
||||
...t,
|
||||
uid: (nextID++).toString(),
|
||||
topLeft: addV2D(t.topLeft, offset),
|
||||
} as Text));
|
||||
const copiedHistories: History[] = parsed.history.map((h: History) => ({
|
||||
...h,
|
||||
uid: (nextID++).toString(),
|
||||
topLeft: addV2D(h.topLeft, offset),
|
||||
}))
|
||||
// @ts-ignore
|
||||
const newSelection: Selection = [
|
||||
...copiedRountangles.map(r => ({uid: r.uid, parts: ["left", "top", "right", "bottom"]})),
|
||||
...copiedDiamonds.map(d => ({uid: d.uid, parts: ["left", "top", "right", "bottom"]})),
|
||||
...copiedArrows.map(a => ({uid: a.uid, parts: ["start", "end"]})),
|
||||
...copiedTexts.map(t => ({uid: t.uid, parts: ["text"]})),
|
||||
...copiedHistories.map(h => ({uid: h.uid, parts: ["history"]})),
|
||||
];
|
||||
makeCheckPoint();
|
||||
return {
|
||||
...state,
|
||||
rountangles: [...state.rountangles, ...copiedRountangles],
|
||||
diamonds: [...state.diamonds, ...copiedDiamonds],
|
||||
arrows: [...state.arrows, ...copiedArrows],
|
||||
texts: [...state.texts, ...copiedTexts],
|
||||
history: [...state.history, ...copiedHistories],
|
||||
nextID: nextID,
|
||||
selection: newSelection,
|
||||
};
|
||||
}
|
||||
catch (e) {
|
||||
console.warn("error pasting data. most likely you're tying to paste nonsense. ", e);
|
||||
return state;
|
||||
}
|
||||
});
|
||||
}
|
||||
catch (e) {
|
||||
console.warn("error pasting data. most likely you're tying to paste nonsense. ", e);
|
||||
}
|
||||
e.preventDefault();
|
||||
}
|
||||
}, [setState]);
|
||||
|
||||
const copyInternal = useCallback((state: VisualEditorState, selection: Selection, e: ClipboardEvent) => {
|
||||
const uidsToCopy = new Set(selection.map(shape => shape.uid));
|
||||
const rountanglesToCopy = state.rountangles.filter(r => uidsToCopy.has(r.uid));
|
||||
const diamondsToCopy = state.diamonds.filter(d => uidsToCopy.has(d.uid));
|
||||
const historiesToCopy = state.history.filter(h => uidsToCopy.has(h.uid));
|
||||
const arrowsToCopy = state.arrows.filter(a => uidsToCopy.has(a.uid));
|
||||
const textsToCopy = state.texts.filter(t => uidsToCopy.has(t.uid));
|
||||
e.clipboardData?.setData("text/plain", JSON.stringify({
|
||||
rountangles: rountanglesToCopy,
|
||||
diamonds: diamondsToCopy,
|
||||
history: historiesToCopy,
|
||||
arrows: arrowsToCopy,
|
||||
texts: textsToCopy,
|
||||
}));
|
||||
}, []);
|
||||
|
||||
const onCopy = useCallback((e: ClipboardEvent) => {
|
||||
if (selection.length > 0) {
|
||||
e.preventDefault();
|
||||
copyInternal(state, selection, e);
|
||||
}
|
||||
}, [state, selection]);
|
||||
|
||||
const onCut = useCallback((e: ClipboardEvent) => {
|
||||
if (selection.length > 0) {
|
||||
copyInternal(state, selection, e);
|
||||
deleteSelection();
|
||||
e.preventDefault();
|
||||
}
|
||||
}, [state, selection]);
|
||||
|
||||
const deleteSelection = useCallback(() => {
|
||||
setState(state => ({
|
||||
...state,
|
||||
rountangles: state.rountangles.filter(r => !state.selection.some(rs => rs.uid === r.uid)),
|
||||
diamonds: state.diamonds.filter(d => !state.selection.some(ds => ds.uid === d.uid)),
|
||||
history: state.history.filter(h => !state.selection.some(hs => hs.uid === h.uid)),
|
||||
arrows: state.arrows.filter(a => !state.selection.some(as => as.uid === a.uid)),
|
||||
texts: state.texts.filter(t => !state.selection.some(ts => ts.uid === t.uid)),
|
||||
selection: [],
|
||||
}));
|
||||
}, [setState]);
|
||||
|
||||
const onKeyDown = (e: KeyboardEvent) => {
|
||||
if (e.key === "Delete") {
|
||||
// delete selection
|
||||
makeCheckPoint();
|
||||
deleteSelection();
|
||||
e.preventDefault();
|
||||
}
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
window.addEventListener("keydown", onKeyDown);
|
||||
return () => window.removeEventListener("keydown", onKeyDown);
|
||||
})
|
||||
|
||||
return {onCopy, onPaste, onCut, deleteSelection};
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue