feat(network): add connect tool and middle-pan
This commit is contained in:
@@ -1,9 +1,9 @@
|
||||
import { useState, useCallback, useRef, useEffect } from 'react'
|
||||
import { useNavigate } from 'react-router-dom'
|
||||
import { ChevronLeft, Save, Download, FileJson, FileCode, Image, FileText, Undo2, Redo2, MousePointer2, Hand, Share2, Upload } from 'lucide-react'
|
||||
import { ChevronLeft, Save, Download, FileJson, FileCode, Image, FileText, Undo2, Redo2, MousePointer2, Hand, Share2, Upload, Cable } from 'lucide-react'
|
||||
import { cn } from '@/lib/utils'
|
||||
|
||||
export type InteractionMode = 'select' | 'pan'
|
||||
export type InteractionMode = 'select' | 'pan' | 'connect'
|
||||
|
||||
interface DiagramHeaderProps {
|
||||
name: string
|
||||
@@ -156,6 +156,18 @@ export function DiagramHeader({
|
||||
>
|
||||
<Hand size={15} />
|
||||
</button>
|
||||
<button
|
||||
onClick={() => onModeChange('connect')}
|
||||
title="Connect (C)"
|
||||
className={cn(
|
||||
'p-1.5 transition-colors',
|
||||
interactionMode === 'connect'
|
||||
? 'bg-elevated text-primary'
|
||||
: 'text-muted-foreground hover:text-primary hover:bg-elevated/50',
|
||||
)}
|
||||
>
|
||||
<Cable size={15} />
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div className="mx-2 h-5 w-px bg-border-default" />
|
||||
|
||||
@@ -99,17 +99,22 @@ export function NetworkCanvas({
|
||||
edgeTypes={edgeTypes}
|
||||
defaultEdgeOptions={{ type: 'connection' }}
|
||||
edgesReconnectable
|
||||
connectOnClick={interactionMode === 'connect'}
|
||||
reconnectRadius={20}
|
||||
connectionRadius={24}
|
||||
deleteKeyCode={['Backspace', 'Delete']}
|
||||
multiSelectionKeyCode="Shift"
|
||||
panOnDrag={interactionMode === 'pan'}
|
||||
panOnDrag={interactionMode === 'pan' ? [0, 1] : [1]}
|
||||
selectionOnDrag={interactionMode === 'select'}
|
||||
panActivationKeyCode="Space"
|
||||
snapToGrid={true}
|
||||
snapGrid={[20, 20]}
|
||||
fitView
|
||||
className={interactionMode === 'pan' ? 'bg-page cursor-grab active:cursor-grabbing' : 'bg-page'}
|
||||
className={cn(
|
||||
'bg-page',
|
||||
interactionMode === 'pan' && 'cursor-grab active:cursor-grabbing',
|
||||
interactionMode === 'connect' && 'rf-connect-mode cursor-crosshair',
|
||||
)}
|
||||
>
|
||||
<Background variant={BackgroundVariant.Dots} color="var(--color-border-default)" gap={20} size={1} />
|
||||
<Controls className="!border-default !bg-card [&>button]:!border-default [&>button]:!bg-card [&>button]:!fill-text-primary" />
|
||||
|
||||
@@ -47,7 +47,7 @@ export function useCanvasShortcuts({
|
||||
onUndo: () => void
|
||||
onRedo: () => void
|
||||
onNudge: (dx: number, dy: number) => void
|
||||
onSetMode: (mode: 'select' | 'pan') => void
|
||||
onSetMode: (mode: 'select' | 'pan' | 'connect') => void
|
||||
}) {
|
||||
const { getNodes, fitView, screenToFlowPosition, setNodes: rfSetNodes } = useReactFlow()
|
||||
const clipboardRef = useRef<ClipboardData | null>(null)
|
||||
@@ -244,7 +244,7 @@ export function useCanvasShortcuts({
|
||||
return
|
||||
}
|
||||
|
||||
// Mode shortcuts: V = select, H = pan
|
||||
// Mode shortcuts: V = select, H = pan, C = connect
|
||||
if (!ctrl && e.key === 'v') {
|
||||
onSetMode('select')
|
||||
return
|
||||
@@ -253,6 +253,10 @@ export function useCanvasShortcuts({
|
||||
onSetMode('pan')
|
||||
return
|
||||
}
|
||||
if (!ctrl && e.key === 'c') {
|
||||
onSetMode('connect')
|
||||
return
|
||||
}
|
||||
|
||||
if (ctrl && e.key === 'c') {
|
||||
e.preventDefault()
|
||||
|
||||
@@ -11,6 +11,7 @@ export function BaseHandle({ className, children, ...props }: ComponentProps<typ
|
||||
className={cn(
|
||||
'h-[14px] w-[14px] rounded-full border border-default bg-elevated transition-opacity',
|
||||
'opacity-0 group-hover:opacity-100 group-focus-within:opacity-100',
|
||||
'[.rf-connect-mode_&]:opacity-100',
|
||||
className,
|
||||
)}
|
||||
>
|
||||
|
||||
@@ -842,6 +842,11 @@ function DiagramEditorInner() {
|
||||
onPaneClick={closeContextMenu}
|
||||
interactionMode={interactionMode}
|
||||
/>
|
||||
{interactionMode === 'connect' && (
|
||||
<div className="pointer-events-none absolute left-1/2 top-4 z-10 -translate-x-1/2 rounded-full border border-accent/30 bg-card/95 px-3 py-1.5 text-[11px] text-muted-foreground">
|
||||
Connect mode: drag between device handles. Middle-click and drag to pan.
|
||||
</div>
|
||||
)}
|
||||
{nodes.length === 0 && !loading && (
|
||||
<CanvasEmptyPrompt onGenerate={handleAIGenerate} />
|
||||
)}
|
||||
|
||||
Reference in New Issue
Block a user