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:
@@ -51,6 +51,7 @@ import ItemCount from "./ui/ItemCount";
|
||||
import ViewSwitcher from "./ViewSwitcher";
|
||||
import VirtualFeed from "./VirtualFeed";
|
||||
import { FocusSafeButton } from "./ui/FocusSafeButton";
|
||||
import SegmentedToggle from "./ui/SegmentedToggle";
|
||||
// Lazy: the in-app YouTube player (IFrame API) loads only when a video is first opened.
|
||||
const PlayerModal = lazy(() => import("./PlayerModal"));
|
||||
|
||||
@@ -95,6 +96,9 @@ function matchesView(status: VideoStatus, show: string): boolean {
|
||||
}
|
||||
}
|
||||
|
||||
// How many live-search results one fetch asks for; `Load more` pulls another batch of the same size.
|
||||
const YT_COUNTS = [20, 40, 60, 100] as const;
|
||||
|
||||
export default function Feed({
|
||||
filters,
|
||||
setFilters,
|
||||
@@ -441,21 +445,20 @@ export default function Feed({
|
||||
<span className="text-xs text-muted">
|
||||
{zeroQuota ? t("feed.yt.freeNote") : t("feed.yt.quotaNote")}
|
||||
</span>
|
||||
<label className="ml-auto flex items-center gap-1.5 text-xs text-muted shrink-0">
|
||||
{t("feed.yt.show")}
|
||||
<select
|
||||
value={ytCount}
|
||||
onChange={(e) => setYtCount(Number(e.target.value))}
|
||||
title={t("feed.yt.showHint")}
|
||||
className="bg-surface border border-border rounded-lg px-2 py-1 text-xs focus:outline-hidden focus:ring-2 focus:ring-accent/40"
|
||||
>
|
||||
{[20, 40, 60, 100].map((n) => (
|
||||
<option key={n} value={n}>
|
||||
{n}
|
||||
</option>
|
||||
))}
|
||||
</select>
|
||||
</label>
|
||||
{/* Four fixed counts: the house's segmented control, not a native <select> (owner's
|
||||
pick, 2026-09-19). The caption is the group's name; the hint rides on the row. */}
|
||||
<div
|
||||
className="ml-auto flex items-center gap-1.5 text-xs text-muted shrink-0"
|
||||
title={t("feed.yt.showHint")}
|
||||
>
|
||||
<span aria-hidden="true">{t("feed.yt.show")}</span>
|
||||
<SegmentedToggle
|
||||
ariaLabel={t("feed.yt.show")}
|
||||
value={String(ytCount)}
|
||||
onChange={(v) => setYtCount(Number(v))}
|
||||
options={YT_COUNTS.map((n) => ({ value: String(n), label: String(n) }))}
|
||||
/>
|
||||
</div>
|
||||
{/* Sorts the results already on screen (see sortYtResults) — it never re-queries, so it
|
||||
costs nothing and works the same on both search sources. */}
|
||||
<SortControl
|
||||
|
||||
Reference in New Issue
Block a user