Refactor showcase cards to use absolute grid overlay expansion system

This commit is contained in:
kenilkb 2026-07-17 16:21:29 +05:30
parent a1626a5c20
commit 9646309a0a

View File

@ -100,8 +100,13 @@ const TwitterEmbed: React.FC<{ url: string }> = ({ url }) => {
};
// ── Collapsible Video Description Component ──
const VideoDescription: React.FC<{ text: string }> = ({ text }) => {
const [isExpanded, setIsExpanded] = useState(false);
interface VideoDescriptionProps {
text: string;
isExpanded: boolean;
onToggleExpand: () => void;
}
const VideoDescription: React.FC<VideoDescriptionProps> = ({ text, isExpanded, onToggleExpand }) => {
const shouldCollapse = text.length > 180 || text.includes('\n');
return (
@ -118,7 +123,7 @@ const VideoDescription: React.FC<{ text: string }> = ({ text }) => {
<button
onClick={(e) => {
e.stopPropagation();
setIsExpanded(!isExpanded);
onToggleExpand();
}}
className="inline-flex items-center text-[10px] font-black uppercase tracking-wider text-blue-600 hover:text-blue-800 transition-colors focus:outline-none cursor-pointer"
>
@ -133,6 +138,7 @@ export const ShowcasePage: React.FC = () => {
const [items, setItems] = useState<ContentShowcase[]>([]);
const [loading, setLoading] = useState(true);
const [playingVideoId, setPlayingVideoId] = useState<string | null>(null);
const [expandedItemId, setExpandedItemId] = useState<string | null>(null);
// Resizable Lightbox state: compact | theater | cinema
const [lightboxSize, setLightboxSize] = useState<'compact' | 'theater' | 'cinema'>('compact');
@ -189,20 +195,27 @@ export const ShowcasePage: React.FC = () => {
<p className="text-xs text-ink-500 mt-1 max-w-sm">There are no featured videos available in the showcase right now. Check back later!</p>
</div>
) : (
<div className="grid grid-cols-1 md:grid-cols-2 xl:grid-cols-3 gap-6 animate-fadeIn">
<div className="grid grid-cols-1 md:grid-cols-2 xl:grid-cols-3 gap-6 items-start animate-fadeIn">
{items.map((item) => {
const ytId = extractYouTubeVideoId(item.youtubeUrl);
const isIg = item.youtubeUrl.includes('instagram.com');
const isTw = item.youtubeUrl.includes('twitter.com') || item.youtubeUrl.includes('x.com');
const thumbnail = item.thumbnailUrl || (ytId ? `https://img.youtube.com/vi/${ytId}/hqdefault.jpg` : null);
const isExpanded = expandedItemId === item.id;
return (
<div
key={item.id}
className="group flex flex-col bg-ink-0 border border-ink-200 hover:border-ink-350 rounded-2xl overflow-hidden hover:shadow-xl transition-all duration-500"
<div key={item.id} className="relative min-h-[400px]">
<motion.div
layout
transition={{ type: "spring", stiffness: 320, damping: 28 }}
className={`group flex flex-col bg-ink-0 border rounded-2xl overflow-hidden transition-all duration-300 ${
isExpanded
? 'absolute z-20 top-0 left-0 right-0 h-auto shadow-2xl border-ink-350 bg-ink-0'
: 'relative w-full h-full border-ink-200 hover:border-ink-350 hover:shadow-xl'
}`}
>
{/* Video Player / Thumbnail */}
<div className="relative aspect-video bg-ink-900 overflow-hidden">
<div className="relative aspect-video bg-ink-900 overflow-hidden shrink-0">
{thumbnail ? (
<img
src={thumbnail}
@ -256,13 +269,17 @@ export const ShowcasePage: React.FC = () => {
</div>
{/* Content Info */}
<div className="p-5 flex-1 flex flex-col justify-between space-y-4">
<div className="p-5 flex-grow flex flex-col justify-between space-y-4">
<div className="space-y-2">
<h3 className="text-sm font-black tracking-tight text-ink-900 leading-snug">
{item.title}
</h3>
{item.description && (
<VideoDescription text={item.description} />
<VideoDescription
text={item.description}
isExpanded={isExpanded}
onToggleExpand={() => setExpandedItemId(isExpanded ? null : item.id)}
/>
)}
</div>
@ -281,6 +298,7 @@ export const ShowcasePage: React.FC = () => {
</div>
)}
</div>
</motion.div>
</div>
);
})}