116 lines
3.8 KiB
TypeScript
116 lines
3.8 KiB
TypeScript
import React, { 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>
|
|
);
|
|
}
|