Skip to content

Commit f82de1a

Browse files
committed
feat(NodePropertiesPanel): add actions management UI with styling
- Introduce new CSS file for panel styling including add/remove buttons - Implement actions list management with add/remove functionality - Add form fields for action configuration (function name, arguments, retry) - Include validation for JSON arguments input
1 parent cbc4a5b commit f82de1a

File tree

2 files changed

+267
-30
lines changed

2 files changed

+267
-30
lines changed
Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
/* NodePropertiesPanel Button Styles */
2+
3+
.add-btn {
4+
margin-left: auto;
5+
padding: 6px 12px;
6+
border: 1px solid #10b981;
7+
background-color: #ffffff;
8+
color: #10b981;
9+
border-radius: 4px;
10+
cursor: pointer;
11+
transition: all 0.2s ease;
12+
font-weight: 500;
13+
font-size: 12px;
14+
display: flex;
15+
align-items: center;
16+
gap: 6px;
17+
}
18+
19+
.add-btn:hover {
20+
background-color: #10b981;
21+
color: white;
22+
transform: translateY(-1px);
23+
box-shadow: 0 2px 4px rgba(16, 185, 129, 0.2);
24+
}
25+
26+
.remove-btn {
27+
padding: 4px 8px;
28+
border: 1px solid #ef4444;
29+
background-color: #ffffff;
30+
color: #ef4444;
31+
border-radius: 4px;
32+
cursor: pointer;
33+
transition: all 0.2s ease;
34+
font-weight: 500;
35+
font-size: 11px;
36+
display: flex;
37+
align-items: center;
38+
gap: 4px;
39+
}
40+
41+
.remove-btn:hover {
42+
background-color: #ef4444;
43+
color: white;
44+
transform: translateY(-1px);
45+
box-shadow: 0 2px 4px rgba(239, 68, 68, 0.2);
46+
}
47+
48+
/* Panel specific styles */
49+
.node-properties-panel {
50+
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Oxygen', 'Ubuntu', 'Cantarell', 'Fira Sans', 'Droid Sans', 'Helvetica Neue', sans-serif;
51+
font-size: 14px;
52+
}
53+
54+
.node-properties-panel button {
55+
font-family: inherit;
56+
font-size: inherit;
57+
border: none;
58+
cursor: pointer;
59+
transition: all 0.2s ease;
60+
}
61+
62+
.node-properties-panel button:hover:not(:disabled) {
63+
opacity: 1;
64+
}
65+
66+
.node-properties-panel button:disabled {
67+
cursor: not-allowed;
68+
opacity: 0.5;
69+
}

src/lib/src/components/editors/NodePropertiesPanel.js

Lines changed: 198 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import React from 'react';
22
import { X, Save, RotateCcw, Settings } from 'lucide-react';
3+
import './NodePropertiesPanel.css';
34

45
/**
56
* A simple node properties panel component for editing node data
@@ -221,38 +222,205 @@ export function NodePropertiesPanel({
221222
{/* Node-specific fields based on type */}
222223
{node.type === 'operation' && (
223224
<div style={{ marginBottom: '16px' }}>
224-
<label style={{
225-
display: 'block',
226-
fontSize: '12px',
227-
fontWeight: '500',
228-
color: '#374151',
229-
marginBottom: '4px'
225+
<div style={{
226+
display: 'flex',
227+
justifyContent: 'space-between',
228+
alignItems: 'center',
229+
marginBottom: '8px'
230230
}}>
231-
Function Reference
232-
</label>
233-
<input
234-
type="text"
235-
value={formData.functionRef?.refName || ''}
236-
onChange={(e) => handleFieldChange('functionRef.refName', e.target.value)}
237-
placeholder="Enter function name"
238-
style={{
239-
width: '100%',
240-
padding: '8px 12px',
241-
border: '1px solid #d1d5db',
231+
<label style={{
232+
fontSize: '12px',
233+
fontWeight: '500',
234+
color: '#374151'
235+
}}>
236+
Actions ({(formData.actions || []).length})
237+
</label>
238+
<button
239+
onClick={() => {
240+
const newAction = {
241+
functionRef: {
242+
refName: '',
243+
arguments: {}
244+
},
245+
retryRef: ''
246+
};
247+
const currentActions = formData.actions || [];
248+
handleFieldChange('actions', [...currentActions, newAction]);
249+
}}
250+
className="add-btn"
251+
>
252+
<span style={{ fontSize: '14px', lineHeight: '1' }}>+</span>
253+
Add Action
254+
</button>
255+
</div>
256+
257+
{(formData.actions || []).map((action, index) => (
258+
<div key={index} style={{
259+
border: '1px solid #e5e7eb',
242260
borderRadius: '6px',
243-
fontSize: '14px',
244-
outline: 'none',
245-
transition: 'border-color 0.2s',
246-
}}
247-
onFocus={(e) => {
248-
e.target.style.borderColor = '#3b82f6';
249-
e.target.style.boxShadow = '0 0 0 3px rgba(59, 130, 246, 0.1)';
250-
}}
251-
onBlur={(e) => {
252-
e.target.style.borderColor = '#d1d5db';
253-
e.target.style.boxShadow = 'none';
254-
}}
255-
/>
261+
padding: '12px',
262+
marginBottom: '8px',
263+
backgroundColor: '#f9fafb'
264+
}}>
265+
<div style={{
266+
display: 'flex',
267+
justifyContent: 'space-between',
268+
alignItems: 'center',
269+
marginBottom: '8px'
270+
}}>
271+
<span style={{
272+
fontSize: '11px',
273+
fontWeight: '500',
274+
color: '#6b7280'
275+
}}>
276+
Action {index + 1}
277+
</span>
278+
<button
279+
onClick={() => {
280+
const newActions = formData.actions.filter((_, i) => i !== index);
281+
handleFieldChange('actions', newActions);
282+
}}
283+
className="remove-btn"
284+
>
285+
<span style={{ fontSize: '12px', lineHeight: '1' }}>×</span>
286+
Remove
287+
</button>
288+
</div>
289+
290+
{/* Function Reference Name */}
291+
<div style={{ marginBottom: '8px' }}>
292+
<label style={{
293+
display: 'block',
294+
fontSize: '11px',
295+
fontWeight: '500',
296+
color: '#374151',
297+
marginBottom: '2px'
298+
}}>
299+
Function Name
300+
</label>
301+
<input
302+
type="text"
303+
value={action.functionRef?.refName || ''}
304+
onChange={(e) => {
305+
const newActions = [...(formData.actions || [])];
306+
if (!newActions[index].functionRef) {
307+
newActions[index].functionRef = {};
308+
}
309+
newActions[index].functionRef.refName = e.target.value;
310+
handleFieldChange('actions', newActions);
311+
}}
312+
placeholder="Enter function name"
313+
style={{
314+
width: '100%',
315+
padding: '6px 8px',
316+
border: '1px solid #d1d5db',
317+
borderRadius: '4px',
318+
fontSize: '12px',
319+
outline: 'none'
320+
}}
321+
/>
322+
</div>
323+
324+
{/* Arguments */}
325+
<div style={{ marginBottom: '8px' }}>
326+
<label style={{
327+
display: 'block',
328+
fontSize: '11px',
329+
fontWeight: '500',
330+
color: '#374151',
331+
marginBottom: '2px'
332+
}}>
333+
Arguments (JSON)
334+
</label>
335+
<textarea
336+
value={typeof action.functionRef?.arguments === 'string'
337+
? action.functionRef.arguments
338+
: JSON.stringify(action.functionRef?.arguments || {}, null, 2)}
339+
onChange={(e) => {
340+
const newActions = [...(formData.actions || [])];
341+
if (!newActions[index].functionRef) {
342+
newActions[index].functionRef = {};
343+
}
344+
345+
// Store as string while editing
346+
newActions[index].functionRef.arguments = e.target.value;
347+
handleFieldChange('actions', newActions);
348+
}}
349+
onBlur={(e) => {
350+
// Try to parse as JSON when user finishes editing
351+
try {
352+
const parsedArgs = JSON.parse(e.target.value);
353+
const newActions = [...(formData.actions || [])];
354+
if (!newActions[index].functionRef) {
355+
newActions[index].functionRef = {};
356+
}
357+
newActions[index].functionRef.arguments = parsedArgs;
358+
handleFieldChange('actions', newActions);
359+
} catch (error) {
360+
// Keep as string if invalid JSON
361+
console.warn('Invalid JSON in arguments field:', error.message);
362+
}
363+
}}
364+
placeholder='{"key": "value"}'
365+
rows={3}
366+
style={{
367+
width: '100%',
368+
padding: '6px 8px',
369+
border: '1px solid #d1d5db',
370+
borderRadius: '4px',
371+
fontSize: '11px',
372+
fontFamily: 'monospace',
373+
outline: 'none',
374+
resize: 'vertical'
375+
}}
376+
/>
377+
</div>
378+
379+
{/* Retry Reference */}
380+
<div>
381+
<label style={{
382+
display: 'block',
383+
fontSize: '11px',
384+
fontWeight: '500',
385+
color: '#374151',
386+
marginBottom: '2px'
387+
}}>
388+
Retry Reference
389+
</label>
390+
<input
391+
type="text"
392+
value={action.retryRef || ''}
393+
onChange={(e) => {
394+
const newActions = [...(formData.actions || [])];
395+
newActions[index].retryRef = e.target.value;
396+
handleFieldChange('actions', newActions);
397+
}}
398+
placeholder="Enter retry strategy name"
399+
style={{
400+
width: '100%',
401+
padding: '6px 8px',
402+
border: '1px solid #d1d5db',
403+
borderRadius: '4px',
404+
fontSize: '12px',
405+
outline: 'none'
406+
}}
407+
/>
408+
</div>
409+
</div>
410+
))}
411+
412+
{(!formData.actions || formData.actions.length === 0) && (
413+
<div style={{
414+
padding: '16px',
415+
textAlign: 'center',
416+
color: '#6b7280',
417+
fontSize: '12px',
418+
border: '1px dashed #d1d5db',
419+
borderRadius: '6px'
420+
}}>
421+
No actions defined. Click "Add Action" to create one.
422+
</div>
423+
)}
256424
</div>
257425
)}
258426

0 commit comments

Comments
 (0)