feat: improve mouse events

This commit is contained in:
2026-03-11 10:27:17 +01:00
parent c330713fa7
commit deb1be0d66
3 changed files with 29 additions and 1 deletions

View File

@@ -188,6 +188,8 @@ export function CanvasPage({ projectId }: CanvasPageProps) {
const flowActionsRef = useRef<{ pasteAtViewportCenter: () => void; fitView: () => void } | null>(null) const flowActionsRef = useRef<{ pasteAtViewportCenter: () => void; fitView: () => void } | null>(null)
const [contextTarget, setContextTarget] = React.useState<null | { type: 'canvas'; clientX: number; clientY: number }>(null) const [contextTarget, setContextTarget] = React.useState<null | { type: 'canvas'; clientX: number; clientY: number }>(null)
const [lastCreatedNodeId, setLastCreatedNodeId] = React.useState<string | null>(null) const [lastCreatedNodeId, setLastCreatedNodeId] = React.useState<string | null>(null)
const [isPanning, setIsPanning] = React.useState(false)
const [isSelecting, setIsSelecting] = React.useState(false)
const [ariaAnnouncement, setAriaAnnouncement] = React.useState<string | null>(null) const [ariaAnnouncement, setAriaAnnouncement] = React.useState<string | null>(null)
const nodesRef = useRef(nodes) const nodesRef = useRef(nodes)
nodesRef.current = nodes nodesRef.current = nodes
@@ -286,6 +288,11 @@ export function CanvasPage({ projectId }: CanvasPageProps) {
const onInit = useCallback((instance: unknown) => setRfInstance(instance), []) const onInit = useCallback((instance: unknown) => setRfInstance(instance), [])
const onMoveStart = useCallback(() => setIsPanning(true), [])
const onMoveEnd = useCallback(() => setIsPanning(false), [])
const onSelectionDragStart = useCallback(() => setIsSelecting(true), [])
const onSelectionDragStop = useCallback(() => setIsSelecting(false), [])
const onNodeDragStart = useCallback(() => saveForDragEnd(), [saveForDragEnd]) const onNodeDragStart = useCallback(() => saveForDragEnd(), [saveForDragEnd])
const onNodeDragStop = useCallback(() => commitDragEnd(), [commitDragEnd]) const onNodeDragStop = useCallback(() => commitDragEnd(), [commitDragEnd])
@@ -612,6 +619,7 @@ export function CanvasPage({ projectId }: CanvasPageProps) {
<FlowFitViewOnLoad /> <FlowFitViewOnLoad />
<FlowKeyboardShortcuts /> <FlowKeyboardShortcuts />
<ReactFlow <ReactFlow
className={isPanning ? 'react-flow--panning' : isSelecting ? 'react-flow--selecting' : undefined}
nodes={nodes.map((n) => ({ nodes={nodes.map((n) => ({
...n, ...n,
className: [n.className, 'nowheel'].filter(Boolean).join(' '), className: [n.className, 'nowheel'].filter(Boolean).join(' '),
@@ -624,6 +632,10 @@ export function CanvasPage({ projectId }: CanvasPageProps) {
onConnectEnd={onConnectEnd} onConnectEnd={onConnectEnd}
onNodeDragStart={onNodeDragStart} onNodeDragStart={onNodeDragStart}
onNodeDragStop={onNodeDragStop} onNodeDragStop={onNodeDragStop}
onMoveStart={onMoveStart}
onMoveEnd={onMoveEnd}
onSelectionDragStart={onSelectionDragStart}
onSelectionDragStop={onSelectionDragStop}
isValidConnection={isValidConnection} isValidConnection={isValidConnection}
nodeTypes={nodeTypes} nodeTypes={nodeTypes}
edgeTypes={edgeTypes} edgeTypes={edgeTypes}
@@ -639,6 +651,11 @@ export function CanvasPage({ projectId }: CanvasPageProps) {
nodesDraggable nodesDraggable
nodesConnectable nodesConnectable
elementsSelectable elementsSelectable
panOnScroll
zoomOnScroll
zoomActivationKeyCode="Meta"
panOnDrag={[1]}
selectionOnDrag
> >
{/* BackgroundVariant from @xyflow/system expects enum; 'dots' is valid at runtime */} {/* BackgroundVariant from @xyflow/system expects enum; 'dots' is valid at runtime */}
<Background variant={'dots' as React.ComponentProps<typeof Background>['variant']} gap={20} /> <Background variant={'dots' as React.ComponentProps<typeof Background>['variant']} gap={20} />

View File

@@ -13,7 +13,7 @@ import { cn } from '@/lib/utils'
const MIN_ZOOM = 0.1 const MIN_ZOOM = 0.1
const MAX_ZOOM = 2 const MAX_ZOOM = 2
/** When zoom <= this value, show compact icon view (bottom ~30% of zoom range). */ /** When zoom <= this value, show compact icon view (bottom ~30% of zoom range). */
export const CONTEXTUAL_ZOOM_THRESHOLD = MIN_ZOOM + 0.3 * (MAX_ZOOM - MIN_ZOOM) export const CONTEXTUAL_ZOOM_THRESHOLD = MIN_ZOOM + 0.1 * (MAX_ZOOM - MIN_ZOOM)
/** Handle id per type for compact view (input, output). */ /** Handle id per type for compact view (input, output). */
const COMPACT_HANDLES: Record<string, { input?: string; output?: string }> = { const COMPACT_HANDLES: Record<string, { input?: string; output?: string }> = {

View File

@@ -20,6 +20,17 @@ body {
min-height: 0; min-height: 0;
} }
/* Canvas pane cursors: crosshair for selection box, grab/grabbing for middle-click pan */
.react-flow__pane {
cursor: crosshair;
}
.react-flow.react-flow--panning .react-flow__pane {
cursor: grabbing;
}
.react-flow.react-flow--selecting .react-flow__pane {
cursor: crosshair;
}
/* Node specific overrides */ /* Node specific overrides */
.react-flow__node { .react-flow__node {
display: block; display: block;