feat(feed): flatten the filter rail — dropdown upload-date, non-collapsible widgets, title triggers

Rework the feed filter rail into simpler label + control rows:
- Remove the rail's own "Clear all" (the chip row above the cards already
  surfaces one whenever any filter is active).
- Replace the Upload date chip group with a single-select dropdown
  (DateFilterSelect): an "Any time" clear, more presets (2 weeks / 3 months /
  2 years / 5 years) and a "Custom range" that reveals from/to on select; the
  trigger then reads the chosen range. Selection logic is the pure lib/dateFilter
  (applyDateChip -> selectDateChip, no toggle-off; tests updated).
- Widgets are no longer collapsible: PanelGroup gains `collapsible` (feed passes
  false) so groups render as always-open label + control rows; reorder + hide in
  edit mode stay. Other rails (Plex/Playlists) keep the collapsible islands.
- A non-collapsible group's title can act as its own trigger (PanelItem
  onTitleClick): "Your tags" opens the tag manager, replacing the inline Manage
  button.
This commit is contained in:
2026-08-22 05:42:30 +02:00
parent 7ebbf45921
commit fca989c098
9 changed files with 368 additions and 185 deletions
+17 -1
View File
@@ -17,7 +17,16 @@ import {
import type { PanelLayout } from "../lib/panelLayout";
import PanelGroup from "./PanelGroup";
export type PanelItem = { id: string; title: string; body: ReactNode };
export type PanelItem = {
id: string;
title: string;
body: ReactNode;
/** When set (and the panel is non-collapsible), the group's title becomes a button firing this —
* e.g. the feed's "Your tags" opens the tag manager. */
onTitleClick?: () => void;
/** Accessible name / tooltip for the title action (what onTitleClick does). */
titleActionLabel?: string;
};
// Renders a panel's islands in the user's saved order, with drag-to-reorder + hide + per-group
// collapse driven by `layout` (see lib/panelLayout). `items` holds only the currently AVAILABLE
@@ -29,11 +38,15 @@ export default function PanelGroups({
layout,
setLayout,
editing,
collapsible = true,
}: {
items: PanelItem[];
layout: PanelLayout;
setLayout: (l: PanelLayout) => void;
editing: boolean;
/** When false, groups render as plain (always-open) label + control rows — no collapse chevron.
* Reorder + hide (in edit mode) stay. Default true (the collapsible-island look). */
collapsible?: boolean;
}) {
const sensors = useSensors(
useSensor(PointerSensor, { activationConstraint: { distance: 4 } }),
@@ -76,10 +89,13 @@ export default function PanelGroups({
id={id}
title={byId.get(id)!.title}
editing={editing}
collapsible={collapsible}
collapsed={!!layout.collapsed[id]}
hidden={!!layout.hidden[id]}
onToggleCollapse={() => toggleCollapse(id)}
onToggleHidden={() => toggleHidden(id)}
onTitleClick={byId.get(id)!.onTitleClick}
titleActionLabel={byId.get(id)!.titleActionLabel}
>
{byId.get(id)!.body}
</PanelGroup>