feat(web/mazenet): interaction layer — pan, drag, resize, reparent
This commit is contained in:
@@ -1,4 +1,4 @@
|
||||
import React, { useEffect, useState } from 'react';
|
||||
import React, { useCallback, useEffect, useRef, useState } from 'react';
|
||||
import { useSearchParams } from 'react-router-dom';
|
||||
import { PanelRightOpen, PanelRightClose, RotateCcw, UploadCloud } from 'lucide-react';
|
||||
import './MazeNET.css';
|
||||
@@ -10,6 +10,7 @@ import { DEFAULT_SERVICES, DEMO_NETS, DEMO_NODES, DEMO_EDGES } from './data';
|
||||
import type { ServiceDef } from './data';
|
||||
import type { Net, MazeNode, Edge, PendingChange } from './types';
|
||||
import { useMazeApi } from './useMazeApi';
|
||||
import { useMazeInteraction } from './useMazeInteraction';
|
||||
|
||||
const MazeNET: React.FC = () => {
|
||||
const api = useMazeApi();
|
||||
@@ -19,12 +20,18 @@ const MazeNET: React.FC = () => {
|
||||
const [nets, setNets] = useState<Net[]>(DEMO_NETS);
|
||||
const [nodes, setNodes] = useState<MazeNode[]>(DEMO_NODES);
|
||||
const [edges, setEdges] = useState<Edge[]>(DEMO_EDGES);
|
||||
const [pending] = useState<PendingChange[]>([]);
|
||||
const [pending, setPending] = useState<PendingChange[]>([]);
|
||||
const [selection, setSelection] = useState<Selection>(null);
|
||||
const [inspectorOpen, setInspectorOpen] = useState(true);
|
||||
const [services, setServices] = useState<ServiceDef[]>(DEFAULT_SERVICES);
|
||||
const [loadErr, setLoadErr] = useState<string | null>(null);
|
||||
|
||||
const canvasRef = useRef<HTMLDivElement>(null);
|
||||
const applyChange = useCallback((pc: PendingChange) => {
|
||||
setPending((p) => [...p, pc]);
|
||||
}, []);
|
||||
const interaction = useMazeInteraction({ nets, nodes, setNets, setNodes, applyChange, canvasRef });
|
||||
|
||||
/* Load service catalog from API (fall back to defaults if 401/offline). */
|
||||
useEffect(() => {
|
||||
let cancelled = false;
|
||||
@@ -58,8 +65,18 @@ const MazeNET: React.FC = () => {
|
||||
setNets(DEMO_NETS); setNodes(DEMO_NODES); setEdges(DEMO_EDGES);
|
||||
}
|
||||
setSelection(null);
|
||||
setPending([]);
|
||||
interaction.resetPan();
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
const onKey = (e: KeyboardEvent) => {
|
||||
if (e.key === 'Escape') setSelection(null);
|
||||
};
|
||||
window.addEventListener('keydown', onKey);
|
||||
return () => window.removeEventListener('keydown', onKey);
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<div className="maze-page">
|
||||
<div className="maze-page-header">
|
||||
@@ -102,11 +119,19 @@ const MazeNET: React.FC = () => {
|
||||
>
|
||||
<Palette services={services} />
|
||||
<Canvas
|
||||
ref={canvasRef}
|
||||
nets={nets}
|
||||
nodes={nodes}
|
||||
edges={edges}
|
||||
selection={selection}
|
||||
setSelection={setSelection}
|
||||
pan={interaction.pan}
|
||||
dropTargetId={interaction.dropTargetId}
|
||||
dragging={interaction.dragging}
|
||||
onCanvasMouseDown={interaction.onCanvasMouseDown}
|
||||
onNodeMouseDown={interaction.onNodeMouseDown}
|
||||
onNetMouseDown={interaction.onNetMouseDown}
|
||||
onNetResizeMouseDown={interaction.onNetResizeMouseDown}
|
||||
/>
|
||||
{inspectorOpen && (
|
||||
<Inspector
|
||||
|
||||
Reference in New Issue
Block a user