Search
fluid-input.tsx
import { MagnifyingGlass, X } from '@phosphor-icons/react'import { AnimatePresence, motion, MotionConfig } from 'motion/react'import React from 'react'import { useOnClickOutside } from 'usehooks-ts'import { cn } from '@/src/utils'export function FluidInput() {const [input, setInput] = React.useState('')const [isActive, setIsActive] = React.useState(false)const ref = React.useRef<HTMLDivElement>(null)useOnClickOutside(ref as React.RefObject<HTMLDivElement>, () => {setInput('')setIsActive(false)})React.useEffect(() => {function onKeyDown(event: KeyboardEvent) {if (event.key === 'Escape') {setInput('')setIsActive(false)}}window.addEventListener('keydown', onKeyDown)return () => window.removeEventListener('keydown', onKeyDown)}, [])return (<MotionConfig transition={{ duration: 0.5, type: 'spring', bounce: 0.2 }}><motion.divref={ref}layouttabIndex={0}style={{ borderRadius: 16 }}onClick={() => setIsActive(true)}onFocus={() => setIsActive(true)}animate={{ width: isActive ? 300 : 116 }}className={cn('relative flex h-10 w-fit max-w-sm cursor-pointer! items-center overflow-hidden bg-neutral-800 outline-none', {'bg-neutral-800 hover:bg-neutral-700/50': !isActive,})}><div className={'pointer-events-none absolute left-4 flex cursor-default items-center'}><MagnifyingGlasssize={18}className={'text-white'}/></div><AnimatePresenceinitial={false}mode={'popLayout'}>{isActive ? (<motion.inputtype={'text'}spellCheck={false}autoComplete={'off'}animate={{ filter: 'blur(0px)' }}exit={{ filter: 'blur(4px)' }}style={{ borderRadius: 16 }}placeholder={'Username or tag'}value={input}autoFocusonChange={e => setInput(e.target.value)}className={'h-full w-full px-12 text-white outline-none placeholder:text-white/60'}/>) : (<motion.panimate={{ filter: 'blur(0px)' }}exit={{ filter: 'blur(4px)' }}className={'ml-11 pr-4 text-white/60'}>{'Search'}</motion.p>)}</AnimatePresence><AnimatePresenceinitial={false}mode={'popLayout'}>{isActive && (<motion.divinitial={{ opacity: 0, scale: 0.25, filter: 'blur(4px)' }}animate={{ opacity: 1, scale: 1, filter: 'blur(0px)' }}exit={{ opacity: 0, scale: 0.25, filter: 'blur(4px)' }}style={{ borderRadius: 9999 }}className={'absolute right-4 flex items-center justify-end overflow-hidden'}><motion.buttonanimate={{ width: input.length > 0 ? 20 : 56 }}transition={{ duration: 0.25, type: 'spring', bounce: 0.2 }}onClick={() => {if (input.length > 0) {setInput('')}}}className={cn('flex size-5 cursor-pointer items-center justify-center', {'bg-neutral-200 px-2 text-xs font-semibold text-neutral-800 uppercase': input.length === 0,'bg-neutral-600 p-1 text-neutral-50': input.length > 0,})}>{input.length > 0 ? <X weight={'bold'} /> : 'Paste'}</motion.button></motion.div>)}</AnimatePresence></motion.div></MotionConfig>)}