|
@@ -1,564 +1,37 @@
|
|
|
-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 {GraphState} from "../onion/graph_state";
|
|
|
-import {CompositeDelta, CompositeLevel} from "../onion/composite_delta";
|
|
|
-import {embed, Version, VersionRegistry} from "../onion/version";
|
|
|
-// import {
|
|
|
-// GraphState,
|
|
|
-// } from "../onion/graph_state";
|
|
|
-
|
|
|
-// import {NodeCreation, NodeDeletion, EdgeCreation, EdgeUpdate} from "../onion/primitive_delta";
|
|
|
-import {PrimitiveDelta, PrimitiveRegistry} from "../onion/primitive_delta";
|
|
|
-import {PrimitiveValue, UUID} from "../onion/types";
|
|
|
-import {mockUuid} from "../onion/test_helpers";
|
|
|
-import {Delta} from "../onion/delta";
|
|
|
-
|
|
|
-import {TrivialParser} from "../parser/trivial_parser";
|
|
|
-
|
|
|
-import {d3Types, Graph} from "./graph"
|
|
|
-import {EditableGraph, UserEditCallback, SetNodePositionCallback, GraphType, NodeType, LinkType} from "./editable_graph";
|
|
|
-import {D3GraphStateUpdater} from "./d3_state";
|
|
|
-import {RountangleEditor} from "./rountangleEditor/RountangleEditor";
|
|
|
-import {
|
|
|
- HistoryGraphType,
|
|
|
- DependencyGraphType,
|
|
|
- versionToNode,
|
|
|
- setCurrentVersion,
|
|
|
- appendToHistoryGraph,
|
|
|
- fullDeltaId,
|
|
|
- setDeltaActive,
|
|
|
- setDeltaInactive,
|
|
|
- deltaToDepGraphNode,
|
|
|
- dependencyToDepGraphLink,
|
|
|
- conflictToDepGraphLink,
|
|
|
- fullVersionId,
|
|
|
- addDeltaAndActivate,
|
|
|
-} from "./app_state";
|
|
|
-
|
|
|
-import {InsidenessParser} from "../parser/insideness_parser";
|
|
|
-import {visitPartialOrdering} from "../util/partial_ordering";
|
|
|
-
|
|
|
-const emptyGraph: d3Types.d3Graph<any,any> = {
|
|
|
- nodes: [],
|
|
|
- links: [],
|
|
|
-};
|
|
|
-
|
|
|
-// "physics" stuff (for graph layout)
|
|
|
-const graphForces = {charge: -200, center: 0.2, link: 2};
|
|
|
-
|
|
|
-const initialHistoryGraph = initialVersion => ({
|
|
|
- nodes: [
|
|
|
- versionToNode(initialVersion, true),
|
|
|
- ],
|
|
|
- links: [],
|
|
|
-});
|
|
|
-
|
|
|
-interface VersionedModelState {
|
|
|
- version: Version; // the 'current version'
|
|
|
- graph: GraphType; // the state what is displayed in the leftmost panel
|
|
|
- historyGraph: HistoryGraphType; // the state of what is displayed in the middle panel
|
|
|
- dependencyGraphL1: DependencyGraphType; // the state of what is displayed in the rightmost panel
|
|
|
- dependencyGraphL0: DependencyGraphType; // the state of what is displayed in the rightmost panel
|
|
|
- d3StateUpdater: D3GraphStateUpdater;
|
|
|
- graphState: GraphState;
|
|
|
- versionRegistry: VersionRegistry;
|
|
|
- compositeLevel: CompositeLevel;
|
|
|
-}
|
|
|
-
|
|
|
-export interface VersionedModelProps {
|
|
|
- title: string;
|
|
|
- generateUUID: () => UUID;
|
|
|
- primitiveRegistry: PrimitiveRegistry;
|
|
|
-
|
|
|
- readonly?: boolean;
|
|
|
-
|
|
|
- onUserEdit?: UserEditCallback;
|
|
|
- setNextNodePosition: SetNodePositionCallback;
|
|
|
- onUndoClicked: (parentVersion: Version, deltaToUndo: Delta) => void;
|
|
|
- onRedoClicked: (childVersion: Version, deltaToRedo: Delta) => void;
|
|
|
- onVersionClicked: (Version) => void;
|
|
|
-
|
|
|
- state: VersionedModelState;
|
|
|
- setState: (callback: (VersionedModelState) => VersionedModelState) => void;
|
|
|
-
|
|
|
- defaultTab0: string;
|
|
|
- defaultTab1: string;
|
|
|
- tabs: string[];
|
|
|
-}
|
|
|
-
|
|
|
-class VersionedModel extends React.Component<VersionedModelProps, {}> {
|
|
|
- readonly textarearef: React.RefObject<HTMLTextAreaElement>;
|
|
|
-
|
|
|
- constructor(props) {
|
|
|
- super(props);
|
|
|
- this.textarearef = React.createRef();
|
|
|
- }
|
|
|
-
|
|
|
- render() {
|
|
|
- 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.props.versionRegistry.lookup(buf);
|
|
|
- // });
|
|
|
- // } catch (e) {
|
|
|
- // alert("Input error:" + e.toString());
|
|
|
- // return;
|
|
|
- // }
|
|
|
-
|
|
|
- // const mergedVersions = this.props.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) {
|
|
|
- return (
|
|
|
- <HoverCard shadow="md">
|
|
|
- <HoverCard.Target>
|
|
|
- <ActionIcon>
|
|
|
- <IconInfoCircle size={18}/>
|
|
|
- </ActionIcon>
|
|
|
- </HoverCard.Target>
|
|
|
- <HoverCard.Dropdown>
|
|
|
- {contents}
|
|
|
- </HoverCard.Dropdown>
|
|
|
- </HoverCard>
|
|
|
- );
|
|
|
- }
|
|
|
-
|
|
|
- const makeTabs = (defaultTab, tabs) => (
|
|
|
- <Tabs defaultValue={defaultTab} keepMounted={false}>
|
|
|
- <Tabs.List>
|
|
|
- {tabs.map(tab => {
|
|
|
- if (tab === "editor")
|
|
|
- return <Tabs.Tab key={tab} value="editor">Editor</Tabs.Tab>;
|
|
|
- if (tab === "state")
|
|
|
- return <Tabs.Tab key={tab} value="state">State</Tabs.Tab>;
|
|
|
- if (tab === "history")
|
|
|
- return <Tabs.Tab key={tab} value="history">History</Tabs.Tab>;
|
|
|
- if (tab === "dependencyL1")
|
|
|
- return <Tabs.Tab key={tab} value="dependencyL1">Deltas (L1)</Tabs.Tab>;
|
|
|
- if (tab === "dependencyL0")
|
|
|
- return <Tabs.Tab key={tab} value="dependencyL0">Deltas (L0)</Tabs.Tab>;
|
|
|
- })}
|
|
|
- </Tabs.List>
|
|
|
-
|
|
|
- <Tabs.Panel value="state">
|
|
|
- {makeInfoHoverCardIcon(<>
|
|
|
- <Text>Left mouse button: Drag node around.</Text>
|
|
|
- <Text>Middle mouse button: Delete node (+ incoming/outgoing edges).</Text>
|
|
|
- <Text>Right mouse button: Create node or edge.</Text>
|
|
|
- <Text>Mouse wheel: Zoom.</Text>
|
|
|
- </>)}
|
|
|
- {this.props.readonly?(
|
|
|
- <Graph graph={this.props.state.graph} forces={graphForces} />
|
|
|
- ):(
|
|
|
- <EditableGraph
|
|
|
- graph={this.props.state.graph}
|
|
|
- graphState={this.props.state.graphState}
|
|
|
- forces={graphForces}
|
|
|
- generateUUID={this.props.generateUUID}
|
|
|
- primitiveRegistry={this.props.primitiveRegistry}
|
|
|
- setNextNodePosition={this.props.setNextNodePosition}
|
|
|
- currentVersion={this.props.state.version}
|
|
|
- onUserEdit={this.props.onUserEdit} />
|
|
|
- )}
|
|
|
- </Tabs.Panel>
|
|
|
-
|
|
|
- <Tabs.Panel value="editor">
|
|
|
- {makeInfoHoverCardIcon(
|
|
|
- <Text>New Rountangle by Alt + Click</Text>
|
|
|
- )}
|
|
|
- <RountangleEditor
|
|
|
- graph={this.props.state.graph}
|
|
|
- generateUUID={this.props.generateUUID}
|
|
|
- primitiveRegistry={this.props.primitiveRegistry}
|
|
|
- onUserEdit={this.props.onUserEdit}
|
|
|
- graphState={this.props.state.graphState} />
|
|
|
- </Tabs.Panel>
|
|
|
-
|
|
|
- <Tabs.Panel value="dependencyL1">
|
|
|
- {makeInfoHoverCardIcon(
|
|
|
- <Text>Active deltas are bold.</Text>
|
|
|
- )}
|
|
|
- <Graph graph={this.props.state.dependencyGraphL1} forces={graphForces} />
|
|
|
- </Tabs.Panel>
|
|
|
-
|
|
|
- <Tabs.Panel value="dependencyL0">
|
|
|
- {makeInfoHoverCardIcon(
|
|
|
- <Text>Active deltas are bold.</Text>
|
|
|
- )}
|
|
|
- <Graph graph={this.props.state.dependencyGraphL0} forces={graphForces} />
|
|
|
- </Tabs.Panel>
|
|
|
-
|
|
|
- <Tabs.Panel value="history">
|
|
|
- {makeInfoHoverCardIcon(<>
|
|
|
- <Text>All links are parent links.</Text>
|
|
|
- <Text>Right or middle mouse button: Load version.</Text>
|
|
|
- </>)}
|
|
|
- <Graph graph={this.props.state.historyGraph} forces={graphForces}
|
|
|
- mouseUpHandler={(e, {x, y}, node) => node ? this.props.onVersionClicked(node.obj) : undefined} />
|
|
|
- </Tabs.Panel>
|
|
|
- </Tabs>
|
|
|
- );
|
|
|
-
|
|
|
- const undoButtons = this.props.state.version.parents.map(([parentVersion,deltaToUndo]) => {
|
|
|
- return (
|
|
|
- <div key={fullVersionId(parentVersion)}>
|
|
|
- <Button fullWidth={true} compact={true} leftIcon={<IconPlayerTrackPrev size={18}/>} onClick={this.props.onUndoClicked.bind(null, parentVersion, deltaToUndo)}>
|
|
|
- UNDO {deltaToUndo.getDescription()}
|
|
|
- </Button>
|
|
|
- <Space h="xs"/>
|
|
|
- </div>
|
|
|
- );
|
|
|
- });
|
|
|
- const redoButtons = this.props.state.version.children.map(([childVersion,deltaToRedo]) => {
|
|
|
- return (
|
|
|
- <div key={fullVersionId(childVersion)}>
|
|
|
- <Button style={{width: "100%"}} compact={true} rightIcon={<IconPlayerTrackNext size={18}/>} onClick={this.props.onRedoClicked.bind(null, childVersion, deltaToRedo)}>
|
|
|
- REDO {deltaToRedo.getDescription()}
|
|
|
- </Button>
|
|
|
- <Space h="xs"/>
|
|
|
- </div>
|
|
|
- );
|
|
|
- });
|
|
|
-
|
|
|
- return (
|
|
|
- <>
|
|
|
- <Title order={4}>{this.props.title}</Title>
|
|
|
- <Stack>
|
|
|
- {makeTabs(this.props.defaultTab0, this.props.tabs)}
|
|
|
- {makeTabs(this.props.defaultTab1, this.props.tabs)}
|
|
|
- </Stack>
|
|
|
-
|
|
|
- <SimpleGrid cols={2}>
|
|
|
- <div>
|
|
|
- {undoButtons}
|
|
|
- </div>
|
|
|
- <div>
|
|
|
- {redoButtons}
|
|
|
- </div>
|
|
|
- </SimpleGrid>
|
|
|
-
|
|
|
-{/* <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>
|
|
|
-*/} </>
|
|
|
- );
|
|
|
- }
|
|
|
-}
|
|
|
-
|
|
|
-interface AppState {
|
|
|
- cs: VersionedModelState;
|
|
|
- corr: VersionedModelState;
|
|
|
- as: VersionedModelState;
|
|
|
-}
|
|
|
-
|
|
|
-export class App extends React.Component<{}, AppState> {
|
|
|
- generateUUID = mockUuid();
|
|
|
- primitiveRegistry = new PrimitiveRegistry();
|
|
|
- parser: TrivialParser;
|
|
|
-
|
|
|
- parsedTo: Map<Version,{corr: Version, target: Version}>;
|
|
|
- renderedTo: Map<Version,{corr: Version, target: Version}>;
|
|
|
-
|
|
|
- setCsState(callback) {
|
|
|
- return this.setState(({cs, ...rest}) => ({cs: callback(cs), ...rest}));
|
|
|
- }
|
|
|
- setCorrState(callback) {
|
|
|
- return this.setState(({corr, ...rest}) => ({corr: callback(corr), ...rest}));
|
|
|
- }
|
|
|
- setAsState(callback) {
|
|
|
- return this.setState(({as, ...rest}) => ({as: callback(as), ...rest}));
|
|
|
- }
|
|
|
-
|
|
|
- constructor(props) {
|
|
|
- super(props);
|
|
|
-
|
|
|
- const makeModelState = (setState) => {
|
|
|
- // 'Glue' callback
|
|
|
- const setGraph = (callback: (GraphType) => GraphType) => {
|
|
|
- setState(({graph, ...rest}) => ({
|
|
|
- graph: callback(graph),
|
|
|
- ...rest,
|
|
|
- }));
|
|
|
- };
|
|
|
- const versionRegistry = new VersionRegistry();
|
|
|
- const d3StateUpdater = new D3GraphStateUpdater(setGraph);
|
|
|
- return {
|
|
|
- version: versionRegistry.initialVersion,
|
|
|
- graph: emptyGraph,
|
|
|
- historyGraph: initialHistoryGraph(versionRegistry.initialVersion),
|
|
|
- dependencyGraphL1: emptyGraph,
|
|
|
- dependencyGraphL0: emptyGraph,
|
|
|
- compositeLevel: new CompositeLevel(),
|
|
|
- versionRegistry,
|
|
|
- d3StateUpdater,
|
|
|
- graphState: new GraphState(),
|
|
|
- };
|
|
|
- }
|
|
|
-
|
|
|
- this.state = {
|
|
|
- cs: makeModelState(this.setCsState.bind(this)),
|
|
|
- corr: makeModelState(this.setCorrState.bind(this)),
|
|
|
- as: makeModelState(this.setAsState.bind(this)),
|
|
|
- };
|
|
|
-
|
|
|
- this.parsedTo = new Map([[this.state.cs.versionRegistry.initialVersion, {corr:this.state.corr.versionRegistry.initialVersion, target:this.state.as.versionRegistry.initialVersion}]]);
|
|
|
- this.renderedTo = new Map([[this.state.as.versionRegistry.initialVersion, {corr:this.state.corr.versionRegistry.initialVersion, target:this.state.cs.versionRegistry.initialVersion}]]);
|
|
|
-
|
|
|
- this.parser = new TrivialParser(this.primitiveRegistry, this.generateUUID);
|
|
|
- }
|
|
|
-
|
|
|
- render() {
|
|
|
- const addVersionAndDeltas = ({version, historyGraph, dependencyGraphL1, dependencyGraphL0, ...rest}: VersionedModelState, newVersion: Version, composite: CompositeDelta) => {
|
|
|
- return {
|
|
|
- version: newVersion,
|
|
|
- // add new version to history graph + highlight the new version as the current version:
|
|
|
- historyGraph: setCurrentVersion(appendToHistoryGraph(historyGraph, newVersion), version, newVersion),
|
|
|
- // add the composite delta to the L1-graph + highlight it as 'active':
|
|
|
- dependencyGraphL1: composite.deltas.length > 0 ? addDeltaAndActivate(dependencyGraphL1, composite) : dependencyGraphL1, // never add an empty composite
|
|
|
- // add the primitive L0-deltas to the L0-graph + highlight them as 'active':
|
|
|
- dependencyGraphL0: composite.deltas.reduce(
|
|
|
- (graph, delta) => {
|
|
|
- return addDeltaAndActivate(graph, delta);
|
|
|
- }, dependencyGraphL0),
|
|
|
- ...rest,
|
|
|
- };
|
|
|
- };
|
|
|
-
|
|
|
- const undoWithoutUpdatingHistoryGraph = (state, setState, deltaToUndo) => {
|
|
|
- state.graphState.unexec(deltaToUndo, state.d3StateUpdater);
|
|
|
- setState(({dependencyGraphL0: prevDepGraphL0, dependencyGraphL1: prevDepGraphL1, ...rest}) => ({
|
|
|
- dependencyGraphL1: setDeltaInactive(prevDepGraphL1, deltaToUndo),
|
|
|
- dependencyGraphL0: deltaToUndo.deltas.reduce((prevDepGraphL0, delta) => setDeltaInactive(prevDepGraphL0, delta), prevDepGraphL0),
|
|
|
- ...rest,
|
|
|
- }));
|
|
|
- };
|
|
|
- const redoWithoutUpdatingHistoryGraph = (state, setState, deltaToRedo) => {
|
|
|
- state.graphState.exec(deltaToRedo, state.d3StateUpdater);
|
|
|
- setState(({dependencyGraphL0: prevDepGraphL0, dependencyGraphL1: prevDepGraphL1, ...rest}) => ({
|
|
|
- dependencyGraphL1: setDeltaActive(prevDepGraphL1, deltaToRedo),
|
|
|
- dependencyGraphL0: deltaToRedo.deltas.reduce((prevDepGraphL0, delta) => setDeltaActive(prevDepGraphL0, delta), prevDepGraphL0),
|
|
|
- ...rest,
|
|
|
- }));
|
|
|
- };
|
|
|
-
|
|
|
- const onUndoClicked = (state, setState, parentVersion, deltaToUndo) => {
|
|
|
- undoWithoutUpdatingHistoryGraph(state, setState, deltaToUndo);
|
|
|
- setState(({historyGraph: prevHistoryGraph, version: prevVersion, ...rest}) => ({
|
|
|
- version: parentVersion,
|
|
|
- historyGraph: setCurrentVersion(prevHistoryGraph, prevVersion, parentVersion),
|
|
|
- ...rest,
|
|
|
- }));
|
|
|
- };
|
|
|
- const onRedoClicked = (state, setState, childVersion, deltaToRedo) => {
|
|
|
- redoWithoutUpdatingHistoryGraph(state, setState, deltaToRedo);
|
|
|
- setState(({historyGraph: prevHistoryGraph, version: prevVersion, ...rest}) => ({
|
|
|
- version: childVersion,
|
|
|
- historyGraph: setCurrentVersion(prevHistoryGraph, prevVersion, childVersion),
|
|
|
- ...rest,
|
|
|
- }));
|
|
|
- };
|
|
|
-
|
|
|
- const gotoVersion = (state, setState, chosenVersion: Version) => {
|
|
|
- const path = state.version.findPathTo(chosenVersion);
|
|
|
- if (path === undefined) {
|
|
|
- throw new Error("Could not find path to version!");
|
|
|
- }
|
|
|
- for (const [linkType, delta] of path) {
|
|
|
- if (linkType === 'p') {
|
|
|
- undoWithoutUpdatingHistoryGraph(state, setState, delta);
|
|
|
- }
|
|
|
- else if (linkType === 'c') {
|
|
|
- redoWithoutUpdatingHistoryGraph(state, setState, delta);
|
|
|
- }
|
|
|
- }
|
|
|
- setState(({historyGraph, version, ...rest}) => ({
|
|
|
- version: chosenVersion,
|
|
|
- historyGraph: setCurrentVersion(historyGraph, version, chosenVersion),
|
|
|
- ...rest,
|
|
|
- }));
|
|
|
- }
|
|
|
-
|
|
|
- // Pure function
|
|
|
- // Replays all deltas in a version to compute the graph state of that version.
|
|
|
- function getGraphState(version: Version, additionalDeltas: PrimitiveDelta[]):GraphState {
|
|
|
- const graphState = new GraphState();
|
|
|
- for (const d of [...version].reverse()) {
|
|
|
- graphState.exec(d);
|
|
|
- }
|
|
|
- for (const d of additionalDeltas) {
|
|
|
- graphState.exec(d);
|
|
|
- }
|
|
|
- return graphState;
|
|
|
- }
|
|
|
-
|
|
|
-
|
|
|
- const parseOrRender = (sourceState: VersionedModelState, targetState: VersionedModelState,
|
|
|
- mapping, parse: boolean) => {
|
|
|
- return (sourceDeltas: PrimitiveDelta[], description: string) => {
|
|
|
-
|
|
|
- const targetDescription = (parse ? "parse:" : "render:") + description;
|
|
|
-
|
|
|
- const sourceParent = sourceState.version;
|
|
|
- const {corr: corrParent, target: targetParent} = mapping.get(sourceParent) || (()=>{throw new Error("X")})();
|
|
|
-
|
|
|
- // SLOW! When performance becomes a problem, look for a faster alternative
|
|
|
- const sourceGraphState = getGraphState(sourceParent, []);
|
|
|
- const corrGraphState = getGraphState(corrParent, []);
|
|
|
- const targetGraphState = getGraphState(targetParent, []);
|
|
|
-
|
|
|
- const {corrDeltas, targetDeltas, sourceOverrides, targetOverrides} = this.parser.propagate_change(parse, sourceDeltas, sourceGraphState, corrGraphState, targetGraphState);
|
|
|
-
|
|
|
- // console.log({corrDeltas})
|
|
|
-
|
|
|
- const sourceComposite = sourceState.compositeLevel.createComposite(sourceDeltas, description);
|
|
|
- const newSourceVersion = sourceState.versionRegistry.createVersion(sourceState.version, sourceComposite);
|
|
|
-
|
|
|
- const targetComposite = targetState.compositeLevel.createComposite(targetDeltas, targetDescription);
|
|
|
- const newTargetVersion = (targetComposite.deltas.length > 0) ?
|
|
|
- targetState.versionRegistry.createVersion(targetParent, targetComposite)
|
|
|
- : targetState.version; // do not create a new target version
|
|
|
-
|
|
|
- const csComposite = parse ? sourceComposite : targetComposite;
|
|
|
- const asComposite = parse ? targetComposite : sourceComposite;
|
|
|
- const newCsVersion = parse ? newSourceVersion : newTargetVersion;
|
|
|
- const newAsVersion = parse ? newTargetVersion : newSourceVersion;
|
|
|
- const csOverrides = parse ? sourceOverrides : targetOverrides;
|
|
|
- const asOverrides = parse ? targetOverrides : sourceOverrides;
|
|
|
-
|
|
|
-
|
|
|
- const corrDeltasOrderedByDependency: PrimitiveDelta[] = [];
|
|
|
- visitPartialOrdering([
|
|
|
- ...sourceDeltas.map(d => sourceOverrides.get(d) || d),
|
|
|
- ...targetDeltas.map(d => targetOverrides.get(d) || d),
|
|
|
- ...corrDeltas],
|
|
|
- (d: Delta) => d.getDependencies(),
|
|
|
- (d: Delta) => corrDeltasOrderedByDependency.push(d));
|
|
|
-
|
|
|
- // console.log({corrDeltasOrderedByDependency});
|
|
|
-
|
|
|
- const corrComposite = this.state.corr.compositeLevel.createComposite(corrDeltasOrderedByDependency, targetDescription);
|
|
|
- const newCorrVersion = this.state.corr.versionRegistry.createVersion(corrParent, corrComposite,
|
|
|
- embed(
|
|
|
- ["cs", newCsVersion, csOverrides],
|
|
|
- ["as", newAsVersion, asOverrides],
|
|
|
- ));
|
|
|
-
|
|
|
- this.parsedTo.set(newCsVersion, {corr: newCorrVersion, target: newAsVersion});
|
|
|
- this.renderedTo.set(newAsVersion, {corr: newCorrVersion, target: newCsVersion});
|
|
|
-
|
|
|
- // Update state:
|
|
|
- this.setState(({cs, corr, as}) => {
|
|
|
- const result = {
|
|
|
- cs: addVersionAndDeltas(cs, newCsVersion, csComposite),
|
|
|
- corr: addVersionAndDeltas(corr, newCorrVersion, corrComposite),
|
|
|
- as: addVersionAndDeltas(as, newAsVersion, asComposite),
|
|
|
- };
|
|
|
- return result;
|
|
|
- });
|
|
|
-
|
|
|
- gotoVersion(this.state.cs, this.setCsState.bind(this), newCsVersion);
|
|
|
- gotoVersion(this.state.corr, this.setCorrState.bind(this), newCorrVersion);
|
|
|
- gotoVersion(this.state.as, this.setAsState.bind(this), newAsVersion);
|
|
|
- };
|
|
|
- };
|
|
|
-
|
|
|
- const parseCsChange = parseOrRender(this.state.cs, this.state.as, this.parsedTo, true);
|
|
|
- const renderAsChange = parseOrRender(this.state.as, this.state.cs, this.renderedTo, false);
|
|
|
-
|
|
|
- return (<>
|
|
|
- <Grid grow>
|
|
|
- <Grid.Col span={1}>
|
|
|
- <VersionedModel title="Concrete Syntax"
|
|
|
- generateUUID={this.generateUUID}
|
|
|
- primitiveRegistry={this.primitiveRegistry}
|
|
|
- tabs={["editor", "state", "history", "dependencyL1", "dependencyL0"]}
|
|
|
- defaultTab0="editor"
|
|
|
- defaultTab1="history"
|
|
|
- state={this.state.cs}
|
|
|
- setState={this.setCsState.bind(this)}
|
|
|
- setNextNodePosition={(x,y)=>{this.state.cs.d3StateUpdater.x = x; this.state.cs.d3StateUpdater.y = y;}}
|
|
|
- onUserEdit={parseCsChange}
|
|
|
- onUndoClicked={onUndoClicked.bind(null, this.state.cs, this.setCsState.bind(this))}
|
|
|
- onRedoClicked={onRedoClicked.bind(null, this.state.cs, this.setCsState.bind(this))}
|
|
|
- onVersionClicked={version => gotoVersion(this.state.cs, this.setCsState.bind(this), version)}
|
|
|
- />
|
|
|
- </Grid.Col>
|
|
|
- <Grid.Col span={1}>
|
|
|
- <VersionedModel title="Correspondence" readonly
|
|
|
- generateUUID={this.generateUUID}
|
|
|
- primitiveRegistry={this.primitiveRegistry}
|
|
|
- tabs={["state", "history", "dependencyL1", "dependencyL0"]}
|
|
|
- defaultTab0="state"
|
|
|
- defaultTab1="history"
|
|
|
- setNextNodePosition={(x,y)=>{this.state.corr.d3StateUpdater.x = x; this.state.corr.d3StateUpdater.y = y;}}
|
|
|
- state={this.state.corr}
|
|
|
- setState={this.setCorrState.bind(this)}
|
|
|
- onUndoClicked={onUndoClicked.bind(null, this.state.corr, this.setCorrState.bind(this))}
|
|
|
- onRedoClicked={onRedoClicked.bind(null, this.state.corr, this.setCorrState.bind(this))}
|
|
|
- onVersionClicked={version => gotoVersion(this.state.corr, this.setCorrState.bind(this), version)}
|
|
|
- />
|
|
|
|
|
|
- </Grid.Col>
|
|
|
- <Grid.Col span={1}>
|
|
|
- <VersionedModel title="Abstract Syntax"
|
|
|
- generateUUID={this.generateUUID}
|
|
|
- primitiveRegistry={this.primitiveRegistry}
|
|
|
- tabs={["state", "history", "dependencyL1", "dependencyL0"]}
|
|
|
- defaultTab0="state"
|
|
|
- defaultTab1="history"
|
|
|
- setNextNodePosition={(x,y)=>{this.state.as.d3StateUpdater.x = x; this.state.as.d3StateUpdater.y = y;}}
|
|
|
- state={this.state.as}
|
|
|
- setState={this.setAsState.bind(this)}
|
|
|
- onUserEdit={renderAsChange}
|
|
|
- onUndoClicked={onUndoClicked.bind(null, this.state.as, this.setAsState.bind(this))}
|
|
|
- onRedoClicked={onRedoClicked.bind(null, this.state.as, this.setAsState.bind(this))}
|
|
|
- onVersionClicked={version => gotoVersion(this.state.as, this.setAsState.bind(this), version)}
|
|
|
- />
|
|
|
- </Grid.Col>
|
|
|
- </Grid>
|
|
|
- </>);
|
|
|
- }
|
|
|
-}
|
|
|
+import {DemoPD} from "./demo_pd";
|
|
|
+import {DemoCorr} from "./demo_corr";
|
|
|
+
|
|
|
+import {Stack, Group, Text, Tabs, createStyles} from "@mantine/core";
|
|
|
+
|
|
|
+// const useStyles = createStyles(theme => ({
|
|
|
+// root: {
|
|
|
+// display: "flex",
|
|
|
+// // flex: "1",
|
|
|
+// },
|
|
|
+// panel: {
|
|
|
+// display: "flex",
|
|
|
+// flex: "1",
|
|
|
+// }
|
|
|
+// }));
|
|
|
+
|
|
|
+export function App(props) {
|
|
|
+ // const {classes} = useStyles();
|
|
|
+
|
|
|
+ return <>
|
|
|
+ <Text>Select a demo:</Text>
|
|
|
+ <Tabs orientation="vertical" defaultValue="pd">
|
|
|
+ <Tabs.List>
|
|
|
+ <Tabs.Tab value="pd">Primitive Delta</Tabs.Tab>
|
|
|
+ <Tabs.Tab value="corr">Correspondence</Tabs.Tab>
|
|
|
+ </Tabs.List>
|
|
|
+ <Tabs.Panel value="pd">
|
|
|
+ <DemoPD/>
|
|
|
+ </Tabs.Panel>
|
|
|
+ <Tabs.Panel value="corr">
|
|
|
+ <DemoCorr/>
|
|
|
+ </Tabs.Panel>
|
|
|
+ </Tabs>
|
|
|
+ </>;
|
|
|
+}
|