robobyte-front-builder 1.0.17 → 1.0.19
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/src/lib/providers/RoboByteFrontBuilderProvider.jsx +6 -2
- package/src/pages/reportModule/reportBuilder/index.js +719 -561
- package/src/pages/reportModule/reportBuilder/reportViewer/index.js +135 -80
- package/src/pages/reportModule/reportBuilder/reports/index.js +51 -11
- package/src/pages/reportModule/reportBuilder/reportsPermissions/index.js +127 -0
- package/src/services/helper/multiSelectEditorByBuilder.js +245 -0
- package/src/services/helper/reportSessionHelper.js +83 -0
- package/src/views/genericTable/ColumnConfiguratorDialog.js +762 -0
- package/src/views/genericTable/FormattingSettingsDialog.js +546 -0
- package/src/views/genericTable/ReportSettingsDialog.js +151 -0
- package/src/views/genericTable/SGrid.js +872 -159
- package/src/views/genericTable/TAGGrid.js +83 -69
- package/src/views/genericTable/convertStringFunctions.js +200 -0
- package/src/views/genericTable/updateRefHelpers.js +421 -0
- package/src/views/rolePermissions/UpdateReportPermissionDialog.js +315 -0
|
@@ -0,0 +1,546 @@
|
|
|
1
|
+
import React, {useState, useEffect} from 'react';
|
|
2
|
+
import {
|
|
3
|
+
Dialog,
|
|
4
|
+
DialogTitle,
|
|
5
|
+
DialogContent,
|
|
6
|
+
DialogActions,
|
|
7
|
+
Button,
|
|
8
|
+
Grid,
|
|
9
|
+
TextField,
|
|
10
|
+
FormControl,
|
|
11
|
+
InputLabel,
|
|
12
|
+
Select,
|
|
13
|
+
MenuItem,
|
|
14
|
+
Typography,
|
|
15
|
+
Paper,
|
|
16
|
+
Box,
|
|
17
|
+
IconButton,
|
|
18
|
+
Divider,
|
|
19
|
+
FormControlLabel,
|
|
20
|
+
Switch,
|
|
21
|
+
Tooltip,
|
|
22
|
+
Autocomplete, Collapse
|
|
23
|
+
} from '@mui/material';
|
|
24
|
+
import {DeleteOutline, Plus, ChevronDown, ChevronUp} from 'mdi-material-ui';
|
|
25
|
+
import dynamic from 'next/dynamic';
|
|
26
|
+
|
|
27
|
+
const MonacoEditor = dynamic(() => import('@monaco-editor/react'), {ssr: false});
|
|
28
|
+
|
|
29
|
+
const FormattingSettingsDialog = ({open, onClose, field, onSave}) => {
|
|
30
|
+
const [settings, setSettings] = useState({
|
|
31
|
+
decimals: 0,
|
|
32
|
+
conditions: [],
|
|
33
|
+
fixedStyle: {
|
|
34
|
+
color: '',
|
|
35
|
+
backgroundColor: '',
|
|
36
|
+
fontWeight: 'normal',
|
|
37
|
+
flashing: false,
|
|
38
|
+
}
|
|
39
|
+
});
|
|
40
|
+
|
|
41
|
+
useEffect(() => {
|
|
42
|
+
if (field && field.formatting) {
|
|
43
|
+
setSettings(field.formatting);
|
|
44
|
+
} else {
|
|
45
|
+
setSettings({
|
|
46
|
+
decimals: 0,
|
|
47
|
+
conditions: [],
|
|
48
|
+
fixedStyle: {
|
|
49
|
+
color: '',
|
|
50
|
+
backgroundColor: '',
|
|
51
|
+
fontWeight: 'normal',
|
|
52
|
+
flashing: false,
|
|
53
|
+
}
|
|
54
|
+
});
|
|
55
|
+
}
|
|
56
|
+
}, [field, open]);
|
|
57
|
+
|
|
58
|
+
const handleSave = () => {
|
|
59
|
+
onSave(settings);
|
|
60
|
+
onClose();
|
|
61
|
+
};
|
|
62
|
+
|
|
63
|
+
const addCondition = () => {
|
|
64
|
+
setSettings(prev => ({
|
|
65
|
+
...prev,
|
|
66
|
+
conditions: [
|
|
67
|
+
...prev.conditions,
|
|
68
|
+
{
|
|
69
|
+
baseField: 'self', // 'self' or path of another field
|
|
70
|
+
operator: '==',
|
|
71
|
+
value: '',
|
|
72
|
+
valueType: 'static', // 'static' or 'field'
|
|
73
|
+
compareField: '', // path of field for comparison
|
|
74
|
+
style: {
|
|
75
|
+
color: '',
|
|
76
|
+
backgroundColor: '',
|
|
77
|
+
fontWeight: 'normal',
|
|
78
|
+
flashing: false,
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
]
|
|
82
|
+
}));
|
|
83
|
+
};
|
|
84
|
+
|
|
85
|
+
const removeCondition = (index) => {
|
|
86
|
+
setSettings(prev => ({
|
|
87
|
+
...prev,
|
|
88
|
+
conditions: prev.conditions.filter((_, i) => i !== index)
|
|
89
|
+
}));
|
|
90
|
+
};
|
|
91
|
+
|
|
92
|
+
const updateCondition = (index, key, value) => {
|
|
93
|
+
setSettings(prev => {
|
|
94
|
+
const newConditions = [...prev.conditions];
|
|
95
|
+
if (key.includes('.')) {
|
|
96
|
+
const [obj, prop] = key.split('.');
|
|
97
|
+
newConditions[index][obj][prop] = value;
|
|
98
|
+
} else {
|
|
99
|
+
newConditions[index][key] = value;
|
|
100
|
+
}
|
|
101
|
+
return {...prev, conditions: newConditions};
|
|
102
|
+
});
|
|
103
|
+
};
|
|
104
|
+
|
|
105
|
+
const updateFixedStyle = (key, value) => {
|
|
106
|
+
setSettings(prev => ({
|
|
107
|
+
...prev,
|
|
108
|
+
fixedStyle: {
|
|
109
|
+
...prev.fixedStyle,
|
|
110
|
+
[key]: value
|
|
111
|
+
}
|
|
112
|
+
}));
|
|
113
|
+
};
|
|
114
|
+
|
|
115
|
+
const [expandedEditors, setExpandedEditors] = useState({});
|
|
116
|
+
|
|
117
|
+
useEffect(() => {
|
|
118
|
+
// Expand math expression editors by default if they have content
|
|
119
|
+
if (open && settings.conditions) {
|
|
120
|
+
const initialExpanded = {};
|
|
121
|
+
settings.conditions.forEach((cond, index) => {
|
|
122
|
+
if (cond.valueType === 'expression') {
|
|
123
|
+
initialExpanded[index] = true;
|
|
124
|
+
}
|
|
125
|
+
});
|
|
126
|
+
setExpandedEditors(initialExpanded);
|
|
127
|
+
}
|
|
128
|
+
}, [open]);
|
|
129
|
+
|
|
130
|
+
const toggleEditorExpansion = (index) => {
|
|
131
|
+
setExpandedEditors(prev => ({
|
|
132
|
+
...prev,
|
|
133
|
+
[index]: !prev[index]
|
|
134
|
+
}));
|
|
135
|
+
};
|
|
136
|
+
|
|
137
|
+
const isNumeric = field?.propertyType === 'Int' || field?.propertyType === 'Double';
|
|
138
|
+
|
|
139
|
+
const fieldOptions = field?.parentFields || []; // This would need to be passed if we want to base on other fields
|
|
140
|
+
|
|
141
|
+
return (
|
|
142
|
+
<Dialog open={open} onClose={onClose} maxWidth="lg" fullWidth dir="ltr">
|
|
143
|
+
<DialogTitle>Formatting Settings: {field?.title || field?.headerName}</DialogTitle>
|
|
144
|
+
<DialogContent>
|
|
145
|
+
<Grid container spacing={3} sx={{mt: 1}}>
|
|
146
|
+
{isNumeric && (
|
|
147
|
+
<Grid size={{ xs: 12 }}>
|
|
148
|
+
<TextField
|
|
149
|
+
label="Decimal Places"
|
|
150
|
+
type="number"
|
|
151
|
+
fullWidth
|
|
152
|
+
value={settings.decimals}
|
|
153
|
+
onChange={(e) => setSettings({...settings, decimals: parseInt(e.target.value) || 0})}
|
|
154
|
+
/>
|
|
155
|
+
</Grid>
|
|
156
|
+
)}
|
|
157
|
+
|
|
158
|
+
<Grid size={{ xs: 12 }}>
|
|
159
|
+
<Typography variant="subtitle1" gutterBottom fontWeight="bold">Fixed Style</Typography>
|
|
160
|
+
<Grid container spacing={2}>
|
|
161
|
+
<Grid size={{ xs: 3 }}>
|
|
162
|
+
<TextField
|
|
163
|
+
label="Text Color"
|
|
164
|
+
type="color"
|
|
165
|
+
fullWidth
|
|
166
|
+
InputLabelProps={{shrink: true}}
|
|
167
|
+
value={settings.fixedStyle?.color || '#000000'}
|
|
168
|
+
onChange={(e) => updateFixedStyle('color', e.target.value)}
|
|
169
|
+
/>
|
|
170
|
+
</Grid>
|
|
171
|
+
<Grid size={{ xs: 3 }}>
|
|
172
|
+
<TextField
|
|
173
|
+
label="Background Color"
|
|
174
|
+
type="color"
|
|
175
|
+
fullWidth
|
|
176
|
+
InputLabelProps={{shrink: true}}
|
|
177
|
+
value={settings.fixedStyle?.backgroundColor || '#ffffff'}
|
|
178
|
+
onChange={(e) => updateFixedStyle('backgroundColor', e.target.value)}
|
|
179
|
+
/>
|
|
180
|
+
</Grid>
|
|
181
|
+
<Grid size={{ xs: 3 }}>
|
|
182
|
+
<FormControl fullWidth>
|
|
183
|
+
<InputLabel>Font Weight</InputLabel>
|
|
184
|
+
<Select
|
|
185
|
+
value={settings.fixedStyle?.fontWeight || 'normal'}
|
|
186
|
+
label="Font Weight"
|
|
187
|
+
onChange={(e) => updateFixedStyle('fontWeight', e.target.value)}
|
|
188
|
+
>
|
|
189
|
+
<MenuItem value="normal">Normal</MenuItem>
|
|
190
|
+
<MenuItem value="bold">Bold</MenuItem>
|
|
191
|
+
</Select>
|
|
192
|
+
</FormControl>
|
|
193
|
+
</Grid>
|
|
194
|
+
<Grid display="flex" alignItems="center" size={{ xs: 1 }}>
|
|
195
|
+
<FormControlLabel
|
|
196
|
+
control={
|
|
197
|
+
<Switch
|
|
198
|
+
checked={settings.fixedStyle?.flashing || false}
|
|
199
|
+
onChange={(e) => updateFixedStyle('flashing', e.target.checked)}
|
|
200
|
+
/>
|
|
201
|
+
}
|
|
202
|
+
label="Flashing"
|
|
203
|
+
/>
|
|
204
|
+
</Grid>
|
|
205
|
+
</Grid>
|
|
206
|
+
</Grid>
|
|
207
|
+
|
|
208
|
+
<Grid size={{ xs: 12 }}>
|
|
209
|
+
<Divider sx={{my: 2}} />
|
|
210
|
+
<Box display="flex" justifyContent="space-between" alignItems="center" mb={2}>
|
|
211
|
+
<Typography variant="subtitle1" fontWeight="bold">Conditional Formatting (Emphasis)</Typography>
|
|
212
|
+
<Button startIcon={<Plus />} variant="outlined" size="small" onClick={addCondition}>
|
|
213
|
+
Add Condition
|
|
214
|
+
</Button>
|
|
215
|
+
</Box>
|
|
216
|
+
|
|
217
|
+
{settings.conditions.map((condition, index) => (
|
|
218
|
+
<Paper key={index} variant="outlined" sx={{p: 2, mb: 2, backgroundColor: '#f9f9f9'}}>
|
|
219
|
+
<Grid container spacing={2} alignItems="center">
|
|
220
|
+
<Grid size={{ xs: 1.5 }}>
|
|
221
|
+
<FormControl fullWidth size="small">
|
|
222
|
+
<InputLabel>Type</InputLabel>
|
|
223
|
+
<Select
|
|
224
|
+
value={condition.valueType || 'static'}
|
|
225
|
+
label="Type"
|
|
226
|
+
onChange={(e) => {
|
|
227
|
+
const newType = e.target.value;
|
|
228
|
+
updateCondition(index, 'valueType', newType);
|
|
229
|
+
if (newType === 'expression') {
|
|
230
|
+
setExpandedEditors(prev => ({...prev, [index]: true}));
|
|
231
|
+
}
|
|
232
|
+
}}
|
|
233
|
+
>
|
|
234
|
+
<MenuItem value="static">Static Value</MenuItem>
|
|
235
|
+
<MenuItem value="field">Another Field</MenuItem>
|
|
236
|
+
<MenuItem value="expression">Math Expression</MenuItem>
|
|
237
|
+
</Select>
|
|
238
|
+
</FormControl>
|
|
239
|
+
</Grid>
|
|
240
|
+
{condition.valueType !== 'expression' && (
|
|
241
|
+
<Grid size={{ xs: 12, sm: 3 }}>
|
|
242
|
+
<Autocomplete
|
|
243
|
+
freeSolo
|
|
244
|
+
size="small"
|
|
245
|
+
options={['self', ...fieldOptions.map(f => f.path)]}
|
|
246
|
+
getOptionLabel={(option) => {
|
|
247
|
+
if (option === 'self') return 'self';
|
|
248
|
+
const f = fieldOptions.find(fo => fo.path === option);
|
|
249
|
+
return f ? `${f.title || f.headerName} (${f.path})` : option;
|
|
250
|
+
}}
|
|
251
|
+
isOptionEqualToValue={(option, value) => option === value}
|
|
252
|
+
value={condition.baseField || 'self'}
|
|
253
|
+
onChange={(e, newValue) => {
|
|
254
|
+
updateCondition(index, 'baseField', newValue || 'self');
|
|
255
|
+
}}
|
|
256
|
+
onBlur={(e) => {
|
|
257
|
+
const val = e.target.value;
|
|
258
|
+
if (val === 'self' || !val) {
|
|
259
|
+
updateCondition(index, 'baseField', 'self');
|
|
260
|
+
} else {
|
|
261
|
+
// Find if the typed value matches any of our options' labels or paths
|
|
262
|
+
const matchedOption = fieldOptions.find(f =>
|
|
263
|
+
f.path === val || `${f.title || f.headerName} (${f.path})` === val
|
|
264
|
+
);
|
|
265
|
+
updateCondition(index, 'baseField', matchedOption ? matchedOption.path : val);
|
|
266
|
+
}
|
|
267
|
+
}}
|
|
268
|
+
renderInput={(params) => (
|
|
269
|
+
<TextField {...params} label="Base Field" />
|
|
270
|
+
)}
|
|
271
|
+
/>
|
|
272
|
+
</Grid>
|
|
273
|
+
)}
|
|
274
|
+
{condition.valueType !== 'expression' && (
|
|
275
|
+
<Grid size={{ xs: 12, sm: 2 }}>
|
|
276
|
+
<FormControl fullWidth size="small">
|
|
277
|
+
<InputLabel>Operator</InputLabel>
|
|
278
|
+
<Select
|
|
279
|
+
value={condition.operator}
|
|
280
|
+
label="Operator"
|
|
281
|
+
onChange={(e) => updateCondition(index, 'operator', e.target.value)}
|
|
282
|
+
>
|
|
283
|
+
<MenuItem value="==">Equals</MenuItem>
|
|
284
|
+
<MenuItem value="!=">Not Equals</MenuItem>
|
|
285
|
+
<MenuItem value=">">Greater Than</MenuItem>
|
|
286
|
+
<MenuItem value="<">Less Than</MenuItem>
|
|
287
|
+
<MenuItem value=">=">Greater Than or Equal</MenuItem>
|
|
288
|
+
<MenuItem value="<=">Less Than or Equal</MenuItem>
|
|
289
|
+
<MenuItem value="contains">Contains</MenuItem>
|
|
290
|
+
</Select>
|
|
291
|
+
</FormControl>
|
|
292
|
+
</Grid>
|
|
293
|
+
)}
|
|
294
|
+
<Grid dir="ltr" size={{ xs: 12, sm: condition.valueType === 'expression' ? 4.5 : 3.5 }}>
|
|
295
|
+
{condition.valueType === 'field' ? (
|
|
296
|
+
<Autocomplete
|
|
297
|
+
freeSolo
|
|
298
|
+
size="small"
|
|
299
|
+
options={fieldOptions.map(f => f.path)}
|
|
300
|
+
getOptionLabel={(option) => {
|
|
301
|
+
const f = fieldOptions.find(fo => fo.path === option);
|
|
302
|
+
return f ? `${f.title || f.headerName} (${f.path})` : option;
|
|
303
|
+
}}
|
|
304
|
+
isOptionEqualToValue={(option, value) => option === value}
|
|
305
|
+
value={condition.compareField || ''}
|
|
306
|
+
onChange={(e, newValue) => {
|
|
307
|
+
updateCondition(index, 'compareField', newValue || '');
|
|
308
|
+
}}
|
|
309
|
+
onBlur={(e) => {
|
|
310
|
+
const val = e.target.value;
|
|
311
|
+
const matchedOption = fieldOptions.find(f =>
|
|
312
|
+
f.path === val || `${f.title || f.headerName} (${f.path})` === val
|
|
313
|
+
);
|
|
314
|
+
updateCondition(index, 'compareField', matchedOption ? matchedOption.path : val);
|
|
315
|
+
}}
|
|
316
|
+
renderInput={(params) => (
|
|
317
|
+
<TextField {...params} label="Compare With" />
|
|
318
|
+
)}
|
|
319
|
+
/>
|
|
320
|
+
) : condition.valueType === 'expression' ? null : (
|
|
321
|
+
<TextField
|
|
322
|
+
label="Value"
|
|
323
|
+
size="small"
|
|
324
|
+
fullWidth
|
|
325
|
+
value={condition.value}
|
|
326
|
+
onChange={(e) => updateCondition(index, 'value', e.target.value)}
|
|
327
|
+
/>
|
|
328
|
+
)}
|
|
329
|
+
</Grid>
|
|
330
|
+
<Grid size={{ xs: 1.5 }}>
|
|
331
|
+
<TextField
|
|
332
|
+
label="Color"
|
|
333
|
+
type="color"
|
|
334
|
+
size="small"
|
|
335
|
+
fullWidth
|
|
336
|
+
InputLabelProps={{shrink: true}}
|
|
337
|
+
value={condition.style.color || '#000000'}
|
|
338
|
+
onChange={(e) => updateCondition(index, 'style.color', e.target.value)}
|
|
339
|
+
/>
|
|
340
|
+
</Grid>
|
|
341
|
+
<Grid size={{ xs: 1.5 }}>
|
|
342
|
+
<TextField
|
|
343
|
+
label="BG Color"
|
|
344
|
+
type="color"
|
|
345
|
+
size="small"
|
|
346
|
+
fullWidth
|
|
347
|
+
InputLabelProps={{shrink: true}}
|
|
348
|
+
value={condition.style.backgroundColor || '#ffffff'}
|
|
349
|
+
onChange={(e) => updateCondition(index, 'style.backgroundColor', e.target.value)}
|
|
350
|
+
/>
|
|
351
|
+
</Grid>
|
|
352
|
+
<Grid size={{ xs: 1.5 }}>
|
|
353
|
+
<FormControl fullWidth size="small">
|
|
354
|
+
<InputLabel>Font Weight</InputLabel>
|
|
355
|
+
<Select
|
|
356
|
+
value={condition.style.fontWeight || 'normal'}
|
|
357
|
+
label="Font Weight"
|
|
358
|
+
onChange={(e) => updateCondition(index, 'style.fontWeight', e.target.value)}
|
|
359
|
+
>
|
|
360
|
+
<MenuItem value="normal">Normal</MenuItem>
|
|
361
|
+
<MenuItem value="bold">Bold</MenuItem>
|
|
362
|
+
</Select>
|
|
363
|
+
</FormControl>
|
|
364
|
+
</Grid>
|
|
365
|
+
<Grid size={{ xs: 1 }}>
|
|
366
|
+
<FormControlLabel
|
|
367
|
+
control={
|
|
368
|
+
<Switch
|
|
369
|
+
size="small"
|
|
370
|
+
checked={condition.style.flashing || false}
|
|
371
|
+
onChange={(e) => updateCondition(index, 'style.flashing', e.target.checked)}
|
|
372
|
+
/>
|
|
373
|
+
}
|
|
374
|
+
label={<Typography variant="caption">Flash</Typography>}
|
|
375
|
+
/>
|
|
376
|
+
</Grid>
|
|
377
|
+
<Grid display="flex" justifyContent="flex-end" size={{ xs: 0.5 }}>
|
|
378
|
+
<IconButton size="small" color="error" onClick={() => removeCondition(index)}>
|
|
379
|
+
<DeleteOutline />
|
|
380
|
+
</IconButton>
|
|
381
|
+
</Grid>
|
|
382
|
+
|
|
383
|
+
{/* Expression Editor in another row */}
|
|
384
|
+
{condition.valueType === 'expression' && (
|
|
385
|
+
<Grid size={{ xs: 12 }}>
|
|
386
|
+
<Box sx={{ border: '1px solid #ced4da', borderRadius: 1, overflow: 'hidden', mt: 1 }}>
|
|
387
|
+
<Box sx={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center', px: 1, py: 0.5, backgroundColor: '#f0f0f0' }}>
|
|
388
|
+
<Typography variant="caption" fontWeight="bold">Math Expression</Typography>
|
|
389
|
+
<IconButton size="small" onClick={() => toggleEditorExpansion(index)}>
|
|
390
|
+
{expandedEditors[index] ? <ChevronUp size="small" /> : <ChevronDown size="small" />}
|
|
391
|
+
</IconButton>
|
|
392
|
+
</Box>
|
|
393
|
+
<Collapse in={expandedEditors[index]}>
|
|
394
|
+
<Box dir="ltr">
|
|
395
|
+
<MonacoEditor
|
|
396
|
+
height="200px"
|
|
397
|
+
language="mazajak-expression"
|
|
398
|
+
theme="vs-dark"
|
|
399
|
+
value={condition.expression || ''}
|
|
400
|
+
onChange={(newValue) => {
|
|
401
|
+
updateCondition(index, 'expression', newValue || '');
|
|
402
|
+
}}
|
|
403
|
+
onMount={(editor, monaco) => {
|
|
404
|
+
// Define a custom language for highlighting
|
|
405
|
+
const langId = 'mazajak-expression';
|
|
406
|
+
|
|
407
|
+
// Register the language if not already registered
|
|
408
|
+
if (!monaco.languages.getLanguages().some(l => l.id === langId)) {
|
|
409
|
+
monaco.languages.register({ id: langId });
|
|
410
|
+
|
|
411
|
+
// Define the tokenizer
|
|
412
|
+
monaco.languages.setMonarchTokensProvider(langId, {
|
|
413
|
+
tokenizer: {
|
|
414
|
+
root: [
|
|
415
|
+
// Double quotes for field placeholders (highlighted as tags/regex/special)
|
|
416
|
+
[/"[^"]*"/, 'tag'],
|
|
417
|
+
|
|
418
|
+
// Single quotes for string literals (standard string highlighting)
|
|
419
|
+
[/'[^']*'/, 'string'],
|
|
420
|
+
|
|
421
|
+
// Standard JavaScript-like tokens
|
|
422
|
+
[/[a-z_$][\w$]*/, {
|
|
423
|
+
cases: {
|
|
424
|
+
'if|else|switch|case|break|default|return': 'keyword',
|
|
425
|
+
'true|false|null': 'constant',
|
|
426
|
+
'@default': 'identifier'
|
|
427
|
+
}
|
|
428
|
+
}],
|
|
429
|
+
[/[{}()\[\]]/, '@brackets'],
|
|
430
|
+
[/[0-9]+/, 'number'],
|
|
431
|
+
[/[><=!|&?:,+*/%-]/, 'operator'],
|
|
432
|
+
[/[ \t\r\n]+/, 'white'],
|
|
433
|
+
]
|
|
434
|
+
}
|
|
435
|
+
});
|
|
436
|
+
|
|
437
|
+
// Define custom theme colors for these tokens if needed,
|
|
438
|
+
// but 'tag' and 'string' already have different colors in vs-dark.
|
|
439
|
+
}
|
|
440
|
+
|
|
441
|
+
// Update the editor's model language
|
|
442
|
+
const model = editor.getModel();
|
|
443
|
+
if (model) {
|
|
444
|
+
monaco.editor.setModelLanguage(model, langId);
|
|
445
|
+
}
|
|
446
|
+
|
|
447
|
+
// Register completion provider for the custom language
|
|
448
|
+
const provider = monaco.languages.registerCompletionItemProvider(langId, {
|
|
449
|
+
triggerCharacters: ['"'],
|
|
450
|
+
provideCompletionItems: (model, position) => {
|
|
451
|
+
const textUntilPosition = model.getValueInRange({
|
|
452
|
+
startLineNumber: 1,
|
|
453
|
+
startColumn: 1,
|
|
454
|
+
endLineNumber: position.lineNumber,
|
|
455
|
+
endColumn: position.column
|
|
456
|
+
});
|
|
457
|
+
|
|
458
|
+
const quoteCount = (textUntilPosition.match(/"/g) || []).length;
|
|
459
|
+
const isInsideQuotes = quoteCount % 2 !== 0;
|
|
460
|
+
|
|
461
|
+
if (isInsideQuotes) {
|
|
462
|
+
return {
|
|
463
|
+
suggestions: fieldOptions.map(f => ({
|
|
464
|
+
label: `"${f.path}"`,
|
|
465
|
+
kind: monaco.languages.CompletionItemKind.Field,
|
|
466
|
+
documentation: f.title || f.headerName,
|
|
467
|
+
insertText: f.path, // Just the path
|
|
468
|
+
range: {
|
|
469
|
+
startLineNumber: position.lineNumber,
|
|
470
|
+
endLineNumber: position.lineNumber,
|
|
471
|
+
startColumn: position.column,
|
|
472
|
+
endColumn: position.column
|
|
473
|
+
}
|
|
474
|
+
}))
|
|
475
|
+
};
|
|
476
|
+
} else {
|
|
477
|
+
return {
|
|
478
|
+
suggestions: [
|
|
479
|
+
{ label: '+', kind: monaco.languages.CompletionItemKind.Operator, insertText: '+ ', range: { startLineNumber: position.lineNumber, endLineNumber: position.lineNumber, startColumn: position.column, endColumn: position.column } },
|
|
480
|
+
{ label: '-', kind: monaco.languages.CompletionItemKind.Operator, insertText: '- ', range: { startLineNumber: position.lineNumber, endLineNumber: position.lineNumber, startColumn: position.column, endColumn: position.column } },
|
|
481
|
+
{ label: '*', kind: monaco.languages.CompletionItemKind.Operator, insertText: '* ', range: { startLineNumber: position.lineNumber, endLineNumber: position.lineNumber, startColumn: position.column, endColumn: position.column } },
|
|
482
|
+
{ label: '/', kind: monaco.languages.CompletionItemKind.Operator, insertText: '/ ', range: { startLineNumber: position.lineNumber, endLineNumber: position.lineNumber, startColumn: position.column, endColumn: position.column } },
|
|
483
|
+
{ label: '(', kind: monaco.languages.CompletionItemKind.Operator, insertText: '( ', range: { startLineNumber: position.lineNumber, endLineNumber: position.lineNumber, startColumn: position.column, endColumn: position.column } },
|
|
484
|
+
{ label: ')', kind: monaco.languages.CompletionItemKind.Operator, insertText: ') ', range: { startLineNumber: position.lineNumber, endLineNumber: position.lineNumber, startColumn: position.column, endColumn: position.column } },
|
|
485
|
+
{ label: '>', kind: monaco.languages.CompletionItemKind.Operator, insertText: '> ', range: { startLineNumber: position.lineNumber, endLineNumber: position.lineNumber, startColumn: position.column, endColumn: position.column } },
|
|
486
|
+
{ label: '<', kind: monaco.languages.CompletionItemKind.Operator, insertText: '< ', range: { startLineNumber: position.lineNumber, endLineNumber: position.lineNumber, startColumn: position.column, endColumn: position.column } },
|
|
487
|
+
{ label: '>=', kind: monaco.languages.CompletionItemKind.Operator, insertText: '>= ', range: { startLineNumber: position.lineNumber, endLineNumber: position.lineNumber, startColumn: position.column, endColumn: position.column } },
|
|
488
|
+
{ label: '<=', kind: monaco.languages.CompletionItemKind.Operator, insertText: '<= ', range: { startLineNumber: position.lineNumber, endLineNumber: position.lineNumber, startColumn: position.column, endColumn: position.column } },
|
|
489
|
+
{ label: '==', kind: monaco.languages.CompletionItemKind.Operator, insertText: '== ', range: { startLineNumber: position.lineNumber, endLineNumber: position.lineNumber, startColumn: position.column, endColumn: position.column } },
|
|
490
|
+
{ label: '!=', kind: monaco.languages.CompletionItemKind.Operator, insertText: '!= ', range: { startLineNumber: position.lineNumber, endLineNumber: position.lineNumber, startColumn: position.column, endColumn: position.column } },
|
|
491
|
+
{ label: '&&', kind: monaco.languages.CompletionItemKind.Operator, insertText: '&& ', range: { startLineNumber: position.lineNumber, endLineNumber: position.lineNumber, startColumn: position.column, endColumn: position.column } },
|
|
492
|
+
{ label: '||', kind: monaco.languages.CompletionItemKind.Operator, insertText: '|| ', range: { startLineNumber: position.lineNumber, endLineNumber: position.lineNumber, startColumn: position.column, endColumn: position.column } },
|
|
493
|
+
{ label: '?', kind: monaco.languages.CompletionItemKind.Operator, insertText: '? ', range: { startLineNumber: position.lineNumber, endLineNumber: position.lineNumber, startColumn: position.column, endColumn: position.column } },
|
|
494
|
+
{ label: ':', kind: monaco.languages.CompletionItemKind.Operator, insertText: ': ', range: { startLineNumber: position.lineNumber, endLineNumber: position.lineNumber, startColumn: position.column, endColumn: position.column } },
|
|
495
|
+
{ label: 'if', kind: monaco.languages.CompletionItemKind.Keyword, insertText: 'if ($1) {\n\t$0\n}', insertTextRules: monaco.languages.CompletionItemInsertRules.InsertAsSnippet, range: { startLineNumber: position.lineNumber, endLineNumber: position.lineNumber, startColumn: position.column, endColumn: position.column } },
|
|
496
|
+
{ label: 'else', kind: monaco.languages.CompletionItemKind.Keyword, insertText: 'else {\n\t$0\n}', insertTextRules: monaco.languages.CompletionItemInsertRules.InsertAsSnippet, range: { startLineNumber: position.lineNumber, endLineNumber: position.lineNumber, startColumn: position.column, endColumn: position.column } },
|
|
497
|
+
{ label: 'switch', kind: monaco.languages.CompletionItemKind.Keyword, insertText: 'switch ($1) {\n\tcase $2:\n\t\t$0\n\t\tbreak;\n}', insertTextRules: monaco.languages.CompletionItemInsertRules.InsertAsSnippet, range: { startLineNumber: position.lineNumber, endLineNumber: position.lineNumber, startColumn: position.column, endColumn: position.column } },
|
|
498
|
+
{ label: 'case', kind: monaco.languages.CompletionItemKind.Keyword, insertText: 'case $1:\n\t$0\n\tbreak;', insertTextRules: monaco.languages.CompletionItemInsertRules.InsertAsSnippet, range: { startLineNumber: position.lineNumber, endLineNumber: position.lineNumber, startColumn: position.column, endColumn: position.column } },
|
|
499
|
+
{ label: 'break', kind: monaco.languages.CompletionItemKind.Keyword, insertText: 'break;', range: { startLineNumber: position.lineNumber, endLineNumber: position.lineNumber, startColumn: position.column, endColumn: position.column } },
|
|
500
|
+
{ label: 'default', kind: monaco.languages.CompletionItemKind.Keyword, insertText: 'default:\n\t$0', insertTextRules: monaco.languages.CompletionItemInsertRules.InsertAsSnippet, range: { startLineNumber: position.lineNumber, endLineNumber: position.lineNumber, startColumn: position.column, endColumn: position.column } },
|
|
501
|
+
{ label: 'return', kind: monaco.languages.CompletionItemKind.Keyword, insertText: 'return $0;', insertTextRules: monaco.languages.CompletionItemInsertRules.InsertAsSnippet, range: { startLineNumber: position.lineNumber, endLineNumber: position.lineNumber, startColumn: position.column, endColumn: position.column } },
|
|
502
|
+
]
|
|
503
|
+
};
|
|
504
|
+
}
|
|
505
|
+
}
|
|
506
|
+
});
|
|
507
|
+
editor.onDidDispose(() => provider.dispose());
|
|
508
|
+
}}
|
|
509
|
+
options={{
|
|
510
|
+
minimap: { enabled: false },
|
|
511
|
+
fontSize: 13,
|
|
512
|
+
lineNumbers: 'off',
|
|
513
|
+
folding: true,
|
|
514
|
+
scrollBeyondLastLine: false,
|
|
515
|
+
automaticLayout: true,
|
|
516
|
+
wordWrap: 'on',
|
|
517
|
+
formatOnPaste: true,
|
|
518
|
+
formatOnType: true,
|
|
519
|
+
suggestOnTriggerCharacters: true,
|
|
520
|
+
}}
|
|
521
|
+
/>
|
|
522
|
+
</Box>
|
|
523
|
+
</Collapse>
|
|
524
|
+
{!expandedEditors[index] && (
|
|
525
|
+
<Box sx={{ p: 1, backgroundColor: '#333', color: '#fff', fontSize: '0.75rem', whiteSpace: 'nowrap', overflow: 'hidden', textOverflow: 'ellipsis', cursor: 'pointer' }} onClick={() => toggleEditorExpansion(index)}>
|
|
526
|
+
{condition.expression || 'Click to edit expression...'}
|
|
527
|
+
</Box>
|
|
528
|
+
)}
|
|
529
|
+
</Box>
|
|
530
|
+
</Grid>
|
|
531
|
+
)}
|
|
532
|
+
</Grid>
|
|
533
|
+
</Paper>
|
|
534
|
+
))}
|
|
535
|
+
</Grid>
|
|
536
|
+
</Grid>
|
|
537
|
+
</DialogContent>
|
|
538
|
+
<DialogActions>
|
|
539
|
+
<Button onClick={onClose}>Cancel</Button>
|
|
540
|
+
<Button onClick={handleSave} variant="contained" color="primary">Save</Button>
|
|
541
|
+
</DialogActions>
|
|
542
|
+
</Dialog>
|
|
543
|
+
);
|
|
544
|
+
};
|
|
545
|
+
|
|
546
|
+
export default FormattingSettingsDialog;
|