mirror of
https://github.com/OFFIS-ESC/constellation-analyzer
synced 2026-01-27 07:43:41 +00:00
feat: enhance relation properties panel with live updates
Improvements to the relation properties panel: 1. Show actor labels instead of IDs in connection display - Display actor icons with type-specific colors - Show actor labels and type names (e.g., "Person", "Organization") - IDs available on hover via tooltips - Layout: [icon] Label (Type) → (Type) Label [icon] 2. Make relation type changes instant - Relation type dropdown now applies changes immediately - No more 500ms delay or "Saving changes..." message - Provides instant visual feedback like directionality toggles 3. Fix connection display updates - Connection info now reads current edge data from store - Source/target actors update immediately when reversing direction - Direction arrow updates immediately when changing directionality - Panel properly reflects all edge changes in real-time Only the custom label text input retains debounced saves to avoid excessive updates while typing. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
parent
e3e5b0768b
commit
2db8b25d9e
1 changed files with 63 additions and 6 deletions
|
|
@ -12,6 +12,7 @@ import { useGraphWithHistory } from '../../hooks/useGraphWithHistory';
|
||||||
import { useDocumentHistory } from '../../hooks/useDocumentHistory';
|
import { useDocumentHistory } from '../../hooks/useDocumentHistory';
|
||||||
import { useConfirm } from '../../hooks/useConfirm';
|
import { useConfirm } from '../../hooks/useConfirm';
|
||||||
import GraphMetrics from '../Common/GraphMetrics';
|
import GraphMetrics from '../Common/GraphMetrics';
|
||||||
|
import { getIconComponent } from '../../utils/iconUtils';
|
||||||
import type { Actor, Relation, EdgeDirectionality } from '../../types';
|
import type { Actor, Relation, EdgeDirectionality } from '../../types';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -398,6 +399,9 @@ const RightPanel = ({ selectedNode, selectedEdge, onClose }: Props) => {
|
||||||
|
|
||||||
// Edge properties view
|
// Edge properties view
|
||||||
if (selectedEdge) {
|
if (selectedEdge) {
|
||||||
|
// Get the current edge data from the store (to reflect live updates)
|
||||||
|
const currentEdge = edges.find(e => e.id === selectedEdge.id) || selectedEdge;
|
||||||
|
|
||||||
const renderStylePreview = () => {
|
const renderStylePreview = () => {
|
||||||
if (!selectedEdgeTypeConfig) return null;
|
if (!selectedEdgeTypeConfig) return null;
|
||||||
|
|
||||||
|
|
@ -439,8 +443,16 @@ const RightPanel = ({ selectedNode, selectedEdge, onClose }: Props) => {
|
||||||
<select
|
<select
|
||||||
value={relationType}
|
value={relationType}
|
||||||
onChange={(e) => {
|
onChange={(e) => {
|
||||||
setRelationType(e.target.value);
|
const newType = e.target.value;
|
||||||
setHasEdgeChanges(true);
|
setRelationType(newType);
|
||||||
|
// Apply relation type change instantly (no debounce)
|
||||||
|
if (selectedEdge) {
|
||||||
|
updateEdge(selectedEdge.id, {
|
||||||
|
type: newType,
|
||||||
|
label: relationLabel.trim() || undefined,
|
||||||
|
directionality: relationDirectionality,
|
||||||
|
});
|
||||||
|
}
|
||||||
}}
|
}}
|
||||||
className="w-full px-3 py-2 text-sm border border-gray-300 rounded focus:outline-none focus:ring-2 focus:ring-blue-500"
|
className="w-full px-3 py-2 text-sm border border-gray-300 rounded focus:outline-none focus:ring-2 focus:ring-blue-500"
|
||||||
>
|
>
|
||||||
|
|
@ -537,14 +549,59 @@ const RightPanel = ({ selectedNode, selectedEdge, onClose }: Props) => {
|
||||||
</Tooltip>
|
</Tooltip>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
<div className="flex items-center justify-center text-xs text-gray-600 py-2 bg-gray-50 rounded">
|
<div className="flex items-center justify-between text-xs text-gray-600 py-2 bg-gray-50 rounded px-2 space-x-2">
|
||||||
<span className="font-medium">{selectedEdge.source}</span>
|
{/* Source Actor */}
|
||||||
<span className="mx-2">
|
<Tooltip title={`ID: ${currentEdge.source}`} placement="top">
|
||||||
|
<div className="flex items-center space-x-1 flex-1">
|
||||||
|
{(() => {
|
||||||
|
const sourceNode = nodes.find(n => n.id === currentEdge.source);
|
||||||
|
const sourceType = nodeTypes.find(nt => nt.id === sourceNode?.data?.type);
|
||||||
|
const IconComponent = sourceType ? getIconComponent(sourceType.icon) : null;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
{IconComponent && (
|
||||||
|
<div className="flex-shrink-0" style={{ color: sourceType?.color }}>
|
||||||
|
<IconComponent style={{ fontSize: '14px' }} />
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
<span className="font-medium truncate">{sourceNode?.data?.label || currentEdge.source}</span>
|
||||||
|
<span className="text-gray-400 text-[10px] flex-shrink-0">({sourceType?.label || 'Unknown'})</span>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
})()}
|
||||||
|
</div>
|
||||||
|
</Tooltip>
|
||||||
|
|
||||||
|
{/* Direction Indicator */}
|
||||||
|
<span className="flex-shrink-0 text-gray-500">
|
||||||
{relationDirectionality === 'directed' && '→'}
|
{relationDirectionality === 'directed' && '→'}
|
||||||
{relationDirectionality === 'bidirectional' && '↔'}
|
{relationDirectionality === 'bidirectional' && '↔'}
|
||||||
{relationDirectionality === 'undirected' && '—'}
|
{relationDirectionality === 'undirected' && '—'}
|
||||||
</span>
|
</span>
|
||||||
<span className="font-medium">{selectedEdge.target}</span>
|
|
||||||
|
{/* Target Actor */}
|
||||||
|
<Tooltip title={`ID: ${currentEdge.target}`} placement="top">
|
||||||
|
<div className="flex items-center space-x-1 flex-1 justify-end">
|
||||||
|
{(() => {
|
||||||
|
const targetNode = nodes.find(n => n.id === currentEdge.target);
|
||||||
|
const targetType = nodeTypes.find(nt => nt.id === targetNode?.data?.type);
|
||||||
|
const IconComponent = targetType ? getIconComponent(targetType.icon) : null;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<span className="text-gray-400 text-[10px] flex-shrink-0">({targetType?.label || 'Unknown'})</span>
|
||||||
|
<span className="font-medium truncate">{targetNode?.data?.label || currentEdge.target}</span>
|
||||||
|
{IconComponent && (
|
||||||
|
<div className="flex-shrink-0" style={{ color: targetType?.color }}>
|
||||||
|
<IconComponent style={{ fontSize: '14px' }} />
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
})()}
|
||||||
|
</div>
|
||||||
|
</Tooltip>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue