feat: improve mouse events
This commit is contained in:
@@ -188,6 +188,8 @@ export function CanvasPage({ projectId }: CanvasPageProps) {
|
||||
const flowActionsRef = useRef<{ pasteAtViewportCenter: () => void; fitView: () => void } | null>(null)
|
||||
const [contextTarget, setContextTarget] = React.useState<null | { type: 'canvas'; clientX: number; clientY: number }>(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 nodesRef = useRef(nodes)
|
||||
nodesRef.current = nodes
|
||||
@@ -286,6 +288,11 @@ export function CanvasPage({ projectId }: CanvasPageProps) {
|
||||
|
||||
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 onNodeDragStop = useCallback(() => commitDragEnd(), [commitDragEnd])
|
||||
|
||||
@@ -612,6 +619,7 @@ export function CanvasPage({ projectId }: CanvasPageProps) {
|
||||
<FlowFitViewOnLoad />
|
||||
<FlowKeyboardShortcuts />
|
||||
<ReactFlow
|
||||
className={isPanning ? 'react-flow--panning' : isSelecting ? 'react-flow--selecting' : undefined}
|
||||
nodes={nodes.map((n) => ({
|
||||
...n,
|
||||
className: [n.className, 'nowheel'].filter(Boolean).join(' '),
|
||||
@@ -624,6 +632,10 @@ export function CanvasPage({ projectId }: CanvasPageProps) {
|
||||
onConnectEnd={onConnectEnd}
|
||||
onNodeDragStart={onNodeDragStart}
|
||||
onNodeDragStop={onNodeDragStop}
|
||||
onMoveStart={onMoveStart}
|
||||
onMoveEnd={onMoveEnd}
|
||||
onSelectionDragStart={onSelectionDragStart}
|
||||
onSelectionDragStop={onSelectionDragStop}
|
||||
isValidConnection={isValidConnection}
|
||||
nodeTypes={nodeTypes}
|
||||
edgeTypes={edgeTypes}
|
||||
@@ -639,6 +651,11 @@ export function CanvasPage({ projectId }: CanvasPageProps) {
|
||||
nodesDraggable
|
||||
nodesConnectable
|
||||
elementsSelectable
|
||||
panOnScroll
|
||||
zoomOnScroll
|
||||
zoomActivationKeyCode="Meta"
|
||||
panOnDrag={[1]}
|
||||
selectionOnDrag
|
||||
>
|
||||
{/* BackgroundVariant from @xyflow/system expects enum; 'dots' is valid at runtime */}
|
||||
<Background variant={'dots' as React.ComponentProps<typeof Background>['variant']} gap={20} />
|
||||
|
||||
@@ -13,7 +13,7 @@ import { cn } from '@/lib/utils'
|
||||
const MIN_ZOOM = 0.1
|
||||
const MAX_ZOOM = 2
|
||||
/** 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). */
|
||||
const COMPACT_HANDLES: Record<string, { input?: string; output?: string }> = {
|
||||
|
||||
@@ -20,6 +20,17 @@ body {
|
||||
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 */
|
||||
.react-flow__node {
|
||||
display: block;
|
||||
|
||||
Reference in New Issue
Block a user