fix(channels): tag-filter row folds overflow into "+N", keeps "…" pinned
The channel manager's tag-filter row wrapped the trailing "…" catalog-actions menu onto a second line once the chips + Clear outgrew the width. Rework it as one non-wrapping row: the TAGS editor trigger (left), a single-line chip lane that collapses overflow into a "+N" pill whose popover holds the rest (still toggleable), then Clear and the "…" menu pinned right — so "…" always stays on the first line. New TagFilterChips reuses the shared measure core (useChipOverflowFit + fitChipsWithPill), the same compression the feed's applied-filter row uses.
This commit is contained in:
@@ -18,7 +18,8 @@ import { useChannelTagActions } from "../lib/useChannelTagActions";
|
||||
import { useChannelPriority } from "../lib/useChannelPriority";
|
||||
import ViewSwitcher, { type ViewOption } from "./ViewSwitcher";
|
||||
import ColumnSortControl from "./ColumnSortControl";
|
||||
import FilterChipGroup, { filterChipClass } from "./FilterChipGroup";
|
||||
import FilterChipGroup from "./FilterChipGroup";
|
||||
import TagFilterChips, { type FilterChip } from "./TagFilterChips";
|
||||
import ActionMenu, { type ActionMenuItem } from "./ActionMenu";
|
||||
import ChannelStatsPopover from "./ChannelStatsPopover";
|
||||
import Pager from "./Pager";
|
||||
@@ -229,6 +230,22 @@ export default function Channels() {
|
||||
const noTagOn = tagFilter.includes(NO_TAG_SENTINEL);
|
||||
const toggleNoTagFilter = () =>
|
||||
setFilters({ ...filters, tags: noTagOn ? [] : [NO_TAG_SENTINEL] });
|
||||
// The tag-filter chips (user tags + the "No tag" sentinel), fed to TagFilterChips which folds any
|
||||
// overflow into a "+N" pill so the row stays one line and the trailing "…" actions can't wrap.
|
||||
const tagFilterChips: FilterChip[] = [
|
||||
...userTags.map((tg) => ({
|
||||
key: `t${tg.id}`,
|
||||
label: tg.name,
|
||||
active: tagFilter.includes(String(tg.id)),
|
||||
onToggle: () => toggleTagFilter(tg.id),
|
||||
})),
|
||||
{
|
||||
key: NO_TAG_SENTINEL,
|
||||
label: t("channels.tags.noTag"),
|
||||
active: noTagOn,
|
||||
onToggle: toggleNoTagFilter,
|
||||
},
|
||||
];
|
||||
// A focus-channel intent (header "without full history" / tag manager) seeds the search box.
|
||||
useEffect(() => {
|
||||
if (focusChannelName) setSearch(focusChannelName);
|
||||
@@ -433,48 +450,31 @@ export default function Channels() {
|
||||
{tabs}
|
||||
{view === "subscribed" && (
|
||||
<div className="px-4 pt-2 pb-2 max-w-384 mx-auto">
|
||||
{/* Your tags (left) share one row with the two catalog-wide actions (right); the channel-name
|
||||
search lives in the shared top SearchBar, tag create/delete in the manager. */}
|
||||
<div className="flex items-center justify-between gap-4 flex-wrap">
|
||||
<div className="flex flex-wrap items-center gap-1.5">
|
||||
{/* The "TAGS" label IS the tags-editor trigger (pencil), replacing the separate
|
||||
"Manage tags" button; the tooltip explains what tags are + that it's clickable. */}
|
||||
<Tooltip hint={t("channels.tags.yourTagsHint")}>
|
||||
<button
|
||||
onClick={() => setTagManagerOpen(true)}
|
||||
className="mr-1 inline-flex items-center gap-1.5 rounded-lg border border-border px-2 py-1 text-xs uppercase tracking-wide text-muted transition hover:border-accent hover:text-accent"
|
||||
>
|
||||
<Pencil className="h-3 w-3" />
|
||||
{t("channels.tags.label")}
|
||||
</button>
|
||||
</Tooltip>
|
||||
{userTags.map((tg) => (
|
||||
<button
|
||||
key={tg.id}
|
||||
onClick={() => toggleTagFilter(tg.id)}
|
||||
aria-pressed={tagFilter.includes(String(tg.id))}
|
||||
className={filterChipClass}
|
||||
>
|
||||
{tg.name}
|
||||
</button>
|
||||
))}
|
||||
{/* One non-wrapping row: the TAGS editor trigger (left) + the tag-filter chip lane (folds
|
||||
overflow into "+N") + Clear, then the catalog "…" actions pinned right. It never wraps, so the
|
||||
"…" menu always stays on the first line. Channel-name search lives in the shared top SearchBar. */}
|
||||
<div className="flex items-center gap-2 min-w-0">
|
||||
{/* The "TAGS" label IS the tags-editor trigger (pencil), replacing the separate
|
||||
"Manage tags" button; the tooltip explains what tags are + that it's clickable. */}
|
||||
<Tooltip hint={t("channels.tags.yourTagsHint")}>
|
||||
<button
|
||||
onClick={toggleNoTagFilter}
|
||||
aria-pressed={noTagOn}
|
||||
className={filterChipClass}
|
||||
onClick={() => setTagManagerOpen(true)}
|
||||
className="shrink-0 inline-flex items-center gap-1.5 rounded-lg border border-border px-2 py-1 text-xs uppercase tracking-wide text-muted transition hover:border-accent hover:text-accent"
|
||||
>
|
||||
{t("channels.tags.noTag")}
|
||||
<Pencil className="h-3 w-3" />
|
||||
{t("channels.tags.label")}
|
||||
</button>
|
||||
{tagFilter.length > 0 && (
|
||||
<button
|
||||
onClick={() => setFilters({ ...filters, tags: [] })}
|
||||
className="text-xs px-2 py-1 text-muted hover:text-accent transition"
|
||||
>
|
||||
{t("datatable.clear")}
|
||||
</button>
|
||||
)}
|
||||
</div>
|
||||
{actionButtons}
|
||||
</Tooltip>
|
||||
<TagFilterChips chips={tagFilterChips} className="flex-1" />
|
||||
{tagFilter.length > 0 && (
|
||||
<button
|
||||
onClick={() => setFilters({ ...filters, tags: [] })}
|
||||
className="shrink-0 text-xs px-2 py-1 text-muted hover:text-accent transition"
|
||||
>
|
||||
{t("datatable.clear")}
|
||||
</button>
|
||||
)}
|
||||
<div className="shrink-0">{actionButtons}</div>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
|
||||
Reference in New Issue
Block a user