@principal-ai/principal-view-react 0.13.7 → 0.13.8
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/dist/components/GraphRenderer.d.ts +1 -15
- package/dist/components/GraphRenderer.d.ts.map +1 -1
- package/dist/components/GraphRenderer.js +94 -269
- package/dist/components/GraphRenderer.js.map +1 -1
- package/dist/components/NodeInfoPanel.d.ts.map +1 -1
- package/dist/components/NodeInfoPanel.js.map +1 -1
- package/dist/edges/CustomEdge.d.ts +2 -2
- package/dist/edges/CustomEdge.d.ts.map +1 -1
- package/dist/edges/CustomEdge.js +5 -4
- package/dist/edges/CustomEdge.js.map +1 -1
- package/dist/hooks/usePathBasedEvents.d.ts.map +1 -1
- package/dist/hooks/usePathBasedEvents.js +2 -1
- package/dist/hooks/usePathBasedEvents.js.map +1 -1
- package/dist/index.d.ts +0 -4
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +0 -2
- package/dist/index.js.map +1 -1
- package/dist/nodes/CustomNode.d.ts +2 -2
- package/dist/nodes/CustomNode.d.ts.map +1 -1
- package/dist/nodes/CustomNode.js +41 -10
- package/dist/nodes/CustomNode.js.map +1 -1
- package/package.json +1 -1
- package/src/components/GraphRenderer.tsx +106 -354
- package/src/edges/CustomEdge.tsx +8 -7
- package/src/hooks/usePathBasedEvents.ts +2 -1
- package/src/index.ts +0 -6
- package/src/nodes/CustomNode.tsx +50 -13
- package/src/stories/ColorPriority.stories.tsx +2 -0
- package/src/stories/EventDrivenAnimations.stories.tsx +332 -326
- package/src/stories/GraphRenderer.stories.tsx +2 -2
- package/src/stories/NodeDefinitionComparison.stories.tsx +1 -1
- package/src/stories/NodeDimensionsTesting.stories.tsx +2 -2
- package/src/stories/OtelComponents.stories.tsx +0 -47
- package/src/stories/data/graph-converter-test-execution.json +244 -26
- package/src/stories/data/graph-converter-validated-execution.json +6 -6
- package/src/components/EdgeInfoPanel.tsx +0 -247
- package/src/components/NodeInfoPanel.tsx +0 -724
|
@@ -1,724 +0,0 @@
|
|
|
1
|
-
import React, { useState } from 'react';
|
|
2
|
-
import type { NodeState, NodeTypeDefinition, JsonValue, PVEventSchema } from '@principal-ai/principal-view-core';
|
|
3
|
-
import { useTheme } from '@principal-ade/industry-theme';
|
|
4
|
-
import { resolveIcon } from '../utils/iconResolver';
|
|
5
|
-
|
|
6
|
-
// Common icons for the icon selector
|
|
7
|
-
const COMMON_ICONS = [
|
|
8
|
-
'Settings',
|
|
9
|
-
'Database',
|
|
10
|
-
'Package',
|
|
11
|
-
'Server',
|
|
12
|
-
'Cloud',
|
|
13
|
-
'Globe',
|
|
14
|
-
'File',
|
|
15
|
-
'Folder',
|
|
16
|
-
'Code',
|
|
17
|
-
'Terminal',
|
|
18
|
-
'Cpu',
|
|
19
|
-
'HardDrive',
|
|
20
|
-
'Network',
|
|
21
|
-
'Wifi',
|
|
22
|
-
'Lock',
|
|
23
|
-
'Unlock',
|
|
24
|
-
'Key',
|
|
25
|
-
'Shield',
|
|
26
|
-
'User',
|
|
27
|
-
'Users',
|
|
28
|
-
'Mail',
|
|
29
|
-
'MessageSquare',
|
|
30
|
-
'Bell',
|
|
31
|
-
'Calendar',
|
|
32
|
-
'Clock',
|
|
33
|
-
'Timer',
|
|
34
|
-
'Zap',
|
|
35
|
-
'Activity',
|
|
36
|
-
'BarChart',
|
|
37
|
-
'PieChart',
|
|
38
|
-
'CheckCircle',
|
|
39
|
-
'XCircle',
|
|
40
|
-
'AlertCircle',
|
|
41
|
-
'Info',
|
|
42
|
-
'HelpCircle',
|
|
43
|
-
'Play',
|
|
44
|
-
'Pause',
|
|
45
|
-
'Square',
|
|
46
|
-
'Circle',
|
|
47
|
-
'Triangle',
|
|
48
|
-
'Hexagon',
|
|
49
|
-
'Box',
|
|
50
|
-
'Layers',
|
|
51
|
-
'GitBranch',
|
|
52
|
-
'GitCommit',
|
|
53
|
-
'GitMerge',
|
|
54
|
-
'GitPullRequest',
|
|
55
|
-
];
|
|
56
|
-
|
|
57
|
-
export interface NodeInfoPanelProps {
|
|
58
|
-
node: NodeState;
|
|
59
|
-
typeDefinition: NodeTypeDefinition;
|
|
60
|
-
/** Available node types for the type selector */
|
|
61
|
-
availableNodeTypes?: Record<string, NodeTypeDefinition>;
|
|
62
|
-
onClose: () => void;
|
|
63
|
-
/** Optional callback to delete the node. If not provided, delete button is hidden. */
|
|
64
|
-
onDelete?: (nodeId: string) => void;
|
|
65
|
-
/** Optional callback to update the node. If not provided, edit fields are disabled. */
|
|
66
|
-
onUpdate?: (nodeId: string, updates: { type?: string; data?: Record<string, JsonValue> }) => void;
|
|
67
|
-
/** Optional callback to resolve event references to full event schemas */
|
|
68
|
-
resolveEventRef?: (eventRef: string) => PVEventSchema | undefined;
|
|
69
|
-
}
|
|
70
|
-
|
|
71
|
-
/**
|
|
72
|
-
* Panel that displays information about a selected node with optional editing
|
|
73
|
-
*/
|
|
74
|
-
export const NodeInfoPanel: React.FC<NodeInfoPanelProps> = ({
|
|
75
|
-
node,
|
|
76
|
-
typeDefinition,
|
|
77
|
-
availableNodeTypes,
|
|
78
|
-
onClose,
|
|
79
|
-
onDelete,
|
|
80
|
-
onUpdate,
|
|
81
|
-
resolveEventRef,
|
|
82
|
-
}) => {
|
|
83
|
-
const { theme } = useTheme();
|
|
84
|
-
|
|
85
|
-
// Color priority: node data color > type definition color > theme primary
|
|
86
|
-
const nodeColor = (node.data?.color as string) || typeDefinition?.color || theme.colors.primary;
|
|
87
|
-
const canEdit = Boolean(onUpdate);
|
|
88
|
-
|
|
89
|
-
// Local state for UI
|
|
90
|
-
const [showIconPicker, setShowIconPicker] = useState(false);
|
|
91
|
-
const [showDetails, setShowDetails] = useState(false);
|
|
92
|
-
const [isExpanded, setIsExpanded] = useState(false);
|
|
93
|
-
|
|
94
|
-
// Current icon - either from node data override or type definition
|
|
95
|
-
const currentIcon = (node.data?.icon as string) || typeDefinition?.icon;
|
|
96
|
-
|
|
97
|
-
// Find the name field from data schema
|
|
98
|
-
const nameField = typeDefinition?.dataSchema
|
|
99
|
-
? Object.entries(typeDefinition.dataSchema).find(([, schema]) => schema.displayInLabel)?.[0]
|
|
100
|
-
: null;
|
|
101
|
-
|
|
102
|
-
// Get fields to display based on dataSchema
|
|
103
|
-
const displayFields = typeDefinition?.dataSchema
|
|
104
|
-
? Object.entries(typeDefinition.dataSchema)
|
|
105
|
-
.filter(([, schema]) => schema.displayInLabel)
|
|
106
|
-
.map(([field]) => ({
|
|
107
|
-
field,
|
|
108
|
-
label: field,
|
|
109
|
-
value: node.data?.[field],
|
|
110
|
-
}))
|
|
111
|
-
: [];
|
|
112
|
-
|
|
113
|
-
// Always show basic node data if no schema is defined
|
|
114
|
-
const hasSchemaFields = displayFields.length > 0;
|
|
115
|
-
// Internal fields that should not be displayed in the popup
|
|
116
|
-
const internalFields = [
|
|
117
|
-
'icon',
|
|
118
|
-
'name',
|
|
119
|
-
'description',
|
|
120
|
-
'sources',
|
|
121
|
-
'color',
|
|
122
|
-
'stroke',
|
|
123
|
-
'width',
|
|
124
|
-
'height',
|
|
125
|
-
'canvasType',
|
|
126
|
-
'text',
|
|
127
|
-
'file',
|
|
128
|
-
'url',
|
|
129
|
-
'shape',
|
|
130
|
-
'states',
|
|
131
|
-
'actions',
|
|
132
|
-
'nodeType',
|
|
133
|
-
'otel',
|
|
134
|
-
'resourceMatch',
|
|
135
|
-
'event', // Handle separately in Event section
|
|
136
|
-
'eventRef', // Handle separately in Event section
|
|
137
|
-
];
|
|
138
|
-
|
|
139
|
-
// Extract Event metadata
|
|
140
|
-
const eventInfo = node.data?.event as unknown as PVEventSchema | undefined;
|
|
141
|
-
const eventRef = node.data?.eventRef as unknown as string | undefined;
|
|
142
|
-
|
|
143
|
-
// Try to resolve eventRef to full event schema if callback is provided
|
|
144
|
-
const resolvedEvent = eventRef && resolveEventRef ? resolveEventRef(eventRef) : undefined;
|
|
145
|
-
|
|
146
|
-
const nodeDataEntries = node.data
|
|
147
|
-
? Object.entries(node.data).filter(([key]) => !internalFields.includes(key))
|
|
148
|
-
: [];
|
|
149
|
-
|
|
150
|
-
const handleTypeChange = (newType: string) => {
|
|
151
|
-
if (onUpdate && newType !== node.type) {
|
|
152
|
-
onUpdate(node.id, { type: newType });
|
|
153
|
-
}
|
|
154
|
-
};
|
|
155
|
-
|
|
156
|
-
const handleIconSelect = (iconName: string) => {
|
|
157
|
-
if (onUpdate) {
|
|
158
|
-
onUpdate(node.id, {
|
|
159
|
-
data: { ...node.data, icon: iconName },
|
|
160
|
-
});
|
|
161
|
-
}
|
|
162
|
-
setShowIconPicker(false);
|
|
163
|
-
};
|
|
164
|
-
|
|
165
|
-
return (
|
|
166
|
-
<div
|
|
167
|
-
style={{
|
|
168
|
-
position: 'absolute',
|
|
169
|
-
bottom: 0,
|
|
170
|
-
left: 0,
|
|
171
|
-
right: 0,
|
|
172
|
-
backgroundColor: theme.colors.background,
|
|
173
|
-
color: theme.colors.text,
|
|
174
|
-
borderTop: `2px solid ${nodeColor}`,
|
|
175
|
-
borderBottom: `2px solid ${nodeColor}`,
|
|
176
|
-
boxShadow: '0 -4px 12px rgba(0,0,0,0.15)',
|
|
177
|
-
padding: '16px 24px',
|
|
178
|
-
zIndex: 1000,
|
|
179
|
-
maxHeight: isExpanded ? '90%' : '40%',
|
|
180
|
-
overflowY: 'auto',
|
|
181
|
-
transition: 'max-height 0.3s ease',
|
|
182
|
-
}}
|
|
183
|
-
>
|
|
184
|
-
{/* Header - shows node name */}
|
|
185
|
-
<div
|
|
186
|
-
style={{
|
|
187
|
-
display: 'flex',
|
|
188
|
-
justifyContent: 'space-between',
|
|
189
|
-
alignItems: 'center',
|
|
190
|
-
marginBottom: '16px',
|
|
191
|
-
}}
|
|
192
|
-
>
|
|
193
|
-
<div style={{ display: 'flex', alignItems: 'center', gap: '12px', flex: 1 }}>
|
|
194
|
-
<div style={{ fontWeight: theme.fontWeights.bold, fontSize: theme.fontSizes[2], fontFamily: theme.fonts.body, color: nodeColor }}>
|
|
195
|
-
{node.name || node.id}
|
|
196
|
-
</div>
|
|
197
|
-
</div>
|
|
198
|
-
<div style={{ display: 'flex', gap: '4px' }}>
|
|
199
|
-
<button
|
|
200
|
-
onClick={() => setIsExpanded(!isExpanded)}
|
|
201
|
-
style={{
|
|
202
|
-
border: 'none',
|
|
203
|
-
background: 'none',
|
|
204
|
-
cursor: 'pointer',
|
|
205
|
-
fontSize: theme.fontSizes[2],
|
|
206
|
-
fontFamily: theme.fonts.body,
|
|
207
|
-
color: theme.colors.textSecondary,
|
|
208
|
-
padding: '4px',
|
|
209
|
-
width: '28px',
|
|
210
|
-
height: '28px',
|
|
211
|
-
display: 'flex',
|
|
212
|
-
alignItems: 'center',
|
|
213
|
-
justifyContent: 'center',
|
|
214
|
-
borderRadius: '4px',
|
|
215
|
-
transition: 'background-color 0.15s',
|
|
216
|
-
marginTop: '-8px',
|
|
217
|
-
}}
|
|
218
|
-
onMouseEnter={(e) => {
|
|
219
|
-
e.currentTarget.style.backgroundColor = theme.colors.muted;
|
|
220
|
-
}}
|
|
221
|
-
onMouseLeave={(e) => {
|
|
222
|
-
e.currentTarget.style.backgroundColor = 'transparent';
|
|
223
|
-
}}
|
|
224
|
-
title={isExpanded ? 'Collapse panel' : 'Expand panel'}
|
|
225
|
-
>
|
|
226
|
-
{isExpanded ? '▼' : '▲'}
|
|
227
|
-
</button>
|
|
228
|
-
<button
|
|
229
|
-
onClick={onClose}
|
|
230
|
-
style={{
|
|
231
|
-
border: 'none',
|
|
232
|
-
background: 'none',
|
|
233
|
-
cursor: 'pointer',
|
|
234
|
-
fontSize: theme.fontSizes[3],
|
|
235
|
-
fontFamily: theme.fonts.body,
|
|
236
|
-
color: theme.colors.textSecondary,
|
|
237
|
-
padding: '4px',
|
|
238
|
-
width: '28px',
|
|
239
|
-
height: '28px',
|
|
240
|
-
display: 'flex',
|
|
241
|
-
alignItems: 'center',
|
|
242
|
-
justifyContent: 'center',
|
|
243
|
-
borderRadius: '4px',
|
|
244
|
-
transition: 'background-color 0.15s',
|
|
245
|
-
marginRight: '-8px',
|
|
246
|
-
marginTop: '-8px',
|
|
247
|
-
}}
|
|
248
|
-
onMouseEnter={(e) => {
|
|
249
|
-
e.currentTarget.style.backgroundColor = theme.colors.muted;
|
|
250
|
-
}}
|
|
251
|
-
onMouseLeave={(e) => {
|
|
252
|
-
e.currentTarget.style.backgroundColor = 'transparent';
|
|
253
|
-
}}
|
|
254
|
-
>
|
|
255
|
-
×
|
|
256
|
-
</button>
|
|
257
|
-
</div>
|
|
258
|
-
</div>
|
|
259
|
-
|
|
260
|
-
{/* Content */}
|
|
261
|
-
<div>
|
|
262
|
-
{/* Description - first field under header */}
|
|
263
|
-
{node.data?.description && (
|
|
264
|
-
<div style={{ marginBottom: '12px' }}>
|
|
265
|
-
<div style={{ fontSize: theme.fontSizes[0], fontFamily: theme.fonts.body, color: theme.colors.textSecondary, marginBottom: '4px' }}>
|
|
266
|
-
Description
|
|
267
|
-
</div>
|
|
268
|
-
<div style={{ fontSize: theme.fontSizes[0], fontFamily: theme.fonts.body }}>{String(node.data.description)}</div>
|
|
269
|
-
</div>
|
|
270
|
-
)}
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
{/* Event Info */}
|
|
274
|
-
{(eventInfo || eventRef || resolvedEvent) && (
|
|
275
|
-
<div style={{ marginBottom: '12px', marginTop: '-4px' }}>
|
|
276
|
-
{(() => {
|
|
277
|
-
// Use resolved event if available, otherwise use inline eventInfo
|
|
278
|
-
const displayEvent = resolvedEvent || eventInfo;
|
|
279
|
-
|
|
280
|
-
if (displayEvent) {
|
|
281
|
-
// Show full event with schema
|
|
282
|
-
return (
|
|
283
|
-
<div>
|
|
284
|
-
<div
|
|
285
|
-
style={{
|
|
286
|
-
fontSize: theme.fontSizes[1],
|
|
287
|
-
fontFamily: 'monospace',
|
|
288
|
-
color: theme.colors.primary,
|
|
289
|
-
marginBottom: '4px',
|
|
290
|
-
}}
|
|
291
|
-
>
|
|
292
|
-
{displayEvent.name}
|
|
293
|
-
</div>
|
|
294
|
-
{displayEvent.description && (
|
|
295
|
-
<div style={{ fontSize: theme.fontSizes[0], fontFamily: theme.fonts.body, color: theme.colors.textSecondary, marginTop: '4px', marginBottom: '8px' }}>
|
|
296
|
-
{displayEvent.description}
|
|
297
|
-
</div>
|
|
298
|
-
)}
|
|
299
|
-
{displayEvent.attributes && Object.keys(displayEvent.attributes).length > 0 && (
|
|
300
|
-
<div style={{ marginTop: '16px' }}>
|
|
301
|
-
<div style={{ fontSize: theme.fontSizes[0], fontFamily: theme.fonts.body, color: theme.colors.textSecondary, marginBottom: '8px' }}>
|
|
302
|
-
Event Attributes
|
|
303
|
-
</div>
|
|
304
|
-
<div style={{ display: 'flex', flexDirection: 'column', gap: '12px' }}>
|
|
305
|
-
{Object.entries(displayEvent.attributes).map(([attrName, attrSchema]: [string, any]) => (
|
|
306
|
-
<div
|
|
307
|
-
key={attrName}
|
|
308
|
-
style={{
|
|
309
|
-
padding: '8px 12px',
|
|
310
|
-
backgroundColor: theme.colors.muted,
|
|
311
|
-
borderRadius: '4px',
|
|
312
|
-
border: `1px solid ${theme.colors.border}`,
|
|
313
|
-
}}
|
|
314
|
-
>
|
|
315
|
-
<div style={{ display: 'flex', alignItems: 'center', gap: '8px', marginBottom: '4px' }}>
|
|
316
|
-
<span
|
|
317
|
-
style={{
|
|
318
|
-
fontSize: theme.fontSizes[0],
|
|
319
|
-
fontFamily: 'monospace',
|
|
320
|
-
fontWeight: theme.fontWeights.semibold,
|
|
321
|
-
color: theme.colors.text,
|
|
322
|
-
}}
|
|
323
|
-
>
|
|
324
|
-
{attrName}
|
|
325
|
-
</span>
|
|
326
|
-
<span
|
|
327
|
-
style={{
|
|
328
|
-
fontSize: '10px',
|
|
329
|
-
fontFamily: theme.fonts.body,
|
|
330
|
-
padding: '2px 6px',
|
|
331
|
-
borderRadius: '3px',
|
|
332
|
-
backgroundColor: theme.colors.surface,
|
|
333
|
-
color: theme.colors.textSecondary,
|
|
334
|
-
border: `1px solid ${theme.colors.border}`,
|
|
335
|
-
}}
|
|
336
|
-
>
|
|
337
|
-
{attrSchema.type || 'any'}
|
|
338
|
-
</span>
|
|
339
|
-
{attrSchema.required && (
|
|
340
|
-
<span
|
|
341
|
-
style={{
|
|
342
|
-
fontSize: '10px',
|
|
343
|
-
fontFamily: theme.fonts.body,
|
|
344
|
-
fontWeight: theme.fontWeights.semibold,
|
|
345
|
-
padding: '2px 6px',
|
|
346
|
-
borderRadius: '3px',
|
|
347
|
-
backgroundColor: '#ef4444',
|
|
348
|
-
color: 'white',
|
|
349
|
-
}}
|
|
350
|
-
>
|
|
351
|
-
required
|
|
352
|
-
</span>
|
|
353
|
-
)}
|
|
354
|
-
</div>
|
|
355
|
-
{attrSchema.description && (
|
|
356
|
-
<div
|
|
357
|
-
style={{
|
|
358
|
-
fontSize: theme.fontSizes[0],
|
|
359
|
-
fontFamily: theme.fonts.body,
|
|
360
|
-
color: theme.colors.textSecondary,
|
|
361
|
-
marginTop: '4px',
|
|
362
|
-
}}
|
|
363
|
-
>
|
|
364
|
-
{attrSchema.description}
|
|
365
|
-
</div>
|
|
366
|
-
)}
|
|
367
|
-
</div>
|
|
368
|
-
))}
|
|
369
|
-
</div>
|
|
370
|
-
</div>
|
|
371
|
-
)}
|
|
372
|
-
</div>
|
|
373
|
-
);
|
|
374
|
-
} else if (eventRef) {
|
|
375
|
-
// Just show the reference string (not resolved)
|
|
376
|
-
return (
|
|
377
|
-
<div
|
|
378
|
-
style={{
|
|
379
|
-
fontSize: theme.fontSizes[0],
|
|
380
|
-
fontFamily: 'monospace',
|
|
381
|
-
padding: '6px 10px',
|
|
382
|
-
backgroundColor: theme.colors.muted,
|
|
383
|
-
borderRadius: '4px',
|
|
384
|
-
color: theme.colors.primary,
|
|
385
|
-
border: `1px solid ${theme.colors.border}`,
|
|
386
|
-
}}
|
|
387
|
-
>
|
|
388
|
-
{eventRef}
|
|
389
|
-
</div>
|
|
390
|
-
);
|
|
391
|
-
}
|
|
392
|
-
return null;
|
|
393
|
-
})()}
|
|
394
|
-
</div>
|
|
395
|
-
)}
|
|
396
|
-
</div>
|
|
397
|
-
|
|
398
|
-
{/* Expand/Collapse button for additional details - only show in editable mode */}
|
|
399
|
-
{canEdit && (
|
|
400
|
-
<button
|
|
401
|
-
onClick={() => setShowDetails(!showDetails)}
|
|
402
|
-
style={{
|
|
403
|
-
width: '100%',
|
|
404
|
-
padding: '8px',
|
|
405
|
-
backgroundColor: theme.colors.surface,
|
|
406
|
-
border: `1px solid ${theme.colors.border}`,
|
|
407
|
-
borderRadius: '4px',
|
|
408
|
-
cursor: 'pointer',
|
|
409
|
-
fontSize: theme.fontSizes[0],
|
|
410
|
-
fontFamily: theme.fonts.body,
|
|
411
|
-
color: theme.colors.textSecondary,
|
|
412
|
-
display: 'flex',
|
|
413
|
-
alignItems: 'center',
|
|
414
|
-
justifyContent: 'center',
|
|
415
|
-
gap: '6px',
|
|
416
|
-
marginBottom: showDetails ? '12px' : '0',
|
|
417
|
-
}}
|
|
418
|
-
>
|
|
419
|
-
<span
|
|
420
|
-
style={{
|
|
421
|
-
transform: showDetails ? 'rotate(180deg)' : 'rotate(0deg)',
|
|
422
|
-
transition: 'transform 0.2s',
|
|
423
|
-
}}
|
|
424
|
-
>
|
|
425
|
-
▼
|
|
426
|
-
</span>
|
|
427
|
-
{showDetails ? 'Hide Details' : 'Show Details'}
|
|
428
|
-
</button>
|
|
429
|
-
)}
|
|
430
|
-
|
|
431
|
-
{/* Expandable details section - only show in editable mode */}
|
|
432
|
-
{canEdit && showDetails && (
|
|
433
|
-
<>
|
|
434
|
-
{/* Icon Selector */}
|
|
435
|
-
<div style={{ marginBottom: '12px' }}>
|
|
436
|
-
<div
|
|
437
|
-
style={{ fontSize: theme.fontSizes[0], fontFamily: theme.fonts.body, color: theme.colors.textSecondary, marginBottom: '4px' }}
|
|
438
|
-
>
|
|
439
|
-
Icon
|
|
440
|
-
</div>
|
|
441
|
-
<div style={{ position: 'relative' }}>
|
|
442
|
-
<button
|
|
443
|
-
onClick={() => canEdit && setShowIconPicker(!showIconPicker)}
|
|
444
|
-
disabled={!canEdit}
|
|
445
|
-
style={{
|
|
446
|
-
display: 'flex',
|
|
447
|
-
alignItems: 'center',
|
|
448
|
-
gap: '8px',
|
|
449
|
-
padding: '6px 10px',
|
|
450
|
-
backgroundColor: theme.colors.surface,
|
|
451
|
-
border: canEdit
|
|
452
|
-
? `1px dashed ${theme.colors.border}`
|
|
453
|
-
: `1px solid ${theme.colors.border}`,
|
|
454
|
-
borderRadius: '4px',
|
|
455
|
-
cursor: canEdit ? 'pointer' : 'default',
|
|
456
|
-
fontSize: theme.fontSizes[0],
|
|
457
|
-
fontFamily: theme.fonts.body,
|
|
458
|
-
width: '100%',
|
|
459
|
-
justifyContent: 'flex-start',
|
|
460
|
-
color: theme.colors.text,
|
|
461
|
-
}}
|
|
462
|
-
>
|
|
463
|
-
<span style={{ display: 'flex', alignItems: 'center' }}>
|
|
464
|
-
{resolveIcon(currentIcon, 18)}
|
|
465
|
-
</span>
|
|
466
|
-
<span>{currentIcon || 'No icon'}</span>
|
|
467
|
-
{canEdit && (
|
|
468
|
-
<span
|
|
469
|
-
style={{ marginLeft: 'auto', color: theme.colors.textMuted, fontSize: theme.fontSizes[0], fontFamily: theme.fonts.body }}
|
|
470
|
-
>
|
|
471
|
-
✎
|
|
472
|
-
</span>
|
|
473
|
-
)}
|
|
474
|
-
</button>
|
|
475
|
-
|
|
476
|
-
{/* Icon Picker Dropdown */}
|
|
477
|
-
{showIconPicker && (
|
|
478
|
-
<div
|
|
479
|
-
style={{
|
|
480
|
-
position: 'absolute',
|
|
481
|
-
top: '100%',
|
|
482
|
-
left: 0,
|
|
483
|
-
right: 0,
|
|
484
|
-
marginTop: '4px',
|
|
485
|
-
backgroundColor: theme.colors.background,
|
|
486
|
-
border: `1px solid ${theme.colors.border}`,
|
|
487
|
-
borderRadius: '4px',
|
|
488
|
-
boxShadow: '0 4px 12px rgba(0,0,0,0.15)',
|
|
489
|
-
padding: '8px',
|
|
490
|
-
maxHeight: '200px',
|
|
491
|
-
overflowY: 'auto',
|
|
492
|
-
zIndex: 1001,
|
|
493
|
-
}}
|
|
494
|
-
>
|
|
495
|
-
<div
|
|
496
|
-
style={{
|
|
497
|
-
display: 'grid',
|
|
498
|
-
gridTemplateColumns: 'repeat(6, 1fr)',
|
|
499
|
-
gap: '4px',
|
|
500
|
-
}}
|
|
501
|
-
>
|
|
502
|
-
{COMMON_ICONS.map((iconName) => (
|
|
503
|
-
<button
|
|
504
|
-
key={iconName}
|
|
505
|
-
onClick={() => handleIconSelect(iconName)}
|
|
506
|
-
title={iconName}
|
|
507
|
-
style={{
|
|
508
|
-
padding: '6px',
|
|
509
|
-
border:
|
|
510
|
-
currentIcon === iconName
|
|
511
|
-
? `2px solid ${nodeColor}`
|
|
512
|
-
: `1px solid ${theme.colors.border}`,
|
|
513
|
-
borderRadius: '4px',
|
|
514
|
-
backgroundColor:
|
|
515
|
-
currentIcon === iconName
|
|
516
|
-
? theme.colors.highlight
|
|
517
|
-
: theme.colors.background,
|
|
518
|
-
cursor: 'pointer',
|
|
519
|
-
display: 'flex',
|
|
520
|
-
alignItems: 'center',
|
|
521
|
-
justifyContent: 'center',
|
|
522
|
-
color: theme.colors.text,
|
|
523
|
-
}}
|
|
524
|
-
>
|
|
525
|
-
{resolveIcon(iconName, 16)}
|
|
526
|
-
</button>
|
|
527
|
-
))}
|
|
528
|
-
</div>
|
|
529
|
-
</div>
|
|
530
|
-
)}
|
|
531
|
-
</div>
|
|
532
|
-
</div>
|
|
533
|
-
|
|
534
|
-
{/* Node Type - Editable if availableNodeTypes provided */}
|
|
535
|
-
<div style={{ marginBottom: '12px' }}>
|
|
536
|
-
<div
|
|
537
|
-
style={{ fontSize: theme.fontSizes[0], fontFamily: theme.fonts.body, color: theme.colors.textSecondary, marginBottom: '4px' }}
|
|
538
|
-
>
|
|
539
|
-
Type
|
|
540
|
-
</div>
|
|
541
|
-
{canEdit && availableNodeTypes && Object.keys(availableNodeTypes).length > 1 ? (
|
|
542
|
-
<select
|
|
543
|
-
value={node.type}
|
|
544
|
-
onChange={(e) => handleTypeChange(e.target.value)}
|
|
545
|
-
style={{
|
|
546
|
-
fontSize: theme.fontSizes[0],
|
|
547
|
-
fontFamily: theme.fonts.body,
|
|
548
|
-
padding: '4px 8px',
|
|
549
|
-
borderRadius: '4px',
|
|
550
|
-
border: `1px solid ${theme.colors.border}`,
|
|
551
|
-
backgroundColor: theme.colors.background,
|
|
552
|
-
color: theme.colors.text,
|
|
553
|
-
cursor: 'pointer',
|
|
554
|
-
width: '100%',
|
|
555
|
-
}}
|
|
556
|
-
>
|
|
557
|
-
{Object.entries(availableNodeTypes).map(([typeName, typeDef]) => (
|
|
558
|
-
<option key={typeName} value={typeName}>
|
|
559
|
-
{typeName} ({typeDef.shape})
|
|
560
|
-
</option>
|
|
561
|
-
))}
|
|
562
|
-
</select>
|
|
563
|
-
) : (
|
|
564
|
-
<div
|
|
565
|
-
style={{
|
|
566
|
-
fontSize: theme.fontSizes[0],
|
|
567
|
-
fontFamily: theme.fonts.body,
|
|
568
|
-
padding: '4px 8px',
|
|
569
|
-
backgroundColor: nodeColor,
|
|
570
|
-
color: theme.colors.background,
|
|
571
|
-
borderRadius: '4px',
|
|
572
|
-
display: 'inline-block',
|
|
573
|
-
}}
|
|
574
|
-
>
|
|
575
|
-
{node.type}
|
|
576
|
-
</div>
|
|
577
|
-
)}
|
|
578
|
-
</div>
|
|
579
|
-
|
|
580
|
-
{/* Node State */}
|
|
581
|
-
{node.state && (
|
|
582
|
-
<div style={{ marginBottom: '12px' }}>
|
|
583
|
-
<div
|
|
584
|
-
style={{ fontSize: theme.fontSizes[0], fontFamily: theme.fonts.body, color: theme.colors.textSecondary, marginBottom: '4px' }}
|
|
585
|
-
>
|
|
586
|
-
State
|
|
587
|
-
</div>
|
|
588
|
-
<div
|
|
589
|
-
style={{
|
|
590
|
-
fontSize: theme.fontSizes[0],
|
|
591
|
-
fontFamily: theme.fonts.body,
|
|
592
|
-
padding: '4px 8px',
|
|
593
|
-
backgroundColor:
|
|
594
|
-
typeDefinition?.states?.[node.state]?.color || theme.colors.secondary,
|
|
595
|
-
color: theme.colors.background,
|
|
596
|
-
borderRadius: '4px',
|
|
597
|
-
display: 'inline-block',
|
|
598
|
-
}}
|
|
599
|
-
>
|
|
600
|
-
{typeDefinition?.states?.[node.state]?.label || node.state}
|
|
601
|
-
</div>
|
|
602
|
-
</div>
|
|
603
|
-
)}
|
|
604
|
-
|
|
605
|
-
{/* Display other schema-defined fields (non-editable for now) */}
|
|
606
|
-
{hasSchemaFields && displayFields.filter((f) => f.field !== nameField).length > 0 && (
|
|
607
|
-
<div style={{ marginBottom: '12px' }}>
|
|
608
|
-
<div
|
|
609
|
-
style={{
|
|
610
|
-
fontSize: theme.fontSizes[0],
|
|
611
|
-
fontFamily: theme.fonts.body,
|
|
612
|
-
color: theme.colors.textSecondary,
|
|
613
|
-
marginBottom: '8px',
|
|
614
|
-
fontWeight: theme.fontWeights.bold,
|
|
615
|
-
}}
|
|
616
|
-
>
|
|
617
|
-
Properties
|
|
618
|
-
</div>
|
|
619
|
-
{displayFields
|
|
620
|
-
.filter((f) => f.field !== nameField)
|
|
621
|
-
.map(({ field, label, value }) => (
|
|
622
|
-
<div key={field} style={{ marginBottom: '8px' }}>
|
|
623
|
-
<div
|
|
624
|
-
style={{
|
|
625
|
-
fontSize: '10px',
|
|
626
|
-
color: theme.colors.textSecondary,
|
|
627
|
-
marginBottom: '2px',
|
|
628
|
-
}}
|
|
629
|
-
>
|
|
630
|
-
{label}
|
|
631
|
-
</div>
|
|
632
|
-
<div style={{ fontSize: theme.fontSizes[0], fontFamily: theme.fonts.body }}>
|
|
633
|
-
{value !== undefined && value !== null
|
|
634
|
-
? typeof value === 'object'
|
|
635
|
-
? JSON.stringify(value, null, 2)
|
|
636
|
-
: String(value)
|
|
637
|
-
: '-'}
|
|
638
|
-
</div>
|
|
639
|
-
</div>
|
|
640
|
-
))}
|
|
641
|
-
</div>
|
|
642
|
-
)}
|
|
643
|
-
|
|
644
|
-
{/* Show all node data if no schema is defined */}
|
|
645
|
-
{!hasSchemaFields && nodeDataEntries.length > 0 && (
|
|
646
|
-
<div style={{ marginBottom: '12px' }}>
|
|
647
|
-
<div
|
|
648
|
-
style={{
|
|
649
|
-
fontSize: theme.fontSizes[0],
|
|
650
|
-
fontFamily: theme.fonts.body,
|
|
651
|
-
color: theme.colors.textSecondary,
|
|
652
|
-
marginBottom: '8px',
|
|
653
|
-
fontWeight: theme.fontWeights.bold,
|
|
654
|
-
}}
|
|
655
|
-
>
|
|
656
|
-
Data
|
|
657
|
-
</div>
|
|
658
|
-
{nodeDataEntries.map(([key, value]) => (
|
|
659
|
-
<div key={key} style={{ marginBottom: '8px' }}>
|
|
660
|
-
<div
|
|
661
|
-
style={{
|
|
662
|
-
fontSize: theme.fontSizes[0],
|
|
663
|
-
fontFamily: theme.fonts.body,
|
|
664
|
-
color: theme.colors.textSecondary,
|
|
665
|
-
marginBottom: '2px',
|
|
666
|
-
}}
|
|
667
|
-
>
|
|
668
|
-
{key}
|
|
669
|
-
</div>
|
|
670
|
-
<div style={{ fontSize: theme.fontSizes[0], fontFamily: theme.fonts.body, wordBreak: 'break-word' }}>
|
|
671
|
-
{value !== undefined && value !== null
|
|
672
|
-
? typeof value === 'object'
|
|
673
|
-
? JSON.stringify(value, null, 2)
|
|
674
|
-
: String(value)
|
|
675
|
-
: '-'}
|
|
676
|
-
</div>
|
|
677
|
-
</div>
|
|
678
|
-
))}
|
|
679
|
-
</div>
|
|
680
|
-
)}
|
|
681
|
-
|
|
682
|
-
{/* Metadata */}
|
|
683
|
-
<div
|
|
684
|
-
style={{
|
|
685
|
-
fontSize: theme.fontSizes[0],
|
|
686
|
-
fontFamily: theme.fonts.body,
|
|
687
|
-
color: theme.colors.textMuted,
|
|
688
|
-
marginTop: '12px',
|
|
689
|
-
paddingTop: '8px',
|
|
690
|
-
borderTop: `1px solid ${theme.colors.border}`,
|
|
691
|
-
}}
|
|
692
|
-
>
|
|
693
|
-
ID: {node.id}
|
|
694
|
-
</div>
|
|
695
|
-
</>
|
|
696
|
-
)}
|
|
697
|
-
|
|
698
|
-
{/* Delete Button */}
|
|
699
|
-
{onDelete && (
|
|
700
|
-
<button
|
|
701
|
-
onClick={() => {
|
|
702
|
-
onDelete(node.id);
|
|
703
|
-
onClose();
|
|
704
|
-
}}
|
|
705
|
-
style={{
|
|
706
|
-
marginTop: '12px',
|
|
707
|
-
width: '100%',
|
|
708
|
-
padding: '8px 12px',
|
|
709
|
-
backgroundColor: theme.colors.error,
|
|
710
|
-
color: theme.colors.background,
|
|
711
|
-
border: 'none',
|
|
712
|
-
borderRadius: '4px',
|
|
713
|
-
cursor: 'pointer',
|
|
714
|
-
fontSize: theme.fontSizes[0],
|
|
715
|
-
fontFamily: theme.fonts.body,
|
|
716
|
-
fontWeight: theme.fontWeights.bold,
|
|
717
|
-
}}
|
|
718
|
-
>
|
|
719
|
-
Delete Node
|
|
720
|
-
</button>
|
|
721
|
-
)}
|
|
722
|
-
</div>
|
|
723
|
-
);
|
|
724
|
-
};
|