'use client'; import { useState, useRef, useCallback } from 'react'; import Link from 'next/link'; import { Card } from '@/components/ui/Card'; import { Badge } from '@/components/ui/Badge'; import { Icons } from '@/components/ui/Icon'; import { useKeyboardNavigation } from '@/lib/hooks/useKeyboardNavigation'; interface Video { vod_id: string; vod_name: string; vod_pic?: string; vod_remarks?: string; vod_year?: string; type_name?: string; source: string; sourceName?: string; isNew?: boolean; } interface VideoGridProps { videos: Video[]; className?: string; } export function VideoGrid({ videos, className = '' }: VideoGridProps) { const [activeCardId, setActiveCardId] = useState(null); const [focusedIndex, setFocusedIndex] = useState(-1); const gridRef = useRef(null); const videoRefs = useRef<(HTMLAnchorElement | null)[]>([]); // Calculate columns for grid navigation const getColumns = () => { if (typeof window === 'undefined') return 6; const width = window.innerWidth; if (width >= 1536) return 6; // 2xl if (width >= 1280) return 6; // xl if (width >= 1024) return 5; // lg if (width >= 768) return 4; // md if (width >= 640) return 3; // sm return 2; // default }; // Keyboard navigation useKeyboardNavigation({ enabled: true, containerRef: gridRef, currentIndex: focusedIndex, itemCount: videos.length, orientation: 'grid', columns: getColumns(), onNavigate: useCallback((index: number) => { setFocusedIndex(index); videoRefs.current[index]?.focus(); }, []), onSelect: useCallback((index: number) => { videoRefs.current[index]?.click(); }, []), }); if (videos.length === 0) { return null; } const handleCardClick = (e: React.MouseEvent, videoId: string, videoUrl: string) => { // Check if it's a mobile device const isMobile = window.innerWidth < 1024; // lg breakpoint if (isMobile) { // On mobile, first click shows details, second click navigates if (activeCardId === videoId) { // Already active, allow navigation window.location.href = videoUrl; } else { // First click, show details e.preventDefault(); setActiveCardId(videoId); } } // On desktop, let the Link work normally }; return (
{videos.map((video, index) => { const videoUrl = `/player?${new URLSearchParams({ id: video.vod_id, source: video.source, title: video.vod_name, }).toString()}`; const cardId = `${video.vod_id}-${index}`; const isActive = activeCardId === cardId; const isFocused = focusedIndex === index; return ( { videoRefs.current[index] = el; }} onClick={(e) => handleCardClick(e, cardId, videoUrl)} onFocus={() => setFocusedIndex(index)} onKeyDown={(e) => { if (e.key === 'Enter' || e.key === ' ') { e.preventDefault(); handleCardClick(e as any, cardId, videoUrl); } }} role="listitem" tabIndex={0} aria-label={`${video.vod_name}${video.vod_remarks ? ` - ${video.vod_remarks}` : ''}`} > {/* Poster */}
{video.vod_pic ? ( {video.vod_name} { e.currentTarget.src = '/placeholder-poster.svg'; }} /> ) : (
)} {/* Source Badge - Top Left */} {video.sourceName && (
{video.sourceName}
)} {/* Overlay - Show on hover (desktop) or when active (mobile) */}
{/* Mobile indicator when active */} {isActive && (
再次点击播放 →
)} {video.type_name && ( {video.type_name} )} {video.vod_year && (
{video.vod_year}
)}
{/* Info - Fixed height section */}

{video.vod_name}

{video.vod_remarks && (

{video.vod_remarks}

)}
); })}
); }