import React, { useState, useCallback } from 'react'; import { TrashIcon, PlusIcon, GripVerticalIcon, EditIcon } from './Icons'; import { UserSettings, SearchEngine } from '../types'; import { useToast } from '../context/ToastContext'; import { useTranslation } from '../i18n'; interface SearchEngineManagerProps { settings: UserSettings; onUpdateSettings: (newSettings: UserSettings) => void; } const SearchEngineManager: React.FC = ({ settings, onUpdateSettings }) => { const [newEngineName, setNewEngineName] = useState(''); const [newEngineUrl, setNewEngineUrl] = useState(''); const [newEngineIcon, setNewEngineIcon] = useState(''); const [isAddingEngine, setIsAddingEngine] = useState(false); const [editingOriginalName, setEditingOriginalName] = useState(null); const [draggedIndex, setDraggedIndex] = useState(null); const { showToast } = useToast(); const { t } = useTranslation(); const handleDeleteEngine = useCallback((nameToDelete: string) => { if (settings.searchEngines.length <= 1) return; const newEngines = settings.searchEngines.filter(e => e.name !== nameToDelete); let newSelected = settings.selectedEngine; if (settings.selectedEngine === nameToDelete) { newSelected = newEngines[0].name; } onUpdateSettings({ ...settings, searchEngines: newEngines, selectedEngine: newSelected }); showToast(t.searchEngineDeleted, 'success'); }, [settings, onUpdateSettings, showToast, t]); const handleSetDefault = useCallback((name: string) => { onUpdateSettings({ ...settings, selectedEngine: name }); // Optional: showToast(`已设置 ${name} 为默认搜索引擎`, 'success'); }, [settings, onUpdateSettings]); const handleDragStart = useCallback((e: React.DragEvent, index: number) => { setDraggedIndex(index); e.dataTransfer.effectAllowed = "move"; e.dataTransfer.setData("text/plain", index.toString()); }, []); const handleDragOver = useCallback((e: React.DragEvent, index: number) => { e.preventDefault(); if (draggedIndex === null || draggedIndex === index) return; const newEngines = [...settings.searchEngines]; const draggedItem = newEngines[draggedIndex]; newEngines.splice(draggedIndex, 1); newEngines.splice(index, 0, draggedItem); onUpdateSettings({ ...settings, searchEngines: newEngines }); setDraggedIndex(index); }, [draggedIndex, settings, onUpdateSettings]); const handleDragEnd = useCallback(() => { setDraggedIndex(null); }, []); const handleEditEngine = useCallback((engine: SearchEngine) => { setNewEngineName(engine.name); setNewEngineUrl(engine.urlPattern); setNewEngineIcon(engine.icon || ''); setEditingOriginalName(engine.name); setIsAddingEngine(true); }, []); const handleSaveEngine = useCallback(() => { if (!newEngineName.trim() || !newEngineUrl.trim()) return; const name = newEngineName.trim(); // Check for duplicate name if we are adding new or renaming const isRenaming = editingOriginalName && editingOriginalName !== name; const isAdding = !editingOriginalName; if (isAdding || isRenaming) { const exists = settings.searchEngines.some(e => e.name === name); if (exists) { showToast(t.duplicateEngineName, 'error'); return; } } const newEngine: SearchEngine = { name: name, urlPattern: newEngineUrl.trim(), icon: newEngineIcon.trim() || undefined }; let updatedEngines = [...settings.searchEngines]; let updatedSelected = settings.selectedEngine; if (editingOriginalName) { // Update existing const index = updatedEngines.findIndex(e => e.name === editingOriginalName); if (index !== -1) { updatedEngines[index] = newEngine; } // If the edited engine was the selected one, update the selection reference if (settings.selectedEngine === editingOriginalName) { updatedSelected = newEngine.name; } showToast(t.searchEngineUpdated, 'success'); } else { // Add new updatedEngines.push(newEngine); showToast(t.newSearchEngineAdded, 'success'); } onUpdateSettings({ ...settings, searchEngines: updatedEngines, selectedEngine: updatedSelected }); setNewEngineName(''); setNewEngineUrl(''); setNewEngineIcon(''); setEditingOriginalName(null); setIsAddingEngine(false); }, [newEngineName, newEngineUrl, newEngineIcon, editingOriginalName, settings, onUpdateSettings, showToast, t]); const handleCancel = useCallback(() => { setIsAddingEngine(false); setNewEngineName(''); setNewEngineUrl(''); setNewEngineIcon(''); setEditingOriginalName(null); }, []); return (

{t.searchEngines}

{!isAddingEngine && ( )}
{/* Search engine list */}
{settings.searchEngines.map((engine, index) => (
handleDragStart(e, index)} onDragOver={(e) => handleDragOver(e, index)} onDragEnd={handleDragEnd} className={` flex items-center justify-between p-3 rounded-xl bg-white/5 border border-white/5 group transition-all duration-200 ${draggedIndex === index ? 'opacity-40 border-dashed border-white/30' : ''} ${editingOriginalName === engine.name ? 'ring-1 ring-white/30 bg-white/10' : ''} `} >
{/* Drag handle */}
{engine.name} {engine.urlPattern}
{/* Default/Set as default button */} {settings.selectedEngine === engine.name ? ( {t.current} ) : ( )} {/* Edit button */} {/* Delete button */}
))}
{/* Add/Edit engine form */} {isAddingEngine && (
{editingOriginalName ? t.editSearchEngine : t.addCustomEngine}
setNewEngineName(e.target.value)} placeholder="e.g.: Google" className="w-full bg-black/20 border border-white/10 rounded-lg px-3 py-2 text-sm text-white focus:border-white/30 focus:outline-none transition-colors" autoFocus />
setNewEngineUrl(e.target.value)} placeholder="https://example.com/search?q=" className="w-full bg-black/20 border border-white/10 rounded-lg px-3 py-2 text-sm text-white focus:border-white/30 focus:outline-none transition-colors font-mono" />