Spaces:
Sleeping
Sleeping
File size: 4,758 Bytes
75fda81 fc91a5e 75fda81 fc91a5e 75fda81 fc91a5e 75fda81 fc91a5e 75fda81 fc91a5e 75fda81 | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 | /**
* LanguageSelector - Dropdown component for selecting UI language.
*
* Supports English, Hindi, and Gujarati.
* Persists selection to localStorage and updates i18n context.
*/
import { useState, useRef, useEffect, useCallback } from "react";
import { useTranslation } from "react-i18next";
import PropTypes from "prop-types";
import { ChevronDownIcon, LanguageIcon, CheckIcon } from "@heroicons/react/24/outline";
import { LANGUAGES, LANGUAGE_NAMES } from "@utils/constants";
const LANGUAGE_OPTIONS = [
{ code: LANGUAGES.EN, name: LANGUAGE_NAMES[LANGUAGES.EN], nativeName: "English" },
{ code: LANGUAGES.HI, name: LANGUAGE_NAMES[LANGUAGES.HI], nativeName: "Hindi" },
{ code: LANGUAGES.GU, name: LANGUAGE_NAMES[LANGUAGES.GU], nativeName: "Gujarati" },
];
function LanguageSelector({ currentLanguage, onLanguageChange, compact = false }) {
const { t } = useTranslation();
const [isOpen, setIsOpen] = useState(false);
const dropdownRef = useRef(null);
const currentOption = LANGUAGE_OPTIONS.find((opt) => opt.code === currentLanguage) || LANGUAGE_OPTIONS[0];
const handleSelect = useCallback(
(langCode) => {
onLanguageChange(langCode);
setIsOpen(false);
},
[onLanguageChange]
);
const toggleDropdown = useCallback(() => {
setIsOpen((prev) => !prev);
}, []);
useEffect(() => {
function handleClickOutside(event) {
if (dropdownRef.current && !dropdownRef.current.contains(event.target)) {
setIsOpen(false);
}
}
function handleEscape(event) {
if (event.key === "Escape") {
setIsOpen(false);
}
}
if (isOpen) {
document.addEventListener("mousedown", handleClickOutside);
document.addEventListener("keydown", handleEscape);
}
return () => {
document.removeEventListener("mousedown", handleClickOutside);
document.removeEventListener("keydown", handleEscape);
};
}, [isOpen]);
return (
<div className="relative" ref={dropdownRef}>
<button
type="button"
onClick={toggleDropdown}
className="inline-flex items-center gap-1.5 rounded-lg px-2.5 py-1.5 text-sm font-medium text-neutral-600 hover:bg-neutral-100 transition-colors focus:outline-none focus-visible:ring-2 focus-visible:ring-primary-500"
aria-expanded={isOpen}
aria-haspopup="listbox"
aria-label={t("common.selectLanguage")}
id="language-selector"
>
<LanguageIcon className="h-4 w-4" aria-hidden="true" />
{!compact && (
<>
<span className="hidden xs:inline">{currentOption.name}</span>
<ChevronDownIcon
className={`h-3.5 w-3.5 transition-transform duration-200 ${isOpen ? "rotate-180" : ""}`}
aria-hidden="true"
/>
</>
)}
{compact && <span className="uppercase text-xs">{currentOption.code}</span>}
</button>
{isOpen && (
<div
className="absolute right-0 z-50 mt-1 w-44 origin-top-right rounded-lg bg-white shadow-lg ring-1 ring-black/5 focus:outline-none animate-fade-in"
role="listbox"
aria-label={t("common.selectLanguage")}
>
<div className="py-1">
{LANGUAGE_OPTIONS.map((option) => {
const isSelected = option.code === currentLanguage;
return (
<button
key={option.code}
type="button"
onClick={() => handleSelect(option.code)}
className={`
w-full flex items-center justify-between px-3 py-2 text-sm transition-colors
${
isSelected
? "bg-primary-50 text-primary-700"
: "text-neutral-700 hover:bg-neutral-50"
}
`}
role="option"
aria-selected={isSelected}
>
<span className="flex flex-col items-start">
<span className="font-medium">{option.name}</span>
<span className="text-xs text-neutral-500">
{option.nativeName}
</span>
</span>
{isSelected && (
<CheckIcon className="h-4 w-4 text-primary-600" aria-hidden="true" />
)}
</button>
);
})}
</div>
</div>
)}
</div>
);
}
LanguageSelector.propTypes = {
currentLanguage: PropTypes.oneOf([LANGUAGES.EN, LANGUAGES.HI, LANGUAGES.GU]).isRequired,
onLanguageChange: PropTypes.func.isRequired,
compact: PropTypes.bool,
};
export default LanguageSelector;
|