diff --git a/frontend/src/assets/countries/brazil.png b/frontend/src/assets/countries/brazil.png new file mode 100644 index 000000000..2bfc67437 Binary files /dev/null and b/frontend/src/assets/countries/brazil.png differ diff --git a/frontend/src/assets/countries/united-states.png b/frontend/src/assets/countries/united-states.png new file mode 100644 index 000000000..82b720f54 Binary files /dev/null and b/frontend/src/assets/countries/united-states.png differ diff --git a/frontend/src/components/LanguageSelector/index.tsx b/frontend/src/components/LanguageSelector/index.tsx new file mode 100644 index 000000000..84b417ea1 --- /dev/null +++ b/frontend/src/components/LanguageSelector/index.tsx @@ -0,0 +1,155 @@ +import { useState, useRef, useEffect } from 'react'; +import { ChevronDownIcon } from '@heroicons/react/20/solid'; +import { AnimatePresence, motion } from 'motion/react'; +import { useTranslation } from 'react-i18next'; +import { cn } from '../../helpers/cn'; +import { Language } from '../../translations/helpers'; + +// Import country flag images +import brazilFlag from '../../assets/countries/brazil.png'; +import usFlag from '../../assets/countries/united-states.png'; + +interface LanguageButtonProps { + selectedLanguage: Language; + isOpen: boolean; + onClick: () => void; + disabled?: boolean; +} + +const LanguageButton = ({ selectedLanguage, isOpen, onClick, disabled }: LanguageButtonProps) => ( + + {selectedLanguage + + + + +); + +interface LanguageDropdownProps { + isOpen: boolean; + onLanguageSelect: (language: Language) => void; + disabled?: boolean; +} + +const LanguageDropdown = ({ isOpen, onLanguageSelect, disabled }: LanguageDropdownProps) => ( + + {isOpen && !disabled && ( + + + + + )} + +); + +function useClickOutside(ref: React.RefObject, callback: () => void) { + useEffect(() => { + const handleClickOutside = (event: MouseEvent) => { + if (ref.current && !ref.current.contains(event.target as Node)) { + callback(); + } + }; + + document.addEventListener('mousedown', handleClickOutside); + return () => document.removeEventListener('mousedown', handleClickOutside); + }, [callback, ref]); +} + +// Helper function to update path with language +const updatePathWithLanguage = (path: string, language: Language): string => { + const languageValues = Object.values(Language); + + // Check if path already contains a language segment + for (const lang of languageValues) { + const langSegment = `/${lang.toLowerCase()}`; + if (path.includes(langSegment)) { + // Replace existing language segment + return path.replace(langSegment, `/${language.toLowerCase()}`); + } + } + + // No language segment found, add it at the beginning + return `/${language.toLowerCase()}${path}`; +}; + +export const LanguageSelector = ({ disabled }: { disabled?: boolean }) => { + const { i18n } = useTranslation(); + const [isOpen, setIsOpen] = useState(false); + const dropdownRef = useRef(null); + + // Get current language from i18n + const currentLanguage = i18n.language === Language.Portuguese_Brazil ? Language.Portuguese_Brazil : Language.English; + + useClickOutside(dropdownRef, () => setIsOpen(false)); + + const handleLanguageSelect = (language: Language) => { + // Get current path and replace language segment or add it + const currentPath = window.location.pathname; + const newPath = updatePathWithLanguage(currentPath, language); + + // Update URL without full page reload + window.history.pushState({}, '', newPath); + + // Update i18n language + i18n.changeLanguage(language); + + setIsOpen(false); + }; + + const wrapperProps = disabled + ? { + className: 'tooltip tooltip-primary tooltip-bottom before:whitespace-pre-wrap before:content-[attr(data-tip)]', + 'data-tip': 'Language selection is disabled.', + } + : {}; + + return ( +
+
+ setIsOpen(!isOpen)} + disabled={disabled} + /> + +
+
+ ); +}; diff --git a/frontend/src/components/Navbar/index.tsx b/frontend/src/components/Navbar/index.tsx index 8b35e2a2f..a50f802b9 100644 --- a/frontend/src/components/Navbar/index.tsx +++ b/frontend/src/components/Navbar/index.tsx @@ -4,6 +4,7 @@ import whiteLogo from '../../assets/logo/white.png'; import whiteMobileLogo from '../../assets/logo/circle.png'; import { ConnectWalletButton } from '../buttons/ConnectWalletButton'; import { NetworkSelector } from '../NetworkSelector'; +import { LanguageSelector } from '../LanguageSelector'; import { useNetwork } from '../../contexts/network'; export const Navbar = () => { @@ -27,6 +28,7 @@ export const Navbar = () => { Vortex Logo
+