diff --git a/src/components/Main.js b/src/components/Main.js index d437f503..d079ab17 100644 --- a/src/components/Main.js +++ b/src/components/Main.js @@ -1,13 +1,14 @@ import React from 'react'; // import MechanismNode from '../model/nodes/mechanism/MechanismNode'; import { withStyles } from '@mui/styles'; +import { PNLClasses } from '../constants'; +import { buildModel } from '../model/utils'; import BG from "../assets/svg/bg-dotted.svg"; import ModelInterpreter from '../model/Interpreter'; +import Composition from './views/compositions/Composition'; +import GenericMechanism from './views/mechanisms/GenericMechanism'; import MetaDiagram, { ComponentsMap } from "@metacell/meta-diagram"; import CustomLinkWidget from './views/projections/CustomLinkWidget'; -import GenericMechanism from './views/mechanisms/GenericMechanism'; -import { buildModel } from '../model/utils'; -import { PNLClasses } from '../constants'; const mockModel = require('../resources/model').mockModel; @@ -33,14 +34,15 @@ class Main extends React.Component { const model = interpreter.getModel(); const metaModel = buildModel(model); - const componentsMap = new ComponentsMap( - new Map(Object.entries({'mechanism': GenericMechanism})), - new Map(Object.entries({'projection': CustomLinkWidget})) - ) + const componentsMap = new ComponentsMap(new Map(), new Map()); + + componentsMap.nodes.set(PNLClasses.COMPOSITION, Composition); + componentsMap.nodes.set(PNLClasses.MECHANISM, GenericMechanism); + componentsMap.links.set(PNLClasses.PROJECTION, CustomLinkWidget); return (
- ({ + root: { + '& .react-draggable': { + background: draggableBg, + border: `0.125rem solid ${chipBorderColor}`, + borderRadius: '0.75rem', + display: "flex !important", + alignItems: "center", + justifyContent: "center", + + '&:hover': { + borderColor: listItemActiveBg + }, + }, + + '& .MuiChip-root': { + background: chipBorderColor, + borderRadius: '0.75rem', + padding: '0 0.5rem', + display: "flex", + left: 0, + position: 'absolute', + color: chipTextColor, + top: '-1.75rem', + alignItems: "center", + height: '1.5rem', + letterSpacing: '-0.005rem', + fontWeight: 510, + fontSize: '0.8125rem', + lineHeight: '1.25rem', + flexDirection: 'row-reverse', + + '& .MuiChip-label': { + padding: 0, + }, + + '& .MuiChip-icon': { + margin: '0 0 0 0.25rem', + }, + }, + }, + + selected: { + '&:before': { + left: 0, + ...commonStyles + }, + + '&:after': { + right: 0, + ...commonStyles + }, + + '& .MuiChip-root': { + background: listItemActiveBg + }, + + '& .react-draggable': { + borderColor: listItemActiveBg, + } + }, +}); + +class Composition extends React.Component { + constructor(props) { + super(props); + this.state = { + expanded: false, + width: 442, + height: 192, + x: 0, + y: 0 + } + this.changeVisibility = this.changeVisibility.bind(this); + } + + changeVisibility() { + this.setState({expanded: !this.state.expanded}); + } + + render() { + const { expanded } = this.state; + const { classes } = this.props; + + return ( + + { + this.setState({ x: d.x, y: d.y }); + }} + onResizeStop={(e, direction, ref, delta, position) => { + this.setState({ + width: ref.style.width, + height: ref.style.height, + ...position + }); + }} + > + } label="New Comp" color="secondary" /> + + + ); + } +} + +export default withStyles(styles)(Composition); diff --git a/src/components/views/mechanisms/GenericMechanism.js b/src/components/views/mechanisms/GenericMechanism.js index 74ebdce1..e591606b 100644 --- a/src/components/views/mechanisms/GenericMechanism.js +++ b/src/components/views/mechanisms/GenericMechanism.js @@ -1,10 +1,7 @@ import * as React from "react"; import MechSimple from "./MechSimple"; import MechMetadata from "./MechMetadata"; -import { Rnd } from "react-rnd"; import { withStyles } from "@mui/styles"; -import { Box, Chip } from "@mui/material"; -import MORE_OPTION from "../../../assets/svg/option.svg" import vars from "../../../assets/styles/variables"; const { draggableBg, listItemActiveBg, textWhite, chipTextColor, chipBorderColor } = vars; @@ -97,37 +94,14 @@ class GenericMechanism extends React.Component { render() { const { expanded } = this.state; - const { classes } = this.props; return ( - // <> - // { expanded - // ? ( ) - // : ( ) - // } - // - - { - this.setState({ x: d.x, y: d.y }); - }} - onResizeStop={(e, direction, ref, delta, position) => { - this.setState({ - width: ref.style.width, - height: ref.style.height, - ...position - }); - }} - > - } label="New Comp" color="secondary" /> - { expanded + <> + { expanded ? ( ) : ( ) } - - + ); } } diff --git a/src/model/Interpreter.ts b/src/model/Interpreter.ts index 58d5fcb2..0fe782c8 100644 --- a/src/model/Interpreter.ts +++ b/src/model/Interpreter.ts @@ -1,9 +1,9 @@ import { GVTypes, PNLClasses } from '../constants'; +import { PortTypes } from '@metacell/meta-diagram'; import ProjectionLink from './links/ProjectionLink'; import QueryService from '../services/queryService'; import MechanismNode from './nodes/mechanism/MechanismNode'; import CompositionNode from './nodes/composition/CompositionNode'; -import { PortTypes } from '@metacell/meta-diagram'; const html2json = require('html2json').html2json const typesArray = Object.values(GVTypes); @@ -13,10 +13,15 @@ const parse = require('dotparser'); export default class ModelInterpreter { nativeModel: any; jsonModel: Object; + modelMap: { [key: string]: Map }; constructor(model: any) { this.nativeModel = model; this.jsonModel = this._convertModel(model); + this.modelMap = { + 'nodes': new Map(), + 'links': new Map() + }; } _convertModel(model: any) : Object { @@ -26,13 +31,13 @@ export default class ModelInterpreter { }; parsedModel[PNLClasses.COMPOSITION] = model[PNLClasses.COMPOSITION].map((singleModel: any) => { - const newModel = parse(singleModel).map((elem: any) => ModelInterpreter.castObject(elem)); + const newModel = parse(singleModel).map((elem: any) => ModelInterpreter.castObject(elem, undefined, this.modelMap)); return newModel; }); parsedModel[PNLClasses.MECHANISM] = model[PNLClasses.MECHANISM].map((singleNode: any) => { let tempNode = parse(singleNode)[0].children.filter((elem: { node_id: { id: string; }; }) => elem.node_id.id !== 'graph'); - let newNode = tempNode.map((elem: any) => ModelInterpreter.castObject(elem)); + let newNode = tempNode.map((elem: any) => ModelInterpreter.castObject(elem, undefined, this.modelMap)); return newNode; }); @@ -51,6 +56,10 @@ export default class ModelInterpreter { return this.nativeModel; } + getModelElementsMap() { + return this.modelMap; + } + static parseNodePorts(name: string, type: string): { [key: string]: any } { let ports: { [key: string]: any[] } = { [PortTypes.INPUT_PORT]: [], @@ -80,7 +89,11 @@ export default class ModelInterpreter { return ports; } - static castObject(item: MechanismNode|CompositionNode|ProjectionLink|any) : MechanismNode|CompositionNode|ProjectionLink { + static castObject( + item: MechanismNode|CompositionNode|ProjectionLink|any, + parent: any|undefined, + modelMap: { [key: string]: Map }) + : MechanismNode|CompositionNode|ProjectionLink { let newNode = item; if (item?.type === undefined) { throw new TypeError('type is missing, object cannot be casted to the right class type.'); @@ -94,6 +107,8 @@ export default class ModelInterpreter { [PNLClasses.PROJECTION]: [], [PNLClasses.COMPOSITION]: [], } + newNode = new CompositionNode(item.id, parent, '', false, ports, extra, children); + modelMap['nodes'].set(newNode.getName(), newNode); item.children.forEach((element: any) => { if (element.type === 'attr_stmt') { extra[element.target] = {} @@ -107,15 +122,15 @@ export default class ModelInterpreter { if (typesArray.includes(element.type)) { switch (element.type) { case GVTypes.COMPOSITION: { - children[PNLClasses.COMPOSITION].push(ModelInterpreter.castObject(element)); + children[PNLClasses.COMPOSITION].push(ModelInterpreter.castObject(element, newNode, modelMap)); break; } case GVTypes.MECHANISM: { - children[PNLClasses.MECHANISM].push(ModelInterpreter.castObject(element)); + children[PNLClasses.MECHANISM].push(ModelInterpreter.castObject(element, newNode, modelMap)); break; } case GVTypes.PROJECTION: { - children[PNLClasses.PROJECTION].push(ModelInterpreter.castObject(element)); + children[PNLClasses.PROJECTION].push(ModelInterpreter.castObject(element, newNode, modelMap)); break; } default: @@ -125,7 +140,6 @@ export default class ModelInterpreter { } } }); - newNode = new CompositionNode(item.id, '', false, ports, extra, children); break; } case GVTypes.MECHANISM: { @@ -142,7 +156,8 @@ export default class ModelInterpreter { extra[singleAttr?.id] = singleAttr?.eq; } }); - newNode = new MechanismNode(item?.node_id?.id, '', false, ports, extra); + newNode = new MechanismNode(item?.node_id?.id, parent, '', false, ports, extra); + modelMap['nodes'].set(newNode.getName(), newNode); break; } case GVTypes.PROJECTION: { @@ -165,6 +180,7 @@ export default class ModelInterpreter { receiverPort = item.edge_list[1]['port']['id']; } newNode = new ProjectionLink(name, sender, senderPort, receiver, receiverPort, false, extra); + modelMap['links'].set(newNode.getName(), newNode); break; } default: diff --git a/src/model/nodes/composition/CompositionNode.ts b/src/model/nodes/composition/CompositionNode.ts index 204ff8a1..9a7fb71f 100644 --- a/src/model/nodes/composition/CompositionNode.ts +++ b/src/model/nodes/composition/CompositionNode.ts @@ -1,20 +1,25 @@ -// import MetaDiagram from 'meta-diagram'; -import Port from '../PortNode'; -import PortNode from '../PortNode'; import { PNLClasses } from '../../../constants'; import ModelInterpreter from '../../Interpreter'; import MechanismNode from '../mechanism/MechanismNode'; import ProjectionLink from '../../links/ProjectionLink'; -// import { castObject } from '../../utils'; - +import { MetaNode, MetaPort, Position, PortTypes } from '@metacell/meta-diagram'; export default class CompositionNode extends MechanismNode { children: {[key: string]: any}; childrenMap: Map; innerClass: String; + parent: MechanismNode|CompositionNode|undefined; - constructor(name: string, icon?: string, isExpaded?: boolean, ports?: { [key: string]: Array }, extra?: Object, children?: {[key: string]: any}) { - super(name, icon, isExpaded, ports, extra); + constructor( + name: string, + parent: MechanismNode|CompositionNode|undefined, + icon?: string, + isExpaded?: boolean, + ports?: { [key: string]: Array }, + extra?: Object, + children?: {[key: string]: any}) + { + super(name, parent, icon, isExpaded, ports, extra); this.childrenMap = new Map(); this.children = children !== undefined ? children : { @@ -34,20 +39,20 @@ export default class CompositionNode extends MechanismNode { return; } - const castChild = ModelInterpreter.castObject(child); - this.childrenMap.set(child.id, castChild); + // const castChild = ModelInterpreter.castObject(child, this.parent); + // this.childrenMap.set(child.id, castChild); - switch(castChild.getType()) { - case PNLClasses.COMPOSITION: - this.children[PNLClasses.COMPOSITION].push(castChild); - break; - case PNLClasses.MECHANISM: - this.children[PNLClasses.MECHANISM].push(castChild); - break; - case PNLClasses.PROJECTION: - this.children[PNLClasses.PROJECTION].push(castChild); - break; - } + // switch(castChild.getType()) { + // case PNLClasses.COMPOSITION: + // this.children[PNLClasses.COMPOSITION].push(castChild); + // break; + // case PNLClasses.MECHANISM: + // this.children[PNLClasses.MECHANISM].push(castChild); + // break; + // case PNLClasses.PROJECTION: + // this.children[PNLClasses.PROJECTION].push(castChild); + // break; + // } } getChildren() : {[key: string]: any} { @@ -58,7 +63,33 @@ export default class CompositionNode extends MechanismNode { return this.innerClass; } - getMetaDiagram() : any { - console.log("Composition Node implementation of getMetaDiagram."); + getMetaNode() : any { + // TODO: get position from the graphviz data + let x = 200 + Math.random() * 600; + let y = 200 + Math.random() * 600; + let parent = this.parent ? this.parent.getMetaNode() : undefined; + let ports: Array = [] + // TODO: the MetaPort has the enum prefix cause the projections are created with that prefix + // from the graphviz data we get. + // this.ports[PortTypes.INPUT_PORT].forEach((port: any) => ports.push(new MetaPort(PortTypes.INPUT_PORT + '-' + port, PortTypes.INPUT_PORT + '-' + port, PortTypes.INPUT_PORT, new Position(0, 0), new Map()))); + // this.ports[PortTypes.OUTPUT_PORT].forEach((port: any) => ports.push(new MetaPort(PortTypes.OUTPUT_PORT + '-' + port, PortTypes.OUTPUT_PORT + '-' + port, PortTypes.OUTPUT_PORT, new Position(0, 0), new Map()))); + // this.ports[PortTypes.PARAMETER_PORT].forEach((port: any) => ports.push(new MetaPort(PortTypes.PARAMETER_PORT + '-' + port, PortTypes.PARAMETER_PORT + '-' + port, PortTypes.PARAMETER_PORT, new Position(0, 0), new Map()))); + return new MetaNode( + this.name, + this.name, + PNLClasses.COMPOSITION, + new Position(x, y), + 'node-gray', + parent, + ports, + new Map(Object.entries({ + name: 'Mechanism Name', + variant: 'node-gray', + pnlClass: 'ProcessingMechanism', + shape: 'circle', + selected: false + }) + ) + ); } } \ No newline at end of file diff --git a/src/model/nodes/mechanism/MechanismNode.ts b/src/model/nodes/mechanism/MechanismNode.ts index 73a75dca..121159ea 100644 --- a/src/model/nodes/mechanism/MechanismNode.ts +++ b/src/model/nodes/mechanism/MechanismNode.ts @@ -1,5 +1,6 @@ import { PNLClasses } from '../../../constants'; import IMetaDiagramConverter from '../IMetaDiagramConverter'; +import CompositionNode from '../composition/CompositionNode'; import { MetaNode, MetaPort, Position, PortTypes } from '@metacell/meta-diagram'; export default class MechanismNode implements IMetaDiagramConverter { @@ -9,14 +10,23 @@ export default class MechanismNode implements IMetaDiagramConverter { ports: { [key: string]: Array } ; extra: Object; innerClass: String; + parent: MechanismNode|CompositionNode|undefined; - constructor(name: string, icon?: string, isExpaded?: boolean, ports?: { [key: string]: Array }, extra?: Object) { + constructor( + name: string, + parent: MechanismNode|CompositionNode|undefined, + icon?: string, + isExpaded?: boolean, + ports?: { [key: string]: Array }, + extra?: Object) + { this.name = name; this.icon = icon !== undefined ? icon : ""; this.ports = ports !== undefined ? ports : {}; this.extra = extra !== undefined ? extra : []; this.isExpanded = isExpaded !== undefined ? isExpaded : false; this.innerClass = PNLClasses.MECHANISM; + this.parent = parent; } getName() : string { @@ -56,18 +66,20 @@ export default class MechanismNode implements IMetaDiagramConverter { // TODO: get position from the graphviz data let x = 200 + Math.random() * 600; let y = 200 + Math.random() * 600; + let parent = this.parent ? this.parent.getMetaNode() : undefined; let ports: Array = [] // TODO: the MetaPort has the enum prefix cause the projections are created with that prefix // from the graphviz data we get. this.ports[PortTypes.INPUT_PORT].forEach((port: any) => ports.push(new MetaPort(PortTypes.INPUT_PORT + '-' + port, PortTypes.INPUT_PORT + '-' + port, PortTypes.INPUT_PORT, new Position(0, 0), new Map()))); this.ports[PortTypes.OUTPUT_PORT].forEach((port: any) => ports.push(new MetaPort(PortTypes.OUTPUT_PORT + '-' + port, PortTypes.OUTPUT_PORT + '-' + port, PortTypes.OUTPUT_PORT, new Position(0, 0), new Map()))); - // this.ports[PortTypes.PARAMETER_PORT].forEach((port: any) => ports.push(new MetaPort(PortTypes.PARAMETER_PORT + '-' + port, PortTypes.PARAMETER_PORT + '-' + port, PortTypes.PARAMETER_PORT, new Position(0, 0), new Map()))); + this.ports[PortTypes.PARAMETER_PORT].forEach((port: any) => ports.push(new MetaPort(PortTypes.PARAMETER_PORT + '-' + port, PortTypes.PARAMETER_PORT + '-' + port, PortTypes.PARAMETER_PORT, new Position(0, 0), new Map()))); return new MetaNode( this.name, this.name, - 'mechanism', + PNLClasses.MECHANISM, new Position(x, y), 'node-gray', + parent, ports, new Map(Object.entries({ name: 'Mechanism Name', diff --git a/src/model/utils.js b/src/model/utils.js index 3a6d2d12..26b25e07 100644 --- a/src/model/utils.js +++ b/src/model/utils.js @@ -48,13 +48,11 @@ export function buildModel(frontendModel, coord, prevModel) { frontendModel[PNLClasses.COMPOSITION]?.forEach( node => { if (Array.isArray(node)) { node.forEach( comp => { - // TODO: create the composition and add it to the model - coordinates.y += 250; + finalModel[PNLClasses.COMPOSITION]?.push(comp.getMetaNode()); buildModel(comp.children, coordinates, finalModel); }); } else { - // TODO: create the composition and add it to the model - coordinates.y += 250; + finalModel[PNLClasses.COMPOSITION]?.push(node.getMetaNode()); buildModel(node.children, coordinates, finalModel); } });