@rific/scroll-view 0.1.1 → 0.3.0

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.mjs CHANGED
@@ -3,9 +3,10 @@ var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { en
3
3
  var __publicField = (obj, key, value) => __defNormalProp(obj, typeof key !== "symbol" ? key + "" : key, value);
4
4
 
5
5
  // src/CustomList.tsx
6
- import { memo, useCallback as useCallback2, useMemo as useMemo2, useRef as useRef2 } from "react";
6
+ import { memo, useCallback as useCallback2, useContext as useContext4, useLayoutEffect as useLayoutEffect2, useMemo as useMemo2, useRef as useRef2 } from "react";
7
7
  import { Dimensions, View } from "react-native";
8
8
  import { Gesture, GestureDetector } from "react-native-gesture-handler";
9
+ import { runOnUI, useSharedValue as useSharedValue4 } from "react-native-reanimated";
9
10
 
10
11
  // src/internal/RefreshControl.tsx
11
12
  import { RefreshControl as RNRefreshControl } from "react-native";
@@ -13,19 +14,26 @@ import { jsx } from "react/jsx-runtime";
13
14
  var RefreshControl = (props) => /* @__PURE__ */ jsx(RNRefreshControl, { refreshing: false, ...props });
14
15
 
15
16
  // src/internal/ScrollViewChip.tsx
17
+ import { Chip } from "@rific/auto-paper";
16
18
  import { StyleSheet } from "react-native";
17
- import { Chip } from "react-native-paper";
18
19
  import Animated from "react-native-reanimated";
19
20
  import { jsx as jsx2 } from "react/jsx-runtime";
20
- var ScrollViewChip = ({ isHorizontal, onPress, style }) => /* @__PURE__ */ jsx2(Animated.View, { style: [styles.chip, style], children: /* @__PURE__ */ jsx2(Chip, { compact: true, icon: isHorizontal ? "chevron-left" : "chevron-up", onPress, style: styles.chipInner, children: isHorizontal ? "Start" : "Top" }) });
21
+ var ScrollViewChip = ({ chipProps, isHorizontal, onChipPress, onPress, style }) => {
22
+ const { label, style: chipStyle, ...restChipProps } = chipProps ?? {};
23
+ const handlePress = onChipPress ? () => {
24
+ onPress();
25
+ onChipPress();
26
+ } : onPress;
27
+ return /* @__PURE__ */ jsx2(Animated.View, { style: [styles.chip, style], children: /* @__PURE__ */ jsx2(Chip, { compact: true, ...restChipProps, icon: isHorizontal ? "chevron-left" : "chevron-up", onPress: handlePress, style: [styles.chipInner, chipStyle], children: label ?? (isHorizontal ? "Start" : "Top") }) });
28
+ };
21
29
  var styles = StyleSheet.create({
22
30
  chip: { alignItems: "center", left: 0, position: "absolute", right: 0, zIndex: 3 },
23
31
  chipInner: { elevation: 2, shadowColor: "#000", shadowOffset: { width: 0, height: 1 }, shadowOpacity: 0.15, shadowRadius: 2 }
24
32
  });
25
33
 
26
34
  // src/internal/useScrollHandler.ts
27
- import { useContext } from "react";
28
- import { runOnJS, useAnimatedScrollHandler, useSharedValue, withTiming } from "react-native-reanimated";
35
+ import { useContext as useContext2 } from "react";
36
+ import { runOnJS, useAnimatedScrollHandler, useSharedValue as useSharedValue2, withTiming } from "react-native-reanimated";
29
37
 
30
38
  // src/ScrollViewContext.ts
31
39
  import { createContext } from "react";
@@ -35,10 +43,16 @@ var ScrollViewContext = createContext({
35
43
  footerHeightShared: null,
36
44
  footerFixed: false,
37
45
  footerOffset: null,
38
- headerHeight: 0,
46
+ headerHeight: null,
39
47
  headerHeightShared: null,
40
48
  headerFixed: false,
41
49
  headerOffset: null,
50
+ jsListGeneration: { current: 0 },
51
+ listGeneration: null,
52
+ onJsListUnmount: () => {
53
+ },
54
+ onListUnmount: () => {
55
+ },
42
56
  progress: null,
43
57
  progressing: false,
44
58
  pullSearchHeightShared: null,
@@ -57,25 +71,215 @@ var ScrollViewContext = createContext({
57
71
  tabBarHeight: 60
58
72
  });
59
73
 
74
+ // src/useScrollInit.ts
75
+ import { createElement, useCallback, useContext, useEffect, useLayoutEffect, useRef, useState } from "react";
76
+ import { useSharedValue } from "react-native-reanimated";
77
+ var REMOUNT_SYNC_MAX_ATTEMPTS = 8;
78
+ var REMOUNT_SYNC_TOLERANCE = 1;
79
+ var REMOUNT_SYNC_FALLBACK_MS = 500;
80
+ var REMOUNT_PRIME_FRAMES = 40;
81
+ var SNAP_VERIFY_TOLERANCE = 1;
82
+ var SNAP_VERIFY_MAX_ATTEMPTS = 5;
83
+ var SNAP_VERIFY_INITIAL_DELAY_MS = 400;
84
+ var SNAP_VERIFY_RETRY_DELAY_MS = 150;
85
+ function useScrollInit({ listHeaderComponent, onMomentumScrollEnd: externalMomentumScrollEnd, onRefresh, onScrollBeginDrag: externalScrollBeginDrag, onScrollEndDrag: externalScrollEndDrag, pullSearchHeight, scrollTo }) {
86
+ const { headerHeight, scrollPosition, setProgressing } = useContext(ScrollViewContext);
87
+ const hasPullSearch = pullSearchHeight !== void 0;
88
+ const [phase, setPhase] = useState(hasPullSearch && !headerHeight ? "measuring" : "ready");
89
+ const [refreshing, setRefreshing] = useState(false);
90
+ const refreshingRef = useRef(refreshing);
91
+ useEffect(() => {
92
+ refreshingRef.current = refreshing;
93
+ }, [refreshing]);
94
+ const dragStartY = useRef(null);
95
+ const pendingSnapTarget = useRef(null);
96
+ const snapVerifyTimeout = useRef(null);
97
+ const snapVerifyAttempts = useRef(0);
98
+ const scheduleSnapVerification = useCallback(
99
+ (target, delay) => {
100
+ if (snapVerifyTimeout.current !== null) clearTimeout(snapVerifyTimeout.current);
101
+ pendingSnapTarget.current = target;
102
+ snapVerifyAttempts.current = 0;
103
+ const check = () => {
104
+ snapVerifyTimeout.current = null;
105
+ if (pendingSnapTarget.current !== target) return;
106
+ if (Math.abs(scrollPosition.value - target) <= SNAP_VERIFY_TOLERANCE) {
107
+ pendingSnapTarget.current = null;
108
+ return;
109
+ }
110
+ snapVerifyAttempts.current += 1;
111
+ if (snapVerifyAttempts.current > SNAP_VERIFY_MAX_ATTEMPTS || refreshingRef.current) {
112
+ pendingSnapTarget.current = null;
113
+ return;
114
+ }
115
+ scrollTo(target, false);
116
+ snapVerifyTimeout.current = setTimeout(check, SNAP_VERIFY_RETRY_DELAY_MS);
117
+ };
118
+ snapVerifyTimeout.current = setTimeout(check, delay);
119
+ },
120
+ [scrollPosition, scrollTo]
121
+ );
122
+ useEffect(
123
+ () => () => {
124
+ if (snapVerifyTimeout.current !== null) clearTimeout(snapVerifyTimeout.current);
125
+ },
126
+ []
127
+ );
128
+ const [remountTarget] = useState(() => headerHeight !== null ? -headerHeight + (pullSearchHeight ?? 0) : null);
129
+ const remountSyncTarget = useSharedValue(remountTarget);
130
+ const [scrollReady, setScrollReady] = useState(remountTarget === null);
131
+ const remountAttempts = useRef(0);
132
+ const onRemountSynced = useCallback(
133
+ (y) => {
134
+ scrollPosition.value = y;
135
+ setScrollReady(true);
136
+ },
137
+ [scrollPosition]
138
+ );
139
+ const onRemountSyncRetry = useCallback(
140
+ (currentY) => {
141
+ if (remountTarget === null) return;
142
+ remountAttempts.current += 1;
143
+ if (remountAttempts.current > REMOUNT_SYNC_MAX_ATTEMPTS) {
144
+ remountSyncTarget.value = null;
145
+ onRemountSynced(currentY);
146
+ return;
147
+ }
148
+ scrollTo(remountTarget, false);
149
+ },
150
+ [onRemountSynced, remountSyncTarget, remountTarget, scrollTo]
151
+ );
152
+ useEffect(() => {
153
+ if (remountTarget === null) return;
154
+ scrollTo(remountTarget, false);
155
+ const fallback = setTimeout(() => {
156
+ if (remountSyncTarget.value === null) return;
157
+ remountSyncTarget.value = null;
158
+ onRemountSynced(remountTarget);
159
+ }, REMOUNT_SYNC_FALLBACK_MS);
160
+ return () => clearTimeout(fallback);
161
+ }, []);
162
+ useEffect(() => {
163
+ if (phase === "ready" || !headerHeight || !pullSearchHeight) return;
164
+ scrollPosition.value = -headerHeight + pullSearchHeight;
165
+ setPhase("ready");
166
+ }, [phase, headerHeight, pullSearchHeight, scrollPosition]);
167
+ const headerInitialized = useRef(false);
168
+ useLayoutEffect(() => {
169
+ if (!headerHeight || headerInitialized.current) return;
170
+ headerInitialized.current = true;
171
+ const target = remountTarget ?? -headerHeight;
172
+ scrollPosition.value = target;
173
+ scrollTo(target, false);
174
+ if (remountTarget === null) return;
175
+ let framesLeft = REMOUNT_PRIME_FRAMES;
176
+ let handle = null;
177
+ const tick = () => {
178
+ const offset = framesLeft === 1 ? target : framesLeft % 2 === 0 ? target + 30 : target - 30;
179
+ scrollTo(offset, false);
180
+ framesLeft -= 1;
181
+ if (framesLeft > 0) handle = requestAnimationFrame(tick);
182
+ };
183
+ handle = requestAnimationFrame(tick);
184
+ return () => {
185
+ if (handle !== null) cancelAnimationFrame(handle);
186
+ };
187
+ }, [remountTarget, headerHeight, scrollPosition, scrollTo]);
188
+ const pullSearchInitialized = useRef(false);
189
+ useLayoutEffect(() => {
190
+ if (remountTarget !== null || !hasPullSearch || phase !== "ready" || pullSearchInitialized.current || !headerHeight || !pullSearchHeight || !scrollReady) return;
191
+ pullSearchInitialized.current = true;
192
+ scrollPosition.value = -headerHeight + pullSearchHeight;
193
+ scrollTo(-headerHeight + pullSearchHeight, false);
194
+ }, [remountTarget, hasPullSearch, phase, headerHeight, pullSearchHeight, scrollPosition, scrollReady, scrollTo]);
195
+ useEffect(() => {
196
+ if (pullSearchHeight) setProgressing(refreshing);
197
+ }, [pullSearchHeight, refreshing, setProgressing]);
198
+ const handleScrollBeginDrag = useCallback(
199
+ (e) => {
200
+ if (snapVerifyTimeout.current !== null) {
201
+ clearTimeout(snapVerifyTimeout.current);
202
+ snapVerifyTimeout.current = null;
203
+ }
204
+ pendingSnapTarget.current = null;
205
+ if (pullSearchHeight) dragStartY.current = e.nativeEvent.contentOffset.y;
206
+ externalScrollBeginDrag?.(e);
207
+ },
208
+ [externalScrollBeginDrag, pullSearchHeight]
209
+ );
210
+ const handleScrollEndDrag = useCallback(
211
+ (e) => {
212
+ if (pullSearchHeight && headerHeight) {
213
+ const y = e.nativeEvent.contentOffset.y;
214
+ const showY = -headerHeight;
215
+ const hideY = -headerHeight + pullSearchHeight;
216
+ const startY = dragStartY.current;
217
+ dragStartY.current = null;
218
+ const pulledDown = (startY ?? hideY) <= hideY;
219
+ const dragDistance = startY !== null ? startY - y : 0;
220
+ if (pulledDown && dragDistance >= pullSearchHeight * 0.5) {
221
+ if (y <= showY && !refreshing && onRefresh) {
222
+ setRefreshing(true);
223
+ Promise.resolve(onRefresh()).finally(() => setRefreshing(false));
224
+ }
225
+ scrollTo(showY, true);
226
+ scheduleSnapVerification(showY, SNAP_VERIFY_INITIAL_DELAY_MS);
227
+ } else if (y < hideY) {
228
+ scrollTo(hideY, true);
229
+ scheduleSnapVerification(hideY, SNAP_VERIFY_INITIAL_DELAY_MS);
230
+ }
231
+ }
232
+ externalScrollEndDrag?.(e);
233
+ },
234
+ [externalScrollEndDrag, headerHeight, onRefresh, pullSearchHeight, refreshing, scheduleSnapVerification, scrollTo]
235
+ );
236
+ const handleMomentumScrollEnd = useCallback(
237
+ (e) => {
238
+ if (pullSearchHeight && headerHeight && !refreshing && pendingSnapTarget.current === null) {
239
+ const y = e.nativeEvent.contentOffset.y;
240
+ const hideY = -headerHeight + pullSearchHeight;
241
+ if (y < hideY - 1) {
242
+ scrollTo(hideY, true);
243
+ scheduleSnapVerification(hideY, SNAP_VERIFY_INITIAL_DELAY_MS);
244
+ }
245
+ }
246
+ externalMomentumScrollEnd?.(e);
247
+ },
248
+ [externalMomentumScrollEnd, headerHeight, pullSearchHeight, refreshing, scheduleSnapVerification, scrollTo]
249
+ );
250
+ const activeListHeader = hasPullSearch && phase === "measuring" ? null : listHeaderComponent;
251
+ const hiddenHeader = phase === "measuring" && listHeaderComponent != null ? typeof listHeaderComponent === "function" ? createElement(listHeaderComponent) : listHeaderComponent : null;
252
+ return { activeListHeader, handleMomentumScrollEnd, handleScrollBeginDrag, handleScrollEndDrag, hiddenHeader, onRemountSyncRetry, onRemountSynced, pullSearchMinHeight: pullSearchHeight ?? 0, remountSyncTarget, remountTarget, scrollReady };
253
+ }
254
+
60
255
  // src/internal/useScrollHandler.ts
61
- function useScrollHandler({ chipHidden, footerFixed, headerFixed, isHorizontal, onPullSearchZoneEnter }) {
62
- const { footerHeightShared, footerOffset, headerHeightShared, headerOffset, pullSearchHeightShared, scrollPosition, snapBackFooterShared, snapBackHeaderShared } = useContext(ScrollViewContext);
63
- const snapUpAccum = useSharedValue(0);
64
- const fromBottomBounce = useSharedValue(false);
65
- const isMomentum = useSharedValue(false);
66
- const momentumPullSearchFired = useSharedValue(false);
256
+ function useScrollHandler({ capturedGeneration, chipHidden, chipThreshold = 100, footerFixed, headerFixed, isHorizontal, listGeneration, onRemountSyncRetry, onRemountSynced, remountSyncTarget }) {
257
+ const { footerHeightShared, footerOffset, headerHeightShared, headerOffset, pullSearchHeightShared, scrollPosition, snapBackFooterShared, snapBackHeaderShared } = useContext2(ScrollViewContext);
258
+ const snapUpAccum = useSharedValue2(0);
259
+ const fromBottomBounce = useSharedValue2(false);
67
260
  return useAnimatedScrollHandler(
68
261
  {
69
262
  onScroll: ({ contentOffset: { x, y }, contentSize: { height: contentHeight }, layoutMeasurement: { height: layoutHeight } }) => {
70
263
  "worklet";
264
+ if (remountSyncTarget && remountSyncTarget.value !== null) {
265
+ const target = remountSyncTarget.value;
266
+ if (Math.abs(y - target) < REMOUNT_SYNC_TOLERANCE) {
267
+ remountSyncTarget.value = null;
268
+ if (onRemountSynced) runOnJS(onRemountSynced)(y);
269
+ } else if (onRemountSyncRetry) {
270
+ runOnJS(onRemountSyncRetry)(y);
271
+ }
272
+ return;
273
+ }
274
+ if (listGeneration.value !== capturedGeneration.value) return;
71
275
  if (isHorizontal) {
72
276
  scrollPosition.value = x;
73
- chipHidden.value = x < 100 ? 1 : 0;
277
+ chipHidden.value = x < chipThreshold ? 1 : 0;
74
278
  return;
75
279
  }
76
280
  const delta = y - scrollPosition.value;
77
281
  scrollPosition.value = y;
78
- chipHidden.value = y < 100 ? 1 : 0;
282
+ chipHidden.value = y < chipThreshold ? 1 : 0;
79
283
  const maxScroll = contentHeight - layoutHeight;
80
284
  if (maxScroll > 0 && y >= maxScroll) {
81
285
  fromBottomBounce.value = true;
@@ -83,13 +287,6 @@ function useScrollHandler({ chipHidden, footerFixed, headerFixed, isHorizontal,
83
287
  } else if (fromBottomBounce.value) {
84
288
  fromBottomBounce.value = false;
85
289
  }
86
- if (isMomentum.value && pullSearchHeightShared.value > 0 && !momentumPullSearchFired.value) {
87
- const hideY = -headerHeightShared.value + pullSearchHeightShared.value;
88
- if (y < hideY) {
89
- momentumPullSearchFired.value = true;
90
- if (onPullSearchZoneEnter) runOnJS(onPullSearchZoneEnter)();
91
- }
92
- }
93
290
  const snapHeader = snapBackHeaderShared.value && !headerFixed;
94
291
  const snapFooter = snapBackFooterShared.value && !footerFixed;
95
292
  if (snapHeader || snapFooter) {
@@ -115,16 +312,9 @@ function useScrollHandler({ chipHidden, footerFixed, headerFixed, isHorizontal,
115
312
  },
116
313
  onBeginDrag: () => {
117
314
  "worklet";
118
- isMomentum.value = false;
119
- momentumPullSearchFired.value = false;
120
315
  fromBottomBounce.value = false;
121
316
  snapUpAccum.value = 0;
122
317
  },
123
- onMomentumBegin: () => {
124
- "worklet";
125
- isMomentum.value = true;
126
- momentumPullSearchFired.value = false;
127
- },
128
318
  onEndDrag: ({ contentOffset: { y }, contentSize: { height: contentHeight }, layoutMeasurement: { height: layoutHeight } }) => {
129
319
  "worklet";
130
320
  if (isHorizontal) return;
@@ -135,28 +325,26 @@ function useScrollHandler({ chipHidden, footerFixed, headerFixed, isHorizontal,
135
325
  },
136
326
  onMomentumEnd: () => {
137
327
  "worklet";
138
- isMomentum.value = false;
139
- momentumPullSearchFired.value = false;
140
328
  fromBottomBounce.value = false;
141
329
  snapUpAccum.value = 0;
142
330
  }
143
331
  },
144
- [headerFixed, footerFixed, isHorizontal, onPullSearchZoneEnter]
332
+ [capturedGeneration, headerFixed, footerFixed, isHorizontal, chipThreshold, listGeneration, onRemountSyncRetry, onRemountSynced, remountSyncTarget]
145
333
  );
146
334
  }
147
335
 
148
336
  // src/internal/useScrollList.ts
149
- import { useContext as useContext2, useEffect, useMemo } from "react";
337
+ import { useContext as useContext3, useEffect as useEffect2, useMemo, useState as useState3 } from "react";
150
338
  import { StyleSheet as StyleSheet2 } from "react-native";
151
- import { useAnimatedStyle, useSharedValue as useSharedValue2, withTiming as withTiming2 } from "react-native-reanimated";
339
+ import { useAnimatedStyle, useSharedValue as useSharedValue3, withTiming as withTiming2 } from "react-native-reanimated";
152
340
  import { useSafeAreaInsets } from "react-native-safe-area-context";
153
341
 
154
342
  // src/useKeyboardInset.ts
155
- import { useState } from "react";
343
+ import { useState as useState2 } from "react";
156
344
  import { useKeyboardHandler } from "react-native-keyboard-controller";
157
345
  import { runOnJS as runOnJS2 } from "react-native-reanimated";
158
346
  var useKeyboardInset = () => {
159
- const [keyboardHeight, setKeyboardHeight] = useState(0);
347
+ const [keyboardHeight, setKeyboardHeight] = useState2(0);
160
348
  useKeyboardHandler(
161
349
  {
162
350
  onMove: (e) => {
@@ -178,28 +366,30 @@ var CHIP_SLIDE = 48;
178
366
  function useScrollList({ footerFixed: footerFixedProp, headerFixed: headerFixedProp, isHorizontal, keyboardAware, pullSearchHeight, style } = {}) {
179
367
  const insets = useSafeAreaInsets();
180
368
  const keyboardHeight = useKeyboardInset();
181
- const { footerHeight, footerFixed: contextFooterFixed, headerHeight, headerFixed: contextHeaderFixed, headerOffset, pullSearchHeightShared, scrollPosition, snapBackHeaderShared } = useContext2(ScrollViewContext);
369
+ const { footerHeight, footerFixed: contextFooterFixed, headerHeight, headerFixed: contextHeaderFixed, headerOffset, pullSearchHeightShared, scrollPosition, snapBackHeaderShared } = useContext3(ScrollViewContext);
182
370
  const headerFixed = isHorizontal ? true : headerFixedProp ?? contextHeaderFixed;
183
371
  const footerFixed = isHorizontal ? true : footerFixedProp ?? contextFooterFixed;
184
- useEffect(() => {
372
+ useEffect2(() => {
185
373
  pullSearchHeightShared.value = pullSearchHeight ?? 0;
186
374
  }, [pullSearchHeight, pullSearchHeightShared]);
187
- const containerStyle = useMemo(() => headerHeight === 0 ? [{ flex: 1 }, style] : [StyleSheet2.absoluteFill, style], [headerHeight, style]);
188
- const contentInset = useMemo(() => ({ bottom: (footerFixed ? footerHeight || insets.bottom : insets.bottom) + (keyboardAware ? keyboardHeight : 0), top: headerHeight }), [footerFixed, footerHeight, insets.bottom, headerHeight, keyboardAware, keyboardHeight]);
189
- const contentOffset = useMemo(() => ({ x: 0, y: -contentInset.top }), [contentInset.top]);
190
- const chipHidden = useSharedValue2(1);
375
+ const containerStyle = useMemo(() => headerHeight === null ? [{ flex: 1, opacity: 0 }, style] : [StyleSheet2.absoluteFill, style], [headerHeight, style]);
376
+ const contentInset = useMemo(() => ({ bottom: (footerFixed ? footerHeight || insets.bottom : insets.bottom) + (keyboardAware ? keyboardHeight : 0), top: headerHeight ?? 0 }), [footerFixed, footerHeight, insets.bottom, headerHeight, keyboardAware, keyboardHeight]);
377
+ const [startedWithHeader] = useState3(headerHeight !== null);
378
+ const contentOffset = useMemo(() => ({ x: 0, y: headerHeight !== null ? -headerHeight + (startedWithHeader && !isHorizontal && pullSearchHeight ? pullSearchHeight : 0) : 0 }), [headerHeight, isHorizontal, pullSearchHeight, startedWithHeader]);
379
+ const chipHidden = useSharedValue3(1);
191
380
  const chipStyle = useAnimatedStyle(() => {
192
381
  const pointerEvents = chipHidden.value ? "none" : "box-none";
193
382
  if (isHorizontal) {
194
383
  return {
195
384
  opacity: chipHidden.value ? withTiming2(0) : withTiming2(1),
196
385
  pointerEvents,
197
- top: Math.max(headerHeight, insets.top) + 4,
386
+ top: Math.max(headerHeight ?? 0, insets.top) + 4,
198
387
  transform: [{ translateX: chipHidden.value ? withTiming2(-CHIP_SLIDE) : withTiming2(0) }]
199
388
  };
200
389
  }
201
- const slide = headerFixed ? 0 : snapBackHeaderShared.value ? -headerOffset.value : Math.max(0, scrollPosition.value + headerHeight - pullSearchHeightShared.value);
202
- const top = Math.max(headerHeight - slide, insets.top) + 4;
390
+ const h = headerHeight ?? 0;
391
+ const slide = headerFixed ? 0 : snapBackHeaderShared.value ? -headerOffset.value : Math.max(0, scrollPosition.value + h - pullSearchHeightShared.value);
392
+ const top = Math.max(h - slide, insets.top) + 4;
203
393
  return {
204
394
  opacity: chipHidden.value ? withTiming2(0) : withTiming2(1),
205
395
  pointerEvents,
@@ -210,90 +400,21 @@ function useScrollList({ footerFixed: footerFixedProp, headerFixed: headerFixedP
210
400
  return { chipHidden, chipStyle, containerStyle, contentInset, contentOffset, footerFixed, headerFixed, headerHeight, insets, isHorizontal: isHorizontal ?? false };
211
401
  }
212
402
 
213
- // src/useScrollInit.ts
214
- import { createElement, useCallback, useContext as useContext3, useEffect as useEffect2, useLayoutEffect, useRef, useState as useState2 } from "react";
215
- function useScrollInit({ listHeaderComponent, onMomentumScrollEnd: externalMomentumScrollEnd, onRefresh, onScrollBeginDrag: externalScrollBeginDrag, onScrollEndDrag: externalScrollEndDrag, pullSearchHeight, scrollTo }) {
216
- const { headerHeight, scrollPosition, setProgressing } = useContext3(ScrollViewContext);
217
- const hasPullSearch = pullSearchHeight !== void 0;
218
- const [phase, setPhase] = useState2(hasPullSearch ? "measuring" : "ready");
219
- const [refreshing, setRefreshing] = useState2(false);
220
- const dragStartY = useRef(null);
221
- const deliberatelyOpened = useRef(false);
222
- useEffect2(() => {
223
- if (phase === "ready" || !headerHeight || !pullSearchHeight) return;
224
- scrollPosition.value = -headerHeight + pullSearchHeight;
225
- setPhase("ready");
226
- }, [phase, headerHeight, pullSearchHeight, scrollPosition]);
227
- const headerInitialized = useRef(false);
228
- useLayoutEffect(() => {
229
- if (!headerHeight || headerInitialized.current) return;
230
- headerInitialized.current = true;
231
- scrollPosition.value = -headerHeight;
232
- scrollTo(-headerHeight, false);
233
- }, [headerHeight, scrollPosition, scrollTo]);
234
- const pullSearchInitialized = useRef(false);
235
- useLayoutEffect(() => {
236
- if (!hasPullSearch || phase !== "ready" || pullSearchInitialized.current || !headerHeight || !pullSearchHeight) return;
237
- pullSearchInitialized.current = true;
238
- scrollPosition.value = -headerHeight + pullSearchHeight;
239
- scrollTo(-headerHeight + pullSearchHeight, false);
240
- }, [hasPullSearch, phase, headerHeight, pullSearchHeight, scrollPosition, scrollTo]);
241
- useEffect2(() => {
242
- if (pullSearchHeight) setProgressing(refreshing);
243
- }, [pullSearchHeight, refreshing, setProgressing]);
244
- const handleScrollBeginDrag = useCallback(
245
- (e) => {
246
- deliberatelyOpened.current = false;
247
- if (pullSearchHeight) dragStartY.current = e.nativeEvent.contentOffset.y;
248
- externalScrollBeginDrag?.(e);
249
- },
250
- [externalScrollBeginDrag, pullSearchHeight]
251
- );
252
- const handleScrollEndDrag = useCallback(
253
- (e) => {
254
- if (pullSearchHeight && headerHeight) {
255
- const y = e.nativeEvent.contentOffset.y;
256
- const showY = -headerHeight;
257
- const hideY = -headerHeight + pullSearchHeight;
258
- const pulledDown = (dragStartY.current ?? hideY) <= hideY;
259
- dragStartY.current = null;
260
- if (pulledDown && y <= showY + pullSearchHeight * 0.5) {
261
- deliberatelyOpened.current = true;
262
- if (y <= showY && !refreshing && onRefresh) {
263
- setRefreshing(true);
264
- Promise.resolve(onRefresh()).finally(() => setRefreshing(false));
265
- }
266
- scrollTo(showY, true);
267
- } else if (y < hideY) {
268
- scrollTo(hideY, true);
269
- }
270
- }
271
- externalScrollEndDrag?.(e);
272
- },
273
- [externalScrollEndDrag, headerHeight, onRefresh, pullSearchHeight, refreshing, scrollTo]
274
- );
275
- const handleMomentumScrollEnd = useCallback(
276
- (e) => {
277
- if (pullSearchHeight && headerHeight && !deliberatelyOpened.current && !refreshing) {
278
- const y = e.nativeEvent.contentOffset.y;
279
- const hideY = -headerHeight + pullSearchHeight;
280
- if (y < hideY - 1) scrollTo(hideY, true);
281
- }
282
- deliberatelyOpened.current = false;
283
- externalMomentumScrollEnd?.(e);
284
- },
285
- [externalMomentumScrollEnd, headerHeight, pullSearchHeight, refreshing, scrollTo]
286
- );
287
- const activeListHeader = hasPullSearch && phase === "measuring" ? null : listHeaderComponent;
288
- const hiddenHeader = phase === "measuring" && listHeaderComponent != null ? typeof listHeaderComponent === "function" ? createElement(listHeaderComponent) : listHeaderComponent : null;
289
- return { activeListHeader, handleMomentumScrollEnd, handleScrollBeginDrag, handleScrollEndDrag, hiddenHeader, pullSearchMinHeight: pullSearchHeight ?? 0 };
290
- }
291
-
292
403
  // src/CustomList.tsx
293
404
  import { Fragment, jsx as jsx3, jsxs } from "react/jsx-runtime";
294
- var CustomListInner = ({ component: List, renderFilters, footerFixed: footerFixedProp, gesture, headerFixed: headerFixedProp, keyboardAware, onMomentumScrollEnd: externalMomentumScrollEnd, onRefresh, onScrollBeginDrag: externalScrollBeginDrag, onScrollEndDrag: externalScrollEndDrag, pullSearchHeight, scrollRef, style, ...props }) => {
405
+ var CustomListInner = ({ chipProps, chipThreshold, component: List, renderFilters, footerFixed: footerFixedProp, gesture, headerFixed: headerFixedProp, keyboardAware, onChipPress, onMomentumScrollEnd: externalMomentumScrollEnd, onRefresh, onScrollBeginDrag: externalScrollBeginDrag, onScrollEndDrag: externalScrollEndDrag, pullSearchHeight, scrollRef, style, ...props }) => {
295
406
  const { ListHeaderComponent: listHeaderComponent, contentContainerStyle: externalContentContainerStyle, ...restProps } = props;
296
407
  const { chipHidden, chipStyle, containerStyle, contentInset, contentOffset, footerFixed, headerFixed } = useScrollList({ footerFixed: footerFixedProp, headerFixed: headerFixedProp, keyboardAware, pullSearchHeight, style });
408
+ const { listGeneration, onListUnmount } = useContext4(ScrollViewContext);
409
+ const capturedGeneration = useSharedValue4(-1);
410
+ useLayoutEffect2(() => {
411
+ const generation = listGeneration.value;
412
+ runOnUI((gen) => {
413
+ "worklet";
414
+ capturedGeneration.value = gen;
415
+ })(generation);
416
+ }, [capturedGeneration, listGeneration]);
417
+ useLayoutEffect2(() => () => onListUnmount(), [onListUnmount]);
297
418
  const scrollViewInternal = useRef2(null);
298
419
  const handleRef = useCallback2(
299
420
  (el) => {
@@ -311,7 +432,10 @@ var CustomListInner = ({ component: List, renderFilters, footerFixed: footerFixe
311
432
  handleScrollBeginDrag,
312
433
  handleScrollEndDrag,
313
434
  hiddenHeader,
314
- pullSearchMinHeight
435
+ onRemountSyncRetry,
436
+ onRemountSynced,
437
+ pullSearchMinHeight,
438
+ remountSyncTarget
315
439
  } = useScrollInit({
316
440
  listHeaderComponent,
317
441
  onMomentumScrollEnd: externalMomentumScrollEnd,
@@ -330,11 +454,7 @@ var CustomListInner = ({ component: List, renderFilters, footerFixed: footerFixe
330
454
  ] });
331
455
  }, [pullSearchHeader, renderFilters]);
332
456
  const contentContainerStyle = useMemo2(() => [{ minHeight: Dimensions.get("window").height - contentInset.top - contentInset.bottom + pullSearchMinHeight }, externalContentContainerStyle], [contentInset.bottom, contentInset.top, externalContentContainerStyle, pullSearchMinHeight]);
333
- const onPullSearchZoneEnter = useCallback2(() => {
334
- if (!pullSearchHeight) return;
335
- scrollViewInternal.current?.scrollToOffset({ offset: -contentInset.top + pullSearchHeight, animated: true });
336
- }, [contentInset.top, pullSearchHeight]);
337
- const onScroll = useScrollHandler({ chipHidden, footerFixed, headerFixed, onPullSearchZoneEnter: pullSearchHeight ? onPullSearchZoneEnter : void 0 });
457
+ const onScroll = useScrollHandler({ capturedGeneration, chipHidden, chipThreshold, footerFixed, headerFixed, listGeneration, onRemountSyncRetry, onRemountSynced, remountSyncTarget });
338
458
  const handleScrollToTop = useCallback2(() => {
339
459
  const offset = pullSearchHeight ? -contentInset.top + pullSearchHeight : -contentInset.top;
340
460
  scrollViewInternal.current?.scrollToOffset({ offset, animated: true });
@@ -347,7 +467,7 @@ var CustomListInner = ({ component: List, renderFilters, footerFixed: footerFixe
347
467
  const content = /* @__PURE__ */ jsxs(View, { style: containerStyle, children: [
348
468
  /* @__PURE__ */ jsx3(TypedList, { ...restProps, contentContainerStyle, contentInset, contentOffset, ListHeaderComponent: activeListHeader, onMomentumScrollEnd: handleMomentumScrollEnd, onScroll, onScrollBeginDrag: handleScrollBeginDrag, onScrollEndDrag: handleScrollEndDrag, ref: handleRef, refreshControl, scrollEventThrottle: 16 }),
349
469
  hiddenHeader && /* @__PURE__ */ jsx3(View, { pointerEvents: "none", style: styles2.measureContainer, children: hiddenHeader }),
350
- /* @__PURE__ */ jsx3(ScrollViewChip, { onPress: handleScrollToTop, style: chipStyle })
470
+ /* @__PURE__ */ jsx3(ScrollViewChip, { chipProps, onChipPress, onPress: handleScrollToTop, style: chipStyle })
351
471
  ] });
352
472
  return detectorGesture ? /* @__PURE__ */ jsx3(GestureDetector, { gesture: detectorGesture, children: content }) : content;
353
473
  };
@@ -355,14 +475,13 @@ var styles2 = { measureContainer: { left: 0, opacity: 0, position: "absolute", r
355
475
  var CustomList = memo(CustomListInner);
356
476
 
357
477
  // src/FlatList.tsx
358
- import { memo as memo2, useCallback as useCallback3, useMemo as useMemo3, useRef as useRef3 } from "react";
359
- import { Dimensions as Dimensions2, View as View3 } from "react-native";
478
+ import { Fragment as Fragment2, memo as memo2, useCallback as useCallback4, useContext as useContext7, useLayoutEffect as useLayoutEffect3, useMemo as useMemo3, useRef as useRef4 } from "react";
479
+ import { Dimensions as Dimensions2, FlatList as RNFlatList, View as View3 } from "react-native";
360
480
  import { Gesture as Gesture2, GestureDetector as GestureDetector2 } from "react-native-gesture-handler";
361
- import Animated3 from "react-native-reanimated";
362
481
 
363
482
  // src/internal/HorizontalDots.tsx
364
483
  import { BlurView } from "@rific/auto-paper";
365
- import { useContext as useContext4 } from "react";
484
+ import { useContext as useContext5 } from "react";
366
485
  import { StyleSheet as StyleSheet3, useWindowDimensions, View as View2 } from "react-native";
367
486
  import { useTheme } from "react-native-paper";
368
487
  import Animated2, { interpolateColor, useAnimatedStyle as useAnimatedStyle2 } from "react-native-reanimated";
@@ -391,7 +510,7 @@ var Dot = ({ activeColor, index, inactiveColor, pageWidth, scrollPosition }) =>
391
510
  return /* @__PURE__ */ jsx4(View2, { style: styles3.dotContainer, children: /* @__PURE__ */ jsx4(Animated2.View, { style: circleStyle }) });
392
511
  };
393
512
  var HorizontalDots = ({ total }) => {
394
- const { blur, footerHeightShared, scrollPosition } = useContext4(ScrollViewContext);
513
+ const { blur, footerHeightShared, scrollPosition } = useContext5(ScrollViewContext);
395
514
  const { width } = useWindowDimensions();
396
515
  const insets = useSafeAreaInsets2();
397
516
  const theme = useTheme();
@@ -427,14 +546,144 @@ var styles3 = StyleSheet3.create({
427
546
  pillContainer: { alignItems: "center", left: 0, position: "absolute", right: 0, zIndex: 3 }
428
547
  });
429
548
 
549
+ // src/internal/useScrollHandlerJS.ts
550
+ import { useCallback as useCallback3, useContext as useContext6, useRef as useRef3 } from "react";
551
+ import { withTiming as withTiming3 } from "react-native-reanimated";
552
+ var REMOUNT_RETRY_TOLERANCE = 1;
553
+ var REMOUNT_RETRY_MAX_ATTEMPTS = 8;
554
+ function useScrollHandlerJS({ capturedGeneration, chipHidden, chipThreshold = 100, footerFixed, headerFixed, isHorizontal, jsListGeneration, remountTarget = null, scrollTo }) {
555
+ const { footerHeightShared, footerOffset, headerHeightShared, headerOffset, pullSearchHeightShared, scrollPosition, snapBackFooterShared, snapBackHeaderShared } = useContext6(ScrollViewContext);
556
+ const snapUpAccum = useRef3(0);
557
+ const fromBottomBounce = useRef3(false);
558
+ const pendingRemountTarget = useRef3(remountTarget);
559
+ const remountAttempts = useRef3(0);
560
+ const onScroll = useCallback3(
561
+ (event) => {
562
+ if (jsListGeneration.current !== capturedGeneration.current) return;
563
+ const {
564
+ contentOffset: { x, y },
565
+ contentSize: { height: contentHeight },
566
+ layoutMeasurement: { height: layoutHeight }
567
+ } = event.nativeEvent;
568
+ if (pendingRemountTarget.current !== null) {
569
+ const target = pendingRemountTarget.current;
570
+ if (Math.abs(y - target) < REMOUNT_RETRY_TOLERANCE) {
571
+ pendingRemountTarget.current = null;
572
+ } else {
573
+ remountAttempts.current += 1;
574
+ if (remountAttempts.current > REMOUNT_RETRY_MAX_ATTEMPTS) {
575
+ pendingRemountTarget.current = null;
576
+ } else {
577
+ scrollTo?.(target, false);
578
+ return;
579
+ }
580
+ }
581
+ }
582
+ if (isHorizontal) {
583
+ scrollPosition.value = x;
584
+ chipHidden.value = x < chipThreshold ? 1 : 0;
585
+ return;
586
+ }
587
+ const delta = y - scrollPosition.value;
588
+ scrollPosition.value = y;
589
+ chipHidden.value = y < chipThreshold ? 1 : 0;
590
+ const maxScroll = contentHeight - layoutHeight;
591
+ if (maxScroll > 0 && y >= maxScroll) {
592
+ fromBottomBounce.current = true;
593
+ snapUpAccum.current = 0;
594
+ } else if (fromBottomBounce.current) {
595
+ fromBottomBounce.current = false;
596
+ }
597
+ const snapHeader = snapBackHeaderShared.value && !headerFixed;
598
+ const snapFooter = snapBackFooterShared.value && !footerFixed;
599
+ if (snapHeader || snapFooter) {
600
+ if (y <= -headerHeightShared.value) {
601
+ snapUpAccum.current = 0;
602
+ if (snapHeader) headerOffset.value = 0;
603
+ if (snapFooter) footerOffset.value = 0;
604
+ } else if (delta > 0) {
605
+ snapUpAccum.current = 0;
606
+ if (y >= -headerHeightShared.value + pullSearchHeightShared.value) {
607
+ if (snapHeader) headerOffset.value = Math.max(-headerHeightShared.value, Math.min(0, headerOffset.value - delta));
608
+ if (snapFooter) footerOffset.value = Math.max(0, Math.min(footerHeightShared.value, footerOffset.value + delta));
609
+ }
610
+ } else if (delta < 0 && !fromBottomBounce.current) {
611
+ if (y >= -headerHeightShared.value + pullSearchHeightShared.value) snapUpAccum.current -= delta;
612
+ if (snapUpAccum.current >= 10) {
613
+ snapUpAccum.current = 999;
614
+ if (snapHeader) headerOffset.value = withTiming3(0, { duration: 200 });
615
+ if (snapFooter) footerOffset.value = withTiming3(0, { duration: 200 });
616
+ }
617
+ }
618
+ }
619
+ },
620
+ [capturedGeneration, chipHidden, chipThreshold, footerFixed, footerHeightShared, footerOffset, headerFixed, headerHeightShared, headerOffset, isHorizontal, jsListGeneration, pullSearchHeightShared, scrollPosition, scrollTo, snapBackFooterShared, snapBackHeaderShared]
621
+ );
622
+ const onScrollBeginDrag = useCallback3(() => {
623
+ fromBottomBounce.current = false;
624
+ snapUpAccum.current = 0;
625
+ }, []);
626
+ const onMomentumScrollBegin = useCallback3(() => {
627
+ }, []);
628
+ const onScrollEndDrag = useCallback3(
629
+ (event) => {
630
+ if (isHorizontal) return;
631
+ const {
632
+ contentOffset: { y },
633
+ contentSize: { height: contentHeight },
634
+ layoutMeasurement: { height: layoutHeight }
635
+ } = event.nativeEvent;
636
+ if (y >= contentHeight - layoutHeight - 10) {
637
+ fromBottomBounce.current = true;
638
+ snapUpAccum.current = 0;
639
+ }
640
+ },
641
+ [isHorizontal]
642
+ );
643
+ const onMomentumScrollEnd = useCallback3(() => {
644
+ fromBottomBounce.current = false;
645
+ snapUpAccum.current = 0;
646
+ }, []);
647
+ return { onMomentumScrollBegin, onMomentumScrollEnd, onScroll, onScrollBeginDrag, onScrollEndDrag };
648
+ }
649
+
430
650
  // src/FlatList.tsx
431
- import { Fragment as Fragment2, jsx as jsx5, jsxs as jsxs3 } from "react/jsx-runtime";
432
- var FlatListInner = ({ contentContainerStyle: externalContentContainerStyle, data, initialNumToRender = 20, maxToRenderPerBatch = 50, renderFilters, footerFixed: footerFixedProp, gesture, headerFixed: headerFixedProp, horizontal, keyboardAware, ListHeaderComponent: externalListHeaderComponent, onMomentumScrollEnd: externalMomentumScrollEnd, onRefresh, onScrollBeginDrag: externalScrollBeginDrag, onScrollEndDrag: externalScrollEndDrag, pagingEnabled, pullSearchHeight, removeClippedSubviews = false, showsHorizontalScrollIndicator, showsVerticalScrollIndicator, style, onContentSizeChange, ref: externalRef, windowSize = 100, ...props }) => {
433
- const scrollView = useRef3(null);
651
+ import { Fragment as Fragment3, jsx as jsx5, jsxs as jsxs3 } from "react/jsx-runtime";
652
+ var FlatListInner = ({ chipProps, chipThreshold, columnWrapperStyle, contentContainerStyle: externalContentContainerStyle, data, initialNumToRender = 20, keyExtractor, maxToRenderPerBatch = 50, numColumns, renderFilters, renderItem, footerFixed: footerFixedProp, gesture, headerFixed: headerFixedProp, horizontal, keyboardAware, ListHeaderComponent: externalListHeaderComponent, onChipPress, onMomentumScrollEnd: externalMomentumScrollEnd, onRefresh, onScrollBeginDrag: externalScrollBeginDrag, onScrollEndDrag: externalScrollEndDrag, pagingEnabled, pullSearchHeight, removeClippedSubviews = false, showsHorizontalScrollIndicator, showsVerticalScrollIndicator, style, onContentSizeChange, ref: externalRef, windowSize = 100, ...props }) => {
653
+ const scrollView = useRef4(null);
434
654
  const isHorizontal = horizontal === true;
435
655
  const showDots = isHorizontal && pagingEnabled === true && (data?.length ?? 0) > 1;
656
+ const cols = numColumns && numColumns > 1 ? numColumns : 1;
657
+ const rowData = useMemo3(() => {
658
+ if (cols <= 1 || !data) return data;
659
+ const items = Array.from(data);
660
+ const rows = [];
661
+ for (let i = 0; i < items.length; i += cols) rows.push(items.slice(i, i + cols));
662
+ return rows;
663
+ }, [cols, data]);
664
+ const rowKeyExtractor = useCallback4(
665
+ (row, rowIndex) => {
666
+ if (cols <= 1) return keyExtractor ? keyExtractor(row, rowIndex) : defaultKeyExtractor(row, rowIndex);
667
+ return row.map((item, kk) => keyExtractor ? keyExtractor(item, rowIndex * cols + kk) : defaultKeyExtractor(item, rowIndex * cols + kk)).join(":");
668
+ },
669
+ [cols, keyExtractor]
670
+ );
671
+ const rowRenderItem = useCallback4(
672
+ (info) => {
673
+ if (cols <= 1) return renderItem ? renderItem(info) : null;
674
+ const row = info.item;
675
+ return /* @__PURE__ */ jsx5(View3, { style: [styles4.row, columnWrapperStyle], children: row.map((item, kk) => /* @__PURE__ */ jsx5(Fragment2, { children: renderItem ? renderItem({ index: info.index * cols + kk, item, separators: info.separators }) : null }, kk)) });
676
+ },
677
+ [cols, columnWrapperStyle, renderItem]
678
+ );
436
679
  const { chipHidden, chipStyle, containerStyle, contentInset, contentOffset, footerFixed, headerFixed } = useScrollList({ footerFixed: footerFixedProp, headerFixed: headerFixedProp, isHorizontal, keyboardAware, pullSearchHeight, style });
437
- const scrollTo = useCallback3((offset, animated) => {
680
+ const { jsListGeneration, onJsListUnmount } = useContext7(ScrollViewContext);
681
+ const capturedGeneration = useRef4(jsListGeneration.current);
682
+ useLayoutEffect3(() => {
683
+ capturedGeneration.current = jsListGeneration.current;
684
+ }, [jsListGeneration]);
685
+ useLayoutEffect3(() => () => onJsListUnmount(), [onJsListUnmount]);
686
+ const scrollTo = useCallback4((offset, animated) => {
438
687
  scrollView.current?.scrollToOffset({ offset, animated });
439
688
  }, []);
440
689
  const {
@@ -443,7 +692,8 @@ var FlatListInner = ({ contentContainerStyle: externalContentContainerStyle, dat
443
692
  handleScrollBeginDrag,
444
693
  handleScrollEndDrag,
445
694
  hiddenHeader,
446
- pullSearchMinHeight
695
+ pullSearchMinHeight,
696
+ remountTarget
447
697
  } = useScrollInit({
448
698
  listHeaderComponent: externalListHeaderComponent,
449
699
  onMomentumScrollEnd: externalMomentumScrollEnd,
@@ -456,18 +706,35 @@ var FlatListInner = ({ contentContainerStyle: externalContentContainerStyle, dat
456
706
  const activeListHeader = useMemo3(() => {
457
707
  if (!renderFilters) return pullSearchHeader;
458
708
  const Inner = pullSearchHeader;
459
- return () => /* @__PURE__ */ jsxs3(Fragment2, { children: [
709
+ return () => /* @__PURE__ */ jsxs3(Fragment3, { children: [
460
710
  Inner ? typeof Inner === "function" ? /* @__PURE__ */ jsx5(Inner, {}) : Inner : null,
461
711
  renderFilters
462
712
  ] });
463
713
  }, [pullSearchHeader, renderFilters]);
464
714
  const contentContainerStyle = useMemo3(() => isHorizontal ? externalContentContainerStyle : [{ minHeight: Dimensions2.get("window").height - contentInset.top - contentInset.bottom + pullSearchMinHeight }, externalContentContainerStyle], [isHorizontal, contentInset.bottom, contentInset.top, externalContentContainerStyle, pullSearchMinHeight]);
465
- const onPullSearchZoneEnter = useCallback3(() => {
466
- if (!pullSearchHeight) return;
467
- scrollView.current?.scrollToOffset({ offset: -contentInset.top + pullSearchHeight, animated: true });
468
- }, [contentInset.top, pullSearchHeight]);
469
- const handleScroll = useScrollHandler({ chipHidden, footerFixed, headerFixed, isHorizontal, onPullSearchZoneEnter: pullSearchHeight ? onPullSearchZoneEnter : void 0 });
470
- const handleScrollToTop = useCallback3(() => {
715
+ const scrollHandler = useScrollHandlerJS({ capturedGeneration, chipHidden, chipThreshold, footerFixed, headerFixed, isHorizontal, jsListGeneration, remountTarget, scrollTo });
716
+ const onScrollBeginDrag = useCallback4(
717
+ (e) => {
718
+ scrollHandler.onScrollBeginDrag(e);
719
+ handleScrollBeginDrag(e);
720
+ },
721
+ [scrollHandler, handleScrollBeginDrag]
722
+ );
723
+ const onScrollEndDrag = useCallback4(
724
+ (e) => {
725
+ scrollHandler.onScrollEndDrag(e);
726
+ handleScrollEndDrag(e);
727
+ },
728
+ [scrollHandler, handleScrollEndDrag]
729
+ );
730
+ const onMomentumScrollEnd = useCallback4(
731
+ (e) => {
732
+ scrollHandler.onMomentumScrollEnd(e);
733
+ handleMomentumScrollEnd(e);
734
+ },
735
+ [scrollHandler, handleMomentumScrollEnd]
736
+ );
737
+ const handleScrollToTop = useCallback4(() => {
471
738
  if (isHorizontal) {
472
739
  scrollView.current?.scrollToOffset({ offset: 0, animated: true });
473
740
  return;
@@ -475,7 +742,7 @@ var FlatListInner = ({ contentContainerStyle: externalContentContainerStyle, dat
475
742
  const offset = pullSearchHeight ? -contentInset.top + pullSearchHeight : -contentInset.top;
476
743
  scrollView.current?.scrollToOffset({ offset, animated: true });
477
744
  }, [isHorizontal, contentInset.top, pullSearchHeight]);
478
- const handleContentSizeChange = useCallback3(
745
+ const handleContentSizeChange = useCallback4(
479
746
  (w, h) => {
480
747
  if (onContentSizeChange) onContentSizeChange(w, h);
481
748
  },
@@ -487,23 +754,27 @@ var FlatListInner = ({ contentContainerStyle: externalContentContainerStyle, dat
487
754
  const detectorGesture = gesture !== void 0 ? combinedGesture : void 0;
488
755
  const content = /* @__PURE__ */ jsxs3(View3, { style: containerStyle, children: [
489
756
  /* @__PURE__ */ jsx5(
490
- Animated3.FlatList,
757
+ RNFlatList,
491
758
  {
492
759
  ...props,
493
760
  contentContainerStyle,
494
761
  contentInset,
762
+ contentInsetAdjustmentBehavior: "never",
495
763
  contentOffset,
496
- data,
764
+ data: cols > 1 ? rowData : data,
497
765
  horizontal: isHorizontal,
498
766
  initialNumToRender,
767
+ keyExtractor: cols > 1 ? rowKeyExtractor : keyExtractor,
499
768
  ListHeaderComponent: activeListHeader,
500
769
  maxToRenderPerBatch,
501
770
  onContentSizeChange: handleContentSizeChange,
502
- onMomentumScrollEnd: handleMomentumScrollEnd,
503
- onScroll: handleScroll,
504
- onScrollBeginDrag: handleScrollBeginDrag,
505
- onScrollEndDrag: handleScrollEndDrag,
771
+ onMomentumScrollBegin: scrollHandler.onMomentumScrollBegin,
772
+ onMomentumScrollEnd,
773
+ onScroll: scrollHandler.onScroll,
774
+ onScrollBeginDrag,
775
+ onScrollEndDrag,
506
776
  pagingEnabled,
777
+ renderItem: cols > 1 ? rowRenderItem : renderItem,
507
778
  ref: (el) => {
508
779
  if (externalRef) externalRef.current = el;
509
780
  scrollView.current = el;
@@ -517,27 +788,31 @@ var FlatListInner = ({ contentContainerStyle: externalContentContainerStyle, dat
517
788
  }
518
789
  ),
519
790
  hiddenHeader && /* @__PURE__ */ jsx5(View3, { pointerEvents: "none", style: styles4.measureContainer, children: hiddenHeader }),
520
- /* @__PURE__ */ jsx5(ScrollViewChip, { isHorizontal, onPress: handleScrollToTop, style: chipStyle }),
791
+ /* @__PURE__ */ jsx5(ScrollViewChip, { chipProps, isHorizontal, onChipPress, onPress: handleScrollToTop, style: chipStyle }),
521
792
  showDots && /* @__PURE__ */ jsx5(HorizontalDots, { total: data?.length ?? 0 })
522
793
  ] });
523
794
  return detectorGesture ? /* @__PURE__ */ jsx5(GestureDetector2, { gesture: detectorGesture, children: content }) : content;
524
795
  };
525
- var styles4 = { measureContainer: { left: 0, opacity: 0, position: "absolute", right: 0, top: -9999 } };
796
+ var defaultKeyExtractor = (item, index) => {
797
+ const key = item?.key ?? item?.id;
798
+ return key != null ? String(key) : String(index);
799
+ };
800
+ var styles4 = { measureContainer: { left: 0, opacity: 0, position: "absolute", right: 0, top: -9999 }, row: { flexDirection: "row" } };
526
801
  var FlatList = memo2(FlatListInner);
527
802
 
528
803
  // src/PullSearch.tsx
529
- import { forwardRef, useCallback as useCallback4, useContext as useContext5, useEffect as useEffect3, useImperativeHandle, useRef as useRef4, useState as useState3 } from "react";
804
+ import { forwardRef, useCallback as useCallback5, useContext as useContext8, useEffect as useEffect3, useImperativeHandle, useRef as useRef5, useState as useState4 } from "react";
530
805
  import { StyleSheet as StyleSheet4 } from "react-native";
531
806
  import { Searchbar, useTheme as useTheme2 } from "react-native-paper";
532
- import Animated4, { Extrapolation, interpolate, runOnJS as runOnJS3, useAnimatedReaction, useAnimatedStyle as useAnimatedStyle3 } from "react-native-reanimated";
807
+ import Animated3, { Extrapolation, interpolate, runOnJS as runOnJS3, useAnimatedReaction, useAnimatedStyle as useAnimatedStyle3 } from "react-native-reanimated";
533
808
  import { jsx as jsx6 } from "react/jsx-runtime";
534
809
  var PullSearch = forwardRef(function PullSearch2({ debounce, onChangeText, onHeightChange, placeholder, value: propValue }, ref) {
535
- const { headerHeight, scrollPosition } = useContext5(ScrollViewContext);
810
+ const { headerHeight, scrollPosition } = useContext8(ScrollViewContext);
536
811
  const theme = useTheme2();
537
- const inputRef = useRef4(null);
538
- const [barHeight, setBarHeight] = useState3(0);
539
- const [value, setValue] = useState3(propValue ?? "");
540
- const prevPropValueRef = useRef4(propValue);
812
+ const inputRef = useRef5(null);
813
+ const [barHeight, setBarHeight] = useState4(0);
814
+ const [value, setValue] = useState4(propValue ?? "");
815
+ const prevPropValueRef = useRef5(propValue);
541
816
  useEffect3(() => {
542
817
  if (prevPropValueRef.current === propValue) return;
543
818
  prevPropValueRef.current = propValue;
@@ -547,22 +822,22 @@ var PullSearch = forwardRef(function PullSearch2({ debounce, onChangeText, onHei
547
822
  blur: () => inputRef.current?.blur(),
548
823
  focus: () => inputRef.current?.focus()
549
824
  }));
550
- const blurInput = useCallback4(() => inputRef.current?.blur(), []);
825
+ const blurInput = useCallback5(() => inputRef.current?.blur(), []);
551
826
  useAnimatedReaction(
552
827
  () => {
553
828
  if (barHeight === 0) return false;
554
- return scrollPosition.value >= -headerHeight + barHeight;
829
+ return scrollPosition.value >= -(headerHeight ?? 0) + barHeight;
555
830
  },
556
831
  (isHidden, wasHidden) => {
557
832
  if (isHidden && !wasHidden) runOnJS3(blurInput)();
558
833
  },
559
834
  [barHeight, headerHeight, blurInput]
560
835
  );
561
- const handleClear = useCallback4(() => {
836
+ const handleClear = useCallback5(() => {
562
837
  setValue("");
563
838
  onChangeText("");
564
839
  }, [onChangeText]);
565
- const handleLayout = useCallback4(
840
+ const handleLayout = useCallback5(
566
841
  ({
567
842
  nativeEvent: {
568
843
  layout: { height }
@@ -583,11 +858,12 @@ var PullSearch = forwardRef(function PullSearch2({ debounce, onChangeText, onHei
583
858
  }, [debounce, onChangeText, value]);
584
859
  const animatedStyle = useAnimatedStyle3(() => {
585
860
  if (barHeight === 0) return { opacity: 0 };
586
- const showY = -headerHeight;
587
- const hideY = -headerHeight + barHeight;
861
+ const h = headerHeight ?? 0;
862
+ const showY = -h;
863
+ const hideY = -h + barHeight;
588
864
  return { opacity: interpolate(scrollPosition.value, [showY, hideY], [1, 0], Extrapolation.CLAMP) };
589
865
  }, [barHeight, headerHeight]);
590
- return /* @__PURE__ */ jsx6(Animated4.View, { onLayout: handleLayout, style: [styles5.container, animatedStyle], children: /* @__PURE__ */ jsx6(Searchbar, { autoCorrect: false, onChangeText: (text) => setValue((text || "").trimStart()), onClearIconPress: handleClear, placeholder, placeholderTextColor: theme.colors.onSurfaceVariant, ref: inputRef, spellCheck: false, style: styles5.input, value }) });
866
+ return /* @__PURE__ */ jsx6(Animated3.View, { onLayout: handleLayout, style: [styles5.container, animatedStyle], children: /* @__PURE__ */ jsx6(Searchbar, { autoCorrect: false, onChangeText: (text) => setValue((text || "").trimStart()), onClearIconPress: handleClear, placeholder, placeholderTextColor: theme.colors.onSurfaceVariant, ref: inputRef, spellCheck: false, style: styles5.input, value }) });
591
867
  });
592
868
  var styles5 = StyleSheet4.create({
593
869
  container: { paddingVertical: 8 },
@@ -1088,6 +1364,24 @@ var objectTraps = {
1088
1364
  get(state, prop) {
1089
1365
  if (prop === DRAFT_STATE)
1090
1366
  return state;
1367
+ if (prop === "constructor" || prop === "__proto__") {
1368
+ const source2 = latest(state);
1369
+ const value2 = source2[prop];
1370
+ return new Proxy(value2 || {}, {
1371
+ get: (target, key) => {
1372
+ if (key === "__proto__" || key === "prototype") {
1373
+ return Object.freeze(/* @__PURE__ */ Object.create(null));
1374
+ }
1375
+ return Reflect.get(target, key);
1376
+ },
1377
+ set: () => {
1378
+ return true;
1379
+ },
1380
+ apply: (target, thisArg, args) => {
1381
+ return Reflect.apply(target, thisArg, args);
1382
+ }
1383
+ });
1384
+ }
1091
1385
  let arrayPlugin = state.scope_.arrayMethodsPlugin_;
1092
1386
  const isArrayWithStringProp = state.type_ === 1 && typeof prop === "string";
1093
1387
  if (isArrayWithStringProp) {
@@ -1117,12 +1411,18 @@ var objectTraps = {
1117
1411
  return value;
1118
1412
  },
1119
1413
  has(state, prop) {
1414
+ if (prop === "constructor" || prop === "__proto__" || prop === "prototype") {
1415
+ return false;
1416
+ }
1120
1417
  return prop in latest(state);
1121
1418
  },
1122
1419
  ownKeys(state) {
1123
1420
  return Reflect.ownKeys(latest(state));
1124
1421
  },
1125
1422
  set(state, prop, value) {
1423
+ if (prop === "constructor" || prop === "__proto__" || prop === "prototype") {
1424
+ return true;
1425
+ }
1126
1426
  const desc = getDescriptorFromProto(latest(state), prop);
1127
1427
  if (desc?.set) {
1128
1428
  desc.set.call(state.draft_, value);
@@ -1142,7 +1442,7 @@ var objectTraps = {
1142
1442
  markChanged(state);
1143
1443
  }
1144
1444
  if (state.copy_[prop] === value && // special case: handle new props with value 'undefined'
1145
- (value !== void 0 || prop in state.copy_) || // special case: NaN
1445
+ (value !== void 0 || has(state.copy_, prop, state.type_)) || // special case: NaN
1146
1446
  Number.isNaN(value) && Number.isNaN(state.copy_[prop]))
1147
1447
  return true;
1148
1448
  state.copy_[prop] = value;
@@ -2158,16 +2458,26 @@ var scrollViewActions = slice.actions;
2158
2458
  var scrollViewReducer = slice.reducer;
2159
2459
 
2160
2460
  // src/ScrollView.tsx
2161
- import { memo as memo3, useCallback as useCallback5, useMemo as useMemo4, useRef as useRef5 } from "react";
2461
+ import { memo as memo3, useCallback as useCallback6, useContext as useContext9, useLayoutEffect as useLayoutEffect4, useMemo as useMemo4, useRef as useRef6 } from "react";
2162
2462
  import { View as View4 } from "react-native";
2163
2463
  import { Gesture as Gesture3, GestureDetector as GestureDetector3 } from "react-native-gesture-handler";
2164
- import Animated5 from "react-native-reanimated";
2464
+ import Animated4, { runOnUI as runOnUI2, useSharedValue as useSharedValue5 } from "react-native-reanimated";
2165
2465
  import { jsx as jsx7, jsxs as jsxs4 } from "react/jsx-runtime";
2166
- var ScrollViewInner = ({ children, footerFixed: footerFixedProp, gesture, headerFixed: headerFixedProp, horizontal, keyboardAware, onMomentumScrollEnd: externalMomentumScrollEnd, onRefresh, onScrollBeginDrag: externalScrollBeginDrag, onScrollEndDrag: externalScrollEndDrag, pullSearchHeight, refreshing, style, ...props }) => {
2167
- const scrollView = useRef5(null);
2466
+ var ScrollViewInner = ({ chipProps, chipThreshold, children, footerFixed: footerFixedProp, gesture, headerFixed: headerFixedProp, horizontal, keyboardAware, onChipPress, onMomentumScrollEnd: externalMomentumScrollEnd, onRefresh, onScrollBeginDrag: externalScrollBeginDrag, onScrollEndDrag: externalScrollEndDrag, pullSearchHeight, refreshing, style, ...props }) => {
2467
+ const scrollView = useRef6(null);
2168
2468
  const isHorizontal = horizontal === true;
2169
2469
  const { chipHidden, chipStyle, containerStyle, contentInset, contentOffset, footerFixed, headerFixed } = useScrollList({ footerFixed: footerFixedProp, headerFixed: headerFixedProp, isHorizontal, keyboardAware, pullSearchHeight, style });
2170
- const scrollTo = useCallback5((offset, animated) => {
2470
+ const { listGeneration, onListUnmount } = useContext9(ScrollViewContext);
2471
+ const capturedGeneration = useSharedValue5(-1);
2472
+ useLayoutEffect4(() => {
2473
+ const generation = listGeneration.value;
2474
+ runOnUI2((gen) => {
2475
+ "worklet";
2476
+ capturedGeneration.value = gen;
2477
+ })(generation);
2478
+ }, [capturedGeneration, listGeneration]);
2479
+ useLayoutEffect4(() => () => onListUnmount(), [onListUnmount]);
2480
+ const scrollTo = useCallback6((offset, animated) => {
2171
2481
  scrollView.current?.scrollTo({ y: offset, animated });
2172
2482
  }, []);
2173
2483
  const { handleMomentumScrollEnd, handleScrollBeginDrag, handleScrollEndDrag } = useScrollInit({
@@ -2177,12 +2487,8 @@ var ScrollViewInner = ({ children, footerFixed: footerFixedProp, gesture, header
2177
2487
  pullSearchHeight,
2178
2488
  scrollTo
2179
2489
  });
2180
- const onPullSearchZoneEnter = useCallback5(() => {
2181
- if (!pullSearchHeight) return;
2182
- scrollTo(-contentInset.top + pullSearchHeight, true);
2183
- }, [contentInset.top, pullSearchHeight, scrollTo]);
2184
- const handleScroll = useScrollHandler({ chipHidden, footerFixed, headerFixed, isHorizontal, onPullSearchZoneEnter: pullSearchHeight ? onPullSearchZoneEnter : void 0 });
2185
- const handleScrollToTop = useCallback5(() => {
2490
+ const handleScroll = useScrollHandler({ capturedGeneration, chipHidden, chipThreshold, footerFixed, headerFixed, isHorizontal, listGeneration });
2491
+ const handleScrollToTop = useCallback6(() => {
2186
2492
  if (isHorizontal) {
2187
2493
  scrollView.current?.scrollTo({ x: 0, animated: true });
2188
2494
  return;
@@ -2196,7 +2502,7 @@ var ScrollViewInner = ({ children, footerFixed: footerFixedProp, gesture, header
2196
2502
  const detectorGesture = gesture !== void 0 ? combinedGesture : void 0;
2197
2503
  const content = /* @__PURE__ */ jsxs4(View4, { style: containerStyle, children: [
2198
2504
  /* @__PURE__ */ jsx7(
2199
- Animated5.ScrollView,
2505
+ Animated4.ScrollView,
2200
2506
  {
2201
2507
  ...props,
2202
2508
  contentInset,
@@ -2214,7 +2520,7 @@ var ScrollViewInner = ({ children, footerFixed: footerFixedProp, gesture, header
2214
2520
  children
2215
2521
  }
2216
2522
  ),
2217
- /* @__PURE__ */ jsx7(ScrollViewChip, { isHorizontal, onPress: handleScrollToTop, style: chipStyle })
2523
+ /* @__PURE__ */ jsx7(ScrollViewChip, { chipProps, isHorizontal, onChipPress, onPress: handleScrollToTop, style: chipStyle })
2218
2524
  ] });
2219
2525
  return detectorGesture ? /* @__PURE__ */ jsx7(GestureDetector3, { gesture: detectorGesture, children: content }) : content;
2220
2526
  };
@@ -2222,13 +2528,13 @@ var ScrollView = memo3(ScrollViewInner);
2222
2528
 
2223
2529
  // src/ScrollViewFooter.tsx
2224
2530
  import { BlurView as BlurView2 } from "@rific/auto-paper";
2225
- import { useContext as useContext6, useEffect as useEffect4 } from "react";
2531
+ import { useContext as useContext10, useEffect as useEffect4 } from "react";
2226
2532
  import { StyleSheet as StyleSheet5, View as View5 } from "react-native";
2227
- import Animated6, { useAnimatedStyle as useAnimatedStyle4 } from "react-native-reanimated";
2533
+ import Animated5, { useAnimatedStyle as useAnimatedStyle4 } from "react-native-reanimated";
2228
2534
  import { useSafeAreaInsets as useSafeAreaInsets3 } from "react-native-safe-area-context";
2229
2535
  import { jsx as jsx8, jsxs as jsxs5 } from "react/jsx-runtime";
2230
2536
  var ScrollViewFooter = ({ children, style }) => {
2231
- const { blur, footerHeight, footerFixed, footerOffset, headerHeightShared, pullSearchHeightShared, scrollPosition, setFooterHeight, snapBackFooterShared } = useContext6(ScrollViewContext);
2537
+ const { blur, footerHeight, footerFixed, footerOffset, headerHeightShared, pullSearchHeightShared, scrollPosition, setFooterHeight, snapBackFooterShared } = useContext10(ScrollViewContext);
2232
2538
  const insets = useSafeAreaInsets3();
2233
2539
  useEffect4(
2234
2540
  () => () => {
@@ -2250,7 +2556,7 @@ var ScrollViewFooter = ({ children, style }) => {
2250
2556
  if (effective <= 0) return { transform: [{ translateY: 0 }] };
2251
2557
  return { transform: [{ translateY: Math.min(effective, footerHeight) }] };
2252
2558
  }, [footerHeight, footerFixed]);
2253
- return /* @__PURE__ */ jsxs5(Animated6.View, { onLayout: handleLayout, pointerEvents: "box-none", style: [styles6.footer, footerStyle], children: [
2559
+ return /* @__PURE__ */ jsxs5(Animated5.View, { onLayout: handleLayout, pointerEvents: "box-none", style: [styles6.footer, footerStyle], children: [
2254
2560
  footerHeight > 0 && /* @__PURE__ */ jsx8(BlurView2, { blur, style: StyleSheet5.absoluteFill }),
2255
2561
  /* @__PURE__ */ jsx8(View5, { style: [styles6.row, { paddingBottom: insets.bottom }, style], children })
2256
2562
  ] });
@@ -2262,16 +2568,16 @@ var styles6 = StyleSheet5.create({
2262
2568
 
2263
2569
  // src/ScrollViewHeader.tsx
2264
2570
  import { BlurView as BlurView3 } from "@rific/auto-paper";
2265
- import { useContext as useContext7 } from "react";
2571
+ import { useContext as useContext11 } from "react";
2266
2572
  import { StyleSheet as StyleSheet6, Text, View as View6 } from "react-native";
2267
2573
  import { Appbar, ProgressBar, Surface, useTheme as useTheme3 } from "react-native-paper";
2268
- import Animated7, { useAnimatedStyle as useAnimatedStyle5 } from "react-native-reanimated";
2574
+ import Animated6, { useAnimatedStyle as useAnimatedStyle5 } from "react-native-reanimated";
2269
2575
  import { useSafeAreaInsets as useSafeAreaInsets4 } from "react-native-safe-area-context";
2270
- import { Fragment as Fragment3, jsx as jsx9, jsxs as jsxs6 } from "react/jsx-runtime";
2576
+ import { Fragment as Fragment4, jsx as jsx9, jsxs as jsxs6 } from "react/jsx-runtime";
2271
2577
  var ActionBg = ({ blur, style }) => blur ? /* @__PURE__ */ jsx9(View6, { style: [StyleSheet6.absoluteFill, styles7.actionBg, style], children: /* @__PURE__ */ jsx9(BlurView3, { blur: true, style: StyleSheet6.absoluteFill }) }) : /* @__PURE__ */ jsx9(View6, { style: [StyleSheet6.absoluteFill, styles7.actionBg, style], pointerEvents: "none", children: /* @__PURE__ */ jsx9(Surface, { style: StyleSheet6.absoluteFill, elevation: 1, children: null }) });
2272
2578
  var ScrollViewHeader = ({ actionSize = 48, actionStyle, backAction, backActionFixed, caption, centerContent, children, iconSize, style, title, topInset = true, trailingAction, trailingActionFixed = true }) => {
2273
- const { blur, headerHeight, headerFixed, headerOffset, progress, progressing, pullSearchHeightShared, scrollPosition, setHeaderHeight, snapBackHeaderShared } = useContext7(ScrollViewContext);
2274
- const { settings } = useContext7(ScrollViewSettingsContext);
2579
+ const { blur, headerHeight, headerFixed, headerOffset, progress, progressing, pullSearchHeightShared, scrollPosition, setHeaderHeight, snapBackHeaderShared } = useContext11(ScrollViewContext);
2580
+ const { settings } = useContext11(ScrollViewSettingsContext);
2275
2581
  const effectiveBackActionFixed = backActionFixed ?? settings.backActionFixed;
2276
2582
  const insets = useSafeAreaInsets4();
2277
2583
  const theme = useTheme3();
@@ -2286,20 +2592,22 @@ var ScrollViewHeader = ({ actionSize = 48, actionStyle, backAction, backActionFi
2286
2592
  const translateStyle = useAnimatedStyle5(() => {
2287
2593
  if (headerFixed) return { transform: [{ translateY: 0 }] };
2288
2594
  if (snapBackHeaderShared.value) return { transform: [{ translateY: headerOffset.value }] };
2289
- const effective = scrollPosition.value + headerHeight - pullSearchHeightShared.value;
2595
+ const effective = scrollPosition.value + (headerHeight ?? 0) - pullSearchHeightShared.value;
2290
2596
  if (effective <= 0) return { transform: [{ translateY: 0 }] };
2291
2597
  return { transform: [{ translateY: -effective }] };
2292
2598
  }, [headerFixed, headerHeight]);
2293
2599
  const blurStyle = useAnimatedStyle5(() => {
2294
2600
  if (!headerHeight) return { height: 0 };
2295
2601
  if (headerFixed) return { height: headerHeight };
2296
- const translateY = snapBackHeaderShared.value ? headerOffset.value : -Math.max(0, scrollPosition.value + headerHeight - pullSearchHeightShared.value);
2297
- return { height: Math.max(headerHeight + translateY, top) };
2602
+ const h = headerHeight ?? 0;
2603
+ const translateY = snapBackHeaderShared.value ? headerOffset.value : -Math.max(0, scrollPosition.value + h - pullSearchHeightShared.value);
2604
+ return { height: Math.max(h + translateY, top) };
2298
2605
  }, [headerFixed, headerHeight, top]);
2299
2606
  const progressStyle = useAnimatedStyle5(() => {
2300
- if (headerFixed) return { top: headerHeight };
2301
- const slide = snapBackHeaderShared.value ? -headerOffset.value : Math.max(0, scrollPosition.value + headerHeight - pullSearchHeightShared.value);
2302
- return { top: Math.max(headerHeight - slide, top) };
2607
+ const h = headerHeight ?? 0;
2608
+ if (headerFixed) return { top: h };
2609
+ const slide = snapBackHeaderShared.value ? -headerOffset.value : Math.max(0, scrollPosition.value + h - pullSearchHeightShared.value);
2610
+ return { top: Math.max(h - slide, top) };
2303
2611
  }, [headerFixed, headerHeight, top]);
2304
2612
  const actionMargin = 4;
2305
2613
  const contentMinHeight = actionSize + 2 * actionMargin;
@@ -2308,32 +2616,31 @@ var ScrollViewHeader = ({ actionSize = 48, actionStyle, backAction, backActionFi
2308
2616
  const actionShadow = { borderRadius: actionSize / 2, elevation: 2, shadowColor: "#000", shadowOffset: { width: 0, height: 1 }, shadowOpacity: 0.15, shadowRadius: 2 };
2309
2617
  const leadingStyle = { alignItems: "center", height: actionSize, justifyContent: "center", left: actionMargin, position: "absolute", top: actionTop, width: actionSize, zIndex: 3, ...actionShadow };
2310
2618
  const trailingStyle = { alignItems: "center", height: actionSize, justifyContent: "center", position: "absolute", right: actionMargin, top: actionTop, width: actionSize, zIndex: 3, ...actionShadow };
2311
- return /* @__PURE__ */ jsxs6(Fragment3, { children: [
2312
- /* @__PURE__ */ jsx9(Animated7.View, { pointerEvents: "none", style: [styles7.blur, blurStyle], children: headerHeight > 0 && /* @__PURE__ */ jsx9(BlurView3, { blur, style: [styles7.blurInner, { height: headerHeight }] }) }),
2313
- /* @__PURE__ */ jsxs6(Animated7.View, { onLayout: handleLayout, pointerEvents: "box-none", style: [headerHeight === 0 ? styles7.headerInit : styles7.header, translateStyle], children: [
2314
- /* @__PURE__ */ jsx9(View6, { style: [{ paddingTop: top }, style], children: /* @__PURE__ */ jsxs6(View6, { style: [styles7.content, { minHeight: contentMinHeight }], children: [
2619
+ return /* @__PURE__ */ jsxs6(Fragment4, { children: [
2620
+ /* @__PURE__ */ jsx9(Animated6.View, { pointerEvents: "none", style: [styles7.blur, blurStyle], children: headerHeight !== null && headerHeight > 0 && /* @__PURE__ */ jsx9(BlurView3, { blur, style: [styles7.blurInner, { height: headerHeight }] }) }),
2621
+ /* @__PURE__ */ jsxs6(Animated6.View, { onLayout: handleLayout, pointerEvents: "box-none", style: [headerHeight === null ? styles7.headerInit : styles7.header, translateStyle], children: [
2622
+ /* @__PURE__ */ jsx9(View6, { style: [{ paddingTop: top }, style], children: children ?? /* @__PURE__ */ jsxs6(View6, { style: [styles7.content, { minHeight: contentMinHeight }], children: [
2315
2623
  /* @__PURE__ */ jsx9(View6, { style: styles7.side }),
2316
2624
  /* @__PURE__ */ jsx9(View6, { style: styles7.spacer }),
2317
- /* @__PURE__ */ jsx9(View6, { style: styles7.side, children }),
2318
- (centerContent || title || caption) && /* @__PURE__ */ jsx9(View6, { style: [StyleSheet6.absoluteFill, styles7.titleContainer], pointerEvents: centerContent ? "box-none" : "none", children: centerContent ?? /* @__PURE__ */ jsxs6(Fragment3, { children: [
2625
+ (centerContent || title || caption) && /* @__PURE__ */ jsx9(View6, { style: [StyleSheet6.absoluteFill, styles7.titleContainer], pointerEvents: centerContent ? "box-none" : "none", children: centerContent ?? /* @__PURE__ */ jsxs6(Fragment4, { children: [
2319
2626
  title && /* @__PURE__ */ jsx9(Text, { numberOfLines: 1, style: [styles7.title, { color: theme.colors.onSurface }], children: title }),
2320
2627
  caption && /* @__PURE__ */ jsx9(Text, { numberOfLines: 1, style: [styles7.caption, { color: theme.colors.onSurfaceVariant }], children: caption })
2321
2628
  ] }) })
2322
2629
  ] }) }),
2323
2630
  !blur && /* @__PURE__ */ jsx9(View6, { style: [styles7.divider, { backgroundColor: theme.colors.outlineVariant }] })
2324
2631
  ] }),
2325
- /* @__PURE__ */ jsx9(Animated7.View, { pointerEvents: "none", style: [styles7.progress, progressStyle], children: /* @__PURE__ */ jsx9(ProgressBar, { indeterminate: progress === null, visible: progressing, progress: progress ?? void 0 }) }),
2326
- backAction && (effectiveBackActionFixed ? /* @__PURE__ */ jsxs6(Animated7.View, { style: leadingStyle, children: [
2632
+ /* @__PURE__ */ jsx9(Animated6.View, { pointerEvents: "none", style: [styles7.progress, progressStyle], children: /* @__PURE__ */ jsx9(ProgressBar, { indeterminate: progress === null, visible: progressing, progress: progress ?? void 0 }) }),
2633
+ backAction && (effectiveBackActionFixed ? /* @__PURE__ */ jsxs6(Animated6.View, { style: leadingStyle, children: [
2327
2634
  /* @__PURE__ */ jsx9(ActionBg, { blur, style: actionStyle }),
2328
2635
  /* @__PURE__ */ jsx9(Appbar.BackAction, { onPress: backAction, size: iconSize ?? actionSize / 2, style: buttonStyle })
2329
- ] }) : /* @__PURE__ */ jsxs6(Animated7.View, { style: [leadingStyle, translateStyle], children: [
2636
+ ] }) : /* @__PURE__ */ jsxs6(Animated6.View, { style: [leadingStyle, translateStyle], children: [
2330
2637
  /* @__PURE__ */ jsx9(ActionBg, { blur, style: actionStyle }),
2331
2638
  /* @__PURE__ */ jsx9(Appbar.BackAction, { onPress: backAction, size: iconSize ?? actionSize / 2, style: buttonStyle })
2332
2639
  ] })),
2333
2640
  trailingAction && (trailingActionFixed ? /* @__PURE__ */ jsxs6(View6, { style: trailingStyle, children: [
2334
2641
  /* @__PURE__ */ jsx9(ActionBg, { blur, style: actionStyle }),
2335
2642
  trailingAction
2336
- ] }) : /* @__PURE__ */ jsxs6(Animated7.View, { style: [trailingStyle, translateStyle], children: [
2643
+ ] }) : /* @__PURE__ */ jsxs6(Animated6.View, { style: [trailingStyle, translateStyle], children: [
2337
2644
  /* @__PURE__ */ jsx9(ActionBg, { blur, style: actionStyle }),
2338
2645
  trailingAction
2339
2646
  ] }))
@@ -2357,62 +2664,77 @@ var styles7 = StyleSheet6.create({
2357
2664
 
2358
2665
  // src/ScrollViewProvider.tsx
2359
2666
  import { useBlur } from "@rific/auto-paper";
2360
- import { useCallback as useCallback6, useContext as useContext8, useEffect as useEffect5, useMemo as useMemo5, useState as useState4 } from "react";
2667
+ import { useCallback as useCallback7, useContext as useContext12, useEffect as useEffect5, useMemo as useMemo5, useRef as useRef7, useState as useState5 } from "react";
2361
2668
  import { Dimensions as Dimensions3 } from "react-native";
2362
- import { useSharedValue as useSharedValue3 } from "react-native-reanimated";
2669
+ import { useSharedValue as useSharedValue6 } from "react-native-reanimated";
2363
2670
  import { jsx as jsx10 } from "react/jsx-runtime";
2364
2671
  var ScrollViewProvider = ({ blur, children, fixed = false, footerFixed, headerFixed, snapBack, snapBackFooter, snapBackHeader, tabBarHeight = 60 }) => {
2365
- const { settings } = useContext8(ScrollViewSettingsContext);
2672
+ const { settings } = useContext12(ScrollViewSettingsContext);
2366
2673
  const effectiveBlur = useBlur(blur);
2367
2674
  const effectiveSnapBack = snapBack ?? settings.snapBack;
2368
2675
  const effectiveSnapBackHeader = snapBackHeader ?? settings.snapBackHeader;
2369
2676
  const effectiveSnapBackFooter = snapBackFooter ?? settings.snapBackFooter;
2370
- const [headerHeight, setHeaderHeightState] = useState4(0);
2371
- const [footerHeight, setFooterHeightState] = useState4(0);
2372
- const [progress, setProgress] = useState4(null);
2373
- const [progressing, setProgressing] = useState4(false);
2374
- const scrollPosition = useSharedValue3(0);
2375
- const headerHeightShared = useSharedValue3(0);
2376
- const footerHeightShared = useSharedValue3(0);
2377
- const headerOffset = useSharedValue3(0);
2378
- const footerOffset = useSharedValue3(0);
2379
- const pullSearchHeightShared = useSharedValue3(0);
2380
- const snapBackHeaderShared = useSharedValue3(false);
2381
- const snapBackFooterShared = useSharedValue3(false);
2677
+ const [headerHeight, setHeaderHeightState] = useState5(null);
2678
+ const [footerHeight, setFooterHeightState] = useState5(0);
2679
+ const [progress, setProgress] = useState5(null);
2680
+ const [progressing, setProgressing] = useState5(false);
2681
+ const scrollPosition = useSharedValue6(0);
2682
+ const listGeneration = useSharedValue6(0);
2683
+ const jsListGeneration = useRef7(0);
2684
+ const headerHeightShared = useSharedValue6(0);
2685
+ const footerHeightShared = useSharedValue6(0);
2686
+ const headerOffset = useSharedValue6(0);
2687
+ const footerOffset = useSharedValue6(0);
2688
+ const pullSearchHeightShared = useSharedValue6(0);
2689
+ const snapBackHeaderShared = useSharedValue6(false);
2690
+ const snapBackFooterShared = useSharedValue6(false);
2382
2691
  useEffect5(() => {
2383
2692
  snapBackHeaderShared.value = effectiveSnapBackHeader ?? effectiveSnapBack;
2384
2693
  snapBackFooterShared.value = effectiveSnapBackFooter ?? effectiveSnapBack;
2385
2694
  }, [effectiveSnapBack, effectiveSnapBackFooter, effectiveSnapBackHeader, snapBackFooterShared, snapBackHeaderShared]);
2386
- const setHeaderHeight = useCallback6(
2695
+ const setHeaderHeight = useCallback7(
2387
2696
  (h) => {
2388
2697
  setHeaderHeightState(h);
2389
- headerHeightShared.value = h;
2698
+ headerHeightShared.value = h ?? 0;
2390
2699
  },
2391
2700
  [headerHeightShared]
2392
2701
  );
2393
- const setFooterHeight = useCallback6(
2702
+ const onListUnmount = useCallback7(() => {
2703
+ listGeneration.value += 1;
2704
+ }, [listGeneration]);
2705
+ const onJsListUnmount = useCallback7(() => {
2706
+ jsListGeneration.current += 1;
2707
+ }, []);
2708
+ useEffect5(() => {
2709
+ if (!__DEV__ || headerHeight !== null) return;
2710
+ const timeout = setTimeout(() => {
2711
+ console.warn("[scroll-view] No ScrollViewHeader ever measured inside this ScrollViewProvider \u2014 scroll content stays hidden (opacity: 0) until one does. Render a <ScrollViewHeader> (it can be empty / zero height) inside this provider.");
2712
+ }, 3e3);
2713
+ return () => clearTimeout(timeout);
2714
+ }, [headerHeight]);
2715
+ const setFooterHeight = useCallback7(
2394
2716
  (h) => {
2395
2717
  setFooterHeightState(h);
2396
2718
  footerHeightShared.value = h;
2397
2719
  },
2398
2720
  [footerHeightShared]
2399
2721
  );
2400
- const scrollHeight = useMemo5(() => Dimensions3.get("window").height - headerHeight - footerHeight, [headerHeight, footerHeight]);
2722
+ const scrollHeight = useMemo5(() => Dimensions3.get("window").height - (headerHeight ?? 0) - footerHeight, [headerHeight, footerHeight]);
2401
2723
  const effectiveHeaderFixed = fixed || (headerFixed ?? settings.headerFixed);
2402
2724
  const effectiveFooterFixed = fixed || (footerFixed ?? settings.footerFixed);
2403
- const value = useMemo5(() => ({ blur: effectiveBlur, footerHeight, footerHeightShared, footerFixed: effectiveFooterFixed, footerOffset, headerHeight, headerHeightShared, headerFixed: effectiveHeaderFixed, headerOffset, progress, pullSearchHeightShared, progressing, scrollHeight, scrollPosition, setFooterHeight, setHeaderHeight, setProgress, setProgressing, snapBackFooterShared, snapBackHeaderShared, tabBarHeight }), [effectiveBlur, effectiveFooterFixed, effectiveHeaderFixed, footerHeight, footerHeightShared, footerOffset, headerHeight, headerHeightShared, headerOffset, progress, pullSearchHeightShared, progressing, scrollHeight, scrollPosition, setFooterHeight, setHeaderHeight, snapBackFooterShared, snapBackHeaderShared, tabBarHeight]);
2725
+ const value = useMemo5(() => ({ blur: effectiveBlur, footerHeight, footerHeightShared, footerFixed: effectiveFooterFixed, footerOffset, headerHeight, headerHeightShared, headerFixed: effectiveHeaderFixed, headerOffset, jsListGeneration, listGeneration, onJsListUnmount, onListUnmount, progress, pullSearchHeightShared, progressing, scrollHeight, scrollPosition, setFooterHeight, setHeaderHeight, setProgress, setProgressing, snapBackFooterShared, snapBackHeaderShared, tabBarHeight }), [effectiveBlur, effectiveFooterFixed, effectiveHeaderFixed, footerHeight, footerHeightShared, footerOffset, headerHeight, headerHeightShared, headerOffset, jsListGeneration, listGeneration, onJsListUnmount, onListUnmount, progress, pullSearchHeightShared, progressing, scrollHeight, scrollPosition, setFooterHeight, setHeaderHeight, snapBackFooterShared, snapBackHeaderShared, tabBarHeight]);
2404
2726
  return /* @__PURE__ */ jsx10(ScrollViewContext.Provider, { value, children });
2405
2727
  };
2406
2728
 
2407
2729
  // src/ScrollViewSettingsProvider.tsx
2408
- import { useCallback as useCallback7, useMemo as useMemo6, useState as useState5 } from "react";
2730
+ import { useCallback as useCallback8, useMemo as useMemo6, useState as useState6 } from "react";
2409
2731
  import { jsx as jsx11 } from "react/jsx-runtime";
2410
- var ScrollViewSettingsProvider = ({ children, onChange, value }) => {
2411
- const [settings, setSettings] = useState5(() => ({
2732
+ var ScrollViewSettingsProvider = ({ children, onChange, initialValue }) => {
2733
+ const [settings, setSettings] = useState6(() => ({
2412
2734
  ...defaultScrollViewSettings,
2413
- ...value
2735
+ ...initialValue
2414
2736
  }));
2415
- const set2 = useCallback7(
2737
+ const set2 = useCallback8(
2416
2738
  (patch) => {
2417
2739
  setSettings((prev) => {
2418
2740
  const next = { ...prev, ...patch };
@@ -2427,27 +2749,27 @@ var ScrollViewSettingsProvider = ({ children, onChange, value }) => {
2427
2749
  };
2428
2750
 
2429
2751
  // src/SectionList.tsx
2430
- import { memo as memo4, useCallback as useCallback9, useEffect as useEffect6, useLayoutEffect as useLayoutEffect3, useMemo as useMemo7, useRef as useRef7 } from "react";
2752
+ import { memo as memo4, useCallback as useCallback10, useContext as useContext14, useEffect as useEffect6, useLayoutEffect as useLayoutEffect6, useMemo as useMemo7, useRef as useRef9 } from "react";
2431
2753
  import { Dimensions as Dimensions4, SectionList as RNSectionList, View as View7 } from "react-native";
2432
2754
  import { Gesture as Gesture4, GestureDetector as GestureDetector4 } from "react-native-gesture-handler";
2433
- import Animated8, { createAnimatedComponent } from "react-native-reanimated";
2755
+ import Animated7, { runOnUI as runOnUI3, useSharedValue as useSharedValue8 } from "react-native-reanimated";
2434
2756
 
2435
2757
  // src/internal/useStickyHeaders.ts
2436
- import { useCallback as useCallback8, useContext as useContext9, useLayoutEffect as useLayoutEffect2, useRef as useRef6, useState as useState6 } from "react";
2437
- import { runOnJS as runOnJS4, useAnimatedReaction as useAnimatedReaction2, useAnimatedStyle as useAnimatedStyle6, useSharedValue as useSharedValue4 } from "react-native-reanimated";
2758
+ import { useCallback as useCallback9, useContext as useContext13, useLayoutEffect as useLayoutEffect5, useRef as useRef8, useState as useState7 } from "react";
2759
+ import { runOnJS as runOnJS4, useAnimatedReaction as useAnimatedReaction2, useAnimatedStyle as useAnimatedStyle6, useSharedValue as useSharedValue7 } from "react-native-reanimated";
2438
2760
  import { useSafeAreaInsets as useSafeAreaInsets5 } from "react-native-safe-area-context";
2439
2761
  function useStickyHeaders(headerFixed, enabled) {
2440
- const { headerHeightShared, headerOffset, pullSearchHeightShared, scrollPosition, snapBackHeaderShared } = useContext9(ScrollViewContext);
2762
+ const { headerHeightShared, headerOffset, pullSearchHeightShared, scrollPosition, snapBackHeaderShared } = useContext13(ScrollViewContext);
2441
2763
  const insetsTop = useSafeAreaInsets5().top;
2442
- const sectionYPositions = useSharedValue4([]);
2443
- const sectionHeights = useSharedValue4([]);
2444
- const [activeIndex, setActiveIndex] = useState6(-1);
2445
- const contentIndexShared = useSharedValue4(-1);
2446
- useLayoutEffect2(() => {
2764
+ const sectionYPositions = useSharedValue7([]);
2765
+ const sectionHeights = useSharedValue7([]);
2766
+ const [activeIndex, setActiveIndex] = useState7(-1);
2767
+ const contentIndexShared = useSharedValue7(-1);
2768
+ useLayoutEffect5(() => {
2447
2769
  contentIndexShared.value = activeIndex;
2448
2770
  }, [activeIndex, contentIndexShared]);
2449
- const positionsCache = useRef6([]);
2450
- const heightsCache = useRef6([]);
2771
+ const positionsCache = useRef8([]);
2772
+ const heightsCache = useRef8([]);
2451
2773
  useAnimatedReaction2(
2452
2774
  () => {
2453
2775
  if (!enabled) return -1;
@@ -2519,7 +2841,7 @@ function useStickyHeaders(headerFixed, enabled) {
2519
2841
  }
2520
2842
  return { opacity: 1, top: 0, transform: [{ translateY }] };
2521
2843
  }, [enabled, headerFixed, insetsTop]);
2522
- const measureHeader = useCallback8(
2844
+ const measureHeader = useCallback9(
2523
2845
  (index, pageY, height) => {
2524
2846
  const contentY = pageY + scrollPosition.value;
2525
2847
  positionsCache.current[index] = contentY;
@@ -2529,7 +2851,7 @@ function useStickyHeaders(headerFixed, enabled) {
2529
2851
  },
2530
2852
  [scrollPosition, sectionHeights, sectionYPositions]
2531
2853
  );
2532
- const resetPositions = useCallback8(
2854
+ const resetPositions = useCallback9(
2533
2855
  (count) => {
2534
2856
  positionsCache.current = Array.from({ length: count }, () => -1);
2535
2857
  sectionYPositions.value = positionsCache.current.slice();
@@ -2542,18 +2864,27 @@ function useStickyHeaders(headerFixed, enabled) {
2542
2864
  }
2543
2865
 
2544
2866
  // src/SectionList.tsx
2545
- import { Fragment as Fragment4, jsx as jsx12, jsxs as jsxs7 } from "react/jsx-runtime";
2546
- var AnimatedSectionList = createAnimatedComponent(RNSectionList);
2547
- var SectionListInner = ({ contentContainerStyle: externalContentContainerStyle, initialNumToRender = 20, maxToRenderPerBatch = 50, renderFilters, footerFixed: footerFixedProp, gesture, headerFixed: headerFixedProp, keyboardAware, ListHeaderComponent: externalListHeaderComponent, onMomentumScrollEnd: externalMomentumScrollEnd, onRefresh, onContentSizeChange, onScrollBeginDrag: externalScrollBeginDrag, onScrollEndDrag: externalScrollEndDrag, pullSearchHeight, refreshing, removeClippedSubviews = false, sections, showsVerticalScrollIndicator, style, ref: externalRef, windowSize = 100, ...props }) => {
2548
- const scrollView = useRef7(null);
2867
+ import { Fragment as Fragment5, jsx as jsx12, jsxs as jsxs7 } from "react/jsx-runtime";
2868
+ var AnimatedSectionList = Animated7.createAnimatedComponent(RNSectionList);
2869
+ var SectionListInner = ({ chipProps, chipThreshold, contentContainerStyle: externalContentContainerStyle, initialNumToRender = 20, maxToRenderPerBatch = 50, renderFilters, footerFixed: footerFixedProp, gesture, headerFixed: headerFixedProp, keyboardAware, ListHeaderComponent: externalListHeaderComponent, onChipPress, onMomentumScrollEnd: externalMomentumScrollEnd, onRefresh, onContentSizeChange, onScrollBeginDrag: externalScrollBeginDrag, onScrollEndDrag: externalScrollEndDrag, pullSearchHeight, refreshing, removeClippedSubviews = false, sections, showsVerticalScrollIndicator, style, ref: externalRef, windowSize = 100, ...props }) => {
2870
+ const scrollView = useRef9(null);
2549
2871
  const { stickySectionHeadersEnabled = true, renderSectionHeader, ...passThroughProps } = props;
2550
2872
  const { chipHidden, chipStyle, containerStyle, contentInset, contentOffset, footerFixed, headerFixed, headerHeight } = useScrollList({ footerFixed: footerFixedProp, headerFixed: headerFixedProp, keyboardAware, pullSearchHeight, style });
2873
+ const { listGeneration, onListUnmount } = useContext14(ScrollViewContext);
2874
+ const capturedGeneration = useSharedValue8(-1);
2875
+ useLayoutEffect6(() => {
2876
+ const generation = listGeneration.value;
2877
+ runOnUI3((gen) => {
2878
+ "worklet";
2879
+ capturedGeneration.value = gen;
2880
+ })(generation);
2881
+ }, [capturedGeneration, listGeneration]);
2551
2882
  const usesCustomSticky = stickySectionHeadersEnabled && renderSectionHeader != null;
2552
2883
  const { activeIndex, clipStyle, measureHeader, overlayStyle, resetPositions } = useStickyHeaders(headerFixed, usesCustomSticky);
2553
- useLayoutEffect3(() => {
2884
+ useLayoutEffect6(() => {
2554
2885
  if (usesCustomSticky) resetPositions(sections.length);
2555
2886
  }, [sections, usesCustomSticky, resetPositions, headerHeight]);
2556
- const sectionViewRefs = useRef7(/* @__PURE__ */ new Map());
2887
+ const sectionViewRefs = useRef9(/* @__PURE__ */ new Map());
2557
2888
  const wrappedRenderSectionHeader = useMemo7(() => {
2558
2889
  if (!usesCustomSticky) return renderSectionHeader ?? void 0;
2559
2890
  return (info) => {
@@ -2575,7 +2906,8 @@ var SectionListInner = ({ contentContainerStyle: externalContentContainerStyle,
2575
2906
  );
2576
2907
  };
2577
2908
  }, [usesCustomSticky, renderSectionHeader, sections, measureHeader]);
2578
- const scrollTo = useCallback9((offset, animated) => {
2909
+ useLayoutEffect6(() => () => onListUnmount(), [onListUnmount]);
2910
+ const scrollTo = useCallback10((offset, animated) => {
2579
2911
  scrollView.current?.getScrollResponder()?.scrollTo({ y: offset, animated });
2580
2912
  }, []);
2581
2913
  const {
@@ -2596,12 +2928,12 @@ var SectionListInner = ({ contentContainerStyle: externalContentContainerStyle,
2596
2928
  const activeListHeader = useMemo7(() => {
2597
2929
  if (!renderFilters) return pullSearchHeader;
2598
2930
  const Inner = pullSearchHeader;
2599
- return () => /* @__PURE__ */ jsxs7(Fragment4, { children: [
2931
+ return () => /* @__PURE__ */ jsxs7(Fragment5, { children: [
2600
2932
  Inner ? typeof Inner === "function" ? /* @__PURE__ */ jsx12(Inner, {}) : Inner : null,
2601
2933
  renderFilters
2602
2934
  ] });
2603
2935
  }, [pullSearchHeader, renderFilters]);
2604
- const pullSearchSettled = useRef7(false);
2936
+ const pullSearchSettled = useRef9(false);
2605
2937
  useEffect6(() => {
2606
2938
  if (!usesCustomSticky || activeListHeader == null || pullSearchSettled.current) return;
2607
2939
  pullSearchSettled.current = true;
@@ -2615,16 +2947,12 @@ var SectionListInner = ({ contentContainerStyle: externalContentContainerStyle,
2615
2947
  return () => cancelAnimationFrame(handle);
2616
2948
  }, [activeListHeader, usesCustomSticky, measureHeader]);
2617
2949
  const contentContainerStyle = useMemo7(() => [{ minHeight: Dimensions4.get("window").height - contentInset.top - contentInset.bottom + pullSearchMinHeight }, externalContentContainerStyle], [contentInset.bottom, contentInset.top, externalContentContainerStyle, pullSearchMinHeight]);
2618
- const onPullSearchZoneEnter = useCallback9(() => {
2619
- if (!pullSearchHeight) return;
2620
- scrollTo(-contentInset.top + pullSearchHeight, true);
2621
- }, [contentInset.top, pullSearchHeight, scrollTo]);
2622
- const handleScroll = useScrollHandler({ chipHidden, footerFixed, headerFixed, onPullSearchZoneEnter: pullSearchHeight ? onPullSearchZoneEnter : void 0 });
2623
- const handleScrollToTop = useCallback9(() => {
2950
+ const handleScroll = useScrollHandler({ capturedGeneration, chipHidden, chipThreshold, footerFixed, headerFixed, listGeneration });
2951
+ const handleScrollToTop = useCallback10(() => {
2624
2952
  const offset = pullSearchHeight ? -contentInset.top + pullSearchHeight : -contentInset.top;
2625
2953
  scrollView.current?.getScrollResponder()?.scrollTo({ y: offset, animated: true });
2626
2954
  }, [contentInset.top, pullSearchHeight]);
2627
- const handleContentSizeChange = useCallback9(
2955
+ const handleContentSizeChange = useCallback10(
2628
2956
  (w, h) => {
2629
2957
  if (onContentSizeChange) onContentSizeChange(w, h);
2630
2958
  },
@@ -2663,11 +2991,11 @@ var SectionListInner = ({ contentContainerStyle: externalContentContainerStyle,
2663
2991
  stickySectionHeadersEnabled: false,
2664
2992
  windowSize
2665
2993
  },
2666
- headerHeight > 0 ? 1 : 0
2994
+ headerHeight !== null && headerHeight > 0 ? 1 : 0
2667
2995
  ),
2668
2996
  hiddenHeader && /* @__PURE__ */ jsx12(View7, { pointerEvents: "none", style: styles8.measureContainer, children: hiddenHeader }),
2669
- /* @__PURE__ */ jsx12(ScrollViewChip, { onPress: handleScrollToTop, style: chipStyle }),
2670
- usesCustomSticky && /* @__PURE__ */ jsx12(Animated8.View, { pointerEvents: "none", style: [styles8.stickyClip, clipStyle], children: activeIndex >= 0 && activeIndex < sections.length && /* @__PURE__ */ jsx12(Animated8.View, { style: [styles8.stickyOverlay, overlayStyle], children: renderSectionHeader({ section: sections[activeIndex] }) }) })
2997
+ /* @__PURE__ */ jsx12(ScrollViewChip, { chipProps, onChipPress, onPress: handleScrollToTop, style: chipStyle }),
2998
+ usesCustomSticky && /* @__PURE__ */ jsx12(Animated7.View, { pointerEvents: "none", style: [styles8.stickyClip, clipStyle], children: activeIndex >= 0 && activeIndex < sections.length && /* @__PURE__ */ jsx12(Animated7.View, { style: [styles8.stickyOverlay, overlayStyle], children: renderSectionHeader({ section: sections[activeIndex] }) }) })
2671
2999
  ] });
2672
3000
  return detectorGesture ? /* @__PURE__ */ jsx12(GestureDetector4, { gesture: detectorGesture, children: content }) : content;
2673
3001
  };
@@ -2679,15 +3007,15 @@ var styles8 = {
2679
3007
  var SectionList = memo4(SectionListInner);
2680
3008
 
2681
3009
  // src/useScrollView.ts
2682
- import { useContext as useContext10 } from "react";
3010
+ import { useContext as useContext15 } from "react";
2683
3011
  var useScrollView = () => {
2684
- const { progress, progressing, scrollHeight, scrollPosition, setProgress, setProgressing } = useContext10(ScrollViewContext);
3012
+ const { progress, progressing, scrollHeight, scrollPosition, setProgress, setProgressing } = useContext15(ScrollViewContext);
2685
3013
  return { progress, progressing, scrollHeight, scrollPosition, setProgress, setProgressing };
2686
3014
  };
2687
3015
 
2688
3016
  // src/useScrollViewSettings.ts
2689
- import { useContext as useContext11 } from "react";
2690
- var useScrollViewSettings = () => useContext11(ScrollViewSettingsContext);
3017
+ import { useContext as useContext16 } from "react";
3018
+ var useScrollViewSettings = () => useContext16(ScrollViewSettingsContext);
2691
3019
  export {
2692
3020
  CustomList,
2693
3021
  FlatList,