import { Children, type ReactNode, useCallback, useEffect, useRef, useState } from "react"; import { ChevronDown, ChevronUp } from "lucide-react"; import { Button } from "~/components/ui/button"; import { cn } from "~/lib/utils"; export function CategoryCardRail({ children, className, }: { children: ReactNode; className?: string; }) { const items = Children.toArray(children); const viewportRef = useRef(null); const trackRef = useRef(null); const [rowHeight, setRowHeight] = useState(); const [overflowing, setOverflowing] = useState(false); const [atTop, setAtTop] = useState(true); const [atBottom, setAtBottom] = useState(false); const updateState = useCallback(() => { const viewport = viewportRef.current; if (!viewport) return; const max = Math.max(0, viewport.scrollHeight - viewport.clientHeight); setOverflowing(max > 2); setAtTop(viewport.scrollTop <= 2); setAtBottom(viewport.scrollTop >= max - 2); }, []); const measure = useCallback(() => { const first = trackRef.current?.querySelector("[data-rail-card]"); if (!first) return; setRowHeight(Math.ceil(first.getBoundingClientRect().height + 2)); requestAnimationFrame(updateState); }, [updateState]); useEffect(() => { measure(); if (typeof ResizeObserver === "undefined") return; const observer = new ResizeObserver(measure); if (trackRef.current) observer.observe(trackRef.current); return () => observer.disconnect(); }, [items.length, measure]); const scrollRow = (direction: -1 | 1) => { const viewport = viewportRef.current; const cards = trackRef.current?.querySelectorAll("[data-rail-card]"); if (!viewport || !cards?.length) return; const absoluteRows = [...new Set([...cards].map((card) => Math.round(card.offsetTop)))].sort((a, b) => a - b); const origin = absoluteRows[0] ?? 0; const rows = absoluteRows.map((top) => top - origin); const current = viewport.scrollTop; const target = direction > 0 ? rows.find((top) => top > current + 3) ?? rows.at(-1) ?? 0 : [...rows].reverse().find((top) => top < current - 3) ?? rows[0] ?? 0; viewport.scrollTo({ top: target, behavior: "smooth" }); }; return (
{items.map((item, index) => (
{item}
))}
{overflowing && (
在卡片区域上下滚动查看更多模型大类
)}
); }