feat(channels): the band's tag chips and status row fold on a short window (B4)
Every other page band folds its filter row below ~600px of height; the Channels band never did, so its tag chip lane and status / pager row kept a short laptop window for themselves. They fold behind one "Filters · n" (n = picked tags + other active column filters); the tabs and the TAGS editor stay, and a folded table keeps its controls at the list's end.
This commit is contained in:
@@ -37,7 +37,9 @@ import ChannelStatsPopover from "./ChannelStatsPopover";
|
||||
import Pager from "./ui/Pager";
|
||||
import { CHANNEL_LAYOUTS, type ChannelLayout } from "../lib/channelLayout";
|
||||
import { sortRows } from "../lib/columnSort";
|
||||
import { filterRows } from "../lib/tableColumns";
|
||||
import { filterRows, isActive } from "../lib/tableColumns";
|
||||
import { useFilterFold } from "../lib/useFilterFold";
|
||||
import { FilterFoldButton } from "./ui/FilterFold";
|
||||
import { useCardPager } from "../lib/useCardPager";
|
||||
import { PageToolbar } from "./ui/PageShell";
|
||||
import { PageMenu } from "./ui/PageMenu";
|
||||
@@ -243,6 +245,13 @@ export default function Channels() {
|
||||
// funnel share ONE state and stay in sync (bidirectional). Toggling a chip rewrites `filters.tags`;
|
||||
// filterRows / DataTable then applies it. Channel-name search stays separate (the shared top box).
|
||||
const tagFilter = Array.isArray(filters.tags) ? filters.tags : [];
|
||||
// B4 (E28 review L7): on a short window the band's filters — the tag chip lane and the status /
|
||||
// pager row — fold behind one "Filters · n"; the tabs and the TAGS editor stay. n = each picked tag,
|
||||
// plus every other active column filter (the status chips are the Sync column's).
|
||||
const fold = useFilterFold();
|
||||
const foldCount =
|
||||
tagFilter.length +
|
||||
Object.entries(filters).filter(([k, v]) => k !== "tags" && isActive(v)).length;
|
||||
// Picking a real tag drops the "No tag" sentinel (item B) — they're mutually exclusive, as in the feed.
|
||||
const toggleTagFilter = (tagId: number) => {
|
||||
const id = String(tagId);
|
||||
@@ -609,8 +618,9 @@ export default function Channels() {
|
||||
{t("channels.tags.label")}
|
||||
</Button>
|
||||
</Tooltip>
|
||||
<TagFilterChips chips={tagFilterChips} className="flex-1" />
|
||||
{tagFilter.length > 0 && (
|
||||
<FilterFoldButton fold={fold} label={t("sidebar.filters")} count={foldCount} />
|
||||
{!fold.folded && <TagFilterChips chips={tagFilterChips} className="flex-1" />}
|
||||
{!fold.folded && tagFilter.length > 0 && (
|
||||
<Button
|
||||
variant="link-muted"
|
||||
onClick={() => setFilters({ ...filters, tags: [] })}
|
||||
@@ -646,7 +656,7 @@ export default function Channels() {
|
||||
)}
|
||||
{/* In table mode the status chips + pager ride in DataTable's own in-band controls row. The card
|
||||
layout has no such row, so surface the same status chips (left) and its pager (right) here. */}
|
||||
{view === "subscribed" && layout === "cards" && (
|
||||
{view === "subscribed" && layout === "cards" && !fold.folded && (
|
||||
<div className="px-4 pb-2 max-w-384 mx-auto flex items-center justify-between gap-3 flex-wrap">
|
||||
{statusChips}
|
||||
{cardPager}
|
||||
@@ -717,7 +727,8 @@ export default function Channels() {
|
||||
columns={columns}
|
||||
rowKey={(c) => c.id}
|
||||
persistKey={accountKey(LS.channelsTable) ?? undefined}
|
||||
controlsPosition="both"
|
||||
// Folded, the status / pager row leaves the band; the list's own end keeps it.
|
||||
controlsPosition={fold.folded ? "bottom" : "both"}
|
||||
controlsLeading={statusChips}
|
||||
controlsInBand
|
||||
controlsBandClassName="px-4 max-w-384 mx-auto"
|
||||
|
||||
@@ -22,3 +22,16 @@ describe("the Channels tag row", () => {
|
||||
expect(block).toContain("@max-[40rem]/tagrow:basis-full");
|
||||
});
|
||||
});
|
||||
|
||||
// E28 review L7: every other page band folds its filter row on a short window (B4, `useFilterFold`);
|
||||
// the Channels band never did — its tag chips and the status / pager row kept a laptop's short window
|
||||
// for themselves. They fold behind one "Filters · n", the tabs and the TAGS editor stay.
|
||||
describe("the Channels band on a short window", () => {
|
||||
it("folds the tag chip lane and the status / pager row behind the fold button", () => {
|
||||
expect(src).toContain("const fold = useFilterFold();");
|
||||
expect(src).toMatch(/<FilterFoldButton\s[^>]*fold=\{fold\}/);
|
||||
expect(src).toMatch(/!fold\.folded && <TagFilterChips/);
|
||||
expect(src).toMatch(/controlsPosition=\{fold\.folded \? "bottom" : "both"\}/);
|
||||
expect(src).toMatch(/layout === "cards" && !fold\.folded/);
|
||||
});
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user