|
@@ -1,21 +1,23 @@
|
|
|
-import * as React from "react";
|
|
|
+import { Buffer } from "buffer"; // NodeJS library
|
|
|
|
|
|
+import * as React from "react";
|
|
|
import {Grid, Text, Title, Group, Stack, SimpleGrid, Button, Space, Textarea, Tabs, HoverCard, ActionIcon} from "@mantine/core";
|
|
|
import {IconPlayerTrackPrev, IconPlayerTrackNext, IconInfoCircle} from "@tabler/icons";
|
|
|
|
|
|
-import {d3Types, Graph} from "./graph"
|
|
|
-import {EditableGraph, GraphType, NodeType, LinkType} from "./editable_graph";
|
|
|
import {GraphDeltaExecutor} from "../onion/delta_executor";
|
|
|
-import {D3StateManipulator} from "./d3_state_manipulator";
|
|
|
-
|
|
|
+import {CompositeDelta, CompositeLevel} from "../onion/composite_delta";
|
|
|
+import {Version, initialVersion, VersionRegistry} from "../onion/version";
|
|
|
+import {NodeCreation, NodeDeletion, EdgeCreation, EdgeUpdate} from "../onion/primitive_delta";
|
|
|
import {PrimitiveValue, UUID} from "../onion/types";
|
|
|
import {mockUuid} from "../onion/test_helpers";
|
|
|
-import {Version, initialVersion, VersionRegistry} from "../onion/version";
|
|
|
import {Delta} from "../onion/delta";
|
|
|
-import {CompositeDelta, CompositeLevel} from "../onion/composite_delta";
|
|
|
-import {NodeCreation, NodeDeletion, EdgeCreation, EdgeUpdate} from "../onion/primitive_delta";
|
|
|
-import {RountangleEditor} from "./rountangleEditor/RountangleEditor";
|
|
|
|
|
|
+import {TrivialParser} from "../parser/trivial_parser";
|
|
|
+
|
|
|
+import {d3Types, Graph} from "./graph"
|
|
|
+import {EditableGraph, GraphType, NodeType, LinkType} from "./editable_graph";
|
|
|
+import {D3StateManipulator} from "./d3_state_manipulator";
|
|
|
+import {RountangleEditor} from "./rountangleEditor/RountangleEditor";
|
|
|
import {
|
|
|
HistoryGraphType,
|
|
|
DependencyGraphType,
|
|
@@ -32,14 +34,12 @@ import {
|
|
|
addDeltaAndActivate,
|
|
|
} from "./app_state";
|
|
|
|
|
|
-import { Buffer } from "buffer"; // NodeJS library
|
|
|
|
|
|
const emptyGraph: d3Types.d3Graph<any,any> = {
|
|
|
nodes: [],
|
|
|
links: [],
|
|
|
};
|
|
|
|
|
|
-
|
|
|
// "physics" stuff (for graph layout)
|
|
|
const historyGraphForces = {charge: -600, center: 0.1, link: 2};
|
|
|
const depGraphForces = {charge: -200, center: 0.1, link: 0.2};
|
|
@@ -59,11 +59,11 @@ interface VersionedModelState {
|
|
|
dependencyGraphL0: DependencyGraphType; // the state of what is displayed in the rightmost panel
|
|
|
}
|
|
|
|
|
|
-interface VersionedModelCallbacks {
|
|
|
- setGraph: (callback: (GraphType) => GraphType) => void;
|
|
|
- newVersionHandler: (version: Version) => void;
|
|
|
- newDeltaHandler: (delta: CompositeDelta) => void;
|
|
|
-}
|
|
|
+// interface VersionedModelCallbacks {
|
|
|
+// setGraph: (callback: (GraphType) => GraphType) => void;
|
|
|
+// newVersionHandler: (version: Version) => void;
|
|
|
+// newDeltaHandler: (delta: CompositeDelta) => void;
|
|
|
+// }
|
|
|
|
|
|
const initialModel: VersionedModelState = {
|
|
|
version: initialVersion,
|
|
@@ -77,8 +77,11 @@ export interface VersionedModelProps {
|
|
|
title: string;
|
|
|
generateUUID: () => UUID;
|
|
|
|
|
|
- model: VersionedModelState;
|
|
|
- callbacks: VersionedModelCallbacks;
|
|
|
+ // model: VersionedModelState;
|
|
|
+ // callbacks: VersionedModelCallbacks;
|
|
|
+
|
|
|
+ state: VersionedModelState;
|
|
|
+ setState: (callback: (VersionedModelState) => VersionedModelState) => void;
|
|
|
}
|
|
|
|
|
|
class VersionedModel extends React.Component<VersionedModelProps, {}> {
|
|
@@ -95,15 +98,16 @@ class VersionedModel extends React.Component<VersionedModelProps, {}> {
|
|
|
// this.state =
|
|
|
|
|
|
// 'Glue' callback
|
|
|
- // const setGraph = (callback: (GraphType) => GraphType) => {
|
|
|
- // this.props.setState(oldState => ({
|
|
|
- // graph: callback(oldState.graph),
|
|
|
- // }));
|
|
|
- // };
|
|
|
+ const setGraph = (callback: (GraphType) => GraphType) => {
|
|
|
+ this.props.setState(({graph, ...rest}) => ({
|
|
|
+ graph: callback(graph),
|
|
|
+ ...rest,
|
|
|
+ }));
|
|
|
+ };
|
|
|
|
|
|
this.textarearef = React.createRef();
|
|
|
|
|
|
- this.manipulator = new D3StateManipulator(this.props.callbacks.setGraph);
|
|
|
+ this.manipulator = new D3StateManipulator(setGraph);
|
|
|
this.graphDeltaExecutor = new GraphDeltaExecutor(this.manipulator);
|
|
|
this.compositeLevel = new CompositeLevel();
|
|
|
this.versionRegistry = new VersionRegistry();
|
|
@@ -111,171 +115,157 @@ class VersionedModel extends React.Component<VersionedModelProps, {}> {
|
|
|
|
|
|
render() {
|
|
|
|
|
|
- // // appends a new version to the history graph
|
|
|
- // // and sets the "current version" to that version.
|
|
|
- // // PRECONDITION: the version is not yet part of the history graph, but all of its parents are!
|
|
|
- // const newVersionHandler = (version: Version) => {
|
|
|
- // this.setState(prevState => {
|
|
|
- // return {
|
|
|
- // version,
|
|
|
- // historyGraph: setCurrentVersion(appendToHistoryGraph(prevState.historyGraph, version), prevState.version, version),
|
|
|
- // };
|
|
|
- // });
|
|
|
- // };
|
|
|
-
|
|
|
- // // called whenever there's a new delta (as a result of a user edit)
|
|
|
- // // this function adds the new delta, its dependencies and conflict links to the dep-graph (if it isn't there already),
|
|
|
- // // and makes sure that the new delta is highlighted as a "current delta".
|
|
|
- // const newDeltaHandler = (deltaL1: CompositeDelta) => {
|
|
|
- // console.log("New delta:", deltaL1);
|
|
|
+ // appends a new version to the history graph
|
|
|
+ // and sets the "current version" to that version.
|
|
|
+ // PRECONDITION: the version is not yet part of the history graph, but all of its parents are!
|
|
|
+ const newVersionHandler = (version: Version) => {
|
|
|
+ this.props.setState(({historyGraph: prevHistoryGraph, version: prevVersion, ...rest}) => {
|
|
|
+ return {
|
|
|
+ version,
|
|
|
+ historyGraph: setCurrentVersion(appendToHistoryGraph(prevHistoryGraph, version), prevVersion, version),
|
|
|
+ ...rest,
|
|
|
+ };
|
|
|
+ });
|
|
|
+ };
|
|
|
|
|
|
- // this.setState(prevState => {
|
|
|
- // const prevDepGraphL0 = prevState.dependencyGraphL0
|
|
|
- // const prevDepGraphL1 = prevState.dependencyGraphL1;
|
|
|
- // return {
|
|
|
- // dependencyGraphL1: addDeltaAndActivate(prevDepGraphL1, deltaL1),
|
|
|
- // dependencyGraphL0: deltaL1.deltas.reduce((prevDepGraphL0, delta) => addDeltaAndActivate(prevDepGraphL0, delta), prevDepGraphL0),
|
|
|
- // }
|
|
|
- // });
|
|
|
- // };
|
|
|
+ // called whenever there's a new delta (as a result of a user edit)
|
|
|
+ // this function adds the new delta, its dependencies and conflict links to the dep-graph (if it isn't there already),
|
|
|
+ // and makes sure that the new delta is highlighted as a "current delta".
|
|
|
+ const newDeltaHandler = (deltaL1: CompositeDelta) => {
|
|
|
+ console.log("New delta:", deltaL1);
|
|
|
+
|
|
|
+ this.props.setState(({dependencyGraphL0: prevDepGraphL0, dependencyGraphL1: prevDepGraphL1, ...rest}) => {
|
|
|
+ return {
|
|
|
+ dependencyGraphL1: addDeltaAndActivate(prevDepGraphL1, deltaL1),
|
|
|
+ dependencyGraphL0: deltaL1.deltas.reduce((prevDepGraphL0, delta) => addDeltaAndActivate(prevDepGraphL0, delta), prevDepGraphL0),
|
|
|
+ ...rest,
|
|
|
+ }
|
|
|
+ });
|
|
|
+ };
|
|
|
|
|
|
const historyMouseUpHandler = (event, {x,y}, node: d3Types.d3Node<Version|null> | undefined) => {
|
|
|
- // if (node !== undefined) {
|
|
|
- // // the user clicked on a version -> the clicked version becomes the "current version"
|
|
|
- // const versionClicked = node.obj;
|
|
|
- // if (versionClicked !== null) {
|
|
|
- // if (this.textarearef.current) {
|
|
|
- // this.textarearef.current.value += node.id + '\n';
|
|
|
- // }
|
|
|
-
|
|
|
- // const path = this.state.version.findPathTo(versionClicked);
|
|
|
- // // console.log("path to clicked version:", path);
|
|
|
- // if (path === undefined) {
|
|
|
- // throw new Error("Could not find path to clicked version!");
|
|
|
- // }
|
|
|
-
|
|
|
- // this.manipulator.x = 0;
|
|
|
- // this.manipulator.y = 0;
|
|
|
-
|
|
|
- // for (const [linkType, delta] of path) {
|
|
|
- // if (linkType === 'p') {
|
|
|
- // undo(delta);
|
|
|
- // }
|
|
|
- // else if (linkType === 'c') {
|
|
|
- // redo(delta);
|
|
|
- // }
|
|
|
- // }
|
|
|
-
|
|
|
- // this.setState(prevState => ({
|
|
|
- // version: versionClicked,
|
|
|
- // historyGraph: setCurrentVersion(prevState.historyGraph, prevState.version, versionClicked),
|
|
|
- // }));
|
|
|
- // }
|
|
|
- // }
|
|
|
+ if (node !== undefined) {
|
|
|
+ // the user clicked on a version -> the clicked version becomes the "current version"
|
|
|
+ const versionClicked = node.obj;
|
|
|
+ if (versionClicked !== null) {
|
|
|
+ if (this.textarearef.current) {
|
|
|
+ this.textarearef.current.value += node.id + '\n';
|
|
|
+ }
|
|
|
+
|
|
|
+ const path = this.props.state.version.findPathTo(versionClicked);
|
|
|
+ // console.log("path to clicked version:", path);
|
|
|
+ if (path === undefined) {
|
|
|
+ throw new Error("Could not find path to clicked version!");
|
|
|
+ }
|
|
|
+
|
|
|
+ this.manipulator.x = 0;
|
|
|
+ this.manipulator.y = 0;
|
|
|
+
|
|
|
+ for (const [linkType, delta] of path) {
|
|
|
+ if (linkType === 'p') {
|
|
|
+ undo(delta);
|
|
|
+ }
|
|
|
+ else if (linkType === 'c') {
|
|
|
+ redo(delta);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ this.props.setState(({historyGraph: prevHistoryGraph, version: prevVersion, ...rest}) => ({
|
|
|
+ version: versionClicked,
|
|
|
+ historyGraph: setCurrentVersion(prevHistoryGraph, prevVersion, versionClicked),
|
|
|
+ ...rest,
|
|
|
+ }));
|
|
|
+ }
|
|
|
+ }
|
|
|
}
|
|
|
|
|
|
const undo = deltaToUndo => {
|
|
|
- // this.manipulator.x = 0;
|
|
|
- // this.manipulator.y = 0;
|
|
|
- // this.graphDeltaExecutor.unexec(deltaToUndo);
|
|
|
- // this.setState(prevState => ({
|
|
|
- // dependencyGraphL1: setDeltaInactive(prevState.dependencyGraphL1, deltaToUndo),
|
|
|
- // dependencyGraphL0: deltaToUndo.deltas.reduce((prevDepGraphL0, delta) => setDeltaInactive(prevDepGraphL0, delta), prevState.dependencyGraphL0),
|
|
|
- // }));
|
|
|
+ this.manipulator.x = 0;
|
|
|
+ this.manipulator.y = 0;
|
|
|
+ this.graphDeltaExecutor.unexec(deltaToUndo);
|
|
|
+ this.props.setState(({dependencyGraphL0: prevDepGraphL0, dependencyGraphL1: prevDepGraphL1, ...rest}) => ({
|
|
|
+ dependencyGraphL1: setDeltaInactive(prevDepGraphL1, deltaToUndo),
|
|
|
+ dependencyGraphL0: deltaToUndo.deltas.reduce((prevDepGraphL0, delta) => setDeltaInactive(prevDepGraphL0, delta), prevDepGraphL0),
|
|
|
+ ...rest,
|
|
|
+ }));
|
|
|
};
|
|
|
const redo = deltaToRedo => {
|
|
|
- // this.manipulator.x = 0;
|
|
|
- // this.manipulator.y = 0;
|
|
|
- // this.graphDeltaExecutor.exec(deltaToRedo);
|
|
|
- // this.setState(prevState => ({
|
|
|
- // dependencyGraphL1: setDeltaActive(prevState.dependencyGraphL1, deltaToRedo),
|
|
|
- // dependencyGraphL0: deltaToRedo.deltas.reduce((prevDepGraphL0, delta) => setDeltaActive(prevDepGraphL0, delta), prevState.dependencyGraphL0),
|
|
|
- // }));
|
|
|
+ this.manipulator.x = 0;
|
|
|
+ this.manipulator.y = 0;
|
|
|
+ this.graphDeltaExecutor.exec(deltaToRedo);
|
|
|
+ this.props.setState(({dependencyGraphL0: prevDepGraphL0, dependencyGraphL1: prevDepGraphL1, ...rest}) => ({
|
|
|
+ dependencyGraphL1: setDeltaActive(prevDepGraphL1, deltaToRedo),
|
|
|
+ dependencyGraphL0: deltaToRedo.deltas.reduce((prevDepGraphL0, delta) => setDeltaActive(prevDepGraphL0, delta), prevDepGraphL0),
|
|
|
+ ...rest,
|
|
|
+ }));
|
|
|
};
|
|
|
|
|
|
const onUndoClicked = (parentVersion, deltaToUndo) => {
|
|
|
- // undo(deltaToUndo);
|
|
|
- // this.setState(prevState => ({
|
|
|
- // version: parentVersion,
|
|
|
- // historyGraph: setCurrentVersion(prevState.historyGraph, prevState.version, parentVersion),
|
|
|
- // }));
|
|
|
+ undo(deltaToUndo);
|
|
|
+ this.props.setState(({historyGraph: prevHistoryGraph, version: prevVersion, ...rest}) => ({
|
|
|
+ version: parentVersion,
|
|
|
+ historyGraph: setCurrentVersion(prevHistoryGraph, prevVersion, parentVersion),
|
|
|
+ ...rest,
|
|
|
+ }));
|
|
|
};
|
|
|
const onRedoClicked = (childVersion, deltaToRedo) => {
|
|
|
- // redo(deltaToRedo);
|
|
|
- // this.setState(prevState => ({
|
|
|
- // version: childVersion,
|
|
|
- // historyGraph: setCurrentVersion(prevState.historyGraph, prevState.version, childVersion),
|
|
|
- // }));
|
|
|
+ redo(deltaToRedo);
|
|
|
+ this.props.setState(({historyGraph: prevHistoryGraph, version: prevVersion, ...rest}) => ({
|
|
|
+ version: childVersion,
|
|
|
+ historyGraph: setCurrentVersion(prevHistoryGraph, prevVersion, childVersion),
|
|
|
+ ...rest,
|
|
|
+ }));
|
|
|
};
|
|
|
|
|
|
- const undoButtons = this.props.model.version.parents.map(([parentVersion,deltaToUndo]) => {
|
|
|
- return (
|
|
|
- <div key={fullVersionId(parentVersion)}>
|
|
|
- <Button fullWidth={true} compact={true} leftIcon={<IconPlayerTrackPrev size={18}/>} onClick={onUndoClicked.bind(null, parentVersion,deltaToUndo)}>
|
|
|
- UNDO {deltaToUndo.getDescription()}
|
|
|
- </Button>
|
|
|
- <Space h="xs"/>
|
|
|
- </div>
|
|
|
- );
|
|
|
- });
|
|
|
- const redoButtons = this.props.model.version.children.map(([childVersion,deltaToRedo]) => {
|
|
|
- return (
|
|
|
- <div key={fullVersionId(childVersion)}>
|
|
|
- <Button style={{width: "100%"}} compact={true} rightIcon={<IconPlayerTrackNext size={18}/>} onClick={onRedoClicked.bind(null, childVersion,deltaToRedo)}>
|
|
|
- REDO {deltaToRedo.getDescription()}
|
|
|
- </Button>
|
|
|
- <Space h="xs"/>
|
|
|
- </div>
|
|
|
- );
|
|
|
- });
|
|
|
|
|
|
const onMergeClicked = () => {
|
|
|
- // const text = this.textarearef.current?.value;
|
|
|
- // if (text) {
|
|
|
- // const lines = text.split('\n');
|
|
|
-
|
|
|
- // const versionIds: Buffer[] = lines.map(s => {
|
|
|
- // try {
|
|
|
- // return Buffer.from(s, 'base64');
|
|
|
- // } catch (e) {
|
|
|
- // return Buffer.alloc(0);
|
|
|
- // }
|
|
|
- // }).filter(buf => buf.length !== 0);
|
|
|
-
|
|
|
- // let versionsToMerge;
|
|
|
- // try {
|
|
|
- // versionsToMerge = versionIds.map((buf: Buffer) => {
|
|
|
- // return this.versionRegistry.lookup(buf);
|
|
|
- // });
|
|
|
- // } catch (e) {
|
|
|
- // alert("Input error:" + e.toString());
|
|
|
- // return;
|
|
|
- // }
|
|
|
-
|
|
|
- // const mergedVersions = this.versionRegistry.merge(versionsToMerge);
|
|
|
-
|
|
|
- // console.log("mergedVersions:", mergedVersions);
|
|
|
-
|
|
|
- // const add = v => {
|
|
|
- // this.setState(prevState => {
|
|
|
- // return {
|
|
|
- // historyGraph: appendToHistoryGraph(prevState.historyGraph, v),
|
|
|
- // };
|
|
|
- // });
|
|
|
- // };
|
|
|
-
|
|
|
- // // add all mergedVersions (and their parents) to history graph:
|
|
|
- // // this may be overkill (and not so scalable), but it does the job :)
|
|
|
- // mergedVersions.forEach(v => {
|
|
|
- // const addParents = v => {
|
|
|
- // v.parents.forEach(([v]) => {
|
|
|
- // addParents(v);
|
|
|
- // });
|
|
|
- // add(v);
|
|
|
- // }
|
|
|
- // addParents(v);
|
|
|
- // });
|
|
|
- // }
|
|
|
+ const text = this.textarearef.current?.value;
|
|
|
+ if (text) {
|
|
|
+ const lines = text.split('\n');
|
|
|
+
|
|
|
+ const versionIds: Buffer[] = lines.map(s => {
|
|
|
+ try {
|
|
|
+ return Buffer.from(s, 'base64');
|
|
|
+ } catch (e) {
|
|
|
+ return Buffer.alloc(0);
|
|
|
+ }
|
|
|
+ }).filter(buf => buf.length !== 0);
|
|
|
+
|
|
|
+ let versionsToMerge;
|
|
|
+ try {
|
|
|
+ versionsToMerge = versionIds.map((buf: Buffer) => {
|
|
|
+ return this.versionRegistry.lookup(buf);
|
|
|
+ });
|
|
|
+ } catch (e) {
|
|
|
+ alert("Input error:" + e.toString());
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ const mergedVersions = this.versionRegistry.merge(versionsToMerge);
|
|
|
+
|
|
|
+ console.log("mergedVersions:", mergedVersions);
|
|
|
+
|
|
|
+ const add = v => {
|
|
|
+ this.props.setState(({historyGraph: prevHistoryGraph, ...rest}) => {
|
|
|
+ return {
|
|
|
+ historyGraph: appendToHistoryGraph(prevHistoryGraph, v),
|
|
|
+ ...rest,
|
|
|
+ };
|
|
|
+ });
|
|
|
+ };
|
|
|
+
|
|
|
+ // add all mergedVersions (and their parents) to history graph:
|
|
|
+ // this may be overkill (and not so scalable), but it does the job :)
|
|
|
+ mergedVersions.forEach(v => {
|
|
|
+ const addParents = v => {
|
|
|
+ v.parents.forEach(([v]) => {
|
|
|
+ addParents(v);
|
|
|
+ });
|
|
|
+ add(v);
|
|
|
+ }
|
|
|
+ addParents(v);
|
|
|
+ });
|
|
|
+ }
|
|
|
}
|
|
|
|
|
|
function makeInfoHoverCardIcon(contents) {
|
|
@@ -311,14 +301,14 @@ class VersionedModel extends React.Component<VersionedModelProps, {}> {
|
|
|
<Text>Mouse wheel: Zoom.</Text>
|
|
|
</>)}
|
|
|
<EditableGraph
|
|
|
- graph={this.props.model.graph}
|
|
|
+ graph={this.props.state.graph}
|
|
|
graphDeltaExecutor={this.graphDeltaExecutor}
|
|
|
forces={historyGraphForces}
|
|
|
generateUUID={this.props.generateUUID}
|
|
|
setNextNodePosition={(x,y)=>{this.manipulator.x = x; this.manipulator.y = y;}}
|
|
|
- newVersionHandler={this.props.callbacks.newVersionHandler}
|
|
|
- newDeltaHandler={this.props.callbacks.newDeltaHandler}
|
|
|
- version={this.props.model.version}
|
|
|
+ newVersionHandler={newVersionHandler}
|
|
|
+ newDeltaHandler={newDeltaHandler}
|
|
|
+ version={this.props.state.version}
|
|
|
compositeLvl={this.compositeLevel}
|
|
|
versionRegistry={this.versionRegistry}
|
|
|
/>
|
|
@@ -329,14 +319,14 @@ class VersionedModel extends React.Component<VersionedModelProps, {}> {
|
|
|
<Text>New Rountangle by Alt + Click</Text>
|
|
|
)}
|
|
|
<RountangleEditor
|
|
|
- graph={this.props.model.graph}
|
|
|
+ graph={this.props.state.graph}
|
|
|
graphDeltaExecutor={this.graphDeltaExecutor}
|
|
|
forces={historyGraphForces}
|
|
|
generateUUID={this.props.generateUUID}
|
|
|
setNextNodePosition={(x,y)=>{this.manipulator.x = x; this.manipulator.y = y;}}
|
|
|
- newVersionHandler={this.props.callbacks.newVersionHandler}
|
|
|
- newDeltaHandler={this.props.callbacks.newDeltaHandler}
|
|
|
- version={this.props.model.version}
|
|
|
+ newVersionHandler={newVersionHandler}
|
|
|
+ newDeltaHandler={newDeltaHandler}
|
|
|
+ version={this.props.state.version}
|
|
|
compositeLvl={this.compositeLevel}
|
|
|
versionRegistry={this.versionRegistry}
|
|
|
/>
|
|
@@ -346,14 +336,14 @@ class VersionedModel extends React.Component<VersionedModelProps, {}> {
|
|
|
{makeInfoHoverCardIcon(
|
|
|
<Text>Active deltas are bold.</Text>
|
|
|
)}
|
|
|
- <Graph graph={this.props.model.dependencyGraphL1} forces={depGraphForces} mouseDownHandler={()=>{}} mouseUpHandler={()=>{}} />
|
|
|
+ <Graph graph={this.props.state.dependencyGraphL1} forces={depGraphForces} mouseDownHandler={()=>{}} mouseUpHandler={()=>{}} />
|
|
|
</Tabs.Panel>
|
|
|
|
|
|
<Tabs.Panel value="dependencyL0">
|
|
|
{makeInfoHoverCardIcon(
|
|
|
<Text>Active deltas are bold.</Text>
|
|
|
)}
|
|
|
- <Graph graph={this.props.model.dependencyGraphL0} forces={depGraphForces} mouseDownHandler={()=>{}} mouseUpHandler={()=>{}} />
|
|
|
+ <Graph graph={this.props.state.dependencyGraphL0} forces={depGraphForces} mouseDownHandler={()=>{}} mouseUpHandler={()=>{}} />
|
|
|
</Tabs.Panel>
|
|
|
|
|
|
<Tabs.Panel value="history">
|
|
@@ -361,20 +351,7 @@ class VersionedModel extends React.Component<VersionedModelProps, {}> {
|
|
|
<Text>All links are parent links.</Text>
|
|
|
<Text>Right or middle mouse button: Load version.</Text>
|
|
|
</>)}
|
|
|
- <Graph graph={this.props.model.historyGraph} forces={historyGraphForces} mouseDownHandler={()=>{}} mouseUpHandler={historyMouseUpHandler} />
|
|
|
- <SimpleGrid cols={2}>
|
|
|
- <div>
|
|
|
- {undoButtons}
|
|
|
- </div>
|
|
|
- <div>
|
|
|
- {redoButtons}
|
|
|
- </div>
|
|
|
- </SimpleGrid>
|
|
|
- <Space h="xs"/>
|
|
|
- <Textarea size="xs" label="Versions to merge:" ref={this.textarearef} autosize></Textarea>
|
|
|
- <Button onClick={() => {if (this.textarearef.current) this.textarearef.current.value = "";}}>CLEAR</Button>
|
|
|
-
|
|
|
- <Button onClick={onMergeClicked}>MERGE</Button>
|
|
|
+ <Graph graph={this.props.state.historyGraph} forces={historyGraphForces} mouseDownHandler={()=>{}} mouseUpHandler={historyMouseUpHandler} />
|
|
|
</Tabs.Panel>
|
|
|
</Tabs>
|
|
|
);
|
|
@@ -386,6 +363,12 @@ class VersionedModel extends React.Component<VersionedModelProps, {}> {
|
|
|
{makeConcreteSyntaxTabs("editor")}
|
|
|
{makeConcreteSyntaxTabs("state")}
|
|
|
</Stack>
|
|
|
+
|
|
|
+ <Textarea size="xs" label="Versions to merge:" ref={this.textarearef}
|
|
|
+ autosize placeholder="Right-click on version in History Graph to add it to this textbox"></Textarea>
|
|
|
+ <Button onClick={() => {if (this.textarearef.current) this.textarearef.current.value = "";}}>CLEAR</Button>
|
|
|
+
|
|
|
+ <Button onClick={onMergeClicked}>MERGE</Button>
|
|
|
</>
|
|
|
);
|
|
|
}
|
|
@@ -397,36 +380,110 @@ interface AppState {
|
|
|
as: VersionedModelState;
|
|
|
}
|
|
|
|
|
|
-export class App extends React.Component<{}, AppState> {
|
|
|
- generateUUID = mockUuid();
|
|
|
+// export class App extends React.Component<{}, AppState> {
|
|
|
+// generateUUID = mockUuid();
|
|
|
|
|
|
- constructor(props) {
|
|
|
- super(props);
|
|
|
- this.state = {
|
|
|
- cs: initialModel,
|
|
|
- corr: initialModel,
|
|
|
- as: initialModel,
|
|
|
- };
|
|
|
- }
|
|
|
+// constructor(props) {
|
|
|
+// super(props);
|
|
|
+// this.state = {
|
|
|
+// cs: initialModel,
|
|
|
+// corr: initialModel,
|
|
|
+// as: initialModel,
|
|
|
+// };
|
|
|
+// }
|
|
|
|
|
|
- render() {
|
|
|
+// render() {
|
|
|
|
|
|
- return (<>
|
|
|
- <Grid grow>
|
|
|
- <Grid.Col span={1}>
|
|
|
- <VersionedModel title="Concrete Syntax" generateUUID={this.generateUUID} model={this.state.cs} callbacks={{
|
|
|
- setGraph: (callback: (GraphType) => GraphType) => {
|
|
|
- this.setState(oldState => ({
|
|
|
- cs: {
|
|
|
- graph: callback(oldState.cs.graph),
|
|
|
- },
|
|
|
- }));
|
|
|
- },
|
|
|
- }} />
|
|
|
- </Grid.Col>
|
|
|
- </Grid>
|
|
|
- </>);
|
|
|
- }
|
|
|
+const generateUUID = mockUuid();
|
|
|
+
|
|
|
+export function App() {
|
|
|
+ const [cs, setCs] = React.useState<VersionedModelState>(initialModel);
|
|
|
+ const [corr, setCorr] = React.useState<VersionedModelState>(initialModel);
|
|
|
+ const [as, setAs] = React.useState<VersionedModelState>(initialModel);
|
|
|
+
|
|
|
+ const [corrVersion, setCorrVersion] = React.useState<Version>(initialVersion);
|
|
|
+
|
|
|
+ const handleNewCsVersion = (cs: Version) => {
|
|
|
+
|
|
|
+ };
|
|
|
+
|
|
|
+ const csCallbacks = {
|
|
|
+ // setGraph: (callback: (GraphType) => GraphType) => {
|
|
|
+
|
|
|
+ // this.setState(({cs: {graph, ...rest}}) => ({
|
|
|
+ // cs: {
|
|
|
+ // graph: callback(graph),
|
|
|
+ // ...rest,
|
|
|
+ // },
|
|
|
+ // }));
|
|
|
+ // },
|
|
|
+ // newVersionHandler: (newVersion: Version) => {
|
|
|
+ // this.setState(({cs: {version, historyGraph, ...rest}}) => ({
|
|
|
+ // cs: {
|
|
|
+ // version: newVersion,
|
|
|
+ // historyGraph: setCurrentVersion(appendToHistoryGraph(historyGraph, newVersion), version, newVersion),
|
|
|
+ // ...rest,
|
|
|
+ // },
|
|
|
+ // }));
|
|
|
+ // },
|
|
|
+ // newDeltaHandler: (deltaL1:CompositeDelta) => {
|
|
|
+ // this.setState(({cs: {dependencyGraphL0, dependencyGraphL1, ...rest}}) => {
|
|
|
+ // return {
|
|
|
+ // cs: {
|
|
|
+ // dependencyGraphL1: addDeltaAndActivate(dependencyGraphL1, deltaL1),
|
|
|
+ // dependencyGraphL0: deltaL1.deltas.reduce((dependencyGraphL0, delta) => addDeltaAndActivate(dependencyGraphL0, delta), dependencyGraphL0),
|
|
|
+ // ...rest,
|
|
|
+ // },
|
|
|
+ // };
|
|
|
+ // });
|
|
|
+ // },
|
|
|
+ };
|
|
|
+
|
|
|
+
|
|
|
+ const undoButtons = corrVersion.parents.map(([parentVersion,deltaToUndo]) => {
|
|
|
+ return (
|
|
|
+ <div key={fullVersionId(parentVersion)}>
|
|
|
+ <Button fullWidth={true} compact={true} leftIcon={<IconPlayerTrackPrev size={18}/>} onClick={onUndoClicked.bind(null, parentVersion,deltaToUndo)}>
|
|
|
+ UNDO {deltaToUndo.getDescription()}
|
|
|
+ </Button>
|
|
|
+ <Space h="xs"/>
|
|
|
+ </div>
|
|
|
+ );
|
|
|
+ });
|
|
|
+ const redoButtons = corrVersion.children.map(([childVersion,deltaToRedo]) => {
|
|
|
+ return (
|
|
|
+ <div key={fullVersionId(childVersion)}>
|
|
|
+ <Button style={{width: "100%"}} compact={true} rightIcon={<IconPlayerTrackNext size={18}/>} onClick={onRedoClicked.bind(null, childVersion,deltaToRedo)}>
|
|
|
+ REDO {deltaToRedo.getDescription()}
|
|
|
+ </Button>
|
|
|
+ <Space h="xs"/>
|
|
|
+ </div>
|
|
|
+ );
|
|
|
+ });
|
|
|
+
|
|
|
+ return (<>
|
|
|
+ <Grid grow>
|
|
|
+ <Grid.Col span={1}>
|
|
|
+ <VersionedModel title="Concrete Syntax" generateUUID={generateUUID} version={corrVersion.getEmbeddedOrThrow("cs").embedded} state={cs} setState={setCs} />
|
|
|
+ </Grid.Col>
|
|
|
+ <Grid.Col span={1}>
|
|
|
+ <VersionedModel title="Correspondence" generateUUID={generateUUID} version={corrVersion} state={corr} setState={setCorr} />
|
|
|
+
|
|
|
+ <SimpleGrid cols={2}>
|
|
|
+ <div>
|
|
|
+ {undoButtons}
|
|
|
+ </div>
|
|
|
+ <div>
|
|
|
+ {redoButtons}
|
|
|
+ </div>
|
|
|
+ </SimpleGrid>
|
|
|
+
|
|
|
+ </Grid.Col>
|
|
|
+ <Grid.Col span={1}>
|
|
|
+ <VersionedModel title="Abstract Syntax" generateUUID={generateUUID} version={corrVersion.getEmbeddedOrThrow("as").embedded} state={as} setState={setAs} />
|
|
|
+ </Grid.Col>
|
|
|
+ </Grid>
|
|
|
+ </>);
|
|
|
}
|
|
|
|
|
|
// export function App() {
|