fix: remove unused variables

This commit is contained in:
Jan-Henrik Bruhn 2025-10-10 18:14:05 +02:00
parent e7ff53dcd7
commit 79edc902c5

View file

@ -1,4 +1,4 @@
import { useCallback, useMemo, useEffect, useState, useRef } from 'react'; import { useCallback, useMemo, useEffect, useState, useRef } from "react";
import ReactFlow, { import ReactFlow, {
Background, Background,
Controls, Controls,
@ -18,24 +18,24 @@ import ReactFlow, {
useReactFlow, useReactFlow,
Viewport, Viewport,
useOnSelectionChange, useOnSelectionChange,
} from 'reactflow'; } from "reactflow";
import 'reactflow/dist/style.css'; import "reactflow/dist/style.css";
import { useGraphWithHistory } from '../../hooks/useGraphWithHistory'; import { useGraphWithHistory } from "../../hooks/useGraphWithHistory";
import { useDocumentHistory } from '../../hooks/useDocumentHistory'; import { useDocumentHistory } from "../../hooks/useDocumentHistory";
import { useEditorStore } from '../../stores/editorStore'; import { useEditorStore } from "../../stores/editorStore";
import { useActiveDocument } from '../../stores/workspace/useActiveDocument'; import { useActiveDocument } from "../../stores/workspace/useActiveDocument";
import { useWorkspaceStore } from '../../stores/workspaceStore'; import { useWorkspaceStore } from "../../stores/workspaceStore";
import CustomNode from '../Nodes/CustomNode'; import CustomNode from "../Nodes/CustomNode";
import CustomEdge from '../Edges/CustomEdge'; import CustomEdge from "../Edges/CustomEdge";
import ContextMenu from './ContextMenu'; import ContextMenu from "./ContextMenu";
import EmptyState from '../Common/EmptyState'; import EmptyState from "../Common/EmptyState";
import { createNode } from '../../utils/nodeUtils'; import { createNode } from "../../utils/nodeUtils";
import EditIcon from '@mui/icons-material/Edit'; import EditIcon from "@mui/icons-material/Edit";
import DeleteIcon from '@mui/icons-material/Delete'; import DeleteIcon from "@mui/icons-material/Delete";
import { useConfirm } from '../../hooks/useConfirm'; import { useConfirm } from "../../hooks/useConfirm";
import type { Actor, Relation } from '../../types'; import type { Actor, Relation } from "../../types";
interface GraphEditorProps { interface GraphEditorProps {
selectedNode: Actor | null; selectedNode: Actor | null;
@ -77,11 +77,21 @@ const GraphEditor = ({ onNodeSelect, onEdgeSelect }: GraphEditorProps) => {
const { pushToHistory } = useDocumentHistory(); const { pushToHistory } = useDocumentHistory();
const { showGrid, snapToGrid, gridSize, panOnDrag, zoomOnScroll, selectedRelationType } = const {
useEditorStore(); showGrid,
snapToGrid,
gridSize,
panOnDrag,
zoomOnScroll,
selectedRelationType,
} = useEditorStore();
// React Flow instance for screen-to-flow coordinates and viewport control // React Flow instance for screen-to-flow coordinates and viewport control
const { screenToFlowPosition, setViewport, getViewport: getCurrentViewport } = useReactFlow(); const {
screenToFlowPosition,
setViewport,
getViewport: getCurrentViewport,
} = useReactFlow();
// Track previous document ID to save viewport before switching // Track previous document ID to save viewport before switching
const prevDocumentIdRef = useRef<string | null>(null); const prevDocumentIdRef = useRef<string | null>(null);
@ -90,8 +100,12 @@ const GraphEditor = ({ onNodeSelect, onEdgeSelect }: GraphEditorProps) => {
const { confirm, ConfirmDialogComponent } = useConfirm(); const { confirm, ConfirmDialogComponent } = useConfirm();
// React Flow state (synchronized with store) // React Flow state (synchronized with store)
const [nodes, setNodesState, onNodesChange] = useNodesState(storeNodes as Node[]); const [nodes, setNodesState, onNodesChange] = useNodesState(
const [edges, setEdgesState, onEdgesChange] = useEdgesState(storeEdges as Edge[]); storeNodes as Node[],
);
const [edges, setEdgesState, onEdgesChange] = useEdgesState(
storeEdges as Edge[],
);
// Track if a drag is in progress to capture state before drag // Track if a drag is in progress to capture state before drag
const dragInProgressRef = useRef(false); const dragInProgressRef = useRef(false);
@ -100,7 +114,7 @@ const GraphEditor = ({ onNodeSelect, onEdgeSelect }: GraphEditorProps) => {
const [contextMenu, setContextMenu] = useState<{ const [contextMenu, setContextMenu] = useState<{
x: number; x: number;
y: number; y: number;
type: 'pane' | 'node' | 'edge'; type: "pane" | "node" | "edge";
target?: Node | Edge; target?: Node | Edge;
} | null>(null); } | null>(null);
@ -118,22 +132,37 @@ const GraphEditor = ({ onNodeSelect, onEdgeSelect }: GraphEditorProps) => {
if (!activeDocumentId) return; if (!activeDocumentId) return;
// Save viewport for the previous document // Save viewport for the previous document
if (prevDocumentIdRef.current && prevDocumentIdRef.current !== activeDocumentId) { if (
prevDocumentIdRef.current &&
prevDocumentIdRef.current !== activeDocumentId
) {
const currentViewport = getCurrentViewport(); const currentViewport = getCurrentViewport();
saveViewport(prevDocumentIdRef.current, currentViewport); saveViewport(prevDocumentIdRef.current, currentViewport);
console.log(`Saved viewport for document: ${prevDocumentIdRef.current}`, currentViewport); console.log(
`Saved viewport for document: ${prevDocumentIdRef.current}`,
currentViewport,
);
} }
// Restore viewport for the new document // Restore viewport for the new document
const savedViewport = getViewport(activeDocumentId); const savedViewport = getViewport(activeDocumentId);
if (savedViewport) { if (savedViewport) {
console.log(`Restoring viewport for document: ${activeDocumentId}`, savedViewport); console.log(
`Restoring viewport for document: ${activeDocumentId}`,
savedViewport,
);
setViewport(savedViewport, { duration: 0 }); setViewport(savedViewport, { duration: 0 });
} }
// Update the ref to current document // Update the ref to current document
prevDocumentIdRef.current = activeDocumentId; prevDocumentIdRef.current = activeDocumentId;
}, [activeDocumentId, saveViewport, getViewport, setViewport, getCurrentViewport]); }, [
activeDocumentId,
saveViewport,
getViewport,
setViewport,
getCurrentViewport,
]);
// Save viewport periodically (debounced) // Save viewport periodically (debounced)
const handleViewportChange = useCallback( const handleViewportChange = useCallback(
@ -147,12 +176,18 @@ const GraphEditor = ({ onNodeSelect, onEdgeSelect }: GraphEditorProps) => {
return () => clearTimeout(timeoutId); return () => clearTimeout(timeoutId);
}, },
[activeDocumentId, saveViewport] [activeDocumentId, saveViewport],
); );
// Handle selection changes using ReactFlow's dedicated hook // Handle selection changes using ReactFlow's dedicated hook
const handleSelectionChange = useCallback( const handleSelectionChange = useCallback(
({ nodes: selectedNodes, edges: selectedEdges }: { nodes: Node[]; edges: Edge[] }) => { ({
nodes: selectedNodes,
edges: selectedEdges,
}: {
nodes: Node[];
edges: Edge[];
}) => {
// If a node is selected, notify parent // If a node is selected, notify parent
if (selectedNodes.length > 0) { if (selectedNodes.length > 0) {
const selectedNode = selectedNodes[0] as Actor; const selectedNode = selectedNodes[0] as Actor;
@ -171,7 +206,7 @@ const GraphEditor = ({ onNodeSelect, onEdgeSelect }: GraphEditorProps) => {
onEdgeSelect(null); onEdgeSelect(null);
} }
}, },
[onNodeSelect, onEdgeSelect] [onNodeSelect, onEdgeSelect],
); );
// Register the selection change handler with ReactFlow // Register the selection change handler with ReactFlow
@ -186,9 +221,9 @@ const GraphEditor = ({ onNodeSelect, onEdgeSelect }: GraphEditorProps) => {
// Check if a drag operation just started (dragging: true) // Check if a drag operation just started (dragging: true)
const dragStartChanges = changes.filter( const dragStartChanges = changes.filter(
(change) => (change) =>
change.type === 'position' && change.type === "position" &&
'dragging' in change && "dragging" in change &&
change.dragging === true change.dragging === true,
); );
// Capture state BEFORE the drag operation begins (for undo/redo) // Capture state BEFORE the drag operation begins (for undo/redo)
@ -196,7 +231,7 @@ const GraphEditor = ({ onNodeSelect, onEdgeSelect }: GraphEditorProps) => {
if (dragStartChanges.length > 0 && !dragInProgressRef.current) { if (dragStartChanges.length > 0 && !dragInProgressRef.current) {
dragInProgressRef.current = true; dragInProgressRef.current = true;
// Capture the state before any changes are applied // Capture the state before any changes are applied
pushToHistory('Move Actor'); pushToHistory("Move Actor");
} }
// Apply the changes // Apply the changes
@ -205,9 +240,9 @@ const GraphEditor = ({ onNodeSelect, onEdgeSelect }: GraphEditorProps) => {
// Check if any drag operation just completed (dragging: false) // Check if any drag operation just completed (dragging: false)
const dragEndChanges = changes.filter( const dragEndChanges = changes.filter(
(change) => (change) =>
change.type === 'position' && change.type === "position" &&
'dragging' in change && "dragging" in change &&
change.dragging === false change.dragging === false,
); );
// If a drag just ended, sync to store // If a drag just ended, sync to store
@ -224,7 +259,10 @@ const GraphEditor = ({ onNodeSelect, onEdgeSelect }: GraphEditorProps) => {
} else { } else {
// For non-drag changes (dimension, etc), just sync to store // For non-drag changes (dimension, etc), just sync to store
const hasNonSelectionChanges = changes.some( const hasNonSelectionChanges = changes.some(
(change) => change.type !== 'select' && change.type !== 'remove' && change.type !== 'position' (change) =>
change.type !== "select" &&
change.type !== "remove" &&
change.type !== "position",
); );
if (hasNonSelectionChanges) { if (hasNonSelectionChanges) {
setTimeout(() => { setTimeout(() => {
@ -236,7 +274,7 @@ const GraphEditor = ({ onNodeSelect, onEdgeSelect }: GraphEditorProps) => {
} }
} }
}, },
[onNodesChange, setNodesState, setNodes, pushToHistory] [onNodesChange, setNodesState, setNodes, pushToHistory],
); );
const handleEdgesChange = useCallback( const handleEdgesChange = useCallback(
@ -245,7 +283,7 @@ const GraphEditor = ({ onNodeSelect, onEdgeSelect }: GraphEditorProps) => {
// Only sync to store for non-selection changes // Only sync to store for non-selection changes
const hasNonSelectionChanges = changes.some( const hasNonSelectionChanges = changes.some(
(change) => change.type !== 'select' && change.type !== 'remove' (change) => change.type !== "select" && change.type !== "remove",
); );
if (hasNonSelectionChanges) { if (hasNonSelectionChanges) {
// Debounce store updates to avoid loops // Debounce store updates to avoid loops
@ -257,7 +295,7 @@ const GraphEditor = ({ onNodeSelect, onEdgeSelect }: GraphEditorProps) => {
}, 0); }, 0);
} }
}, },
[onEdgesChange, setEdgesState, setEdges] [onEdgesChange, setEdgesState, setEdges],
); );
// Handle new edge connections // Handle new edge connections
@ -266,12 +304,13 @@ const GraphEditor = ({ onNodeSelect, onEdgeSelect }: GraphEditorProps) => {
if (!connection.source || !connection.target) return; if (!connection.source || !connection.target) return;
// Use selected relation type or fall back to first available // Use selected relation type or fall back to first available
const edgeType = selectedRelationType || edgeTypeConfigs[0]?.id || 'default'; const edgeType =
selectedRelationType || edgeTypeConfigs[0]?.id || "default";
// Create edge with custom data (no label - will use type default) // Create edge with custom data (no label - will use type default)
const edgeWithData = { const edgeWithData = {
...connection, ...connection,
type: 'custom', type: "custom",
data: { data: {
type: edgeType, type: edgeType,
// Don't set label - will use type's label as default // Don't set label - will use type's label as default
@ -292,7 +331,14 @@ const GraphEditor = ({ onNodeSelect, onEdgeSelect }: GraphEditorProps) => {
// Use the history-tracked addEdge function // Use the history-tracked addEdge function
addEdgeWithHistory(newEdge); addEdgeWithHistory(newEdge);
}, },
[storeEdges, edgeTypeConfigs, addEdgeWithHistory, selectedRelationType, nodes, setNodesState] [
storeEdges,
edgeTypeConfigs,
addEdgeWithHistory,
selectedRelationType,
nodes,
setNodesState,
],
); );
// Handle node deletion // Handle node deletion
@ -302,7 +348,7 @@ const GraphEditor = ({ onNodeSelect, onEdgeSelect }: GraphEditorProps) => {
deleteNode(node.id); deleteNode(node.id);
}); });
}, },
[deleteNode] [deleteNode],
); );
// Handle edge deletion // Handle edge deletion
@ -312,7 +358,7 @@ const GraphEditor = ({ onNodeSelect, onEdgeSelect }: GraphEditorProps) => {
deleteEdge(edge.id); deleteEdge(edge.id);
}); });
}, },
[deleteEdge] [deleteEdge],
); );
// Register custom node types // Register custom node types
@ -320,7 +366,7 @@ const GraphEditor = ({ onNodeSelect, onEdgeSelect }: GraphEditorProps) => {
() => ({ () => ({
custom: CustomNode, custom: CustomNode,
}), }),
[] [],
); );
// Register custom edge types // Register custom edge types
@ -328,37 +374,28 @@ const GraphEditor = ({ onNodeSelect, onEdgeSelect }: GraphEditorProps) => {
() => ({ () => ({
custom: CustomEdge, custom: CustomEdge,
}), }),
[] [],
); );
// Handle node click - ReactFlow handles selection automatically // Handle node click - ReactFlow handles selection automatically
const handleNodeClick = useCallback( const handleNodeClick = useCallback(() => {
(_event: React.MouseEvent, _node: Node) => { setContextMenu(null); // Close context menu if open
setContextMenu(null); // Close context menu if open }, []);
},
[]
);
// Handle edge click - ReactFlow handles selection automatically // Handle edge click - ReactFlow handles selection automatically
const handleEdgeClick = useCallback( const handleEdgeClick = useCallback(() => {
(_event: React.MouseEvent, _edge: Edge) => { setContextMenu(null); // Close context menu if open
setContextMenu(null); // Close context menu if open }, []);
},
[]
);
// Handle right-click on pane (empty space) // Handle right-click on pane (empty space)
const handlePaneContextMenu = useCallback( const handlePaneContextMenu = useCallback((event: React.MouseEvent) => {
(event: React.MouseEvent) => { event.preventDefault();
event.preventDefault(); setContextMenu({
setContextMenu({ x: event.clientX,
x: event.clientX, y: event.clientY,
y: event.clientY, type: "pane",
type: 'pane', });
}); }, []);
},
[]
);
// Handle right-click on node // Handle right-click on node
const handleNodeContextMenu = useCallback( const handleNodeContextMenu = useCallback(
@ -367,11 +404,11 @@ const GraphEditor = ({ onNodeSelect, onEdgeSelect }: GraphEditorProps) => {
setContextMenu({ setContextMenu({
x: event.clientX, x: event.clientX,
y: event.clientY, y: event.clientY,
type: 'node', type: "node",
target: node, target: node,
}); });
}, },
[] [],
); );
// Handle right-click on edge // Handle right-click on edge
@ -381,11 +418,11 @@ const GraphEditor = ({ onNodeSelect, onEdgeSelect }: GraphEditorProps) => {
setContextMenu({ setContextMenu({
x: event.clientX, x: event.clientX,
y: event.clientY, y: event.clientY,
type: 'edge', type: "edge",
target: edge, target: edge,
}); });
}, },
[] [],
); );
// Handle left-click on pane to close context menu // Handle left-click on pane to close context menu
@ -419,7 +456,16 @@ const GraphEditor = ({ onNodeSelect, onEdgeSelect }: GraphEditorProps) => {
addNodeWithHistory(newNode); addNodeWithHistory(newNode);
setContextMenu(null); setContextMenu(null);
}, },
[contextMenu, screenToFlowPosition, nodeTypeConfigs, addNodeWithHistory, nodes, edges, setNodesState, setEdgesState] [
contextMenu,
screenToFlowPosition,
nodeTypeConfigs,
addNodeWithHistory,
nodes,
edges,
setNodesState,
setEdgesState,
],
); );
// Show empty state when no document is active // Show empty state when no document is active
@ -430,7 +476,7 @@ const GraphEditor = ({ onNodeSelect, onEdgeSelect }: GraphEditorProps) => {
onOpenDocumentManager={() => { onOpenDocumentManager={() => {
// This will be handled by the parent component // This will be handled by the parent component
// We'll trigger it via a custom event // We'll trigger it via a custom event
window.dispatchEvent(new CustomEvent('openDocumentManager')); window.dispatchEvent(new CustomEvent("openDocumentManager"));
}} }}
/> />
); );
@ -483,9 +529,9 @@ const GraphEditor = ({ onNodeSelect, onEdgeSelect }: GraphEditorProps) => {
nodeColor={(node) => { nodeColor={(node) => {
const actor = node as Actor; const actor = node as Actor;
const nodeType = nodeTypeConfigs.find( const nodeType = nodeTypeConfigs.find(
(nt) => nt.id === actor.data?.type (nt) => nt.id === actor.data?.type,
); );
return nodeType?.color || '#6b7280'; return nodeType?.color || "#6b7280";
}} }}
pannable pannable
zoomable zoomable
@ -493,13 +539,13 @@ const GraphEditor = ({ onNodeSelect, onEdgeSelect }: GraphEditorProps) => {
</ReactFlow> </ReactFlow>
{/* Context Menu - Pane */} {/* Context Menu - Pane */}
{contextMenu && contextMenu.type === 'pane' && ( {contextMenu && contextMenu.type === "pane" && (
<ContextMenu <ContextMenu
x={contextMenu.x} x={contextMenu.x}
y={contextMenu.y} y={contextMenu.y}
sections={[ sections={[
{ {
title: 'Add Actor', title: "Add Actor",
actions: nodeTypeConfigs.map((nodeType) => ({ actions: nodeTypeConfigs.map((nodeType) => ({
label: nodeType.label, label: nodeType.label,
color: nodeType.color, color: nodeType.color,
@ -512,7 +558,7 @@ const GraphEditor = ({ onNodeSelect, onEdgeSelect }: GraphEditorProps) => {
)} )}
{/* Context Menu - Node */} {/* Context Menu - Node */}
{contextMenu && contextMenu.type === 'node' && contextMenu.target && ( {contextMenu && contextMenu.type === "node" && contextMenu.target && (
<ContextMenu <ContextMenu
x={contextMenu.x} x={contextMenu.x}
y={contextMenu.y} y={contextMenu.y}
@ -520,7 +566,7 @@ const GraphEditor = ({ onNodeSelect, onEdgeSelect }: GraphEditorProps) => {
{ {
actions: [ actions: [
{ {
label: 'Edit Properties', label: "Edit Properties",
icon: <EditIcon fontSize="small" />, icon: <EditIcon fontSize="small" />,
onClick: () => { onClick: () => {
// Select the node in ReactFlow (which will trigger the right panel) // Select the node in ReactFlow (which will trigger the right panel)
@ -529,21 +575,25 @@ const GraphEditor = ({ onNodeSelect, onEdgeSelect }: GraphEditorProps) => {
...n, ...n,
selected: n.id === nodeId, selected: n.id === nodeId,
})); }));
const updatedEdges = edges.map((e) => ({ ...e, selected: false })); const updatedEdges = edges.map((e) => ({
...e,
selected: false,
}));
setNodesState(updatedNodes as Node[]); setNodesState(updatedNodes as Node[]);
setEdgesState(updatedEdges as Edge[]); setEdgesState(updatedEdges as Edge[]);
setContextMenu(null); setContextMenu(null);
}, },
}, },
{ {
label: 'Delete', label: "Delete",
icon: <DeleteIcon fontSize="small" />, icon: <DeleteIcon fontSize="small" />,
onClick: async () => { onClick: async () => {
const confirmed = await confirm({ const confirmed = await confirm({
title: 'Delete Actor', title: "Delete Actor",
message: 'Are you sure you want to delete this actor? All connected relations will also be deleted.', message:
confirmLabel: 'Delete', "Are you sure you want to delete this actor? All connected relations will also be deleted.",
severity: 'danger', confirmLabel: "Delete",
severity: "danger",
}); });
if (confirmed) { if (confirmed) {
deleteNode(contextMenu.target!.id); deleteNode(contextMenu.target!.id);
@ -559,7 +609,7 @@ const GraphEditor = ({ onNodeSelect, onEdgeSelect }: GraphEditorProps) => {
)} )}
{/* Context Menu - Edge */} {/* Context Menu - Edge */}
{contextMenu && contextMenu.type === 'edge' && contextMenu.target && ( {contextMenu && contextMenu.type === "edge" && contextMenu.target && (
<ContextMenu <ContextMenu
x={contextMenu.x} x={contextMenu.x}
y={contextMenu.y} y={contextMenu.y}
@ -567,7 +617,7 @@ const GraphEditor = ({ onNodeSelect, onEdgeSelect }: GraphEditorProps) => {
{ {
actions: [ actions: [
{ {
label: 'Edit Properties', label: "Edit Properties",
icon: <EditIcon fontSize="small" />, icon: <EditIcon fontSize="small" />,
onClick: () => { onClick: () => {
// Select the edge in ReactFlow (which will trigger the right panel) // Select the edge in ReactFlow (which will trigger the right panel)
@ -576,21 +626,24 @@ const GraphEditor = ({ onNodeSelect, onEdgeSelect }: GraphEditorProps) => {
...e, ...e,
selected: e.id === edgeId, selected: e.id === edgeId,
})); }));
const updatedNodes = nodes.map((n) => ({ ...n, selected: false })); const updatedNodes = nodes.map((n) => ({
...n,
selected: false,
}));
setEdgesState(updatedEdges as Edge[]); setEdgesState(updatedEdges as Edge[]);
setNodesState(updatedNodes as Node[]); setNodesState(updatedNodes as Node[]);
setContextMenu(null); setContextMenu(null);
}, },
}, },
{ {
label: 'Delete', label: "Delete",
icon: <DeleteIcon fontSize="small" />, icon: <DeleteIcon fontSize="small" />,
onClick: async () => { onClick: async () => {
const confirmed = await confirm({ const confirmed = await confirm({
title: 'Delete Relation', title: "Delete Relation",
message: 'Are you sure you want to delete this relation?', message: "Are you sure you want to delete this relation?",
confirmLabel: 'Delete', confirmLabel: "Delete",
severity: 'danger', severity: "danger",
}); });
if (confirmed) { if (confirmed) {
deleteEdge(contextMenu.target!.id); deleteEdge(contextMenu.target!.id);