164 lines
4.0 KiB
TypeScript
164 lines
4.0 KiB
TypeScript
"use client";
|
|
|
|
import { useSyncExternalStore } from "react";
|
|
|
|
import { ToggleGroup, ToggleGroupItem } from "@/components/ui/toggle-group";
|
|
|
|
export type ExportLanguage = "th" | "en";
|
|
|
|
const LANGUAGE_OPTIONS = {
|
|
th: { label: "ไทย" },
|
|
en: { label: "English" },
|
|
} satisfies Record<ExportLanguage, { label: string }>;
|
|
|
|
const STORAGE_KEY = "buzz-sheet:export-language";
|
|
const LANGUAGE_EVENT = "buzz-sheet:export-language-change";
|
|
let memoryLanguage: ExportLanguage = "th";
|
|
|
|
function storedLanguage(): ExportLanguage {
|
|
if (typeof window === "undefined") return "th";
|
|
try {
|
|
memoryLanguage = window.localStorage.getItem(STORAGE_KEY) === "en" ? "en" : "th";
|
|
return memoryLanguage;
|
|
} catch {
|
|
return memoryLanguage;
|
|
}
|
|
}
|
|
|
|
export function useExportLanguage() {
|
|
const language = useSyncExternalStore<ExportLanguage>(
|
|
(onStoreChange) => {
|
|
window.addEventListener(LANGUAGE_EVENT, onStoreChange);
|
|
window.addEventListener("storage", onStoreChange);
|
|
return () => {
|
|
window.removeEventListener(LANGUAGE_EVENT, onStoreChange);
|
|
window.removeEventListener("storage", onStoreChange);
|
|
};
|
|
},
|
|
storedLanguage,
|
|
() => "th",
|
|
);
|
|
|
|
function setLanguage(next: ExportLanguage) {
|
|
memoryLanguage = next;
|
|
try {
|
|
window.localStorage.setItem(STORAGE_KEY, next);
|
|
} catch {
|
|
// Browser storage is optional; the current export still uses the selection.
|
|
} finally {
|
|
window.dispatchEvent(new CustomEvent(LANGUAGE_EVENT, { detail: next }));
|
|
}
|
|
}
|
|
|
|
return [language, setLanguage] as const;
|
|
}
|
|
|
|
export function ExportLanguageToggle({
|
|
language,
|
|
onChange,
|
|
}: {
|
|
language: ExportLanguage;
|
|
onChange: (language: ExportLanguage) => void;
|
|
}) {
|
|
return (
|
|
<ToggleGroup
|
|
aria-label="ภาษาในภาพส่งออก"
|
|
variant="outline"
|
|
size="sm"
|
|
value={[language]}
|
|
onValueChange={(values) => {
|
|
const next = values[0];
|
|
if (next === "th" || next === "en") onChange(next);
|
|
}}
|
|
>
|
|
<ToggleGroupItem
|
|
value="th"
|
|
>
|
|
<ExportLanguageOption language="th" />
|
|
</ToggleGroupItem>
|
|
<ToggleGroupItem
|
|
value="en"
|
|
>
|
|
<ExportLanguageOption language="en" />
|
|
</ToggleGroupItem>
|
|
</ToggleGroup>
|
|
);
|
|
}
|
|
|
|
export function ExportLanguageOption({
|
|
language,
|
|
}: {
|
|
language: ExportLanguage;
|
|
}) {
|
|
const option = LANGUAGE_OPTIONS[language];
|
|
|
|
return (
|
|
<>
|
|
<LanguageFlag language={language} />
|
|
{option.label}
|
|
</>
|
|
);
|
|
}
|
|
|
|
function LanguageFlag({ language }: { language: ExportLanguage }) {
|
|
if (language === "th") {
|
|
return (
|
|
<svg
|
|
data-icon="inline-start"
|
|
data-language-flag="th"
|
|
aria-hidden="true"
|
|
viewBox="0 0 9 6"
|
|
width="24"
|
|
height="16"
|
|
>
|
|
<rect width="9" height="6" fill="#a51931" />
|
|
<rect y="1" width="9" height="4" fill="#f4f5f8" />
|
|
<rect y="2" width="9" height="2" fill="#2d2a4a" />
|
|
</svg>
|
|
);
|
|
}
|
|
|
|
return (
|
|
<svg
|
|
data-icon="inline-start"
|
|
data-language-flag="en"
|
|
aria-hidden="true"
|
|
viewBox="0 0 19 10"
|
|
width="24"
|
|
height="16"
|
|
>
|
|
<rect width="19" height="10" fill="#fff" />
|
|
{Array.from({ length: 7 }, (_, index) => (
|
|
<rect
|
|
key={index}
|
|
y={index * 20 / 13}
|
|
width="19"
|
|
height={10 / 13}
|
|
fill="#b22234"
|
|
/>
|
|
))}
|
|
<rect width="7.6" height={70 / 13} fill="#3c3b6e" />
|
|
{Array.from({ length: 9 }, (_, row) =>
|
|
Array.from({ length: row % 2 === 0 ? 6 : 5 }, (_, column) => (
|
|
<circle
|
|
key={`${row}-${column}`}
|
|
cx={0.65 + column * 1.25 + (row % 2 === 0 ? 0 : 0.625)}
|
|
cy={0.35 + row * 0.58}
|
|
r="0.12"
|
|
fill="#fff"
|
|
/>
|
|
)),
|
|
)}
|
|
</svg>
|
|
);
|
|
}
|
|
|
|
export function ExportText({ th, en }: { th: string; en: string }) {
|
|
return (
|
|
<>
|
|
<span data-export-lang="th">{th}</span>
|
|
<span data-export-lang="en">{en}</span>
|
|
</>
|
|
);
|
|
}
|