mirror of
https://github.com/hexastack/hexabot
synced 2024-11-24 04:53:41 +00:00
Merge pull request #96 from Hexastack/47-request-visual-editor-allow-to-link-a-block-to-itself
Feat: Self Attached Node
This commit is contained in:
commit
811d5219f3
@ -8,10 +8,7 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
import { debounce } from "@mui/material";
|
import { debounce } from "@mui/material";
|
||||||
import createEngine, {
|
import createEngine, { DiagramModel } from "@projectstorm/react-diagrams";
|
||||||
DefaultLinkModel,
|
|
||||||
DiagramModel,
|
|
||||||
} from "@projectstorm/react-diagrams";
|
|
||||||
import * as React from "react";
|
import * as React from "react";
|
||||||
import { createContext, useContext } from "react";
|
import { createContext, useContext } from "react";
|
||||||
|
|
||||||
@ -26,6 +23,8 @@ import {
|
|||||||
} from "@/types/visual-editor.types";
|
} from "@/types/visual-editor.types";
|
||||||
|
|
||||||
import { ZOOM_LEVEL } from "../constants";
|
import { ZOOM_LEVEL } from "../constants";
|
||||||
|
import { AdvancedLinkFactory } from "../v2/AdvancedLink/AdvancedLinkFactory";
|
||||||
|
import { AdvancedLinkModel } from "../v2/AdvancedLink/AdvancedLinkModel";
|
||||||
import { CustomCanvasWidget } from "../v2/CustomCanvasWidget";
|
import { CustomCanvasWidget } from "../v2/CustomCanvasWidget";
|
||||||
import { CustomDeleteItemsAction } from "../v2/CustomDiagramNodes/CustomDeleteAction";
|
import { CustomDeleteItemsAction } from "../v2/CustomDiagramNodes/CustomDeleteAction";
|
||||||
import { NodeFactory } from "../v2/CustomDiagramNodes/NodeFactory";
|
import { NodeFactory } from "../v2/CustomDiagramNodes/NodeFactory";
|
||||||
@ -87,6 +86,8 @@ const buildDiagram = ({
|
|||||||
model = new DiagramModel();
|
model = new DiagramModel();
|
||||||
|
|
||||||
engine.getNodeFactories().registerFactory(new NodeFactory());
|
engine.getNodeFactories().registerFactory(new NodeFactory());
|
||||||
|
engine.getLinkFactories().registerFactory(new AdvancedLinkFactory());
|
||||||
|
|
||||||
engine
|
engine
|
||||||
.getActionEventBus()
|
.getActionEventBus()
|
||||||
.registerAction(new CustomDeleteItemsAction({ callback: onRemoveNode }));
|
.registerAction(new CustomDeleteItemsAction({ callback: onRemoveNode }));
|
||||||
@ -139,12 +140,12 @@ const buildDiagram = ({
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
const links: DefaultLinkModel[] = [];
|
const links: AdvancedLinkModel[] = [];
|
||||||
|
|
||||||
data.forEach((datum, index) => {
|
data.forEach((datum, index) => {
|
||||||
if ("nextBlocks" in datum && Array.isArray(datum.nextBlocks)) {
|
if ("nextBlocks" in datum && Array.isArray(datum.nextBlocks)) {
|
||||||
datum.nextBlocks?.forEach((nextBlock) => {
|
datum.nextBlocks?.forEach((nextBlock) => {
|
||||||
const link = new DefaultLinkModel();
|
const link = new AdvancedLinkModel();
|
||||||
const sourceNode = nodes[index];
|
const sourceNode = nodes[index];
|
||||||
const targetNode = nodes.find(
|
const targetNode = nodes.find(
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
@ -164,7 +165,7 @@ const buildDiagram = ({
|
|||||||
|
|
||||||
//recursive link
|
//recursive link
|
||||||
if ("attachedBlock" in datum && datum.attachedBlock) {
|
if ("attachedBlock" in datum && datum.attachedBlock) {
|
||||||
const link = new DefaultLinkModel({
|
const link = new AdvancedLinkModel({
|
||||||
color: "#019185",
|
color: "#019185",
|
||||||
selectedColor: "#019185",
|
selectedColor: "#019185",
|
||||||
type: "default",
|
type: "default",
|
||||||
|
@ -0,0 +1,95 @@
|
|||||||
|
import { css, keyframes } from "@emotion/react";
|
||||||
|
import styled from "@emotion/styled";
|
||||||
|
import {
|
||||||
|
DefaultLinkFactory,
|
||||||
|
DefaultLinkWidget,
|
||||||
|
} from "@projectstorm/react-diagrams";
|
||||||
|
import React from "react";
|
||||||
|
|
||||||
|
import { AdvancedLinkModel } from "./AdvancedLinkModel";
|
||||||
|
|
||||||
|
interface Point {
|
||||||
|
x: number;
|
||||||
|
y: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
const createCurvedPath = (start: Point, end: Point) => {
|
||||||
|
const controlPoint1X = start.x + 220;
|
||||||
|
const controlPoint1Y = start.y - 250;
|
||||||
|
const controlPoint2X = end.x - 250;
|
||||||
|
const controlPoint2Y = end.y - 250;
|
||||||
|
|
||||||
|
return `M ${start.x},${start.y} C ${controlPoint1X},${controlPoint1Y} ${controlPoint2X},${controlPoint2Y} ${end.x},${end.y}`;
|
||||||
|
};
|
||||||
|
|
||||||
|
namespace S {
|
||||||
|
export const Keyframes = keyframes`
|
||||||
|
from {
|
||||||
|
stroke-dashoffset: 24;
|
||||||
|
}
|
||||||
|
to {
|
||||||
|
stroke-dashoffset: 0;
|
||||||
|
}
|
||||||
|
`;
|
||||||
|
|
||||||
|
const selected = css`
|
||||||
|
stroke-dasharray: 10, 2;
|
||||||
|
animation: ${Keyframes} 1s linear infinite;
|
||||||
|
`;
|
||||||
|
|
||||||
|
export const Path = styled.path<{ selected: boolean }>`
|
||||||
|
${(p) => p.selected && selected};
|
||||||
|
fill: none;
|
||||||
|
pointer-events: auto;
|
||||||
|
`;
|
||||||
|
}
|
||||||
|
|
||||||
|
export class AdvancedLinkFactory extends DefaultLinkFactory {
|
||||||
|
constructor() {
|
||||||
|
super("advanced");
|
||||||
|
}
|
||||||
|
|
||||||
|
generateModel(): AdvancedLinkModel {
|
||||||
|
return new AdvancedLinkModel();
|
||||||
|
}
|
||||||
|
|
||||||
|
generateReactWidget(event): JSX.Element {
|
||||||
|
return <DefaultLinkWidget link={event.model} diagramEngine={this.engine} />;
|
||||||
|
}
|
||||||
|
|
||||||
|
generateLinkSegment(
|
||||||
|
model: AdvancedLinkModel,
|
||||||
|
selected: boolean,
|
||||||
|
path: string,
|
||||||
|
) {
|
||||||
|
const isSelfLoop =
|
||||||
|
model.getSourcePort().getNode() === model.getTargetPort().getNode();
|
||||||
|
|
||||||
|
if (isSelfLoop) {
|
||||||
|
// Adjust the path to create a curve
|
||||||
|
const sourcePortPosition = model.getSourcePort().getPosition();
|
||||||
|
const targetPortPosition = model.getTargetPort().getPosition();
|
||||||
|
const startPoint: Point = {
|
||||||
|
x: sourcePortPosition.x + 20,
|
||||||
|
y: sourcePortPosition.y + 20,
|
||||||
|
};
|
||||||
|
const endPoint: Point = {
|
||||||
|
x: targetPortPosition.x + 20,
|
||||||
|
y: targetPortPosition.y + 20,
|
||||||
|
};
|
||||||
|
|
||||||
|
path = createCurvedPath(startPoint, endPoint);
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<S.Path
|
||||||
|
selected={selected}
|
||||||
|
stroke={
|
||||||
|
selected ? model.getOptions().selectedColor : model.getOptions().color
|
||||||
|
}
|
||||||
|
strokeWidth={model.getOptions().width}
|
||||||
|
d={path}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,13 @@
|
|||||||
|
import {
|
||||||
|
DefaultLinkModel,
|
||||||
|
DefaultLinkModelOptions,
|
||||||
|
} from "@projectstorm/react-diagrams";
|
||||||
|
|
||||||
|
export class AdvancedLinkModel extends DefaultLinkModel {
|
||||||
|
constructor(options?: DefaultLinkModelOptions) {
|
||||||
|
super({
|
||||||
|
...options,
|
||||||
|
type: "advanced",
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
@ -25,7 +25,6 @@ import {
|
|||||||
tabsClasses,
|
tabsClasses,
|
||||||
} from "@mui/material";
|
} from "@mui/material";
|
||||||
import {
|
import {
|
||||||
DefaultLinkModel,
|
|
||||||
DefaultPortModel,
|
DefaultPortModel,
|
||||||
DiagramEngine,
|
DiagramEngine,
|
||||||
DiagramModel,
|
DiagramModel,
|
||||||
@ -54,6 +53,7 @@ import { IBlock } from "@/types/block.types";
|
|||||||
import { ICategory } from "@/types/category.types";
|
import { ICategory } from "@/types/category.types";
|
||||||
import { BlockPorts } from "@/types/visual-editor.types";
|
import { BlockPorts } from "@/types/visual-editor.types";
|
||||||
|
|
||||||
|
import { AdvancedLinkModel } from "./AdvancedLink/AdvancedLinkModel";
|
||||||
import BlockDialog from "../BlockDialog";
|
import BlockDialog from "../BlockDialog";
|
||||||
import { ZOOM_LEVEL } from "../constants";
|
import { ZOOM_LEVEL } from "../constants";
|
||||||
import { useVisualEditor } from "../hooks/useVisualEditor";
|
import { useVisualEditor } from "../hooks/useVisualEditor";
|
||||||
@ -195,7 +195,7 @@ const Diagrams = () => {
|
|||||||
entity,
|
entity,
|
||||||
port,
|
port,
|
||||||
}: {
|
}: {
|
||||||
entity: DefaultLinkModel;
|
entity: AdvancedLinkModel;
|
||||||
port: DefaultPortModel;
|
port: DefaultPortModel;
|
||||||
}) => {
|
}) => {
|
||||||
const link = model.getLink(entity.getOptions().id as string);
|
const link = model.getLink(entity.getOptions().id as string);
|
||||||
@ -205,7 +205,10 @@ const Diagrams = () => {
|
|||||||
[BlockPorts.nextBlocksOutPort, BlockPorts.attachmentOutPort].includes(
|
[BlockPorts.nextBlocksOutPort, BlockPorts.attachmentOutPort].includes(
|
||||||
// @ts-expect-error protected attr
|
// @ts-expect-error protected attr
|
||||||
entity.targetPort.getOptions().label,
|
entity.targetPort.getOptions().label,
|
||||||
)
|
) ||
|
||||||
|
(link.getSourcePort().getType() === "attached" &&
|
||||||
|
link.getSourcePort().getParent().getOptions().id ===
|
||||||
|
link.getTargetPort().getParent().getOptions().id)
|
||||||
) {
|
) {
|
||||||
model.removeLink(link);
|
model.removeLink(link);
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user