feat(feed): add the small-card, compact-row and tile views
Tiles answer the row view's wasted space: the actions move under the meta into vertical slack the thumbnail already reserves, which frees ~168px of width, so the block survives in a narrow column and the list can go multi-column. Rows stay single-column on purpose — reading down one edge is why that view exists. The column count now comes from a per-view minimum width, so every multi-column view reflows on the mechanism the card grid already used.
This commit is contained in:
@@ -14,7 +14,7 @@ import AddToPlaylist from "./AddToPlaylist";
|
||||
import DownloadButton from "./DownloadButton";
|
||||
import clsx from "clsx";
|
||||
import type { Video } from "../lib/api";
|
||||
import type { FeedView } from "../lib/feedView";
|
||||
import { FEED_VIEW_SPEC, type FeedView } from "../lib/feedView";
|
||||
import { formatDate, formatDuration, formatViews, relativeTime } from "../lib/format";
|
||||
|
||||
function Actions({
|
||||
@@ -304,7 +304,12 @@ function VideoCard({
|
||||
</>
|
||||
);
|
||||
|
||||
if (view === "rows") {
|
||||
const spec = FEED_VIEW_SPEC[view];
|
||||
const actions = (
|
||||
<Actions video={video} onState={onState} onResetState={onResetState} onToggleSave={onToggleSave} />
|
||||
);
|
||||
|
||||
if (spec.family === "row") {
|
||||
return (
|
||||
<div
|
||||
className={clsx(
|
||||
@@ -312,9 +317,22 @@ function VideoCard({
|
||||
watched && "opacity-55"
|
||||
)}
|
||||
>
|
||||
<Thumb video={video} className="w-44 aspect-video shrink-0" onOpen={onOpen} />
|
||||
<div className="min-w-0 flex-1">{textBlock}</div>
|
||||
<Actions video={video} onState={onState} onResetState={onResetState} onToggleSave={onToggleSave} />
|
||||
{/* `compact` = the thumbnail-less row: everything else (meta, actions) stays put. */}
|
||||
{!spec.compact && (
|
||||
<Thumb
|
||||
video={video}
|
||||
className={clsx("aspect-video shrink-0", spec.actionsBelow ? "w-40" : "w-44")}
|
||||
onOpen={onOpen}
|
||||
/>
|
||||
)}
|
||||
<div className="min-w-0 flex-1">
|
||||
{textBlock}
|
||||
{/* Tiles put the actions under the meta, in vertical space the thumbnail already
|
||||
reserves — which frees the ~168px the docked-right block used, so the block still
|
||||
works in a narrow column. */}
|
||||
{spec.actionsBelow && <div className="mt-1">{actions}</div>}
|
||||
</div>
|
||||
{!spec.actionsBelow && actions}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -322,20 +340,25 @@ function VideoCard({
|
||||
return (
|
||||
<div
|
||||
className={clsx(
|
||||
"cv-card group glass-card glass-hover rounded-2xl p-2.5 transition-all duration-150 hover:-translate-y-1",
|
||||
"cv-card group glass-card glass-hover rounded-2xl transition-all duration-150 hover:-translate-y-1",
|
||||
spec.compact ? "p-2" : "p-2.5",
|
||||
watched && "opacity-55"
|
||||
)}
|
||||
>
|
||||
<Thumb video={video} className="aspect-video" onOpen={onOpen} />
|
||||
<div className="flex gap-3 mt-2.5 px-0.5 pb-0.5">
|
||||
<Avatar
|
||||
src={video.channel_thumbnail}
|
||||
fallback={video.channel_title ?? ""}
|
||||
className="w-9 h-9 rounded-full shrink-0 mt-0.5"
|
||||
/>
|
||||
<div className={clsx("flex gap-3 px-0.5 pb-0.5", spec.compact ? "mt-2" : "mt-2.5")}>
|
||||
{/* The small card drops the channel avatar: at its ~180px column the 36px avatar plus the
|
||||
gap would leave the title barely half the width. The channel name is still below it. */}
|
||||
{!spec.compact && (
|
||||
<Avatar
|
||||
src={video.channel_thumbnail}
|
||||
fallback={video.channel_title ?? ""}
|
||||
className="w-9 h-9 rounded-full shrink-0 mt-0.5"
|
||||
/>
|
||||
)}
|
||||
<div className="min-w-0 flex-1">
|
||||
{textBlock}
|
||||
<Actions video={video} onState={onState} onResetState={onResetState} onToggleSave={onToggleSave} />
|
||||
{actions}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
Reference in New Issue
Block a user