constellation-analyzer/src/types/index.ts
Jan-Henrik Bruhn b1e634d3c4 feat: add group minimize/maximize with floating edges and React Flow v12
Implements comprehensive group minimize/maximize functionality and migrates
to React Flow v12 (@xyflow/react) with improved edge routing.

## Group Minimize/Maximize Features:
- Minimized groups render as compact 220×80px solid rectangles
- Original dimensions preserved in metadata and restored on maximize
- Child actors hidden (not filtered) to prevent React Flow state issues
- Solid color backgrounds (transparency removed for minimized state)
- Internal edges filtered out when group is minimized
- Dimension sync before minimize ensures correct size on maximize

## Floating Edges:
- Dynamic edge routing for connections to/from minimized groups
- Edges connect to closest point on minimized group border
- Regular actors maintain fixed handle connections
- Smooth transitions when toggling group state

## React Flow v12 Migration:
- Updated package from 'reactflow' to '@xyflow/react'
- Changed imports to named imports (ReactFlow is now named)
- Updated CSS imports to '@xyflow/react/dist/style.css'
- Fixed NodeProps/EdgeProps to use full Node/Edge types
- Added Record<string, unknown> to data interfaces for v12 compatibility
- Replaced useStore(state => state.connectionNodeId) with useConnection()
- Updated nodeInternals to nodeLookup (renamed in v12)
- Fixed event handler types for v12 API changes

## Edge Label Improvements:
- Added explicit z-index (1000) to edge labels via EdgeLabelRenderer
- Labels now properly render above edge paths

## Type Safety & Code Quality:
- Removed all 'any' type assertions in useDocumentHistory
- Fixed missing React Hook dependencies
- Fixed unused variable warnings
- All ESLint checks passing (0 errors, 0 warnings)
- TypeScript compilation clean

## Bug Fixes:
- Group drag positions now properly persisted to store
- Minimized group styling (removed dotted border, padding)
- Node visibility using 'hidden' property instead of array filtering
- Dimension sync prevents actors from disappearing on maximize

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-10-20 11:52:44 +02:00

137 lines
4 KiB
TypeScript

import { Node, Edge } from '@xyflow/react';
// Node/Actor Types
export interface ActorData extends Record<string, unknown> {
label: string;
type: string;
description?: string;
labels?: string[]; // Array of LabelConfig IDs
citations?: string[]; // Array of bibliography reference IDs
metadata?: Record<string, unknown>;
}
export type Actor = Node<ActorData>;
// Edge/Relation Types
export type EdgeDirectionality = 'directed' | 'bidirectional' | 'undirected';
export interface RelationData extends Record<string, unknown> {
label?: string;
type: string;
directionality?: EdgeDirectionality;
strength?: number;
labels?: string[]; // Array of LabelConfig IDs
citations?: string[]; // Array of bibliography reference IDs
metadata?: Record<string, unknown>;
}
export type Relation = Edge<RelationData>;
// Node Shape Types
export type NodeShape =
| 'rectangle'
| 'circle'
| 'roundedRectangle'
| 'ellipse'
| 'pill';
// Node Type Configuration
export interface NodeTypeConfig {
id: string;
label: string;
color: string;
shape: NodeShape;
icon?: string;
description?: string;
}
// Edge Type Configuration
export interface EdgeTypeConfig {
id: string;
label: string;
color: string;
style?: 'solid' | 'dashed' | 'dotted';
description?: string;
defaultDirectionality?: EdgeDirectionality;
}
// Label Configuration
export type LabelScope = 'actors' | 'relations' | 'both';
export interface LabelConfig {
id: string;
name: string;
color: string;
appliesTo: LabelScope;
description?: string;
}
// Group Types
export interface GroupData extends Record<string, unknown> {
label: string;
description?: string;
color: string;
actorIds: string[];
minimized?: boolean;
metadata?: Record<string, unknown>;
}
export type Group = Node<GroupData>;
// Graph State
export interface GraphState {
nodes: Actor[];
edges: Relation[];
groups: Group[];
nodeTypes: NodeTypeConfig[];
edgeTypes: EdgeTypeConfig[];
labels: LabelConfig[];
}
// Editor Settings
export interface EditorSettings {
snapToGrid: boolean;
showGrid: boolean;
gridSize: number;
panOnDrag: boolean;
zoomOnScroll: boolean;
}
// Store Actions
export interface GraphActions {
addNode: (node: Actor) => void;
updateNode: (id: string, updates: Partial<Actor>) => void;
deleteNode: (id: string) => void;
addEdge: (edge: Relation) => void;
updateEdge: (id: string, data: Partial<RelationData>) => void;
deleteEdge: (id: string) => void;
addNodeType: (nodeType: NodeTypeConfig) => void;
updateNodeType: (id: string, updates: Partial<Omit<NodeTypeConfig, 'id'>>) => void;
deleteNodeType: (id: string) => void;
addEdgeType: (edgeType: EdgeTypeConfig) => void;
updateEdgeType: (id: string, updates: Partial<Omit<EdgeTypeConfig, 'id'>>) => void;
deleteEdgeType: (id: string) => void;
addLabel: (label: LabelConfig) => void;
updateLabel: (id: string, updates: Partial<Omit<LabelConfig, 'id'>>) => void;
deleteLabel: (id: string) => void;
addGroup: (group: Group) => void;
updateGroup: (id: string, updates: Partial<GroupData>) => void;
deleteGroup: (id: string, ungroupActors?: boolean) => void;
addActorToGroup: (actorId: string, groupId: string) => void;
removeActorFromGroup: (actorId: string, groupId: string) => void;
toggleGroupMinimized: (groupId: string) => void;
clearGraph: () => void;
setNodes: (nodes: Actor[]) => void;
setEdges: (edges: Relation[]) => void;
setGroups: (groups: Group[]) => void;
setNodeTypes: (nodeTypes: NodeTypeConfig[]) => void;
setEdgeTypes: (edgeTypes: EdgeTypeConfig[]) => void;
setLabels: (labels: LabelConfig[]) => void;
// NOTE: exportToFile and importFromFile have been removed
// Import/export is now handled by the workspace-level system (workspaceStore)
loadGraphState: (data: { nodes: Actor[]; edges: Relation[]; groups?: Group[]; nodeTypes: NodeTypeConfig[]; edgeTypes: EdgeTypeConfig[]; labels?: LabelConfig[] }) => void;
}
export interface EditorActions {
updateSettings: (settings: Partial<EditorSettings>) => void;
}