diff --git a/spa/src/components/horizontal-strip.tsx b/spa/src/components/horizontal-strip.tsx new file mode 100644 index 0000000..42825a0 --- /dev/null +++ b/spa/src/components/horizontal-strip.tsx @@ -0,0 +1,71 @@ +import { useRef, useState, useEffect, useCallback } from "react" +import { ChevronLeft, ChevronRight } from "lucide-react" +import { Button } from "@/components/ui/button" + +type HorizontalStripProps = { + children: React.ReactNode + className?: string + gap?: string +} + +export function HorizontalStrip({ children, className, gap = "gap-3" }: HorizontalStripProps) { + const ref = useRef(null) + const [canScrollLeft, setCanScrollLeft] = useState(false) + const [canScrollRight, setCanScrollRight] = useState(false) + + const update = useCallback(() => { + const el = ref.current + if (!el) return + setCanScrollLeft(el.scrollLeft > 0) + setCanScrollRight(el.scrollLeft + el.clientWidth < el.scrollWidth - 1) + }, []) + + useEffect(() => { + const el = ref.current + if (!el) return + update() + el.addEventListener("scroll", update, { passive: true }) + const ro = new ResizeObserver(update) + ro.observe(el) + return () => { + el.removeEventListener("scroll", update) + ro.disconnect() + } + }, [update]) + + function scroll(dir: -1 | 1) { + ref.current?.scrollBy({ left: dir * ref.current.clientWidth * 0.75, behavior: "smooth" }) + } + + return ( +
+ {canScrollLeft && ( + + )} +
+ {children} +
+ {canScrollRight && ( + + )} +
+ ) +} diff --git a/spa/src/routes/_app/movies.$id.tsx b/spa/src/routes/_app/movies.$id.tsx index a7f510a..0baf999 100644 --- a/spa/src/routes/_app/movies.$id.tsx +++ b/spa/src/routes/_app/movies.$id.tsx @@ -5,6 +5,7 @@ import { BackButton } from "@/components/back-button" import { StarDisplay } from "@/components/star-display" import { RatingHistogram } from "@/components/rating-histogram" import { EmptyState } from "@/components/empty-state" +import { HorizontalStrip } from "@/components/horizontal-strip" import { Badge } from "@/components/ui/badge" import { Button } from "@/components/ui/button" import { Card, CardContent, CardHeader, CardTitle, CardDescription } from "@/components/ui/card" @@ -231,7 +232,7 @@ function HeroSection({ function PersonStrip({ items, type }: { items: (CastMemberDto | CrewMemberDto)[]; type: "cast" | "crew" }) { return ( -
+ {items.map((person, i) => { const subtitle = type === "cast" ? (person as CastMemberDto).character @@ -253,7 +254,7 @@ function PersonStrip({ items, type }: { items: (CastMemberDto | CrewMemberDto)[] ) })} -
+ ) } diff --git a/spa/src/routes/_app/people.$id.tsx b/spa/src/routes/_app/people.$id.tsx index b3dcbde..3bd59ae 100644 --- a/spa/src/routes/_app/people.$id.tsx +++ b/spa/src/routes/_app/people.$id.tsx @@ -5,6 +5,7 @@ import { Calendar, ChevronDown, ExternalLink, Film, Globe, MapPin, User } from " import { Link } from "@tanstack/react-router" import { BackButton } from "@/components/back-button" import { EmptyState } from "@/components/empty-state" +import { HorizontalStrip } from "@/components/horizontal-strip" import { SwipeTabs } from "@/components/swipe-tabs" import { Badge } from "@/components/ui/badge" import { Button } from "@/components/ui/button" @@ -167,7 +168,7 @@ type FilmStripItem = { function FilmStrip({ items }: { items: FilmStripItem[] }) { return ( -
+ {items.map((item, i) => (
@@ -184,7 +185,7 @@ function FilmStrip({ items }: { items: FilmStripItem[] }) {

{item.subtitle}

))} -
+
) }