import { type RefObject } from "react"; import { useTranslation } from "react-i18next"; import { Slider, Toggle } from "./plexPlayerControls"; import { clockParts, type PlexPlayerPrefs } from "../lib/plexPlayerPrefs"; import { Pressable } from "./ui/Pressable"; import { ColorInput, Checkbox } from "./ui/NativeControls"; type SettingsTab = "sync" | "playback" | "subtitle" | "clock"; // The gear/tuning panel of the Plex player — a tabbed (sync / playback / subtitle / clock) sheet of // Slider/Toggle rows over the per-account prefs. Extracted verbatim from PlexPlayer to keep that // component focused on the media engine; every row is still `prefs.` + `patchPrefs`, so the // panel owns no state of its own. `now` is the live wall clock (passed in, not a second ticker) used // only for the clock-date-style preview buttons. export function PlexPlayerSettings(props: { // `| null` is not optional under React 19's types: `useRef(null)` is now `RefObject`, // which is the honest shape — the element is null until the div mounts. The old `RefObject` lied // about that and 18's types let it. menuRef: RefObject; settingsTabs: { key: SettingsTab; label: string }[]; activeTab: SettingsTab; setSettingsTab: (tab: SettingsTab) => void; prefs: PlexPlayerPrefs; patchPrefs: (patch: Partial) => void; changeAudioOffset: (v: number) => void; now: Date; }) { const { menuRef, settingsTabs, activeTab, setSettingsTab, prefs, patchPrefs, changeAudioOffset, now, } = props; const { t } = useTranslation(); return (
e.stopPropagation()} onClick={(e) => e.stopPropagation()} className="border border-white/15 bg-neutral-900/95 absolute bottom-full right-0 mb-2 w-80 rounded-xl p-2 text-sm text-white shadow-2xl" >
{settingsTabs.map((tab) => ( setSettingsTab(tab.key)} className={`flex-1 whitespace-nowrap rounded px-2 py-1 text-2xs ${ activeTab === tab.key ? "bg-white/15 text-accent" : "text-white/70 hover:bg-white/10" }`} > {tab.label} ))}
{activeTab === "sync" && ( <> patchPrefs({ subOffset: v })} onReset={() => patchPrefs({ subOffset: 0 })} /> changeAudioOffset(0)} /> )} {activeTab === "playback" && ( <> patchPrefs({ seekStep: v })} /> patchPrefs({ fineSeekStep: v })} /> patchPrefs({ barAutoHide: v })} /> patchPrefs({ autoSkipIntro: v })} /> patchPrefs({ autoSkipCredits: v })} /> patchPrefs({ upNext: v })} /> {(prefs.autoSkipIntro || prefs.autoSkipCredits) && ( patchPrefs({ autoSkipDelay: v })} /> )} )} {activeTab === "subtitle" && ( <> patchPrefs({ subSize: v })} /> patchPrefs({ subPos: v })} />
{t("plex.player.subColor")} patchPrefs({ subColor: v })} />
{t("plex.player.subBg")}
{prefs.subBg !== "transparent" && ( patchPrefs({ subBg: v })} /> )}
patchPrefs({ subShadow: v })} /> {prefs.subShadow > 0 && (
{t("plex.player.subShadowColor")} patchPrefs({ subShadowColor: v })} />
)} )} {activeTab === "clock" && ( <> patchPrefs({ clockShow: v })} /> patchPrefs({ clock24h: v })} /> patchPrefs({ clockSize: v })} />
{t("plex.player.clockColor")} patchPrefs({ clockColor: v })} />
patchPrefs({ clockDate: v })} /> {prefs.clockDate && (
{(["hu", "en"] as const).map((st) => ( patchPrefs({ clockDateStyle: st })} className={`flex-1 rounded px-2 py-1 text-2xs ${ prefs.clockDateStyle === st ? "bg-white/15 text-accent" : "text-white/70 hover:bg-white/10" }`} > {clockParts(now, { ...prefs, clockDate: true, clockDateStyle: st }).date} ))}
)} )}
); }