({ videos })
| 21 | |
| 22 | // 3. Define the 'VideosComponent' functional component that accepts 'VideosComponentProps' |
| 23 | const VideosComponent: React.FC<VideosComponentProps> = ({ videos }) => { |
| 24 | // 4. Declare state variables using the 'useState' hook |
| 25 | const [loadedImages, setLoadedImages] = useState<boolean[]>([]); |
| 26 | const [selectedVideo, setSelectedVideo] = useState<string | null>(null); |
| 27 | |
| 28 | // 5. Use the 'useEffect' hook to initialize the 'loadedImages' state based on the number of videos |
| 29 | useEffect(() => { |
| 30 | setLoadedImages(Array(videos.length).fill(false)); |
| 31 | }, [videos]); |
| 32 | |
| 33 | // 6. Define the 'handleImageLoad' function to update the 'loadedImages' state when an image is loaded |
| 34 | const handleImageLoad = (index: number) => { |
| 35 | setLoadedImages((prevLoadedImages) => { |
| 36 | const updatedLoadedImages = [...prevLoadedImages]; |
| 37 | updatedLoadedImages[index] = true; |
| 38 | return updatedLoadedImages; |
| 39 | }); |
| 40 | }; |
| 41 | |
| 42 | // 7. Define the 'handleVideoSelect' function to set the 'selectedVideo' state when a thumbnail is clicked |
| 43 | const handleVideoSelect = (link: string) => { |
| 44 | setSelectedVideo(link); |
| 45 | }; |
| 46 | |
| 47 | // 8. Define the 'VideosSkeleton' component to display a loading skeleton while videos are loading |
| 48 | const VideosSkeleton = () => ( |
| 49 | <div className="w-full p-1 "> |
| 50 | <div className="w-full overflow-hidden aspect-video mt-5"> |
| 51 | <div className="w-full h-full bg-gray-300 dark:bg-gray-700 rounded animate-pulse"></div> |
| 52 | </div> |
| 53 | </div> |
| 54 | ); |
| 55 | |
| 56 | // 9. Render the 'VideosComponent' JSX |
| 57 | return ( |
| 58 | <div className="bg-white dark:bg-gray-800 shadow-lg rounded-lg mt-4 w-full px-2 flex items-center justify-center"> |
| 59 | {/* 10. Render the video carousel */} |
| 60 | <Carousel |
| 61 | opts={{ |
| 62 | align: "start", |
| 63 | }} |
| 64 | orientation="vertical" |
| 65 | className="w-full max-w-xs" |
| 66 | > |
| 67 | <CarouselContent className=" h-[175px] mx-auto"> |
| 68 | {videos.length === 0 ? ( |
| 69 | <VideosSkeleton /> |
| 70 | ) : ( |
| 71 | videos.map((video, index) => { |
| 72 | const videoId = getYouTubeVideoId(video.link); |
| 73 | const imageUrl = `https://i.ytimg.com/vi/${videoId}/hqdefault.jpg`; |
| 74 | |
| 75 | return ( |
| 76 | <CarouselItem key={index} className="py-5 md:basis-1/2 "> |
| 77 | <div className="p-1"> |
| 78 | <Card> |
| 79 | <CardContent className="flex items-center justify-center p-0 h-[150px]"> |
| 80 | {selectedVideo === video.link ? ( |
nothing calls this directly
no test coverage detected