import { type ReactNode } from "react"; import { useTranslation } from "react-i18next"; import { RotateCcw } from "lucide-react"; import { IconButton } from "./ui/IconButton"; import { Slider as Range, CheckRow } from "./ui/NativeControls"; import { formatDecimal, formatUnit } from "../lib/format"; // --- settings-panel building blocks ------------------------------------------------------------ export function PanelHead({ children }: { children: ReactNode }) { return (
{children}
); } export function Slider({ label, value, min, max, step, suffix, seconds, onChange, onReset, }: { label: string; value: number; min: number; max: number; step: number; /** A symbol after the number ("%", "px"). */ suffix?: string; /** The value is seconds: named in the UI language's unit ("0.5 sec" / "0,5 mp"). */ seconds?: boolean; onChange: (v: number) => void; onReset?: () => void; }) { const { t, i18n } = useTranslation(); const lang = i18n.language; const shown = seconds ? formatUnit(value, "second", lang) : `${formatDecimal(value, lang, 1, 0)}${suffix ?? ""}`; return (
{label} {shown} {onReset && ( )}
onChange(v)} />
); } export function Toggle({ label, checked, onChange, }: { label: string; checked: boolean; onChange: (v: boolean) => void; }) { // The whole row toggles and grows to 44 on touch (CheckRow); the box sits at the row's end. return ( {label} ); } // A control button with a tooltip that opens ABOVE it (so it never clips off the bottom of the // viewport, unlike a native title tooltip on the bottom control bar). export function Ctrl({ label, onClick, disabled, align = "center", children, }: { label: string; onClick: () => void; disabled?: boolean; // Tooltip horizontal anchor: "start"/"end" for edge buttons so it doesn't clip off the viewport. align?: "start" | "center" | "end"; children: ReactNode; }) { const pos = align === "start" ? "left-0" : align === "end" ? "right-0" : "left-1/2 -translate-x-1/2"; // `will-change-[opacity]` keeps each tooltip on its own compositor layer from the start. Without it // the browser promotes a tooltip only while its fade runs, and inside the player's scale(1.25) // the neighbouring glyphs got re-layered with it: every button in the row shimmered as the // pointer crossed one (seen by the owner). return (
{children} {label}
); }