Took out input styling but kept state logic for dropping into input

This commit is contained in:
Emir Karabeg
2025-01-28 15:02:55 -08:00
parent 4d3aead26c
commit 58d5a83150
2 changed files with 38 additions and 163 deletions
@@ -1,8 +1,10 @@
// Imports
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'
// Component Props Interface
interface ShortInputProps {
placeholder?: string
password?: boolean
@@ -18,15 +20,12 @@ export function ShortInput({
password,
isConnecting,
}: ShortInputProps) {
// Hooks and State
const inputRef = useRef<HTMLInputElement>(null)
const [isFocused, setIsFocused] = useState(false)
const [value, setValue] = useSubBlockValue(blockId, subBlockId)
const [connections, setConnections] = useState<any[]>([])
useEffect(() => {
console.log(connections)
}, [connections])
// Auto-scroll effect for input
useEffect(() => {
if (inputRef.current && isFocused) {
const input = inputRef.current
@@ -35,43 +34,29 @@ export function ShortInput({
}
}, [value, isFocused])
// Add regex pattern for connection syntax
const connectionPattern = /<([a-z0-9]+)\.(string|number|boolean|res|any)>/g
const updateConnections = (inputValue: string) => {
const newConnections = Array.from(
inputValue.matchAll(connectionPattern)
).map((match) => match[0].slice(1, -1)) // Remove < and >
setConnections(Array.from(new Set(newConnections))) // Use Set to ensure uniqueness
}
const handleChange = (e: React.ChangeEvent<HTMLInputElement>) => {
const newValue = e.target.value
setValue(newValue)
updateConnections(newValue)
}
// Drag and Drop handlers
const handleDrop = (e: React.DragEvent<HTMLInputElement>) => {
e.preventDefault()
try {
const data = JSON.parse(e.dataTransfer.getData('application/json'))
if (
const isValidConnectionBlock =
data.type === 'connectionBlock' &&
data.connectionData.sourceBlockId === blockId
) {
const currentValue = value?.toString() ?? ''
const formattedName = data.connectionData.name
.replace(' ', '')
.toLowerCase()
const connectionType =
data.connectionData.outputType === 'any'
? 'res'
: data.connectionData.outputType
const newConnection = formattedName + '.' + connectionType
const newValue = currentValue + `<${newConnection}>`
setValue(newValue)
updateConnections(newValue)
}
if (!isValidConnectionBlock) return
const currentValue = value?.toString() ?? ''
const connectionName = data.connectionData.name
.replace(' ', '')
.toLowerCase()
const outputSuffix =
data.connectionData.outputType === 'any'
? 'res'
: data.connectionData.outputType
const newValue = `${currentValue}<${connectionName}.${outputSuffix}>`
setValue(newValue)
} catch (error) {
console.error('Failed to parse drop data:', error)
}
@@ -81,46 +66,24 @@ export function ShortInput({
e.preventDefault() // This is needed to allow drops
}
const handleKeyDown = (e: React.KeyboardEvent<HTMLInputElement>) => {
if (e.key === 'Backspace' && inputRef.current) {
const cursorPosition: any = inputRef.current.selectionStart
const currentValue = value?.toString() ?? ''
// Check if cursor is right after a connection closing bracket
for (const connection of connections) {
const pattern = `<${connection}>`
const index = currentValue.lastIndexOf(pattern, cursorPosition)
if (index !== -1 && index + pattern.length === cursorPosition) {
e.preventDefault()
const newValue =
currentValue.slice(0, index) +
currentValue.slice(index + pattern.length)
setValue(newValue)
return
}
}
}
}
// Value display logic
const displayValue =
password && !isFocused
? '•'.repeat(value?.toString().length ?? 0)
: value?.toString() ?? ''
// Component render
return (
<Input
ref={inputRef}
className={cn(
'w-full placeholder:text-muted-foreground/50 allow-scroll',
isConnecting && 'ring-2 ring-blue-500 ring-offset-2'
isConnecting && 'border-blue-500'
)}
placeholder={placeholder ?? ''}
type="text"
value={displayValue}
connections={connections}
onChange={handleChange}
onKeyDown={handleKeyDown}
onChange={(e) => setValue(e.target.value)}
onFocus={() => setIsFocused(true)}
onBlur={() => setIsFocused(false)}
onDrop={handleDrop}
+13 -101
View File
@@ -2,107 +2,19 @@ import * as React from 'react'
import { cn } from '@/lib/utils'
interface InputProps
extends Omit<React.ComponentProps<'input'>, 'value' | 'onChange'> {
className?: string
value?: string
connections?: string[]
onChange?: (e: React.ChangeEvent<HTMLInputElement>) => void
}
const Input = React.forwardRef<HTMLInputElement, InputProps>(
(
{ className, type, value = '', connections = [], onChange, ...props },
ref
) => {
// Create a hidden input for maintaining focus and handling keyboard events
const hiddenInputRef = React.useRef<HTMLInputElement>(null)
const renderContent = () => {
if (!connections?.length) {
return (
<input
type={type}
className={cn(
'flex h-10 w-full rounded-md border border-input bg-background px-3 py-2 text-base ring-offset-background file:border-0 file:bg-transparent file:text-sm file:font-medium file:text-foreground placeholder:text-muted-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50 md:text-sm',
className
)}
value={value}
onChange={onChange}
ref={ref}
{...props}
/>
)
}
// Split text by connection patterns
const parts = []
let lastIndex = 0
connections.forEach((connection) => {
const pattern = `<${connection}>`
const index = value.indexOf(pattern, lastIndex)
if (index !== -1) {
// Add text before connection
if (index > lastIndex) {
parts.push({
type: 'text',
content: value.slice(lastIndex, index),
})
}
// Add connection
parts.push({
type: 'connection',
content: connection,
})
lastIndex = index + pattern.length
}
})
// Add remaining text
if (lastIndex < value.length) {
parts.push({
type: 'text',
content: value.slice(lastIndex),
})
}
return (
<div
className={cn(
'flex h-10 w-full rounded-md border border-input bg-background px-3 py-2 text-base ring-offset-background focus-within:ring-2 focus-within:ring-ring focus-within:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50 md:text-sm',
className
)}
onClick={() => hiddenInputRef.current?.focus()}
>
<div className="flex flex-wrap gap-1 items-center w-full">
{parts.map((part, index) =>
part.type === 'connection' ? (
<span
key={index}
className="bg-blue-100 text-blue-800 px-1 rounded text-sm"
>
{part.content}
</span>
) : (
<span key={index}>{part.content}</span>
)
)}
<input
ref={hiddenInputRef}
type="text"
className="w-px opacity-0 absolute"
value={value}
onChange={onChange}
{...props}
/>
</div>
</div>
)
}
return renderContent()
const Input = React.forwardRef<HTMLInputElement, React.ComponentProps<'input'>>(
({ className, type, ...props }, ref) => {
return (
<input
type={type}
className={cn(
'flex h-10 w-full rounded-md border border-input bg-background px-3 py-2 text-base ring-offset-background file:border-0 file:bg-transparent file:text-sm file:font-medium file:text-foreground placeholder:text-muted-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50 md:text-sm',
className
)}
ref={ref}
{...props}
/>
)
}
)
Input.displayName = 'Input'