feat(plex): S3 toolbar relocation — scope to header, sort above the cards

Move the two "what am I browsing" controls out of the filter sidebar to where
they're always in reach, matching the Feed's layout:

- Library scope (both/movie/show) → a compact SegmentedToggle in the header next
  to the search box (new PlexScopeToggle, reads the same provider state).
- Sort → a SortControl in the row above the cards (into the PageToolbar band,
  count on the left, sort on the right), like the Feed.

Sidebar cleanup: drop the `scope` and `sort` island cards; the panel badge now
counts only the filters still in the panel (expanded filters + watch-state);
remove the now-unused sort imports. panelLayout drops both from the default plex
order (stale saved-layout keys are auto-filtered, so existing layouts degrade
cleanly). usePlexActiveFilters no longer emits a sort chip — sort is an
always-visible control now, not a removable narrowing (same as the Feed).

FE-only, no backend/migration. typecheck + eslint clean; 171 vitest green;
self-E2E verified (header scope drives the grid, sort control reorders, sidebar
no longer shows the two cards, no sort chip).
This commit is contained in:
2026-08-16 18:30:48 +02:00
parent 5134ba5433
commit d0f5559c49
6 changed files with 60 additions and 66 deletions
@@ -0,0 +1,26 @@
import { useTranslation } from "react-i18next";
import SegmentedToggle from "./SegmentedToggle";
import { usePlex, usePlexActions } from "./PlexProvider";
// The Plex library scope (both | movie | show) as a compact header control, sitting next to the
// search box (S3 toolbar relocation — it used to be a sidebar island). Reads/writes the same
// provider state the sidebar did, so scope stays in sync everywhere; reuses the shared
// SegmentedToggle and the existing plex.filter.scopeOpt.* labels.
const SCOPES = ["both", "movie", "show"] as const;
export default function PlexScopeToggle() {
const { t } = useTranslation();
const { scope } = usePlex();
const { setScope } = usePlexActions();
return (
<div className="pointer-events-auto shrink-0">
<SegmentedToggle
size="md"
ariaLabel={t("plex.filter.scope")}
options={SCOPES.map((s) => ({ value: s, label: t(`plex.filter.scopeOpt.${s}`) }))}
value={scope}
onChange={setScope}
/>
</div>
);
}