AvanzaCast/shared/components/LanguageSelector.tsx
Cesar Mendivil a501d398ff chore: update tailwindcss to version 4.1.0 across admin, broadcast, landing, and studio panels
refactor: remove unnecessary React import in AuthButton component due to new JSX transform

refactor: simplify imports in LanguageSelector component

fix: remove unused AuthResponse type import in useAuth hook

fix: access Vite environment variables in a type-safe manner in api utility
2025-11-09 19:02:50 -07:00

116 lines
3.8 KiB
TypeScript

import { useState } from 'react';
import { useLanguage } from '@avanzacast/shared-hooks';
import type { SupportedLanguage } from '@avanzacast/shared-types';
interface LanguageSelectorProps {
className?: string;
variant?: 'dropdown' | 'flags';
position?: 'left' | 'right';
}
/**
* Componente compartido para selector de idioma
* Puede ser usado en landing-page, broadcast-studio y admin-panel
*/
export function LanguageSelector({
className = '',
variant = 'dropdown',
position = 'right'
}: LanguageSelectorProps) {
const { language, availableLanguages, setLanguage } = useLanguage();
const [isOpen, setIsOpen] = useState(false);
const currentLanguage = availableLanguages.find(lang => lang.code === language);
const handleLanguageChange = (code: SupportedLanguage) => {
setLanguage(code);
setIsOpen(false);
};
if (variant === 'flags') {
return (
<div className={`flex items-center gap-2 ${className}`}>
{availableLanguages.map((lang) => (
<button
key={lang.code}
onClick={() => handleLanguageChange(lang.code)}
className={`text-2xl transition-opacity ${
language === lang.code ? 'opacity-100' : 'opacity-40 hover:opacity-70'
}`}
title={lang.name}
aria-label={`Switch to ${lang.name}`}
>
{lang.flag}
</button>
))}
</div>
);
}
return (
<div className={`relative ${className}`}>
<button
onClick={() => setIsOpen(!isOpen)}
className="flex items-center gap-2 px-3 py-2 text-sm font-medium text-gray-700 hover:text-gray-900 transition-colors"
aria-expanded={isOpen}
aria-haspopup="true"
>
<span className="text-xl">{currentLanguage?.flag}</span>
<span>{currentLanguage?.code.toUpperCase()}</span>
<svg
className={`w-4 h-4 transition-transform ${isOpen ? 'rotate-180' : ''}`}
fill="none"
stroke="currentColor"
viewBox="0 0 24 24"
>
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M19 9l-7 7-7-7" />
</svg>
</button>
{isOpen && (
<>
{/* Overlay para cerrar */}
<div
className="fixed inset-0 z-10"
onClick={() => setIsOpen(false)}
aria-hidden="true"
/>
{/* Dropdown menu */}
<div
className={`absolute z-20 mt-2 w-48 bg-white rounded-lg shadow-lg border border-gray-200 py-1 ${
position === 'right' ? 'right-0' : 'left-0'
}`}
>
{availableLanguages.map((lang) => (
<button
key={lang.code}
onClick={() => handleLanguageChange(lang.code)}
className={`w-full flex items-center gap-3 px-4 py-2 text-sm text-left hover:bg-gray-50 transition-colors ${
language === lang.code ? 'bg-blue-50 text-blue-600' : 'text-gray-700'
}`}
>
<span className="text-xl">{lang.flag}</span>
<span className="font-medium">{lang.name}</span>
{language === lang.code && (
<svg
className="w-4 h-4 ml-auto text-blue-600"
fill="currentColor"
viewBox="0 0 20 20"
>
<path
fillRule="evenodd"
d="M16.707 5.293a1 1 0 010 1.414l-8 8a1 1 0 01-1.414 0l-4-4a1 1 0 011.414-1.414L8 12.586l7.293-7.293a1 1 0 011.414 0z"
clipRule="evenodd"
/>
</svg>
)}
</button>
))}
</div>
</>
)}
</div>
);
}