@flowuent-org/diagramming-core 1.3.4 → 1.3.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/package.json +1 -1
- package/packages/diagrams/src/lib/components/automation/AutomationEndNode.tsx +7 -1
- package/packages/diagrams/src/lib/components/automation/AutomationFormattingNode.tsx +14 -4
- package/packages/diagrams/src/lib/components/automation/AutomationGoogleServicesNode.tsx +7 -7
- package/packages/diagrams/src/lib/components/automation/AutomationNavigationNode.tsx +618 -617
- package/packages/diagrams/src/lib/components/automation/AutomationSlackNode.tsx +7 -7
- package/packages/diagrams/src/lib/components/automation/AutomationTelegramNode.tsx +7 -7
- package/packages/diagrams/src/lib/components/automation/statusColors.ts +25 -10
- package/packages/diagrams/src/lib/contexts/diagramStoreTypes.tsx +1 -0
- package/packages/diagrams/src/lib/contexts/onDragStart.ts +17 -0
- package/packages/diagrams/src/lib/contexts/onNodeDragEnd.ts +109 -15
- package/packages/diagrams/src/lib/contexts/onNodesChange.ts +32 -11
- package/packages/diagrams/src/lib/organisms/WorkFlowNode/NodeTypeDisplay.tsx +103 -15
- package/packages/diagrams/src/lib/organisms/WorkFlowNode/WorkflowNodeIcons.tsx +23 -0
- package/packages/diagrams/src/lib/templates/DiagramContent.tsx +153 -30
- package/packages/molecules/src/index.ts +23 -4
- package/packages/molecules/src/lib/SvgIcons/icons.tsx +291 -0
- package/packages/molecules/src/lib/SvgIcons/index.ts +25 -0
- package/packages/molecules/src/lib/SvgIcons/types.ts +7 -0
|
@@ -55,7 +55,7 @@ export interface AutomationSlackNodeData {
|
|
|
55
55
|
label: string;
|
|
56
56
|
description: string;
|
|
57
57
|
operationType?: SlackOperationType;
|
|
58
|
-
status: '
|
|
58
|
+
status: 'Ready' | 'Running' | 'Completed' | 'Failed' | 'Need to Config' | 'authenticated';
|
|
59
59
|
parameters?: {
|
|
60
60
|
// Message operations
|
|
61
61
|
channel?: string;
|
|
@@ -228,11 +228,11 @@ export const AutomationSlackNode: React.FC<AutomationSlackNodeProps> = ({
|
|
|
228
228
|
const tokenExpired = isTokenExpired(data.parameters?.slackTokenExpiresAt);
|
|
229
229
|
|
|
230
230
|
// Execution state
|
|
231
|
-
const status = data.status || '
|
|
232
|
-
const executionProgress = status === '
|
|
231
|
+
const status = data.status || 'Ready';
|
|
232
|
+
const executionProgress = status === 'Running' ? 50 : 0;
|
|
233
233
|
|
|
234
234
|
// Status configuration - using centralized status colors
|
|
235
|
-
const statusConfig = getStatusColor(status, status === 'authenticated' ? 'authenticated' : '
|
|
235
|
+
const statusConfig = getStatusColor(status, status === 'authenticated' ? 'authenticated' : 'ready');
|
|
236
236
|
|
|
237
237
|
// Handle JSON view
|
|
238
238
|
const handleJsonClick = () => {
|
|
@@ -342,8 +342,8 @@ export const AutomationSlackNode: React.FC<AutomationSlackNodeProps> = ({
|
|
|
342
342
|
size="small"
|
|
343
343
|
label={
|
|
344
344
|
<Box sx={{ display: 'flex', alignItems: 'center', gap: 0.5 }}>
|
|
345
|
-
{status === 'success' && <CheckCircleIcon sx={{ fontSize: 12 }} />}
|
|
346
|
-
{status === 'error' && <ErrorIcon sx={{ fontSize: 12 }} />}
|
|
345
|
+
{(status === 'Completed' || status === 'success') && <CheckCircleIcon sx={{ fontSize: 12 }} />}
|
|
346
|
+
{(status === 'Failed' || status === 'error') && <ErrorIcon sx={{ fontSize: 12 }} />}
|
|
347
347
|
<span>{statusConfig.label}</span>
|
|
348
348
|
</Box>
|
|
349
349
|
}
|
|
@@ -522,7 +522,7 @@ export const AutomationSlackNode: React.FC<AutomationSlackNodeProps> = ({
|
|
|
522
522
|
</Box>
|
|
523
523
|
|
|
524
524
|
{/* Progress Bar (when running) */}
|
|
525
|
-
{status === '
|
|
525
|
+
{status === 'Running' && (
|
|
526
526
|
<Box sx={{ mb: 1.5 }}>
|
|
527
527
|
<LinearProgress
|
|
528
528
|
variant="determinate"
|
|
@@ -58,7 +58,7 @@ export interface AutomationTelegramNodeData {
|
|
|
58
58
|
label: string;
|
|
59
59
|
description: string;
|
|
60
60
|
operationType?: TelegramOperationType;
|
|
61
|
-
status: '
|
|
61
|
+
status: 'Ready' | 'Running' | 'Completed' | 'Failed' | 'Need to Config' | 'authenticated';
|
|
62
62
|
parameters?: {
|
|
63
63
|
// Message operations
|
|
64
64
|
chatId?: string;
|
|
@@ -221,11 +221,11 @@ export const AutomationTelegramNode: React.FC<AutomationTelegramNodeProps> = ({
|
|
|
221
221
|
const botId = data.telegramAuth?.botId || data.parameters?.botId;
|
|
222
222
|
|
|
223
223
|
// Execution state
|
|
224
|
-
const status = data.status || '
|
|
225
|
-
const executionProgress = status === '
|
|
224
|
+
const status = data.status || 'Ready';
|
|
225
|
+
const executionProgress = status === 'Running' ? 50 : 0;
|
|
226
226
|
|
|
227
227
|
// Status configuration - using centralized status colors
|
|
228
|
-
const statusConfig = getStatusColor(status, status === 'authenticated' ? 'authenticated' : '
|
|
228
|
+
const statusConfig = getStatusColor(status, status === 'authenticated' ? 'authenticated' : 'ready');
|
|
229
229
|
|
|
230
230
|
// Handle JSON view
|
|
231
231
|
const handleJsonClick = () => {
|
|
@@ -335,8 +335,8 @@ export const AutomationTelegramNode: React.FC<AutomationTelegramNodeProps> = ({
|
|
|
335
335
|
size="small"
|
|
336
336
|
label={
|
|
337
337
|
<Box sx={{ display: 'flex', alignItems: 'center', gap: 0.5 }}>
|
|
338
|
-
{status === 'success' && <CheckCircleIcon sx={{ fontSize: 12 }} />}
|
|
339
|
-
{status === 'error' && <ErrorIcon sx={{ fontSize: 12 }} />}
|
|
338
|
+
{(status === 'Completed' || status === 'success') && <CheckCircleIcon sx={{ fontSize: 12 }} />}
|
|
339
|
+
{(status === 'Failed' || status === 'error') && <ErrorIcon sx={{ fontSize: 12 }} />}
|
|
340
340
|
<span>{statusConfig.label}</span>
|
|
341
341
|
</Box>
|
|
342
342
|
}
|
|
@@ -515,7 +515,7 @@ export const AutomationTelegramNode: React.FC<AutomationTelegramNodeProps> = ({
|
|
|
515
515
|
</Box>
|
|
516
516
|
|
|
517
517
|
{/* Progress Bar (when running) */}
|
|
518
|
-
{status === '
|
|
518
|
+
{status === 'Running' && (
|
|
519
519
|
<Box sx={{ mb: 1.5 }}>
|
|
520
520
|
<LinearProgress
|
|
521
521
|
variant="determinate"
|
|
@@ -17,8 +17,9 @@ export interface AutomationStatusColors {
|
|
|
17
17
|
completed: StatusColorConfig;
|
|
18
18
|
success: StatusColorConfig; // Alias for completed (used in some nodes)
|
|
19
19
|
error: StatusColorConfig;
|
|
20
|
+
failed: StatusColorConfig; // Alias for error
|
|
20
21
|
ready: StatusColorConfig;
|
|
21
|
-
|
|
22
|
+
'need to config': StatusColorConfig; // Status when configuration is needed
|
|
22
23
|
authenticated?: StatusColorConfig; // Optional, used in auth-enabled nodes
|
|
23
24
|
}
|
|
24
25
|
|
|
@@ -28,8 +29,9 @@ export interface AutomationStatusColors {
|
|
|
28
29
|
* Colors are designed to be visually distinct and accessible:
|
|
29
30
|
* - Running: Blue (#2563EB) - indicates active execution
|
|
30
31
|
* - Completed/Success: Yellow (#FCD34D) - indicates successful completion
|
|
31
|
-
* - Error: Red (#EF4444) - indicates failure
|
|
32
|
-
* - Ready
|
|
32
|
+
* - Error/Failed: Red (#EF4444) - indicates failure
|
|
33
|
+
* - Ready: Green (#86EFAC) - indicates ready to execute
|
|
34
|
+
* - Need to Config: Yellow (#FBBF24) - indicates configuration required
|
|
33
35
|
*/
|
|
34
36
|
export const automationStatusColors: AutomationStatusColors = {
|
|
35
37
|
running: {
|
|
@@ -52,15 +54,20 @@ export const automationStatusColors: AutomationStatusColors = {
|
|
|
52
54
|
bgColor: 'rgba(239, 68, 68, 0.1)',
|
|
53
55
|
label: 'Error',
|
|
54
56
|
},
|
|
57
|
+
failed: {
|
|
58
|
+
color: '#EF4444',
|
|
59
|
+
bgColor: 'rgba(239, 68, 68, 0.1)',
|
|
60
|
+
label: 'Failed',
|
|
61
|
+
},
|
|
55
62
|
ready: {
|
|
56
63
|
color: '#86EFAC',
|
|
57
64
|
bgColor: 'rgba(16, 185, 129, 0.1)',
|
|
58
65
|
label: 'Ready',
|
|
59
66
|
},
|
|
60
|
-
|
|
61
|
-
color: '#
|
|
62
|
-
bgColor: 'rgba(
|
|
63
|
-
label: '
|
|
67
|
+
'need to config': {
|
|
68
|
+
color: '#FBBF24',
|
|
69
|
+
bgColor: 'rgba(251, 191, 36, 0.1)',
|
|
70
|
+
label: 'Need to Config',
|
|
64
71
|
},
|
|
65
72
|
authenticated: {
|
|
66
73
|
color: '#0088cc',
|
|
@@ -88,15 +95,23 @@ export const getStatusColor = (
|
|
|
88
95
|
'completed': 'completed',
|
|
89
96
|
'success': 'success',
|
|
90
97
|
'error': 'error',
|
|
91
|
-
'failed': '
|
|
98
|
+
'failed': 'failed',
|
|
92
99
|
'ready': 'ready',
|
|
93
|
-
'idle': '
|
|
100
|
+
'idle': 'ready', // Map idle to ready
|
|
101
|
+
'need to config': 'need to config',
|
|
102
|
+
'need_to_config': 'need to config',
|
|
103
|
+
'not_ready': 'need to config',
|
|
104
|
+
'not ready': 'need to config',
|
|
94
105
|
'authenticated': 'authenticated',
|
|
95
106
|
'connected': 'authenticated',
|
|
96
107
|
};
|
|
97
108
|
|
|
98
109
|
const configKey = statusMap[normalizedStatus] || defaultStatus;
|
|
99
|
-
|
|
110
|
+
const config = automationStatusColors[configKey];
|
|
111
|
+
if (config) {
|
|
112
|
+
return config;
|
|
113
|
+
}
|
|
114
|
+
return automationStatusColors[defaultStatus];
|
|
100
115
|
};
|
|
101
116
|
|
|
102
117
|
/**
|
|
@@ -66,6 +66,7 @@ export interface DiagramState {
|
|
|
66
66
|
nodes: ICardNode[];
|
|
67
67
|
defaultNodes: ICardNode[];
|
|
68
68
|
dragStartPosition: DragStartPosition | null;
|
|
69
|
+
dragStartPositions: Record<string, { x: number; y: number }> | null;
|
|
69
70
|
edges: Edge[];
|
|
70
71
|
defaultEdges: Edge[];
|
|
71
72
|
history: HistoryEvent[];
|
|
@@ -20,9 +20,26 @@ export const onNodeDragStart =
|
|
|
20
20
|
positionAbsolute: cardNode.position,
|
|
21
21
|
};
|
|
22
22
|
|
|
23
|
+
// Store initial positions for ALL selected nodes (for multi-node dragging)
|
|
24
|
+
// Get selected nodes from state.nodes (they should have selected property set by ReactFlow)
|
|
25
|
+
const selectedNodes = state.nodes.filter(n => (n as any).selected);
|
|
26
|
+
let dragStartPositions: Record<string, { x: number; y: number }> | null = null;
|
|
27
|
+
|
|
28
|
+
if (selectedNodes.length > 1) {
|
|
29
|
+
// Multiple nodes are selected - store all their initial positions
|
|
30
|
+
dragStartPositions = {};
|
|
31
|
+
selectedNodes.forEach(selectedNode => {
|
|
32
|
+
dragStartPositions![selectedNode.id] = {
|
|
33
|
+
x: selectedNode.position.x,
|
|
34
|
+
y: selectedNode.position.y,
|
|
35
|
+
};
|
|
36
|
+
});
|
|
37
|
+
}
|
|
38
|
+
|
|
23
39
|
return {
|
|
24
40
|
...state,
|
|
25
41
|
dragStartPosition: changeForUndo,
|
|
42
|
+
dragStartPositions: dragStartPositions || null,
|
|
26
43
|
};
|
|
27
44
|
});
|
|
28
45
|
};
|
|
@@ -18,10 +18,101 @@ export const onNodeDragEnd =
|
|
|
18
18
|
const cardNode = node as ICardNode;
|
|
19
19
|
set((state: DiagramStore) => {
|
|
20
20
|
const dragStartPosition = state.dragStartPosition;
|
|
21
|
+
const dragStartPositions = state.dragStartPositions;
|
|
21
22
|
|
|
22
|
-
//
|
|
23
|
+
// Get all currently selected nodes from state (they should have selected property)
|
|
24
|
+
// Also get the current node's position from ReactFlow (cardNode parameter)
|
|
25
|
+
const selectedNodesInState = state.nodes.filter(n => (n as any).selected);
|
|
26
|
+
const isMultiDrag = dragStartPositions && Object.keys(dragStartPositions).length > 1;
|
|
27
|
+
|
|
28
|
+
// If multiple nodes were dragged, handle all of them
|
|
29
|
+
if (isMultiDrag && dragStartPositions) {
|
|
30
|
+
const historyEvents: HistoryEvent[] = [];
|
|
31
|
+
const updatedNodes = state.nodes.map((n) => {
|
|
32
|
+
// Check if this node was part of the multi-drag
|
|
33
|
+
if (dragStartPositions![n.id]) {
|
|
34
|
+
const startPos = dragStartPositions[n.id];
|
|
35
|
+
// For the dragged node, use position from ReactFlow's node parameter (most up-to-date)
|
|
36
|
+
// For other nodes, use position from state.nodes (updated by onNodesChange)
|
|
37
|
+
let endPos = n.position;
|
|
38
|
+
if (n.id === cardNode.id) {
|
|
39
|
+
// Use the position from ReactFlow for the node being dragged
|
|
40
|
+
endPos = cardNode.position;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
// Check if this node moved
|
|
44
|
+
if (startPos.x !== endPos.x || startPos.y !== endPos.y) {
|
|
45
|
+
// Create history event for this node
|
|
46
|
+
historyEvents.push({
|
|
47
|
+
title: `Node '${n.id}' moved from (${startPos.x.toFixed(1)}, ${startPos.y.toFixed(1)}) to (${endPos.x.toFixed(1)}, ${endPos.y.toFixed(1)})`,
|
|
48
|
+
backward: {
|
|
49
|
+
t: 'node-change',
|
|
50
|
+
type: 'position',
|
|
51
|
+
id: n.id,
|
|
52
|
+
message: `Revert: Node '${n.id}' position back to (${startPos.x.toFixed(1)}, ${startPos.y.toFixed(1)})`,
|
|
53
|
+
position: startPos,
|
|
54
|
+
positionAbsolute: startPos,
|
|
55
|
+
},
|
|
56
|
+
forward: {
|
|
57
|
+
t: 'node-change',
|
|
58
|
+
type: 'position',
|
|
59
|
+
id: n.id,
|
|
60
|
+
message: `Redo: Node '${n.id}' moved to (${endPos.x}, ${endPos.y})`,
|
|
61
|
+
position: endPos,
|
|
62
|
+
positionAbsolute: endPos,
|
|
63
|
+
},
|
|
64
|
+
});
|
|
65
|
+
|
|
66
|
+
// Update node position - ensure it matches ReactFlow's current state
|
|
67
|
+
return {
|
|
68
|
+
...n,
|
|
69
|
+
position: endPos,
|
|
70
|
+
data: { ...n.data, isPinned: true },
|
|
71
|
+
};
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
return n;
|
|
75
|
+
});
|
|
76
|
+
|
|
77
|
+
// Trigger onChange callbacks for all moved nodes
|
|
78
|
+
if (onChange && historyEvents.length > 0) {
|
|
79
|
+
historyEvents.forEach((historyEvent) => {
|
|
80
|
+
onChange({
|
|
81
|
+
t: 'node-change',
|
|
82
|
+
type: 'position',
|
|
83
|
+
id: historyEvent.forward.id,
|
|
84
|
+
position: historyEvent.forward.position,
|
|
85
|
+
positionAbsolute: historyEvent.forward.positionAbsolute,
|
|
86
|
+
message: historyEvent.title,
|
|
87
|
+
});
|
|
88
|
+
});
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
return {
|
|
92
|
+
...addToHistory(state, historyEvents),
|
|
93
|
+
dragStartPosition: null,
|
|
94
|
+
dragStartPositions: null,
|
|
95
|
+
nodes: updatedNodes,
|
|
96
|
+
};
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
// Single node drag handling (original logic)
|
|
23
100
|
if (!dragStartPosition) {
|
|
24
|
-
|
|
101
|
+
// Ensure the node's current position is persisted in state
|
|
102
|
+
const nodeInState = state.nodes.find((n) => n.id === cardNode.id);
|
|
103
|
+
if (nodeInState && (
|
|
104
|
+
nodeInState.position.x !== cardNode.position.x ||
|
|
105
|
+
nodeInState.position.y !== cardNode.position.y
|
|
106
|
+
)) {
|
|
107
|
+
// Update the node position to match the dragged position
|
|
108
|
+
const updatedNodes = state.nodes.map((n) =>
|
|
109
|
+
n.id === cardNode.id
|
|
110
|
+
? { ...n, position: cardNode.position, data: { ...n.data, isPinned: true } }
|
|
111
|
+
: n,
|
|
112
|
+
);
|
|
113
|
+
return { ...state, nodes: updatedNodes, dragStartPositions: null };
|
|
114
|
+
}
|
|
115
|
+
return { ...state, dragStartPositions: null };
|
|
25
116
|
}
|
|
26
117
|
|
|
27
118
|
// Check if the node has moved
|
|
@@ -29,17 +120,21 @@ export const onNodeDragEnd =
|
|
|
29
120
|
dragStartPosition.position.x !== cardNode.position.x ||
|
|
30
121
|
dragStartPosition.position.y !== cardNode.position.y
|
|
31
122
|
) {
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
123
|
+
// Update nodes array to ensure position is persisted
|
|
124
|
+
const updatedNodes = state.nodes.map((n) => {
|
|
125
|
+
if (n.id === cardNode.id) {
|
|
126
|
+
return {
|
|
127
|
+
...n,
|
|
128
|
+
position: cardNode.position,
|
|
129
|
+
data: { ...n.data, isPinned: true }
|
|
130
|
+
};
|
|
131
|
+
}
|
|
132
|
+
return n;
|
|
133
|
+
});
|
|
37
134
|
|
|
38
|
-
// Create the HistoryEvent entry
|
|
135
|
+
// Create the HistoryEvent entry
|
|
39
136
|
const historyEvent: HistoryEvent = {
|
|
40
137
|
title: `Node '${cardNode.id}' moved from (${dragStartPosition.position.x.toFixed(1)}, ${dragStartPosition.position.y.toFixed(1)}) to (${cardNode.position.x.toFixed(1)}, ${cardNode.position.y.toFixed(1)})`,
|
|
41
|
-
|
|
42
|
-
// Backward change for undo
|
|
43
138
|
backward: {
|
|
44
139
|
t: 'node-change',
|
|
45
140
|
type: 'position',
|
|
@@ -48,8 +143,6 @@ export const onNodeDragEnd =
|
|
|
48
143
|
position: dragStartPosition.position,
|
|
49
144
|
positionAbsolute: dragStartPosition.position,
|
|
50
145
|
},
|
|
51
|
-
|
|
52
|
-
// Forward change for redo
|
|
53
146
|
forward: {
|
|
54
147
|
t: 'node-change',
|
|
55
148
|
type: 'position',
|
|
@@ -74,12 +167,13 @@ export const onNodeDragEnd =
|
|
|
74
167
|
|
|
75
168
|
return {
|
|
76
169
|
...addToHistory(state, [historyEvent]),
|
|
77
|
-
dragStartPosition: null,
|
|
170
|
+
dragStartPosition: null,
|
|
171
|
+
dragStartPositions: null,
|
|
78
172
|
nodes: updatedNodes,
|
|
79
173
|
};
|
|
80
174
|
}
|
|
81
175
|
|
|
82
|
-
// If no movement occurred, just reset dragStartPosition
|
|
83
|
-
return { ...state, dragStartPosition: null };
|
|
176
|
+
// If no movement occurred, just reset dragStartPosition
|
|
177
|
+
return { ...state, dragStartPosition: null, dragStartPositions: null };
|
|
84
178
|
});
|
|
85
179
|
};
|
|
@@ -277,24 +277,44 @@ function handleElementChanges(
|
|
|
277
277
|
|
|
278
278
|
const currentElements = elementType === 'node' ? get().nodes : get().edges;
|
|
279
279
|
|
|
280
|
+
// Separate position changes from other changes
|
|
281
|
+
const positionChanges: NodePositionChange[] = [];
|
|
282
|
+
const dimensionChanges: NodeDimensionChange[] = [];
|
|
283
|
+
const otherChanges: (NodeChange | EdgeChange)[] = [];
|
|
284
|
+
|
|
280
285
|
changes.forEach((change) => {
|
|
281
|
-
if (change.type === 'position'
|
|
282
|
-
|
|
286
|
+
if (change.type === 'position') {
|
|
287
|
+
positionChanges.push(change as NodePositionChange);
|
|
288
|
+
} else if (change.type === 'dimensions') {
|
|
289
|
+
dimensionChanges.push(change as NodeDimensionChange);
|
|
283
290
|
} else if (change.type === 'replace') {
|
|
284
291
|
accumulatedResetChanges.push(change as ResetChange & { t: string });
|
|
285
292
|
} else {
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
293
|
+
otherChanges.push(change);
|
|
294
|
+
}
|
|
295
|
+
});
|
|
296
|
+
|
|
297
|
+
// Handle other changes (add, remove, etc.)
|
|
298
|
+
otherChanges.forEach((change) => {
|
|
299
|
+
const historyEvent = createHistoryEvent(
|
|
300
|
+
change,
|
|
301
|
+
currentElements,
|
|
302
|
+
elementType,
|
|
303
|
+
);
|
|
304
|
+
if (historyEvent) {
|
|
305
|
+
newChanges.push(historyEvent.forward);
|
|
306
|
+
newHistory.push(historyEvent);
|
|
295
307
|
}
|
|
296
308
|
});
|
|
297
309
|
|
|
310
|
+
// Handle position changes - apply them but don't add to history
|
|
311
|
+
// History will be handled by onNodeDragEnd for individual nodes
|
|
312
|
+
// For multiple nodes, we need to ensure positions are applied
|
|
313
|
+
if (positionChanges.length > 0 && elementType === 'node') {
|
|
314
|
+
// Apply position changes immediately to ensure they persist
|
|
315
|
+
// Don't add to history here as onNodeDragEnd will handle it
|
|
316
|
+
}
|
|
317
|
+
|
|
298
318
|
if (accumulatedResetChanges.length > 0) {
|
|
299
319
|
const [resetChangesToEmit, historyChanges] = handleAccumulatedResets(
|
|
300
320
|
accumulatedResetChanges,
|
|
@@ -310,6 +330,7 @@ function handleElementChanges(
|
|
|
310
330
|
newChanges.forEach((change) => onChange(change));
|
|
311
331
|
}
|
|
312
332
|
|
|
333
|
+
// Apply ALL changes including position changes to ensure they persist
|
|
313
334
|
const updatedElements =
|
|
314
335
|
elementType === 'node'
|
|
315
336
|
? applyNodeChanges(
|
|
@@ -1,25 +1,113 @@
|
|
|
1
1
|
import React from 'react';
|
|
2
|
-
import { Typography } from '@mui/material';
|
|
3
|
-
import { useNodeTitle, useNodeType } from '../../contexts/CardDataProvider';
|
|
2
|
+
import { Typography, Box } from '@mui/material';
|
|
3
|
+
import { useNodeTitle, useNodeType, useNodeVariant } from '../../contexts/CardDataProvider';
|
|
4
|
+
import { NodeVariant } from '../../types/card-node';
|
|
5
|
+
import {
|
|
6
|
+
ParallelIcon,
|
|
7
|
+
BranchIcon,
|
|
8
|
+
GroupIcon,
|
|
9
|
+
ReturnIcon,
|
|
10
|
+
LoopIcon,
|
|
11
|
+
SwitchIcon,
|
|
12
|
+
TryIcon,
|
|
13
|
+
CatchIcon,
|
|
14
|
+
TryCatchIcon,
|
|
15
|
+
FunctionIcon,
|
|
16
|
+
CallIcon,
|
|
17
|
+
LetIcon,
|
|
18
|
+
SetIcon,
|
|
19
|
+
EmptyNodeIcon,
|
|
20
|
+
EntityIcon,
|
|
21
|
+
} from '@flowuent-labs/molecules';
|
|
22
|
+
|
|
23
|
+
// Icon mapping for different node types
|
|
24
|
+
const getNodeIcon = (type: NodeVariant | string | undefined) => {
|
|
25
|
+
if (!type) return null;
|
|
26
|
+
|
|
27
|
+
const iconStyle = {
|
|
28
|
+
marginRight: '6px',
|
|
29
|
+
display: 'inline-block',
|
|
30
|
+
verticalAlign: 'middle',
|
|
31
|
+
flexShrink: 0,
|
|
32
|
+
};
|
|
33
|
+
|
|
34
|
+
switch (type) {
|
|
35
|
+
case 'parallel':
|
|
36
|
+
return <ParallelIcon size={15} color="#86EFAC" style={iconStyle} />;
|
|
37
|
+
case 'branch':
|
|
38
|
+
return <BranchIcon size={15} color="#86EFAC" style={iconStyle} />;
|
|
39
|
+
case 'group':
|
|
40
|
+
return <GroupIcon size={15} color="#86EFAC" style={iconStyle} />;
|
|
41
|
+
case 'return':
|
|
42
|
+
return <ReturnIcon size={15} color="#86EFAC" style={iconStyle} />;
|
|
43
|
+
case 'loop':
|
|
44
|
+
case 'forLoop':
|
|
45
|
+
return <LoopIcon size={15} color="#86EFAC" style={iconStyle} />;
|
|
46
|
+
case 'switch':
|
|
47
|
+
return <SwitchIcon size={15} color="#86EFAC" style={iconStyle} />;
|
|
48
|
+
case 'try':
|
|
49
|
+
return <TryIcon size={15} color="#86EFAC" style={iconStyle} />;
|
|
50
|
+
case 'catch':
|
|
51
|
+
return <CatchIcon size={15} color="#86EFAC" style={iconStyle} />;
|
|
52
|
+
case 'tryCatch':
|
|
53
|
+
return <TryCatchIcon size={15} color="#86EFAC" style={iconStyle} />;
|
|
54
|
+
case 'function':
|
|
55
|
+
return <FunctionIcon size={15} color="#86EFAC" style={iconStyle} />;
|
|
56
|
+
case 'call':
|
|
57
|
+
return <CallIcon size={15} color="#86EFAC" style={iconStyle} />;
|
|
58
|
+
case 'let':
|
|
59
|
+
return <LetIcon size={15} color="#86EFAC" style={iconStyle} />;
|
|
60
|
+
case 'set':
|
|
61
|
+
return <SetIcon size={15} color="#86EFAC" style={iconStyle} />;
|
|
62
|
+
case 'emptyNode':
|
|
63
|
+
return <EmptyNodeIcon size={15} color="#86EFAC" style={iconStyle} />;
|
|
64
|
+
case 'entity':
|
|
65
|
+
return <EntityIcon size={15} color="#86EFAC" style={iconStyle} />;
|
|
66
|
+
default:
|
|
67
|
+
return null;
|
|
68
|
+
}
|
|
69
|
+
};
|
|
4
70
|
|
|
5
71
|
const NodeTypeDisplay = React.memo(() => {
|
|
6
72
|
const type = useNodeType();
|
|
73
|
+
const variant = useNodeVariant();
|
|
7
74
|
const title = useNodeTitle();
|
|
75
|
+
// Get type from data.type or data.variant as fallback
|
|
76
|
+
const nodeType = (type || variant) as NodeVariant | string | undefined;
|
|
77
|
+
const icon = getNodeIcon(nodeType as NodeVariant);
|
|
78
|
+
|
|
8
79
|
return (
|
|
9
|
-
<
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
80
|
+
<Box sx={{ display: 'flex', alignItems: 'center', gap: 0.5, flexWrap: 'nowrap' }}>
|
|
81
|
+
{icon && (
|
|
82
|
+
<Box
|
|
83
|
+
component="span"
|
|
84
|
+
sx={{
|
|
85
|
+
display: 'inline-flex',
|
|
86
|
+
alignItems: 'center',
|
|
87
|
+
justifyContent: 'center',
|
|
88
|
+
flexShrink: 0,
|
|
89
|
+
lineHeight: 1,
|
|
90
|
+
}}
|
|
91
|
+
>
|
|
92
|
+
{icon}
|
|
93
|
+
</Box>
|
|
21
94
|
)}
|
|
22
|
-
|
|
95
|
+
<Typography
|
|
96
|
+
sx={{
|
|
97
|
+
textTransform: 'capitalize',
|
|
98
|
+
fontStyle: 'italic',
|
|
99
|
+
fontSize: 12,
|
|
100
|
+
color: '#FFFFFF',
|
|
101
|
+
display: 'inline-flex',
|
|
102
|
+
alignItems: 'center',
|
|
103
|
+
}}
|
|
104
|
+
>
|
|
105
|
+
{`[${nodeType || 'unknown'}]`}
|
|
106
|
+
{(title === 'Then' || title === 'Else') && (
|
|
107
|
+
<span>{': ' + title}</span>
|
|
108
|
+
)}
|
|
109
|
+
</Typography>
|
|
110
|
+
</Box>
|
|
23
111
|
);
|
|
24
112
|
});
|
|
25
113
|
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
// Re-export all icons from molecules package
|
|
2
|
+
export {
|
|
3
|
+
ParallelIcon,
|
|
4
|
+
BranchIcon,
|
|
5
|
+
GroupIcon,
|
|
6
|
+
ReturnIcon,
|
|
7
|
+
LoopIcon,
|
|
8
|
+
SwitchIcon,
|
|
9
|
+
TryIcon,
|
|
10
|
+
CatchIcon,
|
|
11
|
+
TryCatchIcon,
|
|
12
|
+
FunctionIcon,
|
|
13
|
+
CallIcon,
|
|
14
|
+
LetIcon,
|
|
15
|
+
SetIcon,
|
|
16
|
+
EmptyNodeIcon,
|
|
17
|
+
EntityIcon,
|
|
18
|
+
StartNodeIcon,
|
|
19
|
+
NavigationIcon,
|
|
20
|
+
} from '@flowuent-labs/molecules';
|
|
21
|
+
|
|
22
|
+
// Re-export types for backward compatibility
|
|
23
|
+
export type { SvgIconProps as IconProps } from '@flowuent-labs/molecules';
|