feat: 가상 스크롤 동적 높이 지원

- measureElement와 data-index 사용으로 각 아이템 실제 높이 측정
- 고정 높이(h-[120px]) 제거하여 내용에 맞게 자동 조절
This commit is contained in:
caadiq 2026-01-10 09:46:38 +09:00
parent 0cab67dfbe
commit d22466ec23

View file

@ -43,7 +43,7 @@ function Schedule() {
const [searchInput, setSearchInput] = useState('');
const [searchTerm, setSearchTerm] = useState('');
const SEARCH_LIMIT = 20; // 20
const ITEM_HEIGHT = 136; // (120px) + (16px)
const ESTIMATED_ITEM_HEIGHT = 120; // ( )
// Intersection Observer for infinite scroll
const { ref: loadMoreRef, inView } = useInView({
@ -289,11 +289,11 @@ function Schedule() {
});
}, [schedules, selectedDate, currentYearMonth, selectedCategories, isSearchMode, searchTerm, searchResults]);
// ( )
// ( , )
const virtualizer = useVirtualizer({
count: isSearchMode && searchTerm ? filteredSchedules.length : 0,
getScrollElement: () => scrollContainerRef.current,
estimateSize: () => ITEM_HEIGHT,
estimateSize: () => ESTIMATED_ITEM_HEIGHT,
overscan: 5, //
});
@ -834,7 +834,7 @@ function Schedule() {
<>
<div
style={{
height: `${virtualizer.getTotalSize() - 16}px`,
height: `${virtualizer.getTotalSize()}px`,
width: '100%',
position: 'relative',
}}
@ -850,19 +850,20 @@ function Schedule() {
return (
<div
key={virtualItem.key}
ref={virtualizer.measureElement}
data-index={virtualItem.index}
style={{
position: 'absolute',
top: 0,
left: 0,
width: '100%',
height: `${virtualItem.size}px`,
transform: `translateY(${virtualItem.start}px)`,
}}
>
<div className={virtualItem.index < filteredSchedules.length - 1 ? "pb-4" : ""}>
<div
onClick={() => handleScheduleClick(schedule)}
className="flex items-stretch bg-white rounded-2xl shadow-sm hover:shadow-md transition-shadow overflow-hidden cursor-pointer h-[120px]"
className="flex items-stretch bg-white rounded-2xl shadow-sm hover:shadow-md transition-shadow overflow-hidden cursor-pointer"
>
{/* 날짜 영역 */}
<div