فهرست منبع

Continue refactor

Joeri Exelmans 1 سال پیش
والد
کامیت
57adf30381

+ 2 - 2
src/frontend/demos/demo_live.tsx

@@ -462,8 +462,8 @@ export function getDemoLive() {
             </InfoHoverCardOverlay>
           </Stack>
           <Stack>
-            {components.makeTabs("state", ["state", "merge", "historyGraphviz", "deltaL1", "deltaL0"])}
-            {components.makeTabs("deltaL1", ["state", "merge", "historyGraphviz", "deltaL1", "deltaL1Graphviz", "deltaL0", "deltaL0Graphviz"])}
+            {components.makeTabs("deltaL1", ["state", "merge", "historyGraphviz", "deltaL1", "deltaL0"])}
+            {components.makeTabs("merge", ["state", "merge", "historyGraphviz", "deltaL1", "deltaL1Graphviz", "deltaL0", "deltaL0Graphviz"])}
             Run-time model version: {state.version.hash.toString('hex').substring(0,8)}<br/>
             DesignModel version: {rt2d.get(state.version)!.hash.toString('hex').substring(0,8)}
           </Stack>

+ 32 - 111
src/frontend/demos/demo_pd.tsx

@@ -1,13 +1,13 @@
 import * as React from 'react';
 import * as Icons from '@tabler/icons';
-import {Button, Divider, Group, Image, SimpleGrid, Space, Text, Title,} from '@mantine/core';
+import {Button, Divider, Group, Image, SimpleGrid, Space, Text, Title, Stack} from '@mantine/core';
 
 import {PrimitiveRegistry} from 'onion/primitive_delta';
 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 {newOnion, undoButtonHelpText, VersionedModelState} from '../versioned_model/single_model2';
 import {InfoHoverCard} from '../info_hover_card';
 
 export const demo_PD_description =
@@ -89,118 +89,39 @@ export const demo_PD_description =
 
 
 export function getDemoPD() {
+    const primitiveRegistry = new PrimitiveRegistry();
+    const generateUUID = mockUuid();
     const generateBranchID = mockUuid();
 
-    const model = newVersionedModel({readonly: false});
-
-    const initialState: [PrimitiveValue, VersionedModelState, any][] = [
-        [generateBranchID().value, model.initialState, model],
-    ];
+    const onion = newOnion({readonly: false, primitiveRegistry});
 
+    // Functional component
     return function () {
-        const [globalState, setGlobalState] = React.useState<[PrimitiveValue, VersionedModelState, any][]>(initialState);
-
-        const getSetBranchState = i => {
-            return callback => {
-                setGlobalState(prevGlobalState => {
-                    const copy = prevGlobalState.slice();
-                    const [branchName, prevBranchState, m] = copy[i];
-                    copy[i] = [branchName, callback(prevBranchState), m];
-                    return copy;
-                });
-            };
-        }
-
-        return <div style={{minWidth: 1300}}>{globalState.map(([branchName, branchState, {
-            getCurrentVersion,
-            getReducer,
-            getReactComponents
-        }], i) => {
-            const setBranchState = getSetBranchState(i);
-
-            const reducer = getReducer(setBranchState);
-
-            const components = getReactComponents(branchState, setBranchState, {
-                onUserEdit: reducer.createAndGotoNewVersion,
-                onUndoClicked: reducer.undo,
-                onRedoClicked: reducer.redo,
-                onVersionClicked: reducer.gotoVersion,
-                onMerge: reducer.appendVersions,
-            });
-
-            const unionClicked = () => {
-                const [mergeWithBranchName, mergeWithBranchState, {getReducer: getOtherReducer}] = globalState[i - 1];
-                const otherReducer = getOtherReducer(getSetBranchState(i - 1));
-                const addRecursive = ([version, delta, _]) => {
-                    if (version.parents.length > 0) {
-                        addRecursive(version.parents[0])
-                    }
-                    otherReducer.addDeltasAndVersion(delta.deltas, delta.getDescription(), version.hash);
-                }
-                addRecursive(getCurrentVersion().parents[0]);
-                setGlobalState(prevGlobalState => [
-                    ...prevGlobalState.slice(0, i),
-                    ...prevGlobalState.slice(i + 1),
-                ]);
-            }
-
-            const cloneClicked = () => {
-                {/*const newBranchName = prompt("Branch name: (ESC to cancel)", "branch");*/}
-                const newBranchName = generateBranchID().value;
-                if (newBranchName === null) {
-                    return;
-                }
-                if (globalState.some(([existingBranchName]) => existingBranchName === newBranchName)) {
-                    alert("Branch with this name already exists!");
-                    return;
-                }
-                const newModel = newVersionedModel({readonly: false});
-                const newBranchState = newModel.initialState;
-
-                setGlobalState(prevGlobalState => [
-                    ...prevGlobalState.slice(0, i + 1),
-                    [newBranchName, newBranchState, newModel],
-                    ...prevGlobalState.slice(i + 1),
-                ]);
-
-                const setNewBranchState = getSetBranchState(i + 1);
-
-                const reducer = newModel.getReducer(setNewBranchState);
-                const compositeDeltas = [...getCurrentVersion()].reverse();
-                compositeDeltas.forEach((c: any) => {
-                    reducer.createAndGotoNewVersion(c.deltas, c.getDescription());
-                });
-            }
-
-            return <div key={JSON.stringify(branchName)}>
-                {i > 0 ? <><Space h="md"/><Divider my="sm"/></> : <></>}
-                <SimpleGrid cols={3}>
-                    <div>
-                        <Text>State</Text>
-                        {components.graphStateComponent}
-                    </div>
-                    <div>
-                        <Text>History</Text>
-                        {components.historyComponentWithMerge}
-                    </div>
-                    <div>
-                        <Text>Deltas (L0)</Text>
-                        {components.deltaGraphL0Component}
-                    </div>
-                </SimpleGrid>
-                <Space h="md"/>
-                <Group position="center">
-                    <Button onClick={unionClicked} compact disabled={i === 0}
-                            leftIcon={<Icons.IconChevronUp/>}>Union </Button>
-                    {components.undoButton}
-                    {components.redoButton}
-                    <InfoHoverCard>
-                        {undoButtonHelpText}
-                    </InfoHoverCard>
-                    <Button onClick={cloneClicked} compact rightIcon={<Icons.IconChevronDown/>}>Clone</Button>
-                </Group>
-            </div>
-        })
-        }</div>;
+        const {state, reducer, components} = onion.useOnion(reducer => ({}));
+
+        return <div style={{minWidth: 1300}}>
+            <SimpleGrid cols={3}>
+                <Stack>
+                    <Title order={4}>State</Title>
+                    {components.graphStateComponent}
+                    <Group position="center">
+                        {components.undoButton}
+                        {components.redoButton}
+                        <InfoHoverCard>
+                            {undoButtonHelpText}
+                        </InfoHoverCard>
+                    </Group>
+                </Stack>
+                <Stack>
+                    <Title order={4}>History</Title>
+                    {components.historyComponentWithMerge}
+                </Stack>
+                <Stack>
+                    <Title order={4}>Deltas</Title>
+                    {components.deltaGraphL0Component}
+                </Stack>
+            </SimpleGrid>
+            <Space h="md"/>
+        </div>;
     }
 }

+ 23 - 12
src/frontend/versioned_model/graph_view.tsx

@@ -2,7 +2,7 @@ import * as React from "react";
 import * as Mantine from "@mantine/core";
 import { Graphviz } from 'graphviz-react';
 
-import {D3Graph, D3GraphData, defaultGraphForces} from "../d3graph/d3graph";
+import {D3Graph, D3GraphData, D3NodeData, defaultGraphForces} from "../d3graph/d3graph";
 import {InfoHoverCardOverlay} from "../info_hover_card";
 
 function esc(str) {
@@ -20,21 +20,27 @@ const graphvizMap = new Map();
   if (callback) callback(button);
 }
 
-export function GraphView({graphData, help, mouseUpHandler}) {
-  const [renderer, setRenderer] = React.useState<"d3"|"graphviz">("d3");
+export type Renderer = 'd3' | 'graphviz';
+
+interface GraphViewProps<N,L> {
+  graphData: D3GraphData<N,L>;
+  help: JSX.Element;
+  mouseUpHandler: (event, mouse:{x:number, y:number}, node?: D3NodeData<N>) => void;
+  defaultRenderer?: Renderer;
+}
+
+export function GraphView<N,L>(props: GraphViewProps<N,L>) {
+  const {graphData, help, mouseUpHandler, defaultRenderer} = props;
+
+  const [renderer, setRenderer] = React.useState<Renderer>(defaultRenderer || "d3");
+
+  const id = React.useId();
 
   if (mouseUpHandler) {
     for (const node of graphData.nodes) {
-      graphvizMap.set(esc2(node.id), (button) => mouseUpHandler({button}, {x:0, y:0}, node));
+      graphvizMap.set(id+node.id, (button) => mouseUpHandler({button}, {x:0, y:0}, node));
     }
   }
-  const dot = `digraph {
-    bgcolor="transparent";
-    ${graphData.nodes.map(node => `"${esc(node.id)}" [${node.label===""?`shape=circle, width=0.3, height=0.3`:`shape=box`}, label="${esc(node.label)}", fillcolor="${node.color}", style="filled,rounded${node.bold?`,bold`:``}", URL="javascript:${esc2(`graphvizClicked('${node.id}', 2)`)}"]`).join('\n')}
-    ${graphData.links.map(link => `"${esc(link.source.id || link.source)}" -> "${esc(link.target.id || link.target)}" [label="${esc(link.label)}", color="${link.color}"${link.bidirectional?`, dir=none`:``}, labelURL="javascript:${esc2(`graphvizClicked('${link.source.id||link.source}', 1)`)}"]`).join('\n')}
-  }`;
-
-  // console.log(dot);
 
   return <Mantine.Stack>
     <Mantine.Group position="center">
@@ -54,7 +60,12 @@ export function GraphView({graphData, help, mouseUpHandler}) {
       :
       // @ts-ignore:
       <Mantine.ScrollArea style={{backgroundColor:"#eee"}}>
-        <Graphviz dot={dot} options={{fit:false, width:null, height:null, scale:1}}/>
+        <Graphviz dot={`digraph {
+            bgcolor="transparent";
+            ${graphData.nodes.map(node => `"${esc(node.id)}" [${node.label===""?`shape=circle, width=0.3, height=0.3`:`shape=box`}, label="${esc(node.label)}", fillcolor="${node.color}", style="filled,rounded${node.bold?`,bold`:``}", URL="javascript:${esc2(`graphvizClicked('${id+node.id}', 2)`)}"]`).join('\n')}
+            ${graphData.links.map(link => `"${esc(link.source.id || link.source)}" -> "${esc(link.target.id || link.target)}" [label="${esc(link.label)}", color="${link.color}"${link.bidirectional?`, dir=none`:``}]`).join('\n')}
+          }`}
+          options={{fit:false, width:null, height:null, scale:1}}/>
       </Mantine.ScrollArea>
     }
   </Mantine.Stack>;

+ 9 - 3
src/frontend/versioned_model/merge_view.tsx

@@ -57,18 +57,21 @@ export function MergeView({history, setHistory, forces, versionRegistry, onMerge
   const [showTooltip, setShowTooltip] = React.useState<any>(null);
 
   return <>
-    <GraphView graphData={historyHighlighted} help={historyGraphHelpText} 
+    <GraphView<Version,Delta> graphData={historyHighlighted} help={historyGraphHelpText} 
         mouseUpHandler={(e, {x, y}, node) => {
           if (node !== undefined) {
             // @ts-ignore:
-            if (e.button === 2) { // right mouse button
+            if (e.button === 2) { // right mouse button 
               if (inputs.includes(node.obj)) {
+                // remove from inputs
                 setInputs(inputs => inputs.filter(v => v !== node.obj));
               }
               else {
+                // add to inputs
                 setInputs(inputs => inputs.concat(node.obj));
               }
               setOutputs([]);
+              onGoto(node.obj);
             }
             // @ts-ignore:
             else if (e.button === 1) { // middle mouse button
@@ -76,6 +79,7 @@ export function MergeView({history, setHistory, forces, versionRegistry, onMerge
             }
           }
         }}
+      defaultRenderer="graphviz"
       />
     <Mantine.Group style={{minHeight: 30}}>
       {inputs.map(version => <Mantine.Badge key={fullVersionId(version)} pr={3} variant="outline" color="dark" style={{backgroundColor: inputColor}} rightSection={removeButton(version)}>
@@ -104,9 +108,11 @@ export function MergeView({history, setHistory, forces, versionRegistry, onMerge
         setOutputs(outputs);
         onMerge(outputs);
       }}>Merge</Mantine.Button>
-      <Mantine.Button compact leftIcon={<Icons.IconNavigation/>} disabled={inputs.length!==1} onClick={() => {
+
+{/*      <Mantine.Button compact leftIcon={<Icons.IconNavigation/>} disabled={inputs.length!==1} onClick={() => {
         onGoto(inputs[0]);
       }}>Goto</Mantine.Button>
+*/}
       <Mantine.Button compact leftIcon={<Icons.IconDatabaseImport/>} onClick={() => {
         let parsed;
         while (true) {

+ 13 - 10
src/frontend/versioned_model/single_model2.tsx

@@ -20,7 +20,7 @@ import {
 
 import * as helpText from "./help_text";
 import {MergeView} from "./merge_view";
-import {GraphView} from "./graph_view";
+import {GraphView, Renderer} from "./graph_view";
 
 import {D3Graph, emptyGraph, defaultGraphForces} from "../d3graph/d3graph";
 import {RountangleEditor} from "../rountangleEditor/RountangleEditor";
@@ -240,14 +240,17 @@ export function newOnion({readonly, primitiveRegistry}) {
             />
           </InfoHoverCardOverlay>;
 
-    // Serialize delta
-    const onDeltaClick = (e, {x,y}, node) => {
-      if (node) {
-        alert(JSON.stringify(node.obj.serialize(), null, 2));
-      }
-    }
-    const deltaGraphL0Component = <GraphView graphData={state.deltaGraphL0} help={helpText.deltaGraph} mouseUpHandler={onDeltaClick} />;
-    const deltaGraphL1Component = <GraphView graphData={state.deltaGraphL1} help={helpText.deltaGraph} mouseUpHandler={onDeltaClick} />;
+    const deltaComponentProps = {
+      help: helpText.deltaGraph,
+      defaultRenderer: ('graphviz' as Renderer),
+      mouseUpHandler: (e, {x,y}, node) => {
+        if (node) {
+          alert(JSON.stringify(node.obj.serialize(), null, 2));
+        }
+      },
+    };
+    const deltaGraphL0Component = <GraphView<Delta,null> graphData={state.deltaGraphL0} {...deltaComponentProps} />;
+    const deltaGraphL1Component = <GraphView<Delta,null> graphData={state.deltaGraphL1} {...deltaComponentProps} />;
     const historyComponent = <GraphView graphData={state.historyGraph} help={helpText.historyGraph}
       mouseUpHandler={(e, {x, y}, node) => node ? callbacks.onVersionClicked?.(node.obj) : undefined} />;
 
@@ -259,7 +262,7 @@ export function newOnion({readonly, primitiveRegistry}) {
       onMerge={outputs => callbacks.onMerge?.(outputs)}
       onGoto={version => callbacks.onVersionClicked?.(version)}
       {...{primitiveRegistry, compositeLevel, addDeltasAndVersion}}
-      />
+    />;
 
     const rountangleEditor = <InfoHoverCardOverlay contents={helpText.rountangleEditor}>
       <RountangleEditor