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:
@@ -1,29 +1,37 @@
|
||||
import { type ReactNode, type CSSProperties } from "react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { ChevronDown, Eye, EyeOff, GripVertical } from "lucide-react";
|
||||
import { ChevronDown, Eye, EyeOff, GripVertical, Pencil } from "lucide-react";
|
||||
import { useSortable } from "@dnd-kit/sortable";
|
||||
import { CSS } from "@dnd-kit/utilities";
|
||||
|
||||
// One collapsible "island" card inside a SidePanel: a titled glass-card with per-group collapse,
|
||||
// and — in the panel's edit mode — a drag handle (reorder) and a hide toggle. Generalized from
|
||||
// the feed sidebar's WidgetCard so all three panels share the same island look and behavior.
|
||||
// One "island" card inside a SidePanel: a titled glass-card, and — in the panel's edit mode — a drag
|
||||
// handle (reorder) and a hide toggle. Generalized from the feed sidebar's WidgetCard so all three
|
||||
// panels share the same island look and behavior. `collapsible` groups (the default) get a per-group
|
||||
// collapse chevron; non-collapsible groups render as a plain always-open label + control row, and
|
||||
// their title can instead act as a trigger via `onTitleClick` (e.g. feed "Your tags" -> tag manager).
|
||||
export default function PanelGroup({
|
||||
id,
|
||||
title,
|
||||
editing,
|
||||
collapsible = true,
|
||||
collapsed,
|
||||
hidden,
|
||||
onToggleCollapse,
|
||||
onToggleHidden,
|
||||
onTitleClick,
|
||||
titleActionLabel,
|
||||
children,
|
||||
}: {
|
||||
id: string;
|
||||
title: string;
|
||||
editing: boolean;
|
||||
collapsible?: boolean;
|
||||
collapsed: boolean;
|
||||
hidden: boolean;
|
||||
onToggleCollapse: () => void;
|
||||
onToggleHidden: () => void;
|
||||
onTitleClick?: () => void;
|
||||
titleActionLabel?: string;
|
||||
children: ReactNode;
|
||||
}) {
|
||||
const { t } = useTranslation();
|
||||
@@ -32,7 +40,13 @@ export default function PanelGroup({
|
||||
disabled: !editing,
|
||||
});
|
||||
const style: CSSProperties = { transform: CSS.Transform.toString(transform), transition };
|
||||
const showBody = !editing && !collapsed;
|
||||
// Non-collapsible groups are always open (outside edit mode); collapsible ones honour `collapsed`.
|
||||
const showBody = !editing && (!collapsible || !collapsed);
|
||||
// A non-collapsible group's title acts as a trigger when one is provided.
|
||||
const titleIsTrigger = !editing && !collapsible && !!onTitleClick;
|
||||
|
||||
const titleCls =
|
||||
"flex-1 min-w-0 flex items-center gap-1 text-left select-none text-sm uppercase tracking-wide";
|
||||
|
||||
return (
|
||||
<div
|
||||
@@ -53,13 +67,27 @@ export default function PanelGroup({
|
||||
<GripVertical className="w-4 h-4" />
|
||||
</button>
|
||||
)}
|
||||
<button
|
||||
onClick={editing ? undefined : onToggleCollapse}
|
||||
disabled={editing}
|
||||
className="flex-1 min-w-0 flex items-center text-left select-none text-sm uppercase tracking-wide text-muted enabled:hover:text-fg transition"
|
||||
>
|
||||
<span className="truncate">{title}</span>
|
||||
</button>
|
||||
{collapsible && !editing ? (
|
||||
<button
|
||||
onClick={onToggleCollapse}
|
||||
className={`${titleCls} text-muted hover:text-fg transition`}
|
||||
>
|
||||
<span className="truncate">{title}</span>
|
||||
</button>
|
||||
) : titleIsTrigger ? (
|
||||
<button
|
||||
onClick={onTitleClick}
|
||||
title={titleActionLabel}
|
||||
className={`${titleCls} group text-muted hover:text-accent transition`}
|
||||
>
|
||||
<span className="truncate">{title}</span>
|
||||
<Pencil className="w-3 h-3 shrink-0 text-muted/60 group-hover:text-accent" />
|
||||
</button>
|
||||
) : (
|
||||
<span className={`${titleCls} text-muted`}>
|
||||
<span className="truncate">{title}</span>
|
||||
</span>
|
||||
)}
|
||||
{editing ? (
|
||||
<button
|
||||
onClick={onToggleHidden}
|
||||
@@ -69,15 +97,17 @@ export default function PanelGroup({
|
||||
{hidden ? <EyeOff className="w-4 h-4" /> : <Eye className="w-4 h-4" />}
|
||||
</button>
|
||||
) : (
|
||||
<button
|
||||
onClick={onToggleCollapse}
|
||||
title={collapsed ? t("sidebar.expand") : t("sidebar.collapse")}
|
||||
className="text-muted hover:text-fg"
|
||||
>
|
||||
<ChevronDown
|
||||
className={`w-4 h-4 transition-transform ${collapsed ? "-rotate-90" : ""}`}
|
||||
/>
|
||||
</button>
|
||||
collapsible && (
|
||||
<button
|
||||
onClick={onToggleCollapse}
|
||||
title={collapsed ? t("sidebar.expand") : t("sidebar.collapse")}
|
||||
className="text-muted hover:text-fg"
|
||||
>
|
||||
<ChevronDown
|
||||
className={`w-4 h-4 transition-transform ${collapsed ? "-rotate-90" : ""}`}
|
||||
/>
|
||||
</button>
|
||||
)
|
||||
)}
|
||||
</div>
|
||||
{showBody && <div className="px-3 pb-3">{children}</div>}
|
||||
|
||||
Reference in New Issue
Block a user