218 lines
11 KiB
TypeScript
218 lines
11 KiB
TypeScript
import { useEffect, useMemo, useRef, useState, type ReactElement } from "react";
|
|
import { observer } from "mobx-react-lite";
|
|
import { languageStore } from "@shared";
|
|
|
|
const LANGUAGES = ["ru", "zh", "en"] as const;
|
|
type Language = (typeof LANGUAGES)[number];
|
|
|
|
type LanguageSelectorProps = {
|
|
onBack?: () => void;
|
|
isSidebarOpen?: boolean;
|
|
};
|
|
|
|
const renderLanguageIcon = (lang: Language): ReactElement => {
|
|
switch (lang) {
|
|
case "ru":
|
|
return (
|
|
<svg
|
|
className="h-12 w-12 cursor-pointer"
|
|
viewBox="0 0 48 48"
|
|
fill="none"
|
|
xmlns="http://www.w3.org/2000/svg"
|
|
>
|
|
<rect x="3" y="3" width="42" height="42" rx="21" fill="black" />
|
|
<path
|
|
d="M24 0C10.75 0 0 10.75 0 24C0 37.25 10.75 48 24 48C37.25 48 48 37.25 48 24C48 10.75 37.25 0 24 0ZM24.2 33.55H19.92L16.29 26.46H13.11V33.55H9.12V14.18H16.32C18.61 14.18 20.37 14.69 21.62 15.71C22.87 16.73 23.48 18.17 23.48 20.03C23.48 21.35 23.19 22.45 22.62 23.34C22.05 24.22 21.18 24.93 20.02 25.45L24.21 33.37V33.56L24.2 33.55ZM40.3 26.94C40.3 29.06 39.64 30.74 38.31 31.97C36.98 33.2 35.17 33.82 32.87 33.82C30.57 33.82 28.81 33.22 27.48 32.02C26.15 30.82 25.47 29.18 25.44 27.08V14.18H29.43V26.97C29.43 28.24 29.73 29.16 30.34 29.74C30.95 30.32 31.79 30.61 32.86 30.61C35.1 30.61 36.24 29.43 36.28 27.07V14.18H40.28V26.94H40.3Z"
|
|
fill="white"
|
|
/>
|
|
<path
|
|
d="M16.3086 17.4099H13.0986V23.2199H16.3186C17.3186 23.2199 18.0986 22.9599 18.6486 22.4499C19.1986 21.9399 19.4686 21.2399 19.4686 20.3399C19.4686 19.4399 19.2086 18.7099 18.6886 18.1799C18.1686 17.6499 17.3686 17.3899 16.2986 17.3899L16.3086 17.4099Z"
|
|
fill="white"
|
|
/>
|
|
</svg>
|
|
);
|
|
case "zh":
|
|
return (
|
|
<svg
|
|
className="h-12 w-12 cursor-pointer"
|
|
viewBox="0 0 48 48"
|
|
fill="none"
|
|
xmlns="http://www.w3.org/2000/svg"
|
|
>
|
|
<rect x="3" y="3" width="42" height="42" rx="21" fill="black" />
|
|
<path d="M10.287 20.382H6.291V24.147H10.287V20.382Z" fill={"white"} />
|
|
<path
|
|
d="M13.704 24.147H17.721V20.382H13.704V24.147Z"
|
|
fill={"white"}
|
|
/>
|
|
<path
|
|
d="M36.1254 20.046H29.8575C30.6606 21.9406 31.7187 23.6442 33.0513 25.1217C34.3105 23.6927 35.3315 22.0126 36.1254 20.046Z"
|
|
fill={"white"}
|
|
/>
|
|
<path
|
|
fillRule="evenodd"
|
|
clipRule="evenodd"
|
|
d="M24 48C37.2548 48 48 37.2548 48 24C48 10.7452 37.2548 0 24 0C10.7452 0 0 10.7452 0 24C0 37.2548 10.7452 48 24 48ZM10.287 13.5H13.704V17.1541H21.117V28.446H17.721V27.375H13.704V33.969H10.287V27.375H6.291V28.5511H3V17.1541H10.287V13.5ZM31.35 13.5H34.704V16.8181H43.083V20.046H39.8887C38.804 22.9506 37.3746 25.3834 35.581 27.4065C37.6488 28.9237 40.1651 30.0542 43.1682 30.7291L43.8469 30.8817L43.3465 31.3649C42.7753 31.9162 41.9777 33.0771 41.5886 33.7939L41.4484 34.0521L41.1642 33.9778C37.8385 33.1088 35.1249 31.7521 32.8974 29.9253C30.6296 31.6954 27.9389 33.0335 24.802 34.015L24.4889 34.1129L24.3502 33.8156C24.0724 33.2203 23.2933 32.029 22.8051 31.439L22.4307 30.9868L22.9986 30.8373C25.936 30.0648 28.4025 28.9702 30.4373 27.4935C28.6775 25.4061 27.319 22.9142 26.2412 20.046H23.097V16.8181H31.35V13.5Z"
|
|
fill={"white"}
|
|
/>
|
|
</svg>
|
|
);
|
|
case "en":
|
|
default:
|
|
return (
|
|
<svg
|
|
className="h-12 w-12 cursor-pointer"
|
|
viewBox="0 0 48 48"
|
|
fill="none"
|
|
xmlns="http://www.w3.org/2000/svg"
|
|
>
|
|
<rect x="3" y="3" width="42" height="42" rx="21" fill="black" />
|
|
<path
|
|
d="M24 0C10.75 0 0 10.75 0 24C0 37.25 10.75 48 24 48C37.25 48 48 37.25 48 24C48 10.75 37.25 0 24 0ZM21.57 33.79H8.41V14.15H21.55V17.43H12.45V22.11H20.22V25.28H12.45V30.54H21.57V33.79ZM39.54 33.79H35.49L27.61 20.87V33.79H23.56V14.15H27.61L35.5 27.1V14.15H39.53V33.79H39.54Z"
|
|
fill="white"
|
|
/>
|
|
</svg>
|
|
);
|
|
}
|
|
};
|
|
|
|
const CollapsedIcon = () => (
|
|
<svg
|
|
className="h-12 w-12 cursor-pointer"
|
|
viewBox="0 0 48 48"
|
|
fill="none"
|
|
xmlns="http://www.w3.org/2000/svg"
|
|
>
|
|
<rect x="4" y="3" width="39" height="42" rx="19.5" fill="black" />
|
|
<path
|
|
fillRule="evenodd"
|
|
clipRule="evenodd"
|
|
d="M0 24C0 10.75 10.75 0 24 0C37.25 0 48 10.75 48 24C48 37.25 37.25 48 24 48C10.75 48 0 37.25 0 24ZM36.05 19.41L38.27 25.47L40.47 19.39L39.17 19.77C39.16 19.75 39.16 19.35 39.16 19.32C38.58 12.41 32.99 7.15 25.94 7.15C25.42 7.15 24.99 7.58 24.99 8.1C24.99 8.62 25.42 9.05 25.94 9.05C31.92 9.05 36.68 13.47 37.26 19.31C37.26 19.325 37.2625 19.435 37.265 19.545C37.2675 19.655 37.27 19.765 37.27 19.78L36.05 19.41ZM8.90375 27.5369C11.3535 26.9568 13.6332 25.8438 15.5701 24.2838L15.5709 24.2846C17.2124 25.8526 19.2497 26.9784 21.4812 27.5521C21.5875 27.5649 21.6945 27.5649 21.8007 27.5521C22.3194 27.6074 22.8298 27.3911 23.1385 26.9848C23.448 26.5786 23.5086 26.0441 23.2986 25.5826C23.0879 25.1211 22.6389 24.803 22.1202 24.7477C20.4804 24.3182 18.9808 23.4937 17.7634 22.3495C20.2489 20.0131 22.1036 17.1245 23.1659 13.9363C23.2729 13.5077 23.165 13.0566 22.8754 12.716C22.6016 12.3627 22.1709 12.1552 21.7136 12.1552H16.6307V10.402C16.6307 9.90121 16.3535 9.43889 15.9045 9.1881C15.4556 8.9373 14.9012 8.9373 14.4522 9.1881C14.0033 9.43809 13.7261 9.90121 13.7261 10.402V12.1824H8.64317C8.12367 12.1824 7.64484 12.45 7.38509 12.8835C7.12534 13.317 7.12534 13.8522 7.38509 14.2857C7.64484 14.7192 8.1245 14.9868 8.64317 14.9868H19.6655C18.6804 16.9971 17.3443 18.828 15.7153 20.3993C14.8373 19.3977 14.0688 18.3128 13.4207 17.1598C13.2747 16.7896 12.9734 16.4972 12.5909 16.3529C12.2083 16.2087 11.7809 16.2279 11.4141 16.405C11.0473 16.5821 10.7751 16.901 10.6647 17.2824C10.5544 17.6638 10.6166 18.0724 10.8357 18.4074C11.6058 19.7423 12.5153 20.997 13.551 22.1516C12.0207 23.3728 10.2307 24.2533 8.30873 24.7317C7.92284 24.7709 7.56932 24.956 7.32617 25.2469C7.08219 25.5369 6.96767 25.9095 7.00833 26.2813C7.04899 26.6539 7.24069 26.9952 7.54193 27.23C7.84235 27.4656 8.22824 27.5761 8.61329 27.5369C8.70956 27.5513 8.80748 27.5513 8.90375 27.5369ZM34.9002 38.8803C35.2512 39.0301 35.6487 39.0397 36.0072 38.9067C36.3815 38.7865 36.6886 38.5237 36.8587 38.18C37.0288 37.8362 37.0462 37.4404 36.9077 37.0839L30.3434 20.7767C30.238 20.5131 30.0529 20.2863 29.8115 20.1261C29.57 19.9658 29.2845 19.8793 28.9924 19.8785C28.7019 19.8785 28.4173 19.9626 28.1766 20.1196C27.9359 20.2775 27.7501 20.501 27.6422 20.7623L21.136 36.523C20.9443 36.9885 21.024 37.5181 21.346 37.9116C21.668 38.305 22.1825 38.5029 22.6962 38.4308C23.2107 38.3579 23.6455 38.0269 23.8372 37.5606L25.4057 33.6489H32.3185L34.1342 38.1079C34.2737 38.4524 34.5492 38.7304 34.9002 38.8803ZM28.9052 25.1652L31.1857 30.8445H31.1849H26.5667L28.9052 25.1652Z"
|
|
fill="#FFFFFF"
|
|
/>
|
|
</svg>
|
|
);
|
|
|
|
const ArrowIcon = ({ rotation }: { rotation: number }) => (
|
|
<svg
|
|
style={{
|
|
transform: `rotate(${rotation}deg)`,
|
|
transition: "transform 0.15s ease",
|
|
}}
|
|
className="h-12 w-12"
|
|
viewBox="0 0 48 48"
|
|
fill="none"
|
|
xmlns="http://www.w3.org/2000/svg"
|
|
>
|
|
<rect x="8" y="7" width="31" height="33" fill="black" />
|
|
<path
|
|
d="M24.0001 0C10.7501 0 0.00012207 10.75 0.00012207 24C0.00012207 37.25 10.7501 48 24.0001 48C37.2501 48 48.0001 37.25 48.0001 24C48.0001 10.75 37.2501 0 24.0001 0ZM37.5401 25.84C37.5401 26.4 37.0901 26.85 36.5301 26.85H20.5901C20.1401 26.85 19.9201 27.39 20.2301 27.71L27.6801 35.16C28.0801 35.56 28.0801 36.2 27.6801 36.59L25.0801 39.19C24.6801 39.59 24.0401 39.59 23.6501 39.19L12.4901 28.03L9.17012 24.71C8.77012 24.31 8.77012 23.67 9.17012 23.28L12.4901 19.96L23.6501 8.8C24.0501 8.4 24.6901 8.4 25.0801 8.8L27.6801 11.4C28.0801 11.8 28.0801 12.44 27.6801 12.83L20.2301 20.28C19.9101 20.6 20.1401 21.14 20.5901 21.14H36.5301C37.0901 21.14 37.5401 21.59 37.5401 22.15V25.82V25.84Z"
|
|
fill="white"
|
|
/>
|
|
</svg>
|
|
);
|
|
|
|
const LanguageSelector = observer(
|
|
({ onBack, isSidebarOpen = true }: LanguageSelectorProps) => {
|
|
const { language, setLanguage } = languageStore;
|
|
const [isOpen, setIsOpen] = useState(false);
|
|
const containerRef = useRef<HTMLDivElement | null>(null);
|
|
|
|
useEffect(() => {
|
|
if (!isOpen) {
|
|
return;
|
|
}
|
|
|
|
const handleOutside = (event: PointerEvent) => {
|
|
if (
|
|
containerRef.current &&
|
|
!containerRef.current.contains(event.target as Node)
|
|
) {
|
|
setIsOpen(false);
|
|
}
|
|
};
|
|
|
|
document.addEventListener("pointerdown", handleOutside);
|
|
return () => {
|
|
document.removeEventListener("pointerdown", handleOutside);
|
|
};
|
|
}, [isOpen]);
|
|
|
|
const handleSelect = (code: Language) => {
|
|
setLanguage(code);
|
|
setIsOpen(false);
|
|
};
|
|
|
|
const toggle = () => setIsOpen((prev) => !prev);
|
|
|
|
const containerWidth = useMemo(() => {
|
|
const BUTTON_SIZE = 56;
|
|
const GAP = 8;
|
|
const backWidth = onBack ? BUTTON_SIZE + GAP : 0;
|
|
const toggleWidth = BUTTON_SIZE;
|
|
const collapsedWidth = backWidth + toggleWidth + BUTTON_SIZE;
|
|
const expandedWidth =
|
|
backWidth +
|
|
toggleWidth +
|
|
LANGUAGES.length * BUTTON_SIZE +
|
|
(LANGUAGES.length - 1) * GAP;
|
|
return isOpen ? expandedWidth : collapsedWidth;
|
|
}, [isOpen, onBack]);
|
|
|
|
return (
|
|
<div
|
|
ref={containerRef}
|
|
className="pointer-events-auto"
|
|
style={{
|
|
width: "500px",
|
|
transition: "width 0.25s ease",
|
|
flex: "0 0 auto",
|
|
}}
|
|
>
|
|
<div className="flex items-center gap-3">
|
|
<div className="flex items-center gap-2 ">
|
|
<button
|
|
type="button"
|
|
onClick={(event) => {
|
|
event.stopPropagation();
|
|
onBack?.();
|
|
}}
|
|
className="flex h-12 w-12 items-center justify-center"
|
|
aria-label={
|
|
isOpen ? "Скрыть выбор языка" : "Показать выбор языка"
|
|
}
|
|
>
|
|
<ArrowIcon rotation={isSidebarOpen ? 0 : 180} />
|
|
</button>
|
|
{isOpen ? (
|
|
LANGUAGES.map((lang) => (
|
|
<button
|
|
key={lang}
|
|
type="button"
|
|
onClick={(event) => {
|
|
event.stopPropagation();
|
|
handleSelect(lang);
|
|
}}
|
|
className="flex h-12 w-12 items-center justify-center"
|
|
aria-label={`Переключить язык на ${lang.toUpperCase()}`}
|
|
>
|
|
{renderLanguageIcon(lang)}
|
|
</button>
|
|
))
|
|
) : (
|
|
<div
|
|
className="flex h-12 w-12 items-center justify-center"
|
|
onClick={toggle}
|
|
>
|
|
<CollapsedIcon />
|
|
</div>
|
|
)}
|
|
</div>
|
|
</div>
|
|
</div>
|
|
);
|
|
}
|
|
);
|
|
|
|
export default LanguageSelector;
|