feat(design): a page's background menu on the five list pages (E28 S3e4c)
A right-click on the EMPTY area of a page opens a menu of that page's own toolbar: the feed (sort, reshuffle, view, watch state, content, source, clear filters), the Plex library grid (sort, watch state, clear), the channel manager (layout, the cards' sort, sync status, edit tags, clear the tag filter, read subscriptions), a playlist (play all, group by, reset view, save order, undo/redo, the list's own menu) and the notification center (mark all read). No destructive row and no backfill. PageShell listens on the page area; lib/backgroundTarget decides empty (not text, a control, a link, media, or a row/card with its own menu). Shift+right- click, a touch long-press and a right-click on selected words keep the browser's menu. ui/PageMenu registers while mounted, so a loading or error state keeps the browser's menu too. The rows mirror each control through ui/pageMenuItems (sortSubmenu via pickSort, choiceSubmenu, multiSubmenu). pageMenuScope.contract.test.ts pins the five pages.
This commit is contained in:
@@ -39,6 +39,10 @@ import { sortRows } from "../lib/columnSort";
|
||||
import { filterRows } from "../lib/tableColumns";
|
||||
import { useCardPager } from "../lib/useCardPager";
|
||||
import { PageToolbar } from "./ui/PageShell";
|
||||
import { PageMenu } from "./ui/PageMenu";
|
||||
import { menuGroups } from "./ui/menuGroups";
|
||||
import { choiceSubmenu, sortSubmenu } from "./ui/pageMenuItems";
|
||||
import { optionsFromColumns } from "../lib/sortOptions";
|
||||
import ChannelDiscovery from "./ChannelDiscovery";
|
||||
import TagManager from "./TagManager";
|
||||
import { useChannelLayout, useChannels, useChannelsActions } from "./channelsContext";
|
||||
@@ -516,8 +520,65 @@ export default function Channels() {
|
||||
</div>
|
||||
);
|
||||
|
||||
// The page's background menu (DESIGN "Background menu"): the toolbar's own controls, no more — the
|
||||
// layout, the cards' sort, the status and tag filters, the tag editor and the catalog sync. Backfill
|
||||
// stays in the "…" only: a long quota-heavy run is not a thing to start from a stray right-click.
|
||||
const syncOptions = SYNC_CHIPS.map((c) => ({ value: c.value, label: t(c.labelKey) }));
|
||||
const pageMenuItems = menuGroups(
|
||||
hasLayout
|
||||
? [
|
||||
choiceSubmenu({
|
||||
id: "layout",
|
||||
label: t("channels.layoutLabel"),
|
||||
options: layoutOptions.map((o) => ({ value: o.id, label: o.label })),
|
||||
selected: layout,
|
||||
onPick: setLayout,
|
||||
}),
|
||||
]
|
||||
: [],
|
||||
view === "subscribed" && isCards
|
||||
? [
|
||||
sortSubmenu({
|
||||
id: "sort",
|
||||
label: t("channels.sort.label"),
|
||||
options: optionsFromColumns(columns),
|
||||
sort,
|
||||
onChange: setSort,
|
||||
ascLabel: t("channels.sort.asc"),
|
||||
descLabel: t("channels.sort.desc"),
|
||||
defaultLabel: t("channels.sort.default"),
|
||||
}),
|
||||
]
|
||||
: [],
|
||||
view === "subscribed"
|
||||
? [
|
||||
choiceSubmenu({
|
||||
id: "status",
|
||||
label: t("channels.filters.syncLabel"),
|
||||
options: syncOptions,
|
||||
selected: syncFilter,
|
||||
onPick: (sync) => setFilters({ ...filters, sync }),
|
||||
}),
|
||||
{ id: "tags", label: t("channels.tags.edit"), onSelect: () => setTagManagerOpen(true) },
|
||||
...(tagFilter.length > 0
|
||||
? [
|
||||
{
|
||||
id: "clearTags",
|
||||
label: t("channels.tags.clearFilter"),
|
||||
onSelect: () => setFilters({ ...filters, tags: [] }),
|
||||
},
|
||||
]
|
||||
: []),
|
||||
]
|
||||
: [],
|
||||
view === "subscribed" ? catalogActions.filter((a) => a.id === "sync") : [],
|
||||
);
|
||||
|
||||
return (
|
||||
<>
|
||||
{pageMenuItems.length > 0 && (
|
||||
<PageMenu ariaLabel={t("common.pageMenu")} items={pageMenuItems} />
|
||||
)}
|
||||
{/* The tab bar is always fixed chrome (it portals into the shell's fixed band, so it never
|
||||
scrolls and doesn't jump between tabs). On Subscriptions the stats/description/tags ride
|
||||
with it, and the status-chip + pager row lands just below (DataTable's controlsInBand). */}
|
||||
|
||||
Reference in New Issue
Block a user