@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
|
@@ -1,370 +1,370 @@
|
|
|
1
|
-
import { memo, useCallback, useState } from 'react';
|
|
2
|
-
import { useTranslation } from 'react-i18next';
|
|
3
|
-
|
|
4
|
-
import ExpressionInput from '../../atoms/ExpressionInput';
|
|
5
|
-
import { useDiagram } from '
|
|
6
|
-
import { FunctionSignature } from '../../types/FunctionSignature';
|
|
7
|
-
import { RiArrowDownSLine } from 'react-icons/ri';
|
|
8
|
-
import MenuList from '@mui/material/MenuList';
|
|
9
|
-
import MenuItem from '@mui/material/MenuItem';
|
|
10
|
-
import ListItemText from '@mui/material/ListItemText';
|
|
11
|
-
import Typography from '@mui/material/Typography';
|
|
12
|
-
import Button from '@mui/material/Button';
|
|
13
|
-
import Dialog from '@mui/material/Dialog';
|
|
14
|
-
import DialogActions from '@mui/material/DialogActions';
|
|
15
|
-
import DialogContent from '@mui/material/DialogContent';
|
|
16
|
-
import DialogTitle from '@mui/material/DialogTitle';
|
|
17
|
-
import Stack from '@mui/material/Stack';
|
|
18
|
-
import { Box, Divider, IconButton } from '@mui/material';
|
|
19
|
-
import { useDeleteFunction } from '../../contexts/DiagramProvider';
|
|
20
|
-
import DeleteIcon from '@mui/icons-material/Delete';
|
|
21
|
-
import DialogContentText from '@mui/material/DialogContentText';
|
|
22
|
-
import {
|
|
23
|
-
CallFormData,
|
|
24
|
-
CallFormProps,
|
|
25
|
-
CustomFunctionSelectProps,
|
|
26
|
-
FunctionArgument,
|
|
27
|
-
FunctionReturnValue,
|
|
28
|
-
} from '../../types/function-execution';
|
|
29
|
-
import { Expression } from '../../types/ndoe-form-types';
|
|
30
|
-
import { useFormValidation } from '../../hooks/useFormValidation';
|
|
31
|
-
import ValidationError from '../../atoms/ValidationError';
|
|
32
|
-
|
|
33
|
-
// Utility function to handle function execution and return value
|
|
34
|
-
const executeFunctionAndGetReturnValue = (
|
|
35
|
-
functionId: string,
|
|
36
|
-
returnValueName: string,
|
|
37
|
-
args: FunctionArgument[],
|
|
38
|
-
availableFunctions: FunctionSignature[] | undefined,
|
|
39
|
-
): FunctionReturnValue => {
|
|
40
|
-
if (!functionId || !returnValueName || !args || !args.every(arg => arg.value !== '')) {
|
|
41
|
-
return {};
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
const selectedFunction = availableFunctions?.find(f => f.id === functionId);
|
|
45
|
-
if (!selectedFunction) {
|
|
46
|
-
return {};
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
try {
|
|
50
|
-
const argNames = selectedFunction.args.map(arg => arg.name);
|
|
51
|
-
const argValues = args.map(arg => {
|
|
52
|
-
if (typeof arg.value === 'string') {
|
|
53
|
-
try {
|
|
54
|
-
return JSON.parse(arg.value);
|
|
55
|
-
} catch {
|
|
56
|
-
return arg.value;
|
|
57
|
-
}
|
|
58
|
-
}
|
|
59
|
-
// If it's an Expression object, convert it to a string representation
|
|
60
|
-
return String(arg.value);
|
|
61
|
-
});
|
|
62
|
-
// eslint-disable-next-line no-new-func
|
|
63
|
-
const fn = new Function(...argNames, selectedFunction.code);
|
|
64
|
-
const result = fn(...argValues);
|
|
65
|
-
return { [returnValueName]: result };
|
|
66
|
-
} catch (err) {
|
|
67
|
-
return { [returnValueName]: 'Error: ' + (err instanceof Error ? err.message : String(err)) };
|
|
68
|
-
}
|
|
69
|
-
};
|
|
70
|
-
|
|
71
|
-
const CustomFunctionSelect = memo(({
|
|
72
|
-
options,
|
|
73
|
-
value,
|
|
74
|
-
onChange,
|
|
75
|
-
}: CustomFunctionSelectProps) => {
|
|
76
|
-
const [open, setOpen] = useState(false);
|
|
77
|
-
const [deleteDialogOpen, setDeleteDialogOpen] = useState(false);
|
|
78
|
-
const [deleteFuncId, setDeleteFuncId] = useState<string | null>(null);
|
|
79
|
-
const deleteFunction = useDeleteFunction();
|
|
80
|
-
const { t } = useTranslation();
|
|
81
|
-
|
|
82
|
-
const handleClickOpen = useCallback(() => {
|
|
83
|
-
setOpen(true);
|
|
84
|
-
}, []);
|
|
85
|
-
|
|
86
|
-
const handleClose = useCallback(() => {
|
|
87
|
-
setOpen(false);
|
|
88
|
-
}, []);
|
|
89
|
-
|
|
90
|
-
const handleSelectFunction = useCallback(
|
|
91
|
-
(func: FunctionSignature) => {
|
|
92
|
-
onChange(func.id);
|
|
93
|
-
handleClose();
|
|
94
|
-
},
|
|
95
|
-
[onChange, handleClose],
|
|
96
|
-
);
|
|
97
|
-
|
|
98
|
-
const handleDelete = useCallback((id: string) => {
|
|
99
|
-
setDeleteFuncId(id);
|
|
100
|
-
setDeleteDialogOpen(true);
|
|
101
|
-
}, []);
|
|
102
|
-
|
|
103
|
-
const handleConfirmDelete = useCallback(() => {
|
|
104
|
-
if (deleteFuncId) {
|
|
105
|
-
deleteFunction(deleteFuncId);
|
|
106
|
-
}
|
|
107
|
-
setDeleteDialogOpen(false);
|
|
108
|
-
setDeleteFuncId(null);
|
|
109
|
-
}, [deleteFuncId, deleteFunction]);
|
|
110
|
-
|
|
111
|
-
const handleCloseDeleteDialog = useCallback(() => {
|
|
112
|
-
setDeleteDialogOpen(false);
|
|
113
|
-
}, []);
|
|
114
|
-
|
|
115
|
-
const handleDeleteClick = useCallback((e: React.MouseEvent, id: string) => {
|
|
116
|
-
e.stopPropagation();
|
|
117
|
-
handleDelete(id);
|
|
118
|
-
}, [handleDelete]);
|
|
119
|
-
|
|
120
|
-
return (
|
|
121
|
-
<>
|
|
122
|
-
<Button
|
|
123
|
-
onClick={handleClickOpen}
|
|
124
|
-
variant="outlined"
|
|
125
|
-
endIcon={<RiArrowDownSLine />}
|
|
126
|
-
fullWidth
|
|
127
|
-
sx={{
|
|
128
|
-
backgroundColor: '#232323',
|
|
129
|
-
color: '#FFFFFF',
|
|
130
|
-
border: '1px solid #8E8E93',
|
|
131
|
-
'&:hover': { border: '1px solid #000000' },
|
|
132
|
-
}}
|
|
133
|
-
>
|
|
134
|
-
{value !== null
|
|
135
|
-
? options.find((func) => func.id === value)?.name ||
|
|
136
|
-
t('callForm.selectFunction')
|
|
137
|
-
: t('callForm.selectFunction')}
|
|
138
|
-
</Button>
|
|
139
|
-
<Dialog
|
|
140
|
-
open={open}
|
|
141
|
-
onClose={handleClose}
|
|
142
|
-
fullWidth
|
|
143
|
-
maxWidth="sm"
|
|
144
|
-
sx={{
|
|
145
|
-
'& .MuiDialog-paper': {
|
|
146
|
-
backgroundColor: '#232323',
|
|
147
|
-
color: '#FFFFFF',
|
|
148
|
-
},
|
|
149
|
-
}}
|
|
150
|
-
>
|
|
151
|
-
<DialogTitle>
|
|
152
|
-
{t('callForm.selectFunction')}
|
|
153
|
-
</DialogTitle>
|
|
154
|
-
<DialogContent>
|
|
155
|
-
<MenuList>
|
|
156
|
-
{options.map((func) => (
|
|
157
|
-
<MenuItem
|
|
158
|
-
key={func.id}
|
|
159
|
-
onClick={() => handleSelectFunction(func)}
|
|
160
|
-
>
|
|
161
|
-
<Box sx={{ display: 'flex', alignItems: 'center', width: '100%' }}>
|
|
162
|
-
<ListItemText sx={{ flex: 1 }}>
|
|
163
|
-
{`${func.name}(${func.args.map(a => a.name).join(', ')})`}
|
|
164
|
-
</ListItemText>
|
|
165
|
-
<IconButton
|
|
166
|
-
size="small"
|
|
167
|
-
onClick={(e) => handleDeleteClick(e, func.id)}
|
|
168
|
-
sx={{ color: '#fff' }}
|
|
169
|
-
>
|
|
170
|
-
<DeleteIcon fontSize="small" />
|
|
171
|
-
</IconButton>
|
|
172
|
-
</Box>
|
|
173
|
-
</MenuItem>
|
|
174
|
-
))}
|
|
175
|
-
</MenuList>
|
|
176
|
-
</DialogContent>
|
|
177
|
-
<DialogActions>
|
|
178
|
-
<Button onClick={handleClose} color="secondary" sx={{ color: '#fff' }}>{t('button.cancel')}</Button>
|
|
179
|
-
</DialogActions>
|
|
180
|
-
</Dialog>
|
|
181
|
-
<Dialog
|
|
182
|
-
open={deleteDialogOpen}
|
|
183
|
-
onClose={handleCloseDeleteDialog}
|
|
184
|
-
maxWidth="xs"
|
|
185
|
-
PaperProps={{ style: { background: '#232323', color: '#fff' } }}
|
|
186
|
-
>
|
|
187
|
-
<DialogTitle>Delete Function</DialogTitle>
|
|
188
|
-
<DialogContent>
|
|
189
|
-
<DialogContentText sx={{ color: '#fff' }}>
|
|
190
|
-
Are you sure you want to delete this function?
|
|
191
|
-
</DialogContentText>
|
|
192
|
-
</DialogContent>
|
|
193
|
-
<DialogActions>
|
|
194
|
-
<Button onClick={handleCloseDeleteDialog} color="secondary" sx={{ color: '#fff' }}>Cancel</Button>
|
|
195
|
-
<Button onClick={handleConfirmDelete} color="error" variant="contained">Delete</Button>
|
|
196
|
-
</DialogActions>
|
|
197
|
-
</Dialog>
|
|
198
|
-
</>
|
|
199
|
-
);
|
|
200
|
-
});
|
|
201
|
-
|
|
202
|
-
export const CallForm = memo(({
|
|
203
|
-
formData,
|
|
204
|
-
handleMultipleInputChanges,
|
|
205
|
-
}: CallFormProps) => {
|
|
206
|
-
const availableFunctions = useDiagram((state) => state.availableFunctions);
|
|
207
|
-
const { t } = useTranslation();
|
|
208
|
-
|
|
209
|
-
const {
|
|
210
|
-
validationState,
|
|
211
|
-
setFieldError,
|
|
212
|
-
setFieldTouched,
|
|
213
|
-
shouldShowFieldError,
|
|
214
|
-
getFieldError,
|
|
215
|
-
clearFieldError,
|
|
216
|
-
} = useFormValidation();
|
|
217
|
-
|
|
218
|
-
const handleFunctionChange = useCallback(
|
|
219
|
-
(funcId: string) => {
|
|
220
|
-
const selectedFunction = availableFunctions?.find((f) => f.id === funcId);
|
|
221
|
-
const args = selectedFunction?.args || [];
|
|
222
|
-
handleMultipleInputChanges({
|
|
223
|
-
returnValueName: '',
|
|
224
|
-
functionId: funcId,
|
|
225
|
-
code: selectedFunction?.code || '',
|
|
226
|
-
functionName: selectedFunction?.name || '',
|
|
227
|
-
args: args.map((arg) => ({
|
|
228
|
-
name: typeof arg === 'string' ? arg : arg.name,
|
|
229
|
-
value: '' as string | Expression,
|
|
230
|
-
dataType: 'String'
|
|
231
|
-
})),
|
|
232
|
-
});
|
|
233
|
-
},
|
|
234
|
-
[availableFunctions, handleMultipleInputChanges],
|
|
235
|
-
);
|
|
236
|
-
|
|
237
|
-
const handleArgChange = useCallback(
|
|
238
|
-
(index: number, value: string | Expression) => {
|
|
239
|
-
const updatedArgs = [...(formData.args || [])];
|
|
240
|
-
updatedArgs[index] = { ...updatedArgs[index], value };
|
|
241
|
-
handleMultipleInputChanges({
|
|
242
|
-
args: updatedArgs,
|
|
243
|
-
});
|
|
244
|
-
},
|
|
245
|
-
[formData.args, handleMultipleInputChanges],
|
|
246
|
-
);
|
|
247
|
-
|
|
248
|
-
// Find the selected function to get argument types
|
|
249
|
-
const selectedFunction = availableFunctions?.find((f) => f.id === formData.functionId);
|
|
250
|
-
|
|
251
|
-
return (
|
|
252
|
-
<Stack spacing={1}>
|
|
253
|
-
<Stack spacing={3} direction={'column'} sx={{ width: '100%' }}>
|
|
254
|
-
{availableFunctions && (
|
|
255
|
-
<CustomFunctionSelect
|
|
256
|
-
options={availableFunctions}
|
|
257
|
-
value={formData.functionId || null}
|
|
258
|
-
onChange={(funcId: string) => {
|
|
259
|
-
handleFunctionChange(funcId);
|
|
260
|
-
}}
|
|
261
|
-
/>
|
|
262
|
-
)}
|
|
263
|
-
{getFieldError('functionId') && shouldShowFieldError('functionId') && (
|
|
264
|
-
<ValidationError
|
|
265
|
-
error={getFieldError('functionId')}
|
|
266
|
-
show={true}
|
|
267
|
-
/>
|
|
268
|
-
)}
|
|
269
|
-
</Stack>
|
|
270
|
-
<Divider variant={'inset'} />
|
|
271
|
-
{formData.functionId && (
|
|
272
|
-
<div>
|
|
273
|
-
{formData.args?.map((arg, index) => (
|
|
274
|
-
// Find the type for this argument from the selected function
|
|
275
|
-
(() => {
|
|
276
|
-
const argType = selectedFunction?.args?.find(a => a.name === arg.name)?.type || '';
|
|
277
|
-
return (
|
|
278
|
-
<Stack
|
|
279
|
-
spacing={1}
|
|
280
|
-
direction={'row'}
|
|
281
|
-
sx={{ alignItems: 'center', mb: 2 }}
|
|
282
|
-
key={arg.name}
|
|
283
|
-
>
|
|
284
|
-
<Typography
|
|
285
|
-
variant="body1"
|
|
286
|
-
sx={{ width: '80px', fontSize: '12px', color: '#FFFFFF', fontWeight: 'bold' }}
|
|
287
|
-
>
|
|
288
|
-
{arg.name}:
|
|
289
|
-
</Typography>
|
|
290
|
-
<ExpressionInput
|
|
291
|
-
label=""
|
|
292
|
-
value={arg.value as string}
|
|
293
|
-
onChange={(newValue) => {
|
|
294
|
-
handleArgChange(index, newValue);
|
|
295
|
-
}}
|
|
296
|
-
showDataTypeDropdown={true}
|
|
297
|
-
dataType={arg.dataType || 'String'}
|
|
298
|
-
onDataTypeChange={(dataType) => {
|
|
299
|
-
const updatedArgs = [...(formData.args || [])];
|
|
300
|
-
updatedArgs[index] = { ...updatedArgs[index], dataType };
|
|
301
|
-
handleMultipleInputChanges({
|
|
302
|
-
args: updatedArgs,
|
|
303
|
-
});
|
|
304
|
-
}}
|
|
305
|
-
onValidationChange={(isValid, errors) => {
|
|
306
|
-
// Handle validation from ExpressionInput
|
|
307
|
-
if (!isValid && errors.length > 0) {
|
|
308
|
-
setFieldError(`args[${index}].value`, errors[0]);
|
|
309
|
-
} else {
|
|
310
|
-
clearFieldError(`args[${index}].value`);
|
|
311
|
-
}
|
|
312
|
-
}}
|
|
313
|
-
/>
|
|
314
|
-
{getFieldError(`args[${index}].value`) && (
|
|
315
|
-
<ValidationError
|
|
316
|
-
error={getFieldError(`args[${index}].value`)}
|
|
317
|
-
show={true}
|
|
318
|
-
/>
|
|
319
|
-
)}
|
|
320
|
-
</Stack>
|
|
321
|
-
);
|
|
322
|
-
})()
|
|
323
|
-
))}
|
|
324
|
-
</div>
|
|
325
|
-
)}
|
|
326
|
-
<Divider variant={'inset'} />
|
|
327
|
-
{formData.functionId && (
|
|
328
|
-
<Box>
|
|
329
|
-
<label style={{ fontSize: '12px', color: '#FFFFFF', fontWeight: 'bold' }}>
|
|
330
|
-
{t('callForm.returnValueName')}
|
|
331
|
-
</label>
|
|
332
|
-
<ExpressionInput
|
|
333
|
-
label=""
|
|
334
|
-
value={formData.returnValueName as string}
|
|
335
|
-
onChange={(newValue) => {
|
|
336
|
-
handleMultipleInputChanges({
|
|
337
|
-
returnValueName: newValue,
|
|
338
|
-
});
|
|
339
|
-
}}
|
|
340
|
-
showDataTypeDropdown={true}
|
|
341
|
-
dataType={formData.returnValueDataType || 'String'}
|
|
342
|
-
onDataTypeChange={(dataType) => {
|
|
343
|
-
handleMultipleInputChanges({
|
|
344
|
-
returnValueDataType: dataType,
|
|
345
|
-
});
|
|
346
|
-
}}
|
|
347
|
-
onValidationChange={(isValid, errors) => {
|
|
348
|
-
// Handle validation from ExpressionInput
|
|
349
|
-
if (!isValid && errors.length > 0) {
|
|
350
|
-
setFieldError('returnValueName', errors[0]);
|
|
351
|
-
} else {
|
|
352
|
-
clearFieldError('returnValueName');
|
|
353
|
-
}
|
|
354
|
-
}}
|
|
355
|
-
/>
|
|
356
|
-
{getFieldError('returnValueName') && (
|
|
357
|
-
<ValidationError
|
|
358
|
-
error={getFieldError('returnValueName')}
|
|
359
|
-
show={true}
|
|
360
|
-
/>
|
|
361
|
-
)}
|
|
362
|
-
</Box>
|
|
363
|
-
)}
|
|
364
|
-
</Stack>
|
|
365
|
-
);
|
|
366
|
-
});
|
|
367
|
-
|
|
368
|
-
// Add display names for memoized components
|
|
369
|
-
CustomFunctionSelect.displayName = 'CustomFunctionSelect';
|
|
370
|
-
CallForm.displayName = 'CallForm';
|
|
1
|
+
import { memo, useCallback, useState } from 'react';
|
|
2
|
+
import { useTranslation } from 'react-i18next';
|
|
3
|
+
|
|
4
|
+
import ExpressionInput from '../../atoms/ExpressionInput';
|
|
5
|
+
import { useDiagram } from '@flowuent-labs/diagrams';
|
|
6
|
+
import { FunctionSignature } from '../../types/FunctionSignature';
|
|
7
|
+
import { RiArrowDownSLine } from 'react-icons/ri';
|
|
8
|
+
import MenuList from '@mui/material/MenuList';
|
|
9
|
+
import MenuItem from '@mui/material/MenuItem';
|
|
10
|
+
import ListItemText from '@mui/material/ListItemText';
|
|
11
|
+
import Typography from '@mui/material/Typography';
|
|
12
|
+
import Button from '@mui/material/Button';
|
|
13
|
+
import Dialog from '@mui/material/Dialog';
|
|
14
|
+
import DialogActions from '@mui/material/DialogActions';
|
|
15
|
+
import DialogContent from '@mui/material/DialogContent';
|
|
16
|
+
import DialogTitle from '@mui/material/DialogTitle';
|
|
17
|
+
import Stack from '@mui/material/Stack';
|
|
18
|
+
import { Box, Divider, IconButton } from '@mui/material';
|
|
19
|
+
import { useDeleteFunction } from '../../contexts/DiagramProvider';
|
|
20
|
+
import DeleteIcon from '@mui/icons-material/Delete';
|
|
21
|
+
import DialogContentText from '@mui/material/DialogContentText';
|
|
22
|
+
import {
|
|
23
|
+
CallFormData,
|
|
24
|
+
CallFormProps,
|
|
25
|
+
CustomFunctionSelectProps,
|
|
26
|
+
FunctionArgument,
|
|
27
|
+
FunctionReturnValue,
|
|
28
|
+
} from '../../types/function-execution';
|
|
29
|
+
import { Expression } from '../../types/ndoe-form-types';
|
|
30
|
+
import { useFormValidation } from '../../hooks/useFormValidation';
|
|
31
|
+
import ValidationError from '../../atoms/ValidationError';
|
|
32
|
+
|
|
33
|
+
// Utility function to handle function execution and return value
|
|
34
|
+
const executeFunctionAndGetReturnValue = (
|
|
35
|
+
functionId: string,
|
|
36
|
+
returnValueName: string,
|
|
37
|
+
args: FunctionArgument[],
|
|
38
|
+
availableFunctions: FunctionSignature[] | undefined,
|
|
39
|
+
): FunctionReturnValue => {
|
|
40
|
+
if (!functionId || !returnValueName || !args || !args.every(arg => arg.value !== '')) {
|
|
41
|
+
return {};
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
const selectedFunction = availableFunctions?.find(f => f.id === functionId);
|
|
45
|
+
if (!selectedFunction) {
|
|
46
|
+
return {};
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
try {
|
|
50
|
+
const argNames = selectedFunction.args.map(arg => arg.name);
|
|
51
|
+
const argValues = args.map(arg => {
|
|
52
|
+
if (typeof arg.value === 'string') {
|
|
53
|
+
try {
|
|
54
|
+
return JSON.parse(arg.value);
|
|
55
|
+
} catch {
|
|
56
|
+
return arg.value;
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
// If it's an Expression object, convert it to a string representation
|
|
60
|
+
return String(arg.value);
|
|
61
|
+
});
|
|
62
|
+
// eslint-disable-next-line no-new-func
|
|
63
|
+
const fn = new Function(...argNames, selectedFunction.code);
|
|
64
|
+
const result = fn(...argValues);
|
|
65
|
+
return { [returnValueName]: result };
|
|
66
|
+
} catch (err) {
|
|
67
|
+
return { [returnValueName]: 'Error: ' + (err instanceof Error ? err.message : String(err)) };
|
|
68
|
+
}
|
|
69
|
+
};
|
|
70
|
+
|
|
71
|
+
const CustomFunctionSelect = memo(({
|
|
72
|
+
options,
|
|
73
|
+
value,
|
|
74
|
+
onChange,
|
|
75
|
+
}: CustomFunctionSelectProps) => {
|
|
76
|
+
const [open, setOpen] = useState(false);
|
|
77
|
+
const [deleteDialogOpen, setDeleteDialogOpen] = useState(false);
|
|
78
|
+
const [deleteFuncId, setDeleteFuncId] = useState<string | null>(null);
|
|
79
|
+
const deleteFunction = useDeleteFunction();
|
|
80
|
+
const { t } = useTranslation();
|
|
81
|
+
|
|
82
|
+
const handleClickOpen = useCallback(() => {
|
|
83
|
+
setOpen(true);
|
|
84
|
+
}, []);
|
|
85
|
+
|
|
86
|
+
const handleClose = useCallback(() => {
|
|
87
|
+
setOpen(false);
|
|
88
|
+
}, []);
|
|
89
|
+
|
|
90
|
+
const handleSelectFunction = useCallback(
|
|
91
|
+
(func: FunctionSignature) => {
|
|
92
|
+
onChange(func.id);
|
|
93
|
+
handleClose();
|
|
94
|
+
},
|
|
95
|
+
[onChange, handleClose],
|
|
96
|
+
);
|
|
97
|
+
|
|
98
|
+
const handleDelete = useCallback((id: string) => {
|
|
99
|
+
setDeleteFuncId(id);
|
|
100
|
+
setDeleteDialogOpen(true);
|
|
101
|
+
}, []);
|
|
102
|
+
|
|
103
|
+
const handleConfirmDelete = useCallback(() => {
|
|
104
|
+
if (deleteFuncId) {
|
|
105
|
+
deleteFunction(deleteFuncId);
|
|
106
|
+
}
|
|
107
|
+
setDeleteDialogOpen(false);
|
|
108
|
+
setDeleteFuncId(null);
|
|
109
|
+
}, [deleteFuncId, deleteFunction]);
|
|
110
|
+
|
|
111
|
+
const handleCloseDeleteDialog = useCallback(() => {
|
|
112
|
+
setDeleteDialogOpen(false);
|
|
113
|
+
}, []);
|
|
114
|
+
|
|
115
|
+
const handleDeleteClick = useCallback((e: React.MouseEvent, id: string) => {
|
|
116
|
+
e.stopPropagation();
|
|
117
|
+
handleDelete(id);
|
|
118
|
+
}, [handleDelete]);
|
|
119
|
+
|
|
120
|
+
return (
|
|
121
|
+
<>
|
|
122
|
+
<Button
|
|
123
|
+
onClick={handleClickOpen}
|
|
124
|
+
variant="outlined"
|
|
125
|
+
endIcon={<RiArrowDownSLine />}
|
|
126
|
+
fullWidth
|
|
127
|
+
sx={{
|
|
128
|
+
backgroundColor: '#232323',
|
|
129
|
+
color: '#FFFFFF',
|
|
130
|
+
border: '1px solid #8E8E93',
|
|
131
|
+
'&:hover': { border: '1px solid #000000' },
|
|
132
|
+
}}
|
|
133
|
+
>
|
|
134
|
+
{value !== null
|
|
135
|
+
? options.find((func) => func.id === value)?.name ||
|
|
136
|
+
t('callForm.selectFunction')
|
|
137
|
+
: t('callForm.selectFunction')}
|
|
138
|
+
</Button>
|
|
139
|
+
<Dialog
|
|
140
|
+
open={open}
|
|
141
|
+
onClose={handleClose}
|
|
142
|
+
fullWidth
|
|
143
|
+
maxWidth="sm"
|
|
144
|
+
sx={{
|
|
145
|
+
'& .MuiDialog-paper': {
|
|
146
|
+
backgroundColor: '#232323',
|
|
147
|
+
color: '#FFFFFF',
|
|
148
|
+
},
|
|
149
|
+
}}
|
|
150
|
+
>
|
|
151
|
+
<DialogTitle>
|
|
152
|
+
{t('callForm.selectFunction')}
|
|
153
|
+
</DialogTitle>
|
|
154
|
+
<DialogContent>
|
|
155
|
+
<MenuList>
|
|
156
|
+
{options.map((func) => (
|
|
157
|
+
<MenuItem
|
|
158
|
+
key={func.id}
|
|
159
|
+
onClick={() => handleSelectFunction(func)}
|
|
160
|
+
>
|
|
161
|
+
<Box sx={{ display: 'flex', alignItems: 'center', width: '100%' }}>
|
|
162
|
+
<ListItemText sx={{ flex: 1 }}>
|
|
163
|
+
{`${func.name}(${func.args.map(a => a.name).join(', ')})`}
|
|
164
|
+
</ListItemText>
|
|
165
|
+
<IconButton
|
|
166
|
+
size="small"
|
|
167
|
+
onClick={(e) => handleDeleteClick(e, func.id)}
|
|
168
|
+
sx={{ color: '#fff' }}
|
|
169
|
+
>
|
|
170
|
+
<DeleteIcon fontSize="small" />
|
|
171
|
+
</IconButton>
|
|
172
|
+
</Box>
|
|
173
|
+
</MenuItem>
|
|
174
|
+
))}
|
|
175
|
+
</MenuList>
|
|
176
|
+
</DialogContent>
|
|
177
|
+
<DialogActions>
|
|
178
|
+
<Button onClick={handleClose} color="secondary" sx={{ color: '#fff' }}>{t('button.cancel')}</Button>
|
|
179
|
+
</DialogActions>
|
|
180
|
+
</Dialog>
|
|
181
|
+
<Dialog
|
|
182
|
+
open={deleteDialogOpen}
|
|
183
|
+
onClose={handleCloseDeleteDialog}
|
|
184
|
+
maxWidth="xs"
|
|
185
|
+
PaperProps={{ style: { background: '#232323', color: '#fff' } }}
|
|
186
|
+
>
|
|
187
|
+
<DialogTitle>Delete Function</DialogTitle>
|
|
188
|
+
<DialogContent>
|
|
189
|
+
<DialogContentText sx={{ color: '#fff' }}>
|
|
190
|
+
Are you sure you want to delete this function?
|
|
191
|
+
</DialogContentText>
|
|
192
|
+
</DialogContent>
|
|
193
|
+
<DialogActions>
|
|
194
|
+
<Button onClick={handleCloseDeleteDialog} color="secondary" sx={{ color: '#fff' }}>Cancel</Button>
|
|
195
|
+
<Button onClick={handleConfirmDelete} color="error" variant="contained">Delete</Button>
|
|
196
|
+
</DialogActions>
|
|
197
|
+
</Dialog>
|
|
198
|
+
</>
|
|
199
|
+
);
|
|
200
|
+
});
|
|
201
|
+
|
|
202
|
+
export const CallForm = memo(({
|
|
203
|
+
formData,
|
|
204
|
+
handleMultipleInputChanges,
|
|
205
|
+
}: CallFormProps) => {
|
|
206
|
+
const availableFunctions = useDiagram((state) => state.availableFunctions);
|
|
207
|
+
const { t } = useTranslation();
|
|
208
|
+
|
|
209
|
+
const {
|
|
210
|
+
validationState,
|
|
211
|
+
setFieldError,
|
|
212
|
+
setFieldTouched,
|
|
213
|
+
shouldShowFieldError,
|
|
214
|
+
getFieldError,
|
|
215
|
+
clearFieldError,
|
|
216
|
+
} = useFormValidation();
|
|
217
|
+
|
|
218
|
+
const handleFunctionChange = useCallback(
|
|
219
|
+
(funcId: string) => {
|
|
220
|
+
const selectedFunction = availableFunctions?.find((f) => f.id === funcId);
|
|
221
|
+
const args = selectedFunction?.args || [];
|
|
222
|
+
handleMultipleInputChanges({
|
|
223
|
+
returnValueName: '',
|
|
224
|
+
functionId: funcId,
|
|
225
|
+
code: selectedFunction?.code || '',
|
|
226
|
+
functionName: selectedFunction?.name || '',
|
|
227
|
+
args: args.map((arg) => ({
|
|
228
|
+
name: typeof arg === 'string' ? arg : arg.name,
|
|
229
|
+
value: '' as string | Expression,
|
|
230
|
+
dataType: 'String'
|
|
231
|
+
})),
|
|
232
|
+
});
|
|
233
|
+
},
|
|
234
|
+
[availableFunctions, handleMultipleInputChanges],
|
|
235
|
+
);
|
|
236
|
+
|
|
237
|
+
const handleArgChange = useCallback(
|
|
238
|
+
(index: number, value: string | Expression) => {
|
|
239
|
+
const updatedArgs = [...(formData.args || [])];
|
|
240
|
+
updatedArgs[index] = { ...updatedArgs[index], value };
|
|
241
|
+
handleMultipleInputChanges({
|
|
242
|
+
args: updatedArgs,
|
|
243
|
+
});
|
|
244
|
+
},
|
|
245
|
+
[formData.args, handleMultipleInputChanges],
|
|
246
|
+
);
|
|
247
|
+
|
|
248
|
+
// Find the selected function to get argument types
|
|
249
|
+
const selectedFunction = availableFunctions?.find((f) => f.id === formData.functionId);
|
|
250
|
+
|
|
251
|
+
return (
|
|
252
|
+
<Stack spacing={1}>
|
|
253
|
+
<Stack spacing={3} direction={'column'} sx={{ width: '100%' }}>
|
|
254
|
+
{availableFunctions && (
|
|
255
|
+
<CustomFunctionSelect
|
|
256
|
+
options={availableFunctions}
|
|
257
|
+
value={formData.functionId || null}
|
|
258
|
+
onChange={(funcId: string) => {
|
|
259
|
+
handleFunctionChange(funcId);
|
|
260
|
+
}}
|
|
261
|
+
/>
|
|
262
|
+
)}
|
|
263
|
+
{getFieldError('functionId') && shouldShowFieldError('functionId') && (
|
|
264
|
+
<ValidationError
|
|
265
|
+
error={getFieldError('functionId')}
|
|
266
|
+
show={true}
|
|
267
|
+
/>
|
|
268
|
+
)}
|
|
269
|
+
</Stack>
|
|
270
|
+
<Divider variant={'inset'} />
|
|
271
|
+
{formData.functionId && (
|
|
272
|
+
<div>
|
|
273
|
+
{formData.args?.map((arg, index) => (
|
|
274
|
+
// Find the type for this argument from the selected function
|
|
275
|
+
(() => {
|
|
276
|
+
const argType = selectedFunction?.args?.find(a => a.name === arg.name)?.type || '';
|
|
277
|
+
return (
|
|
278
|
+
<Stack
|
|
279
|
+
spacing={1}
|
|
280
|
+
direction={'row'}
|
|
281
|
+
sx={{ alignItems: 'center', mb: 2 }}
|
|
282
|
+
key={arg.name}
|
|
283
|
+
>
|
|
284
|
+
<Typography
|
|
285
|
+
variant="body1"
|
|
286
|
+
sx={{ width: '80px', fontSize: '12px', color: '#FFFFFF', fontWeight: 'bold' }}
|
|
287
|
+
>
|
|
288
|
+
{arg.name}:
|
|
289
|
+
</Typography>
|
|
290
|
+
<ExpressionInput
|
|
291
|
+
label=""
|
|
292
|
+
value={arg.value as string}
|
|
293
|
+
onChange={(newValue) => {
|
|
294
|
+
handleArgChange(index, newValue);
|
|
295
|
+
}}
|
|
296
|
+
showDataTypeDropdown={true}
|
|
297
|
+
dataType={arg.dataType || 'String'}
|
|
298
|
+
onDataTypeChange={(dataType) => {
|
|
299
|
+
const updatedArgs = [...(formData.args || [])];
|
|
300
|
+
updatedArgs[index] = { ...updatedArgs[index], dataType };
|
|
301
|
+
handleMultipleInputChanges({
|
|
302
|
+
args: updatedArgs,
|
|
303
|
+
});
|
|
304
|
+
}}
|
|
305
|
+
onValidationChange={(isValid, errors) => {
|
|
306
|
+
// Handle validation from ExpressionInput
|
|
307
|
+
if (!isValid && errors.length > 0) {
|
|
308
|
+
setFieldError(`args[${index}].value`, errors[0]);
|
|
309
|
+
} else {
|
|
310
|
+
clearFieldError(`args[${index}].value`);
|
|
311
|
+
}
|
|
312
|
+
}}
|
|
313
|
+
/>
|
|
314
|
+
{getFieldError(`args[${index}].value`) && (
|
|
315
|
+
<ValidationError
|
|
316
|
+
error={getFieldError(`args[${index}].value`)}
|
|
317
|
+
show={true}
|
|
318
|
+
/>
|
|
319
|
+
)}
|
|
320
|
+
</Stack>
|
|
321
|
+
);
|
|
322
|
+
})()
|
|
323
|
+
))}
|
|
324
|
+
</div>
|
|
325
|
+
)}
|
|
326
|
+
<Divider variant={'inset'} />
|
|
327
|
+
{formData.functionId && (
|
|
328
|
+
<Box>
|
|
329
|
+
<label style={{ fontSize: '12px', color: '#FFFFFF', fontWeight: 'bold' }}>
|
|
330
|
+
{t('callForm.returnValueName')}
|
|
331
|
+
</label>
|
|
332
|
+
<ExpressionInput
|
|
333
|
+
label=""
|
|
334
|
+
value={formData.returnValueName as string}
|
|
335
|
+
onChange={(newValue) => {
|
|
336
|
+
handleMultipleInputChanges({
|
|
337
|
+
returnValueName: newValue,
|
|
338
|
+
});
|
|
339
|
+
}}
|
|
340
|
+
showDataTypeDropdown={true}
|
|
341
|
+
dataType={formData.returnValueDataType || 'String'}
|
|
342
|
+
onDataTypeChange={(dataType) => {
|
|
343
|
+
handleMultipleInputChanges({
|
|
344
|
+
returnValueDataType: dataType,
|
|
345
|
+
});
|
|
346
|
+
}}
|
|
347
|
+
onValidationChange={(isValid, errors) => {
|
|
348
|
+
// Handle validation from ExpressionInput
|
|
349
|
+
if (!isValid && errors.length > 0) {
|
|
350
|
+
setFieldError('returnValueName', errors[0]);
|
|
351
|
+
} else {
|
|
352
|
+
clearFieldError('returnValueName');
|
|
353
|
+
}
|
|
354
|
+
}}
|
|
355
|
+
/>
|
|
356
|
+
{getFieldError('returnValueName') && (
|
|
357
|
+
<ValidationError
|
|
358
|
+
error={getFieldError('returnValueName')}
|
|
359
|
+
show={true}
|
|
360
|
+
/>
|
|
361
|
+
)}
|
|
362
|
+
</Box>
|
|
363
|
+
)}
|
|
364
|
+
</Stack>
|
|
365
|
+
);
|
|
366
|
+
});
|
|
367
|
+
|
|
368
|
+
// Add display names for memoized components
|
|
369
|
+
CustomFunctionSelect.displayName = 'CustomFunctionSelect';
|
|
370
|
+
CallForm.displayName = 'CallForm';
|