@principal-ai/principal-view-react 0.6.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/README.md +111 -0
- package/dist/components/ConfigurationSelector.d.ts +37 -0
- package/dist/components/ConfigurationSelector.d.ts.map +1 -0
- package/dist/components/ConfigurationSelector.js +67 -0
- package/dist/components/ConfigurationSelector.js.map +1 -0
- package/dist/components/EdgeInfoPanel.d.ts +16 -0
- package/dist/components/EdgeInfoPanel.d.ts.map +1 -0
- package/dist/components/EdgeInfoPanel.js +85 -0
- package/dist/components/EdgeInfoPanel.js.map +1 -0
- package/dist/components/EventLog.d.ts +20 -0
- package/dist/components/EventLog.d.ts.map +1 -0
- package/dist/components/EventLog.js +13 -0
- package/dist/components/EventLog.js.map +1 -0
- package/dist/components/EventLog.test.d.ts +2 -0
- package/dist/components/EventLog.test.d.ts.map +1 -0
- package/dist/components/EventLog.test.js +73 -0
- package/dist/components/EventLog.test.js.map +1 -0
- package/dist/components/GraphRenderer.d.ts +121 -0
- package/dist/components/GraphRenderer.d.ts.map +1 -0
- package/dist/components/GraphRenderer.js +809 -0
- package/dist/components/GraphRenderer.js.map +1 -0
- package/dist/components/GraphRenderer.test.d.ts +2 -0
- package/dist/components/GraphRenderer.test.d.ts.map +1 -0
- package/dist/components/GraphRenderer.test.js +88 -0
- package/dist/components/GraphRenderer.test.js.map +1 -0
- package/dist/components/MetricsDashboard.d.ts +14 -0
- package/dist/components/MetricsDashboard.d.ts.map +1 -0
- package/dist/components/MetricsDashboard.js +13 -0
- package/dist/components/MetricsDashboard.js.map +1 -0
- package/dist/components/NodeInfoPanel.d.ts +21 -0
- package/dist/components/NodeInfoPanel.d.ts.map +1 -0
- package/dist/components/NodeInfoPanel.js +217 -0
- package/dist/components/NodeInfoPanel.js.map +1 -0
- package/dist/edges/CustomEdge.d.ts +16 -0
- package/dist/edges/CustomEdge.d.ts.map +1 -0
- package/dist/edges/CustomEdge.js +200 -0
- package/dist/edges/CustomEdge.js.map +1 -0
- package/dist/edges/GenericEdge.d.ts +18 -0
- package/dist/edges/GenericEdge.d.ts.map +1 -0
- package/dist/edges/GenericEdge.js +14 -0
- package/dist/edges/GenericEdge.js.map +1 -0
- package/dist/hooks/usePathBasedEvents.d.ts +42 -0
- package/dist/hooks/usePathBasedEvents.d.ts.map +1 -0
- package/dist/hooks/usePathBasedEvents.js +122 -0
- package/dist/hooks/usePathBasedEvents.js.map +1 -0
- package/dist/index.d.ts +33 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +41 -0
- package/dist/index.js.map +1 -0
- package/dist/nodes/CustomNode.d.ts +18 -0
- package/dist/nodes/CustomNode.d.ts.map +1 -0
- package/dist/nodes/CustomNode.js +298 -0
- package/dist/nodes/CustomNode.js.map +1 -0
- package/dist/nodes/GenericNode.d.ts +20 -0
- package/dist/nodes/GenericNode.d.ts.map +1 -0
- package/dist/nodes/GenericNode.js +24 -0
- package/dist/nodes/GenericNode.js.map +1 -0
- package/dist/utils/animationMapping.d.ts +53 -0
- package/dist/utils/animationMapping.d.ts.map +1 -0
- package/dist/utils/animationMapping.js +133 -0
- package/dist/utils/animationMapping.js.map +1 -0
- package/dist/utils/graphConverter.d.ts +22 -0
- package/dist/utils/graphConverter.d.ts.map +1 -0
- package/dist/utils/graphConverter.js +176 -0
- package/dist/utils/graphConverter.js.map +1 -0
- package/dist/utils/iconResolver.d.ts +29 -0
- package/dist/utils/iconResolver.d.ts.map +1 -0
- package/dist/utils/iconResolver.js +68 -0
- package/dist/utils/iconResolver.js.map +1 -0
- package/package.json +61 -0
- package/src/components/ConfigurationSelector.tsx +147 -0
- package/src/components/EdgeInfoPanel.tsx +198 -0
- package/src/components/EventLog.test.tsx +85 -0
- package/src/components/EventLog.tsx +51 -0
- package/src/components/GraphRenderer.test.tsx +118 -0
- package/src/components/GraphRenderer.tsx +1222 -0
- package/src/components/MetricsDashboard.tsx +40 -0
- package/src/components/NodeInfoPanel.tsx +425 -0
- package/src/edges/CustomEdge.tsx +344 -0
- package/src/edges/GenericEdge.tsx +40 -0
- package/src/hooks/usePathBasedEvents.ts +182 -0
- package/src/index.ts +67 -0
- package/src/nodes/CustomNode.tsx +432 -0
- package/src/nodes/GenericNode.tsx +54 -0
- package/src/stories/AnimationWorkshop.stories.tsx +608 -0
- package/src/stories/EventDrivenAnimations.stories.tsx +499 -0
- package/src/stories/EventLog.stories.tsx +161 -0
- package/src/stories/GraphRenderer.stories.tsx +628 -0
- package/src/stories/Introduction.mdx +51 -0
- package/src/stories/MetricsDashboard.stories.tsx +227 -0
- package/src/stories/MultiConfig.stories.tsx +531 -0
- package/src/stories/MultiDirectionalConnections.stories.tsx +345 -0
- package/src/stories/NodeShapes.stories.tsx +769 -0
- package/src/utils/animationMapping.ts +170 -0
- package/src/utils/graphConverter.ts +218 -0
- package/src/utils/iconResolver.tsx +49 -0
package/README.md
ADDED
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
# @principal-ai/visual-validation-react
|
|
2
|
+
|
|
3
|
+
React UI **component library** for the Visual Validation Framework.
|
|
4
|
+
|
|
5
|
+
This library provides **building blocks** for creating graph visualization applications. The actual "panel" application should be built separately using these components.
|
|
6
|
+
|
|
7
|
+
## Features
|
|
8
|
+
|
|
9
|
+
- 🎨 `GraphRenderer` - Interactive graph visualization with @xyflow/react
|
|
10
|
+
- Custom node shapes (circle, rectangle, hexagon, diamond)
|
|
11
|
+
- State-based node styling
|
|
12
|
+
- Animated and styled edges
|
|
13
|
+
- Auto-layout algorithms (hierarchical, circular)
|
|
14
|
+
- Zoom, pan, drag interactions
|
|
15
|
+
- Minimap and controls
|
|
16
|
+
- 📝 `EventLog` - Event log component (coming soon)
|
|
17
|
+
- 📊 `MetricsDashboard` - Metrics dashboard component (coming soon)
|
|
18
|
+
- 🎭 `CustomNode` / `CustomEdge` - Configurable xyflow renderers
|
|
19
|
+
|
|
20
|
+
## Installation
|
|
21
|
+
|
|
22
|
+
```bash
|
|
23
|
+
npm install @principal-ai/visual-validation-react
|
|
24
|
+
# or
|
|
25
|
+
bun add @principal-ai/visual-validation-react
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
## Peer Dependencies
|
|
29
|
+
|
|
30
|
+
```json
|
|
31
|
+
{
|
|
32
|
+
"react": "^18.0.0 || ^19.0.0",
|
|
33
|
+
"react-dom": "^18.0.0 || ^19.0.0"
|
|
34
|
+
}
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
## Usage
|
|
38
|
+
|
|
39
|
+
These are **building block components**. You compose them to build your panel application:
|
|
40
|
+
|
|
41
|
+
```typescript
|
|
42
|
+
import {
|
|
43
|
+
GraphRenderer,
|
|
44
|
+
EventLog,
|
|
45
|
+
MetricsDashboard,
|
|
46
|
+
} from '@principal-ai/visual-validation-react';
|
|
47
|
+
import { EventProcessor } from '@principal-ai/visual-validation-core';
|
|
48
|
+
import type { GraphConfiguration } from '@principal-ai/visual-validation-core';
|
|
49
|
+
|
|
50
|
+
const config: GraphConfiguration = {
|
|
51
|
+
// ... your configuration
|
|
52
|
+
};
|
|
53
|
+
|
|
54
|
+
function MyPanel() {
|
|
55
|
+
const processor = new EventProcessor(config);
|
|
56
|
+
const state = processor.getGraphState();
|
|
57
|
+
const events = processor.getEventHistory();
|
|
58
|
+
|
|
59
|
+
return (
|
|
60
|
+
<div style={{ display: 'grid', gridTemplateColumns: '2fr 1fr' }}>
|
|
61
|
+
<GraphRenderer
|
|
62
|
+
configuration={config}
|
|
63
|
+
nodes={Array.from(state.nodes.values())}
|
|
64
|
+
edges={Array.from(state.edges.values())}
|
|
65
|
+
/>
|
|
66
|
+
<div>
|
|
67
|
+
<MetricsDashboard metrics={/* ... */} />
|
|
68
|
+
<EventLog events={events} />
|
|
69
|
+
</div>
|
|
70
|
+
</div>
|
|
71
|
+
);
|
|
72
|
+
}
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
**Building a Complete Panel:**
|
|
76
|
+
For a complete panel application with all features (playback controls, filters, etc.), you should create a separate project using a panel starter template and compose these components together.
|
|
77
|
+
|
|
78
|
+
## Documentation
|
|
79
|
+
|
|
80
|
+
Comprehensive guides with Mermaid diagrams:
|
|
81
|
+
|
|
82
|
+
- **[Configuration Guide](../../docs/CONFIGURATION.md)** - Define your graph structure, node/edge types, and validation rules
|
|
83
|
+
- **[Event System Guide](../../docs/EVENT_SYSTEM.md)** - Stream events to update your graph in real-time
|
|
84
|
+
- **[Usage Guide](../../docs/USAGE.md)** - Build complete panels with React components
|
|
85
|
+
|
|
86
|
+
Or browse the [full documentation index](../../docs/README.md).
|
|
87
|
+
|
|
88
|
+
## Storybook
|
|
89
|
+
|
|
90
|
+
Interactive component examples:
|
|
91
|
+
|
|
92
|
+
```bash
|
|
93
|
+
bun run storybook
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
## Status
|
|
97
|
+
|
|
98
|
+
**Alpha** - Core placeholder implemented. Full visualization features coming soon.
|
|
99
|
+
|
|
100
|
+
### TODO
|
|
101
|
+
- ✅ Integrate xyflow for graph visualization
|
|
102
|
+
- ✅ Implement node renderers with shapes and states
|
|
103
|
+
- ✅ Implement edge renderers with styles and animations
|
|
104
|
+
- ✅ Add auto-layout algorithms
|
|
105
|
+
- 🔲 Build event log panel with filtering
|
|
106
|
+
- 🔲 Build metrics dashboard with charts
|
|
107
|
+
- ✅ Add Storybook stories
|
|
108
|
+
|
|
109
|
+
## License
|
|
110
|
+
|
|
111
|
+
MIT
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import type { ConfigurationFile } from '@principal-ai/principal-view-core';
|
|
3
|
+
export interface ConfigurationSelectorProps {
|
|
4
|
+
/** Available configurations */
|
|
5
|
+
configurations: ConfigurationFile[];
|
|
6
|
+
/** Currently selected configuration name */
|
|
7
|
+
selectedConfig: string;
|
|
8
|
+
/** Callback when configuration changes */
|
|
9
|
+
onConfigChange: (configName: string) => void;
|
|
10
|
+
/** Optional custom className */
|
|
11
|
+
className?: string;
|
|
12
|
+
/** Optional custom styles */
|
|
13
|
+
style?: React.CSSProperties;
|
|
14
|
+
/** Whether to show description */
|
|
15
|
+
showDescription?: boolean;
|
|
16
|
+
/** Whether to show version */
|
|
17
|
+
showVersion?: boolean;
|
|
18
|
+
/** Label text for the selector */
|
|
19
|
+
label?: string;
|
|
20
|
+
}
|
|
21
|
+
/**
|
|
22
|
+
* Configuration selector component for switching between multiple graph configurations
|
|
23
|
+
*
|
|
24
|
+
* @example
|
|
25
|
+
* ```tsx
|
|
26
|
+
* const [selectedConfig, setSelectedConfig] = useState('simple-service');
|
|
27
|
+
*
|
|
28
|
+
* <ConfigurationSelector
|
|
29
|
+
* configurations={loadedConfigs}
|
|
30
|
+
* selectedConfig={selectedConfig}
|
|
31
|
+
* onConfigChange={setSelectedConfig}
|
|
32
|
+
* showDescription
|
|
33
|
+
* />
|
|
34
|
+
* ```
|
|
35
|
+
*/
|
|
36
|
+
export declare const ConfigurationSelector: React.FC<ConfigurationSelectorProps>;
|
|
37
|
+
//# sourceMappingURL=ConfigurationSelector.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ConfigurationSelector.d.ts","sourceRoot":"","sources":["../../src/components/ConfigurationSelector.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,OAAO,CAAC;AAC1B,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,mCAAmC,CAAC;AAE3E,MAAM,WAAW,0BAA0B;IACzC,+BAA+B;IAC/B,cAAc,EAAE,iBAAiB,EAAE,CAAC;IAEpC,4CAA4C;IAC5C,cAAc,EAAE,MAAM,CAAC;IAEvB,0CAA0C;IAC1C,cAAc,EAAE,CAAC,UAAU,EAAE,MAAM,KAAK,IAAI,CAAC;IAE7C,gCAAgC;IAChC,SAAS,CAAC,EAAE,MAAM,CAAC;IAEnB,6BAA6B;IAC7B,KAAK,CAAC,EAAE,KAAK,CAAC,aAAa,CAAC;IAE5B,kCAAkC;IAClC,eAAe,CAAC,EAAE,OAAO,CAAC;IAE1B,8BAA8B;IAC9B,WAAW,CAAC,EAAE,OAAO,CAAC;IAEtB,kCAAkC;IAClC,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAED;;;;;;;;;;;;;;GAcG;AACH,eAAO,MAAM,qBAAqB,EAAE,KAAK,CAAC,EAAE,CAAC,0BAA0B,CAsGtE,CAAC"}
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.ConfigurationSelector = void 0;
|
|
4
|
+
const jsx_runtime_1 = require("react/jsx-runtime");
|
|
5
|
+
/**
|
|
6
|
+
* Configuration selector component for switching between multiple graph configurations
|
|
7
|
+
*
|
|
8
|
+
* @example
|
|
9
|
+
* ```tsx
|
|
10
|
+
* const [selectedConfig, setSelectedConfig] = useState('simple-service');
|
|
11
|
+
*
|
|
12
|
+
* <ConfigurationSelector
|
|
13
|
+
* configurations={loadedConfigs}
|
|
14
|
+
* selectedConfig={selectedConfig}
|
|
15
|
+
* onConfigChange={setSelectedConfig}
|
|
16
|
+
* showDescription
|
|
17
|
+
* />
|
|
18
|
+
* ```
|
|
19
|
+
*/
|
|
20
|
+
const ConfigurationSelector = ({ configurations, selectedConfig, onConfigChange, className, style, showDescription = false, showVersion = false, label = 'Graph Configuration', }) => {
|
|
21
|
+
const handleChange = (event) => {
|
|
22
|
+
onConfigChange(event.target.value);
|
|
23
|
+
};
|
|
24
|
+
const selectedConfigData = configurations.find(c => c.name === selectedConfig);
|
|
25
|
+
const defaultStyle = {
|
|
26
|
+
backgroundColor: 'white',
|
|
27
|
+
padding: '12px 16px',
|
|
28
|
+
borderRadius: '8px',
|
|
29
|
+
boxShadow: '0 2px 8px rgba(0,0,0,0.1)',
|
|
30
|
+
fontFamily: '-apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif',
|
|
31
|
+
...style,
|
|
32
|
+
};
|
|
33
|
+
const labelStyle = {
|
|
34
|
+
display: 'block',
|
|
35
|
+
fontSize: '14px',
|
|
36
|
+
fontWeight: 600,
|
|
37
|
+
marginBottom: '8px',
|
|
38
|
+
color: '#333',
|
|
39
|
+
};
|
|
40
|
+
const selectStyle = {
|
|
41
|
+
width: '100%',
|
|
42
|
+
padding: '8px 12px',
|
|
43
|
+
fontSize: '14px',
|
|
44
|
+
border: '1px solid #ddd',
|
|
45
|
+
borderRadius: '4px',
|
|
46
|
+
backgroundColor: 'white',
|
|
47
|
+
cursor: 'pointer',
|
|
48
|
+
outline: 'none',
|
|
49
|
+
transition: 'border-color 0.2s',
|
|
50
|
+
};
|
|
51
|
+
const infoStyle = {
|
|
52
|
+
marginTop: '8px',
|
|
53
|
+
fontSize: '12px',
|
|
54
|
+
color: '#666',
|
|
55
|
+
lineHeight: '1.5',
|
|
56
|
+
};
|
|
57
|
+
if (configurations.length === 0) {
|
|
58
|
+
return ((0, jsx_runtime_1.jsx)("div", { className: className, style: defaultStyle, children: (0, jsx_runtime_1.jsx)("div", { style: { ...labelStyle, color: '#999' }, children: "No configurations available" }) }));
|
|
59
|
+
}
|
|
60
|
+
return ((0, jsx_runtime_1.jsxs)("div", { className: className, style: defaultStyle, children: [(0, jsx_runtime_1.jsx)("label", { style: labelStyle, htmlFor: "config-selector", children: label }), (0, jsx_runtime_1.jsx)("select", { id: "config-selector", value: selectedConfig, onChange: handleChange, style: selectStyle, onMouseEnter: (e) => {
|
|
61
|
+
e.currentTarget.style.borderColor = '#4A90E2';
|
|
62
|
+
}, onMouseLeave: (e) => {
|
|
63
|
+
e.currentTarget.style.borderColor = '#ddd';
|
|
64
|
+
}, children: configurations.map((config) => ((0, jsx_runtime_1.jsxs)("option", { value: config.name, children: [config.config.metadata.name, showVersion && config.config.metadata.version && ` (v${config.config.metadata.version})`] }, config.name))) }), selectedConfigData && (showDescription || showVersion) && ((0, jsx_runtime_1.jsxs)("div", { style: infoStyle, children: [showVersion && selectedConfigData.config.metadata.version && ((0, jsx_runtime_1.jsxs)("div", { style: { marginBottom: '4px' }, children: [(0, jsx_runtime_1.jsx)("strong", { children: "Version:" }), " ", selectedConfigData.config.metadata.version] })), showDescription && selectedConfigData.config.metadata.description && ((0, jsx_runtime_1.jsx)("div", { children: selectedConfigData.config.metadata.description }))] }))] }));
|
|
65
|
+
};
|
|
66
|
+
exports.ConfigurationSelector = ConfigurationSelector;
|
|
67
|
+
//# sourceMappingURL=ConfigurationSelector.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ConfigurationSelector.js","sourceRoot":"","sources":["../../src/components/ConfigurationSelector.tsx"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import type { EdgeState, EdgeTypeDefinition } from '@principal-ai/principal-view-core';
|
|
3
|
+
export interface EdgeInfoPanelProps {
|
|
4
|
+
edge: EdgeState;
|
|
5
|
+
typeDefinition: EdgeTypeDefinition;
|
|
6
|
+
sourceNodeId: string;
|
|
7
|
+
targetNodeId: string;
|
|
8
|
+
onClose: () => void;
|
|
9
|
+
/** Optional callback to delete the edge. If not provided, delete button is hidden. */
|
|
10
|
+
onDelete?: (edgeId: string) => void;
|
|
11
|
+
}
|
|
12
|
+
/**
|
|
13
|
+
* Panel that displays information about a selected edge
|
|
14
|
+
*/
|
|
15
|
+
export declare const EdgeInfoPanel: React.FC<EdgeInfoPanelProps>;
|
|
16
|
+
//# sourceMappingURL=EdgeInfoPanel.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"EdgeInfoPanel.d.ts","sourceRoot":"","sources":["../../src/components/EdgeInfoPanel.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,OAAO,CAAC;AAC1B,OAAO,KAAK,EAAE,SAAS,EAAE,kBAAkB,EAAE,MAAM,mCAAmC,CAAC;AAEvF,MAAM,WAAW,kBAAkB;IACjC,IAAI,EAAE,SAAS,CAAC;IAChB,cAAc,EAAE,kBAAkB,CAAC;IACnC,YAAY,EAAE,MAAM,CAAC;IACrB,YAAY,EAAE,MAAM,CAAC;IACrB,OAAO,EAAE,MAAM,IAAI,CAAC;IACpB,sFAAsF;IACtF,QAAQ,CAAC,EAAE,CAAC,MAAM,EAAE,MAAM,KAAK,IAAI,CAAC;CACrC;AAED;;GAEG;AACH,eAAO,MAAM,aAAa,EAAE,KAAK,CAAC,EAAE,CAAC,kBAAkB,CAqLtD,CAAC"}
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.EdgeInfoPanel = void 0;
|
|
4
|
+
const jsx_runtime_1 = require("react/jsx-runtime");
|
|
5
|
+
/**
|
|
6
|
+
* Panel that displays information about a selected edge
|
|
7
|
+
*/
|
|
8
|
+
const EdgeInfoPanel = ({ edge, typeDefinition, sourceNodeId, targetNodeId, onClose, onDelete, }) => {
|
|
9
|
+
const color = typeDefinition.color || '#888';
|
|
10
|
+
// Get fields to display based on dataSchema
|
|
11
|
+
const displayFields = typeDefinition.dataSchema
|
|
12
|
+
? Object.entries(typeDefinition.dataSchema)
|
|
13
|
+
.filter(([, schema]) => schema.displayInInfo)
|
|
14
|
+
.map(([field, schema]) => ({
|
|
15
|
+
field,
|
|
16
|
+
label: schema.label || field,
|
|
17
|
+
value: edge.data?.[field],
|
|
18
|
+
}))
|
|
19
|
+
: [];
|
|
20
|
+
// Always show basic edge data if no schema is defined
|
|
21
|
+
const hasSchemaFields = displayFields.length > 0;
|
|
22
|
+
const edgeDataEntries = edge.data ? Object.entries(edge.data) : [];
|
|
23
|
+
return ((0, jsx_runtime_1.jsxs)("div", { style: {
|
|
24
|
+
position: 'absolute',
|
|
25
|
+
top: '60px',
|
|
26
|
+
right: '20px',
|
|
27
|
+
backgroundColor: 'white',
|
|
28
|
+
borderRadius: '8px',
|
|
29
|
+
boxShadow: '0 4px 12px rgba(0,0,0,0.15)',
|
|
30
|
+
padding: '16px',
|
|
31
|
+
minWidth: '250px',
|
|
32
|
+
maxWidth: '350px',
|
|
33
|
+
zIndex: 1000,
|
|
34
|
+
}, children: [(0, jsx_runtime_1.jsxs)("div", { style: {
|
|
35
|
+
display: 'flex',
|
|
36
|
+
justifyContent: 'space-between',
|
|
37
|
+
alignItems: 'center',
|
|
38
|
+
marginBottom: '12px',
|
|
39
|
+
paddingBottom: '8px',
|
|
40
|
+
borderBottom: `2px solid ${color}`,
|
|
41
|
+
}, children: [(0, jsx_runtime_1.jsx)("div", { style: { fontWeight: 'bold', fontSize: '14px' }, children: "Edge Information" }), (0, jsx_runtime_1.jsx)("button", { onClick: onClose, style: {
|
|
42
|
+
border: 'none',
|
|
43
|
+
background: 'none',
|
|
44
|
+
cursor: 'pointer',
|
|
45
|
+
fontSize: '18px',
|
|
46
|
+
color: '#666',
|
|
47
|
+
padding: '0',
|
|
48
|
+
width: '24px',
|
|
49
|
+
height: '24px',
|
|
50
|
+
display: 'flex',
|
|
51
|
+
alignItems: 'center',
|
|
52
|
+
justifyContent: 'center',
|
|
53
|
+
}, children: "\u00D7" })] }), (0, jsx_runtime_1.jsxs)("div", { style: { marginBottom: '12px' }, children: [(0, jsx_runtime_1.jsx)("div", { style: { fontSize: '10px', color: '#666', marginBottom: '4px' }, children: "Type" }), (0, jsx_runtime_1.jsx)("div", { style: {
|
|
54
|
+
fontSize: '12px',
|
|
55
|
+
padding: '4px 8px',
|
|
56
|
+
backgroundColor: color,
|
|
57
|
+
color: 'white',
|
|
58
|
+
borderRadius: '4px',
|
|
59
|
+
display: 'inline-block',
|
|
60
|
+
}, children: edge.type })] }), (0, jsx_runtime_1.jsxs)("div", { style: { marginBottom: '12px' }, children: [(0, jsx_runtime_1.jsx)("div", { style: { fontSize: '10px', color: '#666', marginBottom: '4px' }, children: "Connection" }), (0, jsx_runtime_1.jsxs)("div", { style: { fontSize: '12px', color: '#333' }, children: [(0, jsx_runtime_1.jsx)("span", { style: { fontFamily: 'monospace', backgroundColor: '#f0f0f0', padding: '2px 6px', borderRadius: '3px' }, children: sourceNodeId }), (0, jsx_runtime_1.jsx)("span", { style: { margin: '0 8px', color: '#888' }, children: "\u2192" }), (0, jsx_runtime_1.jsx)("span", { style: { fontFamily: 'monospace', backgroundColor: '#f0f0f0', padding: '2px 6px', borderRadius: '3px' }, children: targetNodeId })] })] }), hasSchemaFields && ((0, jsx_runtime_1.jsxs)("div", { style: { marginBottom: '12px' }, children: [(0, jsx_runtime_1.jsx)("div", { style: { fontSize: '10px', color: '#666', marginBottom: '8px', fontWeight: 'bold' }, children: "Properties" }), displayFields.map(({ field, label, value }) => ((0, jsx_runtime_1.jsxs)("div", { style: { marginBottom: '8px' }, children: [(0, jsx_runtime_1.jsx)("div", { style: { fontSize: '10px', color: '#666', marginBottom: '2px' }, children: label }), (0, jsx_runtime_1.jsx)("div", { style: { fontSize: '12px', color: '#333' }, children: value !== undefined && value !== null
|
|
61
|
+
? typeof value === 'object'
|
|
62
|
+
? JSON.stringify(value, null, 2)
|
|
63
|
+
: String(value)
|
|
64
|
+
: '-' })] }, field)))] })), !hasSchemaFields && edgeDataEntries.length > 0 && ((0, jsx_runtime_1.jsxs)("div", { style: { marginBottom: '12px' }, children: [(0, jsx_runtime_1.jsx)("div", { style: { fontSize: '10px', color: '#666', marginBottom: '8px', fontWeight: 'bold' }, children: "Data" }), edgeDataEntries.map(([key, value]) => ((0, jsx_runtime_1.jsxs)("div", { style: { marginBottom: '8px' }, children: [(0, jsx_runtime_1.jsx)("div", { style: { fontSize: '10px', color: '#666', marginBottom: '2px' }, children: key }), (0, jsx_runtime_1.jsx)("div", { style: { fontSize: '12px', color: '#333', wordBreak: 'break-word' }, children: value !== undefined && value !== null
|
|
65
|
+
? typeof value === 'object'
|
|
66
|
+
? JSON.stringify(value, null, 2)
|
|
67
|
+
: String(value)
|
|
68
|
+
: '-' })] }, key)))] })), (0, jsx_runtime_1.jsxs)("div", { style: { fontSize: '10px', color: '#999', marginTop: '12px', paddingTop: '8px', borderTop: '1px solid #eee' }, children: ["ID: ", edge.id] }), onDelete && ((0, jsx_runtime_1.jsx)("button", { onClick: () => {
|
|
69
|
+
onDelete(edge.id);
|
|
70
|
+
onClose();
|
|
71
|
+
}, style: {
|
|
72
|
+
marginTop: '12px',
|
|
73
|
+
width: '100%',
|
|
74
|
+
padding: '8px 12px',
|
|
75
|
+
backgroundColor: '#dc3545',
|
|
76
|
+
color: 'white',
|
|
77
|
+
border: 'none',
|
|
78
|
+
borderRadius: '4px',
|
|
79
|
+
cursor: 'pointer',
|
|
80
|
+
fontSize: '12px',
|
|
81
|
+
fontWeight: 'bold',
|
|
82
|
+
}, children: "Delete Edge" }))] }));
|
|
83
|
+
};
|
|
84
|
+
exports.EdgeInfoPanel = EdgeInfoPanel;
|
|
85
|
+
//# sourceMappingURL=EdgeInfoPanel.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"EdgeInfoPanel.js","sourceRoot":"","sources":["../../src/components/EdgeInfoPanel.tsx"],"names":[],"mappings":";;;;AAaA;;GAEG;AACI,MAAM,aAAa,GAAiC,CAAC,EAC1D,IAAI,EACJ,cAAc,EACd,YAAY,EACZ,YAAY,EACZ,OAAO,EACP,QAAQ,GACT,EAAE,EAAE;IACH,MAAM,KAAK,GAAG,cAAc,CAAC,KAAK,IAAI,MAAM,CAAC;IAE7C,4CAA4C;IAC5C,MAAM,aAAa,GAAG,cAAc,CAAC,UAAU;QAC7C,CAAC,CAAC,MAAM,CAAC,OAAO,CAAC,cAAc,CAAC,UAAU,CAAC;aACtC,MAAM,CAAC,CAAC,CAAC,EAAE,MAAM,CAAC,EAAE,EAAE,CAAC,MAAM,CAAC,aAAa,CAAC;aAC5C,GAAG,CAAC,CAAC,CAAC,KAAK,EAAE,MAAM,CAAC,EAAE,EAAE,CAAC,CAAC;YACzB,KAAK;YACL,KAAK,EAAE,MAAM,CAAC,KAAK,IAAI,KAAK;YAC5B,KAAK,EAAE,IAAI,CAAC,IAAI,EAAE,CAAC,KAAK,CAAC;SAC1B,CAAC,CAAC;QACP,CAAC,CAAC,EAAE,CAAC;IAEP,sDAAsD;IACtD,MAAM,eAAe,GAAG,aAAa,CAAC,MAAM,GAAG,CAAC,CAAC;IACjD,MAAM,eAAe,GAAG,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,MAAM,CAAC,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC;IAEnE,OAAO,CACL,iCACE,KAAK,EAAE;YACL,QAAQ,EAAE,UAAU;YACpB,GAAG,EAAE,MAAM;YACX,KAAK,EAAE,MAAM;YACb,eAAe,EAAE,OAAO;YACxB,YAAY,EAAE,KAAK;YACnB,SAAS,EAAE,6BAA6B;YACxC,OAAO,EAAE,MAAM;YACf,QAAQ,EAAE,OAAO;YACjB,QAAQ,EAAE,OAAO;YACjB,MAAM,EAAE,IAAI;SACb,aAGD,iCAAK,KAAK,EAAE;oBACV,OAAO,EAAE,MAAM;oBACf,cAAc,EAAE,eAAe;oBAC/B,UAAU,EAAE,QAAQ;oBACpB,YAAY,EAAE,MAAM;oBACpB,aAAa,EAAE,KAAK;oBACpB,YAAY,EAAE,aAAa,KAAK,EAAE;iBACnC,aACC,gCAAK,KAAK,EAAE,EAAE,UAAU,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,EAAE,iCAE9C,EACN,mCACE,OAAO,EAAE,OAAO,EAChB,KAAK,EAAE;4BACL,MAAM,EAAE,MAAM;4BACd,UAAU,EAAE,MAAM;4BAClB,MAAM,EAAE,SAAS;4BACjB,QAAQ,EAAE,MAAM;4BAChB,KAAK,EAAE,MAAM;4BACb,OAAO,EAAE,GAAG;4BACZ,KAAK,EAAE,MAAM;4BACb,MAAM,EAAE,MAAM;4BACd,OAAO,EAAE,MAAM;4BACf,UAAU,EAAE,QAAQ;4BACpB,cAAc,EAAE,QAAQ;yBACzB,uBAGM,IACL,EAGN,iCAAK,KAAK,EAAE,EAAE,YAAY,EAAE,MAAM,EAAE,aAClC,gCAAK,KAAK,EAAE,EAAE,QAAQ,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,YAAY,EAAE,KAAK,EAAE,qBAE9D,EACN,gCAAK,KAAK,EAAE;4BACV,QAAQ,EAAE,MAAM;4BAChB,OAAO,EAAE,SAAS;4BAClB,eAAe,EAAE,KAAK;4BACtB,KAAK,EAAE,OAAO;4BACd,YAAY,EAAE,KAAK;4BACnB,OAAO,EAAE,cAAc;yBACxB,YACE,IAAI,CAAC,IAAI,GACN,IACF,EAGN,iCAAK,KAAK,EAAE,EAAE,YAAY,EAAE,MAAM,EAAE,aAClC,gCAAK,KAAK,EAAE,EAAE,QAAQ,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,YAAY,EAAE,KAAK,EAAE,2BAE9D,EACN,iCAAK,KAAK,EAAE,EAAE,QAAQ,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,aAC7C,iCAAM,KAAK,EAAE,EAAE,UAAU,EAAE,WAAW,EAAE,eAAe,EAAE,SAAS,EAAE,OAAO,EAAE,SAAS,EAAE,YAAY,EAAE,KAAK,EAAE,YAC1G,YAAY,GACR,EACP,iCAAM,KAAK,EAAE,EAAE,MAAM,EAAE,OAAO,EAAE,KAAK,EAAE,MAAM,EAAE,uBAAU,EACzD,iCAAM,KAAK,EAAE,EAAE,UAAU,EAAE,WAAW,EAAE,eAAe,EAAE,SAAS,EAAE,OAAO,EAAE,SAAS,EAAE,YAAY,EAAE,KAAK,EAAE,YAC1G,YAAY,GACR,IACH,IACF,EAGL,eAAe,IAAI,CAClB,iCAAK,KAAK,EAAE,EAAE,YAAY,EAAE,MAAM,EAAE,aAClC,gCAAK,KAAK,EAAE,EAAE,QAAQ,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,YAAY,EAAE,KAAK,EAAE,UAAU,EAAE,MAAM,EAAE,2BAElF,EACL,aAAa,CAAC,GAAG,CAAC,CAAC,EAAE,KAAK,EAAE,KAAK,EAAE,KAAK,EAAE,EAAE,EAAE,CAAC,CAC9C,iCAAiB,KAAK,EAAE,EAAE,YAAY,EAAE,KAAK,EAAE,aAC7C,gCAAK,KAAK,EAAE,EAAE,QAAQ,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,YAAY,EAAE,KAAK,EAAE,YACjE,KAAK,GACF,EACN,gCAAK,KAAK,EAAE,EAAE,QAAQ,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,YAC5C,KAAK,KAAK,SAAS,IAAI,KAAK,KAAK,IAAI;oCACpC,CAAC,CAAC,OAAO,KAAK,KAAK,QAAQ;wCACzB,CAAC,CAAC,IAAI,CAAC,SAAS,CAAC,KAAK,EAAE,IAAI,EAAE,CAAC,CAAC;wCAChC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC;oCACjB,CAAC,CAAC,GAAG,GACH,KAVE,KAAK,CAWT,CACP,CAAC,IACE,CACP,EAGA,CAAC,eAAe,IAAI,eAAe,CAAC,MAAM,GAAG,CAAC,IAAI,CACjD,iCAAK,KAAK,EAAE,EAAE,YAAY,EAAE,MAAM,EAAE,aAClC,gCAAK,KAAK,EAAE,EAAE,QAAQ,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,YAAY,EAAE,KAAK,EAAE,UAAU,EAAE,MAAM,EAAE,qBAElF,EACL,eAAe,CAAC,GAAG,CAAC,CAAC,CAAC,GAAG,EAAE,KAAK,CAAC,EAAE,EAAE,CAAC,CACrC,iCAAe,KAAK,EAAE,EAAE,YAAY,EAAE,KAAK,EAAE,aAC3C,gCAAK,KAAK,EAAE,EAAE,QAAQ,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,YAAY,EAAE,KAAK,EAAE,YACjE,GAAG,GACA,EACN,gCAAK,KAAK,EAAE,EAAE,QAAQ,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,SAAS,EAAE,YAAY,EAAE,YACrE,KAAK,KAAK,SAAS,IAAI,KAAK,KAAK,IAAI;oCACpC,CAAC,CAAC,OAAO,KAAK,KAAK,QAAQ;wCACzB,CAAC,CAAC,IAAI,CAAC,SAAS,CAAC,KAAK,EAAE,IAAI,EAAE,CAAC,CAAC;wCAChC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC;oCACjB,CAAC,CAAC,GAAG,GACH,KAVE,GAAG,CAWP,CACP,CAAC,IACE,CACP,EAGD,iCAAK,KAAK,EAAE,EAAE,QAAQ,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,EAAE,UAAU,EAAE,KAAK,EAAE,SAAS,EAAE,gBAAgB,EAAE,qBAC3G,IAAI,CAAC,EAAE,IACR,EAGL,QAAQ,IAAI,CACX,mCACE,OAAO,EAAE,GAAG,EAAE;oBACZ,QAAQ,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;oBAClB,OAAO,EAAE,CAAC;gBACZ,CAAC,EACD,KAAK,EAAE;oBACL,SAAS,EAAE,MAAM;oBACjB,KAAK,EAAE,MAAM;oBACb,OAAO,EAAE,UAAU;oBACnB,eAAe,EAAE,SAAS;oBAC1B,KAAK,EAAE,OAAO;oBACd,MAAM,EAAE,MAAM;oBACd,YAAY,EAAE,KAAK;oBACnB,MAAM,EAAE,SAAS;oBACjB,QAAQ,EAAE,MAAM;oBAChB,UAAU,EAAE,MAAM;iBACnB,4BAGM,CACV,IACG,CACP,CAAC;AACJ,CAAC,CAAC;AArLW,QAAA,aAAa,iBAqLxB"}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import type { GraphEvent, Violation } from '@principal-ai/principal-view-core';
|
|
3
|
+
export interface EventLogProps {
|
|
4
|
+
/** List of events to display */
|
|
5
|
+
events: GraphEvent[];
|
|
6
|
+
/** Optional violations to highlight */
|
|
7
|
+
violations?: Violation[];
|
|
8
|
+
/** Callback when an event is clicked */
|
|
9
|
+
onEventClick?: (event: GraphEvent) => void;
|
|
10
|
+
/** Optional class name */
|
|
11
|
+
className?: string;
|
|
12
|
+
/** Optional max height */
|
|
13
|
+
maxHeight?: number | string;
|
|
14
|
+
}
|
|
15
|
+
/**
|
|
16
|
+
* Event log component for displaying graph events
|
|
17
|
+
* TODO: Implement filtering, search, and severity indicators
|
|
18
|
+
*/
|
|
19
|
+
export declare const EventLog: React.FC<EventLogProps>;
|
|
20
|
+
//# sourceMappingURL=EventLog.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"EventLog.d.ts","sourceRoot":"","sources":["../../src/components/EventLog.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,OAAO,CAAC;AAC1B,OAAO,KAAK,EAAE,UAAU,EAAE,SAAS,EAAE,MAAM,mCAAmC,CAAC;AAE/E,MAAM,WAAW,aAAa;IAC5B,gCAAgC;IAChC,MAAM,EAAE,UAAU,EAAE,CAAC;IAErB,uCAAuC;IACvC,UAAU,CAAC,EAAE,SAAS,EAAE,CAAC;IAEzB,wCAAwC;IACxC,YAAY,CAAC,EAAE,CAAC,KAAK,EAAE,UAAU,KAAK,IAAI,CAAC;IAE3C,0BAA0B;IAC1B,SAAS,CAAC,EAAE,MAAM,CAAC;IAEnB,0BAA0B;IAC1B,SAAS,CAAC,EAAE,MAAM,GAAG,MAAM,CAAC;CAC7B;AAED;;;GAGG;AACH,eAAO,MAAM,QAAQ,EAAE,KAAK,CAAC,EAAE,CAAC,aAAa,CA0B5C,CAAC"}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.EventLog = void 0;
|
|
4
|
+
const jsx_runtime_1 = require("react/jsx-runtime");
|
|
5
|
+
/**
|
|
6
|
+
* Event log component for displaying graph events
|
|
7
|
+
* TODO: Implement filtering, search, and severity indicators
|
|
8
|
+
*/
|
|
9
|
+
const EventLog = ({ events, violations = [], className, maxHeight = '400px', }) => {
|
|
10
|
+
return ((0, jsx_runtime_1.jsx)("div", { className: className, style: { maxHeight, overflowY: 'auto', border: '1px solid #ccc' }, children: (0, jsx_runtime_1.jsxs)("div", { style: { padding: '10px' }, children: [(0, jsx_runtime_1.jsx)("h3", { children: "Event Log (TODO)" }), (0, jsx_runtime_1.jsxs)("p", { children: ["Events: ", events.length] }), (0, jsx_runtime_1.jsxs)("p", { children: ["Violations: ", violations.length] }), (0, jsx_runtime_1.jsxs)("div", { children: [(0, jsx_runtime_1.jsx)("strong", { children: "TODO:" }), (0, jsx_runtime_1.jsxs)("ul", { children: [(0, jsx_runtime_1.jsx)("li", { children: "Display events in chronological order" }), (0, jsx_runtime_1.jsx)("li", { children: "Show event type, category, operation" }), (0, jsx_runtime_1.jsx)("li", { children: "Highlight violations with color coding" }), (0, jsx_runtime_1.jsx)("li", { children: "Add filtering by category, type, severity" }), (0, jsx_runtime_1.jsx)("li", { children: "Add text search" }), (0, jsx_runtime_1.jsx)("li", { children: "Add timestamp formatting" })] })] })] }) }));
|
|
11
|
+
};
|
|
12
|
+
exports.EventLog = EventLog;
|
|
13
|
+
//# sourceMappingURL=EventLog.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"EventLog.js","sourceRoot":"","sources":["../../src/components/EventLog.tsx"],"names":[],"mappings":";;;;AAoBA;;;GAGG;AACI,MAAM,QAAQ,GAA4B,CAAC,EAChD,MAAM,EACN,UAAU,GAAG,EAAE,EACf,SAAS,EACT,SAAS,GAAG,OAAO,GACpB,EAAE,EAAE;IACH,OAAO,CACL,gCAAK,SAAS,EAAE,SAAS,EAAE,KAAK,EAAE,EAAE,SAAS,EAAE,SAAS,EAAE,MAAM,EAAE,MAAM,EAAE,gBAAgB,EAAE,YAC1F,iCAAK,KAAK,EAAE,EAAE,OAAO,EAAE,MAAM,EAAE,aAC7B,8DAAyB,EACzB,sDAAY,MAAM,CAAC,MAAM,IAAK,EAC9B,0DAAgB,UAAU,CAAC,MAAM,IAAK,EACtC,4CACE,uDAAsB,EACtB,2CACE,mFAA8C,EAC9C,kFAA6C,EAC7C,oFAA+C,EAC/C,uFAAkD,EAClD,6DAAwB,EACxB,sEAAiC,IAC9B,IACD,IACF,GACF,CACP,CAAC;AACJ,CAAC,CAAC;AA1BW,QAAA,QAAQ,YA0BnB"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"EventLog.test.d.ts","sourceRoot":"","sources":["../../src/components/EventLog.test.tsx"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
const jsx_runtime_1 = require("react/jsx-runtime");
|
|
4
|
+
const react_1 = require("@testing-library/react");
|
|
5
|
+
const EventLog_1 = require("./EventLog");
|
|
6
|
+
describe('EventLog', () => {
|
|
7
|
+
const testEvents = [
|
|
8
|
+
{
|
|
9
|
+
id: 'evt-1',
|
|
10
|
+
type: 'node_created',
|
|
11
|
+
timestamp: Date.now(),
|
|
12
|
+
category: 'node',
|
|
13
|
+
operation: 'create',
|
|
14
|
+
payload: {
|
|
15
|
+
operation: 'create',
|
|
16
|
+
nodeId: 'user-1',
|
|
17
|
+
nodeType: 'user',
|
|
18
|
+
data: { userId: 'alice' },
|
|
19
|
+
},
|
|
20
|
+
expected: true,
|
|
21
|
+
},
|
|
22
|
+
{
|
|
23
|
+
id: 'evt-2',
|
|
24
|
+
type: 'edge_created',
|
|
25
|
+
timestamp: Date.now(),
|
|
26
|
+
category: 'edge',
|
|
27
|
+
operation: 'create',
|
|
28
|
+
payload: {
|
|
29
|
+
operation: 'create',
|
|
30
|
+
edgeId: 'conn-1',
|
|
31
|
+
edgeType: 'connection',
|
|
32
|
+
from: 'user-1',
|
|
33
|
+
to: 'user-2',
|
|
34
|
+
},
|
|
35
|
+
expected: true,
|
|
36
|
+
},
|
|
37
|
+
];
|
|
38
|
+
const testViolations = [
|
|
39
|
+
{
|
|
40
|
+
id: 'violation-1',
|
|
41
|
+
severity: 'error',
|
|
42
|
+
type: 'connection',
|
|
43
|
+
description: 'Invalid connection',
|
|
44
|
+
},
|
|
45
|
+
];
|
|
46
|
+
it('should render without crashing', () => {
|
|
47
|
+
(0, react_1.render)((0, jsx_runtime_1.jsx)(EventLog_1.EventLog, { events: testEvents }));
|
|
48
|
+
expect(react_1.screen.getByText(/Event Log/i)).toBeDefined();
|
|
49
|
+
});
|
|
50
|
+
it('should display event count', () => {
|
|
51
|
+
(0, react_1.render)((0, jsx_runtime_1.jsx)(EventLog_1.EventLog, { events: testEvents }));
|
|
52
|
+
expect(react_1.screen.getByText(/Events: 2/i)).toBeDefined();
|
|
53
|
+
});
|
|
54
|
+
it('should display violation count', () => {
|
|
55
|
+
(0, react_1.render)((0, jsx_runtime_1.jsx)(EventLog_1.EventLog, { events: testEvents, violations: testViolations }));
|
|
56
|
+
expect(react_1.screen.getByText(/Violations: 1/i)).toBeDefined();
|
|
57
|
+
});
|
|
58
|
+
it('should render with empty events', () => {
|
|
59
|
+
(0, react_1.render)((0, jsx_runtime_1.jsx)(EventLog_1.EventLog, { events: [] }));
|
|
60
|
+
expect(react_1.screen.getByText(/Events: 0/i)).toBeDefined();
|
|
61
|
+
});
|
|
62
|
+
it('should apply custom className', () => {
|
|
63
|
+
const { container } = (0, react_1.render)((0, jsx_runtime_1.jsx)(EventLog_1.EventLog, { events: testEvents, className: "custom-log" }));
|
|
64
|
+
const element = container.querySelector('.custom-log');
|
|
65
|
+
expect(element).toBeDefined();
|
|
66
|
+
});
|
|
67
|
+
it('should apply custom maxHeight', () => {
|
|
68
|
+
const { container } = (0, react_1.render)((0, jsx_runtime_1.jsx)(EventLog_1.EventLog, { events: testEvents, maxHeight: "300px" }));
|
|
69
|
+
const element = container.querySelector('div');
|
|
70
|
+
expect(element?.style.maxHeight).toBe('300px');
|
|
71
|
+
});
|
|
72
|
+
});
|
|
73
|
+
//# sourceMappingURL=EventLog.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"EventLog.test.js","sourceRoot":"","sources":["../../src/components/EventLog.test.tsx"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import '@xyflow/react/dist/style.css';
|
|
3
|
+
import type { Violation, GraphEvent, ExtendedCanvas, ComponentLibrary } from '@principal-ai/principal-view-core';
|
|
4
|
+
/** Position change event for tracking node movements */
|
|
5
|
+
export interface NodePositionChange {
|
|
6
|
+
nodeId: string;
|
|
7
|
+
position: {
|
|
8
|
+
x: number;
|
|
9
|
+
y: number;
|
|
10
|
+
};
|
|
11
|
+
}
|
|
12
|
+
/** All pending changes that can be saved */
|
|
13
|
+
export interface PendingChanges {
|
|
14
|
+
/** Node position changes */
|
|
15
|
+
positionChanges: NodePositionChange[];
|
|
16
|
+
/** Node updates (type, data changes) */
|
|
17
|
+
nodeUpdates: Array<{
|
|
18
|
+
nodeId: string;
|
|
19
|
+
updates: {
|
|
20
|
+
type?: string;
|
|
21
|
+
data?: Record<string, unknown>;
|
|
22
|
+
};
|
|
23
|
+
}>;
|
|
24
|
+
/** Deleted node IDs */
|
|
25
|
+
deletedNodeIds: string[];
|
|
26
|
+
/** New edges created (with optional handle info for connection points) */
|
|
27
|
+
createdEdges: Array<{
|
|
28
|
+
from: string;
|
|
29
|
+
to: string;
|
|
30
|
+
type: string;
|
|
31
|
+
sourceHandle?: string;
|
|
32
|
+
targetHandle?: string;
|
|
33
|
+
}>;
|
|
34
|
+
/** Deleted edges (with full connection info for config removal) */
|
|
35
|
+
deletedEdges: Array<{
|
|
36
|
+
from: string;
|
|
37
|
+
to: string;
|
|
38
|
+
type: string;
|
|
39
|
+
}>;
|
|
40
|
+
/** Whether there are any changes */
|
|
41
|
+
hasChanges: boolean;
|
|
42
|
+
}
|
|
43
|
+
/** Ref handle for imperative actions */
|
|
44
|
+
export interface GraphRendererHandle {
|
|
45
|
+
/** Get all pending changes */
|
|
46
|
+
getPendingChanges: () => PendingChanges;
|
|
47
|
+
/** Reset edit state to match current props */
|
|
48
|
+
resetEditState: () => void;
|
|
49
|
+
/** Check if there are unsaved changes */
|
|
50
|
+
hasUnsavedChanges: () => boolean;
|
|
51
|
+
}
|
|
52
|
+
/** Base props shared by all render modes */
|
|
53
|
+
interface GraphRendererBaseProps {
|
|
54
|
+
/** Optional violations to highlight */
|
|
55
|
+
violations?: Violation[];
|
|
56
|
+
/** Optional configuration name for identification (used with multi-config setups) */
|
|
57
|
+
configName?: string;
|
|
58
|
+
/** Optional class name */
|
|
59
|
+
className?: string;
|
|
60
|
+
/** Optional width */
|
|
61
|
+
width?: number | string;
|
|
62
|
+
/** Optional height */
|
|
63
|
+
height?: number | string;
|
|
64
|
+
/** Whether to show minimap */
|
|
65
|
+
showMinimap?: boolean;
|
|
66
|
+
/** Whether to show controls */
|
|
67
|
+
showControls?: boolean;
|
|
68
|
+
/** Whether to show background */
|
|
69
|
+
showBackground?: boolean;
|
|
70
|
+
/** Optional event stream for triggering animations */
|
|
71
|
+
events?: GraphEvent[];
|
|
72
|
+
/** Optional callback when an event is processed */
|
|
73
|
+
onEventProcessed?: (event: GraphEvent) => void;
|
|
74
|
+
/**
|
|
75
|
+
* Whether edit mode is enabled.
|
|
76
|
+
* When true, nodes can be dragged, edited, deleted, and edges can be created/deleted.
|
|
77
|
+
* All changes are tracked internally and can be retrieved via ref.getPendingChanges()
|
|
78
|
+
*/
|
|
79
|
+
editable?: boolean;
|
|
80
|
+
/**
|
|
81
|
+
* Callback when pending changes state changes.
|
|
82
|
+
* Called with true when there are unsaved changes, false when there are none.
|
|
83
|
+
* Use this to enable/disable save buttons in the parent component.
|
|
84
|
+
*/
|
|
85
|
+
onPendingChangesChange?: (hasChanges: boolean) => void;
|
|
86
|
+
}
|
|
87
|
+
/** GraphRenderer props - canvas format only */
|
|
88
|
+
export interface GraphRendererProps extends GraphRendererBaseProps {
|
|
89
|
+
/** Extended Canvas document */
|
|
90
|
+
canvas: ExtendedCanvas;
|
|
91
|
+
/**
|
|
92
|
+
* Optional component library containing reusable node and edge type definitions.
|
|
93
|
+
* Types from the library are merged with canvas-level types, with canvas types taking precedence.
|
|
94
|
+
* This allows sharing type definitions across multiple canvas files via a library.yaml file.
|
|
95
|
+
*/
|
|
96
|
+
library?: ComponentLibrary;
|
|
97
|
+
}
|
|
98
|
+
/**
|
|
99
|
+
* Core graph visualization component using xyflow.
|
|
100
|
+
*
|
|
101
|
+
* Accepts an ExtendedCanvas document for rendering.
|
|
102
|
+
*
|
|
103
|
+
* When `editable` is true, the component manages its own edit state internally.
|
|
104
|
+
* Use the ref to get pending changes when the user wants to save:
|
|
105
|
+
*
|
|
106
|
+
* ```tsx
|
|
107
|
+
* <GraphRenderer canvas={myCanvas} />
|
|
108
|
+
*
|
|
109
|
+
* // With edit mode
|
|
110
|
+
* const graphRef = useRef<GraphRendererHandle>(null);
|
|
111
|
+
* <GraphRenderer
|
|
112
|
+
* ref={graphRef}
|
|
113
|
+
* canvas={myCanvas}
|
|
114
|
+
* editable={isEditMode}
|
|
115
|
+
* onPendingChangesChange={setHasUnsavedChanges}
|
|
116
|
+
* />
|
|
117
|
+
* ```
|
|
118
|
+
*/
|
|
119
|
+
export declare const GraphRenderer: React.ForwardRefExoticComponent<GraphRendererProps & React.RefAttributes<GraphRendererHandle>>;
|
|
120
|
+
export {};
|
|
121
|
+
//# sourceMappingURL=GraphRenderer.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"GraphRenderer.d.ts","sourceRoot":"","sources":["../../src/components/GraphRenderer.tsx"],"names":[],"mappings":"AAAA,OAAO,KAA6F,MAAM,OAAO,CAAC;AAclH,OAAO,8BAA8B,CAAC;AACtC,OAAO,KAAK,EAA4C,SAAS,EAAE,UAAU,EAAE,cAAc,EAAE,gBAAgB,EAAE,MAAM,mCAAmC,CAAC;AAU3J,wDAAwD;AACxD,MAAM,WAAW,kBAAkB;IACjC,MAAM,EAAE,MAAM,CAAC;IACf,QAAQ,EAAE;QAAE,CAAC,EAAE,MAAM,CAAC;QAAC,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC;CACpC;AAED,4CAA4C;AAC5C,MAAM,WAAW,cAAc;IAC7B,4BAA4B;IAC5B,eAAe,EAAE,kBAAkB,EAAE,CAAC;IACtC,wCAAwC;IACxC,WAAW,EAAE,KAAK,CAAC;QAAE,MAAM,EAAE,MAAM,CAAC;QAAC,OAAO,EAAE;YAAE,IAAI,CAAC,EAAE,MAAM,CAAC;YAAC,IAAI,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;SAAE,CAAA;KAAE,CAAC,CAAC;IACnG,uBAAuB;IACvB,cAAc,EAAE,MAAM,EAAE,CAAC;IACzB,0EAA0E;IAC1E,YAAY,EAAE,KAAK,CAAC;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,EAAE,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAC;QAAC,YAAY,CAAC,EAAE,MAAM,CAAC;QAAC,YAAY,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;IAC9G,mEAAmE;IACnE,YAAY,EAAE,KAAK,CAAC;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,EAAE,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;IAChE,oCAAoC;IACpC,UAAU,EAAE,OAAO,CAAC;CACrB;AAED,wCAAwC;AACxC,MAAM,WAAW,mBAAmB;IAClC,8BAA8B;IAC9B,iBAAiB,EAAE,MAAM,cAAc,CAAC;IACxC,8CAA8C;IAC9C,cAAc,EAAE,MAAM,IAAI,CAAC;IAC3B,yCAAyC;IACzC,iBAAiB,EAAE,MAAM,OAAO,CAAC;CAClC;AAED,4CAA4C;AAC5C,UAAU,sBAAsB;IAC9B,uCAAuC;IACvC,UAAU,CAAC,EAAE,SAAS,EAAE,CAAC;IAEzB,qFAAqF;IACrF,UAAU,CAAC,EAAE,MAAM,CAAC;IAEpB,0BAA0B;IAC1B,SAAS,CAAC,EAAE,MAAM,CAAC;IAEnB,qBAAqB;IACrB,KAAK,CAAC,EAAE,MAAM,GAAG,MAAM,CAAC;IAExB,sBAAsB;IACtB,MAAM,CAAC,EAAE,MAAM,GAAG,MAAM,CAAC;IAEzB,8BAA8B;IAC9B,WAAW,CAAC,EAAE,OAAO,CAAC;IAEtB,+BAA+B;IAC/B,YAAY,CAAC,EAAE,OAAO,CAAC;IAEvB,iCAAiC;IACjC,cAAc,CAAC,EAAE,OAAO,CAAC;IAEzB,sDAAsD;IACtD,MAAM,CAAC,EAAE,UAAU,EAAE,CAAC;IAEtB,mDAAmD;IACnD,gBAAgB,CAAC,EAAE,CAAC,KAAK,EAAE,UAAU,KAAK,IAAI,CAAC;IAE/C;;;;OAIG;IACH,QAAQ,CAAC,EAAE,OAAO,CAAC;IAEnB;;;;OAIG;IACH,sBAAsB,CAAC,EAAE,CAAC,UAAU,EAAE,OAAO,KAAK,IAAI,CAAC;CACxD;AAED,+CAA+C;AAC/C,MAAM,WAAW,kBAAmB,SAAQ,sBAAsB;IAChE,+BAA+B;IAC/B,MAAM,EAAE,cAAc,CAAC;IAEvB;;;;OAIG;IACH,OAAO,CAAC,EAAE,gBAAgB,CAAC;CAC5B;AA29BD;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,eAAO,MAAM,aAAa,gGAgGxB,CAAC"}
|