@flowuent-org/diagramming-core 1.0.5 → 1.0.6
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/apps/diagramming/src/DiagramTabs.tsx +205 -205
- package/apps/diagramming/src/sample-workflow-content.ts +55 -54
- package/package.json +116 -116
- package/packages/diagrams/NODE_DATA_UPDATE_API.md +430 -430
- package/packages/diagrams/UNDO_REDO_API.md +306 -306
- package/packages/diagrams/package.json +25 -45
- package/packages/diagrams/pnpm-lock.yaml +2606 -0
- package/packages/diagrams/project.json +1 -2
- package/packages/diagrams/rollup.config.js +26 -31
- package/packages/diagrams/src/lib/atoms/CardEditableTitle.tsx +76 -76
- package/packages/diagrams/src/lib/components/automation/AutomationApiNode.tsx +24 -3
- package/packages/diagrams/src/lib/components/automation/AutomationEndNode.tsx +23 -2
- package/packages/diagrams/src/lib/components/automation/AutomationFormattingNode.tsx +24 -3
- package/packages/diagrams/src/lib/components/automation/AutomationStartNode.tsx +23 -2
- package/packages/diagrams/src/lib/contexts/onWorkflowNodeDelete.ts +1 -1
- package/packages/diagrams/src/lib/organisms/CustomEdge/useCreateBendPoint.tsx +121 -119
- package/packages/diagrams/src/lib/organisms/WorkFlowNode/NodeActionButtons.tsx +1 -1
- package/packages/diagrams/src/lib/templates/node-forms/CallForm.tsx +370 -370
- package/packages/diagrams/src/lib/types/node-types.ts +29 -29
- package/packages/diagrams/tsconfig.lib.json +25 -27
- package/tsconfig.base.json +30 -30
|
@@ -15,8 +15,7 @@
|
|
|
15
15
|
"entryFile": "packages/diagrams/src/index.ts",
|
|
16
16
|
"external": ["react", "react-dom", "react/jsx-runtime"],
|
|
17
17
|
"rollupConfig": "packages/diagrams/rollup.config.js",
|
|
18
|
-
"compiler": "
|
|
19
|
-
"skipTypeCheck": false,
|
|
18
|
+
"compiler": "babel",
|
|
20
19
|
"assets": [
|
|
21
20
|
{
|
|
22
21
|
"glob": "packages/diagrams/README.md",
|
|
@@ -1,31 +1,26 @@
|
|
|
1
|
-
const getRollupOptions = require('@nx/react/plugins/bundle-rollup');
|
|
2
|
-
const preserveDirectives = require('rollup-preserve-directives').default;
|
|
3
|
-
|
|
4
|
-
module.exports = (options) => {
|
|
5
|
-
// Get the default Nx Rollup options
|
|
6
|
-
const nxRollupOptions = getRollupOptions(options);
|
|
7
|
-
|
|
8
|
-
// Enable tree shaking
|
|
9
|
-
nxRollupOptions.treeshake = true; // Explicitly enable tree shaking
|
|
10
|
-
|
|
11
|
-
//
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
/^@mui\/icons-material\/.*/,
|
|
28
|
-
];
|
|
29
|
-
|
|
30
|
-
return nxRollupOptions;
|
|
31
|
-
};
|
|
1
|
+
const getRollupOptions = require('@nx/react/plugins/bundle-rollup');
|
|
2
|
+
const preserveDirectives = require('rollup-preserve-directives').default;
|
|
3
|
+
|
|
4
|
+
module.exports = (options) => {
|
|
5
|
+
// Get the default Nx Rollup options
|
|
6
|
+
const nxRollupOptions = getRollupOptions(options);
|
|
7
|
+
|
|
8
|
+
// Enable tree shaking
|
|
9
|
+
nxRollupOptions.treeshake = true; // Explicitly enable tree shaking
|
|
10
|
+
|
|
11
|
+
// Add the preserveDirectives plugin to the existing plugins
|
|
12
|
+
nxRollupOptions.plugins = [
|
|
13
|
+
...(Array.isArray(nxRollupOptions.plugins) ? nxRollupOptions.plugins : []),
|
|
14
|
+
preserveDirectives(),
|
|
15
|
+
];
|
|
16
|
+
|
|
17
|
+
// Externalize MUI packages
|
|
18
|
+
nxRollupOptions.external = [
|
|
19
|
+
...(Array.isArray(nxRollupOptions.external) ? nxRollupOptions.external : []),
|
|
20
|
+
'@mui/material',
|
|
21
|
+
'@mui/icons-material',
|
|
22
|
+
/^@mui\/icons-material\/.*/,
|
|
23
|
+
];
|
|
24
|
+
|
|
25
|
+
return nxRollupOptions;
|
|
26
|
+
};
|
|
@@ -1,76 +1,76 @@
|
|
|
1
|
-
import React, { useCallback, useEffect, useRef, useState } from 'react';
|
|
2
|
-
import { Box, TextField } from '@mui/material';
|
|
3
|
-
import { ICardNode } from '../types/card-node';
|
|
4
|
-
import { useCustomReactFlow } from '../hooks/customUseReactFlow';
|
|
5
|
-
import { useNodeId, useNodeTitle } from '
|
|
6
|
-
|
|
7
|
-
export const CardEditableTitle = React.memo(() => {
|
|
8
|
-
const title = useNodeTitle()
|
|
9
|
-
const nodeId = useNodeId()
|
|
10
|
-
const { getNodes, setNodes } = useCustomReactFlow();
|
|
11
|
-
const [text, setText] = useState(title);
|
|
12
|
-
const inputRef = useRef<HTMLInputElement>(null);
|
|
13
|
-
|
|
14
|
-
useEffect(() => {
|
|
15
|
-
if (title !== text) setText(title);
|
|
16
|
-
}, [title]);
|
|
17
|
-
|
|
18
|
-
const changeTitle = useCallback(
|
|
19
|
-
(val: string) => {
|
|
20
|
-
const prevNodes = [...getNodes()] as ICardNode[];
|
|
21
|
-
const newNodes: ICardNode[] = prevNodes.map((node) => {
|
|
22
|
-
if (node.id === nodeId) {
|
|
23
|
-
return {
|
|
24
|
-
...node,
|
|
25
|
-
data: { ...node.data, title: val },
|
|
26
|
-
};
|
|
27
|
-
} else return node;
|
|
28
|
-
});
|
|
29
|
-
setNodes(newNodes);
|
|
30
|
-
},
|
|
31
|
-
[getNodes, setNodes, nodeId],
|
|
32
|
-
);
|
|
33
|
-
|
|
34
|
-
const handleSave = useCallback(() => {
|
|
35
|
-
changeTitle(text ?? '');
|
|
36
|
-
}, [text, changeTitle]);
|
|
37
|
-
|
|
38
|
-
const handleKeyDown = useCallback(
|
|
39
|
-
(e: React.KeyboardEvent<HTMLInputElement>) => {
|
|
40
|
-
if (e.key === 'Enter') {
|
|
41
|
-
handleSave();
|
|
42
|
-
} else if (e.key === 'Escape') {
|
|
43
|
-
setText(title);
|
|
44
|
-
}
|
|
45
|
-
},
|
|
46
|
-
[handleSave, title],
|
|
47
|
-
);
|
|
48
|
-
|
|
49
|
-
return (
|
|
50
|
-
<Box
|
|
51
|
-
sx={{
|
|
52
|
-
display: 'flex',
|
|
53
|
-
justifyContent: 'center',
|
|
54
|
-
width: '100%',
|
|
55
|
-
my: 1,
|
|
56
|
-
backgroundColor: '#6B04F1',
|
|
57
|
-
borderRadius: '3px',
|
|
58
|
-
}}
|
|
59
|
-
>
|
|
60
|
-
<TextField
|
|
61
|
-
size={'small'}
|
|
62
|
-
inputProps={{
|
|
63
|
-
style: { fontSize: '18px', fontWeight: 'bold', textAlign: 'center', color: '#fff' },
|
|
64
|
-
}}
|
|
65
|
-
variant="outlined"
|
|
66
|
-
value={text}
|
|
67
|
-
onChange={(e) => setText(e.target.value)}
|
|
68
|
-
onKeyDown={handleKeyDown}
|
|
69
|
-
inputRef={inputRef}
|
|
70
|
-
fullWidth
|
|
71
|
-
/>
|
|
72
|
-
</Box>
|
|
73
|
-
);
|
|
74
|
-
});
|
|
75
|
-
|
|
76
|
-
export default CardEditableTitle;
|
|
1
|
+
import React, { useCallback, useEffect, useRef, useState } from 'react';
|
|
2
|
+
import { Box, TextField } from '@mui/material';
|
|
3
|
+
import { ICardNode } from '../types/card-node';
|
|
4
|
+
import { useCustomReactFlow } from '../hooks/customUseReactFlow';
|
|
5
|
+
import { useNodeId, useNodeTitle } from '@flowuent-labs/diagrams';
|
|
6
|
+
|
|
7
|
+
export const CardEditableTitle = React.memo(() => {
|
|
8
|
+
const title = useNodeTitle()
|
|
9
|
+
const nodeId = useNodeId()
|
|
10
|
+
const { getNodes, setNodes } = useCustomReactFlow();
|
|
11
|
+
const [text, setText] = useState(title);
|
|
12
|
+
const inputRef = useRef<HTMLInputElement>(null);
|
|
13
|
+
|
|
14
|
+
useEffect(() => {
|
|
15
|
+
if (title !== text) setText(title);
|
|
16
|
+
}, [title]);
|
|
17
|
+
|
|
18
|
+
const changeTitle = useCallback(
|
|
19
|
+
(val: string) => {
|
|
20
|
+
const prevNodes = [...getNodes()] as ICardNode[];
|
|
21
|
+
const newNodes: ICardNode[] = prevNodes.map((node) => {
|
|
22
|
+
if (node.id === nodeId) {
|
|
23
|
+
return {
|
|
24
|
+
...node,
|
|
25
|
+
data: { ...node.data, title: val },
|
|
26
|
+
};
|
|
27
|
+
} else return node;
|
|
28
|
+
});
|
|
29
|
+
setNodes(newNodes);
|
|
30
|
+
},
|
|
31
|
+
[getNodes, setNodes, nodeId],
|
|
32
|
+
);
|
|
33
|
+
|
|
34
|
+
const handleSave = useCallback(() => {
|
|
35
|
+
changeTitle(text ?? '');
|
|
36
|
+
}, [text, changeTitle]);
|
|
37
|
+
|
|
38
|
+
const handleKeyDown = useCallback(
|
|
39
|
+
(e: React.KeyboardEvent<HTMLInputElement>) => {
|
|
40
|
+
if (e.key === 'Enter') {
|
|
41
|
+
handleSave();
|
|
42
|
+
} else if (e.key === 'Escape') {
|
|
43
|
+
setText(title);
|
|
44
|
+
}
|
|
45
|
+
},
|
|
46
|
+
[handleSave, title],
|
|
47
|
+
);
|
|
48
|
+
|
|
49
|
+
return (
|
|
50
|
+
<Box
|
|
51
|
+
sx={{
|
|
52
|
+
display: 'flex',
|
|
53
|
+
justifyContent: 'center',
|
|
54
|
+
width: '100%',
|
|
55
|
+
my: 1,
|
|
56
|
+
backgroundColor: '#6B04F1',
|
|
57
|
+
borderRadius: '3px',
|
|
58
|
+
}}
|
|
59
|
+
>
|
|
60
|
+
<TextField
|
|
61
|
+
size={'small'}
|
|
62
|
+
inputProps={{
|
|
63
|
+
style: { fontSize: '18px', fontWeight: 'bold', textAlign: 'center', color: '#fff' },
|
|
64
|
+
}}
|
|
65
|
+
variant="outlined"
|
|
66
|
+
value={text}
|
|
67
|
+
onChange={(e) => setText(e.target.value)}
|
|
68
|
+
onKeyDown={handleKeyDown}
|
|
69
|
+
inputRef={inputRef}
|
|
70
|
+
fullWidth
|
|
71
|
+
/>
|
|
72
|
+
</Box>
|
|
73
|
+
);
|
|
74
|
+
});
|
|
75
|
+
|
|
76
|
+
export default CardEditableTitle;
|
|
@@ -278,6 +278,19 @@ export const AutomationApiNode: React.FC<AutomationApiNodeProps> = ({ data, sele
|
|
|
278
278
|
transition: 'all 0.2s ease',
|
|
279
279
|
cursor: 'pointer',
|
|
280
280
|
overflow: 'hidden',
|
|
281
|
+
...(data.status === 'Running' && {
|
|
282
|
+
animation: 'pulse-glow 2s ease-in-out infinite',
|
|
283
|
+
'@keyframes pulse-glow': {
|
|
284
|
+
'0%, 100%': {
|
|
285
|
+
boxShadow: '0 0 20px rgba(251, 191, 36, 0.4), 0 0 40px rgba(251, 191, 36, 0.2)',
|
|
286
|
+
borderColor: 'rgba(251, 191, 36, 0.6)',
|
|
287
|
+
},
|
|
288
|
+
'50%': {
|
|
289
|
+
boxShadow: '0 0 30px rgba(251, 191, 36, 0.6), 0 0 60px rgba(251, 191, 36, 0.3)',
|
|
290
|
+
borderColor: 'rgba(251, 191, 36, 0.9)',
|
|
291
|
+
},
|
|
292
|
+
},
|
|
293
|
+
}),
|
|
281
294
|
}}
|
|
282
295
|
onClick={handleJsonClick}
|
|
283
296
|
>
|
|
@@ -322,8 +335,16 @@ export const AutomationApiNode: React.FC<AutomationApiNodeProps> = ({ data, sele
|
|
|
322
335
|
label={data.status}
|
|
323
336
|
size="small"
|
|
324
337
|
sx={{
|
|
325
|
-
backgroundColor:
|
|
326
|
-
|
|
338
|
+
backgroundColor: data.status === 'Completed'
|
|
339
|
+
? 'rgba(37, 99, 235, 0.1)'
|
|
340
|
+
: data.status === 'Running'
|
|
341
|
+
? 'rgba(251, 191, 36, 0.1)'
|
|
342
|
+
: 'rgba(16, 185, 129, 0.1)',
|
|
343
|
+
color: data.status === 'Completed'
|
|
344
|
+
? '#93C5FD'
|
|
345
|
+
: data.status === 'Running'
|
|
346
|
+
? '#FCD34D'
|
|
347
|
+
: '#86EFAC',
|
|
327
348
|
fontWeight: 500,
|
|
328
349
|
fontSize: '12px',
|
|
329
350
|
height: '24px',
|
|
@@ -370,7 +391,7 @@ export const AutomationApiNode: React.FC<AutomationApiNodeProps> = ({ data, sele
|
|
|
370
391
|
{t('automation.apiNode.additionalInfo')}
|
|
371
392
|
</Typography>
|
|
372
393
|
|
|
373
|
-
{(data.formData?.executionResult || data.formData?.schemaMatch) && (
|
|
394
|
+
{data.status === 'Completed' && (data.formData?.executionResult || data.formData?.schemaMatch) && (
|
|
374
395
|
<>
|
|
375
396
|
{/* Schema Row */}
|
|
376
397
|
<Box sx={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center', mb: 2 }}>
|
|
@@ -207,6 +207,19 @@ export const AutomationEndNode: React.FC<AutomationEndNodeProps> = ({ data, sele
|
|
|
207
207
|
transition: 'all 0.2s ease',
|
|
208
208
|
cursor: 'pointer',
|
|
209
209
|
overflow: 'hidden',
|
|
210
|
+
...(data.status === 'Running' && {
|
|
211
|
+
animation: 'pulse-glow 2s ease-in-out infinite',
|
|
212
|
+
'@keyframes pulse-glow': {
|
|
213
|
+
'0%, 100%': {
|
|
214
|
+
boxShadow: '0 0 20px rgba(251, 191, 36, 0.4), 0 0 40px rgba(251, 191, 36, 0.2)',
|
|
215
|
+
borderColor: 'rgba(251, 191, 36, 0.6)',
|
|
216
|
+
},
|
|
217
|
+
'50%': {
|
|
218
|
+
boxShadow: '0 0 30px rgba(251, 191, 36, 0.6), 0 0 60px rgba(251, 191, 36, 0.3)',
|
|
219
|
+
borderColor: 'rgba(251, 191, 36, 0.9)',
|
|
220
|
+
},
|
|
221
|
+
},
|
|
222
|
+
}),
|
|
210
223
|
}}
|
|
211
224
|
onClick={handleJsonClick}
|
|
212
225
|
>
|
|
@@ -251,8 +264,16 @@ export const AutomationEndNode: React.FC<AutomationEndNodeProps> = ({ data, sele
|
|
|
251
264
|
label={data.status}
|
|
252
265
|
size="small"
|
|
253
266
|
sx={{
|
|
254
|
-
backgroundColor:
|
|
255
|
-
|
|
267
|
+
backgroundColor: data.status === 'Completed'
|
|
268
|
+
? 'rgba(37, 99, 235, 0.1)'
|
|
269
|
+
: data.status === 'Running'
|
|
270
|
+
? 'rgba(251, 191, 36, 0.1)'
|
|
271
|
+
: 'rgba(16, 185, 129, 0.1)',
|
|
272
|
+
color: data.status === 'Completed'
|
|
273
|
+
? '#93C5FD'
|
|
274
|
+
: data.status === 'Running'
|
|
275
|
+
? '#FCD34D'
|
|
276
|
+
: '#86EFAC',
|
|
256
277
|
fontWeight: 500,
|
|
257
278
|
fontSize: '12px',
|
|
258
279
|
height: '24px',
|
|
@@ -282,6 +282,19 @@ export const AutomationFormattingNode: React.FC<AutomationFormattingNodeProps> =
|
|
|
282
282
|
transition: 'all 0.2s ease',
|
|
283
283
|
cursor: 'pointer',
|
|
284
284
|
overflow: 'hidden',
|
|
285
|
+
...(data.status === 'Running' && {
|
|
286
|
+
animation: 'pulse-glow 2s ease-in-out infinite',
|
|
287
|
+
'@keyframes pulse-glow': {
|
|
288
|
+
'0%, 100%': {
|
|
289
|
+
boxShadow: '0 0 20px rgba(251, 191, 36, 0.4), 0 0 40px rgba(251, 191, 36, 0.2)',
|
|
290
|
+
borderColor: 'rgba(251, 191, 36, 0.6)',
|
|
291
|
+
},
|
|
292
|
+
'50%': {
|
|
293
|
+
boxShadow: '0 0 30px rgba(251, 191, 36, 0.6), 0 0 60px rgba(251, 191, 36, 0.3)',
|
|
294
|
+
borderColor: 'rgba(251, 191, 36, 0.9)',
|
|
295
|
+
},
|
|
296
|
+
},
|
|
297
|
+
}),
|
|
285
298
|
}}
|
|
286
299
|
onClick={handleJsonClick}
|
|
287
300
|
>
|
|
@@ -326,8 +339,16 @@ export const AutomationFormattingNode: React.FC<AutomationFormattingNodeProps> =
|
|
|
326
339
|
label={data.status}
|
|
327
340
|
size="small"
|
|
328
341
|
sx={{
|
|
329
|
-
backgroundColor:
|
|
330
|
-
|
|
342
|
+
backgroundColor: data.status === 'Completed'
|
|
343
|
+
? 'rgba(37, 99, 235, 0.1)'
|
|
344
|
+
: data.status === 'Running'
|
|
345
|
+
? 'rgba(251, 191, 36, 0.1)'
|
|
346
|
+
: 'rgba(16, 185, 129, 0.1)',
|
|
347
|
+
color: data.status === 'Completed'
|
|
348
|
+
? '#93C5FD'
|
|
349
|
+
: data.status === 'Running'
|
|
350
|
+
? '#FCD34D'
|
|
351
|
+
: '#86EFAC',
|
|
331
352
|
fontWeight: 500,
|
|
332
353
|
fontSize: '12px',
|
|
333
354
|
height: '24px',
|
|
@@ -408,7 +429,7 @@ export const AutomationFormattingNode: React.FC<AutomationFormattingNodeProps> =
|
|
|
408
429
|
</Box>
|
|
409
430
|
</Box>
|
|
410
431
|
|
|
411
|
-
{(data.formData?.executionResult || data.formData?.schemaMatch) && (
|
|
432
|
+
{data.status === 'Completed' && (data.formData?.executionResult || data.formData?.schemaMatch) && (
|
|
412
433
|
<>
|
|
413
434
|
{/* Schema Row */}
|
|
414
435
|
<Box sx={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center', mb: 2 }}>
|
|
@@ -208,6 +208,19 @@ export const AutomationStartNode: React.FC<AutomationStartNodeProps> = ({ data,
|
|
|
208
208
|
transition: 'all 0.2s ease',
|
|
209
209
|
cursor: 'pointer',
|
|
210
210
|
overflow: 'hidden',
|
|
211
|
+
...(data.status === 'Running' && {
|
|
212
|
+
animation: 'pulse-glow 2s ease-in-out infinite',
|
|
213
|
+
'@keyframes pulse-glow': {
|
|
214
|
+
'0%, 100%': {
|
|
215
|
+
boxShadow: '0 0 20px rgba(251, 191, 36, 0.4), 0 0 40px rgba(251, 191, 36, 0.2)',
|
|
216
|
+
borderColor: 'rgba(251, 191, 36, 0.6)',
|
|
217
|
+
},
|
|
218
|
+
'50%': {
|
|
219
|
+
boxShadow: '0 0 30px rgba(251, 191, 36, 0.6), 0 0 60px rgba(251, 191, 36, 0.3)',
|
|
220
|
+
borderColor: 'rgba(251, 191, 36, 0.9)',
|
|
221
|
+
},
|
|
222
|
+
},
|
|
223
|
+
}),
|
|
211
224
|
}}
|
|
212
225
|
onClick={handleJsonClick}
|
|
213
226
|
>
|
|
@@ -252,8 +265,16 @@ export const AutomationStartNode: React.FC<AutomationStartNodeProps> = ({ data,
|
|
|
252
265
|
label={data.status}
|
|
253
266
|
size="small"
|
|
254
267
|
sx={{
|
|
255
|
-
backgroundColor:
|
|
256
|
-
|
|
268
|
+
backgroundColor: data.status === 'Completed'
|
|
269
|
+
? 'rgba(37, 99, 235, 0.1)'
|
|
270
|
+
: data.status === 'Running'
|
|
271
|
+
? 'rgba(251, 191, 36, 0.1)'
|
|
272
|
+
: 'rgba(16, 185, 129, 0.1)',
|
|
273
|
+
color: data.status === 'Completed'
|
|
274
|
+
? '#93C5FD'
|
|
275
|
+
: data.status === 'Running'
|
|
276
|
+
? '#FCD34D'
|
|
277
|
+
: '#86EFAC',
|
|
257
278
|
fontWeight: 500,
|
|
258
279
|
fontSize: '12px',
|
|
259
280
|
height: '24px',
|
|
@@ -1,119 +1,121 @@
|
|
|
1
|
-
import { Edge } from '@xyflow/react';
|
|
2
|
-
import {
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
import
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
const
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
1
|
+
import { Edge } from '@xyflow/react';
|
|
2
|
+
import {
|
|
3
|
+
closestPoint,
|
|
4
|
+
DiagramStore,
|
|
5
|
+
EdgeTypes,
|
|
6
|
+
HistoryEvent,
|
|
7
|
+
ICardNode,
|
|
8
|
+
useCustomReactFlow,
|
|
9
|
+
useDiagram,
|
|
10
|
+
} from '@flowuent-labs/diagrams';
|
|
11
|
+
import React, { useCallback } from 'react';
|
|
12
|
+
import { v4 as uuidv4 } from 'uuid';
|
|
13
|
+
import addToHistory from '../../utils/addToHistory';
|
|
14
|
+
|
|
15
|
+
export const useCreateBendPoint = (
|
|
16
|
+
currentEdge: Edge,
|
|
17
|
+
sourceNode: ICardNode,
|
|
18
|
+
targetNode: ICardNode,
|
|
19
|
+
pathRef: React.RefObject<SVGPathElement>,
|
|
20
|
+
) => {
|
|
21
|
+
const { setEdges, getEdge, screenToFlowPosition, addNodes } =
|
|
22
|
+
useCustomReactFlow();
|
|
23
|
+
const store = useDiagram<DiagramStore>();
|
|
24
|
+
const { diagramType, nodes, edges, setStore } = store;
|
|
25
|
+
|
|
26
|
+
return useCallback(
|
|
27
|
+
(evt: React.MouseEvent<SVGPathElement, MouseEvent>) => {
|
|
28
|
+
if (diagramType === 'workflow') {
|
|
29
|
+
return;
|
|
30
|
+
}
|
|
31
|
+
evt.preventDefault();
|
|
32
|
+
evt.stopPropagation();
|
|
33
|
+
if (!pathRef.current) return;
|
|
34
|
+
|
|
35
|
+
const position = screenToFlowPosition({
|
|
36
|
+
x: evt.clientX,
|
|
37
|
+
y: evt.clientY,
|
|
38
|
+
});
|
|
39
|
+
const cpoint = closestPoint(pathRef.current, [position.x, position.y]);
|
|
40
|
+
if (!currentEdge || !sourceNode || !targetNode)
|
|
41
|
+
throw new Error(
|
|
42
|
+
'Invalid Edge found with source, target or curr edge missing',
|
|
43
|
+
);
|
|
44
|
+
|
|
45
|
+
const nodesAndEdgesBefore = {
|
|
46
|
+
nodes: [...nodes],
|
|
47
|
+
edges: [...edges],
|
|
48
|
+
};
|
|
49
|
+
const nodeId = uuidv4();
|
|
50
|
+
const newBendPoint: ICardNode = {
|
|
51
|
+
id: nodeId,
|
|
52
|
+
position: { x: cpoint[0], y: cpoint[1] },
|
|
53
|
+
data: { nodeId: nodeId },
|
|
54
|
+
type: 'bendPoint',
|
|
55
|
+
};
|
|
56
|
+
const newEdge: Edge = {
|
|
57
|
+
id: uuidv4(),
|
|
58
|
+
source: newBendPoint.id,
|
|
59
|
+
target: currentEdge.target,
|
|
60
|
+
type:
|
|
61
|
+
currentEdge.type === EdgeTypes.Default ||
|
|
62
|
+
currentEdge.type === EdgeTypes.Bend2Target
|
|
63
|
+
? EdgeTypes.Bend2Target
|
|
64
|
+
: EdgeTypes.Bend2Bend,
|
|
65
|
+
};
|
|
66
|
+
newEdge.markerEnd =
|
|
67
|
+
newEdge.type === EdgeTypes.Bend2Target
|
|
68
|
+
? currentEdge.markerEnd
|
|
69
|
+
: undefined;
|
|
70
|
+
currentEdge.target = newBendPoint.id;
|
|
71
|
+
currentEdge.type =
|
|
72
|
+
currentEdge.type === EdgeTypes.Default ||
|
|
73
|
+
currentEdge.type === EdgeTypes.Source2Bend
|
|
74
|
+
? EdgeTypes.Source2Bend
|
|
75
|
+
: EdgeTypes.Bend2Bend;
|
|
76
|
+
delete currentEdge.markerEnd;
|
|
77
|
+
currentEdge.markerStart =
|
|
78
|
+
currentEdge.type === EdgeTypes.Source2Bend
|
|
79
|
+
? currentEdge.markerStart
|
|
80
|
+
: undefined;
|
|
81
|
+
|
|
82
|
+
const newEdges = [
|
|
83
|
+
...edges.filter((e) => e.id !== currentEdge.id),
|
|
84
|
+
currentEdge,
|
|
85
|
+
newEdge,
|
|
86
|
+
];
|
|
87
|
+
const newNodes = [...nodes, newBendPoint];
|
|
88
|
+
const nodesAndEdgesAfter = {
|
|
89
|
+
nodes: newNodes,
|
|
90
|
+
edges: newEdges,
|
|
91
|
+
};
|
|
92
|
+
|
|
93
|
+
const event: HistoryEvent = {
|
|
94
|
+
forward: {
|
|
95
|
+
t: 'complex-change',
|
|
96
|
+
type: 'complex',
|
|
97
|
+
nodesAndEdges: nodesAndEdgesAfter,
|
|
98
|
+
},
|
|
99
|
+
backward: {
|
|
100
|
+
t: 'complex-change',
|
|
101
|
+
type: 'complex',
|
|
102
|
+
nodesAndEdges: nodesAndEdgesBefore,
|
|
103
|
+
},
|
|
104
|
+
title: 'Bend point created in the middle of ' + currentEdge.id,
|
|
105
|
+
};
|
|
106
|
+
setStore({
|
|
107
|
+
...addToHistory(store, [event]),
|
|
108
|
+
...nodesAndEdgesAfter,
|
|
109
|
+
});
|
|
110
|
+
},
|
|
111
|
+
[
|
|
112
|
+
pathRef.current,
|
|
113
|
+
currentEdge.id,
|
|
114
|
+
getEdge,
|
|
115
|
+
setEdges,
|
|
116
|
+
addNodes,
|
|
117
|
+
sourceNode,
|
|
118
|
+
targetNode,
|
|
119
|
+
],
|
|
120
|
+
);
|
|
121
|
+
};
|