/
PashigorevAY
/
TestFastAPI
Обзор
Документация
Войти
/
PashigorevAY
/
TestFastAPI
Код
Запросы
0
Задачи
Вики
Пакеты
0
Релизы
0
CI/CD
Аналитика
Безопасность
master
frontend/src/components/MultiplePersonSelect.tsx
195 строк
6 KB
Pashigorev
Initial commit
05 июл 2026, 21:43
05 июл 2026, 21:43
19770a6
Код
Авторство
О чём код?
import { useState, useEffect, useRef } from 'react'; import { Person } from '../types'; import { api } from '../api'; interface MultiplePersonSelectProps { value: string[]; onChange: (value: string[]) => void; label: string; } export function MultiplePersonSelect({ value, onChange, label }: MultiplePersonSelectProps) { const [people, setPeople] = useState<Person[]>([]); const [inputValue, setInputValue] = useState(''); const [showDropdown, setShowDropdown] = useState(false); const dropdownRef = useRef<HTMLDivElement>(null); useEffect(() => { loadPeople(); }, []); useEffect(() => { const handleClickOutside = (event: MouseEvent) => { if (dropdownRef.current && !dropdownRef.current.contains(event.target as Node)) { setShowDropdown(false); } }; document.addEventListener('mousedown', handleClickOutside); return () => document.removeEventListener('mousedown', handleClickOutside); }, []); async function loadPeople() { const data = await api.getPeople(); setPeople(data.people); } const getCurrentSearchTerm = () => { const parts = inputValue.split(','); return parts[parts.length - 1].trim(); }; const filteredPeople = people.filter((p) => { const searchTerm = getCurrentSearchTerm().toLowerCase(); return ( p.name.toLowerCase().includes(searchTerm) || p.code.toLowerCase().includes(searchTerm) ) && !value.includes(p.code); }); function handleInputChange(e: React.ChangeEvent<HTMLInputElement>) { const newInputValue = e.target.value; setInputValue(newInputValue); setShowDropdown(true); } function handleKeyDown(e: React.KeyboardEvent<HTMLInputElement>) { if (e.key === ',' || e.key === 'Enter') { e.preventDefault(); const searchTerm = getCurrentSearchTerm().trim(); if (searchTerm && !value.includes(searchTerm)) { const newValue = [...value, searchTerm]; onChange(newValue); const parts = inputValue.split(','); parts.pop(); setInputValue(parts.length > 0 ? parts.join(',') + ',' : ''); } } else if (e.key === 'Backspace' && inputValue === '') { const newValue = [...value]; newValue.pop(); onChange(newValue); } } function handleSelectPerson(person: Person) { const newValue = [...value, person.code]; onChange(newValue); const parts = inputValue.split(','); parts.pop(); setInputValue(parts.length > 0 ? parts.join(',') + ',' : ''); setShowDropdown(true); } function removePerson(code: string) { onChange(value.filter((c) => c !== code)); } return ( <div style={{ position: 'relative' }}> <label style={{ display: 'block', marginBottom: '4px' }}>{label}</label> <div style={{ display: 'flex', flexWrap: 'wrap', gap: '4px', padding: '6px', border: '1px solid #ddd', borderRadius: '4px', cursor: 'text', }} onClick={() => setShowDropdown(true)} > {value.map((code) => { const person = people.find((p) => p.code === code); return ( <span key={code} style={{ background: '#e0e0e0', padding: '2px 6px', borderRadius: '4px', fontSize: '13px', display: 'flex', alignItems: 'center', gap: '4px', }} > {person ? person.name : code} <button onClick={(e) => { e.stopPropagation(); removePerson(code); }} style={{ background: 'transparent', border: 'none', cursor: 'pointer', padding: 0, fontSize: '14px', lineHeight: 1, }} > × </button> </span> ); })} <input type="text" value={inputValue} onChange={handleInputChange} onKeyDown={handleKeyDown} onFocus={() => setShowDropdown(true)} placeholder={value.length === 0 ? 'Выберите или введите коды' : ''} style={{ border: 'none', outline: 'none', flex: 1, minWidth: '150px', padding: '4px', }} /> </div> {showDropdown && ( <div ref={dropdownRef} style={{ position: 'absolute', top: '100%', left: 0, right: 0, background: 'white', border: '1px solid #ddd', borderRadius: '4px', boxShadow: '0 2px 8px rgba(0,0,0,0.1)', zIndex: 100, maxHeight: '200px', overflowY: 'auto', }} > {filteredPeople.length > 0 ? ( filteredPeople.map((person) => ( <div key={person.code} onClick={() => handleSelectPerson(person)} style={{ padding: '8px 12px', cursor: 'pointer', borderBottom: '1px solid #eee', }} onMouseEnter={(e) => e.currentTarget.style.backgroundColor = '#f5f5f5'} onMouseLeave={(e) => e.currentTarget.style.backgroundColor = 'transparent'} > <div style={{ fontWeight: '500' }}>{person.name}</div> <div style={{ color: '#666', fontSize: '12px' }}>{person.code} • {person.position}</div> </div> )) ) : ( <div style={{ padding: '12px', color: '#999', textAlign: 'center' }}> {getCurrentSearchTerm() ? 'Ничего не найдено' : 'Начните вводить'} </div> )} </div> )} </div> ); }