@flowuent-org/diagramming-core 1.1.9 → 1.2.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +116 -116
- package/packages/diagrams/src/index.ts +1 -0
- package/packages/diagrams/src/lib/atoms/ConnectionPoints.tsx +149 -0
- package/packages/diagrams/src/lib/components/automation/AutomationApiNode.tsx +151 -13
- package/packages/diagrams/src/lib/components/automation/AutomationEndNode.tsx +158 -7
- package/packages/diagrams/src/lib/components/automation/AutomationFormattingNode.tsx +151 -13
- package/packages/diagrams/src/lib/components/automation/AutomationNoteNode.tsx +152 -13
- package/packages/diagrams/src/lib/components/automation/AutomationSheetsNode.tsx +151 -13
- package/packages/diagrams/src/lib/components/automation/AutomationStartNode.tsx +166 -7
- package/packages/diagrams/src/lib/components/automation/NodeActionButtons.tsx +145 -0
- package/packages/diagrams/src/lib/components/automation/index.ts +1 -0
- package/packages/diagrams/src/lib/molecules/SideHandles.tsx +177 -12
- package/packages/diagrams/src/lib/organisms/CustomEdge/custom-edge-generator.tsx +10 -5
- package/packages/diagrams/src/lib/styles.css +53 -0
- package/packages/diagrams/src/lib/templates/DiagramContainer.tsx +59 -0
- package/packages/diagrams/src/lib/templates/Diagramming.tsx +25 -24
- package/packages/diagrams/src/lib/types/edge-types.ts +17 -0
- package/packages/diagrams/src/lib/utils/generateEdgesFromNodeOrder.ts +113 -0
package/package.json
CHANGED
|
@@ -1,116 +1,116 @@
|
|
|
1
|
-
{
|
|
2
|
-
"name": "@flowuent-org/diagramming-core",
|
|
3
|
-
"version": "1.1
|
|
4
|
-
"license": "MIT",
|
|
5
|
-
"publishConfig": {
|
|
6
|
-
"access": "public"
|
|
7
|
-
},
|
|
8
|
-
"scripts": {
|
|
9
|
-
"dev-diagramming": "nx serve diagramming",
|
|
10
|
-
"build-diagrams": "nx build diagrams"
|
|
11
|
-
},
|
|
12
|
-
"dependencies": {
|
|
13
|
-
"@dnd-kit/core": "^6.2.0",
|
|
14
|
-
"@dnd-kit/modifiers": "^8.0.0",
|
|
15
|
-
"@dnd-kit/sortable": "^9.0.0",
|
|
16
|
-
"@dnd-kit/utilities": "^3.2.2",
|
|
17
|
-
"@emotion/react": "^11.13.5",
|
|
18
|
-
"@emotion/styled": "^11.13.5",
|
|
19
|
-
"@fontsource/roboto": "^5.1.0",
|
|
20
|
-
"@hookform/resolvers": "^3.9.1",
|
|
21
|
-
"@mui/base": "5.0.0-beta.62",
|
|
22
|
-
"@mui/icons-material": "^6.1.8",
|
|
23
|
-
"@mui/material": "^6.1.8",
|
|
24
|
-
"@mui/types": "^7.2.19",
|
|
25
|
-
"@mui/x-data-grid": "^7.22.3",
|
|
26
|
-
"@tabler/icons-react": "^3.22.0",
|
|
27
|
-
"@welldone-software/why-did-you-render": "^8.0.3",
|
|
28
|
-
"@xyflow/react": "^12.3.5",
|
|
29
|
-
"axios": "^1.7.8",
|
|
30
|
-
"elkjs": "^0.9.3",
|
|
31
|
-
"file-saver": "^2.0.5",
|
|
32
|
-
"formik": "^2.4.6",
|
|
33
|
-
"framer-motion": "^11.11.17",
|
|
34
|
-
"gapi-script": "^1.2.0",
|
|
35
|
-
"html-to-image": "^1.11.11",
|
|
36
|
-
"i18next": "^24.0.2",
|
|
37
|
-
"immer": "^10.1.1",
|
|
38
|
-
"jotai": "^2.10.3",
|
|
39
|
-
"js-beautify": "^1.15.1",
|
|
40
|
-
"lodash": "^4.17.21",
|
|
41
|
-
"nanoid": "^5.0.9",
|
|
42
|
-
"react": "19.0.0",
|
|
43
|
-
"react-color": "^2.19.3",
|
|
44
|
-
"react-dnd": "^16.0.1",
|
|
45
|
-
"react-dom": "19.0.0",
|
|
46
|
-
"react-from-json": "^0.8.0",
|
|
47
|
-
"react-hook-form": "^7.53.2",
|
|
48
|
-
"react-i18next": "^15.1.2",
|
|
49
|
-
"react-icons": "^5.3.0",
|
|
50
|
-
"react-json-view": "^1.21.3",
|
|
51
|
-
"react-rnd": "^10.4.13",
|
|
52
|
-
"react-router-dom": "^7.0.1",
|
|
53
|
-
"react-syntax-highlighter": "^15.6.1",
|
|
54
|
-
"reactflow": "^11.11.4",
|
|
55
|
-
"tslib": "^2.8.1",
|
|
56
|
-
"use-undo": "^1.1.1",
|
|
57
|
-
"uuid": "^11.1.0",
|
|
58
|
-
"xlsx": "^0.18.5",
|
|
59
|
-
"yup": "^1.4.0",
|
|
60
|
-
"zustand": "^5.0.1"
|
|
61
|
-
},
|
|
62
|
-
"devDependencies": {
|
|
63
|
-
"@babel/core": "^7.26.0",
|
|
64
|
-
"@babel/preset-react": "^7.25.9",
|
|
65
|
-
"@nx/cypress": "20.6.4",
|
|
66
|
-
"@nx/eslint": "20.6.4",
|
|
67
|
-
"@nx/eslint-plugin": "20.6.4",
|
|
68
|
-
"@nx/js": "20.6.4",
|
|
69
|
-
"@nx/react": "20.6.4",
|
|
70
|
-
"@nx/rollup": "20.6.4",
|
|
71
|
-
"@nx/vite": "20.6.4",
|
|
72
|
-
"@nx/web": "20.6.4",
|
|
73
|
-
"@nx/webpack": "20.6.4",
|
|
74
|
-
"@nx/workspace": "20.6.4",
|
|
75
|
-
"@rollup/plugin-typescript": "^12.1.1",
|
|
76
|
-
"@rollup/plugin-url": "^8.0.2",
|
|
77
|
-
"@svgr/rollup": "^8.1.0",
|
|
78
|
-
"@svgr/webpack": "^8.1.0",
|
|
79
|
-
"@swc-node/register": "~1.10.9",
|
|
80
|
-
"@swc/cli": "~0.5.1",
|
|
81
|
-
"@swc/core": "~1.9.3",
|
|
82
|
-
"@swc/helpers": "~0.5.15",
|
|
83
|
-
"@testing-library/react": "16.1.0",
|
|
84
|
-
"@types/file-saver": "^2.0.7",
|
|
85
|
-
"@types/jest": "^29.5.14",
|
|
86
|
-
"@types/js-beautify": "^1.14.3",
|
|
87
|
-
"@types/lodash": "^4.17.13",
|
|
88
|
-
"@types/node": "22.10.0",
|
|
89
|
-
"@types/react": "18.3.12",
|
|
90
|
-
"@types/react-color": "^3.0.12",
|
|
91
|
-
"@types/react-dom": "18.3.1",
|
|
92
|
-
"@types/uuid": "^10.0.0",
|
|
93
|
-
"@typescript-eslint/eslint-plugin": "^8.16.0",
|
|
94
|
-
"@typescript-eslint/parser": "^8.16.0",
|
|
95
|
-
"@vitejs/plugin-react": "^4.3.4",
|
|
96
|
-
"@vitest/coverage-v8": "^2.1.6",
|
|
97
|
-
"@vitest/ui": "^2.1.6",
|
|
98
|
-
"eslint": "~9.15.0",
|
|
99
|
-
"eslint-config-prettier": "^9.1.0",
|
|
100
|
-
"eslint-plugin-cypress": "^4.1.0",
|
|
101
|
-
"eslint-plugin-import": "2.31.0",
|
|
102
|
-
"eslint-plugin-jsx-a11y": "6.10.2",
|
|
103
|
-
"eslint-plugin-react": "7.37.2",
|
|
104
|
-
"eslint-plugin-react-hooks": "5.0.0",
|
|
105
|
-
"eslint-plugin-react-refresh": "^0.4.14",
|
|
106
|
-
"jsdom": "~25.0.1",
|
|
107
|
-
"nx": "20.6.4",
|
|
108
|
-
"prettier": "^3.4.1",
|
|
109
|
-
"rollup-preserve-directives": "^1.1.3",
|
|
110
|
-
"swc-loader": "0.1.15",
|
|
111
|
-
"typescript": "~5.7.2",
|
|
112
|
-
"vite": "^6.3.6",
|
|
113
|
-
"vitest": "^2.1.6",
|
|
114
|
-
"webpack-cli": "^5.1.4"
|
|
115
|
-
}
|
|
116
|
-
}
|
|
1
|
+
{
|
|
2
|
+
"name": "@flowuent-org/diagramming-core",
|
|
3
|
+
"version": "1.2.1",
|
|
4
|
+
"license": "MIT",
|
|
5
|
+
"publishConfig": {
|
|
6
|
+
"access": "public"
|
|
7
|
+
},
|
|
8
|
+
"scripts": {
|
|
9
|
+
"dev-diagramming": "nx serve diagramming",
|
|
10
|
+
"build-diagrams": "nx build diagrams"
|
|
11
|
+
},
|
|
12
|
+
"dependencies": {
|
|
13
|
+
"@dnd-kit/core": "^6.2.0",
|
|
14
|
+
"@dnd-kit/modifiers": "^8.0.0",
|
|
15
|
+
"@dnd-kit/sortable": "^9.0.0",
|
|
16
|
+
"@dnd-kit/utilities": "^3.2.2",
|
|
17
|
+
"@emotion/react": "^11.13.5",
|
|
18
|
+
"@emotion/styled": "^11.13.5",
|
|
19
|
+
"@fontsource/roboto": "^5.1.0",
|
|
20
|
+
"@hookform/resolvers": "^3.9.1",
|
|
21
|
+
"@mui/base": "5.0.0-beta.62",
|
|
22
|
+
"@mui/icons-material": "^6.1.8",
|
|
23
|
+
"@mui/material": "^6.1.8",
|
|
24
|
+
"@mui/types": "^7.2.19",
|
|
25
|
+
"@mui/x-data-grid": "^7.22.3",
|
|
26
|
+
"@tabler/icons-react": "^3.22.0",
|
|
27
|
+
"@welldone-software/why-did-you-render": "^8.0.3",
|
|
28
|
+
"@xyflow/react": "^12.3.5",
|
|
29
|
+
"axios": "^1.7.8",
|
|
30
|
+
"elkjs": "^0.9.3",
|
|
31
|
+
"file-saver": "^2.0.5",
|
|
32
|
+
"formik": "^2.4.6",
|
|
33
|
+
"framer-motion": "^11.11.17",
|
|
34
|
+
"gapi-script": "^1.2.0",
|
|
35
|
+
"html-to-image": "^1.11.11",
|
|
36
|
+
"i18next": "^24.0.2",
|
|
37
|
+
"immer": "^10.1.1",
|
|
38
|
+
"jotai": "^2.10.3",
|
|
39
|
+
"js-beautify": "^1.15.1",
|
|
40
|
+
"lodash": "^4.17.21",
|
|
41
|
+
"nanoid": "^5.0.9",
|
|
42
|
+
"react": "19.0.0",
|
|
43
|
+
"react-color": "^2.19.3",
|
|
44
|
+
"react-dnd": "^16.0.1",
|
|
45
|
+
"react-dom": "19.0.0",
|
|
46
|
+
"react-from-json": "^0.8.0",
|
|
47
|
+
"react-hook-form": "^7.53.2",
|
|
48
|
+
"react-i18next": "^15.1.2",
|
|
49
|
+
"react-icons": "^5.3.0",
|
|
50
|
+
"react-json-view": "^1.21.3",
|
|
51
|
+
"react-rnd": "^10.4.13",
|
|
52
|
+
"react-router-dom": "^7.0.1",
|
|
53
|
+
"react-syntax-highlighter": "^15.6.1",
|
|
54
|
+
"reactflow": "^11.11.4",
|
|
55
|
+
"tslib": "^2.8.1",
|
|
56
|
+
"use-undo": "^1.1.1",
|
|
57
|
+
"uuid": "^11.1.0",
|
|
58
|
+
"xlsx": "^0.18.5",
|
|
59
|
+
"yup": "^1.4.0",
|
|
60
|
+
"zustand": "^5.0.1"
|
|
61
|
+
},
|
|
62
|
+
"devDependencies": {
|
|
63
|
+
"@babel/core": "^7.26.0",
|
|
64
|
+
"@babel/preset-react": "^7.25.9",
|
|
65
|
+
"@nx/cypress": "20.6.4",
|
|
66
|
+
"@nx/eslint": "20.6.4",
|
|
67
|
+
"@nx/eslint-plugin": "20.6.4",
|
|
68
|
+
"@nx/js": "20.6.4",
|
|
69
|
+
"@nx/react": "20.6.4",
|
|
70
|
+
"@nx/rollup": "20.6.4",
|
|
71
|
+
"@nx/vite": "20.6.4",
|
|
72
|
+
"@nx/web": "20.6.4",
|
|
73
|
+
"@nx/webpack": "20.6.4",
|
|
74
|
+
"@nx/workspace": "20.6.4",
|
|
75
|
+
"@rollup/plugin-typescript": "^12.1.1",
|
|
76
|
+
"@rollup/plugin-url": "^8.0.2",
|
|
77
|
+
"@svgr/rollup": "^8.1.0",
|
|
78
|
+
"@svgr/webpack": "^8.1.0",
|
|
79
|
+
"@swc-node/register": "~1.10.9",
|
|
80
|
+
"@swc/cli": "~0.5.1",
|
|
81
|
+
"@swc/core": "~1.9.3",
|
|
82
|
+
"@swc/helpers": "~0.5.15",
|
|
83
|
+
"@testing-library/react": "16.1.0",
|
|
84
|
+
"@types/file-saver": "^2.0.7",
|
|
85
|
+
"@types/jest": "^29.5.14",
|
|
86
|
+
"@types/js-beautify": "^1.14.3",
|
|
87
|
+
"@types/lodash": "^4.17.13",
|
|
88
|
+
"@types/node": "22.10.0",
|
|
89
|
+
"@types/react": "18.3.12",
|
|
90
|
+
"@types/react-color": "^3.0.12",
|
|
91
|
+
"@types/react-dom": "18.3.1",
|
|
92
|
+
"@types/uuid": "^10.0.0",
|
|
93
|
+
"@typescript-eslint/eslint-plugin": "^8.16.0",
|
|
94
|
+
"@typescript-eslint/parser": "^8.16.0",
|
|
95
|
+
"@vitejs/plugin-react": "^4.3.4",
|
|
96
|
+
"@vitest/coverage-v8": "^2.1.6",
|
|
97
|
+
"@vitest/ui": "^2.1.6",
|
|
98
|
+
"eslint": "~9.15.0",
|
|
99
|
+
"eslint-config-prettier": "^9.1.0",
|
|
100
|
+
"eslint-plugin-cypress": "^4.1.0",
|
|
101
|
+
"eslint-plugin-import": "2.31.0",
|
|
102
|
+
"eslint-plugin-jsx-a11y": "6.10.2",
|
|
103
|
+
"eslint-plugin-react": "7.37.2",
|
|
104
|
+
"eslint-plugin-react-hooks": "5.0.0",
|
|
105
|
+
"eslint-plugin-react-refresh": "^0.4.14",
|
|
106
|
+
"jsdom": "~25.0.1",
|
|
107
|
+
"nx": "20.6.4",
|
|
108
|
+
"prettier": "^3.4.1",
|
|
109
|
+
"rollup-preserve-directives": "^1.1.3",
|
|
110
|
+
"swc-loader": "0.1.15",
|
|
111
|
+
"typescript": "~5.7.2",
|
|
112
|
+
"vite": "^6.3.6",
|
|
113
|
+
"vitest": "^2.1.6",
|
|
114
|
+
"webpack-cli": "^5.1.4"
|
|
115
|
+
}
|
|
116
|
+
}
|
|
@@ -7,6 +7,7 @@ export * from './lib/atoms/AddParallelColButton';
|
|
|
7
7
|
export * from './lib/atoms/BendpointNode';
|
|
8
8
|
export * from './lib/atoms/CardBlockTypeSelector';
|
|
9
9
|
export * from './lib/atoms/CardMainContent';
|
|
10
|
+
export * from './lib/atoms/ConnectionPoints';
|
|
10
11
|
export * from './lib/atoms/FloatingConnectionLine';
|
|
11
12
|
export * from './lib/atoms/MarkerSelector';
|
|
12
13
|
export * from './lib/atoms/PropertyInput';
|
|
@@ -0,0 +1,149 @@
|
|
|
1
|
+
import React, { useMemo } from 'react';
|
|
2
|
+
import { Handle, Position, useNodeId } from '@xyflow/react';
|
|
3
|
+
import { Box } from '@mui/material';
|
|
4
|
+
import { useSelectedNode } from '../contexts/DiagramProvider';
|
|
5
|
+
|
|
6
|
+
export type ConnectionPointPosition = 'top' | 'bottom' | 'left' | 'right';
|
|
7
|
+
|
|
8
|
+
export interface ConnectionPointConfig {
|
|
9
|
+
position: ConnectionPointPosition;
|
|
10
|
+
type: 'source' | 'target' | 'both';
|
|
11
|
+
offset?: number;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
interface ConnectionPointsProps {
|
|
15
|
+
/** Show handles regardless of node selection */
|
|
16
|
+
alwaysVisible?: boolean;
|
|
17
|
+
/** Custom positions configuration */
|
|
18
|
+
positions?: ConnectionPointConfig[];
|
|
19
|
+
/** Custom handle size */
|
|
20
|
+
handleSize?: number;
|
|
21
|
+
/** Custom handle color */
|
|
22
|
+
handleColor?: string;
|
|
23
|
+
/** Custom background color */
|
|
24
|
+
backgroundColor?: string;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
const positionMap: Record<ConnectionPointPosition, Position> = {
|
|
28
|
+
top: Position.Top,
|
|
29
|
+
bottom: Position.Bottom,
|
|
30
|
+
left: Position.Left,
|
|
31
|
+
right: Position.Right,
|
|
32
|
+
};
|
|
33
|
+
|
|
34
|
+
const defaultPositions: ConnectionPointConfig[] = [
|
|
35
|
+
{ position: 'top', type: 'target' },
|
|
36
|
+
{ position: 'bottom', type: 'source' },
|
|
37
|
+
{ position: 'left', type: 'target' },
|
|
38
|
+
{ position: 'right', type: 'source' },
|
|
39
|
+
];
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* ConnectionPoints component displays interactive connection handles
|
|
43
|
+
* on a node when it's selected. Users can drag from these points to
|
|
44
|
+
* create edges connecting to other nodes.
|
|
45
|
+
*/
|
|
46
|
+
export const ConnectionPoints: React.FC<ConnectionPointsProps> = React.memo(({
|
|
47
|
+
alwaysVisible = false,
|
|
48
|
+
positions = defaultPositions,
|
|
49
|
+
handleSize = 14,
|
|
50
|
+
handleColor = '#10B981',
|
|
51
|
+
backgroundColor = '#1a1a2e',
|
|
52
|
+
}) => {
|
|
53
|
+
const nodeId = useNodeId();
|
|
54
|
+
const selectedNode = useSelectedNode();
|
|
55
|
+
|
|
56
|
+
const isSelected = selectedNode === nodeId;
|
|
57
|
+
const shouldShow = alwaysVisible || isSelected;
|
|
58
|
+
|
|
59
|
+
const handleStyle = useMemo(() => ({
|
|
60
|
+
width: `${handleSize}px`,
|
|
61
|
+
height: `${handleSize}px`,
|
|
62
|
+
borderRadius: '50%',
|
|
63
|
+
border: `3px solid ${handleColor}`,
|
|
64
|
+
backgroundColor: backgroundColor,
|
|
65
|
+
transition: 'all 0.2s ease-in-out',
|
|
66
|
+
cursor: 'crosshair',
|
|
67
|
+
zIndex: 10,
|
|
68
|
+
}), [handleSize, handleColor, backgroundColor]);
|
|
69
|
+
|
|
70
|
+
const getPositionStyle = (position: ConnectionPointPosition): React.CSSProperties => {
|
|
71
|
+
const offset = -Math.floor(handleSize / 2);
|
|
72
|
+
switch (position) {
|
|
73
|
+
case 'top': return { top: `${offset}px` };
|
|
74
|
+
case 'bottom': return { bottom: `${offset}px` };
|
|
75
|
+
case 'left': return { left: `${offset}px` };
|
|
76
|
+
case 'right': return { right: `${offset}px` };
|
|
77
|
+
}
|
|
78
|
+
};
|
|
79
|
+
|
|
80
|
+
const renderHandle = (config: ConnectionPointConfig, index: number) => {
|
|
81
|
+
const { position, type, offset } = config;
|
|
82
|
+
const baseId = `${nodeId}-${position}`;
|
|
83
|
+
|
|
84
|
+
if (type === 'both') {
|
|
85
|
+
// Render both source and target handles at the same position
|
|
86
|
+
return (
|
|
87
|
+
<React.Fragment key={`${position}-${index}`}>
|
|
88
|
+
<Handle
|
|
89
|
+
type="source"
|
|
90
|
+
id={`${baseId}-source`}
|
|
91
|
+
position={positionMap[position]}
|
|
92
|
+
className="connection-handle"
|
|
93
|
+
style={{
|
|
94
|
+
...handleStyle,
|
|
95
|
+
...getPositionStyle(position),
|
|
96
|
+
}}
|
|
97
|
+
/>
|
|
98
|
+
<Handle
|
|
99
|
+
type="target"
|
|
100
|
+
id={`${baseId}-target`}
|
|
101
|
+
position={positionMap[position]}
|
|
102
|
+
className="connection-handle"
|
|
103
|
+
style={{
|
|
104
|
+
...handleStyle,
|
|
105
|
+
...getPositionStyle(position),
|
|
106
|
+
opacity: 0, // Hidden but functional
|
|
107
|
+
pointerEvents: shouldShow ? 'auto' : 'none',
|
|
108
|
+
}}
|
|
109
|
+
/>
|
|
110
|
+
</React.Fragment>
|
|
111
|
+
);
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
return (
|
|
115
|
+
<Handle
|
|
116
|
+
key={`${position}-${type}-${index}`}
|
|
117
|
+
type={type}
|
|
118
|
+
id={baseId}
|
|
119
|
+
position={positionMap[position]}
|
|
120
|
+
className="connection-handle"
|
|
121
|
+
style={{
|
|
122
|
+
...handleStyle,
|
|
123
|
+
...getPositionStyle(position),
|
|
124
|
+
}}
|
|
125
|
+
/>
|
|
126
|
+
);
|
|
127
|
+
};
|
|
128
|
+
|
|
129
|
+
return (
|
|
130
|
+
<Box
|
|
131
|
+
className="connection-points-container"
|
|
132
|
+
sx={{
|
|
133
|
+
opacity: shouldShow ? 1 : 0,
|
|
134
|
+
transition: 'opacity 0.2s ease-in-out',
|
|
135
|
+
pointerEvents: shouldShow ? 'auto' : 'none',
|
|
136
|
+
'& .react-flow__handle': {
|
|
137
|
+
visibility: shouldShow ? 'visible' : 'hidden',
|
|
138
|
+
},
|
|
139
|
+
}}
|
|
140
|
+
>
|
|
141
|
+
{positions.map((config, index) => renderHandle(config, index))}
|
|
142
|
+
</Box>
|
|
143
|
+
);
|
|
144
|
+
});
|
|
145
|
+
|
|
146
|
+
ConnectionPoints.displayName = 'ConnectionPoints';
|
|
147
|
+
|
|
148
|
+
export default ConnectionPoints;
|
|
149
|
+
|
|
@@ -19,6 +19,7 @@ import { useTranslation } from 'react-i18next';
|
|
|
19
19
|
import { useDiagram } from '../../contexts/DiagramProvider';
|
|
20
20
|
import { AISuggestion } from './AISuggestionsModal';
|
|
21
21
|
import { AISuggestionsPanel } from './AISuggestionsPanel';
|
|
22
|
+
import { NodeActionButtons } from './NodeActionButtons';
|
|
22
23
|
|
|
23
24
|
interface AutomationApiNodeProps {
|
|
24
25
|
data: {
|
|
@@ -66,6 +67,9 @@ export const AutomationApiNode: React.FC<AutomationApiNodeProps> = ({ data, sele
|
|
|
66
67
|
const nodeId = useNodeId();
|
|
67
68
|
const setSelectedNode = useDiagram((state) => state.setSelectedNode);
|
|
68
69
|
const enableJson = useDiagram((state) => state.enableNodeJsonPopover ?? true);
|
|
70
|
+
const onNodesChange = useDiagram((state) => state.onNodesChange);
|
|
71
|
+
const nodes = useDiagram((state) => state.nodes);
|
|
72
|
+
const setNodes = useDiagram((state) => state.setNodes);
|
|
69
73
|
|
|
70
74
|
// Get the icon component based on the iconName
|
|
71
75
|
const IconComponent = getIconByName(data.iconName);
|
|
@@ -525,35 +529,169 @@ export const AutomationApiNode: React.FC<AutomationApiNodeProps> = ({ data, sele
|
|
|
525
529
|
</Box>
|
|
526
530
|
</Box>
|
|
527
531
|
|
|
528
|
-
{/* Handles -
|
|
532
|
+
{/* Connection Handles - Bidirectional (source + target at each position) */}
|
|
533
|
+
{/* Top - Source */}
|
|
534
|
+
<Handle
|
|
535
|
+
type="source"
|
|
536
|
+
position={Position.Top}
|
|
537
|
+
id="top-source"
|
|
538
|
+
className="connection-handle"
|
|
539
|
+
style={{
|
|
540
|
+
background: selected ? '#10B981' : '#1a1a2e',
|
|
541
|
+
width: '14px',
|
|
542
|
+
height: '14px',
|
|
543
|
+
border: '3px solid #10B981',
|
|
544
|
+
top: '-8px',
|
|
545
|
+
opacity: selected ? 1 : 0,
|
|
546
|
+
transition: 'all 0.2s ease-in-out',
|
|
547
|
+
cursor: 'crosshair',
|
|
548
|
+
zIndex: 10,
|
|
549
|
+
}}
|
|
550
|
+
/>
|
|
551
|
+
{/* Top - Target (hidden but functional) */}
|
|
552
|
+
<Handle
|
|
553
|
+
type="target"
|
|
554
|
+
position={Position.Top}
|
|
555
|
+
id="top-target"
|
|
556
|
+
style={{
|
|
557
|
+
background: 'transparent',
|
|
558
|
+
width: '14px',
|
|
559
|
+
height: '14px',
|
|
560
|
+
border: 'none',
|
|
561
|
+
top: '-8px',
|
|
562
|
+
opacity: 0,
|
|
563
|
+
pointerEvents: selected ? 'all' : 'none',
|
|
564
|
+
}}
|
|
565
|
+
/>
|
|
566
|
+
{/* Bottom - Source */}
|
|
567
|
+
<Handle
|
|
568
|
+
type="source"
|
|
569
|
+
position={Position.Bottom}
|
|
570
|
+
id="bottom-source"
|
|
571
|
+
className="connection-handle"
|
|
572
|
+
style={{
|
|
573
|
+
background: selected ? '#10B981' : '#1a1a2e',
|
|
574
|
+
width: '14px',
|
|
575
|
+
height: '14px',
|
|
576
|
+
border: '3px solid #10B981',
|
|
577
|
+
bottom: '-8px',
|
|
578
|
+
opacity: selected ? 1 : 0,
|
|
579
|
+
transition: 'all 0.2s ease-in-out',
|
|
580
|
+
cursor: 'crosshair',
|
|
581
|
+
zIndex: 10,
|
|
582
|
+
}}
|
|
583
|
+
/>
|
|
584
|
+
{/* Bottom - Target (hidden but functional) */}
|
|
585
|
+
<Handle
|
|
586
|
+
type="target"
|
|
587
|
+
position={Position.Bottom}
|
|
588
|
+
id="bottom-target"
|
|
589
|
+
style={{
|
|
590
|
+
background: 'transparent',
|
|
591
|
+
width: '14px',
|
|
592
|
+
height: '14px',
|
|
593
|
+
border: 'none',
|
|
594
|
+
bottom: '-8px',
|
|
595
|
+
opacity: 0,
|
|
596
|
+
pointerEvents: selected ? 'all' : 'none',
|
|
597
|
+
}}
|
|
598
|
+
/>
|
|
599
|
+
{/* Left - Source */}
|
|
600
|
+
<Handle
|
|
601
|
+
type="source"
|
|
602
|
+
position={Position.Left}
|
|
603
|
+
id="left-source"
|
|
604
|
+
className="connection-handle"
|
|
605
|
+
style={{
|
|
606
|
+
background: selected ? '#10B981' : '#1a1a2e',
|
|
607
|
+
width: '14px',
|
|
608
|
+
height: '14px',
|
|
609
|
+
border: '3px solid #10B981',
|
|
610
|
+
left: '-8px',
|
|
611
|
+
opacity: selected ? 1 : 0,
|
|
612
|
+
transition: 'all 0.2s ease-in-out',
|
|
613
|
+
cursor: 'crosshair',
|
|
614
|
+
zIndex: 10,
|
|
615
|
+
}}
|
|
616
|
+
/>
|
|
617
|
+
{/* Left - Target (hidden but functional) */}
|
|
529
618
|
<Handle
|
|
530
619
|
type="target"
|
|
531
620
|
position={Position.Left}
|
|
532
|
-
id="left"
|
|
621
|
+
id="left-target"
|
|
533
622
|
style={{
|
|
534
|
-
background: '
|
|
535
|
-
width: '
|
|
536
|
-
height: '
|
|
537
|
-
border: '
|
|
623
|
+
background: 'transparent',
|
|
624
|
+
width: '14px',
|
|
625
|
+
height: '14px',
|
|
626
|
+
border: 'none',
|
|
538
627
|
left: '-8px',
|
|
539
|
-
opacity: 0,
|
|
628
|
+
opacity: 0,
|
|
629
|
+
pointerEvents: selected ? 'all' : 'none',
|
|
540
630
|
}}
|
|
541
631
|
/>
|
|
632
|
+
{/* Right - Source */}
|
|
542
633
|
<Handle
|
|
543
634
|
type="source"
|
|
544
635
|
position={Position.Right}
|
|
545
|
-
id="right"
|
|
636
|
+
id="right-source"
|
|
637
|
+
className="connection-handle"
|
|
638
|
+
style={{
|
|
639
|
+
background: selected ? '#10B981' : '#1a1a2e',
|
|
640
|
+
width: '14px',
|
|
641
|
+
height: '14px',
|
|
642
|
+
border: '3px solid #10B981',
|
|
643
|
+
right: '-8px',
|
|
644
|
+
opacity: selected ? 1 : 0,
|
|
645
|
+
transition: 'all 0.2s ease-in-out',
|
|
646
|
+
cursor: 'crosshair',
|
|
647
|
+
zIndex: 10,
|
|
648
|
+
}}
|
|
649
|
+
/>
|
|
650
|
+
{/* Right - Target (hidden but functional) */}
|
|
651
|
+
<Handle
|
|
652
|
+
type="target"
|
|
653
|
+
position={Position.Right}
|
|
654
|
+
id="right-target"
|
|
546
655
|
style={{
|
|
547
|
-
background: '
|
|
548
|
-
width: '
|
|
549
|
-
height: '
|
|
550
|
-
border: '
|
|
656
|
+
background: 'transparent',
|
|
657
|
+
width: '14px',
|
|
658
|
+
height: '14px',
|
|
659
|
+
border: 'none',
|
|
551
660
|
right: '-8px',
|
|
552
|
-
opacity: 0,
|
|
661
|
+
opacity: 0,
|
|
662
|
+
pointerEvents: selected ? 'all' : 'none',
|
|
553
663
|
}}
|
|
554
664
|
/>
|
|
665
|
+
|
|
555
666
|
</Box>
|
|
556
667
|
|
|
668
|
+
{/* Node Action Buttons - Shows when selected */}
|
|
669
|
+
<NodeActionButtons
|
|
670
|
+
selected={selected}
|
|
671
|
+
onDelete={() => {
|
|
672
|
+
if (nodeId && onNodesChange) {
|
|
673
|
+
onNodesChange([{ id: nodeId, type: 'remove' }]);
|
|
674
|
+
}
|
|
675
|
+
}}
|
|
676
|
+
onDuplicate={() => {
|
|
677
|
+
if (nodeId) {
|
|
678
|
+
const currentNode = nodes.find(n => n.id === nodeId);
|
|
679
|
+
if (currentNode) {
|
|
680
|
+
const newNode = {
|
|
681
|
+
...currentNode,
|
|
682
|
+
id: `${currentNode.id}-copy-${Date.now()}`,
|
|
683
|
+
position: {
|
|
684
|
+
x: currentNode.position.x + 50,
|
|
685
|
+
y: currentNode.position.y + 50,
|
|
686
|
+
},
|
|
687
|
+
selected: false,
|
|
688
|
+
};
|
|
689
|
+
setNodes([...nodes, newNode]);
|
|
690
|
+
}
|
|
691
|
+
}
|
|
692
|
+
}}
|
|
693
|
+
/>
|
|
694
|
+
|
|
557
695
|
{/* AI Suggestions Button - Positioned below the node box */}
|
|
558
696
|
{data.formData?.aiSuggestionsCount !== undefined && data.formData.aiSuggestionsCount > 0 && (
|
|
559
697
|
<Box
|