import { useState, useEffect, useRef } from "react"; // ============================================= // Slide Data (DO NOT MODIFY) // ============================================= interface Slide { id: number; url: string; alt: string; } const SLIDES: Slide[] = [ { id: 1, url: "https://picsum.photos/seed/carousel1/800/450", alt: "Mountain landscape" }, { id: 2, url: "https://picsum.photos/seed/carousel2/800/450", alt: "Ocean sunset" }, { id: 3, url: "https://picsum.photos/seed/carousel3/800/450", alt: "Forest trail" }, { id: 4, url: "https://picsum.photos/seed/carousel4/800/450", alt: "City skyline" }, { id: 5, url: "https://picsum.photos/seed/carousel5/800/450", alt: "Desert dunes" }, ]; // ============================================= // TODO: Implement the Image Carousel // ============================================= // // Requirements: // 1. Display one image at a time with prev/next buttons // 2. Show dot indicators — click a dot to jump to that slide // 3. Auto-play: advance every 3 seconds // 4. Pause auto-play on hover // 5. Keyboard navigation (ArrowLeft / ArrowRight) // 6. Smooth CSS transition between slides // 7. Wrap around (last → first, first → last) // // Hints: // - Track active slide with `currentIndex` state // - Slide track: a flex row of all images, shifted with // transform: translateX(-${currentIndex * 100}%) // - Auto-play: useEffect + setInterval, clear on unmount // - Pause: onMouseEnter clears interval, onMouseLeave restarts it // - Keyboard: onKeyDown on the container (needs tabIndex={0}) // - Wrap: (prev - 1 + length) % length for prev, // (prev + 1) % length for next export default function App() { const [currentIndex, setCurrentIndex] = useState(0); // TODO: Create a ref to hold the interval ID for auto-play // const intervalRef = useRef<number | null>(null); // ============================================= // TODO: Implement goNext — advance to next slide (wrap around) // ============================================= const goNext = () => { // Your code here }; // ============================================= // TODO: Implement goPrev — go to previous slide (wrap around) // ============================================= const goPrev = () => { // Your code here }; // ============================================= // TODO: Implement goTo — jump to a specific slide index // ============================================= const goTo = (index: number) => { // Your code here }; // ============================================= // TODO: Implement auto-play // ============================================= // Start a setInterval that calls goNext every 3000ms. // Store the interval ID in intervalRef so you can clear it. // // const startAutoplay = () => { ... }; // const stopAutoplay = () => { ... }; // // useEffect(() => { // startAutoplay(); // return () => stopAutoplay(); // }, []); // ============================================= // TODO: Implement keyboard navigation // ============================================= // ArrowLeft → goPrev() // ArrowRight → goNext() // // const handleKeyDown = (e: React.KeyboardEvent) => { ... }; return ( <div style={{ maxWidth: 640, margin: "0 auto", padding: 24, fontFamily: "system-ui" }}> <h2 style={{ fontSize: 20, fontWeight: 700, marginBottom: 4 }}> Image Carousel </h2> <p style={{ fontSize: 14, color: "#666", marginBottom: 24 }}> Build a carousel with prev/next buttons, dot indicators, auto-play, pause on hover, and keyboard navigation. </p> {/* ============================================= */} {/* Carousel container */} {/* ============================================= */} {/* TODO: Add onMouseEnter={stopAutoplay} */} {/* Add onMouseLeave={startAutoplay} */} {/* Add onKeyDown={handleKeyDown} */} {/* Add tabIndex={0} for keyboard focus */} <div style={{ position: "relative", overflow: "hidden", borderRadius: 12, border: "1px solid #e5e7eb", background: "#f3f4f6", }} > {/* =========================================== */} {/* TODO: Slides track */} {/* =========================================== */} {/* A flex row containing all images side by side. Shift the entire row with: transform: translateX(-${currentIndex * 100}%) Add transition for smooth animation: transition: "transform 0.5s ease-in-out" */} <div style={{ display: "flex", // TODO: Add transform and transition }} > {SLIDES.map((slide) => ( <div key={slide.id} style={{ width: "100%", flexShrink: 0 }}> <img src={slide.url} alt={slide.alt} style={{ width: "100%", aspectRatio: "16/9", objectFit: "cover", display: "block" }} /> </div> ))} </div> {/* =========================================== */} {/* TODO: Prev button */} {/* =========================================== */} {/* Position absolute, left side, vertically centered */} <button onClick={goPrev} aria-label="Previous slide" style={{ position: "absolute", left: 12, top: "50%", transform: "translateY(-50%)", width: 40, height: 40, borderRadius: "50%", background: "rgba(255,255,255,0.8)", border: "1px solid #e5e7eb", cursor: "pointer", display: "flex", alignItems: "center", justifyContent: "center", fontSize: 18, }} > ‹ </button> {/* =========================================== */} {/* TODO: Next button */} {/* =========================================== */} <button onClick={goNext} aria-label="Next slide" style={{ position: "absolute", right: 12, top: "50%", transform: "translateY(-50%)", width: 40, height: 40, borderRadius: "50%", background: "rgba(255,255,255,0.8)", border: "1px solid #e5e7eb", cursor: "pointer", display: "flex", alignItems: "center", justifyContent: "center", fontSize: 18, }} > › </button> {/* =========================================== */} {/* TODO: Dot indicators */} {/* =========================================== */} {/* - Map over SLIDES - Active dot: solid white - Inactive dot: semi-transparent white - Click calls goTo(index) */} <div style={{ position: "absolute", bottom: 14, left: "50%", transform: "translateX(-50%)", display: "flex", gap: 8, }}> {SLIDES.map((slide, index) => ( <button key={slide.id} onClick={() => goTo(index)} aria-label={`Go to slide ${index + 1}`} style={{ width: 10, height: 10, borderRadius: "50%", border: "none", cursor: "pointer", background: index === currentIndex ? "#fff" : "rgba(255,255,255,0.5)", transition: "background 0.2s", }} /> ))} </div> </div> {/* Slide counter */} <div style={{ textAlign: "center", fontSize: 13, color: "#999", marginTop: 12 }}> {currentIndex + 1} / {SLIDES.length} </div> {/* Hint */} <div style={{ marginTop: 24, padding: 16, background: "#f9fafb", borderLeft: "4px solid #111", borderRadius: "0 8px 8px 0", fontSize: 13, color: "#555", }}> <strong>Steps:</strong> <ol style={{ margin: "8px 0 0 16px", padding: 0, lineHeight: 1.8 }}> <li>Implement goNext, goPrev, goTo (wrap-around logic)</li> <li>Add <code>transform</code> and <code>transition</code> to the slides track</li> <li>Add auto-play with setInterval (every 3s)</li> <li>Pause on hover (onMouseEnter/onMouseLeave)</li> <li>Keyboard nav (ArrowLeft/ArrowRight via onKeyDown + tabIndex)</li> </ol> </div> </div> ); }