feat(filters): pick one year from a decade grid, in the feed and in Plex

A shared YearPicker: the recent years as chips with their counts, and a
More dialog that lays every year out by decade (sortable by count), so a
ninety-year library fits on one screen. The feed's scrolling popover gives
way to it; the Plex rail gets it above its range fields, a pick setting
both ends. The Plex facets now count titles per year, the year filter left
out.
This commit is contained in:
2026-11-14 15:08:50 +01:00
parent 94f9ffdb1c
commit 1dff809268
10 changed files with 338 additions and 161 deletions
+28 -12
View File
@@ -21,6 +21,7 @@ import { usePlexSavedViews } from "./plexSavedViews";
import { usePlex, usePlexActions } from "./plexContext";
import { DismissibleChip } from "../../components/ui/Chip";
import { Input } from "../../components/ui/Input";
import YearPicker from "../../components/ui/YearPicker";
import { useRail } from "../../lib/useRail";
import { msg } from "../../lib/notifications";
@@ -173,18 +174,33 @@ export default function PlexSidebar() {
id: "year",
title: t("plex.filter.year"),
body: (
<div className="flex items-center gap-2">
<NumberInput
value={filters.yearMin ?? null}
placeholder={facets?.year_min ?? undefined}
onChange={(v) => patch({ yearMin: v })}
/>
<span className="text-muted text-xs">–</span>
<NumberInput
value={filters.yearMax ?? null}
placeholder={facets?.year_max ?? undefined}
onChange={(v) => patch({ yearMax: v })}
/>
<div className="flex flex-col gap-2">
{/* One year is a range of one: picking it sets both ends, as the year on an info page does,
so the range fields below show it and a decade stays typeable there. */}
{facets && facets.years.length > 0 && (
<YearPicker
years={facets.years}
selected={
filters.yearMin != null && filters.yearMin === filters.yearMax
? filters.yearMin
: undefined
}
onPick={(year) => patch({ yearMin: year ?? null, yearMax: year ?? null })}
/>
)}
<div className="flex items-center gap-2">
<NumberInput
value={filters.yearMin ?? null}
placeholder={facets?.year_min ?? undefined}
onChange={(v) => patch({ yearMin: v })}
/>
<span className="text-muted text-xs">–</span>
<NumberInput
value={filters.yearMax ?? null}
placeholder={facets?.year_max ?? undefined}
onChange={(v) => patch({ yearMax: v })}
/>
</div>
</div>
),
},