Refactors the SimpleCarousel component to use a flex-wrap container, removing horizontal scroll buttons and implementing responsive styling for video posters based on screen size. Adds CSS for the new layout and adjusts the "Top 10" number overlay positioning. Replit-Commit-Author: Agent Replit-Commit-Session-Id: 2eb1084e-b728-4449-9231-f1665924c8d5 Replit-Commit-Checkpoint-Type: full_checkpoint Replit-Commit-Screenshot-Url: https://storage.googleapis.com/screenshot-production-us-central1/8cc42625-c1f5-4e43-99bd-77f2c4dedee2/2eb1084e-b728-4449-9231-f1665924c8d5/kdQ95gE
128 lines
4.5 KiB
TypeScript
128 lines
4.5 KiB
TypeScript
import { useState, useRef, useEffect } from "react";
|
|
import { type Video } from "@shared/schema";
|
|
import VideoCard from "./video-card";
|
|
import { ChevronLeft, ChevronRight } from "lucide-react";
|
|
|
|
interface SimpleCarouselProps {
|
|
category: {
|
|
title: string;
|
|
videos: Video[];
|
|
};
|
|
onVideoClick: (video: Video) => void;
|
|
}
|
|
|
|
export default function SimpleCarousel({ category, onVideoClick }: SimpleCarouselProps) {
|
|
const scrollContainerRef = useRef<HTMLDivElement>(null);
|
|
const scrollIntervalRef = useRef<NodeJS.Timeout | null>(null);
|
|
const [isScrolling, setIsScrolling] = useState(false);
|
|
const [currentDirection, setCurrentDirection] = useState<'left' | 'right' | null>(null);
|
|
const [speed, setSpeed] = useState<'normal' | 'fast'>('normal');
|
|
|
|
const scroll = (direction: 'left' | 'right') => {
|
|
// If already scrolling in same direction, toggle speed
|
|
if (isScrolling && currentDirection === direction) {
|
|
const newSpeed = speed === 'normal' ? 'fast' : 'normal';
|
|
setSpeed(newSpeed);
|
|
|
|
// Restart with new speed immediately
|
|
if (scrollIntervalRef.current) {
|
|
clearInterval(scrollIntervalRef.current);
|
|
}
|
|
|
|
const speedValue = newSpeed === 'fast' ? 1.8 : 0.8;
|
|
scrollIntervalRef.current = setInterval(() => {
|
|
if (!scrollContainerRef.current) return;
|
|
|
|
const scrollAmount = direction === 'right' ? speedValue : -speedValue;
|
|
scrollContainerRef.current.scrollBy({
|
|
left: scrollAmount,
|
|
behavior: 'auto'
|
|
});
|
|
}, 8);
|
|
} else {
|
|
// If not scrolling or different direction, start scrolling
|
|
startAutoScroll(direction);
|
|
}
|
|
};
|
|
|
|
const startAutoScroll = (direction: 'left' | 'right') => {
|
|
if (scrollIntervalRef.current) {
|
|
clearInterval(scrollIntervalRef.current);
|
|
}
|
|
|
|
setIsScrolling(true);
|
|
setCurrentDirection(direction);
|
|
setSpeed('normal'); // Reset to normal speed when starting
|
|
|
|
const speedValue = 0.8; // Always start with normal speed
|
|
scrollIntervalRef.current = setInterval(() => {
|
|
if (!scrollContainerRef.current) return;
|
|
|
|
const scrollAmount = direction === 'right' ? speedValue : -speedValue;
|
|
scrollContainerRef.current.scrollBy({
|
|
left: scrollAmount,
|
|
behavior: 'auto'
|
|
});
|
|
}, 8);
|
|
};
|
|
|
|
const stopAutoScroll = () => {
|
|
if (scrollIntervalRef.current) {
|
|
clearInterval(scrollIntervalRef.current);
|
|
scrollIntervalRef.current = null;
|
|
}
|
|
setIsScrolling(false);
|
|
setCurrentDirection(null);
|
|
setSpeed('normal');
|
|
};
|
|
|
|
// Initialize scroll position in the middle so we can scroll both ways
|
|
useEffect(() => {
|
|
if (scrollContainerRef.current && category.videos.length > 0) {
|
|
// Wait for content to load, then scroll to middle
|
|
setTimeout(() => {
|
|
if (scrollContainerRef.current) {
|
|
const containerWidth = scrollContainerRef.current.scrollWidth;
|
|
const viewportWidth = scrollContainerRef.current.clientWidth;
|
|
const middlePosition = (containerWidth - viewportWidth) / 2;
|
|
|
|
scrollContainerRef.current.scrollTo({
|
|
left: middlePosition,
|
|
behavior: 'auto'
|
|
});
|
|
}
|
|
}, 100);
|
|
}
|
|
}, [category.videos.length]);
|
|
|
|
return (
|
|
<div className="relative group mb-12">
|
|
<h2 className="oswald-text text-2xl text-bunny-light mb-6 px-4">
|
|
{category.title}
|
|
</h2>
|
|
|
|
{/* Container z flex wrap */}
|
|
<div className="container flex flex-wrap justify-center gap-2.5 p-2.5 md:flex-row flex-col items-center">
|
|
{category.videos.map((video, videoIndex) => (
|
|
<div key={video.id} className="relative">
|
|
{/* Top 10 Number overlay for first category */}
|
|
{category.title.includes("Top 10") && (
|
|
<div className="absolute top-1 left-1 z-30 text-white font-black text-xl md:text-2xl drop-shadow-2xl pointer-events-none"
|
|
style={{
|
|
textShadow: '4px 4px 8px rgba(0,0,0,0.8), -2px -2px 4px rgba(0,0,0,0.6)',
|
|
WebkitTextStroke: '1px rgba(0,0,0,0.8)',
|
|
}}>
|
|
{videoIndex + 1}
|
|
</div>
|
|
)}
|
|
<VideoCard
|
|
video={video}
|
|
onClick={onVideoClick}
|
|
className="poster w-[150px] md:w-[150px] sm:w-[120px] h-auto hover:scale-105 transition-all duration-300 hover:shadow-2xl rounded-md overflow-hidden relative"
|
|
/>
|
|
</div>
|
|
))}
|
|
</div>
|
|
</div>
|
|
);
|
|
} |