improvement(feed): give the compact row's title the room back
Three from UAT: the row's cap goes 1280 -> 1472 (+15%), since with the rail and filters hidden there was a lot of page left over and the fixed columns mean the extra width can only go to the title; the views column drops the word and keeps it in the tooltip (a table doesn't repeat its unit every line), taking that column 144 -> 64; and the view switcher names the current mode where the toolbar has room, falling back to the icon alone below xl. Title: 431 -> 511 at 1440, 706 at 1900.
This commit is contained in:
@@ -353,8 +353,15 @@ function VideoCard({
|
|||||||
const noThumb = !spec.thumb;
|
const noThumb = !spec.thumb;
|
||||||
// Each datum on its own, so the one-line row can give each a column of its own while the stacked
|
// Each datum on its own, so the one-line row can give each a column of its own while the stacked
|
||||||
// layouts still run them together as one sentence.
|
// layouts still run them together as one sentence.
|
||||||
|
// Two shapes for the count: the stacked layouts read it as prose ("1.5K views · 2 min ago"), so
|
||||||
|
// they need the word; the row's column doesn't — a table doesn't repeat its unit on every line,
|
||||||
|
// and the tooltip has the exact figure anyway. Same tooltip either way.
|
||||||
|
const viewsTitle = video.view_count != null ? `${video.view_count.toLocaleString()} ${t("card.views")}` : "";
|
||||||
|
const viewsCount = video.view_count != null && (
|
||||||
|
<span title={viewsTitle}>{formatViews(video.view_count)}</span>
|
||||||
|
);
|
||||||
const views = video.view_count != null && (
|
const views = video.view_count != null && (
|
||||||
<span title={`${video.view_count.toLocaleString()} ${t("card.views")}`}>
|
<span title={viewsTitle}>
|
||||||
{formatViews(video.view_count)} {t("card.views")}
|
{formatViews(video.view_count)} {t("card.views")}
|
||||||
</span>
|
</span>
|
||||||
);
|
);
|
||||||
@@ -439,12 +446,12 @@ function VideoCard({
|
|||||||
// still right WITHIN a cell (see RowMetaCells): fixed slots, so they line up across rows too.
|
// still right WITHIN a cell (see RowMetaCells): fixed slots, so they line up across rows too.
|
||||||
// Widths are measured, not chosen by eye — against the worst case in the LIBRARY and in BOTH
|
// Widths are measured, not chosen by eye — against the worst case in the LIBRARY and in BOTH
|
||||||
// languages, since both have caught me out here:
|
// languages, since both have caught me out here:
|
||||||
// actions 188 (6 x 28 + 5 x 4) · meta cells 104 (see RowMetaCells) · views 139 ("101.7K
|
// actions 188 (6 x 28 + 5 x 4) · meta cells 104 (see RowMetaCells) · views 48 (the bare
|
||||||
// megtekintés"; the English "views" is only 79) · published 183 ("11 months ago ·
|
// "101.7K"; language-proof now the word is gone — with it, Hungarian's "megtekintés" needed
|
||||||
// Feb 28, 2026"; HU is shorter) · channel 128, which is a deliberate cut rather than a fit:
|
// 139) · published 183 ("11 months ago · Feb 28, 2026"; HU is shorter) · channel 128,
|
||||||
// names run to 342px, the median is 87, and 12 of 102 truncate here vs 5 at 160 — worth 32px
|
// which is a deliberate cut rather than a fit: names run to 342px, the median is 87, and 12
|
||||||
// to the title, since a clipped channel name still reads while a clipped view count loses the
|
// of 102 truncate here vs 5 at 160 — worth 32px to the title, since a clipped channel name
|
||||||
// number.
|
// still reads while a clipped view count loses the number.
|
||||||
// Three ways this went wrong before: sizing off a PAGE of data (the badges came from 60 videos
|
// Three ways this went wrong before: sizing off a PAGE of data (the badges came from 60 videos
|
||||||
// whose longest was 2:00:58, then wrapped on the 11-hour one); measuring with a hand-built probe
|
// whose longest was 2:00:58, then wrapped on the 11-hour one); measuring with a hand-built probe
|
||||||
// instead of the real element (it read "101.7K megtekintés" as 122px where the live cell renders
|
// instead of the real element (it read "101.7K megtekintés" as 122px where the live cell renders
|
||||||
@@ -467,7 +474,12 @@ function VideoCard({
|
|||||||
<div className="min-w-0 flex-1">{title}</div>
|
<div className="min-w-0 flex-1">{title}</div>
|
||||||
<div className="shrink-0 w-32 hidden sm:block">{channelButton}</div>
|
<div className="shrink-0 w-32 hidden sm:block">{channelButton}</div>
|
||||||
<RowMetaCells video={video} className="hidden md:grid" />
|
<RowMetaCells video={video} className="hidden md:grid" />
|
||||||
<div className="shrink-0 w-36 text-sm text-muted truncate hidden lg:block">{views}</div>
|
{/* Right-aligned like the duration: it's a number, so the digits share an edge. 64px
|
||||||
|
holds the widest bare form ("101.7K" at 48) in either language — dropping the word
|
||||||
|
took this from 144 and handed the difference to the title. */}
|
||||||
|
<div className="shrink-0 w-16 text-sm text-muted text-right tabular-nums truncate hidden lg:block">
|
||||||
|
{viewsCount}
|
||||||
|
</div>
|
||||||
<div className="shrink-0 w-48 text-sm text-muted truncate hidden xl:block">{published}</div>
|
<div className="shrink-0 w-48 text-sm text-muted truncate hidden xl:block">{published}</div>
|
||||||
{/* Thumb draws this over the image; with no image it goes on the row itself. */}
|
{/* Thumb draws this over the image; with no image it goes on the row itself. */}
|
||||||
{pct > 0 && (
|
{pct > 0 && (
|
||||||
|
|||||||
@@ -96,12 +96,17 @@ export default function ViewSwitcher<T extends string>({
|
|||||||
onClick={() => (open ? close() : openMenu())}
|
onClick={() => (open ? close() : openMenu())}
|
||||||
aria-haspopup="menu"
|
aria-haspopup="menu"
|
||||||
aria-expanded={open}
|
aria-expanded={open}
|
||||||
|
// The aria-label carries the mode's name at every size — the visible text below is a
|
||||||
|
// nicety for wide windows, not the accessible name.
|
||||||
aria-label={`${label}: ${current.label}`}
|
aria-label={`${label}: ${current.label}`}
|
||||||
title={`${label}: ${current.label}`}
|
title={`${label}: ${current.label}`}
|
||||||
className="shrink-0 inline-flex items-center gap-1 pl-1.5 pr-1 py-1.5 rounded-lg border border-border bg-card text-fg hover:border-accent hover:text-accent active:translate-y-px transition"
|
className="shrink-0 inline-flex items-center gap-1 pl-2 pr-1 py-1.5 rounded-lg border border-border bg-card text-fg hover:border-accent hover:text-accent active:translate-y-px transition"
|
||||||
>
|
>
|
||||||
<CurrentIcon className="w-4 h-4" />
|
<CurrentIcon className="w-4 h-4 shrink-0" />
|
||||||
<ChevronDown className={`w-3 h-3 transition-transform ${open ? "rotate-180" : ""}`} />
|
{/* Name the current mode where the toolbar has room; below xl it's a busy row (show chips,
|
||||||
|
content chips, source, count, sort) and the icon has to carry it alone. */}
|
||||||
|
<span className="hidden xl:inline text-sm whitespace-nowrap">{current.label}</span>
|
||||||
|
<ChevronDown className={`w-3 h-3 shrink-0 transition-transform ${open ? "rotate-180" : ""}`} />
|
||||||
</button>
|
</button>
|
||||||
{open && (
|
{open && (
|
||||||
<div
|
<div
|
||||||
|
|||||||
@@ -41,9 +41,10 @@ export const FEED_VIEW_SPEC: Record<FeedView, FeedViewSpec> = {
|
|||||||
// Keeps the 896px cap: its actions dock at the right edge, so a wider row would only stretch the
|
// Keeps the 896px cap: its actions dock at the right edge, so a wider row would only stretch the
|
||||||
// dead space between them and the meta.
|
// dead space between them and the meta.
|
||||||
rows: { family: "row", minCol: null, maxCol: 896, thumb: true, dense: false, actionsBelow: false },
|
rows: { family: "row", minCol: null, maxCol: 896, thumb: true, dense: false, actionsBelow: false },
|
||||||
// Wider (1280): everything sits on one line with the actions right-aligned, so there's no gap to
|
// Much wider (1472): everything sits on one line and the columns after the title are fixed, so
|
||||||
// grow — the extra width just buys more title before it truncates.
|
// there's no gap for the extra width to stretch — it all goes to the title. Raised from 1280 at
|
||||||
rowsCompact: { family: "row", minCol: null, maxCol: 1280, thumb: false, dense: false, actionsBelow: false },
|
// the user's request once they saw how much room was left over with the rail and filters hidden.
|
||||||
|
rowsCompact: { family: "row", minCol: null, maxCol: 1472, thumb: false, dense: false, actionsBelow: false },
|
||||||
// 380 = thumbnail (160) + gap (12) + ~192 for the text + the block's own padding (16). The text
|
// 380 = thumbnail (160) + gap (12) + ~192 for the text + the block's own padding (16). The text
|
||||||
// floor is the action row's measured 156px plus enough for a title to be worth reading.
|
// floor is the action row's measured 156px plus enough for a title to be worth reading.
|
||||||
// Tuned by measuring the real layouts, not guessed: 3 columns need 3n+32 px, so 380 turns the
|
// Tuned by measuring the real layouts, not guessed: 3 columns need 3n+32 px, so 380 turns the
|
||||||
|
|||||||
Reference in New Issue
Block a user