@dynamic-scroll/core 1.2.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.d.mts +346 -0
- package/dist/index.d.ts +346 -0
- package/dist/index.js +893 -0
- package/dist/index.js.map +1 -0
- package/dist/index.mjs +880 -0
- package/dist/index.mjs.map +1 -0
- package/package.json +64 -0
package/dist/index.d.mts
ADDED
|
@@ -0,0 +1,346 @@
|
|
|
1
|
+
import * as react from 'react';
|
|
2
|
+
import { ReactNode } from 'react';
|
|
3
|
+
import * as react_jsx_runtime from 'react/jsx-runtime';
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* 가상 스크롤에 사용되는 아이템의 기본 인터페이스.
|
|
7
|
+
* 모든 아이템은 고유한 id를 가져야 한다.
|
|
8
|
+
*/
|
|
9
|
+
interface VirtualScrollItem {
|
|
10
|
+
id: string;
|
|
11
|
+
}
|
|
12
|
+
/**
|
|
13
|
+
* 가시 영역 계산 결과.
|
|
14
|
+
* useScrollState 훅이 반환하는 값.
|
|
15
|
+
*/
|
|
16
|
+
interface ScrollState {
|
|
17
|
+
/** 실제 뷰포트에 보이는 첫 번째 노드 인덱스 (renderAhead 미적용) */
|
|
18
|
+
firstVisibleNode: number;
|
|
19
|
+
/** 실제 뷰포트에 보이는 마지막 노드 인덱스 (renderAhead 미적용) */
|
|
20
|
+
lastVisibleNode: number;
|
|
21
|
+
/** 렌더링할 시작 인덱스 (renderAhead 적용) */
|
|
22
|
+
startNode: number;
|
|
23
|
+
/** 렌더링할 끝 인덱스 (renderAhead 적용) */
|
|
24
|
+
endNode: number;
|
|
25
|
+
/** 렌더링할 총 노드 수 */
|
|
26
|
+
visibleNodeCount: number;
|
|
27
|
+
}
|
|
28
|
+
/**
|
|
29
|
+
* 외부에 노출되는 imperative API.
|
|
30
|
+
* ref를 통해 스크롤 제어를 할 수 있다.
|
|
31
|
+
*/
|
|
32
|
+
interface DynamicScrollHandle {
|
|
33
|
+
/** 특정 인덱스의 아이템으로 스크롤 이동 */
|
|
34
|
+
scrollToItem: (index: number, align?: ScrollAlign) => void;
|
|
35
|
+
/** 하단으로 스크롤 이동 */
|
|
36
|
+
scrollToBottom: (behavior?: ScrollBehavior) => void;
|
|
37
|
+
/** 특정 offset으로 스크롤 이동 */
|
|
38
|
+
scrollToOffset: (offset: number, behavior?: ScrollBehavior) => void;
|
|
39
|
+
/** 현재 스크롤 offset 반환 */
|
|
40
|
+
getScrollOffset: () => number;
|
|
41
|
+
}
|
|
42
|
+
/** scrollToItem의 정렬 옵션 */
|
|
43
|
+
type ScrollAlign = "start" | "center" | "end";
|
|
44
|
+
/**
|
|
45
|
+
* 초기 스크롤 위치 설정.
|
|
46
|
+
* - "top": 최상단에서 시작
|
|
47
|
+
* - "bottom": 최하단에서 시작 (기본값)
|
|
48
|
+
* - { index, align? }: 특정 아이템 위치에서 시작 (예: 마지막으로 읽은 메시지)
|
|
49
|
+
*/
|
|
50
|
+
type InitialScrollPosition = "top" | "bottom" | {
|
|
51
|
+
index: number;
|
|
52
|
+
align?: ScrollAlign;
|
|
53
|
+
};
|
|
54
|
+
/**
|
|
55
|
+
* DynamicScroll 컴포넌트의 props.
|
|
56
|
+
*/
|
|
57
|
+
interface DynamicScrollProps<T extends VirtualScrollItem> {
|
|
58
|
+
/** 렌더링할 아이템 배열 */
|
|
59
|
+
items: T[];
|
|
60
|
+
/** 아이템 렌더링 함수 */
|
|
61
|
+
renderItem: (item: T, index: number) => ReactNode;
|
|
62
|
+
/** 뷰포트 전후로 추가 렌더링할 아이템 수 (기본값: 8) */
|
|
63
|
+
overscanCount?: number;
|
|
64
|
+
/** 추정 아이템 높이. 사전 측정을 건너뛰고 이 값을 기본값으로 사용 (fallback 옵션) */
|
|
65
|
+
estimatedItemSize?: number;
|
|
66
|
+
/** 상단 도달 시 호출되는 콜백 (이전 데이터 로드) */
|
|
67
|
+
onStartReached?: () => void | Promise<void>;
|
|
68
|
+
/** 하단 도달 시 호출되는 콜백 (다음 데이터 로드) */
|
|
69
|
+
onEndReached?: () => void | Promise<void>;
|
|
70
|
+
/** 상단/하단 도달 감지 임계값 (px). 기본값: 0 */
|
|
71
|
+
threshold?: number;
|
|
72
|
+
/** 하단 고정 상태 변경 시 호출되는 콜백 */
|
|
73
|
+
onAtBottomChange?: (isAtBottom: boolean) => void;
|
|
74
|
+
/** flushSync로 스크롤 업데이트를 동기 처리할지 여부 (기본값: false) */
|
|
75
|
+
syncScrollUpdates?: boolean;
|
|
76
|
+
/** 스크롤 컨테이너에 적용할 className */
|
|
77
|
+
className?: string;
|
|
78
|
+
/** 스크롤 컨테이너에 적용할 style */
|
|
79
|
+
style?: React.CSSProperties;
|
|
80
|
+
/** 그룹 분류 함수. 아이템을 그룹핑할 키를 반환 */
|
|
81
|
+
groupBy?: (item: T) => string;
|
|
82
|
+
/** sticky 그룹 헤더 렌더링 함수. 스크롤 시 상단에 떠다니는 날짜 텍스트 등 */
|
|
83
|
+
renderGroupHeader?: (groupKey: string) => ReactNode;
|
|
84
|
+
/** 그룹 구분선 렌더링 함수. 각 그룹 첫 번째에 삽입되는 수평선. 일반 아이템처럼 높이가 측정됨 */
|
|
85
|
+
renderGroupSeparator?: (groupKey: string) => ReactNode;
|
|
86
|
+
/** 상단 로딩 중 표시할 컴포넌트 */
|
|
87
|
+
loadingComponent?: ReactNode;
|
|
88
|
+
/** 하단 로딩 중 표시할 컴포넌트 */
|
|
89
|
+
bottomLoadingComponent?: ReactNode;
|
|
90
|
+
/** 초기 스크롤 위치. 기본값: "bottom" */
|
|
91
|
+
initialScrollPosition?: InitialScrollPosition;
|
|
92
|
+
/** 새 아이템의 높이 측정이 완료되었을 때 호출되는 콜백. 리마운트 없이 scrollToItem 등을 안전하게 호출할 수 있는 시점. */
|
|
93
|
+
onMeasurementComplete?: () => void;
|
|
94
|
+
/** 초기 높이 측정 중 표시할 로딩 컴포넌트 */
|
|
95
|
+
initialLoadingComponent?: ReactNode;
|
|
96
|
+
}
|
|
97
|
+
/**
|
|
98
|
+
* 저수준 VirtualScroll 컴포넌트의 props.
|
|
99
|
+
* 높이 측정이 이미 완료된 상태에서 사용한다.
|
|
100
|
+
*/
|
|
101
|
+
interface VirtualScrollProps<T extends VirtualScrollItem> {
|
|
102
|
+
/** 렌더링할 아이템 배열 */
|
|
103
|
+
items: T[];
|
|
104
|
+
/** 아이템 렌더링 함수 */
|
|
105
|
+
renderItem: (item: T, index: number) => ReactNode;
|
|
106
|
+
/** 각 아이템의 누적 top 위치 배열 */
|
|
107
|
+
childPositions: number[];
|
|
108
|
+
/** 전체 컨텐츠 높이 */
|
|
109
|
+
totalHeight: number;
|
|
110
|
+
/** 높이 맵 (ref) */
|
|
111
|
+
heightMapRef: React.RefObject<Map<string, number>>;
|
|
112
|
+
/** 높이 변경 시 호출되는 콜백 */
|
|
113
|
+
onHeightChange: (id: string, height: number) => void;
|
|
114
|
+
/** 뷰포트 전후로 추가 렌더링할 아이템 수 */
|
|
115
|
+
overscanCount?: number;
|
|
116
|
+
/** 상단 도달 시 호출되는 콜백 */
|
|
117
|
+
onStartReached?: () => void | Promise<void>;
|
|
118
|
+
/** 하단 도달 시 호출되는 콜백 */
|
|
119
|
+
onEndReached?: () => void | Promise<void>;
|
|
120
|
+
/** 상단/하단 도달 감지 임계값 (px) */
|
|
121
|
+
threshold?: number;
|
|
122
|
+
/** 하단 고정 상태 변경 시 호출되는 콜백 */
|
|
123
|
+
onAtBottomChange?: (isAtBottom: boolean) => void;
|
|
124
|
+
/** flushSync로 스크롤 업데이트를 동기 처리할지 여부 */
|
|
125
|
+
syncScrollUpdates?: boolean;
|
|
126
|
+
/** 스크롤 컨테이너에 적용할 className */
|
|
127
|
+
className?: string;
|
|
128
|
+
/** 스크롤 컨테이너에 적용할 style */
|
|
129
|
+
style?: React.CSSProperties;
|
|
130
|
+
/** 백그라운드 측정 진행 중 여부 (DynamicScroll 내부용) */
|
|
131
|
+
isMeasuring?: boolean;
|
|
132
|
+
/** 그룹 정보 (GroupWrapper 렌더링용) */
|
|
133
|
+
groupInfo?: {
|
|
134
|
+
heightByGroup: Map<string, number>;
|
|
135
|
+
groupKeyByIndex: string[];
|
|
136
|
+
groupStartPositions: Map<string, number>;
|
|
137
|
+
} | null;
|
|
138
|
+
/** sticky 그룹 헤더 렌더링 함수 */
|
|
139
|
+
renderGroupHeader?: (groupKey: string) => ReactNode;
|
|
140
|
+
/** 상단 로딩 중 표시할 컴포넌트 */
|
|
141
|
+
loadingComponent?: ReactNode;
|
|
142
|
+
/** 하단 로딩 중 표시할 컴포넌트 */
|
|
143
|
+
bottomLoadingComponent?: ReactNode;
|
|
144
|
+
/** 초기 스크롤 위치. 기본값: "bottom" */
|
|
145
|
+
initialScrollPosition?: InitialScrollPosition;
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
declare const DynamicScroll: <T extends VirtualScrollItem>(props: DynamicScrollProps<T> & {
|
|
149
|
+
ref?: React.Ref<DynamicScrollHandle>;
|
|
150
|
+
}) => React.ReactElement | null;
|
|
151
|
+
|
|
152
|
+
declare const VirtualScroll: <T extends VirtualScrollItem>(props: VirtualScrollProps<T> & {
|
|
153
|
+
ref?: React.Ref<DynamicScrollHandle>;
|
|
154
|
+
}) => React.ReactElement | null;
|
|
155
|
+
|
|
156
|
+
interface MeasureProps {
|
|
157
|
+
children: React.ReactNode;
|
|
158
|
+
/** 아이템 고유 식별자 */
|
|
159
|
+
itemId: string;
|
|
160
|
+
/** absolute top 위치 (px) */
|
|
161
|
+
position: number;
|
|
162
|
+
/** 높이 변경 시 호출되는 콜백 */
|
|
163
|
+
onHeightChange: (id: string, height: number) => void;
|
|
164
|
+
/** 사전 측정된 높이 (InitialMeasure에서 측정한 값). 깜빡임 방지용 높이 잠금에 사용 */
|
|
165
|
+
knownHeight?: number;
|
|
166
|
+
}
|
|
167
|
+
/**
|
|
168
|
+
* 가상 스크롤 내 각 아이템을 감싸는 컴포넌트.
|
|
169
|
+
*
|
|
170
|
+
* 높이 잠금 메커니즘:
|
|
171
|
+
* 1. knownHeight가 있으면 height style로 잠금 (이미지 재로드 중 깜빡임 방지)
|
|
172
|
+
* 2. MutationObserver로 내부 DOM 변경 감지 (이미지 로드 등)
|
|
173
|
+
* 3. 변경 감지 → height style 제거 (자연 리플로우)
|
|
174
|
+
* 4. ResizeObserver로 새 높이 감지 → onHeightChange 호출
|
|
175
|
+
*/
|
|
176
|
+
declare function MeasureInner({ children, itemId, position, onHeightChange, knownHeight, }: MeasureProps): react_jsx_runtime.JSX.Element;
|
|
177
|
+
declare const Measure: react.MemoExoticComponent<typeof MeasureInner>;
|
|
178
|
+
|
|
179
|
+
interface InitialMeasureProps {
|
|
180
|
+
children: React.ReactNode;
|
|
181
|
+
itemId: string;
|
|
182
|
+
onMeasured: (id: string, height: number) => void;
|
|
183
|
+
}
|
|
184
|
+
/**
|
|
185
|
+
* 사전 높이 측정용 컴포넌트.
|
|
186
|
+
* visibility: hidden으로 렌더링하여 실제 높이를 측정한다.
|
|
187
|
+
* 이미지가 있으면 onload를 대기한다.
|
|
188
|
+
*/
|
|
189
|
+
declare function InitialMeasure({ children, itemId, onMeasured, }: InitialMeasureProps): react_jsx_runtime.JSX.Element;
|
|
190
|
+
|
|
191
|
+
interface StickyGroupHeaderProps {
|
|
192
|
+
/** 현재 표시할 그룹 키 */
|
|
193
|
+
groupKey: string;
|
|
194
|
+
/** 그룹 헤더 렌더링 함수 */
|
|
195
|
+
renderGroupHeader: (groupKey: string) => ReactNode;
|
|
196
|
+
/** 전체 컨텐츠 높이 */
|
|
197
|
+
totalHeight: number;
|
|
198
|
+
/** 해당 그룹 아래 모든 그룹의 누적 높이 */
|
|
199
|
+
cumulativeHeight: number;
|
|
200
|
+
/** 상단 오프셋 (px) */
|
|
201
|
+
topOffset?: number;
|
|
202
|
+
}
|
|
203
|
+
/**
|
|
204
|
+
* Sticky Group Header 컴포넌트.
|
|
205
|
+
*
|
|
206
|
+
* 가상 스크롤에서 CSS position:sticky가 동작하지 않는 문제를 해결한다.
|
|
207
|
+
* (absolute positioned 아이템들과 sticky는 호환되지 않음)
|
|
208
|
+
*
|
|
209
|
+
* 구현 방식:
|
|
210
|
+
* - position: sticky + top: 0으로 고정
|
|
211
|
+
* - height를 totalHeight - cumulativeHeight로 제한하여
|
|
212
|
+
* 해당 그룹 영역이 끝나면 자연스럽게 밀려 올라가는 push-up 효과 구현
|
|
213
|
+
* - z-index로 아이템들 위에 표시
|
|
214
|
+
*/
|
|
215
|
+
declare function StickyGroupHeader({ groupKey, renderGroupHeader, totalHeight, cumulativeHeight, topOffset, }: StickyGroupHeaderProps): react_jsx_runtime.JSX.Element;
|
|
216
|
+
|
|
217
|
+
interface UseScrollStateParams {
|
|
218
|
+
/** 현재 스크롤 위치 */
|
|
219
|
+
scrollTop: number;
|
|
220
|
+
/** 전체 아이템 수 */
|
|
221
|
+
itemCount: number;
|
|
222
|
+
/** 뷰포트 전후로 추가 렌더링할 아이템 수 (기본값: 8) */
|
|
223
|
+
overscanCount?: number;
|
|
224
|
+
/** 뷰포트 높이 */
|
|
225
|
+
viewportHeight: number;
|
|
226
|
+
/** 각 아이템의 누적 top 위치 배열 */
|
|
227
|
+
childPositions: number[];
|
|
228
|
+
}
|
|
229
|
+
/**
|
|
230
|
+
* scrollTop과 childPositions를 기반으로 가시 영역의 노드 범위를 계산하는 훅.
|
|
231
|
+
*
|
|
232
|
+
* - generateStartNodeIndex: 이진탐색 O(log n)으로 시작 노드 계산
|
|
233
|
+
* - generateEndNodeIndex: 이진탐색 O(log n)으로 끝 노드 계산
|
|
234
|
+
* - overscanCount만큼 전후로 버퍼를 추가하여 스크롤 시 빈 영역 방지
|
|
235
|
+
*/
|
|
236
|
+
declare function useScrollState({ scrollTop, itemCount, overscanCount, viewportHeight, childPositions, }: UseScrollStateParams): ScrollState;
|
|
237
|
+
|
|
238
|
+
interface UsePositionsParams<T extends VirtualScrollItem> {
|
|
239
|
+
/** 아이템 배열 */
|
|
240
|
+
items: T[];
|
|
241
|
+
/** 높이 맵 ref (useHeightMap에서 제공) */
|
|
242
|
+
heightMapRef: React.RefObject<Map<string, number>>;
|
|
243
|
+
/** heightMap 변경 시 재계산 트리거용 버전 (useHeightMap에서 제공) */
|
|
244
|
+
version: number;
|
|
245
|
+
/** 추정 아이템 높이 (미측정 아이템의 fallback) */
|
|
246
|
+
estimatedItemSize?: number;
|
|
247
|
+
}
|
|
248
|
+
interface UsePositionsReturn {
|
|
249
|
+
/** 각 아이템의 누적 top 위치 배열. positions[i] = i번째 아이템의 top. positions[length] = totalHeight */
|
|
250
|
+
childPositions: number[];
|
|
251
|
+
/** 전체 컨텐츠 높이 */
|
|
252
|
+
totalHeight: number;
|
|
253
|
+
}
|
|
254
|
+
/**
|
|
255
|
+
* heightMapRef + version을 기반으로 childPositions를 계산하는 훅.
|
|
256
|
+
*
|
|
257
|
+
* - heightMapRef는 useHeightMap에서 관리하는 ref를 그대로 받는다
|
|
258
|
+
* - version이 변경될 때마다 useMemo가 재계산된다
|
|
259
|
+
* - 미측정 아이템은 estimatedItemSize 또는 0으로 처리
|
|
260
|
+
*/
|
|
261
|
+
declare function usePositions<T extends VirtualScrollItem>({ items, heightMapRef, version, estimatedItemSize, }: UsePositionsParams<T>): UsePositionsReturn;
|
|
262
|
+
|
|
263
|
+
interface UseHeightMapParams<T extends VirtualScrollItem> {
|
|
264
|
+
items: T[];
|
|
265
|
+
estimatedItemSize?: number;
|
|
266
|
+
}
|
|
267
|
+
interface UseHeightMapReturn {
|
|
268
|
+
heightMapRef: React.RefObject<Map<string, number>>;
|
|
269
|
+
isAllMeasured: boolean;
|
|
270
|
+
unmeasuredIds: string[];
|
|
271
|
+
onItemMeasured: (id: string, height: number) => void;
|
|
272
|
+
onHeightChange: (id: string, height: number) => void;
|
|
273
|
+
version: number;
|
|
274
|
+
}
|
|
275
|
+
/**
|
|
276
|
+
* 아이템 높이 맵을 관리하는 훅.
|
|
277
|
+
*
|
|
278
|
+
* - heightMap은 useRef<Map>으로 저장
|
|
279
|
+
* - InitialMeasure: pendingIds Set으로 미측정 추적, 0이 되면 1번 리렌더
|
|
280
|
+
* - ResizeObserver: rAF 배치
|
|
281
|
+
*/
|
|
282
|
+
declare function useHeightMap<T extends VirtualScrollItem>({ items, estimatedItemSize, }: UseHeightMapParams<T>): UseHeightMapReturn;
|
|
283
|
+
|
|
284
|
+
interface GroupInfo {
|
|
285
|
+
/** 그룹 키 → 해당 그룹의 전체 높이 */
|
|
286
|
+
heightByGroup: Map<string, number>;
|
|
287
|
+
/** 그룹 키 → 해당 그룹 아래 모든 그룹의 누적 높이 (sticky 헤더 height 제한용) */
|
|
288
|
+
cumulativeHeightByGroup: Map<string, number>;
|
|
289
|
+
/** 인덱스 → 해당 아이템이 속한 그룹 키 */
|
|
290
|
+
groupKeyByIndex: string[];
|
|
291
|
+
}
|
|
292
|
+
/**
|
|
293
|
+
* 그룹별 높이 정보를 계산하는 훅.
|
|
294
|
+
*
|
|
295
|
+
* sticky group header의 height 제한에 사용된다.
|
|
296
|
+
* cumulativeHeightByGroup은 최신 그룹부터 0, 이전 그룹일수록 아래 그룹들의 누적 높이.
|
|
297
|
+
*
|
|
298
|
+
* @param items - 아이템 배열
|
|
299
|
+
* @param groupBy - 아이템을 그룹핑할 키를 반환하는 함수
|
|
300
|
+
* @param heightMapRef - 높이 맵 ref
|
|
301
|
+
* @param version - heightMap 변경 트리거용 버전
|
|
302
|
+
*/
|
|
303
|
+
declare function useGroupPositions<T extends VirtualScrollItem>(items: T[], groupBy: ((item: T) => string) | undefined, heightMapRef: React.RefObject<Map<string, number>>, version: number): GroupInfo | null;
|
|
304
|
+
|
|
305
|
+
/**
|
|
306
|
+
* 이진 탐색으로 scrollTop에 해당하는 첫 번째 가시 노드 인덱스를 찾는다.
|
|
307
|
+
*
|
|
308
|
+
* nodePositions[i]는 i번째 아이템의 top 위치 (누적합 배열).
|
|
309
|
+
* 노드의 중앙점(nodeCenter)과 scrollTop을 비교하여
|
|
310
|
+
* 현재 뷰포트 최상단에 위치한 노드를 O(log n)으로 찾는다.
|
|
311
|
+
*
|
|
312
|
+
* @param scrollTop - 현재 스크롤 위치
|
|
313
|
+
* @param nodePositions - 누적 위치 배열 (길이: itemCount + 1)
|
|
314
|
+
* @param itemCount - 전체 아이템 수
|
|
315
|
+
* @returns 첫 번째 가시 노드의 인덱스
|
|
316
|
+
*/
|
|
317
|
+
declare function generateStartNodeIndex(scrollTop: number, nodePositions: number[], itemCount: number): number;
|
|
318
|
+
/**
|
|
319
|
+
* 이진 탐색으로 뷰포트를 넘어서는 마지막 가시 노드 인덱스를 찾는다.
|
|
320
|
+
*
|
|
321
|
+
* 기존 구현은 선형 탐색 O(k)였으나, 이진 탐색 O(log n)으로 개선.
|
|
322
|
+
* startNodeIndex의 다음 노드 위치를 기준으로 viewportHeight를 더한
|
|
323
|
+
* 경계값을 초과하는 첫 번째 노드를 찾는다.
|
|
324
|
+
*
|
|
325
|
+
* @param nodePositions - 누적 위치 배열
|
|
326
|
+
* @param startNodeIndex - 첫 번째 가시 노드 인덱스 (renderAhead 미적용)
|
|
327
|
+
* @param itemCount - 전체 아이템 수
|
|
328
|
+
* @param viewportHeight - 뷰포트 높이
|
|
329
|
+
* @returns 마지막 가시 노드의 인덱스
|
|
330
|
+
*/
|
|
331
|
+
declare function generateEndNodeIndex(nodePositions: number[], startNodeIndex: number, itemCount: number, viewportHeight: number): number;
|
|
332
|
+
/**
|
|
333
|
+
* 이진 탐색으로 타겟 아이템을 뷰포트 중앙에 배치하기 위한 시작 노드 인덱스를 찾는다.
|
|
334
|
+
*
|
|
335
|
+
* 타겟 아이템의 중심이 뷰포트 중앙(viewportHeight / 2)에 오도록 하는
|
|
336
|
+
* scrollTop 위치의 첫 번째 가시 노드를 찾는다.
|
|
337
|
+
*
|
|
338
|
+
* @param nodePositions - 누적 위치 배열
|
|
339
|
+
* @param targetRowHeight - 타겟 아이템의 높이
|
|
340
|
+
* @param targetRowIndex - 타겟 아이템의 인덱스
|
|
341
|
+
* @param viewportHeight - 뷰포트 높이
|
|
342
|
+
* @returns 시작 노드 인덱스
|
|
343
|
+
*/
|
|
344
|
+
declare function generateCenteredStartNodeIndex(nodePositions: number[], targetRowHeight: number, targetRowIndex: number, viewportHeight: number): number;
|
|
345
|
+
|
|
346
|
+
export { DynamicScroll, type DynamicScrollHandle, type DynamicScrollProps, InitialMeasure, type InitialScrollPosition, Measure, type ScrollAlign, type ScrollState, StickyGroupHeader, VirtualScroll, type VirtualScrollItem, type VirtualScrollProps, generateCenteredStartNodeIndex, generateEndNodeIndex, generateStartNodeIndex, useGroupPositions, useHeightMap, usePositions, useScrollState };
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,346 @@
|
|
|
1
|
+
import * as react from 'react';
|
|
2
|
+
import { ReactNode } from 'react';
|
|
3
|
+
import * as react_jsx_runtime from 'react/jsx-runtime';
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* 가상 스크롤에 사용되는 아이템의 기본 인터페이스.
|
|
7
|
+
* 모든 아이템은 고유한 id를 가져야 한다.
|
|
8
|
+
*/
|
|
9
|
+
interface VirtualScrollItem {
|
|
10
|
+
id: string;
|
|
11
|
+
}
|
|
12
|
+
/**
|
|
13
|
+
* 가시 영역 계산 결과.
|
|
14
|
+
* useScrollState 훅이 반환하는 값.
|
|
15
|
+
*/
|
|
16
|
+
interface ScrollState {
|
|
17
|
+
/** 실제 뷰포트에 보이는 첫 번째 노드 인덱스 (renderAhead 미적용) */
|
|
18
|
+
firstVisibleNode: number;
|
|
19
|
+
/** 실제 뷰포트에 보이는 마지막 노드 인덱스 (renderAhead 미적용) */
|
|
20
|
+
lastVisibleNode: number;
|
|
21
|
+
/** 렌더링할 시작 인덱스 (renderAhead 적용) */
|
|
22
|
+
startNode: number;
|
|
23
|
+
/** 렌더링할 끝 인덱스 (renderAhead 적용) */
|
|
24
|
+
endNode: number;
|
|
25
|
+
/** 렌더링할 총 노드 수 */
|
|
26
|
+
visibleNodeCount: number;
|
|
27
|
+
}
|
|
28
|
+
/**
|
|
29
|
+
* 외부에 노출되는 imperative API.
|
|
30
|
+
* ref를 통해 스크롤 제어를 할 수 있다.
|
|
31
|
+
*/
|
|
32
|
+
interface DynamicScrollHandle {
|
|
33
|
+
/** 특정 인덱스의 아이템으로 스크롤 이동 */
|
|
34
|
+
scrollToItem: (index: number, align?: ScrollAlign) => void;
|
|
35
|
+
/** 하단으로 스크롤 이동 */
|
|
36
|
+
scrollToBottom: (behavior?: ScrollBehavior) => void;
|
|
37
|
+
/** 특정 offset으로 스크롤 이동 */
|
|
38
|
+
scrollToOffset: (offset: number, behavior?: ScrollBehavior) => void;
|
|
39
|
+
/** 현재 스크롤 offset 반환 */
|
|
40
|
+
getScrollOffset: () => number;
|
|
41
|
+
}
|
|
42
|
+
/** scrollToItem의 정렬 옵션 */
|
|
43
|
+
type ScrollAlign = "start" | "center" | "end";
|
|
44
|
+
/**
|
|
45
|
+
* 초기 스크롤 위치 설정.
|
|
46
|
+
* - "top": 최상단에서 시작
|
|
47
|
+
* - "bottom": 최하단에서 시작 (기본값)
|
|
48
|
+
* - { index, align? }: 특정 아이템 위치에서 시작 (예: 마지막으로 읽은 메시지)
|
|
49
|
+
*/
|
|
50
|
+
type InitialScrollPosition = "top" | "bottom" | {
|
|
51
|
+
index: number;
|
|
52
|
+
align?: ScrollAlign;
|
|
53
|
+
};
|
|
54
|
+
/**
|
|
55
|
+
* DynamicScroll 컴포넌트의 props.
|
|
56
|
+
*/
|
|
57
|
+
interface DynamicScrollProps<T extends VirtualScrollItem> {
|
|
58
|
+
/** 렌더링할 아이템 배열 */
|
|
59
|
+
items: T[];
|
|
60
|
+
/** 아이템 렌더링 함수 */
|
|
61
|
+
renderItem: (item: T, index: number) => ReactNode;
|
|
62
|
+
/** 뷰포트 전후로 추가 렌더링할 아이템 수 (기본값: 8) */
|
|
63
|
+
overscanCount?: number;
|
|
64
|
+
/** 추정 아이템 높이. 사전 측정을 건너뛰고 이 값을 기본값으로 사용 (fallback 옵션) */
|
|
65
|
+
estimatedItemSize?: number;
|
|
66
|
+
/** 상단 도달 시 호출되는 콜백 (이전 데이터 로드) */
|
|
67
|
+
onStartReached?: () => void | Promise<void>;
|
|
68
|
+
/** 하단 도달 시 호출되는 콜백 (다음 데이터 로드) */
|
|
69
|
+
onEndReached?: () => void | Promise<void>;
|
|
70
|
+
/** 상단/하단 도달 감지 임계값 (px). 기본값: 0 */
|
|
71
|
+
threshold?: number;
|
|
72
|
+
/** 하단 고정 상태 변경 시 호출되는 콜백 */
|
|
73
|
+
onAtBottomChange?: (isAtBottom: boolean) => void;
|
|
74
|
+
/** flushSync로 스크롤 업데이트를 동기 처리할지 여부 (기본값: false) */
|
|
75
|
+
syncScrollUpdates?: boolean;
|
|
76
|
+
/** 스크롤 컨테이너에 적용할 className */
|
|
77
|
+
className?: string;
|
|
78
|
+
/** 스크롤 컨테이너에 적용할 style */
|
|
79
|
+
style?: React.CSSProperties;
|
|
80
|
+
/** 그룹 분류 함수. 아이템을 그룹핑할 키를 반환 */
|
|
81
|
+
groupBy?: (item: T) => string;
|
|
82
|
+
/** sticky 그룹 헤더 렌더링 함수. 스크롤 시 상단에 떠다니는 날짜 텍스트 등 */
|
|
83
|
+
renderGroupHeader?: (groupKey: string) => ReactNode;
|
|
84
|
+
/** 그룹 구분선 렌더링 함수. 각 그룹 첫 번째에 삽입되는 수평선. 일반 아이템처럼 높이가 측정됨 */
|
|
85
|
+
renderGroupSeparator?: (groupKey: string) => ReactNode;
|
|
86
|
+
/** 상단 로딩 중 표시할 컴포넌트 */
|
|
87
|
+
loadingComponent?: ReactNode;
|
|
88
|
+
/** 하단 로딩 중 표시할 컴포넌트 */
|
|
89
|
+
bottomLoadingComponent?: ReactNode;
|
|
90
|
+
/** 초기 스크롤 위치. 기본값: "bottom" */
|
|
91
|
+
initialScrollPosition?: InitialScrollPosition;
|
|
92
|
+
/** 새 아이템의 높이 측정이 완료되었을 때 호출되는 콜백. 리마운트 없이 scrollToItem 등을 안전하게 호출할 수 있는 시점. */
|
|
93
|
+
onMeasurementComplete?: () => void;
|
|
94
|
+
/** 초기 높이 측정 중 표시할 로딩 컴포넌트 */
|
|
95
|
+
initialLoadingComponent?: ReactNode;
|
|
96
|
+
}
|
|
97
|
+
/**
|
|
98
|
+
* 저수준 VirtualScroll 컴포넌트의 props.
|
|
99
|
+
* 높이 측정이 이미 완료된 상태에서 사용한다.
|
|
100
|
+
*/
|
|
101
|
+
interface VirtualScrollProps<T extends VirtualScrollItem> {
|
|
102
|
+
/** 렌더링할 아이템 배열 */
|
|
103
|
+
items: T[];
|
|
104
|
+
/** 아이템 렌더링 함수 */
|
|
105
|
+
renderItem: (item: T, index: number) => ReactNode;
|
|
106
|
+
/** 각 아이템의 누적 top 위치 배열 */
|
|
107
|
+
childPositions: number[];
|
|
108
|
+
/** 전체 컨텐츠 높이 */
|
|
109
|
+
totalHeight: number;
|
|
110
|
+
/** 높이 맵 (ref) */
|
|
111
|
+
heightMapRef: React.RefObject<Map<string, number>>;
|
|
112
|
+
/** 높이 변경 시 호출되는 콜백 */
|
|
113
|
+
onHeightChange: (id: string, height: number) => void;
|
|
114
|
+
/** 뷰포트 전후로 추가 렌더링할 아이템 수 */
|
|
115
|
+
overscanCount?: number;
|
|
116
|
+
/** 상단 도달 시 호출되는 콜백 */
|
|
117
|
+
onStartReached?: () => void | Promise<void>;
|
|
118
|
+
/** 하단 도달 시 호출되는 콜백 */
|
|
119
|
+
onEndReached?: () => void | Promise<void>;
|
|
120
|
+
/** 상단/하단 도달 감지 임계값 (px) */
|
|
121
|
+
threshold?: number;
|
|
122
|
+
/** 하단 고정 상태 변경 시 호출되는 콜백 */
|
|
123
|
+
onAtBottomChange?: (isAtBottom: boolean) => void;
|
|
124
|
+
/** flushSync로 스크롤 업데이트를 동기 처리할지 여부 */
|
|
125
|
+
syncScrollUpdates?: boolean;
|
|
126
|
+
/** 스크롤 컨테이너에 적용할 className */
|
|
127
|
+
className?: string;
|
|
128
|
+
/** 스크롤 컨테이너에 적용할 style */
|
|
129
|
+
style?: React.CSSProperties;
|
|
130
|
+
/** 백그라운드 측정 진행 중 여부 (DynamicScroll 내부용) */
|
|
131
|
+
isMeasuring?: boolean;
|
|
132
|
+
/** 그룹 정보 (GroupWrapper 렌더링용) */
|
|
133
|
+
groupInfo?: {
|
|
134
|
+
heightByGroup: Map<string, number>;
|
|
135
|
+
groupKeyByIndex: string[];
|
|
136
|
+
groupStartPositions: Map<string, number>;
|
|
137
|
+
} | null;
|
|
138
|
+
/** sticky 그룹 헤더 렌더링 함수 */
|
|
139
|
+
renderGroupHeader?: (groupKey: string) => ReactNode;
|
|
140
|
+
/** 상단 로딩 중 표시할 컴포넌트 */
|
|
141
|
+
loadingComponent?: ReactNode;
|
|
142
|
+
/** 하단 로딩 중 표시할 컴포넌트 */
|
|
143
|
+
bottomLoadingComponent?: ReactNode;
|
|
144
|
+
/** 초기 스크롤 위치. 기본값: "bottom" */
|
|
145
|
+
initialScrollPosition?: InitialScrollPosition;
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
declare const DynamicScroll: <T extends VirtualScrollItem>(props: DynamicScrollProps<T> & {
|
|
149
|
+
ref?: React.Ref<DynamicScrollHandle>;
|
|
150
|
+
}) => React.ReactElement | null;
|
|
151
|
+
|
|
152
|
+
declare const VirtualScroll: <T extends VirtualScrollItem>(props: VirtualScrollProps<T> & {
|
|
153
|
+
ref?: React.Ref<DynamicScrollHandle>;
|
|
154
|
+
}) => React.ReactElement | null;
|
|
155
|
+
|
|
156
|
+
interface MeasureProps {
|
|
157
|
+
children: React.ReactNode;
|
|
158
|
+
/** 아이템 고유 식별자 */
|
|
159
|
+
itemId: string;
|
|
160
|
+
/** absolute top 위치 (px) */
|
|
161
|
+
position: number;
|
|
162
|
+
/** 높이 변경 시 호출되는 콜백 */
|
|
163
|
+
onHeightChange: (id: string, height: number) => void;
|
|
164
|
+
/** 사전 측정된 높이 (InitialMeasure에서 측정한 값). 깜빡임 방지용 높이 잠금에 사용 */
|
|
165
|
+
knownHeight?: number;
|
|
166
|
+
}
|
|
167
|
+
/**
|
|
168
|
+
* 가상 스크롤 내 각 아이템을 감싸는 컴포넌트.
|
|
169
|
+
*
|
|
170
|
+
* 높이 잠금 메커니즘:
|
|
171
|
+
* 1. knownHeight가 있으면 height style로 잠금 (이미지 재로드 중 깜빡임 방지)
|
|
172
|
+
* 2. MutationObserver로 내부 DOM 변경 감지 (이미지 로드 등)
|
|
173
|
+
* 3. 변경 감지 → height style 제거 (자연 리플로우)
|
|
174
|
+
* 4. ResizeObserver로 새 높이 감지 → onHeightChange 호출
|
|
175
|
+
*/
|
|
176
|
+
declare function MeasureInner({ children, itemId, position, onHeightChange, knownHeight, }: MeasureProps): react_jsx_runtime.JSX.Element;
|
|
177
|
+
declare const Measure: react.MemoExoticComponent<typeof MeasureInner>;
|
|
178
|
+
|
|
179
|
+
interface InitialMeasureProps {
|
|
180
|
+
children: React.ReactNode;
|
|
181
|
+
itemId: string;
|
|
182
|
+
onMeasured: (id: string, height: number) => void;
|
|
183
|
+
}
|
|
184
|
+
/**
|
|
185
|
+
* 사전 높이 측정용 컴포넌트.
|
|
186
|
+
* visibility: hidden으로 렌더링하여 실제 높이를 측정한다.
|
|
187
|
+
* 이미지가 있으면 onload를 대기한다.
|
|
188
|
+
*/
|
|
189
|
+
declare function InitialMeasure({ children, itemId, onMeasured, }: InitialMeasureProps): react_jsx_runtime.JSX.Element;
|
|
190
|
+
|
|
191
|
+
interface StickyGroupHeaderProps {
|
|
192
|
+
/** 현재 표시할 그룹 키 */
|
|
193
|
+
groupKey: string;
|
|
194
|
+
/** 그룹 헤더 렌더링 함수 */
|
|
195
|
+
renderGroupHeader: (groupKey: string) => ReactNode;
|
|
196
|
+
/** 전체 컨텐츠 높이 */
|
|
197
|
+
totalHeight: number;
|
|
198
|
+
/** 해당 그룹 아래 모든 그룹의 누적 높이 */
|
|
199
|
+
cumulativeHeight: number;
|
|
200
|
+
/** 상단 오프셋 (px) */
|
|
201
|
+
topOffset?: number;
|
|
202
|
+
}
|
|
203
|
+
/**
|
|
204
|
+
* Sticky Group Header 컴포넌트.
|
|
205
|
+
*
|
|
206
|
+
* 가상 스크롤에서 CSS position:sticky가 동작하지 않는 문제를 해결한다.
|
|
207
|
+
* (absolute positioned 아이템들과 sticky는 호환되지 않음)
|
|
208
|
+
*
|
|
209
|
+
* 구현 방식:
|
|
210
|
+
* - position: sticky + top: 0으로 고정
|
|
211
|
+
* - height를 totalHeight - cumulativeHeight로 제한하여
|
|
212
|
+
* 해당 그룹 영역이 끝나면 자연스럽게 밀려 올라가는 push-up 효과 구현
|
|
213
|
+
* - z-index로 아이템들 위에 표시
|
|
214
|
+
*/
|
|
215
|
+
declare function StickyGroupHeader({ groupKey, renderGroupHeader, totalHeight, cumulativeHeight, topOffset, }: StickyGroupHeaderProps): react_jsx_runtime.JSX.Element;
|
|
216
|
+
|
|
217
|
+
interface UseScrollStateParams {
|
|
218
|
+
/** 현재 스크롤 위치 */
|
|
219
|
+
scrollTop: number;
|
|
220
|
+
/** 전체 아이템 수 */
|
|
221
|
+
itemCount: number;
|
|
222
|
+
/** 뷰포트 전후로 추가 렌더링할 아이템 수 (기본값: 8) */
|
|
223
|
+
overscanCount?: number;
|
|
224
|
+
/** 뷰포트 높이 */
|
|
225
|
+
viewportHeight: number;
|
|
226
|
+
/** 각 아이템의 누적 top 위치 배열 */
|
|
227
|
+
childPositions: number[];
|
|
228
|
+
}
|
|
229
|
+
/**
|
|
230
|
+
* scrollTop과 childPositions를 기반으로 가시 영역의 노드 범위를 계산하는 훅.
|
|
231
|
+
*
|
|
232
|
+
* - generateStartNodeIndex: 이진탐색 O(log n)으로 시작 노드 계산
|
|
233
|
+
* - generateEndNodeIndex: 이진탐색 O(log n)으로 끝 노드 계산
|
|
234
|
+
* - overscanCount만큼 전후로 버퍼를 추가하여 스크롤 시 빈 영역 방지
|
|
235
|
+
*/
|
|
236
|
+
declare function useScrollState({ scrollTop, itemCount, overscanCount, viewportHeight, childPositions, }: UseScrollStateParams): ScrollState;
|
|
237
|
+
|
|
238
|
+
interface UsePositionsParams<T extends VirtualScrollItem> {
|
|
239
|
+
/** 아이템 배열 */
|
|
240
|
+
items: T[];
|
|
241
|
+
/** 높이 맵 ref (useHeightMap에서 제공) */
|
|
242
|
+
heightMapRef: React.RefObject<Map<string, number>>;
|
|
243
|
+
/** heightMap 변경 시 재계산 트리거용 버전 (useHeightMap에서 제공) */
|
|
244
|
+
version: number;
|
|
245
|
+
/** 추정 아이템 높이 (미측정 아이템의 fallback) */
|
|
246
|
+
estimatedItemSize?: number;
|
|
247
|
+
}
|
|
248
|
+
interface UsePositionsReturn {
|
|
249
|
+
/** 각 아이템의 누적 top 위치 배열. positions[i] = i번째 아이템의 top. positions[length] = totalHeight */
|
|
250
|
+
childPositions: number[];
|
|
251
|
+
/** 전체 컨텐츠 높이 */
|
|
252
|
+
totalHeight: number;
|
|
253
|
+
}
|
|
254
|
+
/**
|
|
255
|
+
* heightMapRef + version을 기반으로 childPositions를 계산하는 훅.
|
|
256
|
+
*
|
|
257
|
+
* - heightMapRef는 useHeightMap에서 관리하는 ref를 그대로 받는다
|
|
258
|
+
* - version이 변경될 때마다 useMemo가 재계산된다
|
|
259
|
+
* - 미측정 아이템은 estimatedItemSize 또는 0으로 처리
|
|
260
|
+
*/
|
|
261
|
+
declare function usePositions<T extends VirtualScrollItem>({ items, heightMapRef, version, estimatedItemSize, }: UsePositionsParams<T>): UsePositionsReturn;
|
|
262
|
+
|
|
263
|
+
interface UseHeightMapParams<T extends VirtualScrollItem> {
|
|
264
|
+
items: T[];
|
|
265
|
+
estimatedItemSize?: number;
|
|
266
|
+
}
|
|
267
|
+
interface UseHeightMapReturn {
|
|
268
|
+
heightMapRef: React.RefObject<Map<string, number>>;
|
|
269
|
+
isAllMeasured: boolean;
|
|
270
|
+
unmeasuredIds: string[];
|
|
271
|
+
onItemMeasured: (id: string, height: number) => void;
|
|
272
|
+
onHeightChange: (id: string, height: number) => void;
|
|
273
|
+
version: number;
|
|
274
|
+
}
|
|
275
|
+
/**
|
|
276
|
+
* 아이템 높이 맵을 관리하는 훅.
|
|
277
|
+
*
|
|
278
|
+
* - heightMap은 useRef<Map>으로 저장
|
|
279
|
+
* - InitialMeasure: pendingIds Set으로 미측정 추적, 0이 되면 1번 리렌더
|
|
280
|
+
* - ResizeObserver: rAF 배치
|
|
281
|
+
*/
|
|
282
|
+
declare function useHeightMap<T extends VirtualScrollItem>({ items, estimatedItemSize, }: UseHeightMapParams<T>): UseHeightMapReturn;
|
|
283
|
+
|
|
284
|
+
interface GroupInfo {
|
|
285
|
+
/** 그룹 키 → 해당 그룹의 전체 높이 */
|
|
286
|
+
heightByGroup: Map<string, number>;
|
|
287
|
+
/** 그룹 키 → 해당 그룹 아래 모든 그룹의 누적 높이 (sticky 헤더 height 제한용) */
|
|
288
|
+
cumulativeHeightByGroup: Map<string, number>;
|
|
289
|
+
/** 인덱스 → 해당 아이템이 속한 그룹 키 */
|
|
290
|
+
groupKeyByIndex: string[];
|
|
291
|
+
}
|
|
292
|
+
/**
|
|
293
|
+
* 그룹별 높이 정보를 계산하는 훅.
|
|
294
|
+
*
|
|
295
|
+
* sticky group header의 height 제한에 사용된다.
|
|
296
|
+
* cumulativeHeightByGroup은 최신 그룹부터 0, 이전 그룹일수록 아래 그룹들의 누적 높이.
|
|
297
|
+
*
|
|
298
|
+
* @param items - 아이템 배열
|
|
299
|
+
* @param groupBy - 아이템을 그룹핑할 키를 반환하는 함수
|
|
300
|
+
* @param heightMapRef - 높이 맵 ref
|
|
301
|
+
* @param version - heightMap 변경 트리거용 버전
|
|
302
|
+
*/
|
|
303
|
+
declare function useGroupPositions<T extends VirtualScrollItem>(items: T[], groupBy: ((item: T) => string) | undefined, heightMapRef: React.RefObject<Map<string, number>>, version: number): GroupInfo | null;
|
|
304
|
+
|
|
305
|
+
/**
|
|
306
|
+
* 이진 탐색으로 scrollTop에 해당하는 첫 번째 가시 노드 인덱스를 찾는다.
|
|
307
|
+
*
|
|
308
|
+
* nodePositions[i]는 i번째 아이템의 top 위치 (누적합 배열).
|
|
309
|
+
* 노드의 중앙점(nodeCenter)과 scrollTop을 비교하여
|
|
310
|
+
* 현재 뷰포트 최상단에 위치한 노드를 O(log n)으로 찾는다.
|
|
311
|
+
*
|
|
312
|
+
* @param scrollTop - 현재 스크롤 위치
|
|
313
|
+
* @param nodePositions - 누적 위치 배열 (길이: itemCount + 1)
|
|
314
|
+
* @param itemCount - 전체 아이템 수
|
|
315
|
+
* @returns 첫 번째 가시 노드의 인덱스
|
|
316
|
+
*/
|
|
317
|
+
declare function generateStartNodeIndex(scrollTop: number, nodePositions: number[], itemCount: number): number;
|
|
318
|
+
/**
|
|
319
|
+
* 이진 탐색으로 뷰포트를 넘어서는 마지막 가시 노드 인덱스를 찾는다.
|
|
320
|
+
*
|
|
321
|
+
* 기존 구현은 선형 탐색 O(k)였으나, 이진 탐색 O(log n)으로 개선.
|
|
322
|
+
* startNodeIndex의 다음 노드 위치를 기준으로 viewportHeight를 더한
|
|
323
|
+
* 경계값을 초과하는 첫 번째 노드를 찾는다.
|
|
324
|
+
*
|
|
325
|
+
* @param nodePositions - 누적 위치 배열
|
|
326
|
+
* @param startNodeIndex - 첫 번째 가시 노드 인덱스 (renderAhead 미적용)
|
|
327
|
+
* @param itemCount - 전체 아이템 수
|
|
328
|
+
* @param viewportHeight - 뷰포트 높이
|
|
329
|
+
* @returns 마지막 가시 노드의 인덱스
|
|
330
|
+
*/
|
|
331
|
+
declare function generateEndNodeIndex(nodePositions: number[], startNodeIndex: number, itemCount: number, viewportHeight: number): number;
|
|
332
|
+
/**
|
|
333
|
+
* 이진 탐색으로 타겟 아이템을 뷰포트 중앙에 배치하기 위한 시작 노드 인덱스를 찾는다.
|
|
334
|
+
*
|
|
335
|
+
* 타겟 아이템의 중심이 뷰포트 중앙(viewportHeight / 2)에 오도록 하는
|
|
336
|
+
* scrollTop 위치의 첫 번째 가시 노드를 찾는다.
|
|
337
|
+
*
|
|
338
|
+
* @param nodePositions - 누적 위치 배열
|
|
339
|
+
* @param targetRowHeight - 타겟 아이템의 높이
|
|
340
|
+
* @param targetRowIndex - 타겟 아이템의 인덱스
|
|
341
|
+
* @param viewportHeight - 뷰포트 높이
|
|
342
|
+
* @returns 시작 노드 인덱스
|
|
343
|
+
*/
|
|
344
|
+
declare function generateCenteredStartNodeIndex(nodePositions: number[], targetRowHeight: number, targetRowIndex: number, viewportHeight: number): number;
|
|
345
|
+
|
|
346
|
+
export { DynamicScroll, type DynamicScrollHandle, type DynamicScrollProps, InitialMeasure, type InitialScrollPosition, Measure, type ScrollAlign, type ScrollState, StickyGroupHeader, VirtualScroll, type VirtualScrollItem, type VirtualScrollProps, generateCenteredStartNodeIndex, generateEndNodeIndex, generateStartNodeIndex, useGroupPositions, useHeightMap, usePositions, useScrollState };
|