ERP-node/frontend/lib/registry/pop-components/pop-dashboard/modes/ScrollMode.tsx

91 lines
2.5 KiB
TypeScript
Raw Normal View History

"use client";
/**
*
*
* + CSS scroll-snap으로
*
*/
import React, { useRef, useState, useEffect, useCallback } from "react";
// ===== Props =====
export interface ScrollModeProps {
/** 총 아이템 수 */
itemCount: number;
/** 페이지 인디케이터 표시 여부 */
showIndicator?: boolean;
/** 현재 인덱스에 해당하는 아이템 렌더링 */
renderItem: (index: number) => React.ReactNode;
}
// ===== 메인 컴포넌트 =====
export function ScrollModeComponent({
itemCount,
showIndicator = true,
renderItem,
}: ScrollModeProps) {
const scrollRef = useRef<HTMLDivElement>(null);
const [activeIndex, setActiveIndex] = useState(0);
// 스크롤 위치로 현재 인덱스 계산
const handleScroll = useCallback(() => {
const el = scrollRef.current;
if (!el || !el.clientWidth) return;
const index = Math.round(el.scrollLeft / el.clientWidth);
setActiveIndex(Math.min(index, itemCount - 1));
}, [itemCount]);
useEffect(() => {
const el = scrollRef.current;
if (!el) return;
el.addEventListener("scroll", handleScroll, { passive: true });
return () => el.removeEventListener("scroll", handleScroll);
}, [handleScroll]);
if (itemCount === 0) {
return (
<div className="flex h-full w-full items-center justify-center">
<span className="text-xs text-muted-foreground"> </span>
</div>
);
}
return (
<div className="flex h-full w-full flex-col">
{/* 스크롤 영역 */}
<div
ref={scrollRef}
className="flex min-h-0 flex-1 snap-x snap-mandatory overflow-x-auto scrollbar-none"
>
{Array.from({ length: itemCount }).map((_, i) => (
<div
key={i}
className="h-full w-full shrink-0 snap-center"
>
{renderItem(i)}
</div>
))}
</div>
{/* 페이지 인디케이터 */}
{showIndicator && itemCount > 1 && (
<div className="flex items-center justify-center gap-1.5 py-1">
{Array.from({ length: itemCount }).map((_, i) => (
<span
key={i}
className={`h-1.5 rounded-full transition-all ${
i === activeIndex
? "w-4 bg-primary"
: "w-1.5 bg-muted-foreground/30"
}`}
/>
))}
</div>
)}
</div>
);
}