Files
siftlode/frontend/src/components/SelectedViewChip.tsx
T
peter 7ebbf45921 feat(feed): saved views as a header popover + Personal Views modal + a view chip
Retire the standalone 'Saved views' sidebar panel. The saved-views feature now
lives in three lighter surfaces, coordinated by a new SavedViewsProvider (query +
CRUD + the active/sticky view + modal state):

- the filter panel's header ICON opens a quick-pick popover (apply a view, active
  one checked, plus 'Manage views…');
- the header TITLE ('Filters') opens a new Personal Views modal — a master/detail
  manager: left = the reorderable list + a save-current-filters input, right = the
  selected view's name, its captured filters (shown as the same chips the toolbar
  row uses), default toggle, apply, and delete;
- a SelectedViewChip leads the applied-filter row, naming the active view with a
  warn-coloured 'modified' dot once the live filters diverge from it; clicking it
  reopens the picker.

SidePanel/FilterRail gain optional iconExpanded + onTitleClick slots (feed-only,
hidden for the demo account); the collapsed tab keeps its plain pin behaviour.
SavedViewsWidget is deleted and dropped from the panel layout.

Verified live: icon popover lists HH + Manage; applying HH surfaces the chip and
switches scope; removing a filter lights the dirty dot; the title opens the
master/detail modal with the captured-filters summary. 374 vitest, tsc, eslint
(0 err).
2026-08-22 04:03:58 +02:00

35 lines
1.5 KiB
TypeScript

import { useTranslation } from "react-i18next";
import { Bookmark, ChevronDown } from "lucide-react";
import SavedViewsPopover from "./SavedViewsPopover";
import { useSavedViews } from "./SavedViewsProvider";
// The active personal view, shown as the leading pill before the active-filter chip row. Styled like
// the toolbar's FilterSelect pills; a warn-coloured dot appears when the live filters have diverged
// from the saved view. Clicking it opens the saved-views picker to switch views. Renders nothing when
// no view is active (nothing applied and the filters match no saved view).
export default function SelectedViewChip() {
const { t } = useTranslation();
const { displayView, isDirty } = useSavedViews();
if (!displayView) return null;
return (
<SavedViewsPopover
ariaLabel={t("views.saved")}
className="glass-card glass-hover inline-flex items-center gap-1.5 rounded-full border border-border px-3 py-1 text-sm text-fg transition hover:border-accent/60"
trigger={
<>
<Bookmark className="h-3.5 w-3.5 shrink-0 text-accent" aria-hidden="true" />
<span className="max-w-40 truncate">{displayView.name}</span>
{isDirty && (
<span
className="h-1.5 w-1.5 shrink-0 rounded-full bg-warn"
title={t("views.modified")}
aria-label={t("views.modified")}
/>
)}
<ChevronDown className="h-3.5 w-3.5 shrink-0 text-muted" aria-hidden="true" />
</>
}
/>
);
}