فهرست منبع

Moved modules around again

Joeri Exelmans 2 سال پیش
والد
کامیت
bbfe08d845

src/frontend/demos/d3graph/d3graph.css → src/frontend/d3graph/d3graph.css


src/frontend/demos/d3graph/d3graph.tsx → src/frontend/d3graph/d3graph.tsx


+ 5 - 16
src/frontend/demos/d3graph/d3graph_editable.tsx

@@ -1,24 +1,13 @@
 import * as React from 'react';
-import {D3Graph, D3GraphData, D3NodeData, D3LinkData, D3Forces} from "./d3graph";
+import {D3Graph, D3Forces} from "./d3graph";
+import {D3OnionGraphData, D3OnionNodeData} from "./reducers/onion_graph";
 
-import {OnionContext, OnionContextType} from "../../onion_context";
-import {
-  PrimitiveDelta,
-  PrimitiveRegistry,
-} from "onion/primitive_delta";
+import {OnionContext, OnionContextType} from "../onion_context";
+import {PrimitiveDelta} from "onion/primitive_delta";
 import {INodeState, IValueState, GraphState} from "onion/graph_state";
-import {UUID} from "onion/types";
-import {
-  Version,
-  VersionRegistry,
-} from "onion/version";
-
-export type D3OnionNodeData = D3NodeData<INodeState|IValueState>;
-export type D3OnionLinkData = D3LinkData<null>;
-export type D3OnionGraphData = D3GraphData<INodeState|IValueState,null>;
 
 export type UserEditCallback = (deltas: PrimitiveDelta[], description: string) => void;
-export type SetNodePositionCallback = (x:number, y:number) => void;
+type SetNodePositionCallback = (x:number, y:number) => void;
 
 export interface D3GraphEditableProps {
   graph: D3OnionGraphData;

+ 1 - 1
src/frontend/demos/versioned_model/delta_graph.ts

@@ -1,7 +1,7 @@
 import {Delta} from "onion/delta";
 import {NodeCreation, NodeDeletion, EdgeCreation, EdgeUpdate} from "onion/primitive_delta";
 import {CompositeDelta} from "onion/composite_delta";
-import {D3GraphData, D3NodeData, D3LinkData} from "../d3graph/d3graph";
+import {D3GraphData, D3NodeData, D3LinkData} from "../d3graph";
 
 export type DeltaGraphState = D3GraphData<Delta,null>;
 

+ 1 - 1
src/frontend/demos/versioned_model/history_graph.ts

@@ -1,6 +1,6 @@
 import {Version} from "onion/version";
 import {Delta} from "onion/delta";
-import {D3GraphData, D3NodeData, D3LinkData} from "../d3graph/d3graph";
+import {D3GraphData, D3NodeData, D3LinkData} from "../d3graph";
 
 export type HistoryGraphState = D3GraphData<Version|null,Delta>;
 

+ 148 - 0
src/frontend/d3graph/reducers/onion_graph.ts

@@ -0,0 +1,148 @@
+// The React state and reducer of a D3Graph component showing an onion graph state.
+
+import {PrimitiveValue} from "onion/types";
+import {INodeState, IValueState, GraphStateListener} from "onion/graph_state";
+import {D3GraphData, D3NodeData, D3LinkData} from "../d3graph";
+
+export type D3OnionNodeData = D3NodeData<INodeState|IValueState>;
+export type D3OnionLinkData = D3LinkData<null>;
+export type D3OnionGraphData = D3GraphData<INodeState|IValueState,null>;
+
+interface AddNode {type:'addNode', ns: INodeState, x: number, y: number}
+interface RemoveNode {type:'removeNode', id: PrimitiveValue}
+interface AddValue {type:'addValue', vs: IValueState, x: number, y: number}
+interface RemoveValue {type:'removeValue', value: PrimitiveValue}
+interface AddLinkToNode {type:'addLinkToNode', sourceId: PrimitiveValue, label: string, targetId: PrimitiveValue}
+interface AddLinkToValue {type:'addLinkToValue', sourceId: PrimitiveValue, label: string, targetValue: PrimitiveValue}
+interface RemoveLink {type:'removeLink', sourceId: PrimitiveValue, label: string}
+
+export type D3OnionGraphAction =
+    Readonly<AddNode>
+  | Readonly<RemoveNode>
+  | Readonly<AddValue>
+  | Readonly<RemoveValue>
+  | Readonly<AddLinkToNode>
+  | Readonly<AddLinkToValue>
+  | Readonly<RemoveLink>;
+
+export function onionGraphReducer(prevState: D3OnionGraphData, action: D3OnionGraphAction): D3OnionGraphData {
+  switch (action.type) {
+    case 'addNode': {
+      return {
+        nodes: [...prevState.nodes, {
+          id: nodeNodeId(action.ns.creation.id.value),
+          label: JSON.stringify(action.ns.creation.id.value),
+          x: action.x,
+          y: action.y,
+          color: "darkturquoise",
+          obj: action.ns,
+          highlight: false,
+        }],
+        links: prevState.links,
+      };
+    }
+    case 'removeNode': {
+      return {
+        nodes: prevState.nodes.filter(n => !n.obj.isNode(action.id)),
+        links: prevState.links,
+      };
+    }
+    case 'addValue': {
+      return {      
+        nodes: [...prevState.nodes, {
+          id: valueNodeId(action.vs.value),
+          label: JSON.stringify(action.vs.value),
+          x: action.x,
+          y: action.y,
+          color: "darkorange",
+          obj: action.vs,
+          highlight: false,
+        }],
+        links: prevState.links,
+      };
+    }
+    case 'removeValue': {
+      return {
+        nodes: prevState.nodes.filter(n => !n.obj.isValue(action.value)),
+        links: prevState.links,
+      };
+    }
+    case 'addLinkToNode': {
+      return {    
+        nodes: prevState.nodes,
+        links: [...prevState.links, {
+          source: prevState.nodes.find(n => n.obj.isNode(action.sourceId)),   // AR: here is the problem!
+          target: prevState.nodes.find(n => n.obj.isNode(action.targetId)),
+          label: action.label,
+          color: 'black',
+          obj: null,
+        }],
+      };
+    }
+    case 'addLinkToValue': {
+      return {    
+        nodes: prevState.nodes,
+        links: [...prevState.links, {
+          source: prevState.nodes.find(n => n.obj.isNode(action.sourceId)),
+          target: prevState.nodes.find(n => n.obj.isValue(action.targetValue)),
+          label: action.label,
+          color: 'black',
+          obj: null,
+        }],
+      };
+    }
+    case 'removeLink': {
+      return {    
+        nodes: prevState.nodes,
+        links: prevState.links.filter(l => l.source.obj.creation.id.value !== action.sourceId || l.label !== action.label),
+      };
+    }
+  }
+}
+
+function nodeNodeId(nodeId: PrimitiveValue) {
+  return "N"+JSON.stringify(nodeId);
+}
+
+function valueNodeId(value: PrimitiveValue) {
+  return "V"+JSON.stringify(value);
+}
+
+
+// Responds to changes to a GraphState object by updating the React state of a d3Graph component.
+export class D3GraphUpdater implements GraphStateListener {
+  readonly setGraph: (cb: (prevGraph: D3OnionGraphData) => D3OnionGraphData) => void;
+
+  // SVG coordinates for newly created nodes
+  // This information cannot be part of our NodeCreation deltas, but it must come from somewhere...
+  x: number;
+  y: number;
+
+  constructor(setGraph: (cb: (prevGraph: D3OnionGraphData) => D3OnionGraphData) => void, x, y) {
+    this.setGraph = setGraph;
+    this.x = x;
+    this.y = y;
+  }
+
+  createNode(ns: INodeState) {
+    this.setGraph(prevGraph => onionGraphReducer(prevGraph, {type: 'addNode', ns, x: this.x, y: this.y}));
+  }
+  createValue(vs: IValueState) {
+    this.setGraph(prevGraph => onionGraphReducer(prevGraph, {type: 'addValue', vs, x: this.x, y: this.y}));
+  }
+  deleteNode(id: PrimitiveValue) {
+    this.setGraph(prevGraph => onionGraphReducer(prevGraph, {type: 'removeNode', id}));
+  }
+  deleteValue(value: PrimitiveValue) {
+    this.setGraph(prevGraph => onionGraphReducer(prevGraph, {type: 'removeValue', value}));
+  }
+  createLinkToNode(sourceId: PrimitiveValue, label: string, targetId: PrimitiveValue) {
+    this.setGraph(prevGraph => onionGraphReducer(prevGraph, {type: 'addLinkToNode', sourceId, label, targetId}));
+  }
+  createLinkToValue(sourceId: PrimitiveValue, label: string, targetValue: PrimitiveValue) {
+    this.setGraph(prevGraph => onionGraphReducer(prevGraph, {type: 'addLinkToValue', sourceId, label, targetValue}));
+  }
+  deleteLink(sourceId: PrimitiveValue, label: string) {
+    this.setGraph(prevGraph => onionGraphReducer(prevGraph, {type: 'removeLink', sourceId, label}));
+  }
+}

+ 0 - 104
src/frontend/demos/d3graph/d3graph_updater.ts

@@ -1,104 +0,0 @@
-import {PrimitiveValue} from "onion/types";
-import {INodeState, IValueState, GraphStateListener} from "onion/graph_state"; 
-import {D3OnionGraphData} from "./d3graph_editable";
-
-function nodeNodeId(nodeId: PrimitiveValue) {
-  return "N"+JSON.stringify(nodeId);
-}
-
-function valueNodeId(value: PrimitiveValue) {
-  return "V"+JSON.stringify(value);
-}
-
-// Responds to changes to a GraphState object by updating the React state of a d3Graph component.
-export class D3GraphUpdater implements GraphStateListener {
-  readonly setGraph: (cb: (prevGraph: D3OnionGraphData) => D3OnionGraphData) => void;
-
-  // SVG coordinates for newly created nodes
-  // This information cannot be part of our NodeCreation deltas, but it must come from somewhere...
-  x: number;
-  y: number;
-
-  constructor(setGraph: (cb: (prevGraph: D3OnionGraphData) => D3OnionGraphData) => void, x, y) {
-    this.setGraph = setGraph;
-    this.x = x;
-    this.y = y;
-  }
-
-  createNode(ns: INodeState) {
-    this.setGraph(prevGraph => ({
-      nodes: [...prevGraph.nodes, {
-        id: nodeNodeId(ns.creation.id.value),
-        label: JSON.stringify(ns.creation.id.value),
-        x: this.x,
-        y: this.y,
-        color: "darkturquoise",
-        obj: ns,
-        highlight: false,
-      }],
-      links: prevGraph.links,
-    }));
-  }
-
-  createValue(vs: IValueState) {
-    this.setGraph(prevGraph => ({
-      nodes: [...prevGraph.nodes, {
-        id: valueNodeId(vs.value),
-        label: JSON.stringify(vs.value),
-        x: this.x,
-        y: this.y,
-        color: "darkorange",
-        obj: vs,
-        highlight: false,
-      }],
-      links: prevGraph.links,
-    }));
-  }
-
-  deleteNode(id: PrimitiveValue) {
-    this.setGraph(prevGraph => ({
-      nodes: prevGraph.nodes.filter(n => !n.obj.isNode(id)),
-      links: prevGraph.links,
-    }));
-  }
-
-  deleteValue(value: PrimitiveValue) {
-    this.setGraph(prevGraph => ({
-      nodes: prevGraph.nodes.filter(n => !n.obj.isValue(value)),
-      links: prevGraph.links,
-    }));
-  }
-
-  createLinkToNode(sourceId: PrimitiveValue, label: string, targetId: PrimitiveValue) {
-    this.setGraph(prevGraph => ({
-      nodes: prevGraph.nodes,
-      links: [...prevGraph.links, {
-        source: prevGraph.nodes.find(n => n.obj.isNode(sourceId)),   // AR: here is the problem!
-        target: prevGraph.nodes.find(n => n.obj.isNode(targetId)),
-        label: label,
-        color: 'black',
-        obj: null,
-      }],
-    }));
-  }
-
-  createLinkToValue(sourceId: PrimitiveValue, label: string, targetValue: PrimitiveValue) {
-    this.setGraph(prevGraph => ({
-      nodes: prevGraph.nodes,
-      links: [...prevGraph.links, {
-        source: prevGraph.nodes.find(n => n.obj.isNode(sourceId)),
-        target: prevGraph.nodes.find(n => n.obj.isValue(targetValue)),
-        label: label,
-        color: 'black',
-        obj: null,
-      }],
-    }));
-  }
-
-  deleteLink(sourceId: PrimitiveValue, label: string) {
-    this.setGraph(prevGraph => ({
-      nodes: prevGraph.nodes,
-      links: prevGraph.links.filter(l => l.source.obj.creation.id.value !== sourceId || l.label !== label),
-    }));
-  }
-}

+ 19 - 13
src/frontend/demos/demo_bm.tsx

@@ -2,12 +2,12 @@ import * as React from 'react';
 import {Center, Grid, Group, Space, Stack, Text, Title} from '@mantine/core';
 
 import {OnionContext} from "../onion_context";
-import {PrimitiveRegistry} from '../../onion/primitive_delta';
-import {mockUuid} from '../../onion/test_helpers';
-import {makeInfoHoverCardIcon} from './help_icons';
-import {newVersionedModel, undoButtonHelpText, VersionedModelState} from './versioned_model/single_model';
-import {newCorrespondence, undoButtonHelpTextCorr} from './versioned_model/correspondence';
-import {ManualRendererProps, newManualRenderer} from './manual_renderer';
+import {PrimitiveRegistry} from 'onion/primitive_delta';
+import {mockUuid} from 'onion/test_helpers';
+import {InfoHoverCard} from '../info_hover_card';
+import {newVersionedModel, undoButtonHelpText, VersionedModelState} from '../versioned_model/single_model';
+import {newCorrespondence, undoButtonHelpTextCorr} from '../versioned_model/correspondence';
+import {ManualRendererProps, newManualRenderer} from '../versioned_model/manual_renderer';
 import {Actionblock, Resultblock} from './blocks';
 
 export const demo_BM_description =
@@ -249,7 +249,9 @@ export function getDemoBM() {
                         <Center>
                             {cs1Components.undoRedoButtons}
                             <Space w="sm"/>
-                            {makeInfoHoverCardIcon(undoButtonHelpText)}
+                            <InfoHoverCard>
+                                {undoButtonHelpText}
+                            </InfoHoverCard>
                         </Center>
                     </Stack>
                 </Grid.Col>
@@ -260,11 +262,11 @@ export function getDemoBM() {
                         <Center>
                             {corr1Components.undoRedoButtons}
                             <Space w="sm"/>
-                            {makeInfoHoverCardIcon(<>
+                            <InfoHoverCard>
                                 {undoButtonHelpText}
                                 <Space h="sm"/>
                                 {undoButtonHelpTextCorr}
-                            </>)}
+                            </InfoHoverCard>
                         </Center>
                     </Stack>
                 </Grid.Col>
@@ -275,7 +277,9 @@ export function getDemoBM() {
                         <Center>
                             {asComponents.undoRedoButtons}
                             <Space w="sm"/>
-                            {makeInfoHoverCardIcon(undoButtonHelpText)}
+                            <InfoHoverCard>
+                                {undoButtonHelpText}
+                            </InfoHoverCard>
                         </Center>
                     </Stack>
                 </Grid.Col>
@@ -286,11 +290,11 @@ export function getDemoBM() {
                         <Center>
                             {corr2Components.undoRedoButtons}
                             <Space w="sm"/>
-                            {makeInfoHoverCardIcon(<>
+                            <InfoHoverCard>
                                 {undoButtonHelpText}
                                 <Space h="sm"/>
                                 {undoButtonHelpTextCorr}
-                            </>)}
+                            </InfoHoverCard>
                         </Center>
                     </Stack>
                 </Grid.Col>
@@ -301,7 +305,9 @@ export function getDemoBM() {
                         <Center>
                             {cs2Components.undoRedoButtons}
                             <Space w="sm"/>
-                            {makeInfoHoverCardIcon(undoButtonHelpText)}
+                            <InfoHoverCard>
+                                {undoButtonHelpText}
+                            </InfoHoverCard>
                         </Center>
                     </Stack>
                 </Grid.Col>

+ 12 - 8
src/frontend/demos/demo_corr.tsx

@@ -2,10 +2,10 @@ import * as React from "react";
 import {SimpleGrid, Text, Title, Stack, Center, Image, Group, Space} from "@mantine/core";
 
 import {OnionContext} from "../onion_context";
-import {newVersionedModel, VersionedModelState, undoButtonHelpText} from "./versioned_model/single_model";
-import {newCorrespondence, undoButtonHelpTextCorr} from "./versioned_model/correspondence";
-import {newManualRenderer, ManualRendererProps} from "./manual_renderer";
-import {makeInfoHoverCardIcon} from "./help_icons";
+import {newVersionedModel, VersionedModelState, undoButtonHelpText} from "../versioned_model/single_model";
+import {newCorrespondence, undoButtonHelpTextCorr} from "../versioned_model/correspondence";
+import {newManualRenderer, ManualRendererProps} from "../versioned_model/manual_renderer";
+import {InfoHoverCard} from "../info_hover_card";
 import corr_csImage from './assets/corr_cs.svg';
 import corr_asImage from './assets/corr_as.svg';
 import corr_corrImage from './assets/corr_corr.svg';
@@ -184,7 +184,9 @@ export function getDemoCorr() {
                         <Center>
                             {csComponents.undoRedoButtons}
                             <Space w="sm"/>
-                            {makeInfoHoverCardIcon(undoButtonHelpText)}
+                            <InfoHoverCard>
+                                {undoButtonHelpText}
+                            </InfoHoverCard>
                         </Center>
                     </Stack>
                 </div>
@@ -199,11 +201,11 @@ export function getDemoCorr() {
                         <Center>
                             {corrComponents.undoRedoButtons}
                             <Space w="sm"/>
-                            {makeInfoHoverCardIcon(<>
+                            <InfoHoverCard>
                                 {undoButtonHelpText}
                                 <Space h="sm"/>
                                 {undoButtonHelpTextCorr}
-                            </>)}
+                            </InfoHoverCard>
                         </Center>
                     </Stack>
                 </div>
@@ -220,7 +222,9 @@ export function getDemoCorr() {
                         <Center>
                             {asComponents.undoRedoButtons}
                             <Space w="sm"/>
-                            {makeInfoHoverCardIcon(undoButtonHelpText)}
+                            <InfoHoverCard>
+                                {undoButtonHelpText}
+                            </InfoHoverCard>
                         </Center>
                     </Stack>
                 </div>

+ 6 - 4
src/frontend/demos/demo_editor.tsx

@@ -4,9 +4,9 @@ import {SimpleGrid, Text, Title, Stack, Center, Group, Space, Image} from "@mant
 import {PrimitiveRegistry} from "onion/primitive_delta";
 import {mockUuid} from "onion/test_helpers";
 
-import {newVersionedModel, VersionedModelState} from "./versioned_model/single_model";
-import {newManualRenderer, ManualRendererProps} from "./manual_renderer";
-import {makeInfoHoverCardIcon} from "./help_icons";
+import {newVersionedModel, VersionedModelState} from "../versioned_model/single_model";
+import {newManualRenderer, ManualRendererProps} from "../versioned_model/manual_renderer";
+import {InfoHoverCard} from "../info_hover_card";
 import {Actionblock, Resultblock} from "./blocks";
 import editor from './assets/editor.svg';
 
@@ -112,7 +112,9 @@ export function getDemoEditor() {
                         <Center>
                             {csComponents.undoRedoButtons}
                             <Space w="sm"/>
-                            {makeInfoHoverCardIcon(undoButtonHelpText)}
+                            <InfoHoverCard>
+                                {undoButtonHelpText}
+                            </InfoHoverCard>
                         </Center>
                     </Stack>
                 </div>

+ 5 - 3
src/frontend/demos/demo_pd.tsx

@@ -7,8 +7,8 @@ import {PrimitiveValue} from 'onion/types';
 import {mockUuid} from 'onion/test_helpers';
 import {Actionblock, Resultblock} from './blocks';
 import pdImage from './assets/pd.svg';
-import {newVersionedModel, undoButtonHelpText, VersionedModelState,} from './versioned_model/single_model';
-import {makeInfoHoverCardIcon} from './help_icons';
+import {newVersionedModel, undoButtonHelpText, VersionedModelState,} from '../versioned_model/single_model';
+import {InfoHoverCard} from '../info_hover_card';
 
 export const demo_PD_description =
     <>
@@ -193,7 +193,9 @@ export function getDemoPD() {
                             leftIcon={<Icons.IconChevronUp/>}>Union </Button>
                     {components.undoButton}
                     {components.redoButton}
-                    {makeInfoHoverCardIcon(undoButtonHelpText)}
+                    <InfoHoverCard>
+                        {undoButtonHelpText}
+                    </InfoHoverCard>
                     <Button onClick={cloneClicked} compact rightIcon={<Icons.IconChevronDown/>}>Clone</Button>
                 </Group>
             </div>

+ 2 - 2
src/frontend/index.tsx

@@ -1,7 +1,7 @@
 import * as React from 'react';
 import {createRoot} from 'react-dom/client';
-import './demos/rountangleEditor/RountangleEditor.css';
-import './demos/d3graph/d3graph.css';
+import './rountangleEditor/RountangleEditor.css';
+import './d3graph/d3graph.css';
 import './index.css';
 
 import {getApp} from "./app";

+ 7 - 5
src/frontend/demos/help_icons.tsx

@@ -2,7 +2,7 @@ import * as React from "react";
 import {ActionIcon, HoverCard} from "@mantine/core";
 import {IconInfoCircle} from "@tabler/icons";
 
-export function makeInfoHoverCardIcon(contents) {
+export function InfoHoverCard({children}) {
   return (
     <HoverCard shadow="md" width={300}>
       <HoverCard.Target>
@@ -11,18 +11,20 @@ export function makeInfoHoverCardIcon(contents) {
         </ActionIcon>
       </HoverCard.Target>
       <HoverCard.Dropdown>
-        {contents}
+        {children}
       </HoverCard.Dropdown>
     </HoverCard>
   );
 }
 
-export function makeOverlayHelpIcon(background, overlayText) {
+export function InfoHoverCardOverlay({children, contents}) {
   return (
     <div style={{position:"relative"}}>
-      {background}
+      {children}
       <div style={{position: "absolute", top: 0, right: 0}}>
-        {makeInfoHoverCardIcon(overlayText)}
+        <InfoHoverCard>
+          {contents}
+        </InfoHoverCard>
       </div>
     </div>
   );

+ 2 - 2
src/frontend/demos/rountangleEditor/RountangleActions.ts

@@ -1,6 +1,6 @@
-import {PrimitiveValue} from "onion/types";
+import {PrimitiveValue, UUID} from "onion/types";
 
-interface CreateRountangle  {tag: 'createRountangle',  id: PrimitiveValue, posX: number, posY: number, width: number, height: number}
+interface CreateRountangle  {tag: 'createRountangle',  id: UUID, posX: number, posY: number, width: number, height: number}
 interface MoveRountangle    {tag: 'moveRountangle',    id: PrimitiveValue, newPosX: number, newPosY: number}
 interface ResizeRountangle  {tag: 'resizeRountangle',  id: PrimitiveValue, width: number, height: number}
 interface DeleteRountangle  {tag: 'deleteRountangle',  id: PrimitiveValue}

src/frontend/demos/rountangleEditor/RountangleComponent.tsx → src/frontend/rountangleEditor/RountangleComponent.tsx


src/frontend/demos/rountangleEditor/RountangleEditor.css → src/frontend/rountangleEditor/RountangleEditor.css


+ 7 - 9
src/frontend/demos/rountangleEditor/RountangleEditor.tsx

@@ -1,12 +1,12 @@
 import * as React from "react";
 import {RountangleComponent} from "./RountangleComponent";
-import {OnionContext, OnionContextType} from "../../onion_context";
-import {PrimitiveValue, UUID} from "onion/types";
-import {D3OnionGraphData, D3OnionNodeData, D3OnionLinkData} from "../d3graph/d3graph_editable";
+import {OnionContext, OnionContextType} from "../onion_context";
+import {PrimitiveValue} from "onion/types";
+import {D3OnionGraphData, D3OnionNodeData, D3OnionLinkData} from "../d3graph/reducers/onion_graph";
 import {RountangleAction} from "./RountangleActions";
 import {INodeState, IValueState, GraphState} from "onion/graph_state";
-import {PrimitiveDelta, PrimitiveRegistry} from "onion/primitive_delta";
-import {assert, assertNever} from "../../../util/assert";
+import {PrimitiveDelta} from "onion/primitive_delta";
+import {assert, assertNever} from "../../util/assert";
 
 export interface Rountangle {
     readonly posX:   number;
@@ -87,13 +87,11 @@ export function RountangleEditor(props: RountangleEditorProps) {
 
     const dispatch = React.useCallback((action: RountangleAction) => {
         const deltas: PrimitiveDelta[] = [];
-        let uuid: UUID;
         let nodeState: INodeState | undefined;
 
         switch (action.tag) {
             case "createRountangle":
-                uuid = new UUID(action.id);
-                const createRountangleNodeDelta = onionContext.primitiveRegistry.newNodeCreation(uuid);
+                const createRountangleNodeDelta = onionContext.primitiveRegistry.newNodeCreation(action.id);
                 deltas.push(createRountangleNodeDelta);
                 const edgeSpec: [string,PrimitiveValue][] = [
                     ["type","Rountangle"],
@@ -199,7 +197,7 @@ export function RountangleEditor(props: RountangleEditorProps) {
             if (cursorPoint) {
                 dispatch({
                     tag: 'createRountangle',
-                    id: onionContext.generateUUID().value,
+                    id: onionContext.generateUUID(),
                     posX: cursorPoint.x,
                     posY: cursorPoint.y,
                     width: 100,

src/frontend/demos/rountangleEditor/RountangleResizeHandleComponent.tsx → src/frontend/rountangleEditor/RountangleResizeHandleComponent.tsx


src/frontend/demos/rountangleEditor/RountangleStore.ts → src/frontend/rountangleEditor/RountangleStore.ts


+ 3 - 3
src/frontend/demos/versioned_model/correspondence.tsx

@@ -2,11 +2,11 @@ import * as React from "react";
 import * as Mantine from "@mantine/core";
 import * as Icons from "@tabler/icons";
 
-import {OnionContext, OnionContextType} from "../../onion_context";
+import {OnionContext, OnionContextType} from "../onion_context";
 import {newVersionedModel} from "./single_model";
 import {emptyGraph} from "../d3graph/d3graph";
-import {D3GraphUpdater} from "../d3graph/d3graph_updater";
-import {RountangleParser} from "../../../parser/rountangle_parser";
+import {D3GraphUpdater} from "../d3graph/reducers/onion_graph";
+import {RountangleParser} from "../../parser/rountangle_parser";
 import {Version} from "onion/version";
 import {GraphState} from "onion/graph_state"; 
 import {CompositeDelta} from "onion/composite_delta";

src/frontend/demos/versioned_model/help_text.tsx → src/frontend/versioned_model/help_text.tsx


+ 13 - 13
src/frontend/demos/manual_renderer.tsx

@@ -9,16 +9,15 @@ import {
 } from "@tabler/icons";
 import {Button, Group, List, Modal, Stack, Table, Text, Title} from "@mantine/core";
 
-import {RountangleEditor} from "./rountangleEditor/RountangleEditor";
+import {RountangleEditor} from "../rountangleEditor/RountangleEditor";
 import {Version} from "onion/version";
-import {makeOverlayHelpIcon} from "./help_icons";
-import {D3Graph, defaultGraphForces} from "./d3graph/d3graph";
-import {D3OnionGraphData} from "./d3graph/d3graph_editable";
+import {InfoHoverCardOverlay} from "../info_hover_card";
+import {D3Graph, defaultGraphForces} from "../d3graph/d3graph";
+import {D3OnionGraphData, D3GraphUpdater} from "../d3graph/reducers/onion_graph";
 import {PrimitiveDelta, EdgeCreation, EdgeUpdate, PrimitiveRegistry, NodeCreation, NodeDeletion} from "onion/primitive_delta";
 import {PrimitiveValue} from "onion/types";
 import {GraphState, INodeState} from "onion/graph_state";
 import {Geometry2DRect, getGeometry, isInside} from "../../parser/rountangle_parser";
-import {D3GraphUpdater} from "./d3graph/d3graph_updater";
 
 export interface ManualRendererProps {
   asGraph: D3OnionGraphData;
@@ -159,7 +158,7 @@ export function newManualRenderer() {
               }} compact rightIcon={<IconChevronRight/>}>Redo</Button>
             </Group>
           </Group>
-          {makeOverlayHelpIcon(
+          <InfoHoverCardOverlay contents={<Text><b>Left-Drag</b>: Move/Resize Rountangle / Pan Canvas</Text>}>
             <RountangleEditor
               graph={csGraph}
               graphState={csGraphState}
@@ -171,9 +170,8 @@ export function newManualRenderer() {
                 filteredDeltas.forEach(d => csGraphState.exec(d, new D3GraphUpdater(setCsGraph, 0, 0)));
                 setInconsistencies(findInconsistencies(csGraphState, props.asGraphState));
               }}
-            />,
-            <Text><b>Left-Drag</b>: Move/Resize Rountangle / Pan Canvas</Text>
-          )}
+            />
+          </InfoHoverCardOverlay>
         </Stack>
         <Stack align="flex-start">
           <Stack style={{height:"100%"}}>
@@ -193,10 +191,12 @@ export function newManualRenderer() {
         </Stack>
         <Stack>
           <Title order={5}>Abstract Syntax (read-only)</Title>
-          {makeOverlayHelpIcon(<D3Graph
-            graph={props.asGraph}
-            forces={defaultGraphForces}
-          />, <Text><b>Left-Drag</b>: Drag Node</Text>)}
+          <InfoHoverCardOverlay contents={<Text><b>Left-Drag</b>: Drag Node</Text>}>
+            <D3Graph
+              graph={props.asGraph}
+              forces={defaultGraphForces}
+            />
+          </InfoHoverCardOverlay>
         </Stack>
       </Group>
       <Group position="apart">

+ 31 - 27
src/frontend/demos/versioned_model/single_model.tsx

@@ -2,27 +2,27 @@ import * as React from "react";
 import * as Mantine from "@mantine/core";
 import * as Icons from "@tabler/icons";
 
-import {D3GraphUpdater} from "../d3graph/d3graph_updater";
-import {D3GraphEditable, UserEditCallback, SetNodePositionCallback, D3OnionGraphData} from "../d3graph/d3graph_editable";
+import {D3OnionGraphData, D3GraphUpdater} from "../d3graph/reducers/onion_graph";
+import {D3GraphEditable, UserEditCallback} from "../d3graph/d3graph_editable";
 
 import {
   DeltaGraphState,
   fullDeltaId,
   deltaGraphReducer,
-} from "./delta_graph";
+} from "../d3graph/reducers/delta_graph";
 
 import {
   HistoryGraphState,
   initialHistoryGraph,
   fullVersionId,
   historyGraphReducer,
-} from "./history_graph";
+} from "../d3graph/reducers/history_graph";
 
 import * as helpText from "./help_text";
 
 import {D3Graph, emptyGraph, defaultGraphForces} from "../d3graph/d3graph";
 import {RountangleEditor} from "../rountangleEditor/RountangleEditor";
-import {makeOverlayHelpIcon} from "../help_icons";
+import {InfoHoverCardOverlay} from "../info_hover_card";
 
 import {embed, Version, VersionRegistry} from "onion/version";
 import {PrimitiveDelta, PrimitiveRegistry} from "onion/primitive_delta";
@@ -191,35 +191,39 @@ export function newVersionedModel({readonly}) {
   }
 
   function getReactComponents(state: VersionedModelState, callbacks: VersionedModelCallbacks) {
-    const graphStateComponent = makeOverlayHelpIcon(readonly ? 
-      <D3Graph graph={state.graph} forces={defaultGraphForces} />
-      : <D3GraphEditable
-          graph={state.graph}
-          graphState={graphState}
-          forces={defaultGraphForces}
-          setNextNodePosition={(newX,newY) => {x = newX; y = newY;}}
-          onUserEdit={callbacks.onUserEdit}
-        />, readonly ? helpText.graphEditorReadonly : helpText.graphEditor);
-
-    const depGraphL1Component = makeOverlayHelpIcon(
-      <D3Graph graph={state.deltaGraphL1} forces={defaultGraphForces} />,
-      helpText.depGraph);
-    const depGraphL0Component = makeOverlayHelpIcon(
-      <D3Graph graph={state.deltaGraphL0} forces={defaultGraphForces} />,
-      helpText.depGraph);
-
-    const historyComponent = makeOverlayHelpIcon(
+    const graphStateComponent = <InfoHoverCardOverlay
+      contents={readonly ? helpText.graphEditorReadonly : helpText.graphEditor}>
+        {readonly ? 
+          <D3Graph graph={state.graph} forces={defaultGraphForces} />
+        : <D3GraphEditable
+            graph={state.graph}
+            graphState={graphState}
+            forces={defaultGraphForces}
+            setNextNodePosition={(newX,newY) => {x = newX; y = newY;}}
+            onUserEdit={callbacks.onUserEdit}
+          />}
+    </InfoHoverCardOverlay>;
+
+    const depGraphL1Component = <InfoHoverCardOverlay contents={helpText.depGraph}>
+      <D3Graph graph={state.deltaGraphL1} forces={defaultGraphForces} />
+    </InfoHoverCardOverlay>;
+
+    const depGraphL0Component = <InfoHoverCardOverlay contents={helpText.depGraph}>
+      <D3Graph graph={state.deltaGraphL0} forces={defaultGraphForces} />
+    </InfoHoverCardOverlay>;
+
+    const historyComponent = <InfoHoverCardOverlay contents={helpText.historyGraph}>
       <D3Graph graph={state.historyGraph} forces={defaultGraphForces}
-        mouseUpHandler={(e, {x, y}, node) => node ? callbacks.onVersionClicked?.(node.obj) : undefined} />,
-      helpText.historyGraph);
+        mouseUpHandler={(e, {x, y}, node) => node ? callbacks.onVersionClicked?.(node.obj) : undefined} />
+    </InfoHoverCardOverlay>;
 
-    const rountangleEditor = makeOverlayHelpIcon(
+    const rountangleEditor = <InfoHoverCardOverlay contents={helpText.rountangleEditor}>
       <RountangleEditor
         graph={state.graph}
         graphState={graphState}
         onUserEdit={callbacks.onUserEdit}
       />,
-      helpText.rountangleEditor);
+    </InfoHoverCardOverlay>;
 
     const makeUndoOrRedoButton = (parentsOrChildren, text, leftIcon?, rightIcon?, callback?) => {
       if (parentsOrChildren.length === 0) {