CreepURL / frontend /src /components /features /InputPanel.tsx
Dipan04's picture
Initial commit
d6e2363
Raw
History Blame Contribute Delete
3.7 kB
import { useState, FormEvent } from 'react'
import { motion } from 'framer-motion'
import { DestructionSlider } from './DestructionSlider'
import { isValidURL } from '@/lib/utils'
interface InputPanelProps {
onSubmit: (url: string, level: number) => void
isLoading: boolean
}
export function InputPanel({ onSubmit, isLoading }: InputPanelProps) {
const [url, setUrl] = useState('')
const [level, setLevel] = useState(3)
const [touched, setTouched] = useState(false)
const isValid = isValidURL(url) || url.trim().length === 0
const showError = touched && url.trim().length > 0 && !isValidURL(url)
const handleSubmit = (e: FormEvent) => {
e.preventDefault()
setTouched(true)
const cleaned = url.trim()
if (!cleaned) return
const withScheme = cleaned.startsWith('http') ? cleaned : `https://${cleaned}`
if (!isValidURL(withScheme)) return
onSubmit(withScheme, level)
}
return (
<motion.div
initial={{ opacity: 0, y: 20, rotate: 1.5 }}
animate={{ opacity: 1, y: 0, rotate: 1.5 }}
transition={{ duration: 0.45, delay: 0.15, ease: [0.16, 1, 0.3, 1] }}
className="brutal-panel paper-stack relative w-full"
style={{ background: '#efe5d1' }}
>
<div className="border-b-[3px] border-black bg-black px-4 py-3 text-[0.72rem] font-semibold uppercase tracking-[0.2em] text-[#f5f0e6]">
Input bay / corruption console
</div>
<form onSubmit={handleSubmit} className="flex flex-col gap-5 p-5 sm:p-6">
<div className="grid gap-2">
<label className="mono-label">Feed a clean URL</label>
<div className="relative">
<input
type="text"
value={url}
onChange={e => {
setUrl(e.target.value)
setTouched(false)
}}
onBlur={() => setTouched(true)}
placeholder="https://company.com/looks-legit"
className="input-field pr-12"
autoComplete="off"
autoCorrect="off"
autoCapitalize="off"
spellCheck={false}
/>
{url && (
<button
type="button"
onClick={() => {
setUrl('')
setTouched(false)
}}
className="absolute right-3 top-1/2 flex h-8 w-8 -translate-y-1/2 items-center justify-center border-2 border-black bg-[#ffd84d] font-black"
>
X
</button>
)}
</div>
{showError && (
<motion.p initial={{ opacity: 0, y: -4 }} animate={{ opacity: 1, y: 0 }} className="mono-label text-[#cf3d1f]">
invalid input / scheme missing / format rejected
</motion.p>
)}
</div>
<div className="divider" />
<DestructionSlider value={level} onChange={setLevel} />
<div className="grid gap-3 sm:grid-cols-[1fr_auto] sm:items-end">
<div className="brutal-panel-alt p-3 text-sm" style={{ background: '#ffd84d' }}>
<p className="mono-label mb-1">Machine note</p>
<p>Higher levels produce weirder slugs, lower trust, and more visible digital rot.</p>
</div>
<motion.button
type="submit"
disabled={isLoading || !url.trim() || !isValid}
whileHover={!isLoading ? { y: -1 } : {}}
whileTap={!isLoading ? { y: 0 } : {}}
className="btn-primary min-w-[220px]"
>
{isLoading ? 'Corrupting link...' : 'Destroy trust'}
</motion.button>
</div>
</form>
</motion.div>
)
}