The ~124 px-frozen text-[Npx] labels didn't track the Settings font-scale slider while their rem-sized neighbours did. Add text-2xs (0.6875rem) / text-3xs (0.625rem) font-size-ONLY tokens (no paired line-height -> matches the arbitrary they replace, zero vertical-rhythm shift) and codemod every text-[Npx] to a rem class (rare 8/9/12/13px -> rem arbitraries). Also: --hdr-h 68px -> 4.25rem so header content clears the rem-sized pills at scale 1.3 instead of sliding under; NavSidebar/SidePanel slot widths + their icon/badge px sizes go rem so the rails keep pace with their (now-scaling) labels; a w-24/h-[54px] thumb -> h-13.5 to preserve 16:9 at scale. Media thumbnails, page-width caps and fixed container widths deliberately stay px. Verified via getComputedStyle at scale 1.06/1.3: text-2xs 11.66->14.3px, header clearance 72->88px, nav slot 246->302px — all scale proportionally.
91 lines
3.4 KiB
TypeScript
91 lines
3.4 KiB
TypeScript
import { useState } from "react";
|
|
import { Check, Globe } from "lucide-react";
|
|
import { LANGUAGES, type LangCode } from "../i18n";
|
|
import { PopoverItem, PopoverSurface, useAnchoredPopover } from "./AnchoredPopover";
|
|
|
|
// Compact language picker (globe + current code). Presentational: the parent decides what
|
|
// changing the language does (set it; persist server-side when signed in).
|
|
//
|
|
// Two variants, now one implementation on the R8 popover platform: "header" opens below the pill
|
|
// (used on the landing page and the setup wizard) and "rail" is an icon-only button in the left
|
|
// nav's bottom cluster whose menu opens to the RIGHT, bottom-aligned. Both used to hand-roll their
|
|
// own anchoring, and the header one also opened on hover — dropped on purpose: the platform focuses
|
|
// the first item when a menu opens, so a hover-open would yank focus off whatever the user was doing.
|
|
export default function LanguageSwitcher({
|
|
value,
|
|
onChange,
|
|
align = "right",
|
|
variant = "header",
|
|
}: {
|
|
value: LangCode;
|
|
onChange: (code: LangCode) => void;
|
|
align?: "left" | "right";
|
|
variant?: "header" | "rail";
|
|
}) {
|
|
const [open, setOpen] = useState(false);
|
|
const currentIdx = Math.max(
|
|
0,
|
|
LANGUAGES.findIndex((l) => l.code === value),
|
|
);
|
|
const current = LANGUAGES.find((l) => l.code === value) ?? LANGUAGES[0];
|
|
const rail = variant === "rail";
|
|
|
|
const pop = useAnchoredPopover({
|
|
open,
|
|
onOpenChange: setOpen,
|
|
placement: rail ? "right-end" : align === "right" ? "bottom-end" : "bottom-start",
|
|
offset: rail ? 8 : 4,
|
|
// Single choice: opening lands on the language already in use.
|
|
selectedIndex: currentIdx,
|
|
});
|
|
|
|
return (
|
|
<>
|
|
<button
|
|
ref={pop.refs.setReference}
|
|
{...pop.getReferenceProps()}
|
|
title={current.label}
|
|
aria-label={current.label}
|
|
className={
|
|
rail
|
|
? "relative p-2 rounded-lg text-muted hover:text-fg hover:bg-card transition"
|
|
: "flex items-center gap-1.5 px-2.5 py-1.5 rounded-lg text-sm text-muted hover:text-fg hover:bg-card transition"
|
|
}
|
|
>
|
|
<Globe className={rail ? "w-5 h-5" : "w-4 h-4"} />
|
|
{rail ? (
|
|
/* Current-language badge (HU/EN) so the active language reads at a glance without
|
|
opening the menu. The ring matches the page background to detach it from the icon. */
|
|
<span className="absolute -bottom-1 -right-1 text-[0.5rem] font-bold leading-none px-1 py-px rounded-sm bg-accent text-accent-fg ring-2 ring-bg">
|
|
{current.code.toUpperCase()}
|
|
</span>
|
|
) : (
|
|
<span className="text-xs font-semibold uppercase">{current.code}</span>
|
|
)}
|
|
</button>
|
|
|
|
<PopoverSurface
|
|
popover={pop}
|
|
className="glass-menu w-40 rounded-xl p-1.5 animate-[popIn_0.16s_ease]"
|
|
>
|
|
{LANGUAGES.map((l) => (
|
|
<PopoverItem
|
|
key={l.code}
|
|
role="menuitemradio"
|
|
checked={l.code === value}
|
|
onSelect={() => {
|
|
onChange(l.code);
|
|
setOpen(false);
|
|
}}
|
|
label={l.label}
|
|
className="w-full flex items-center justify-between gap-2 text-sm px-2 py-2 rounded-lg text-muted outline-hidden transition hover:text-fg"
|
|
>
|
|
<span>{l.label}</span>
|
|
{l.code === value && <Check className="w-4 h-4 text-accent" />}
|
|
</PopoverItem>
|
|
))}
|
|
</PopoverSurface>
|
|
</>
|
|
);
|
|
}
|