bookwiz.io / components / search / SearchOptions.tsx
SearchOptions.tsx
Raw
import { useState } from 'react'
import { IoOptionsOutline, IoChevronDownOutline, IoChevronUpOutline } from 'react-icons/io5'
import { SearchMode } from './SearchInput'

export interface SearchOptions {
  matchCase: boolean
  matchWholeWord: boolean
  useRegex: boolean
  includeIgnored: boolean
}

interface SearchOptionsProps {
  mode: SearchMode
  options: SearchOptions
  onOptionsChange: (options: SearchOptions) => void
  includeFilter: string
  excludeFilter: string
  onIncludeFilterChange: (filter: string) => void
  onExcludeFilterChange: (filter: string) => void
}

export default function SearchOptionsComponent({ 
  mode, 
  options, 
  onOptionsChange,
  includeFilter,
  excludeFilter,
  onIncludeFilterChange,
  onExcludeFilterChange
}: SearchOptionsProps) {
  const [showAdvanced, setShowAdvanced] = useState(false)

  // Only show for text search mode
  if (mode !== 'text') return null

  const updateOption = (key: keyof SearchOptions, value: boolean) => {
    onOptionsChange({ ...options, [key]: value })
  }

  return (
    <div className="border-b border-slate-800/60 backdrop-blur-sm">
      {/* Basic Options */}
      <div className="px-3 py-2 flex items-center justify-between">
        <div className="flex items-center gap-1.5">
          <button 
            className={`px-2 py-1 text-xs font-semibold rounded-md transition-all duration-200 transform hover:scale-105 ${
              options.matchCase 
                ? 'bg-blue-500/20 text-blue-300 shadow-sm border border-blue-500/30 backdrop-blur-sm' 
                : 'text-slate-400 hover:text-slate-200 hover:bg-white/10 border border-transparent'
            }`}
            onClick={() => updateOption('matchCase', !options.matchCase)}
            title="Match Case"
          >
            Aa
          </button>
          <button 
            className={`px-2 py-1 text-xs font-mono font-semibold rounded-md transition-all duration-200 transform hover:scale-105 ${
              options.matchWholeWord 
                ? 'bg-blue-500/20 text-blue-300 shadow-sm border border-blue-500/30 backdrop-blur-sm' 
                : 'text-slate-400 hover:text-slate-200 hover:bg-white/10 border border-transparent'
            }`}
            onClick={() => updateOption('matchWholeWord', !options.matchWholeWord)}
            title="Match Whole Word"
          >
            \b
          </button>
          <button 
            className={`px-2 py-1 text-xs font-mono font-semibold rounded-md transition-all duration-200 transform hover:scale-105 ${
              options.useRegex 
                ? 'bg-blue-500/20 text-blue-300 shadow-sm border border-blue-500/30 backdrop-blur-sm' 
                : 'text-slate-400 hover:text-slate-200 hover:bg-white/10 border border-transparent'
            }`}
            onClick={() => updateOption('useRegex', !options.useRegex)}
            title="Use Regular Expression"
          >
            .*
          </button>
        </div>
        
        <button 
          className={`flex items-center gap-1.5 px-2 py-1 text-xs font-semibold rounded-md transition-all duration-200 transform hover:scale-105 ${
            showAdvanced
              ? 'text-slate-200 bg-white/10 backdrop-blur-sm border border-white/10 shadow-sm'
              : 'text-slate-400 hover:text-slate-200 hover:bg-white/10 border border-transparent'
          }`}
          onClick={() => setShowAdvanced(!showAdvanced)}
          title="Advanced Options"
        >
          <IoOptionsOutline className="w-3 h-3" />
          <span>More</span>
          <div className={`transition-transform duration-200 ${showAdvanced ? 'rotate-180' : ''}`}>
            <IoChevronDownOutline className="w-2.5 h-2.5" />
          </div>
        </button>
      </div>

      {/* Advanced Options */}
      <div className={`overflow-hidden transition-all duration-300 ease-out ${
        showAdvanced ? 'max-h-96 opacity-100' : 'max-h-0 opacity-0'
      }`}>
        <div className="px-3 pb-3 space-y-3 border-t border-slate-800/40">
          <div className="pt-3 space-y-3">
            <div className="space-y-1.5">
              <label className="block text-xs font-semibold text-slate-300">Include</label>
              <input
                type="text"
                placeholder="*.md, *.txt"
                value={includeFilter}
                onChange={(e) => onIncludeFilterChange(e.target.value)}
                className="w-full px-2.5 py-2 text-xs font-medium bg-slate-900/60 backdrop-blur-sm border border-slate-700/50 rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500/30 focus:border-blue-500/50 text-slate-200 placeholder-slate-500 transition-all duration-200 hover:border-slate-600/60"
              />
            </div>
            <div className="space-y-1.5">
              <label className="block text-xs font-semibold text-slate-300">Exclude</label>
              <input
                type="text"
                placeholder="*.log, temp*"
                value={excludeFilter}
                onChange={(e) => onExcludeFilterChange(e.target.value)}
                className="w-full px-2.5 py-2 text-xs font-medium bg-slate-900/60 backdrop-blur-sm border border-slate-700/50 rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500/30 focus:border-blue-500/50 text-slate-200 placeholder-slate-500 transition-all duration-200 hover:border-slate-600/60"
              />
            </div>
            <div className="pt-1">
              <label className="flex items-center gap-2.5 text-xs font-semibold text-slate-300 cursor-pointer group">
                <div className="relative">
                  <input
                    type="checkbox"
                    checked={options.includeIgnored}
                    onChange={(e) => updateOption('includeIgnored', e.target.checked)}
                    className="sr-only"
                  />
                  <div className={`w-3.5 h-3.5 rounded border-2 transition-all duration-200 ${
                    options.includeIgnored
                      ? 'bg-blue-500 border-blue-500'
                      : 'border-slate-600 group-hover:border-slate-500'
                  }`}>
                    {options.includeIgnored && (
                      <svg className="w-2.5 h-2.5 text-white absolute top-0 left-0" viewBox="0 0 20 20" fill="currentColor">
                        <path fillRule="evenodd" d="M16.707 5.293a1 1 0 010 1.414l-8 8a1 1 0 01-1.414 0l-4-4a1 1 0 011.414-1.414L8 12.586l7.293-7.293a1 1 0 011.414 0z" clipRule="evenodd" />
                      </svg>
                    )}
                  </div>
                </div>
                <span className="group-hover:text-slate-200 transition-colors">Include ignored</span>
              </label>
            </div>
          </div>
        </div>
      </div>
    </div>
  )
}