polish(plex): match the feed rail's segmented-pill look for the any/all toggle

The genre + actor MATCH toggles were a boxed button row (rounded-lg border, no
track); restyle them as the feed rail's tag-match toggle — a rounded-full track
(border + bg-card + p-0.5) with an inset rounded-full active pill, plus a MATCH
label. Extracted a shared MatchToggle so both read identically. i18n
plex.filter.matchLabel (en/hu). (Plex cast-explore polish)
This commit is contained in:
2026-07-19 04:31:02 +02:00
parent d91f27a22e
commit 12d17ec7b0
3 changed files with 50 additions and 31 deletions
+46 -29
View File
@@ -33,6 +33,43 @@ const DURATION_BUCKETS: { key: string; min: number | null; max: number | null }[
{ key: "long", min: 120 * 60, max: null },
];
// The any/all combine toggle for a multi-value filter (genres, actors). Same segmented-pill look as
// the feed rail's tag MATCH toggle (rounded-full track + inset active pill), not a boxed button row.
function MatchToggle({
mode,
onChange,
}: {
mode: "any" | "all";
onChange: (m: "any" | "all") => void;
}) {
const { t } = useTranslation();
return (
<div className="mb-1.5 flex items-center justify-between">
<span className="text-[10px] uppercase tracking-wide text-muted">
{t("plex.filter.matchLabel")}
</span>
<div
className="flex items-center rounded-full border border-border bg-card p-0.5 text-[10px]"
role="group"
aria-label={t("plex.filter.matchLabel")}
>
{(["any", "all"] as const).map((m) => (
<button
key={m}
onClick={() => onChange(m)}
aria-pressed={mode === m}
className={`rounded-full px-2 py-0.5 transition ${
mode === m ? "bg-accent text-accent-fg" : "text-muted hover:text-fg"
}`}
>
{t(`plex.filter.match.${m}`)}
</button>
))}
</div>
</div>
);
}
export default function PlexSidebar({
layout,
setLayout,
@@ -212,20 +249,11 @@ export default function PlexSidebar({
/>
))}
{filters.actors.length > 1 && (
<div className="w-full inline-flex overflow-hidden rounded-lg border border-border text-[11px]">
{(["any", "all"] as const).map((m) => (
<button
key={m}
onClick={() => patch({ actorMode: m })}
className={`px-2 py-0.5 transition ${
(filters.actorMode ?? "any") === m
? "bg-accent text-accent-fg"
: "text-muted hover:bg-surface"
}`}
>
{t(`plex.filter.match.${m}`)}
</button>
))}
<div className="w-full">
<MatchToggle
mode={filters.actorMode ?? "any"}
onChange={(m) => patch({ actorMode: m })}
/>
</div>
)}
{filters.actors.map((a) => (
@@ -312,21 +340,10 @@ export default function PlexSidebar({
body: (
<>
{filters.genres.length > 1 && (
<div className="mb-1.5 inline-flex overflow-hidden rounded-lg border border-border text-[11px]">
{(["any", "all"] as const).map((m) => (
<button
key={m}
onClick={() => patch({ genreMode: m })}
className={`px-2 py-0.5 transition ${
(filters.genreMode ?? "any") === m
? "bg-accent text-accent-fg"
: "text-muted hover:bg-surface"
}`}
>
{t(`plex.filter.match.${m}`)}
</button>
))}
</div>
<MatchToggle
mode={filters.genreMode ?? "any"}
onChange={(m) => patch({ genreMode: m })}
/>
)}
<ChipRow>
{facets.genres.map((g) => (
+2 -1
View File
@@ -57,7 +57,8 @@
},
"collection": "Collection",
"collectionSearch": "Search collections…",
"collectionEmpty": "No collections"
"collectionEmpty": "No collections",
"matchLabel": "Match"
},
"count": "{{count}} titles",
"searchCount": "{{count}} matches",
+2 -1
View File
@@ -57,7 +57,8 @@
},
"collection": "Kollekció",
"collectionSearch": "Kollekciók keresése…",
"collectionEmpty": "Nincs kollekció"
"collectionEmpty": "Nincs kollekció",
"matchLabel": "Egyezés"
},
"count": "{{count}} cím",
"searchCount": "{{count}} találat",