mirror of
https://github.com/simstudioai/sim.git
synced 2026-09-24 15:45:35 +08:00
Took out input styling but kept state logic for dropping into input
This commit is contained in:
@@ -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
@@ -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'
|
||||
|
||||
Reference in New Issue
Block a user