import React, { useState, useEffect, useRef } from 'react' import { CheckIcon, XMarkIcon } from '@heroicons/react/24/outline' import { LanguageInfo } from '../../services/api' import { useTheme } from '@mui/material/styles' import { Box, Typography, Chip, Button, Paper, Divider, Popper, ClickAwayListener } from '@mui/material' interface LanguageSelectorProps { selectedLanguages: string[] primaryLanguage?: string onLanguagesChange: (languages: string[], primary?: string) => void maxLanguages?: number disabled?: boolean showPrimarySelector?: boolean className?: string } // Common languages with display names const COMMON_LANGUAGES: LanguageInfo[] = [ { code: 'eng', name: 'English', installed: true }, { code: 'spa', name: 'Spanish', installed: true }, { code: 'fra', name: 'French', installed: true }, { code: 'deu', name: 'German', installed: true }, { code: 'ita', name: 'Italian', installed: true }, { code: 'por', name: 'Portuguese', installed: true }, { code: 'rus', name: 'Russian', installed: true }, { code: 'chi_sim', name: 'Chinese (Simplified)', installed: true }, { code: 'chi_tra', name: 'Chinese (Traditional)', installed: true }, { code: 'jpn', name: 'Japanese', installed: true }, { code: 'kor', name: 'Korean', installed: true }, { code: 'ara', name: 'Arabic', installed: true }, { code: 'hin', name: 'Hindi', installed: true }, { code: 'nld', name: 'Dutch', installed: true }, { code: 'swe', name: 'Swedish', installed: true }, { code: 'nor', name: 'Norwegian', installed: true }, { code: 'dan', name: 'Danish', installed: true }, { code: 'fin', name: 'Finnish', installed: true }, { code: 'pol', name: 'Polish', installed: true }, { code: 'ces', name: 'Czech', installed: true }, { code: 'hun', name: 'Hungarian', installed: true }, { code: 'tur', name: 'Turkish', installed: true }, { code: 'tha', name: 'Thai', installed: true }, { code: 'vie', name: 'Vietnamese', installed: true }, ] function LanguageSelector({ selectedLanguages, primaryLanguage, onLanguagesChange, maxLanguages = 4, disabled = false, showPrimarySelector = true, className = '', }: LanguageSelectorProps) { const theme = useTheme() const [availableLanguages, setAvailableLanguages] = useState(COMMON_LANGUAGES) const [isOpen, setIsOpen] = useState(false) const anchorRef = useRef(null) // Auto-set primary language to first selected if not specified const effectivePrimary = primaryLanguage || selectedLanguages[0] || '' const handleLanguageToggle = (languageCode: string) => { if (disabled) return let newLanguages: string[] let newPrimary = effectivePrimary if (selectedLanguages.includes(languageCode)) { // Remove language newLanguages = selectedLanguages.filter(lang => lang !== languageCode) // If removing the primary language, set new primary to first remaining language if (languageCode === effectivePrimary && newLanguages.length > 0) { newPrimary = newLanguages[0] } else if (newLanguages.length === 0) { newPrimary = '' } } else { // Add language (check max limit) if (selectedLanguages.length >= maxLanguages) { return } newLanguages = [...selectedLanguages, languageCode] // If this is the first language, make it primary if (newLanguages.length === 1) { newPrimary = languageCode } } onLanguagesChange(newLanguages, newPrimary) } const handlePrimaryChange = (languageCode: string) => { if (disabled || !selectedLanguages.includes(languageCode)) return onLanguagesChange(selectedLanguages, languageCode) } const removeLanguage = (languageCode: string) => { handleLanguageToggle(languageCode) } const handleClose = () => { setIsOpen(false) } const getLanguageName = (code: string) => { const language = availableLanguages.find(lang => lang.code === code) return language?.name || code } return ( {/* Selected Languages Display */} OCR Languages {selectedLanguages.length > 0 && `(${selectedLanguages.length}/${maxLanguages})`} {selectedLanguages.length > 0 ? ( {selectedLanguages.map((langCode) => ( {getLanguageName(langCode)} {langCode === effectivePrimary && ( (Primary) )} } variant={langCode === effectivePrimary ? 'filled' : 'outlined'} color={langCode === effectivePrimary ? 'primary' : 'default'} size="small" onDelete={!disabled ? () => removeLanguage(langCode) : undefined} deleteIcon={} sx={{ '& .MuiChip-deleteIcon': { color: 'text.secondary', '&:hover': { color: 'text.primary', }, }, }} /> ))} ) : ( No languages selected. Documents will use default OCR language. )} {/* Language Selector Button */} {!disabled && ( )} {/* Dropdown Panel */} Available Languages {availableLanguages .filter(lang => lang.installed) .map((language) => { const isSelected = selectedLanguages.includes(language.code) const isPrimary = language.code === effectivePrimary const canSelect = !isSelected && selectedLanguages.length < maxLanguages return ( theme.palette.mode === 'dark' ? 'rgba(144, 202, 249, 0.16)' : 'rgba(25, 118, 210, 0.08)' : 'transparent', cursor: canSelect || isSelected ? 'pointer' : 'not-allowed', opacity: !canSelect && !isSelected ? 0.5 : 1, transition: 'all 0.2s ease-in-out', '&:hover': canSelect ? { backgroundColor: (theme) => theme.palette.mode === 'dark' ? 'rgba(255, 255, 255, 0.05)' : 'rgba(0, 0, 0, 0.04)', transform: 'translateY(-1px)', } : {}, }} > canSelect || isSelected ? handleLanguageToggle(language.code) : undefined} > {isSelected && ( )} {language.name} {isPrimary && ( )} {/* Primary selector */} {isSelected && showPrimarySelector && selectedLanguages.length > 1 && ( )} ) })} {selectedLanguages.length >= maxLanguages && ( theme.palette.mode === 'dark' ? 'rgba(255, 193, 7, 0.1)' : 'rgba(255, 193, 7, 0.08)', border: '1px solid', borderColor: (theme) => theme.palette.mode === 'dark' ? 'rgba(255, 193, 7, 0.3)' : 'rgba(255, 193, 7, 0.3)', borderRadius: 2, }}> theme.palette.mode === 'dark' ? '#ffb74d' : '#e65100', fontWeight: 500, }}> Maximum {maxLanguages} languages allowed for optimal performance. )} {/* Help Text */} {selectedLanguages.length > 1 && ( Primary language is processed first for better accuracy. Multiple languages help with mixed-language documents. )} ) } export default LanguageSelector