From 51835e9aa6569898d3177992af872e8695d374be Mon Sep 17 00:00:00 2001 From: Emir Karabeg Date: Tue, 21 Jan 2025 20:52:10 -0800 Subject: [PATCH] Set up basic drag connection block into sub block. For now only finished short-input. --- app/w/[id]/workflow.tsx | 18 +++++--- .../connection-blocks/connection-blocks.tsx | 14 +++++- .../sub-block/components/short-input.tsx | 43 ++++++++++++++++--- .../components/sub-block/sub-block.tsx | 4 +- .../workflow-block/workflow-block.tsx | 11 ++++- 5 files changed, 72 insertions(+), 18 deletions(-) diff --git a/app/w/[id]/workflow.tsx b/app/w/[id]/workflow.tsx index 121ba28520..114b0ceb23 100644 --- a/app/w/[id]/workflow.tsx +++ b/app/w/[id]/workflow.tsx @@ -178,28 +178,32 @@ function WorkflowCanvas() { event.preventDefault() try { + const data = JSON.parse(event.dataTransfer.getData('application/json')) + + // Early return if this is a connection block drag + if (data.type === 'connectionBlock') { + return + } + const reactFlowBounds = event.currentTarget.getBoundingClientRect() const position = project({ x: event.clientX - reactFlowBounds.left, y: event.clientY - reactFlowBounds.top, }) - const { type } = JSON.parse( - event.dataTransfer.getData('application/json') - ) - const blockConfig = getBlock(type) + const blockConfig = getBlock(data.type) if (!blockConfig) { - console.error('Invalid block type:', type) + console.error('Invalid block type:', data.type) return } const id = crypto.randomUUID() const name = `${blockConfig.toolbar.title} ${ - Object.values(blocks).filter((b) => b.type === type).length + 1 + Object.values(blocks).filter((b) => b.type === data.type).length + 1 }` - addBlock(id, type, name, position) + addBlock(id, data.type, name, position) } catch (err) { console.error('Error dropping block:', err) } diff --git a/app/w/components/workflow-block/components/connection-blocks/connection-blocks.tsx b/app/w/components/workflow-block/components/connection-blocks/connection-blocks.tsx index b8e9243a13..73dd1589f1 100644 --- a/app/w/components/workflow-block/components/connection-blocks/connection-blocks.tsx +++ b/app/w/components/workflow-block/components/connection-blocks/connection-blocks.tsx @@ -3,9 +3,13 @@ import { Card } from '@/components/ui/card' interface ConnectionBlocksProps { blockId: string + setIsConnecting: (isConnecting: boolean) => void } -export function ConnectionBlocks({ blockId }: ConnectionBlocksProps) { +export function ConnectionBlocks({ + blockId, + setIsConnecting, +}: ConnectionBlocksProps) { const { incomingConnections, hasIncomingConnections } = useBlockConnections(blockId) @@ -13,10 +17,11 @@ export function ConnectionBlocks({ blockId }: ConnectionBlocksProps) { const handleDragStart = (e: React.DragEvent, connection: any) => { e.stopPropagation() // Prevent parent drag handlers from firing + setIsConnecting(true) e.dataTransfer.setData( 'application/json', JSON.stringify({ - type: 'connection', + type: 'connectionBlock', connectionData: { id: connection.id, name: connection.name, @@ -28,6 +33,10 @@ export function ConnectionBlocks({ blockId }: ConnectionBlocksProps) { e.dataTransfer.effectAllowed = 'copy' } + const handleDragEnd = (e: React.DragEvent) => { + setIsConnecting(false) + } + return (
{incomingConnections.map((connection) => ( @@ -35,6 +44,7 @@ export function ConnectionBlocks({ blockId }: ConnectionBlocksProps) { key={connection.id} draggable onDragStart={(e) => handleDragStart(e, connection)} + onDragEnd={handleDragEnd} className="group flex items-center rounded-lg border bg-card p-2 shadow-sm transition-colors hover:bg-accent/50 cursor-grab active:cursor-grabbing w-fit" >
diff --git a/app/w/components/workflow-block/components/sub-block/components/short-input.tsx b/app/w/components/workflow-block/components/sub-block/components/short-input.tsx index 2d3adeae87..d280511bf3 100644 --- a/app/w/components/workflow-block/components/sub-block/components/short-input.tsx +++ b/app/w/components/workflow-block/components/sub-block/components/short-input.tsx @@ -1,12 +1,14 @@ import { Input } from '@/components/ui/input' import { useState, useRef, useEffect } from 'react' import { useSubBlockValue } from '../hooks/use-sub-block-value' +import { cn } from '@/lib/utils' interface ShortInputProps { placeholder?: string password?: boolean blockId: string subBlockId: string + isConnecting: boolean } export function ShortInput({ @@ -14,16 +16,12 @@ export function ShortInput({ subBlockId, placeholder, password, + isConnecting, }: ShortInputProps) { const inputRef = useRef(null) const [isFocused, setIsFocused] = useState(false) const [value, setValue] = useSubBlockValue(blockId, subBlockId) - const displayValue = - password && !isFocused - ? '•'.repeat(value?.toString().length ?? 0) - : value?.toString() ?? '' - useEffect(() => { if (inputRef.current && isFocused) { const input = inputRef.current @@ -32,16 +30,49 @@ export function ShortInput({ } }, [value, isFocused]) + const handleDrop = (e: React.DragEvent) => { + e.preventDefault() + try { + const data = JSON.parse(e.dataTransfer.getData('application/json')) + if (data.type === 'connectionBlock') { + const currentValue = value?.toString() ?? '' + const newValue = + currentValue + + data.connectionData.name.replace(' ', '').toLowerCase() + + (data.connectionData.outputType === 'any' + ? '.res' + : `.${data.connectionData.outputType}`) + setValue(newValue) + } + } catch (error) { + console.error('Failed to parse drop data:', error) + } + } + + const handleDragOver = (e: React.DragEvent) => { + e.preventDefault() // This is needed to allow drops + } + + const displayValue = + password && !isFocused + ? '•'.repeat(value?.toString().length ?? 0) + : value?.toString() ?? '' + return ( setValue(e.target.value)} onFocus={() => setIsFocused(true)} onBlur={() => setIsFocused(false)} + onDrop={handleDrop} + onDragOver={handleDragOver} autoComplete="off" /> ) diff --git a/app/w/components/workflow-block/components/sub-block/sub-block.tsx b/app/w/components/workflow-block/components/sub-block/sub-block.tsx index c4712031b7..0a2e290bfd 100644 --- a/app/w/components/workflow-block/components/sub-block/sub-block.tsx +++ b/app/w/components/workflow-block/components/sub-block/sub-block.tsx @@ -10,9 +10,10 @@ import { Code } from './components/code' interface SubBlockProps { blockId: string config: SubBlockConfig + isConnecting: boolean } -export function SubBlock({ blockId, config }: SubBlockProps) { +export function SubBlock({ blockId, config, isConnecting }: SubBlockProps) { const handleMouseDown = (e: React.MouseEvent) => { e.stopPropagation() } @@ -26,6 +27,7 @@ export function SubBlock({ blockId, config }: SubBlockProps) { subBlockId={config.id} placeholder={config.placeholder} password={config.password} + isConnecting={isConnecting} /> ) case 'long-input': diff --git a/app/w/components/workflow-block/workflow-block.tsx b/app/w/components/workflow-block/workflow-block.tsx index 73e622e1e4..45e87215a9 100644 --- a/app/w/components/workflow-block/workflow-block.tsx +++ b/app/w/components/workflow-block/workflow-block.tsx @@ -5,6 +5,7 @@ import { Handle, Position } from 'reactflow' import { cn } from '@/lib/utils' import { ActionBar } from './components/action-bar/action-bar' import { ConnectionBlocks } from './components/connection-blocks/connection-blocks' +import { useState } from 'react' interface WorkflowBlockProps { id: string @@ -23,6 +24,8 @@ export function WorkflowBlock({ selected, }: WorkflowBlockProps) { const { toolbar, workflow } = config + // Dragging connection state + const [isConnecting, setIsConnecting] = useState(false) function groupSubBlocks(subBlocks: SubBlockConfig[]) { const rows: SubBlockConfig[][] = [] @@ -53,7 +56,7 @@ export function WorkflowBlock({ return ( {selected && } - + - +
))}