crush-level-web/src/app/(main)/home/components/StartChat/index.tsx

139 lines
4.0 KiB
TypeScript

"use client";
import useEmblaCarousel from "embla-carousel-react";
import { useCallback, useEffect, useState } from "react";
import { useHomeData } from "../../context/HomeDataContext";
import { AudioPlayerProvider } from "../../context/AudioPlayerContext";
import StartChatItem from "./StartChatItem";
import StartChatSkeleton from "./StartChatSkeleton";
import { IconButton } from "@/components/ui/button";
const StartChat = () => {
// 从 Context 获取数据
const { data: homeData, isLoading } = useHomeData();
// 只取前20条数据
const displayData = homeData?.starAChat?.slice(0, 20) || [];
// 根据屏幕宽度计算每次滑动的卡片数量
const getSlidesToScroll = () => {
if (typeof window === 'undefined') return 4;
const width = window.innerWidth;
if (width < 640) return 1; // 移动端: 1列
if (width < 768) return 2; // sm: 2列
if (width < 1024) return 2; // md: 2列
if (width < 1280) return 3; // lg: 3列
return 4; // xl+: 4列
};
const [slidesToScroll, setSlidesToScroll] = useState(getSlidesToScroll());
const [emblaRef, emblaApi] = useEmblaCarousel({
align: "start",
containScroll: "trimSnaps",
slidesToScroll: slidesToScroll,
skipSnaps: false
});
const [canScrollPrev, setCanScrollPrev] = useState(false);
const [canScrollNext, setCanScrollNext] = useState(false);
const scrollPrev = useCallback(() => {
if (emblaApi) emblaApi.scrollPrev();
}, [emblaApi]);
const scrollNext = useCallback(() => {
if (emblaApi) emblaApi.scrollNext();
}, [emblaApi]);
const onSelect = useCallback(() => {
if (!emblaApi) return;
setCanScrollPrev(emblaApi.canScrollPrev());
setCanScrollNext(emblaApi.canScrollNext());
}, [emblaApi]);
useEffect(() => {
if (!emblaApi) return;
onSelect();
emblaApi.on("select", onSelect);
emblaApi.on("reInit", onSelect);
return () => {
emblaApi.off("select", onSelect);
emblaApi.off("reInit", onSelect);
};
}, [emblaApi, onSelect]);
// 监听窗口大小变化,动态调整 slidesToScroll
useEffect(() => {
const handleResize = () => {
const newSlidesToScroll = getSlidesToScroll();
if (newSlidesToScroll !== slidesToScroll) {
setSlidesToScroll(newSlidesToScroll);
// 重新初始化 Embla
if (emblaApi) {
emblaApi.reInit();
}
}
};
window.addEventListener('resize', handleResize);
return () => window.removeEventListener('resize', handleResize);
}, [slidesToScroll, emblaApi]);
return (
<AudioPlayerProvider>
<div className="mt-12">
{/* 标题栏 */}
<div className="flex items-center gap-2 mb-6">
<h2 className="txt-headline-s">🎲 Start Chatting</h2>
</div>
{/* 轮播容器 */}
<div className="relative">
<div className="overflow-hidden" ref={emblaRef}>
{isLoading ? (
<StartChatSkeleton />
) : (
<div className="flex gap-6">
{displayData.map((character) => (
<StartChatItem
key={character.aiId}
character={character}
/>
))}
</div>
)}
</div>
{/* 左侧箭头按钮 */}
{!isLoading && canScrollPrev && (
<IconButton
onClick={scrollPrev}
variant="tertiary"
size="large"
iconfont="icon-arrow-left-border"
className="absolute left-0 top-1/2 -translate-y-1/2 -translate-x-1/2 z-10"
/>
)}
{/* 右侧箭头按钮 */}
{!isLoading && canScrollNext && (
<IconButton
onClick={scrollNext}
variant="tertiary"
size="large"
iconfont="icon-arrow-right-border"
className="absolute right-0 top-1/2 -translate-y-1/2 translate-x-1/2 z-10"
/>
)}
</div>
</div>
</AudioPlayerProvider>
);
};
export default StartChat;