Set up basic drag connection block into sub block. For now only finished short-input.

This commit is contained in:
Emir Karabeg
2025-01-21 20:52:10 -08:00
parent cc68272b48
commit 51835e9aa6
5 changed files with 72 additions and 18 deletions
+11 -7
View File
@@ -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)
}
@@ -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 (
<div className="absolute -left-[180px] top-0 space-y-2 flex flex-col items-end w-[160px]">
{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"
>
<div className="text-sm">
@@ -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<HTMLInputElement>(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<HTMLInputElement>) => {
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<HTMLInputElement>) => {
e.preventDefault() // This is needed to allow drops
}
const displayValue =
password && !isFocused
? '•'.repeat(value?.toString().length ?? 0)
: value?.toString() ?? ''
return (
<Input
ref={inputRef}
className="w-full placeholder:text-muted-foreground/50 allow-scroll"
className={cn(
'w-full placeholder:text-muted-foreground/50 allow-scroll',
isConnecting && 'border-blue-500'
)}
placeholder={placeholder ?? ''}
type="text"
value={displayValue}
onChange={(e) => setValue(e.target.value)}
onFocus={() => setIsFocused(true)}
onBlur={() => setIsFocused(false)}
onDrop={handleDrop}
onDragOver={handleDragOver}
autoComplete="off"
/>
)
@@ -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':
@@ -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 (
<Card className="w-[320px] shadow-md select-none group relative cursor-default">
{selected && <ActionBar blockId={id} />}
<ConnectionBlocks blockId={id} />
<ConnectionBlocks blockId={id} setIsConnecting={setIsConnecting} />
<Handle
type="target"
@@ -88,7 +91,11 @@ export function WorkflowBlock({
subBlock.layout === 'half' ? 'flex-1' : 'w-full'
}`}
>
<SubBlock blockId={id} config={subBlock} />
<SubBlock
blockId={id}
config={subBlock}
isConnecting={isConnecting}
/>
</div>
))}
</div>