2026-03-31 18:52:37 +08:00
|
|
|
/* ============================================================
|
|
|
|
|
SELECT PAGE — Universal full-screen option picker
|
|
|
|
|
Design: iOS Settings style selection list
|
|
|
|
|
Usage: Navigate to /select with state object
|
|
|
|
|
|
|
|
|
|
State params:
|
|
|
|
|
title - Page title shown in header
|
|
|
|
|
options - Array of option strings
|
|
|
|
|
selected - Currently selected index (number)
|
|
|
|
|
back - Route to navigate back to after selection
|
|
|
|
|
key - Identifier for which setting this controls
|
|
|
|
|
============================================================ */
|
|
|
|
|
import { useLocation } from "wouter";
|
|
|
|
|
import { ChevronLeft, Check } from "lucide-react";
|
|
|
|
|
import { useDevice } from "@/contexts/DeviceContext";
|
|
|
|
|
import { useEffect, useState } from "react";
|
|
|
|
|
|
|
|
|
|
/* ── Key → API field mapping ── */
|
|
|
|
|
const KEY_TO_SETTING: Record<string, string> = {
|
|
|
|
|
effect_value: "effect_value",
|
|
|
|
|
crossfeed_value: "crossfeed_value",
|
|
|
|
|
language: "language",
|
|
|
|
|
analogGain: "analogGain",
|
|
|
|
|
soundStep: "soundStep",
|
|
|
|
|
xlr: "xlr",
|
|
|
|
|
dacGain: "dacGain",
|
|
|
|
|
dacImpedance: "dacImpedance",
|
|
|
|
|
vu: "vu",
|
|
|
|
|
input: "input",
|
|
|
|
|
output: "output",
|
|
|
|
|
screenLight: "screenLight",
|
|
|
|
|
screenOff: "screenOff",
|
|
|
|
|
buttonLight: "buttonLight",
|
|
|
|
|
autoHome: "autoHome",
|
|
|
|
|
sleep: "sleep",
|
2026-04-01 16:29:52 +08:00
|
|
|
headphone: "peqSelect",
|
2026-03-31 18:52:37 +08:00
|
|
|
};
|
|
|
|
|
|
|
|
|
|
// Global state for select page (to avoid URL params)
|
|
|
|
|
export let selectPageState: {
|
|
|
|
|
title: string;
|
|
|
|
|
options: string[];
|
|
|
|
|
selected: number;
|
|
|
|
|
back: string;
|
|
|
|
|
key: string;
|
|
|
|
|
} | null = null;
|
|
|
|
|
|
|
|
|
|
// Helper function to set select page state
|
|
|
|
|
export function navigateToSelect(title: string, options: string[], selected: number, back: string, key: string) {
|
|
|
|
|
selectPageState = { title, options, selected, back, key };
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
export default function SelectPage() {
|
|
|
|
|
const [, setLocation] = useLocation();
|
|
|
|
|
const { updateSetting } = useDevice();
|
|
|
|
|
const [state, setState] = useState<{
|
|
|
|
|
title: string;
|
|
|
|
|
options: string[];
|
|
|
|
|
selected: number;
|
|
|
|
|
back: string;
|
|
|
|
|
key: string;
|
|
|
|
|
} | null>(selectPageState);
|
|
|
|
|
|
|
|
|
|
// Clear state when component unmounts
|
|
|
|
|
useEffect(() => {
|
|
|
|
|
return () => {
|
|
|
|
|
selectPageState = null;
|
|
|
|
|
};
|
|
|
|
|
}, []);
|
|
|
|
|
|
|
|
|
|
if (!state) {
|
|
|
|
|
return null;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const { title, options, selected: selectedIdx, back, key } = state;
|
|
|
|
|
|
|
|
|
|
const handleSelect = (idx: number) => {
|
|
|
|
|
// Apply setting if key maps to an API field
|
|
|
|
|
const apiField = KEY_TO_SETTING[key];
|
|
|
|
|
if (apiField) {
|
|
|
|
|
updateSetting({ [apiField]: idx } as Parameters<typeof updateSetting>[0]);
|
|
|
|
|
}
|
|
|
|
|
// Navigate back
|
|
|
|
|
setLocation(back);
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
return (
|
|
|
|
|
<div className="min-h-screen bg-black">
|
|
|
|
|
{/* ── Header ── */}
|
|
|
|
|
<div className="page-header">
|
|
|
|
|
<button
|
|
|
|
|
onClick={() => setLocation(back)}
|
|
|
|
|
className="mr-4 text-white/60 active:text-white transition-colors">
|
|
|
|
|
<ChevronLeft size={24} />
|
|
|
|
|
</button>
|
|
|
|
|
<h1 className="flex-1 text-center text-[17px] font-semibold text-white">{title}</h1>
|
|
|
|
|
<div className="w-8" />
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
{/* ── Options list ── */}
|
|
|
|
|
<div className="px-4 pt-4 pb-24">
|
|
|
|
|
<div className="ios-list-group">
|
|
|
|
|
{options.map((opt, idx) => {
|
|
|
|
|
const isSelected = idx === selectedIdx;
|
|
|
|
|
return (
|
|
|
|
|
<button
|
|
|
|
|
key={idx}
|
|
|
|
|
className="ios-list-row w-full text-left active:bg-white/5 transition-colors"
|
|
|
|
|
onClick={() => handleSelect(idx)}>
|
|
|
|
|
<span className={`flex-1 text-[16px] ${isSelected ? "text-white font-medium" : "text-white/75"}`}>
|
|
|
|
|
{opt}
|
|
|
|
|
</span>
|
|
|
|
|
{isSelected && (
|
|
|
|
|
<Check size={18} style={{ color: "#00FFF6" }} />
|
|
|
|
|
)}
|
|
|
|
|
</button>
|
|
|
|
|
);
|
|
|
|
|
})}
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
);
|
|
|
|
|
}
|