feat(ui): the six native selects become the house controls

Owner's pick from the S7 mockups (2026-09-19): a short fixed set is a SegmentedToggle — the live
search's result count, the table's rows per page, a download profile's mode (on its own row, so the
three worded choices fit a phone) and audio format — and a longer set or the user's own list is a
FilterSelect: the profile's quality (seven heights) and the download dialog's profile.

A lint rule refuses a native <select> from now on (the canonical "FE control" row). The px-frozen
size rules also caught up with a fraction: `text-[9.5px]` had slipped past the integer-only pattern
in the Glass tuner, which is rem now.
This commit is contained in:
2026-09-19 17:53:22 +02:00
parent 17a26894b0
commit 0ef25a8037
9 changed files with 171 additions and 84 deletions
+12 -12
View File
@@ -13,6 +13,7 @@ import { notify } from "../lib/notifications";
import { notifyActionError } from "../lib/actionErrors";
import { navigateTo } from "../lib/nav";
import { inputCls } from "./ui/form";
import FilterSelect from "./ui/FilterSelect";
// Preset picker shown when the user hits Download on a card / in the player, or adds a URL from
// the Download Center. Enqueues via the API and points the user at the Download Center.
@@ -65,18 +66,17 @@ export default function DownloadDialog({
<Modal title={t("downloads.dialog.title")} onClose={onClose}>
{title && <p className="text-sm text-muted mb-4 line-clamp-2">{title}</p>}
<label className="block text-sm font-medium mb-1">{t("downloads.dialog.profile")}</label>
<select
value={chosen ?? ""}
onChange={(e) => setProfileId(Number(e.target.value))}
className={inputCls}
>
{profiles.map((p) => (
<option key={p.id} value={p.id}>
{p.name}
</option>
))}
</select>
<span className="block text-sm font-medium mb-1" aria-hidden="true">
{t("downloads.dialog.profile")}
</span>
{/* The user's own list, of any length: a menu, not a segmented bar (owner's pick, 2026-09-19). */}
<FilterSelect
label={t("downloads.dialog.profile")}
hideLabel
selected={chosen == null ? [] : [String(chosen)]}
onChange={([v]) => v && setProfileId(Number(v))}
options={profiles.map((p) => ({ value: String(p.id), label: p.name }))}
/>
<button
type="button"
onClick={() => setManage(true)}