|
@@ -4,8 +4,9 @@ import * as _ from "lodash";
|
|
|
import {Grid, Text, Title, Group, Stack} from "@mantine/core";
|
|
|
|
|
|
import {d3Types, Graph} from "./graph"
|
|
|
-import {EditableGraph} from "./editable_graph";
|
|
|
+import {EditableGraph, GraphType, NodeType, LinkType, nodeCreationToD3} from "./editable_graph";
|
|
|
|
|
|
+import {UUID} from "../onion/types";
|
|
|
import {mockUuid} from "../onion/test_helpers";
|
|
|
import {Version, initialVersion} from "../onion/version";
|
|
|
import {Delta} from "../onion/delta";
|
|
@@ -71,30 +72,64 @@ function getDeltaColor(delta: Delta) {
|
|
|
return color;
|
|
|
}
|
|
|
|
|
|
-export function App() {
|
|
|
- const getUuid = mockUuid();
|
|
|
- const refHistoryGraph = React.useRef<Graph<Version,Delta>>(null);
|
|
|
- const refDependencyGraphL1 = React.useRef<Graph<Delta,null>>(null);
|
|
|
- const refDependencyGraphL0 = React.useRef<Graph<Delta,null>>(null);
|
|
|
+interface BranchProps {
|
|
|
+ getUuid: () => UUID;
|
|
|
+}
|
|
|
+
|
|
|
+export function Branch(props: BranchProps) {
|
|
|
+ const [version, setVersion] = React.useState<Version>(initialVersion); // the version currently being displayed in the Graph State (on the left)
|
|
|
+ const [graph, setGraph] = React.useState<GraphType>(_.cloneDeep(emptyGraph));
|
|
|
+ const [historyGraph, setHistoryGraph] = React.useState<d3Types.d3Graph<Version,Delta>>(_.cloneDeep(initialHistoryGraph));
|
|
|
+ const [dependencyGraph, setDependencyGraph] = React.useState<d3Types.d3Graph<Delta,null>>(_.cloneDeep(emptyGraph));
|
|
|
|
|
|
+ // Callbacks for our EditableGraph component:
|
|
|
+ const createNode = (node: NodeType) => {
|
|
|
+ setGraph((prevState: GraphType) => ({
|
|
|
+ nodes: [...prevState.nodes, node],
|
|
|
+ links: prevState.links,
|
|
|
+ }));
|
|
|
+ }
|
|
|
+ const deleteNode = (id: string) => {
|
|
|
+ setGraph((prevState: GraphType) => {
|
|
|
+ const newLinks = prevState.links.filter(l => l.source.id !== id && l.target.id !== id);
|
|
|
+ return {
|
|
|
+ nodes: prevState.nodes.filter(n => n.id !== id),
|
|
|
+ links: newLinks,
|
|
|
+ };
|
|
|
+ });
|
|
|
+ };
|
|
|
+ const createLink = ({source, label, target, obj}: LinkType) => {
|
|
|
+ setGraph((prevState: GraphType) => ({
|
|
|
+ nodes: prevState.nodes,
|
|
|
+ links: [...prevState.links, {source, target, label, obj}],
|
|
|
+ }));
|
|
|
+ };
|
|
|
+ const deleteLink = (source: NodeType, label: string) => {
|
|
|
+ setGraph((prevState: GraphType) => {
|
|
|
+ const newLinks = prevState.links.filter(l => l.source !== source || l.label !== label)
|
|
|
+ return {
|
|
|
+ nodes: prevState.nodes,
|
|
|
+ links: newLinks,
|
|
|
+ };
|
|
|
+ });
|
|
|
+ };
|
|
|
const newVersionHandler = (version: Version) => {
|
|
|
- if (refHistoryGraph.current !== null) {
|
|
|
- refHistoryGraph.current.createNode(versionToNode(version));
|
|
|
- for (const parent of version.parents) {
|
|
|
- const [parentVersion, delta] = parent;
|
|
|
- refHistoryGraph.current.createLink({source:fullVersionId(version), label:"", target:fullVersionId(parentVersion), obj: delta});
|
|
|
- }
|
|
|
- }
|
|
|
+ setHistoryGraph(prevHistoryGraph => {
|
|
|
+ return {
|
|
|
+ nodes: prevHistoryGraph.nodes.concat(versionToNode(version)),
|
|
|
+ links: prevHistoryGraph.links.concat(...version.parents.map(([parentVersion,delta]) => ({source:fullVersionId(version), label:"", target:fullVersionId(parentVersion), obj: delta}))),
|
|
|
+ };
|
|
|
+ });
|
|
|
+ setVersion(version);
|
|
|
};
|
|
|
-
|
|
|
const newDeltaHandler = (delta: Delta) => {
|
|
|
- if (refDependencyGraphL1.current !== null) {
|
|
|
+ setDependencyGraph(prevDepGraph => {
|
|
|
const color = getDeltaColor(delta);
|
|
|
- refDependencyGraphL1.current.createNode({id: fullDeltaId(delta), label: delta.getDescription(), color, obj: delta});
|
|
|
- for (const [dep,depType] of delta.getTypedDependencies()) {
|
|
|
- refDependencyGraphL1.current.createLink({source: fullDeltaId(delta), label: depType, target: fullDeltaId(dep), obj: null});
|
|
|
- }
|
|
|
- }
|
|
|
+ return {
|
|
|
+ nodes: prevDepGraph.nodes.concat({id: fullDeltaId(delta), label: delta.getDescription(), color, obj: delta}),
|
|
|
+ links: prevDepGraph.links.concat(...delta.getTypedDependencies().map(([dep,depType]) => ({source: fullDeltaId(delta), label: depType, target: fullDeltaId(dep), obj: null}))),
|
|
|
+ };
|
|
|
+ });
|
|
|
}
|
|
|
|
|
|
// "physics" stuff (for graph layout)
|
|
@@ -102,30 +137,71 @@ export function App() {
|
|
|
const historyGraphForces = {charge: -100, center:0.1, link:2};
|
|
|
const depGraphForces = {charge: -200, center:0.1, link:0.2};
|
|
|
|
|
|
+
|
|
|
+
|
|
|
+ const historyMouseUpHandler = (event, {x,y}, node: d3Types.d3Node<Version> | undefined) => {
|
|
|
+ if (node !== undefined) {
|
|
|
+ // the user clicked on a version
|
|
|
+ const versionClicked = node.obj;
|
|
|
+ const graphState = _.cloneDeep(emptyGraph);
|
|
|
+ const deltas = [...versionClicked].reverse(); // all deltas of versionClicked, from early to late.
|
|
|
+ function execDelta(delta) {
|
|
|
+ // just update graphState, in-place.
|
|
|
+ if (delta instanceof NodeCreation) {
|
|
|
+ graphState.nodes.push(nodeCreationToD3(delta, 0, 0));
|
|
|
+ }
|
|
|
+ else if (delta instanceof NodeDeletion) {
|
|
|
+ graphState.nodes.splice(graphState.nodes.findIndex((node: NodeType) => node.id === delta.creation.id.toString()), 1);
|
|
|
+ }
|
|
|
+ else if (delta instanceof EdgeCreation) {
|
|
|
+ // graphState.links.push()
|
|
|
+ // ?
|
|
|
+ }
|
|
|
+ else if (delta instanceof CompositeDelta) {
|
|
|
+ delta.deltas.forEach(execDelta);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ deltas.forEach(execDelta);
|
|
|
+ console.log(graphState);
|
|
|
+ setGraph(graphState);
|
|
|
+ setVersion(versionClicked);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ return (
|
|
|
+ <Grid grow>
|
|
|
+ <Grid.Col span={1}>
|
|
|
+ <Group>
|
|
|
+ <Title order={4}>Graph state</Title>
|
|
|
+ </Group>
|
|
|
+ <EditableGraph graph={graph} forces={editableGraphForces} getUuid={props.getUuid} newVersionHandler={newVersionHandler} newDeltaHandler={newDeltaHandler}
|
|
|
+ version={version}
|
|
|
+ createNode={createNode} deleteNode={deleteNode} createLink={createLink} deleteLink={deleteLink} />
|
|
|
+ <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>
|
|
|
+ </Grid.Col>
|
|
|
+ <Grid.Col span={1}>
|
|
|
+ <Title order={4}>History Graph</Title>
|
|
|
+ <Graph graph={historyGraph} forces={historyGraphForces} mouseDownHandler={()=>{}} mouseUpHandler={historyMouseUpHandler} />
|
|
|
+ <Text>All links are parent links.</Text>
|
|
|
+ </Grid.Col>
|
|
|
+ <Grid.Col span={1}>
|
|
|
+ <Title order={4}>Dependency Graph (L1)</Title>
|
|
|
+ <Graph graph={dependencyGraph} forces={depGraphForces} mouseDownHandler={()=>{}} mouseUpHandler={()=>{}} />
|
|
|
+ </Grid.Col>
|
|
|
+ </Grid>
|
|
|
+ );
|
|
|
+}
|
|
|
+
|
|
|
+export function App() {
|
|
|
+ const getUuid = mockUuid();
|
|
|
+
|
|
|
return (
|
|
|
<Stack>
|
|
|
<Title order={2}>Onion VCS Demo</Title>
|
|
|
- <Grid grow>
|
|
|
- <Grid.Col span={1}>
|
|
|
- <Group>
|
|
|
- <Title order={4}>Graph state</Title>
|
|
|
- </Group>
|
|
|
- <EditableGraph graph={_.cloneDeep(emptyGraph)} forces={editableGraphForces} getUuid={getUuid} newVersionHandler={newVersionHandler} newDeltaHandler={newDeltaHandler} />
|
|
|
- <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>
|
|
|
- </Grid.Col>
|
|
|
- <Grid.Col span={1}>
|
|
|
- <Title order={4}>History Graph</Title>
|
|
|
- <Graph ref={refHistoryGraph} graph={_.cloneDeep(initialHistoryGraph)} forces={historyGraphForces} mouseDownHandler={()=>{}} mouseUpHandler={()=>{}} />
|
|
|
- <Text>All links are parent links.</Text>
|
|
|
- </Grid.Col>
|
|
|
- <Grid.Col span={1}>
|
|
|
- <Title order={4}>Dependency Graph (L1)</Title>
|
|
|
- <Graph ref={refDependencyGraphL1} graph={_.cloneDeep(emptyGraph)} forces={depGraphForces} mouseDownHandler={()=>{}} mouseUpHandler={()=>{}} />
|
|
|
- </Grid.Col>
|
|
|
- </Grid>
|
|
|
+ <Branch getUuid={getUuid} />
|
|
|
</Stack>
|
|
|
);
|
|
|
}
|