diff --git a/src/client/components/common/Breadcrumbs.js b/src/client/components/common/Breadcrumbs.js
index 249f96f8..a286dc77 100644
--- a/src/client/components/common/Breadcrumbs.js
+++ b/src/client/components/common/Breadcrumbs.js
@@ -1,225 +1,249 @@
import {
- Box,
- ClickAwayListener,
- IconButton,
- Link,
- MenuItem,
- Popper,
- Typography,
+ Box,
+ ClickAwayListener,
+ IconButton,
+ Link,
+ MenuItem,
+ Popper,
+ Typography,
} from '@mui/material';
-import React, { useMemo } from 'react';
+import React, {useMemo} from 'react';
import Breadcrumbs from '@mui/material/Breadcrumbs';
-import { makeStyles } from '@mui/styles';
+import {makeStyles} from '@mui/styles';
import ArrowRightIcon from '@mui/icons-material/KeyboardArrowRight';
-import ArrowDownIcon from '@mui/icons-material/KeyboardArrowDown';
import MoreHorizIcon from '@mui/icons-material/MoreHoriz';
import vars from '../../assets/styles/variables';
+import {useSelector, useDispatch} from 'react-redux';
+import {closeComposition, openComposition} from "../../redux/actions/general";
+import ModelSingleton from "../../model/ModelSingleton";
-const { dropdownSecBg, breadcrumbLinkColor, breadcrumbTextColor } = vars;
+
+const {dropdownSecBg, breadcrumbLinkColor, breadcrumbTextColor} = vars;
const useStyles = makeStyles(() => ({
- crumb: {
- fontWeight: 500,
- fontSize: '0.875rem',
- lineHeight: '1.25rem',
- color: `${breadcrumbLinkColor} !important`,
- },
- expand: {
- padding: '0.125rem',
- borderRadius: '0.25rem',
- },
- lastCrumb: {
- display: 'flex',
- alignItems: 'center',
- color: breadcrumbTextColor,
-
- '& .MuiTypography-root': {
- fontWeight: 500,
- fontSize: '0.875rem',
- lineHeight: '1.25rem',
- color: 'inherit',
+ crumb: {
+ fontWeight: 500,
+ fontSize: '0.875rem',
+ lineHeight: '1.25rem',
+ color: `${breadcrumbLinkColor} !important`,
},
- },
- dropDown: {
- width: '8rem',
- maxWidth: 'content',
- background: dropdownSecBg,
- boxShadow:
- '0 0.25rem 2.5rem rgba(0, 0, 0, 0.1), 0 1rem 7.5rem rgba(60, 60, 67, 0.2)',
- padding: '0.375rem 0.25rem',
- borderRadius: '0.5rem',
- inset: '1rem auto auto 0 !important',
-
- '&:before': {
- content: '""',
- width: 0,
- height: 0,
- borderStyle: 'solid',
- borderWidth: '0 0.75rem 0.75rem 0.75rem',
- borderColor: `transparent transparent ${dropdownSecBg} transparent`,
- position: 'absolute',
- top: '-0.5rem',
- left: '3rem',
+ expand: {
+ padding: '0.125rem',
+ borderRadius: '0.25rem',
},
-
- '& .MuiMenuItem-root': {
- fontWeight: 500,
- fontSize: '0.875rem',
- lineHeight: '1.25rem',
- whiteSpace: 'normal',
- borderRadius: '0.375rem',
- color: breadcrumbLinkColor,
- padding: '0.25rem 0.5rem',
-
- '&:hover': {
+ lastCrumb: {
+ display: 'flex',
+ alignItems: 'center',
color: breadcrumbTextColor,
- },
+
+ '& .MuiTypography-root': {
+ fontWeight: 500,
+ fontSize: '0.875rem',
+ lineHeight: '1.25rem',
+ color: 'inherit',
+ },
+ },
+ dropDown: {
+ width: '8rem',
+ maxWidth: 'content',
+ background: dropdownSecBg,
+ boxShadow:
+ '0 0.25rem 2.5rem rgba(0, 0, 0, 0.1), 0 1rem 7.5rem rgba(60, 60, 67, 0.2)',
+ padding: '0.375rem 0.25rem',
+ borderRadius: '0.5rem',
+ inset: '1rem auto auto 0 !important',
+
+ '&:before': {
+ content: '""',
+ width: 0,
+ height: 0,
+ borderStyle: 'solid',
+ borderWidth: '0 0.75rem 0.75rem 0.75rem',
+ borderColor: `transparent transparent ${dropdownSecBg} transparent`,
+ position: 'absolute',
+ top: '-0.5rem',
+ left: '3rem',
+ },
+
+ '& .MuiMenuItem-root': {
+ fontWeight: 500,
+ fontSize: '0.875rem',
+ lineHeight: '1.25rem',
+ whiteSpace: 'normal',
+ borderRadius: '0.375rem',
+ color: breadcrumbLinkColor,
+ padding: '0.25rem 0.5rem',
+
+ '&:hover': {
+ color: breadcrumbTextColor,
+ },
+ },
},
- },
}));
const EXPAND_ID = 'expand';
+const HOME_ID = 'home'
+
+function Crumb({id, text, handleClick, href, last = false}) {
+ const classes = useStyles();
-function Crumb({ id, text, handleClick, href, last = false }) {
- const classes = useStyles();
+ if (last) {
+ return (
+
+ {text}
+
+ );
+ }
- if (last) {
return (
-
- {text}
-
-
+ {
+ event.preventDefault();
+ handleClick();
+ }}
+ className={classes.crumb}
+ >
+ {text}
+
);
- }
-
- return (
- {
- event.preventDefault();
- handleClick();
- }}
- className={classes.crumb}
- >
- {text}
-
- );
}
-export const CustomBreadcrumbsWithMenu = ({ breadcrumbs, max = 4}) => {
- const classes = useStyles();
- const [anchorEl, setAnchorEl] = React.useState(null);
- const [collapsedCrumbs, setCollapsedCrumbs] = React.useState([]);
- const open = Boolean(anchorEl);
- const collapsed = !!breadcrumbs && breadcrumbs.length > max;
-
- const handleClick = (event) => {
- if (event && anchorEl === null) {
- setAnchorEl(event.currentTarget);
- } else if (anchorEl !== null) {
- setAnchorEl(null);
- }
- };
+export const CustomBreadcrumbsWithMenu = ({max = 4}) => {
+ const classes = useStyles();
+ const [anchorEl, setAnchorEl] = React.useState(null);
+ const compositionOpened = useSelector(state => state.general.compositionOpened);
+ const dispatch = useDispatch();
+ const open = Boolean(anchorEl);
+
+ const rawBreadcrumbs = compositionOpened?.getGraphPath() || [];
- const handleClose = () => {
- setAnchorEl(null);
- };
+ const breadcrumbs = [{id: HOME_ID, text: 'Home'}].concat(
+ rawBreadcrumbs.map((item, idx) => ({
+ id: item,
+ text: item,
+ }))
+ );
+
+ const collapsed = !!breadcrumbs && breadcrumbs.length > max;
+ const collapsedCrumbs = collapsed ? breadcrumbs.slice(1, breadcrumbs.length - (max - 1)) : [];
+
+ const _breadcrumbs = useMemo(
+ () => {
+ if (!!breadcrumbs && breadcrumbs.length > 0) {
+ if (breadcrumbs.length <= max) {
+ return breadcrumbs;
+ }
- const id = open ? 'simple-popper-9' : undefined;
+ const firstItemCrumbs = breadcrumbs.slice(0, 1);
+ const lastFourCrumbs = breadcrumbs.slice(-(max - 1));
- const _breadcrumbs = useMemo(
- function getBreadcrumbs() {
- if (!!breadcrumbs && breadcrumbs.length > 0) {
- if (breadcrumbs.length <= max) {
- return breadcrumbs;
+ return firstItemCrumbs.concat(
+ [{id: EXPAND_ID}],
+ lastFourCrumbs
+ );
+ }
+
+ return [];
+ },
+ [breadcrumbs, max]
+ );
+
+
+ const handleCrumbClick = (id) => {
+ if (compositionOpened) {
+ dispatch(closeComposition(compositionOpened));
+ }
+ if (id !== HOME_ID) {
+ const metaGraph = ModelSingleton.getInstance().getMetaGraph()
+ dispatch(openComposition(metaGraph.getNodeDFS(id)));
}
+ };
- const firstItemCrumbs = breadcrumbs.slice(0, 1);
- const restCrumbs = breadcrumbs.slice(1, breadcrumbs.length - (max - 1));
- const lastFourCrumbs = breadcrumbs.slice(-(max - 1));
- const newBreadCrumbs = firstItemCrumbs.concat(
- [{ id: EXPAND_ID }],
- lastFourCrumbs
- );
- setCollapsedCrumbs(restCrumbs);
+ const handleClick = (event) => {
+ if (event && anchorEl === null) {
+ setAnchorEl(event.currentTarget);
+ } else if (anchorEl !== null) {
+ setAnchorEl(null);
+ }
+ };
- return newBreadCrumbs;
- }
+ const handleClose = () => {
+ setAnchorEl(null);
+ };
- return undefined;
- },
- [breadcrumbs, max]
- );
-
- return (
-
- {collapsedCrumbs && collapsedCrumbs.length > 1 && (
-
-
-
- {collapsedCrumbs && collapsedCrumbs.length > 1
- ? collapsedCrumbs.map((crumb) => (
-
- ))
- : null}
-
-
-
- )}
-
- }
- >
- {_breadcrumbs &&
- _breadcrumbs.map((crumb, idx) => {
- const index = idx + 1;
-
- if (crumb.id === EXPAND_ID) {
- return (
-
-
-
-
-
- );
- }
+ const id = open ? 'simple-popper-9' : undefined;
- return (
-
- );
- })}
-
-
- );
+ return (
+
+ {collapsedCrumbs && collapsedCrumbs.length > 1 && (
+
+
+
+ {collapsedCrumbs && collapsedCrumbs.length > 1
+ ? collapsedCrumbs.map((crumb) => (
+
+ ))
+ : null}
+
+
+
+ )}
+
+ }
+ >
+ {_breadcrumbs &&
+ _breadcrumbs.map((crumb, idx) => {
+ const index = idx + 1;
+
+ if (crumb.id === EXPAND_ID) {
+ return (
+
+
+
+
+
+ );
+ }
+
+ return (
+ handleCrumbClick(crumb.id)}
+ />
+ );
+ })}
+
+
+ );
};
diff --git a/src/client/components/common/Header.js b/src/client/components/common/Header.js
index f233f6e8..6a5760aa 100644
--- a/src/client/components/common/Header.js
+++ b/src/client/components/common/Header.js
@@ -24,7 +24,8 @@ const useStyles = makeStyles(() => ({
display: 'flex',
alignItems: 'center',
border: `1px solid ${headerBorderColor}`,
- inset: '1rem auto auto 0 !important',
+ position: 'relative',
+ zIndex: 9999,
},
leftSection: {
@@ -91,18 +92,6 @@ const useStyles = makeStyles(() => ({
},
}));
-const breadcrumbs = [
- { id: 'home', text: 'Home' },
- { id: 'breadSubItem1', text: 'breadSubItem1' },
- { id: 'breadSubItem2', text: 'breadSubItem2' },
- { id: 'breadSubItem3', text: 'breadSubItem3' },
- { id: 'breadSubItem4', text: 'breadSubItem4' },
- { id: 'breadSubItem5', text: 'breadSubItem5' },
- { id: 'breadSubItem6', text: 'breadSubItem6' },
- { id: 'breadSubItem7', text: 'breadSubItem7' },
- { id: 'composition2', text: 'Composition 2' },
-];
-
const listItems = [
{ label: 'Build', value: 'build', soon: false, action: GUIViews.EDIT},
{ label: 'Visualise', value: 'visualise', soon: false, action: GUIViews.VIEW},
@@ -127,7 +116,7 @@ const Header = () => {
-
+
diff --git a/src/client/components/views/editView/mechanisms/GenericMechanism.js b/src/client/components/views/editView/mechanisms/GenericMechanism.js
index 7d18838e..085140ac 100644
--- a/src/client/components/views/editView/mechanisms/GenericMechanism.js
+++ b/src/client/components/views/editView/mechanisms/GenericMechanism.js
@@ -89,6 +89,7 @@ class GenericMechanism extends React.Component {
}
changeVisibility() {
+ this.props.model.isExpanded = !this.state.expanded;
this.setState({expanded: !this.state.expanded});
}
diff --git a/src/client/components/views/editView/mechanisms/InputOutputNode.js b/src/client/components/views/editView/mechanisms/InputOutputNode.js
index 4dea3395..b63b688c 100644
--- a/src/client/components/views/editView/mechanisms/InputOutputNode.js
+++ b/src/client/components/views/editView/mechanisms/InputOutputNode.js
@@ -1,18 +1,21 @@
import * as React from "react";
import { Box, Typography } from "@mui/material";
+import {PortWidget} from "@projectstorm/react-diagrams";
class InputOutputNode extends React.Component {
- render() {
- const { text, direction } = this.props;
- const nodeClass = direction === 'right' ? 'block reverse' : 'block';
+ render() {
+ const { text, direction, engine, port } = this.props;
+ const nodeClass = direction === 'right' ? 'block reverse' : 'block';
- return (
-
-
- {text}
-
- );
- }
+ return (
+
+
+
+
+ {text}
+
+ );
+ }
}
export default InputOutputNode;
diff --git a/src/client/components/views/editView/mechanisms/MechMetadata.js b/src/client/components/views/editView/mechanisms/MechMetadata.js
index 75e2aaa9..de887165 100644
--- a/src/client/components/views/editView/mechanisms/MechMetadata.js
+++ b/src/client/components/views/editView/mechanisms/MechMetadata.js
@@ -1,108 +1,117 @@
import * as React from "react";
import Box from "@mui/material/Box";
-import { withStyles } from "@mui/styles";
+import {withStyles} from "@mui/styles";
import NodeSelection from "./NodeSelection";
import InputOutputNode from "./InputOutputNode";
-// import TextField from '@mui/material/TextField';
import Typography from "@mui/material/Typography";
-import { PortTypes, PortWidget } from "@metacell/meta-diagram";
+import {PortTypes} from "@metacell/meta-diagram";
import vars from "../../../../assets/styles/variables";
const styles = {
- textColor: {
- color: vars.functionTextColor
- },
- codeColor: {
- color: vars.functionCodeColor
- }
+ textColor: {
+ color: vars.functionTextColor
+ },
+ codeColor: {
+ color: vars.functionCodeColor
+ },
};
class MechMetadata extends React.Component {
- render() {
- const { classes, model, model: { options }, engine, changeVisibility } = this.props;
- console.log(classes)
- const functionValues = (label, value) => (
-
- {label}
- {/* {console.log(e)} }
- variant="outlined"
- style={{ zIndex: 11 }}
- /> */}
- {value}
-
- )
- return (
-
- {options.selected && (
-
- )}
-
-
-
- {options.name}
-
-
+ constructor() {
+ super();
+ this.elementRef = React.createRef();
+ }
-
- { options.ports.map(port => {
- switch (port.getType()) {
- case PortTypes.INPUT_PORT:
- return (
-
-
-
- );
- default:
- return <>>
- }
- })}
-
+ componentDidMount() {
+ this.forceUpdate() // so that we get the ref to the element
+ }
-
+ componentDidUpdate(prevProps, prevState, snapshot) {
+ const parentElement = this.elementRef.current.parentElement;
+ parentElement.style.clipPath = '';
+ parentElement.style.zIndex = 10000;
+ }
-
- {
- functionValues('Context', '12')
- }
- {
- functionValues('Size', '8.90')
- }
- {
- functionValues('Prefs', '44')
- }
+ render() {
+ const {classes, model, model: {options}, engine, changeVisibility} = this.props;
+ const functionValues = (label, value) => (
- Function
-
-
- function
- =pnl.Logistic(gain=1.0, bias=-4)
+ {label}
+ {value}
-
+ )
+
+ return (
+
+ {options.selected && (
+
+ )}
+
+
+
+ {options.name}
+
+
-
+
+ {options.ports.map(port => {
+ switch (port.getType()) {
+ case PortTypes.INPUT_PORT:
+ return (
+
+ );
+ default:
+ return <>>
+ }
+ })}
+
-
- { options.ports.map(port => {
- switch (port.getType()) {
- case PortTypes.OUTPUT_PORT:
- return (
-
-
-
- );
- default:
- return <>>
- }
- })}
-
-
- );
- }
+
+
+
+ {
+ functionValues('Context', '12')
+ }
+ {
+ functionValues('Size', '8.90')
+ }
+ {
+ functionValues('Prefs', '44')
+ }
+
+ Function
+
+
+ function
+ =pnl.Logistic(gain=1.0,
+ bias=-4)
+
+
+
+
+
+
+ {options.ports.map(port => {
+ switch (port.getType()) {
+ case PortTypes.OUTPUT_PORT:
+ return (
+
+ )
+ ;
+ default:
+ return <>>
+ }
+ })}
+
+
+ );
+ }
}
export default withStyles(styles)(MechMetadata);
diff --git a/src/client/components/views/editView/mechanisms/MechSimple.js b/src/client/components/views/editView/mechanisms/MechSimple.js
index e5719193..5b658593 100644
--- a/src/client/components/views/editView/mechanisms/MechSimple.js
+++ b/src/client/components/views/editView/mechanisms/MechSimple.js
@@ -2,7 +2,6 @@ import * as React from "react";
import NodeSelection from "./NodeSelection";
import {Box, Typography} from "@mui/material";
import {PortWidget, PortTypes, CallbackTypes} from "@metacell/meta-diagram";
-import {clipPathBorderSize} from "../../../../../constants";
import {getClipPath} from "../../../../services/clippingService";
import ModelSingleton from "../../../../model/ModelSingleton";
@@ -59,7 +58,7 @@ class MechSimple extends React.Component {
getMechClipPath(parentNode) {
const {model} = this.props;
- return parentNode ? getClipPath(parentNode, model, clipPathBorderSize) : null
+ return parentNode ? getClipPath(parentNode, model) : null
}
getListenerID(node) {
diff --git a/src/client/components/views/editView/projections/CustomLinkWidget.js b/src/client/components/views/editView/projections/CustomLinkWidget.js
index ef607366..9ed59a62 100644
--- a/src/client/components/views/editView/projections/CustomLinkWidget.js
+++ b/src/client/components/views/editView/projections/CustomLinkWidget.js
@@ -3,11 +3,10 @@ import {DefaultLinkWidget} from '@projectstorm/react-diagrams';
import {projectionLink, projectionLinkArrow} from "../../../../assets/styles/variables";
import ModelSingleton from "../../../../model/ModelSingleton";
import {
- updateLinkPoints
+ getEdgePoint, updateLinkPoints
} from "../../../../services/clippingService";
import {CallbackTypes} from "@metacell/meta-diagram";
-const pointlength = 6;
/**
* CustomLinkArrowWidget is a functional React component that renders a custom arrow for the link.
@@ -19,12 +18,13 @@ const pointlength = 6;
*/
const CustomLinkArrowWidget = (props) => {
const {point, previousPoint} = props;
+ const POINTS_LENGTH = 6;
const angle =
90 +
(Math.atan2(
point.getY() - previousPoint.getY(),
- (point.getX() - 10) - (previousPoint.getX() + 10)
+ (point.getX()) - (previousPoint.getX())
) *
180) /
Math.PI;
@@ -32,9 +32,9 @@ const CustomLinkArrowWidget = (props) => {
return (
-
+
parentBoundingBox.getTopRight().x || // Child is to the right of parent
- childBoundingBox.getTopLeft().y > parentBoundingBox.getBottomLeft().y || // Child is above parent
- childBoundingBox.getBottomLeft().y < parentBoundingBox.getTopLeft().y; // Child is below parent
-
-}
-
-/**
- * Checks if the given outsideData indicates that the child node is outside its parent node in any direction.
- * @param {DirectionalData} outsideData - The outside data of a child node or link relative to its parent.
- * @returns {boolean} - Returns true if the child is outside its parent node in any direction, false otherwise.
- */
-
-export function isAnyDirectionOutside(outsideData: DirectionalData) {
- if (!outsideData) {
- return false
- }
- return outsideData.top > 0 || outsideData.bottom > 0 || outsideData.left > 0 || outsideData.right > 0;
-}
-
/**
* Calculates the outside data of a child node or link relative to its parent node.
* @param {MetaNodeModel} parent - The parent node.
@@ -48,34 +20,33 @@ export function getOutsideData(parent: MetaNodeModel, child: MetaNodeModel | Met
if (!parent || !child) {
return null
}
- const parentBoundingBox = parent.getBoundingBox();
- const childBoundingBox = child.getBoundingBox();
- if (!childBoundingBox) {
- return null
+ let childTopAdjustment = 0
+ let childSelectedBorderAdjustment = 0
+ // Adjustments are only considered when the child is selected
+ if (child.getOptions().selected) {
+ // Adjustment to make the show properties button visible
+ childTopAdjustment = clipPathTopAdjustment;
+ // Adjustment to make the selected border visible
+ childSelectedBorderAdjustment = clipPathSelectedBorder;
}
- return {
- left: Math.max(0, parentBoundingBox.getTopLeft().x - childBoundingBox.getTopLeft().x),
- right: Math.max(0, childBoundingBox.getTopRight().x - parentBoundingBox.getTopRight().x),
- top: Math.max(0, parentBoundingBox.getTopLeft().y - childBoundingBox.getTopLeft().y),
- bottom: Math.max(0, childBoundingBox.getBottomLeft().y - parentBoundingBox.getBottomLeft().y)
+ // Adjustment to make exclude the parent border from the bounding box
+ let parentBorderAdjustment = clipPathParentBorderSize
+ // if in detached mode then there's no border
+ if (parent.getOption(snapshotDimensionsLabel)){
+ parentBorderAdjustment = 0
}
-}
-/**
- * Calculates the border offset based on outside data and the provided border size.
- * @param {DirectionalData} outsideData - The outside data of a child node or link relative to its parent.
- * @param {number} borderSize - The size of the border.
- * @returns {{rightBorderOffset: number, leftBorderOffset: number, topBorderOffset: number, bottomBorderOffset: number}} - Returns an object containing the border offset values.
- */
+ const parentBoundingBox = parent.getBoundingBox();
+ const childBoundingBox = child.getBoundingBox();
-function getBorderOffset(outsideData: DirectionalData, borderSize: number) {
- const rightBorderOffset = outsideData.right > 0 ? borderSize : 0
- const leftBorderOffset = outsideData.left > 0 ? borderSize : 0
- const topBorderOffset = outsideData.top > 0 ? borderSize : 0
- const bottomBorderOffset = outsideData.bottom > 0 ? borderSize : 0
- return {rightBorderOffset, leftBorderOffset, topBorderOffset, bottomBorderOffset};
+ return {
+ left: Math.max(0, (parentBoundingBox.getTopLeft().x + parentBorderAdjustment) - childBoundingBox.getTopLeft().x),
+ right: Math.max(0, (childBoundingBox.getTopRight().x + childSelectedBorderAdjustment) - (parentBoundingBox.getTopRight().x - parentBorderAdjustment)),
+ top: Math.max(0, (parentBoundingBox.getTopLeft().y + parentBorderAdjustment) - (childBoundingBox.getTopLeft().y + childTopAdjustment)),
+ bottom: Math.max(0, (childBoundingBox.getBottomLeft().y + childSelectedBorderAdjustment) - (parentBoundingBox.getBottomLeft().y - parentBorderAdjustment))
+ };
}
/**
@@ -91,52 +62,8 @@ function getClipPathStr(left: number, top: number, right: number, bottom: number
return `polygon(${left}px ${top}px, ${right}px ${top}px,${right}px ${bottom}px, ${left}px ${bottom}px)`;
}
-/**
- * Calculates the bottom value for the clip path based on outside data, height, and bottom border offset.
- * @param {DirectionalData} outsideData - The outside data of a child node or link relative to its parent.
- * @param {number} height - The height of the child node or link.
- * @param {number} bottomBorderOffset - The bottom border offset.
- * @returns {number} - Returns the bottom value for the clip path.
- */
-function getBottom(outsideData: DirectionalData, height: number, bottomBorderOffset: number) {
- return (height - outsideData.bottom - bottomBorderOffset);
-}
-
-/**
- * Calculates the right value for the clip path based on outside data,
- * width, and right border offset.
- *
- * @param {DirectionalData} outsideData - The outside data of a child node or link relative to its parent.
- * @param {number} width - The width of the child node or link.
- * @param {number} rightBorderOffset - The right border offset.
- * @returns {number} - Returns the right value for the clip path.
- * */
-function getRight(outsideData: DirectionalData, width: number, rightBorderOffset: number) {
- return (width - outsideData.right - rightBorderOffset);
-}
-
-/**
-
- Calculates the left value for the clip path based on outside data and left border offset.
- @param {DirectionalData} outsideData - The outside data of a child node or link relative to its parent.
- @param {number} leftBorderOffset - The left border offset.
- @returns {number} - Returns the left value for the clip path.
- */
-function getLeft(outsideData: DirectionalData, leftBorderOffset: number) {
- return (outsideData.left + leftBorderOffset);
-}
-/**
-
- Calculates the top value for the clip path based on outside data and top border offset.
- @param {DirectionalData} outsideData - The outside data of a child node or link relative to its parent.
- @param {number} topBorderOffset - The top border offset.
-*/
-function getTop(outsideData: DirectionalData, topBorderOffset: number) {
- return (outsideData.top + topBorderOffset);
-}
-
-export function getClipPath(parent: MetaNodeModel | null, child: MetaNodeModel | null, borderSize: number = 0) {
+export function getClipPath(parent: MetaNodeModel | null, child: MetaNodeModel | null) {
if (!parent || !child) {
return null;
}
@@ -144,79 +71,89 @@ export function getClipPath(parent: MetaNodeModel | null, child: MetaNodeModel |
if (!outsideData) {
return null
}
- const {
- rightBorderOffset,
- leftBorderOffset,
- topBorderOffset,
- bottomBorderOffset
- } = getBorderOffset(outsideData, borderSize);
const childBB = child.getBoundingBox();
- const childWidth = childBB.getWidth()
- const childHeight = childBB.getHeight()
- const top = getTop(outsideData, topBorderOffset);
- const left = getLeft(outsideData, leftBorderOffset);
- const right = getRight(outsideData, childWidth, rightBorderOffset);
- const bottom = getBottom(outsideData, childHeight, bottomBorderOffset);
+
+ const {left} = outsideData
+ let top = outsideData.top
+ let right = childBB.getWidth() - outsideData.right
+ let bottom = childBB.getHeight() - outsideData.bottom
+
+ if (child.getOptions().selected) {
+ top += clipPathTopAdjustment
+ right += clipPathSelectedBorder
+ bottom += clipPathSelectedBorder
+ }
// Workaround for issue with the first render
if (left == 0 && top == 0 && right == 0 && bottom == 0) {
- // Convert the polygon vertex coordinates to a string representation that can be used as a CSS value
return null;
}
+ // Convert the polygon vertex coordinates to a string representation that can be used as a CSS value
return getClipPathStr(left, top, right, bottom)
}
/**
* Gets the nearest parent point model based on the original port, considering input/output buffers.
* @param {MetaNodeModel} parent - The parent node.
- * @param {PortModel} originalPort - The original port associated with the link.
- * @param {any} link - The link associated with the port.
- * @returns {PointModel} - Returns the nearest parent point model.
+ * @param {PortModel} position - The original port associated with the link.
+ * @returns {Point} - Returns the nearest parent point.
*/
-export function getNearestParentPointModel(parent: MetaNodeModel, originalPort: PortModel, link: any) {
- const bufferSignal = originalPort.getOptions().alignment == 'left' ? -1 : 1
- let yPos = originalPort.getY()
- let xPos = originalPort.getX()
+export function getNearestParentPointModel(parent: MetaNodeModel, position: Point) {
+ let yPos = position.y
+ let xPos = position.x
// port is on the left side of the node
- if (originalPort.getX() < parent.getX()) {
- xPos = parent.getX() + clipPathHorizontalBorderBuffer * bufferSignal
+ if (position.x < parent.getX()) {
+ xPos = parent.getX() + clipPathParentBorderSize
}
// port is on the right side of the node
- if (originalPort.getX() > parent.getX() + parent.width) {
- xPos = parent.getX() + parent.width
+ if (position.x > parent.getX() + parent.width) {
+ xPos = parent.getX() + parent.width - clipPathParentBorderSize
}
// port is on the top of the node
- if (originalPort.getY() < parent.getY()) {
- yPos = parent.getY() + clipPathBorderBuffer * bufferSignal
+ if (position.y < parent.getY()) {
+ yPos = parent.getY() + clipPathParentBorderSize
}
// port is on the bottom of the node
- if (originalPort.getY() > parent.getY() + parent.height) {
- yPos = parent.getY() + parent.height
+ if (position.y > parent.getY() + parent.height) {
+ yPos = parent.getY() + parent.height - clipPathParentBorderSize
}
- return new PointModel({
- link: link,
- position: new Point(xPos, yPos)
- })
+ return new Point(xPos, yPos)
}
+
/**
- * Updates the link points based on the provided port and index.
- * @param {PortModel} port - The port associated with the link.
- * @param {MetaLinkModel} link - The link to update.
- * @param {PointModel[]} points - The array of points used to define the link path.
- * @param {number} index - The index of the point in the points array to be updated.
- * @returns {DirectionalData | null} - Returns the outside data of the link relative to its parent and the updated points
+ * Updates the point position to the nearestParentPoint if the point is outside the parent.
+ * @param {MetaNodeModel} node - The node associated with the link.
+ * @param {PointModel} pointModel - The point to update.
+ * @returns {boolean} - Returns true if the point was updated
*/
-export function updateLinkPoints(port: PortModel, link: MetaLinkModel, points: PointModel[], index: number) {
- const node = port.getParent() as MetaNodeModel
+export function updateLinkPoints(node: MetaNodeModel, pointModel: PointModel) {
const parentNode = ModelSingleton.getInstance().getMetaGraph().getParent(node);
- if(parentNode){
- const outsideData = getOutsideData(parentNode, link);
- if (outsideData && isAnyDirectionOutside(outsideData) && parentNode) {
- points[index] = getNearestParentPointModel(parentNode, port, link);
- }
- return outsideData
+ if (parentNode && !parentNode.getBoundingBox().containsPoint(pointModel.getPosition())) {
+ pointModel.setPosition(getNearestParentPointModel(parentNode, pointModel.getPosition()));
+ return true
}
+ return false
}
+
+export function getEdgePoint(center: Point, target: Point, radius: number, link: MetaLinkModel) {
+ // Calculate the direction of the link
+ let dx = target.x - center.x;
+ let dy = target.y - center.y;
+
+ // Normalize the direction to have a length of 1
+ let length = Math.sqrt(dx * dx + dy * dy);
+ dx /= length;
+ dy /= length;
+
+ // Scale the direction by the radius of the node to get the edge point
+ let edgeX = center.x + dx * radius;
+ let edgeY = center.y + dy * radius;
+
+ return new PointModel({
+ link: link,
+ position: new Point(edgeX, edgeY)
+ });
+}
\ No newline at end of file
diff --git a/src/constants.ts b/src/constants.ts
index 4b1ec879..5290c3f6 100644
--- a/src/constants.ts
+++ b/src/constants.ts
@@ -33,6 +33,7 @@ export enum updateStates {
export const snapshotDimensionsLabel = 'snapshotDimensions'
// fixme: we should be getting this from styles
-export const clipPathBorderSize = (0.125 * 25) * 2 // container border size in (rem * pixels per rem) * 2 to work around the corner border radius
-export const clipPathBorderBuffer = 10
-export const clipPathHorizontalBorderBuffer = clipPathBorderBuffer + 2
\ No newline at end of file
+const fontsize = 16
+export const clipPathParentBorderSize = (0.125 * fontsize) * 2.5 // container border size in (rem * pixels per rem) * 2 to work around the corner border radius
+export const clipPathTopAdjustment = -2.625 * fontsize // top adjustment in (rem * pixels per rem)
+export const clipPathSelectedBorder = 0.09375 * fontsize // selected border size in (rem * pixels per rem)
\ No newline at end of file
diff --git a/src/index.css b/src/index.css
index d4d679c8..2b9c7b9c 100644
--- a/src/index.css
+++ b/src/index.css
@@ -15,11 +15,11 @@ code {
.simple-input-port {
position:absolute;
top: 50%;
- left: 0%;
+ left: 50%;
}
.simple-output-port {
position:absolute;
top: 50%;
- left: 100%;
+ left: 50%;
}
\ No newline at end of file