feat(filters): the any/all switch sits on the panel above its picker

ui/MatchToggle replaces the Combobox popover footer (Feed tags, topics,
languages; Plex collections) and PlexSidebar's own toggle (genres,
actors): shown from two picks, one wording. A closed picker had hidden
that "all" was on.
This commit is contained in:
2026-09-27 07:12:51 +02:00
parent 955c23d321
commit f48f16830e
12 changed files with 159 additions and 168 deletions
-33
View File
@@ -61,11 +61,6 @@ export interface ComboboxProps {
/** Chip/label text for a selected value whose option is not in the current (e.g. server-narrowed)
* list — the collection picker passes the stored title here so a reloaded filter still names itself. */
selectedLabels?: Record<string, string>;
/** Optional "any/all" match segment in the popover footer (like SortControl's direction segment):
* "or" = a match on ANY selected value, "and" = must match ALL. Provide BOTH to show it; it appears
* only once 2+ values are selected (below that, any and all are the same). */
matchMode?: "or" | "and";
onMatchModeChange?: (mode: "or" | "and") => void;
}
export default function Combobox({
@@ -84,8 +79,6 @@ export default function Combobox({
maxVisible = 50,
renderOption,
selectedLabels,
matchMode,
onMatchModeChange,
}: ComboboxProps) {
const { t } = useTranslation();
const [open, setOpen] = useState(false);
@@ -299,32 +292,6 @@ export default function Combobox({
</>
)}
</div>
{/* Any/all match segment (like SortControl's direction footer) — only meaningful, so only shown,
once 2+ values are picked. mousedown-preventDefault keeps the input focused so the list stays
open in the combobox's focus model. */}
{matchMode != null && onMatchModeChange && selected.length >= 2 && (
<div className="mt-1 flex items-center gap-2 border-t border-border px-1 pt-2">
<span className="text-2xs text-muted">{t("common.matchMode")}</span>
<div className="ml-auto flex gap-1">
{(["or", "and"] as const).map((m) => (
<button
key={m}
type="button"
aria-pressed={matchMode === m}
onMouseDown={(e) => e.preventDefault()}
onClick={() => onMatchModeChange(m)}
className={`rounded-md border px-2 py-0.5 text-2xs transition ${
matchMode === m
? "border-accent bg-accent/15 text-accent"
: "border-border text-muted hover:border-accent/50 hover:text-fg"
}`}
>
{t(m === "or" ? "sidebar.any" : "sidebar.all")}
</button>
))}
</div>
</div>
)}
</PopoverSurface>
</div>
);