bookwiz.io / components / search / SearchInput.tsx
SearchInput.tsx
Raw
import { useState } from 'react'
import { IoSearchOutline, IoCloseOutline, IoSparklesOutline } from 'react-icons/io5'

export type SearchMode = 'text' | 'semantic'

interface SearchInputProps {
  value: string
  onChange: (value: string) => void
  mode: SearchMode
  onModeChange: (mode: SearchMode) => void
  isSearching?: boolean
}

export default function SearchInput({ 
  value, 
  onChange, 
  mode, 
  onModeChange, 
  isSearching = false 
}: SearchInputProps) {
  const [isFocused, setIsFocused] = useState(false)

  return (
    <div className="p-3 border-b border-slate-800/60 backdrop-blur-sm">
      {/* Search Mode Toggle */}
      <div className="flex mb-2.5 bg-slate-900/60 backdrop-blur-sm rounded-lg p-0.5 shadow-inner border border-slate-800/40">
        <button
          onClick={() => onModeChange('text')}
          className={`flex-1 flex items-center justify-center gap-1.5 py-1.5 px-2 rounded-md text-xs font-semibold transition-all duration-200 transform ${
            mode === 'text'
              ? 'bg-white/10 text-slate-100 shadow-lg backdrop-blur-sm border border-white/10 scale-[0.98]'
              : 'text-slate-400 hover:text-slate-200 hover:bg-white/5'
          }`}
        >
          <IoSearchOutline className="w-3 h-3" />
          <span>Text</span>
        </button>
        <button
          onClick={() => onModeChange('semantic')}
          className={`flex-1 flex items-center justify-center gap-1.5 py-1.5 px-2 rounded-md text-xs font-semibold transition-all duration-200 transform ${
            mode === 'semantic'
              ? 'bg-gradient-to-r from-violet-600/90 to-purple-600/90 text-white shadow-lg backdrop-blur-sm border border-violet-500/20 scale-[0.98]'
              : 'text-slate-400 hover:text-slate-200 hover:bg-white/5'
          }`}
        >
          <IoSparklesOutline className="w-3 h-3" />
          <span>AI</span>
        </button>
      </div>

      {/* Search Input */}
      <div className="relative group">
        <input
          type="text"
          value={value}
          onChange={(e) => onChange(e.target.value)}
          onFocus={() => setIsFocused(true)}
          onBlur={() => setIsFocused(false)}
          placeholder={
            mode === 'semantic' 
              ? "Search by meaning..." 
              : "Search files..."
          }
          className={`w-full px-3 py-2.5 pl-9 pr-9 bg-slate-900/60 backdrop-blur-sm border rounded-lg focus:outline-none focus:ring-2 text-xs font-medium text-slate-100 placeholder-slate-400 transition-all duration-200 shadow-sm ${
            mode === 'semantic'
              ? 'border-violet-500/30 focus:ring-violet-500/30 focus:border-violet-500/50 focus:shadow-violet-500/10'
              : 'border-slate-700/50 focus:ring-blue-500/30 focus:border-blue-500/50 focus:shadow-blue-500/10'
          } ${isFocused ? 'shadow-xl transform scale-[1.01]' : 'group-hover:border-slate-600/60'}`}
        />
        
        {/* Search Icon */}
        <div className={`absolute left-3 top-1/2 -translate-y-1/2 w-4 h-4 transition-all duration-200 ${
          mode === 'semantic' ? 'text-violet-400/80' : 'text-slate-400'
        } ${isFocused ? 'scale-110' : ''}`}>
          {isSearching ? (
            <div className={`animate-spin rounded-full h-4 w-4 border-2 border-transparent ${
              mode === 'semantic' 
                ? 'border-t-violet-400' 
                : 'border-t-slate-400'
            }`} />
          ) : mode === 'semantic' ? (
            <IoSparklesOutline className="w-4 h-4" />
          ) : (
            <IoSearchOutline className="w-4 h-4" />
          )}
        </div>

        {/* Clear Button */}
        {value && (
          <button 
            onClick={() => onChange('')}
            className="absolute right-3 top-1/2 -translate-y-1/2 p-1 text-slate-400 hover:text-slate-200 hover:bg-white/10 rounded transition-all duration-150 hover:scale-110"
          >
            <IoCloseOutline className="w-3.5 h-3.5" />
          </button>
        )}
      </div>

      {/* Mode Description */}
      <div className="mt-2 text-xs font-medium text-slate-500 leading-tight">
        {mode === 'semantic' ? (
          <span className="flex items-center gap-1">
            <span className="w-1.5 h-1.5 bg-violet-400/60 rounded-full"></span>
            AI-powered semantic search
          </span>
        ) : (
          <span className="flex items-center gap-1">
            <span className="w-1.5 h-1.5 bg-blue-400/60 rounded-full"></span>
            Exact text matching
          </span>
        )}
      </div>
    </div>
  )
}