import { memo, useCallback, useEffect, useRef, useState } from 'react'; import { createPortal } from 'react-dom'; import { IoIosArrowDown, IoIosArrowUp, IoIosCloseCircle } from 'react-icons/io'; import '../../../Styles/Product/TurboAddForm.scss'; const CellSelect = memo( ({ value, onChange, onKeyDown, dataCell, options, hasError, placeholder = 'Select option', onClick, }) => { const [isOpen, setIsOpen] = useState(false); const [dropdownDirection, setDropdownDirection] = useState('down'); const [searchTerm, setSearchTerm] = useState(''); const [dropdownPosition, setDropdownPosition] = useState({ top: 0, left: 0, width: 0, }); const selectRef = useRef(null); const dropdownRef = useRef(null); const searchInputRef = useRef(null); // Filter options based on search term const filteredOptions = options?.filter((option) => option.label.toLowerCase().includes(searchTerm.toLowerCase()) ) || []; // Calculate dropdown position const calculateDropdownPosition = useCallback(() => { if (!selectRef.current) return 'down'; const rect = selectRef.current.getBoundingClientRect(); const spaceBelow = window.innerHeight - rect.bottom; const spaceAbove = rect.top; return spaceBelow < 200 && spaceAbove > spaceBelow ? 'up' : 'down'; }, []); // Close dropdown on outside click or scroll useEffect(() => { if (!isOpen) return; const handler = (e) => { if ( !dropdownRef.current?.contains(e.target) && !selectRef.current?.contains(e.target) ) { setIsOpen(false); // Reset search term when closing dropdown setSearchTerm(''); } }; const scrollHandler = (e) => { if ( dropdownRef.current && !dropdownRef.current.contains(e.target) && selectRef.current && !selectRef.current.contains(e.target) ) { setIsOpen(false); setSearchTerm(''); } }; document.addEventListener('mousedown', handler); window.addEventListener('scroll', scrollHandler, true); return () => { document.removeEventListener('mousedown', handler); window.removeEventListener('scroll', scrollHandler, true); }; }, [isOpen]); // Focus search input when dropdown opens useEffect(() => { if (isOpen && searchInputRef.current) { searchInputRef.current.focus(); } }, [isOpen]); // Update dropdown direction and position useEffect(() => { if (isOpen && selectRef.current) { const rect = selectRef.current.getBoundingClientRect(); const spaceBelow = window.innerHeight - rect.bottom; const spaceAbove = rect.top; const direction = spaceBelow < 200 && spaceAbove > spaceBelow ? 'up' : 'down'; setDropdownDirection(direction); setDropdownPosition({ top: direction === 'up' ? rect.top : rect.bottom, left: rect.left, width: rect.width, }); } }, [isOpen]); // Keyboard navigation const handleKeyDown = (e) => { if ( !isOpen && ['ArrowUp', 'ArrowDown', 'ArrowLeft', 'ArrowRight'].includes(e.key) ) { e.preventDefault(); onKeyDown(e); return; } if (isOpen && ['ArrowLeft', 'ArrowRight'].includes(e.key)) { e.preventDefault(); return; } if (e.key === 'Enter') { e.preventDefault(); if (isOpen && filteredOptions.length > 0) { // Select the first option when pressing Enter handleSelectChange(filteredOptions[0].value); } else { setIsOpen((v) => !v); } return; } if (e.key === 'Escape') { e.preventDefault(); setIsOpen(false); setSearchTerm(''); return; } // Allow typing to search when dropdown is open if (isOpen && e.key.length === 1 && !e.ctrlKey && !e.metaKey) { setSearchTerm((prev) => prev + e.key); e.preventDefault(); return; } onKeyDown(e); }; const handleSelectChange = (v) => { onChange({ target: { value: v } }); setIsOpen(false); setSearchTerm(''); // Clear search term after selection }; const handleSearchChange = (e) => { setSearchTerm(e.target.value); }; const clearSearch = () => { setSearchTerm(''); if (searchInputRef.current) { searchInputRef.current.focus(); } }; const selectedOption = options?.find((opt) => opt.value === value); const displayValue = selectedOption ? selectedOption.label : placeholder; return (
{ setIsOpen((v) => !v); onClick?.(e); }} onKeyDown={handleKeyDown} data-cell={dataCell} tabIndex={0} > {isOpen ? ( e.stopPropagation()} placeholder={selectedOption?.label || placeholder} /> ) : ( displayValue )} {isOpen ? : }
{isOpen && createPortal(
{filteredOptions.length === 0 ? (
{searchTerm ? 'No matches found' : 'No data'}
) : ( filteredOptions.map((option) => (
handleSelectChange(option.value)} > {option.label}
)) )}
, document.body )}
); } ); export default CellSelect;