| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139 |
- import React, { useState } from 'react';
- import { useLanguage } from '../contexts/LanguageContext';
- import { KnowledgeGroup } from '../types';
- import { Check, X, Search, Database } from 'lucide-react';
- interface GroupSelectionDrawerProps {
- isOpen: boolean;
- onClose: () => void;
- groups: KnowledgeGroup[];
- selectedGroups: string[];
- onSelectionChange: (groupIds: string[]) => void;
- }
- export const GroupSelectionDrawer: React.FC<GroupSelectionDrawerProps> = ({
- isOpen,
- onClose,
- groups,
- selectedGroups,
- onSelectionChange
- }) => {
- const { t } = useLanguage();
- const [searchTerm, setSearchTerm] = useState('');
- if (!isOpen) return null;
- const filteredGroups = groups.filter(g =>
- g.name.toLowerCase().includes(searchTerm.toLowerCase())
- );
- const isAllSelected = selectedGroups.length === 0;
- const handleToggleGroup = (groupId: string) => {
- if (selectedGroups.includes(groupId)) {
- onSelectionChange(selectedGroups.filter(id => id !== groupId));
- } else {
- onSelectionChange([...selectedGroups, groupId]);
- }
- };
- const handleSelectAll = () => {
- onSelectionChange([]);
- };
- return (
- <div className="fixed inset-0 z-50 overflow-hidden">
- <div className="absolute inset-0 bg-black bg-opacity-30 transition-opacity" onClick={onClose} />
- <div className="absolute inset-y-0 right-0 max-w-md w-full flex">
- <div className="flex-1 flex flex-col bg-white shadow-xl animate-in slide-in-from-right duration-300">
- <div className="flex items-center justify-between px-4 py-3 border-b border-gray-200">
- <h2 className="text-lg font-medium text-gray-900 flex items-center gap-2">
- <Database size={20} />
- {t('selectKnowledgeGroups')}
- </h2>
- <button
- onClick={onClose}
- className="text-gray-400 hover:text-gray-500 focus:outline-none"
- >
- <X size={24} />
- </button>
- </div>
- {/* Search Box */}
- <div className="p-4 border-b border-gray-100">
- <div className="relative">
- <Search className="absolute left-3 top-1/2 -translate-y-1/2 w-4 h-4 text-gray-400" />
- <input
- type="text"
- placeholder={t('searchGroupsPlaceholder')}
- value={searchTerm}
- onChange={(e) => setSearchTerm(e.target.value)}
- className="w-full pl-9 pr-4 py-2 text-sm border border-gray-200 rounded-lg focus:outline-none focus:border-blue-500 focus:ring-1 focus:ring-blue-500 bg-gray-50"
- />
- </div>
- </div>
- <div className="flex-1 overflow-y-auto p-2">
- {!searchTerm && (
- <div
- onClick={handleSelectAll}
- className={`flex items-center px-4 py-3 cursor-pointer hover:bg-gray-50 rounded-lg mb-1 transition-colors ${isAllSelected ? 'bg-blue-50 text-blue-700' : 'text-gray-700'
- }`}
- >
- <div className={`w-5 h-5 mr-3 border rounded flex items-center justify-center ${isAllSelected ? 'bg-blue-600 border-blue-600' : 'border-gray-300'
- }`}>
- {isAllSelected && <Check size={14} className="text-white" />}
- </div>
- <span className="font-medium">{t('all')}</span>
- </div>
- )}
- {filteredGroups.map((group) => {
- const isSelected = selectedGroups.includes(group.id);
- return (
- <div
- key={group.id}
- onClick={() => handleToggleGroup(group.id)}
- className={`flex items-center px-4 py-3 cursor-pointer hover:bg-gray-50 rounded-lg mb-1 transition-colors ${isSelected ? 'bg-blue-50' : ''
- }`}
- >
- <div className={`w-5 h-5 mr-3 border rounded flex items-center justify-center flex-shrink-0 ${isSelected ? 'bg-blue-600 border-blue-600' : 'border-gray-300'
- }`}>
- {isSelected && <Check size={14} className="text-white" />}
- </div>
- <div
- className="w-3 h-3 rounded-full mr-3 flex-shrink-0"
- style={{ backgroundColor: group.color }}
- />
- <div className="flex-1 min-w-0">
- <div className={`text-sm truncate ${isSelected ? 'text-blue-700 font-medium' : 'text-gray-700'}`}>
- {group.name}
- </div>
- <div className="text-xs text-gray-400 mt-0.5">
- {group.fileCount} 个文件
- </div>
- </div>
- </div>
- );
- })}
- {filteredGroups.length === 0 && (
- <div className="py-8 text-center text-gray-400 text-sm">
- {searchTerm ? t('noGroupsFound') : t('noGroups')}
- </div>
- )}
- </div>
- <div className="p-4 border-t border-gray-200 bg-gray-50">
- <button
- onClick={onClose}
- className="w-full py-2 bg-blue-600 text-white rounded-lg hover:bg-blue-700 transition-colors font-medium"
- >
- {t('done')} ({isAllSelected ? t('all') : selectedGroups.length})
- </button>
- </div>
- </div>
- </div>
- </div>
- );
- };
|