@rific/scroll-view 0.2.0 → 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.js CHANGED
@@ -56,6 +56,7 @@ module.exports = __toCommonJS(index_exports);
56
56
  var import_react6 = require("react");
57
57
  var import_react_native4 = require("react-native");
58
58
  var import_react_native_gesture_handler = require("react-native-gesture-handler");
59
+ var import_react_native_reanimated6 = require("react-native-reanimated");
59
60
 
60
61
  // src/internal/RefreshControl.tsx
61
62
  var import_react_native = require("react-native");
@@ -81,8 +82,8 @@ var styles = import_react_native2.StyleSheet.create({
81
82
  });
82
83
 
83
84
  // src/internal/useScrollHandler.ts
84
- var import_react2 = require("react");
85
- var import_react_native_reanimated2 = require("react-native-reanimated");
85
+ var import_react3 = require("react");
86
+ var import_react_native_reanimated3 = require("react-native-reanimated");
86
87
 
87
88
  // src/ScrollViewContext.ts
88
89
  var import_react = require("react");
@@ -92,10 +93,16 @@ var ScrollViewContext = (0, import_react.createContext)({
92
93
  footerHeightShared: null,
93
94
  footerFixed: false,
94
95
  footerOffset: null,
95
- headerHeight: 0,
96
+ headerHeight: null,
96
97
  headerHeightShared: null,
97
98
  headerFixed: false,
98
99
  headerOffset: null,
100
+ jsListGeneration: { current: 0 },
101
+ listGeneration: null,
102
+ onJsListUnmount: () => {
103
+ },
104
+ onListUnmount: () => {
105
+ },
99
106
  progress: null,
100
107
  progressing: false,
101
108
  pullSearchHeightShared: null,
@@ -114,17 +121,207 @@ var ScrollViewContext = (0, import_react.createContext)({
114
121
  tabBarHeight: 60
115
122
  });
116
123
 
124
+ // src/useScrollInit.ts
125
+ var import_react2 = require("react");
126
+ var import_react_native_reanimated2 = require("react-native-reanimated");
127
+ var REMOUNT_SYNC_MAX_ATTEMPTS = 8;
128
+ var REMOUNT_SYNC_TOLERANCE = 1;
129
+ var REMOUNT_SYNC_FALLBACK_MS = 500;
130
+ var REMOUNT_PRIME_FRAMES = 40;
131
+ var SNAP_VERIFY_TOLERANCE = 1;
132
+ var SNAP_VERIFY_MAX_ATTEMPTS = 5;
133
+ var SNAP_VERIFY_INITIAL_DELAY_MS = 400;
134
+ var SNAP_VERIFY_RETRY_DELAY_MS = 150;
135
+ function useScrollInit({ listHeaderComponent, onMomentumScrollEnd: externalMomentumScrollEnd, onRefresh, onScrollBeginDrag: externalScrollBeginDrag, onScrollEndDrag: externalScrollEndDrag, pullSearchHeight, scrollTo }) {
136
+ const { headerHeight, scrollPosition, setProgressing } = (0, import_react2.useContext)(ScrollViewContext);
137
+ const hasPullSearch = pullSearchHeight !== void 0;
138
+ const [phase, setPhase] = (0, import_react2.useState)(hasPullSearch && !headerHeight ? "measuring" : "ready");
139
+ const [refreshing, setRefreshing] = (0, import_react2.useState)(false);
140
+ const refreshingRef = (0, import_react2.useRef)(refreshing);
141
+ (0, import_react2.useEffect)(() => {
142
+ refreshingRef.current = refreshing;
143
+ }, [refreshing]);
144
+ const dragStartY = (0, import_react2.useRef)(null);
145
+ const pendingSnapTarget = (0, import_react2.useRef)(null);
146
+ const snapVerifyTimeout = (0, import_react2.useRef)(null);
147
+ const snapVerifyAttempts = (0, import_react2.useRef)(0);
148
+ const scheduleSnapVerification = (0, import_react2.useCallback)(
149
+ (target, delay) => {
150
+ if (snapVerifyTimeout.current !== null) clearTimeout(snapVerifyTimeout.current);
151
+ pendingSnapTarget.current = target;
152
+ snapVerifyAttempts.current = 0;
153
+ const check = () => {
154
+ snapVerifyTimeout.current = null;
155
+ if (pendingSnapTarget.current !== target) return;
156
+ if (Math.abs(scrollPosition.value - target) <= SNAP_VERIFY_TOLERANCE) {
157
+ pendingSnapTarget.current = null;
158
+ return;
159
+ }
160
+ snapVerifyAttempts.current += 1;
161
+ if (snapVerifyAttempts.current > SNAP_VERIFY_MAX_ATTEMPTS || refreshingRef.current) {
162
+ pendingSnapTarget.current = null;
163
+ return;
164
+ }
165
+ scrollTo(target, false);
166
+ snapVerifyTimeout.current = setTimeout(check, SNAP_VERIFY_RETRY_DELAY_MS);
167
+ };
168
+ snapVerifyTimeout.current = setTimeout(check, delay);
169
+ },
170
+ [scrollPosition, scrollTo]
171
+ );
172
+ (0, import_react2.useEffect)(
173
+ () => () => {
174
+ if (snapVerifyTimeout.current !== null) clearTimeout(snapVerifyTimeout.current);
175
+ },
176
+ []
177
+ );
178
+ const [remountTarget] = (0, import_react2.useState)(() => headerHeight !== null ? -headerHeight + (pullSearchHeight ?? 0) : null);
179
+ const remountSyncTarget = (0, import_react_native_reanimated2.useSharedValue)(remountTarget);
180
+ const [scrollReady, setScrollReady] = (0, import_react2.useState)(remountTarget === null);
181
+ const remountAttempts = (0, import_react2.useRef)(0);
182
+ const onRemountSynced = (0, import_react2.useCallback)(
183
+ (y) => {
184
+ scrollPosition.value = y;
185
+ setScrollReady(true);
186
+ },
187
+ [scrollPosition]
188
+ );
189
+ const onRemountSyncRetry = (0, import_react2.useCallback)(
190
+ (currentY) => {
191
+ if (remountTarget === null) return;
192
+ remountAttempts.current += 1;
193
+ if (remountAttempts.current > REMOUNT_SYNC_MAX_ATTEMPTS) {
194
+ remountSyncTarget.value = null;
195
+ onRemountSynced(currentY);
196
+ return;
197
+ }
198
+ scrollTo(remountTarget, false);
199
+ },
200
+ [onRemountSynced, remountSyncTarget, remountTarget, scrollTo]
201
+ );
202
+ (0, import_react2.useEffect)(() => {
203
+ if (remountTarget === null) return;
204
+ scrollTo(remountTarget, false);
205
+ const fallback = setTimeout(() => {
206
+ if (remountSyncTarget.value === null) return;
207
+ remountSyncTarget.value = null;
208
+ onRemountSynced(remountTarget);
209
+ }, REMOUNT_SYNC_FALLBACK_MS);
210
+ return () => clearTimeout(fallback);
211
+ }, []);
212
+ (0, import_react2.useEffect)(() => {
213
+ if (phase === "ready" || !headerHeight || !pullSearchHeight) return;
214
+ scrollPosition.value = -headerHeight + pullSearchHeight;
215
+ setPhase("ready");
216
+ }, [phase, headerHeight, pullSearchHeight, scrollPosition]);
217
+ const headerInitialized = (0, import_react2.useRef)(false);
218
+ (0, import_react2.useLayoutEffect)(() => {
219
+ if (!headerHeight || headerInitialized.current) return;
220
+ headerInitialized.current = true;
221
+ const target = remountTarget ?? -headerHeight;
222
+ scrollPosition.value = target;
223
+ scrollTo(target, false);
224
+ if (remountTarget === null) return;
225
+ let framesLeft = REMOUNT_PRIME_FRAMES;
226
+ let handle = null;
227
+ const tick = () => {
228
+ const offset = framesLeft === 1 ? target : framesLeft % 2 === 0 ? target + 30 : target - 30;
229
+ scrollTo(offset, false);
230
+ framesLeft -= 1;
231
+ if (framesLeft > 0) handle = requestAnimationFrame(tick);
232
+ };
233
+ handle = requestAnimationFrame(tick);
234
+ return () => {
235
+ if (handle !== null) cancelAnimationFrame(handle);
236
+ };
237
+ }, [remountTarget, headerHeight, scrollPosition, scrollTo]);
238
+ const pullSearchInitialized = (0, import_react2.useRef)(false);
239
+ (0, import_react2.useLayoutEffect)(() => {
240
+ if (remountTarget !== null || !hasPullSearch || phase !== "ready" || pullSearchInitialized.current || !headerHeight || !pullSearchHeight || !scrollReady) return;
241
+ pullSearchInitialized.current = true;
242
+ scrollPosition.value = -headerHeight + pullSearchHeight;
243
+ scrollTo(-headerHeight + pullSearchHeight, false);
244
+ }, [remountTarget, hasPullSearch, phase, headerHeight, pullSearchHeight, scrollPosition, scrollReady, scrollTo]);
245
+ (0, import_react2.useEffect)(() => {
246
+ if (pullSearchHeight) setProgressing(refreshing);
247
+ }, [pullSearchHeight, refreshing, setProgressing]);
248
+ const handleScrollBeginDrag = (0, import_react2.useCallback)(
249
+ (e) => {
250
+ if (snapVerifyTimeout.current !== null) {
251
+ clearTimeout(snapVerifyTimeout.current);
252
+ snapVerifyTimeout.current = null;
253
+ }
254
+ pendingSnapTarget.current = null;
255
+ if (pullSearchHeight) dragStartY.current = e.nativeEvent.contentOffset.y;
256
+ externalScrollBeginDrag?.(e);
257
+ },
258
+ [externalScrollBeginDrag, pullSearchHeight]
259
+ );
260
+ const handleScrollEndDrag = (0, import_react2.useCallback)(
261
+ (e) => {
262
+ if (pullSearchHeight && headerHeight) {
263
+ const y = e.nativeEvent.contentOffset.y;
264
+ const showY = -headerHeight;
265
+ const hideY = -headerHeight + pullSearchHeight;
266
+ const startY = dragStartY.current;
267
+ dragStartY.current = null;
268
+ const pulledDown = (startY ?? hideY) <= hideY;
269
+ const dragDistance = startY !== null ? startY - y : 0;
270
+ if (pulledDown && dragDistance >= pullSearchHeight * 0.5) {
271
+ if (y <= showY && !refreshing && onRefresh) {
272
+ setRefreshing(true);
273
+ Promise.resolve(onRefresh()).finally(() => setRefreshing(false));
274
+ }
275
+ scrollTo(showY, true);
276
+ scheduleSnapVerification(showY, SNAP_VERIFY_INITIAL_DELAY_MS);
277
+ } else if (y < hideY) {
278
+ scrollTo(hideY, true);
279
+ scheduleSnapVerification(hideY, SNAP_VERIFY_INITIAL_DELAY_MS);
280
+ }
281
+ }
282
+ externalScrollEndDrag?.(e);
283
+ },
284
+ [externalScrollEndDrag, headerHeight, onRefresh, pullSearchHeight, refreshing, scheduleSnapVerification, scrollTo]
285
+ );
286
+ const handleMomentumScrollEnd = (0, import_react2.useCallback)(
287
+ (e) => {
288
+ if (pullSearchHeight && headerHeight && !refreshing && pendingSnapTarget.current === null) {
289
+ const y = e.nativeEvent.contentOffset.y;
290
+ const hideY = -headerHeight + pullSearchHeight;
291
+ if (y < hideY - 1) {
292
+ scrollTo(hideY, true);
293
+ scheduleSnapVerification(hideY, SNAP_VERIFY_INITIAL_DELAY_MS);
294
+ }
295
+ }
296
+ externalMomentumScrollEnd?.(e);
297
+ },
298
+ [externalMomentumScrollEnd, headerHeight, pullSearchHeight, refreshing, scheduleSnapVerification, scrollTo]
299
+ );
300
+ const activeListHeader = hasPullSearch && phase === "measuring" ? null : listHeaderComponent;
301
+ const hiddenHeader = phase === "measuring" && listHeaderComponent != null ? typeof listHeaderComponent === "function" ? (0, import_react2.createElement)(listHeaderComponent) : listHeaderComponent : null;
302
+ return { activeListHeader, handleMomentumScrollEnd, handleScrollBeginDrag, handleScrollEndDrag, hiddenHeader, onRemountSyncRetry, onRemountSynced, pullSearchMinHeight: pullSearchHeight ?? 0, remountSyncTarget, remountTarget, scrollReady };
303
+ }
304
+
117
305
  // src/internal/useScrollHandler.ts
118
- function useScrollHandler({ chipHidden, chipThreshold = 100, footerFixed, headerFixed, isHorizontal, onPullSearchZoneEnter }) {
119
- const { footerHeightShared, footerOffset, headerHeightShared, headerOffset, pullSearchHeightShared, scrollPosition, snapBackFooterShared, snapBackHeaderShared } = (0, import_react2.useContext)(ScrollViewContext);
120
- const snapUpAccum = (0, import_react_native_reanimated2.useSharedValue)(0);
121
- const fromBottomBounce = (0, import_react_native_reanimated2.useSharedValue)(false);
122
- const isMomentum = (0, import_react_native_reanimated2.useSharedValue)(false);
123
- const momentumPullSearchFired = (0, import_react_native_reanimated2.useSharedValue)(false);
124
- return (0, import_react_native_reanimated2.useAnimatedScrollHandler)(
306
+ function useScrollHandler({ capturedGeneration, chipHidden, chipThreshold = 100, footerFixed, headerFixed, isHorizontal, listGeneration, onRemountSyncRetry, onRemountSynced, remountSyncTarget }) {
307
+ const { footerHeightShared, footerOffset, headerHeightShared, headerOffset, pullSearchHeightShared, scrollPosition, snapBackFooterShared, snapBackHeaderShared } = (0, import_react3.useContext)(ScrollViewContext);
308
+ const snapUpAccum = (0, import_react_native_reanimated3.useSharedValue)(0);
309
+ const fromBottomBounce = (0, import_react_native_reanimated3.useSharedValue)(false);
310
+ return (0, import_react_native_reanimated3.useAnimatedScrollHandler)(
125
311
  {
126
312
  onScroll: ({ contentOffset: { x, y }, contentSize: { height: contentHeight }, layoutMeasurement: { height: layoutHeight } }) => {
127
313
  "worklet";
314
+ if (remountSyncTarget && remountSyncTarget.value !== null) {
315
+ const target = remountSyncTarget.value;
316
+ if (Math.abs(y - target) < REMOUNT_SYNC_TOLERANCE) {
317
+ remountSyncTarget.value = null;
318
+ if (onRemountSynced) (0, import_react_native_reanimated3.runOnJS)(onRemountSynced)(y);
319
+ } else if (onRemountSyncRetry) {
320
+ (0, import_react_native_reanimated3.runOnJS)(onRemountSyncRetry)(y);
321
+ }
322
+ return;
323
+ }
324
+ if (listGeneration.value !== capturedGeneration.value) return;
128
325
  if (isHorizontal) {
129
326
  scrollPosition.value = x;
130
327
  chipHidden.value = x < chipThreshold ? 1 : 0;
@@ -140,13 +337,6 @@ function useScrollHandler({ chipHidden, chipThreshold = 100, footerFixed, header
140
337
  } else if (fromBottomBounce.value) {
141
338
  fromBottomBounce.value = false;
142
339
  }
143
- if (isMomentum.value && pullSearchHeightShared.value > 0 && !momentumPullSearchFired.value) {
144
- const hideY = -headerHeightShared.value + pullSearchHeightShared.value;
145
- if (y < hideY) {
146
- momentumPullSearchFired.value = true;
147
- if (onPullSearchZoneEnter) (0, import_react_native_reanimated2.runOnJS)(onPullSearchZoneEnter)();
148
- }
149
- }
150
340
  const snapHeader = snapBackHeaderShared.value && !headerFixed;
151
341
  const snapFooter = snapBackFooterShared.value && !footerFixed;
152
342
  if (snapHeader || snapFooter) {
@@ -164,24 +354,17 @@ function useScrollHandler({ chipHidden, chipThreshold = 100, footerFixed, header
164
354
  if (y >= -headerHeightShared.value + pullSearchHeightShared.value) snapUpAccum.value -= delta;
165
355
  if (snapUpAccum.value >= 10) {
166
356
  snapUpAccum.value = 999;
167
- if (snapHeader) headerOffset.value = (0, import_react_native_reanimated2.withTiming)(0, { duration: 200 });
168
- if (snapFooter) footerOffset.value = (0, import_react_native_reanimated2.withTiming)(0, { duration: 200 });
357
+ if (snapHeader) headerOffset.value = (0, import_react_native_reanimated3.withTiming)(0, { duration: 200 });
358
+ if (snapFooter) footerOffset.value = (0, import_react_native_reanimated3.withTiming)(0, { duration: 200 });
169
359
  }
170
360
  }
171
361
  }
172
362
  },
173
363
  onBeginDrag: () => {
174
364
  "worklet";
175
- isMomentum.value = false;
176
- momentumPullSearchFired.value = false;
177
365
  fromBottomBounce.value = false;
178
366
  snapUpAccum.value = 0;
179
367
  },
180
- onMomentumBegin: () => {
181
- "worklet";
182
- isMomentum.value = true;
183
- momentumPullSearchFired.value = false;
184
- },
185
368
  onEndDrag: ({ contentOffset: { y }, contentSize: { height: contentHeight }, layoutMeasurement: { height: layoutHeight } }) => {
186
369
  "worklet";
187
370
  if (isHorizontal) return;
@@ -192,37 +375,35 @@ function useScrollHandler({ chipHidden, chipThreshold = 100, footerFixed, header
192
375
  },
193
376
  onMomentumEnd: () => {
194
377
  "worklet";
195
- isMomentum.value = false;
196
- momentumPullSearchFired.value = false;
197
378
  fromBottomBounce.value = false;
198
379
  snapUpAccum.value = 0;
199
380
  }
200
381
  },
201
- [headerFixed, footerFixed, isHorizontal, chipThreshold, onPullSearchZoneEnter]
382
+ [capturedGeneration, headerFixed, footerFixed, isHorizontal, chipThreshold, listGeneration, onRemountSyncRetry, onRemountSynced, remountSyncTarget]
202
383
  );
203
384
  }
204
385
 
205
386
  // src/internal/useScrollList.ts
206
- var import_react4 = require("react");
387
+ var import_react5 = require("react");
207
388
  var import_react_native3 = require("react-native");
208
- var import_react_native_reanimated4 = require("react-native-reanimated");
389
+ var import_react_native_reanimated5 = require("react-native-reanimated");
209
390
  var import_react_native_safe_area_context = require("react-native-safe-area-context");
210
391
 
211
392
  // src/useKeyboardInset.ts
212
- var import_react3 = require("react");
393
+ var import_react4 = require("react");
213
394
  var import_react_native_keyboard_controller = require("react-native-keyboard-controller");
214
- var import_react_native_reanimated3 = require("react-native-reanimated");
395
+ var import_react_native_reanimated4 = require("react-native-reanimated");
215
396
  var useKeyboardInset = () => {
216
- const [keyboardHeight, setKeyboardHeight] = (0, import_react3.useState)(0);
397
+ const [keyboardHeight, setKeyboardHeight] = (0, import_react4.useState)(0);
217
398
  (0, import_react_native_keyboard_controller.useKeyboardHandler)(
218
399
  {
219
400
  onMove: (e) => {
220
401
  "worklet";
221
- (0, import_react_native_reanimated3.runOnJS)(setKeyboardHeight)(e.height);
402
+ (0, import_react_native_reanimated4.runOnJS)(setKeyboardHeight)(e.height);
222
403
  },
223
404
  onEnd: (e) => {
224
405
  "worklet";
225
- (0, import_react_native_reanimated3.runOnJS)(setKeyboardHeight)(e.height);
406
+ (0, import_react_native_reanimated4.runOnJS)(setKeyboardHeight)(e.height);
226
407
  }
227
408
  },
228
409
  []
@@ -235,122 +416,55 @@ var CHIP_SLIDE = 48;
235
416
  function useScrollList({ footerFixed: footerFixedProp, headerFixed: headerFixedProp, isHorizontal, keyboardAware, pullSearchHeight, style } = {}) {
236
417
  const insets = (0, import_react_native_safe_area_context.useSafeAreaInsets)();
237
418
  const keyboardHeight = useKeyboardInset();
238
- const { footerHeight, footerFixed: contextFooterFixed, headerHeight, headerFixed: contextHeaderFixed, headerOffset, pullSearchHeightShared, scrollPosition, snapBackHeaderShared } = (0, import_react4.useContext)(ScrollViewContext);
419
+ const { footerHeight, footerFixed: contextFooterFixed, headerHeight, headerFixed: contextHeaderFixed, headerOffset, pullSearchHeightShared, scrollPosition, snapBackHeaderShared } = (0, import_react5.useContext)(ScrollViewContext);
239
420
  const headerFixed = isHorizontal ? true : headerFixedProp ?? contextHeaderFixed;
240
421
  const footerFixed = isHorizontal ? true : footerFixedProp ?? contextFooterFixed;
241
- (0, import_react4.useEffect)(() => {
422
+ (0, import_react5.useEffect)(() => {
242
423
  pullSearchHeightShared.value = pullSearchHeight ?? 0;
243
424
  }, [pullSearchHeight, pullSearchHeightShared]);
244
- const containerStyle = (0, import_react4.useMemo)(() => headerHeight === 0 ? [{ flex: 1 }, style] : [import_react_native3.StyleSheet.absoluteFill, style], [headerHeight, style]);
245
- const contentInset = (0, import_react4.useMemo)(() => ({ bottom: (footerFixed ? footerHeight || insets.bottom : insets.bottom) + (keyboardAware ? keyboardHeight : 0), top: headerHeight }), [footerFixed, footerHeight, insets.bottom, headerHeight, keyboardAware, keyboardHeight]);
246
- const contentOffset = (0, import_react4.useMemo)(() => ({ x: 0, y: -contentInset.top }), [contentInset.top]);
247
- const chipHidden = (0, import_react_native_reanimated4.useSharedValue)(1);
248
- const chipStyle = (0, import_react_native_reanimated4.useAnimatedStyle)(() => {
425
+ const containerStyle = (0, import_react5.useMemo)(() => headerHeight === null ? [{ flex: 1, opacity: 0 }, style] : [import_react_native3.StyleSheet.absoluteFill, style], [headerHeight, style]);
426
+ const contentInset = (0, import_react5.useMemo)(() => ({ bottom: (footerFixed ? footerHeight || insets.bottom : insets.bottom) + (keyboardAware ? keyboardHeight : 0), top: headerHeight ?? 0 }), [footerFixed, footerHeight, insets.bottom, headerHeight, keyboardAware, keyboardHeight]);
427
+ const [startedWithHeader] = (0, import_react5.useState)(headerHeight !== null);
428
+ const contentOffset = (0, import_react5.useMemo)(() => ({ x: 0, y: headerHeight !== null ? -headerHeight + (startedWithHeader && !isHorizontal && pullSearchHeight ? pullSearchHeight : 0) : 0 }), [headerHeight, isHorizontal, pullSearchHeight, startedWithHeader]);
429
+ const chipHidden = (0, import_react_native_reanimated5.useSharedValue)(1);
430
+ const chipStyle = (0, import_react_native_reanimated5.useAnimatedStyle)(() => {
249
431
  const pointerEvents = chipHidden.value ? "none" : "box-none";
250
432
  if (isHorizontal) {
251
433
  return {
252
- opacity: chipHidden.value ? (0, import_react_native_reanimated4.withTiming)(0) : (0, import_react_native_reanimated4.withTiming)(1),
434
+ opacity: chipHidden.value ? (0, import_react_native_reanimated5.withTiming)(0) : (0, import_react_native_reanimated5.withTiming)(1),
253
435
  pointerEvents,
254
- top: Math.max(headerHeight, insets.top) + 4,
255
- transform: [{ translateX: chipHidden.value ? (0, import_react_native_reanimated4.withTiming)(-CHIP_SLIDE) : (0, import_react_native_reanimated4.withTiming)(0) }]
436
+ top: Math.max(headerHeight ?? 0, insets.top) + 4,
437
+ transform: [{ translateX: chipHidden.value ? (0, import_react_native_reanimated5.withTiming)(-CHIP_SLIDE) : (0, import_react_native_reanimated5.withTiming)(0) }]
256
438
  };
257
439
  }
258
- const slide = headerFixed ? 0 : snapBackHeaderShared.value ? -headerOffset.value : Math.max(0, scrollPosition.value + headerHeight - pullSearchHeightShared.value);
259
- const top = Math.max(headerHeight - slide, insets.top) + 4;
440
+ const h = headerHeight ?? 0;
441
+ const slide = headerFixed ? 0 : snapBackHeaderShared.value ? -headerOffset.value : Math.max(0, scrollPosition.value + h - pullSearchHeightShared.value);
442
+ const top = Math.max(h - slide, insets.top) + 4;
260
443
  return {
261
- opacity: chipHidden.value ? (0, import_react_native_reanimated4.withTiming)(0) : (0, import_react_native_reanimated4.withTiming)(1),
444
+ opacity: chipHidden.value ? (0, import_react_native_reanimated5.withTiming)(0) : (0, import_react_native_reanimated5.withTiming)(1),
262
445
  pointerEvents,
263
446
  top,
264
- transform: [{ translateY: chipHidden.value ? (0, import_react_native_reanimated4.withTiming)(-CHIP_SLIDE) : (0, import_react_native_reanimated4.withTiming)(0) }]
447
+ transform: [{ translateY: chipHidden.value ? (0, import_react_native_reanimated5.withTiming)(-CHIP_SLIDE) : (0, import_react_native_reanimated5.withTiming)(0) }]
265
448
  };
266
449
  }, [isHorizontal, headerFixed, headerHeight, insets.top]);
267
450
  return { chipHidden, chipStyle, containerStyle, contentInset, contentOffset, footerFixed, headerFixed, headerHeight, insets, isHorizontal: isHorizontal ?? false };
268
451
  }
269
452
 
270
- // src/useScrollInit.ts
271
- var import_react5 = require("react");
272
- function useScrollInit({ listHeaderComponent, onMomentumScrollEnd: externalMomentumScrollEnd, onRefresh, onScrollBeginDrag: externalScrollBeginDrag, onScrollEndDrag: externalScrollEndDrag, pullSearchHeight, scrollTo }) {
273
- const { headerHeight, scrollPosition, setProgressing } = (0, import_react5.useContext)(ScrollViewContext);
274
- const hasPullSearch = pullSearchHeight !== void 0;
275
- const [phase, setPhase] = (0, import_react5.useState)(hasPullSearch ? "measuring" : "ready");
276
- const [refreshing, setRefreshing] = (0, import_react5.useState)(false);
277
- const dragStartY = (0, import_react5.useRef)(null);
278
- const deliberatelyOpened = (0, import_react5.useRef)(false);
279
- (0, import_react5.useEffect)(() => {
280
- if (phase === "ready" || !headerHeight || !pullSearchHeight) return;
281
- scrollPosition.value = -headerHeight + pullSearchHeight;
282
- setPhase("ready");
283
- }, [phase, headerHeight, pullSearchHeight, scrollPosition]);
284
- const headerInitialized = (0, import_react5.useRef)(false);
285
- (0, import_react5.useLayoutEffect)(() => {
286
- if (!headerHeight || headerInitialized.current) return;
287
- headerInitialized.current = true;
288
- scrollPosition.value = -headerHeight;
289
- scrollTo(-headerHeight, false);
290
- }, [headerHeight, scrollPosition, scrollTo]);
291
- const pullSearchInitialized = (0, import_react5.useRef)(false);
292
- (0, import_react5.useLayoutEffect)(() => {
293
- if (!hasPullSearch || phase !== "ready" || pullSearchInitialized.current || !headerHeight || !pullSearchHeight) return;
294
- pullSearchInitialized.current = true;
295
- scrollPosition.value = -headerHeight + pullSearchHeight;
296
- scrollTo(-headerHeight + pullSearchHeight, false);
297
- }, [hasPullSearch, phase, headerHeight, pullSearchHeight, scrollPosition, scrollTo]);
298
- (0, import_react5.useEffect)(() => {
299
- if (pullSearchHeight) setProgressing(refreshing);
300
- }, [pullSearchHeight, refreshing, setProgressing]);
301
- const handleScrollBeginDrag = (0, import_react5.useCallback)(
302
- (e) => {
303
- deliberatelyOpened.current = false;
304
- if (pullSearchHeight) dragStartY.current = e.nativeEvent.contentOffset.y;
305
- externalScrollBeginDrag?.(e);
306
- },
307
- [externalScrollBeginDrag, pullSearchHeight]
308
- );
309
- const handleScrollEndDrag = (0, import_react5.useCallback)(
310
- (e) => {
311
- if (pullSearchHeight && headerHeight) {
312
- const y = e.nativeEvent.contentOffset.y;
313
- const showY = -headerHeight;
314
- const hideY = -headerHeight + pullSearchHeight;
315
- const pulledDown = (dragStartY.current ?? hideY) <= hideY;
316
- dragStartY.current = null;
317
- if (pulledDown && y <= showY + pullSearchHeight * 0.5) {
318
- deliberatelyOpened.current = true;
319
- if (y <= showY && !refreshing && onRefresh) {
320
- setRefreshing(true);
321
- Promise.resolve(onRefresh()).finally(() => setRefreshing(false));
322
- }
323
- scrollTo(showY, true);
324
- } else if (y < hideY) {
325
- scrollTo(hideY, true);
326
- }
327
- }
328
- externalScrollEndDrag?.(e);
329
- },
330
- [externalScrollEndDrag, headerHeight, onRefresh, pullSearchHeight, refreshing, scrollTo]
331
- );
332
- const handleMomentumScrollEnd = (0, import_react5.useCallback)(
333
- (e) => {
334
- if (pullSearchHeight && headerHeight && !deliberatelyOpened.current && !refreshing) {
335
- const y = e.nativeEvent.contentOffset.y;
336
- const hideY = -headerHeight + pullSearchHeight;
337
- if (y < hideY - 1) scrollTo(hideY, true);
338
- }
339
- deliberatelyOpened.current = false;
340
- externalMomentumScrollEnd?.(e);
341
- },
342
- [externalMomentumScrollEnd, headerHeight, pullSearchHeight, refreshing, scrollTo]
343
- );
344
- const activeListHeader = hasPullSearch && phase === "measuring" ? null : listHeaderComponent;
345
- const hiddenHeader = phase === "measuring" && listHeaderComponent != null ? typeof listHeaderComponent === "function" ? (0, import_react5.createElement)(listHeaderComponent) : listHeaderComponent : null;
346
- return { activeListHeader, handleMomentumScrollEnd, handleScrollBeginDrag, handleScrollEndDrag, hiddenHeader, pullSearchMinHeight: pullSearchHeight ?? 0 };
347
- }
348
-
349
453
  // src/CustomList.tsx
350
454
  var import_jsx_runtime3 = require("react/jsx-runtime");
351
455
  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 }) => {
352
456
  const { ListHeaderComponent: listHeaderComponent, contentContainerStyle: externalContentContainerStyle, ...restProps } = props;
353
457
  const { chipHidden, chipStyle, containerStyle, contentInset, contentOffset, footerFixed, headerFixed } = useScrollList({ footerFixed: footerFixedProp, headerFixed: headerFixedProp, keyboardAware, pullSearchHeight, style });
458
+ const { listGeneration, onListUnmount } = (0, import_react6.useContext)(ScrollViewContext);
459
+ const capturedGeneration = (0, import_react_native_reanimated6.useSharedValue)(-1);
460
+ (0, import_react6.useLayoutEffect)(() => {
461
+ const generation = listGeneration.value;
462
+ (0, import_react_native_reanimated6.runOnUI)((gen) => {
463
+ "worklet";
464
+ capturedGeneration.value = gen;
465
+ })(generation);
466
+ }, [capturedGeneration, listGeneration]);
467
+ (0, import_react6.useLayoutEffect)(() => () => onListUnmount(), [onListUnmount]);
354
468
  const scrollViewInternal = (0, import_react6.useRef)(null);
355
469
  const handleRef = (0, import_react6.useCallback)(
356
470
  (el) => {
@@ -368,7 +482,10 @@ var CustomListInner = ({ chipProps, chipThreshold, component: List, renderFilter
368
482
  handleScrollBeginDrag,
369
483
  handleScrollEndDrag,
370
484
  hiddenHeader,
371
- pullSearchMinHeight
485
+ onRemountSyncRetry,
486
+ onRemountSynced,
487
+ pullSearchMinHeight,
488
+ remountSyncTarget
372
489
  } = useScrollInit({
373
490
  listHeaderComponent,
374
491
  onMomentumScrollEnd: externalMomentumScrollEnd,
@@ -387,11 +504,7 @@ var CustomListInner = ({ chipProps, chipThreshold, component: List, renderFilter
387
504
  ] });
388
505
  }, [pullSearchHeader, renderFilters]);
389
506
  const contentContainerStyle = (0, import_react6.useMemo)(() => [{ minHeight: import_react_native4.Dimensions.get("window").height - contentInset.top - contentInset.bottom + pullSearchMinHeight }, externalContentContainerStyle], [contentInset.bottom, contentInset.top, externalContentContainerStyle, pullSearchMinHeight]);
390
- const onPullSearchZoneEnter = (0, import_react6.useCallback)(() => {
391
- if (!pullSearchHeight) return;
392
- scrollViewInternal.current?.scrollToOffset({ offset: -contentInset.top + pullSearchHeight, animated: true });
393
- }, [contentInset.top, pullSearchHeight]);
394
- const onScroll = useScrollHandler({ chipHidden, chipThreshold, footerFixed, headerFixed, onPullSearchZoneEnter: pullSearchHeight ? onPullSearchZoneEnter : void 0 });
507
+ const onScroll = useScrollHandler({ capturedGeneration, chipHidden, chipThreshold, footerFixed, headerFixed, listGeneration, onRemountSyncRetry, onRemountSynced, remountSyncTarget });
395
508
  const handleScrollToTop = (0, import_react6.useCallback)(() => {
396
509
  const offset = pullSearchHeight ? -contentInset.top + pullSearchHeight : -contentInset.top;
397
510
  scrollViewInternal.current?.scrollToOffset({ offset, animated: true });
@@ -412,17 +525,16 @@ var styles2 = { measureContainer: { left: 0, opacity: 0, position: "absolute", r
412
525
  var CustomList = (0, import_react6.memo)(CustomListInner);
413
526
 
414
527
  // src/FlatList.tsx
415
- var import_react8 = require("react");
528
+ var import_react9 = require("react");
416
529
  var import_react_native6 = require("react-native");
417
530
  var import_react_native_gesture_handler2 = require("react-native-gesture-handler");
418
- var import_react_native_reanimated6 = __toESM(require("react-native-reanimated"));
419
531
 
420
532
  // src/internal/HorizontalDots.tsx
421
533
  var import_auto_paper2 = require("@rific/auto-paper");
422
534
  var import_react7 = require("react");
423
535
  var import_react_native5 = require("react-native");
424
536
  var import_react_native_paper = require("react-native-paper");
425
- var import_react_native_reanimated5 = __toESM(require("react-native-reanimated"));
537
+ var import_react_native_reanimated7 = __toESM(require("react-native-reanimated"));
426
538
  var import_react_native_safe_area_context2 = require("react-native-safe-area-context");
427
539
  var import_jsx_runtime4 = require("react/jsx-runtime");
428
540
  var DOT_FULL = 8;
@@ -437,15 +549,15 @@ var PILL_HEIGHT = DOT_FULL + PILL_PAD_V * 2;
437
549
  var PILL_RADIUS = PILL_HEIGHT / 2;
438
550
  var PILL_WIDTH = CLIP_WIDTH + PILL_PAD_H * 2;
439
551
  var Dot = ({ activeColor, index, inactiveColor, pageWidth, scrollPosition }) => {
440
- const circleStyle = (0, import_react_native_reanimated5.useAnimatedStyle)(() => {
552
+ const circleStyle = (0, import_react_native_reanimated7.useAnimatedStyle)(() => {
441
553
  const page = scrollPosition.value / pageWidth;
442
554
  const distance = Math.abs(index - page);
443
555
  const t = Math.max(0, Math.min((distance - 0.5) / 2, 1));
444
556
  const size = DOT_FULL + (DOT_SMALL - DOT_FULL) * t;
445
- const backgroundColor = (0, import_react_native_reanimated5.interpolateColor)(t, [0, 1], [activeColor, inactiveColor]);
557
+ const backgroundColor = (0, import_react_native_reanimated7.interpolateColor)(t, [0, 1], [activeColor, inactiveColor]);
446
558
  return { backgroundColor, borderRadius: size / 2, height: size, width: size };
447
559
  });
448
- return /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(import_react_native5.View, { style: styles3.dotContainer, children: /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(import_react_native_reanimated5.default.View, { style: circleStyle }) });
560
+ return /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(import_react_native5.View, { style: styles3.dotContainer, children: /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(import_react_native_reanimated7.default.View, { style: circleStyle }) });
449
561
  };
450
562
  var HorizontalDots = ({ total }) => {
451
563
  const { blur, footerHeightShared, scrollPosition } = (0, import_react7.useContext)(ScrollViewContext);
@@ -455,18 +567,18 @@ var HorizontalDots = ({ total }) => {
455
567
  const insetsBottom = insets.bottom;
456
568
  const activeColor = theme.colors.primary;
457
569
  const inactiveColor = theme.colors.outlineVariant;
458
- const pillContainerStyle = (0, import_react_native_reanimated5.useAnimatedStyle)(() => ({
570
+ const pillContainerStyle = (0, import_react_native_reanimated7.useAnimatedStyle)(() => ({
459
571
  bottom: Math.max(footerHeightShared.value, insetsBottom) + 8
460
572
  }));
461
- const rowStyle = (0, import_react_native_reanimated5.useAnimatedStyle)(() => {
573
+ const rowStyle = (0, import_react_native_reanimated7.useAnimatedStyle)(() => {
462
574
  const page = scrollPosition.value / width;
463
575
  const maxOffset = Math.max(0, (total - CLIP_COUNT) * DOT_STEP);
464
576
  const offset = Math.max(0, Math.min((page - (CLIP_COUNT - 1) / 2) * DOT_STEP, maxOffset));
465
577
  return { transform: [{ translateX: -offset }] };
466
578
  });
467
- return /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(import_react_native_reanimated5.default.View, { pointerEvents: "none", style: [styles3.pillContainer, pillContainerStyle], children: /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(import_react_native5.View, { style: styles3.pill, children: [
579
+ return /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(import_react_native_reanimated7.default.View, { pointerEvents: "none", style: [styles3.pillContainer, pillContainerStyle], children: /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(import_react_native5.View, { style: styles3.pill, children: [
468
580
  /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(import_auto_paper2.BlurView, { blur, style: import_react_native5.StyleSheet.absoluteFill }),
469
- /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(import_react_native5.View, { style: styles3.dotsClip, children: /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(import_react_native_reanimated5.default.View, { style: [styles3.dotsRow, rowStyle], children: Array.from({ length: total }).map((_, i) => /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(Dot, { activeColor, index: i, inactiveColor, pageWidth: width, scrollPosition }, i)) }) })
581
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(import_react_native5.View, { style: styles3.dotsClip, children: /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(import_react_native_reanimated7.default.View, { style: [styles3.dotsRow, rowStyle], children: Array.from({ length: total }).map((_, i) => /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(Dot, { activeColor, index: i, inactiveColor, pageWidth: width, scrollPosition }, i)) }) })
470
582
  ] }) });
471
583
  };
472
584
  var styles3 = import_react_native5.StyleSheet.create({
@@ -484,14 +596,144 @@ var styles3 = import_react_native5.StyleSheet.create({
484
596
  pillContainer: { alignItems: "center", left: 0, position: "absolute", right: 0, zIndex: 3 }
485
597
  });
486
598
 
599
+ // src/internal/useScrollHandlerJS.ts
600
+ var import_react8 = require("react");
601
+ var import_react_native_reanimated8 = require("react-native-reanimated");
602
+ var REMOUNT_RETRY_TOLERANCE = 1;
603
+ var REMOUNT_RETRY_MAX_ATTEMPTS = 8;
604
+ function useScrollHandlerJS({ capturedGeneration, chipHidden, chipThreshold = 100, footerFixed, headerFixed, isHorizontal, jsListGeneration, remountTarget = null, scrollTo }) {
605
+ const { footerHeightShared, footerOffset, headerHeightShared, headerOffset, pullSearchHeightShared, scrollPosition, snapBackFooterShared, snapBackHeaderShared } = (0, import_react8.useContext)(ScrollViewContext);
606
+ const snapUpAccum = (0, import_react8.useRef)(0);
607
+ const fromBottomBounce = (0, import_react8.useRef)(false);
608
+ const pendingRemountTarget = (0, import_react8.useRef)(remountTarget);
609
+ const remountAttempts = (0, import_react8.useRef)(0);
610
+ const onScroll = (0, import_react8.useCallback)(
611
+ (event) => {
612
+ if (jsListGeneration.current !== capturedGeneration.current) return;
613
+ const {
614
+ contentOffset: { x, y },
615
+ contentSize: { height: contentHeight },
616
+ layoutMeasurement: { height: layoutHeight }
617
+ } = event.nativeEvent;
618
+ if (pendingRemountTarget.current !== null) {
619
+ const target = pendingRemountTarget.current;
620
+ if (Math.abs(y - target) < REMOUNT_RETRY_TOLERANCE) {
621
+ pendingRemountTarget.current = null;
622
+ } else {
623
+ remountAttempts.current += 1;
624
+ if (remountAttempts.current > REMOUNT_RETRY_MAX_ATTEMPTS) {
625
+ pendingRemountTarget.current = null;
626
+ } else {
627
+ scrollTo?.(target, false);
628
+ return;
629
+ }
630
+ }
631
+ }
632
+ if (isHorizontal) {
633
+ scrollPosition.value = x;
634
+ chipHidden.value = x < chipThreshold ? 1 : 0;
635
+ return;
636
+ }
637
+ const delta = y - scrollPosition.value;
638
+ scrollPosition.value = y;
639
+ chipHidden.value = y < chipThreshold ? 1 : 0;
640
+ const maxScroll = contentHeight - layoutHeight;
641
+ if (maxScroll > 0 && y >= maxScroll) {
642
+ fromBottomBounce.current = true;
643
+ snapUpAccum.current = 0;
644
+ } else if (fromBottomBounce.current) {
645
+ fromBottomBounce.current = false;
646
+ }
647
+ const snapHeader = snapBackHeaderShared.value && !headerFixed;
648
+ const snapFooter = snapBackFooterShared.value && !footerFixed;
649
+ if (snapHeader || snapFooter) {
650
+ if (y <= -headerHeightShared.value) {
651
+ snapUpAccum.current = 0;
652
+ if (snapHeader) headerOffset.value = 0;
653
+ if (snapFooter) footerOffset.value = 0;
654
+ } else if (delta > 0) {
655
+ snapUpAccum.current = 0;
656
+ if (y >= -headerHeightShared.value + pullSearchHeightShared.value) {
657
+ if (snapHeader) headerOffset.value = Math.max(-headerHeightShared.value, Math.min(0, headerOffset.value - delta));
658
+ if (snapFooter) footerOffset.value = Math.max(0, Math.min(footerHeightShared.value, footerOffset.value + delta));
659
+ }
660
+ } else if (delta < 0 && !fromBottomBounce.current) {
661
+ if (y >= -headerHeightShared.value + pullSearchHeightShared.value) snapUpAccum.current -= delta;
662
+ if (snapUpAccum.current >= 10) {
663
+ snapUpAccum.current = 999;
664
+ if (snapHeader) headerOffset.value = (0, import_react_native_reanimated8.withTiming)(0, { duration: 200 });
665
+ if (snapFooter) footerOffset.value = (0, import_react_native_reanimated8.withTiming)(0, { duration: 200 });
666
+ }
667
+ }
668
+ }
669
+ },
670
+ [capturedGeneration, chipHidden, chipThreshold, footerFixed, footerHeightShared, footerOffset, headerFixed, headerHeightShared, headerOffset, isHorizontal, jsListGeneration, pullSearchHeightShared, scrollPosition, scrollTo, snapBackFooterShared, snapBackHeaderShared]
671
+ );
672
+ const onScrollBeginDrag = (0, import_react8.useCallback)(() => {
673
+ fromBottomBounce.current = false;
674
+ snapUpAccum.current = 0;
675
+ }, []);
676
+ const onMomentumScrollBegin = (0, import_react8.useCallback)(() => {
677
+ }, []);
678
+ const onScrollEndDrag = (0, import_react8.useCallback)(
679
+ (event) => {
680
+ if (isHorizontal) return;
681
+ const {
682
+ contentOffset: { y },
683
+ contentSize: { height: contentHeight },
684
+ layoutMeasurement: { height: layoutHeight }
685
+ } = event.nativeEvent;
686
+ if (y >= contentHeight - layoutHeight - 10) {
687
+ fromBottomBounce.current = true;
688
+ snapUpAccum.current = 0;
689
+ }
690
+ },
691
+ [isHorizontal]
692
+ );
693
+ const onMomentumScrollEnd = (0, import_react8.useCallback)(() => {
694
+ fromBottomBounce.current = false;
695
+ snapUpAccum.current = 0;
696
+ }, []);
697
+ return { onMomentumScrollBegin, onMomentumScrollEnd, onScroll, onScrollBeginDrag, onScrollEndDrag };
698
+ }
699
+
487
700
  // src/FlatList.tsx
488
701
  var import_jsx_runtime5 = require("react/jsx-runtime");
489
- var FlatListInner = ({ chipProps, chipThreshold, contentContainerStyle: externalContentContainerStyle, data, initialNumToRender = 20, maxToRenderPerBatch = 50, renderFilters, 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 }) => {
490
- const scrollView = (0, import_react8.useRef)(null);
702
+ 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 }) => {
703
+ const scrollView = (0, import_react9.useRef)(null);
491
704
  const isHorizontal = horizontal === true;
492
705
  const showDots = isHorizontal && pagingEnabled === true && (data?.length ?? 0) > 1;
706
+ const cols = numColumns && numColumns > 1 ? numColumns : 1;
707
+ const rowData = (0, import_react9.useMemo)(() => {
708
+ if (cols <= 1 || !data) return data;
709
+ const items = Array.from(data);
710
+ const rows = [];
711
+ for (let i = 0; i < items.length; i += cols) rows.push(items.slice(i, i + cols));
712
+ return rows;
713
+ }, [cols, data]);
714
+ const rowKeyExtractor = (0, import_react9.useCallback)(
715
+ (row, rowIndex) => {
716
+ if (cols <= 1) return keyExtractor ? keyExtractor(row, rowIndex) : defaultKeyExtractor(row, rowIndex);
717
+ return row.map((item, kk) => keyExtractor ? keyExtractor(item, rowIndex * cols + kk) : defaultKeyExtractor(item, rowIndex * cols + kk)).join(":");
718
+ },
719
+ [cols, keyExtractor]
720
+ );
721
+ const rowRenderItem = (0, import_react9.useCallback)(
722
+ (info) => {
723
+ if (cols <= 1) return renderItem ? renderItem(info) : null;
724
+ const row = info.item;
725
+ return /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(import_react_native6.View, { style: [styles4.row, columnWrapperStyle], children: row.map((item, kk) => /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(import_react9.Fragment, { children: renderItem ? renderItem({ index: info.index * cols + kk, item, separators: info.separators }) : null }, kk)) });
726
+ },
727
+ [cols, columnWrapperStyle, renderItem]
728
+ );
493
729
  const { chipHidden, chipStyle, containerStyle, contentInset, contentOffset, footerFixed, headerFixed } = useScrollList({ footerFixed: footerFixedProp, headerFixed: headerFixedProp, isHorizontal, keyboardAware, pullSearchHeight, style });
494
- const scrollTo = (0, import_react8.useCallback)((offset, animated) => {
730
+ const { jsListGeneration, onJsListUnmount } = (0, import_react9.useContext)(ScrollViewContext);
731
+ const capturedGeneration = (0, import_react9.useRef)(jsListGeneration.current);
732
+ (0, import_react9.useLayoutEffect)(() => {
733
+ capturedGeneration.current = jsListGeneration.current;
734
+ }, [jsListGeneration]);
735
+ (0, import_react9.useLayoutEffect)(() => () => onJsListUnmount(), [onJsListUnmount]);
736
+ const scrollTo = (0, import_react9.useCallback)((offset, animated) => {
495
737
  scrollView.current?.scrollToOffset({ offset, animated });
496
738
  }, []);
497
739
  const {
@@ -500,7 +742,8 @@ var FlatListInner = ({ chipProps, chipThreshold, contentContainerStyle: external
500
742
  handleScrollBeginDrag,
501
743
  handleScrollEndDrag,
502
744
  hiddenHeader,
503
- pullSearchMinHeight
745
+ pullSearchMinHeight,
746
+ remountTarget
504
747
  } = useScrollInit({
505
748
  listHeaderComponent: externalListHeaderComponent,
506
749
  onMomentumScrollEnd: externalMomentumScrollEnd,
@@ -510,7 +753,7 @@ var FlatListInner = ({ chipProps, chipThreshold, contentContainerStyle: external
510
753
  pullSearchHeight,
511
754
  scrollTo
512
755
  });
513
- const activeListHeader = (0, import_react8.useMemo)(() => {
756
+ const activeListHeader = (0, import_react9.useMemo)(() => {
514
757
  if (!renderFilters) return pullSearchHeader;
515
758
  const Inner = pullSearchHeader;
516
759
  return () => /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(import_jsx_runtime5.Fragment, { children: [
@@ -518,13 +761,30 @@ var FlatListInner = ({ chipProps, chipThreshold, contentContainerStyle: external
518
761
  renderFilters
519
762
  ] });
520
763
  }, [pullSearchHeader, renderFilters]);
521
- const contentContainerStyle = (0, import_react8.useMemo)(() => isHorizontal ? externalContentContainerStyle : [{ minHeight: import_react_native6.Dimensions.get("window").height - contentInset.top - contentInset.bottom + pullSearchMinHeight }, externalContentContainerStyle], [isHorizontal, contentInset.bottom, contentInset.top, externalContentContainerStyle, pullSearchMinHeight]);
522
- const onPullSearchZoneEnter = (0, import_react8.useCallback)(() => {
523
- if (!pullSearchHeight) return;
524
- scrollView.current?.scrollToOffset({ offset: -contentInset.top + pullSearchHeight, animated: true });
525
- }, [contentInset.top, pullSearchHeight]);
526
- const handleScroll = useScrollHandler({ chipHidden, chipThreshold, footerFixed, headerFixed, isHorizontal, onPullSearchZoneEnter: pullSearchHeight ? onPullSearchZoneEnter : void 0 });
527
- const handleScrollToTop = (0, import_react8.useCallback)(() => {
764
+ const contentContainerStyle = (0, import_react9.useMemo)(() => isHorizontal ? externalContentContainerStyle : [{ minHeight: import_react_native6.Dimensions.get("window").height - contentInset.top - contentInset.bottom + pullSearchMinHeight }, externalContentContainerStyle], [isHorizontal, contentInset.bottom, contentInset.top, externalContentContainerStyle, pullSearchMinHeight]);
765
+ const scrollHandler = useScrollHandlerJS({ capturedGeneration, chipHidden, chipThreshold, footerFixed, headerFixed, isHorizontal, jsListGeneration, remountTarget, scrollTo });
766
+ const onScrollBeginDrag = (0, import_react9.useCallback)(
767
+ (e) => {
768
+ scrollHandler.onScrollBeginDrag(e);
769
+ handleScrollBeginDrag(e);
770
+ },
771
+ [scrollHandler, handleScrollBeginDrag]
772
+ );
773
+ const onScrollEndDrag = (0, import_react9.useCallback)(
774
+ (e) => {
775
+ scrollHandler.onScrollEndDrag(e);
776
+ handleScrollEndDrag(e);
777
+ },
778
+ [scrollHandler, handleScrollEndDrag]
779
+ );
780
+ const onMomentumScrollEnd = (0, import_react9.useCallback)(
781
+ (e) => {
782
+ scrollHandler.onMomentumScrollEnd(e);
783
+ handleMomentumScrollEnd(e);
784
+ },
785
+ [scrollHandler, handleMomentumScrollEnd]
786
+ );
787
+ const handleScrollToTop = (0, import_react9.useCallback)(() => {
528
788
  if (isHorizontal) {
529
789
  scrollView.current?.scrollToOffset({ offset: 0, animated: true });
530
790
  return;
@@ -532,35 +792,39 @@ var FlatListInner = ({ chipProps, chipThreshold, contentContainerStyle: external
532
792
  const offset = pullSearchHeight ? -contentInset.top + pullSearchHeight : -contentInset.top;
533
793
  scrollView.current?.scrollToOffset({ offset, animated: true });
534
794
  }, [isHorizontal, contentInset.top, pullSearchHeight]);
535
- const handleContentSizeChange = (0, import_react8.useCallback)(
795
+ const handleContentSizeChange = (0, import_react9.useCallback)(
536
796
  (w, h) => {
537
797
  if (onContentSizeChange) onContentSizeChange(w, h);
538
798
  },
539
799
  [onContentSizeChange]
540
800
  );
541
- const refreshControl = (0, import_react8.useMemo)(() => /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(RefreshControl, {}), []);
542
- const nativeGesture = (0, import_react8.useMemo)(() => import_react_native_gesture_handler2.Gesture.Native(), []);
543
- const combinedGesture = (0, import_react8.useMemo)(() => gesture !== void 0 ? import_react_native_gesture_handler2.Gesture.Simultaneous(gesture, nativeGesture) : nativeGesture, [gesture, nativeGesture]);
801
+ const refreshControl = (0, import_react9.useMemo)(() => /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(RefreshControl, {}), []);
802
+ const nativeGesture = (0, import_react9.useMemo)(() => import_react_native_gesture_handler2.Gesture.Native(), []);
803
+ const combinedGesture = (0, import_react9.useMemo)(() => gesture !== void 0 ? import_react_native_gesture_handler2.Gesture.Simultaneous(gesture, nativeGesture) : nativeGesture, [gesture, nativeGesture]);
544
804
  const detectorGesture = gesture !== void 0 ? combinedGesture : void 0;
545
805
  const content = /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(import_react_native6.View, { style: containerStyle, children: [
546
806
  /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
547
- import_react_native_reanimated6.default.FlatList,
807
+ import_react_native6.FlatList,
548
808
  {
549
809
  ...props,
550
810
  contentContainerStyle,
551
811
  contentInset,
812
+ contentInsetAdjustmentBehavior: "never",
552
813
  contentOffset,
553
- data,
814
+ data: cols > 1 ? rowData : data,
554
815
  horizontal: isHorizontal,
555
816
  initialNumToRender,
817
+ keyExtractor: cols > 1 ? rowKeyExtractor : keyExtractor,
556
818
  ListHeaderComponent: activeListHeader,
557
819
  maxToRenderPerBatch,
558
820
  onContentSizeChange: handleContentSizeChange,
559
- onMomentumScrollEnd: handleMomentumScrollEnd,
560
- onScroll: handleScroll,
561
- onScrollBeginDrag: handleScrollBeginDrag,
562
- onScrollEndDrag: handleScrollEndDrag,
821
+ onMomentumScrollBegin: scrollHandler.onMomentumScrollBegin,
822
+ onMomentumScrollEnd,
823
+ onScroll: scrollHandler.onScroll,
824
+ onScrollBeginDrag,
825
+ onScrollEndDrag,
563
826
  pagingEnabled,
827
+ renderItem: cols > 1 ? rowRenderItem : renderItem,
564
828
  ref: (el) => {
565
829
  if (externalRef) externalRef.current = el;
566
830
  scrollView.current = el;
@@ -579,47 +843,51 @@ var FlatListInner = ({ chipProps, chipThreshold, contentContainerStyle: external
579
843
  ] });
580
844
  return detectorGesture ? /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(import_react_native_gesture_handler2.GestureDetector, { gesture: detectorGesture, children: content }) : content;
581
845
  };
582
- var styles4 = { measureContainer: { left: 0, opacity: 0, position: "absolute", right: 0, top: -9999 } };
583
- var FlatList = (0, import_react8.memo)(FlatListInner);
846
+ var defaultKeyExtractor = (item, index) => {
847
+ const key = item?.key ?? item?.id;
848
+ return key != null ? String(key) : String(index);
849
+ };
850
+ var styles4 = { measureContainer: { left: 0, opacity: 0, position: "absolute", right: 0, top: -9999 }, row: { flexDirection: "row" } };
851
+ var FlatList = (0, import_react9.memo)(FlatListInner);
584
852
 
585
853
  // src/PullSearch.tsx
586
- var import_react9 = require("react");
854
+ var import_react10 = require("react");
587
855
  var import_react_native7 = require("react-native");
588
856
  var import_react_native_paper2 = require("react-native-paper");
589
- var import_react_native_reanimated7 = __toESM(require("react-native-reanimated"));
857
+ var import_react_native_reanimated9 = __toESM(require("react-native-reanimated"));
590
858
  var import_jsx_runtime6 = require("react/jsx-runtime");
591
- var PullSearch = (0, import_react9.forwardRef)(function PullSearch2({ debounce, onChangeText, onHeightChange, placeholder, value: propValue }, ref) {
592
- const { headerHeight, scrollPosition } = (0, import_react9.useContext)(ScrollViewContext);
859
+ var PullSearch = (0, import_react10.forwardRef)(function PullSearch2({ debounce, onChangeText, onHeightChange, placeholder, value: propValue }, ref) {
860
+ const { headerHeight, scrollPosition } = (0, import_react10.useContext)(ScrollViewContext);
593
861
  const theme = (0, import_react_native_paper2.useTheme)();
594
- const inputRef = (0, import_react9.useRef)(null);
595
- const [barHeight, setBarHeight] = (0, import_react9.useState)(0);
596
- const [value, setValue] = (0, import_react9.useState)(propValue ?? "");
597
- const prevPropValueRef = (0, import_react9.useRef)(propValue);
598
- (0, import_react9.useEffect)(() => {
862
+ const inputRef = (0, import_react10.useRef)(null);
863
+ const [barHeight, setBarHeight] = (0, import_react10.useState)(0);
864
+ const [value, setValue] = (0, import_react10.useState)(propValue ?? "");
865
+ const prevPropValueRef = (0, import_react10.useRef)(propValue);
866
+ (0, import_react10.useEffect)(() => {
599
867
  if (prevPropValueRef.current === propValue) return;
600
868
  prevPropValueRef.current = propValue;
601
869
  setValue(propValue ?? "");
602
870
  }, [propValue]);
603
- (0, import_react9.useImperativeHandle)(ref, () => ({
871
+ (0, import_react10.useImperativeHandle)(ref, () => ({
604
872
  blur: () => inputRef.current?.blur(),
605
873
  focus: () => inputRef.current?.focus()
606
874
  }));
607
- const blurInput = (0, import_react9.useCallback)(() => inputRef.current?.blur(), []);
608
- (0, import_react_native_reanimated7.useAnimatedReaction)(
875
+ const blurInput = (0, import_react10.useCallback)(() => inputRef.current?.blur(), []);
876
+ (0, import_react_native_reanimated9.useAnimatedReaction)(
609
877
  () => {
610
878
  if (barHeight === 0) return false;
611
- return scrollPosition.value >= -headerHeight + barHeight;
879
+ return scrollPosition.value >= -(headerHeight ?? 0) + barHeight;
612
880
  },
613
881
  (isHidden, wasHidden) => {
614
- if (isHidden && !wasHidden) (0, import_react_native_reanimated7.runOnJS)(blurInput)();
882
+ if (isHidden && !wasHidden) (0, import_react_native_reanimated9.runOnJS)(blurInput)();
615
883
  },
616
884
  [barHeight, headerHeight, blurInput]
617
885
  );
618
- const handleClear = (0, import_react9.useCallback)(() => {
886
+ const handleClear = (0, import_react10.useCallback)(() => {
619
887
  setValue("");
620
888
  onChangeText("");
621
889
  }, [onChangeText]);
622
- const handleLayout = (0, import_react9.useCallback)(
890
+ const handleLayout = (0, import_react10.useCallback)(
623
891
  ({
624
892
  nativeEvent: {
625
893
  layout: { height }
@@ -630,7 +898,7 @@ var PullSearch = (0, import_react9.forwardRef)(function PullSearch2({ debounce,
630
898
  },
631
899
  [onHeightChange]
632
900
  );
633
- (0, import_react9.useEffect)(() => {
901
+ (0, import_react10.useEffect)(() => {
634
902
  const normalizedValue = value.trim();
635
903
  if (debounce) {
636
904
  const timeout = setTimeout(() => onChangeText(normalizedValue), 500);
@@ -638,13 +906,14 @@ var PullSearch = (0, import_react9.forwardRef)(function PullSearch2({ debounce,
638
906
  }
639
907
  onChangeText(normalizedValue);
640
908
  }, [debounce, onChangeText, value]);
641
- const animatedStyle = (0, import_react_native_reanimated7.useAnimatedStyle)(() => {
909
+ const animatedStyle = (0, import_react_native_reanimated9.useAnimatedStyle)(() => {
642
910
  if (barHeight === 0) return { opacity: 0 };
643
- const showY = -headerHeight;
644
- const hideY = -headerHeight + barHeight;
645
- return { opacity: (0, import_react_native_reanimated7.interpolate)(scrollPosition.value, [showY, hideY], [1, 0], import_react_native_reanimated7.Extrapolation.CLAMP) };
911
+ const h = headerHeight ?? 0;
912
+ const showY = -h;
913
+ const hideY = -h + barHeight;
914
+ return { opacity: (0, import_react_native_reanimated9.interpolate)(scrollPosition.value, [showY, hideY], [1, 0], import_react_native_reanimated9.Extrapolation.CLAMP) };
646
915
  }, [barHeight, headerHeight]);
647
- return /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(import_react_native_reanimated7.default.View, { onLayout: handleLayout, style: [styles5.container, animatedStyle], children: /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(import_react_native_paper2.Searchbar, { autoCorrect: false, onChangeText: (text) => setValue((text || "").trimStart()), onClearIconPress: handleClear, placeholder, placeholderTextColor: theme.colors.onSurfaceVariant, ref: inputRef, spellCheck: false, style: styles5.input, value }) });
916
+ return /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(import_react_native_reanimated9.default.View, { onLayout: handleLayout, style: [styles5.container, animatedStyle], children: /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(import_react_native_paper2.Searchbar, { autoCorrect: false, onChangeText: (text) => setValue((text || "").trimStart()), onClearIconPress: handleClear, placeholder, placeholderTextColor: theme.colors.onSurfaceVariant, ref: inputRef, spellCheck: false, style: styles5.input, value }) });
648
917
  });
649
918
  var styles5 = import_react_native7.StyleSheet.create({
650
919
  container: { paddingVertical: 8 },
@@ -1145,6 +1414,24 @@ var objectTraps = {
1145
1414
  get(state, prop) {
1146
1415
  if (prop === DRAFT_STATE)
1147
1416
  return state;
1417
+ if (prop === "constructor" || prop === "__proto__") {
1418
+ const source2 = latest(state);
1419
+ const value2 = source2[prop];
1420
+ return new Proxy(value2 || {}, {
1421
+ get: (target, key) => {
1422
+ if (key === "__proto__" || key === "prototype") {
1423
+ return Object.freeze(/* @__PURE__ */ Object.create(null));
1424
+ }
1425
+ return Reflect.get(target, key);
1426
+ },
1427
+ set: () => {
1428
+ return true;
1429
+ },
1430
+ apply: (target, thisArg, args) => {
1431
+ return Reflect.apply(target, thisArg, args);
1432
+ }
1433
+ });
1434
+ }
1148
1435
  let arrayPlugin = state.scope_.arrayMethodsPlugin_;
1149
1436
  const isArrayWithStringProp = state.type_ === 1 && typeof prop === "string";
1150
1437
  if (isArrayWithStringProp) {
@@ -1174,12 +1461,18 @@ var objectTraps = {
1174
1461
  return value;
1175
1462
  },
1176
1463
  has(state, prop) {
1464
+ if (prop === "constructor" || prop === "__proto__" || prop === "prototype") {
1465
+ return false;
1466
+ }
1177
1467
  return prop in latest(state);
1178
1468
  },
1179
1469
  ownKeys(state) {
1180
1470
  return Reflect.ownKeys(latest(state));
1181
1471
  },
1182
1472
  set(state, prop, value) {
1473
+ if (prop === "constructor" || prop === "__proto__" || prop === "prototype") {
1474
+ return true;
1475
+ }
1183
1476
  const desc = getDescriptorFromProto(latest(state), prop);
1184
1477
  if (desc?.set) {
1185
1478
  desc.set.call(state.draft_, value);
@@ -1199,7 +1492,7 @@ var objectTraps = {
1199
1492
  markChanged(state);
1200
1493
  }
1201
1494
  if (state.copy_[prop] === value && // special case: handle new props with value 'undefined'
1202
- (value !== void 0 || prop in state.copy_) || // special case: NaN
1495
+ (value !== void 0 || has(state.copy_, prop, state.type_)) || // special case: NaN
1203
1496
  Number.isNaN(value) && Number.isNaN(state.copy_[prop]))
1204
1497
  return true;
1205
1498
  state.copy_[prop] = value;
@@ -2190,14 +2483,14 @@ function formatProdErrorMessage(code) {
2190
2483
  }
2191
2484
 
2192
2485
  // src/ScrollViewSettingsContext.ts
2193
- var import_react10 = require("react");
2486
+ var import_react11 = require("react");
2194
2487
  var defaultScrollViewSettings = {
2195
2488
  backActionFixed: true,
2196
2489
  footerFixed: false,
2197
2490
  headerFixed: false,
2198
2491
  snapBack: false
2199
2492
  };
2200
- var ScrollViewSettingsContext = (0, import_react10.createContext)({
2493
+ var ScrollViewSettingsContext = (0, import_react11.createContext)({
2201
2494
  settings: defaultScrollViewSettings,
2202
2495
  set: () => {
2203
2496
  }
@@ -2215,16 +2508,26 @@ var scrollViewActions = slice.actions;
2215
2508
  var scrollViewReducer = slice.reducer;
2216
2509
 
2217
2510
  // src/ScrollView.tsx
2218
- var import_react11 = require("react");
2511
+ var import_react12 = require("react");
2219
2512
  var import_react_native8 = require("react-native");
2220
2513
  var import_react_native_gesture_handler3 = require("react-native-gesture-handler");
2221
- var import_react_native_reanimated8 = __toESM(require("react-native-reanimated"));
2514
+ var import_react_native_reanimated10 = __toESM(require("react-native-reanimated"));
2222
2515
  var import_jsx_runtime7 = require("react/jsx-runtime");
2223
2516
  var ScrollViewInner = ({ chipProps, chipThreshold, children, footerFixed: footerFixedProp, gesture, headerFixed: headerFixedProp, horizontal, keyboardAware, onChipPress, onMomentumScrollEnd: externalMomentumScrollEnd, onRefresh, onScrollBeginDrag: externalScrollBeginDrag, onScrollEndDrag: externalScrollEndDrag, pullSearchHeight, refreshing, style, ...props }) => {
2224
- const scrollView = (0, import_react11.useRef)(null);
2517
+ const scrollView = (0, import_react12.useRef)(null);
2225
2518
  const isHorizontal = horizontal === true;
2226
2519
  const { chipHidden, chipStyle, containerStyle, contentInset, contentOffset, footerFixed, headerFixed } = useScrollList({ footerFixed: footerFixedProp, headerFixed: headerFixedProp, isHorizontal, keyboardAware, pullSearchHeight, style });
2227
- const scrollTo = (0, import_react11.useCallback)((offset, animated) => {
2520
+ const { listGeneration, onListUnmount } = (0, import_react12.useContext)(ScrollViewContext);
2521
+ const capturedGeneration = (0, import_react_native_reanimated10.useSharedValue)(-1);
2522
+ (0, import_react12.useLayoutEffect)(() => {
2523
+ const generation = listGeneration.value;
2524
+ (0, import_react_native_reanimated10.runOnUI)((gen) => {
2525
+ "worklet";
2526
+ capturedGeneration.value = gen;
2527
+ })(generation);
2528
+ }, [capturedGeneration, listGeneration]);
2529
+ (0, import_react12.useLayoutEffect)(() => () => onListUnmount(), [onListUnmount]);
2530
+ const scrollTo = (0, import_react12.useCallback)((offset, animated) => {
2228
2531
  scrollView.current?.scrollTo({ y: offset, animated });
2229
2532
  }, []);
2230
2533
  const { handleMomentumScrollEnd, handleScrollBeginDrag, handleScrollEndDrag } = useScrollInit({
@@ -2234,12 +2537,8 @@ var ScrollViewInner = ({ chipProps, chipThreshold, children, footerFixed: footer
2234
2537
  pullSearchHeight,
2235
2538
  scrollTo
2236
2539
  });
2237
- const onPullSearchZoneEnter = (0, import_react11.useCallback)(() => {
2238
- if (!pullSearchHeight) return;
2239
- scrollTo(-contentInset.top + pullSearchHeight, true);
2240
- }, [contentInset.top, pullSearchHeight, scrollTo]);
2241
- const handleScroll = useScrollHandler({ chipHidden, chipThreshold, footerFixed, headerFixed, isHorizontal, onPullSearchZoneEnter: pullSearchHeight ? onPullSearchZoneEnter : void 0 });
2242
- const handleScrollToTop = (0, import_react11.useCallback)(() => {
2540
+ const handleScroll = useScrollHandler({ capturedGeneration, chipHidden, chipThreshold, footerFixed, headerFixed, isHorizontal, listGeneration });
2541
+ const handleScrollToTop = (0, import_react12.useCallback)(() => {
2243
2542
  if (isHorizontal) {
2244
2543
  scrollView.current?.scrollTo({ x: 0, animated: true });
2245
2544
  return;
@@ -2247,13 +2546,13 @@ var ScrollViewInner = ({ chipProps, chipThreshold, children, footerFixed: footer
2247
2546
  const offset = pullSearchHeight ? -contentInset.top + pullSearchHeight : -contentInset.top;
2248
2547
  scrollView.current?.scrollTo({ y: offset, animated: true });
2249
2548
  }, [isHorizontal, contentInset.top, pullSearchHeight]);
2250
- const refreshControl = (0, import_react11.useMemo)(() => onRefresh ? /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(RefreshControl, { onRefresh, refreshing: refreshing ?? false }) : /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(RefreshControl, {}), [onRefresh, refreshing]);
2251
- const nativeGesture = (0, import_react11.useMemo)(() => import_react_native_gesture_handler3.Gesture.Native(), []);
2252
- const combinedGesture = (0, import_react11.useMemo)(() => gesture !== void 0 ? import_react_native_gesture_handler3.Gesture.Simultaneous(gesture, nativeGesture) : nativeGesture, [gesture, nativeGesture]);
2549
+ const refreshControl = (0, import_react12.useMemo)(() => onRefresh ? /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(RefreshControl, { onRefresh, refreshing: refreshing ?? false }) : /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(RefreshControl, {}), [onRefresh, refreshing]);
2550
+ const nativeGesture = (0, import_react12.useMemo)(() => import_react_native_gesture_handler3.Gesture.Native(), []);
2551
+ const combinedGesture = (0, import_react12.useMemo)(() => gesture !== void 0 ? import_react_native_gesture_handler3.Gesture.Simultaneous(gesture, nativeGesture) : nativeGesture, [gesture, nativeGesture]);
2253
2552
  const detectorGesture = gesture !== void 0 ? combinedGesture : void 0;
2254
2553
  const content = /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)(import_react_native8.View, { style: containerStyle, children: [
2255
2554
  /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
2256
- import_react_native_reanimated8.default.ScrollView,
2555
+ import_react_native_reanimated10.default.ScrollView,
2257
2556
  {
2258
2557
  ...props,
2259
2558
  contentInset,
@@ -2275,19 +2574,19 @@ var ScrollViewInner = ({ chipProps, chipThreshold, children, footerFixed: footer
2275
2574
  ] });
2276
2575
  return detectorGesture ? /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(import_react_native_gesture_handler3.GestureDetector, { gesture: detectorGesture, children: content }) : content;
2277
2576
  };
2278
- var ScrollView = (0, import_react11.memo)(ScrollViewInner);
2577
+ var ScrollView = (0, import_react12.memo)(ScrollViewInner);
2279
2578
 
2280
2579
  // src/ScrollViewFooter.tsx
2281
2580
  var import_auto_paper3 = require("@rific/auto-paper");
2282
- var import_react12 = require("react");
2581
+ var import_react13 = require("react");
2283
2582
  var import_react_native9 = require("react-native");
2284
- var import_react_native_reanimated9 = __toESM(require("react-native-reanimated"));
2583
+ var import_react_native_reanimated11 = __toESM(require("react-native-reanimated"));
2285
2584
  var import_react_native_safe_area_context3 = require("react-native-safe-area-context");
2286
2585
  var import_jsx_runtime8 = require("react/jsx-runtime");
2287
2586
  var ScrollViewFooter = ({ children, style }) => {
2288
- const { blur, footerHeight, footerFixed, footerOffset, headerHeightShared, pullSearchHeightShared, scrollPosition, setFooterHeight, snapBackFooterShared } = (0, import_react12.useContext)(ScrollViewContext);
2587
+ const { blur, footerHeight, footerFixed, footerOffset, headerHeightShared, pullSearchHeightShared, scrollPosition, setFooterHeight, snapBackFooterShared } = (0, import_react13.useContext)(ScrollViewContext);
2289
2588
  const insets = (0, import_react_native_safe_area_context3.useSafeAreaInsets)();
2290
- (0, import_react12.useEffect)(
2589
+ (0, import_react13.useEffect)(
2291
2590
  () => () => {
2292
2591
  setFooterHeight(0);
2293
2592
  },
@@ -2300,14 +2599,14 @@ var ScrollViewFooter = ({ children, style }) => {
2300
2599
  }) => {
2301
2600
  if (footerHeight !== height) setFooterHeight(height);
2302
2601
  };
2303
- const footerStyle = (0, import_react_native_reanimated9.useAnimatedStyle)(() => {
2602
+ const footerStyle = (0, import_react_native_reanimated11.useAnimatedStyle)(() => {
2304
2603
  if (footerFixed) return { transform: [{ translateY: 0 }] };
2305
2604
  if (snapBackFooterShared.value) return { transform: [{ translateY: footerOffset.value }] };
2306
2605
  const effective = scrollPosition.value + headerHeightShared.value - pullSearchHeightShared.value;
2307
2606
  if (effective <= 0) return { transform: [{ translateY: 0 }] };
2308
2607
  return { transform: [{ translateY: Math.min(effective, footerHeight) }] };
2309
2608
  }, [footerHeight, footerFixed]);
2310
- return /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)(import_react_native_reanimated9.default.View, { onLayout: handleLayout, pointerEvents: "box-none", style: [styles6.footer, footerStyle], children: [
2609
+ return /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)(import_react_native_reanimated11.default.View, { onLayout: handleLayout, pointerEvents: "box-none", style: [styles6.footer, footerStyle], children: [
2311
2610
  footerHeight > 0 && /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(import_auto_paper3.BlurView, { blur, style: import_react_native9.StyleSheet.absoluteFill }),
2312
2611
  /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(import_react_native9.View, { style: [styles6.row, { paddingBottom: insets.bottom }, style], children })
2313
2612
  ] });
@@ -2319,16 +2618,16 @@ var styles6 = import_react_native9.StyleSheet.create({
2319
2618
 
2320
2619
  // src/ScrollViewHeader.tsx
2321
2620
  var import_auto_paper4 = require("@rific/auto-paper");
2322
- var import_react13 = require("react");
2621
+ var import_react14 = require("react");
2323
2622
  var import_react_native10 = require("react-native");
2324
2623
  var import_react_native_paper3 = require("react-native-paper");
2325
- var import_react_native_reanimated10 = __toESM(require("react-native-reanimated"));
2624
+ var import_react_native_reanimated12 = __toESM(require("react-native-reanimated"));
2326
2625
  var import_react_native_safe_area_context4 = require("react-native-safe-area-context");
2327
2626
  var import_jsx_runtime9 = require("react/jsx-runtime");
2328
2627
  var ActionBg = ({ blur, style }) => blur ? /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(import_react_native10.View, { style: [import_react_native10.StyleSheet.absoluteFill, styles7.actionBg, style], children: /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(import_auto_paper4.BlurView, { blur: true, style: import_react_native10.StyleSheet.absoluteFill }) }) : /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(import_react_native10.View, { style: [import_react_native10.StyleSheet.absoluteFill, styles7.actionBg, style], pointerEvents: "none", children: /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(import_react_native_paper3.Surface, { style: import_react_native10.StyleSheet.absoluteFill, elevation: 1, children: null }) });
2329
2628
  var ScrollViewHeader = ({ actionSize = 48, actionStyle, backAction, backActionFixed, caption, centerContent, children, iconSize, style, title, topInset = true, trailingAction, trailingActionFixed = true }) => {
2330
- const { blur, headerHeight, headerFixed, headerOffset, progress, progressing, pullSearchHeightShared, scrollPosition, setHeaderHeight, snapBackHeaderShared } = (0, import_react13.useContext)(ScrollViewContext);
2331
- const { settings } = (0, import_react13.useContext)(ScrollViewSettingsContext);
2629
+ const { blur, headerHeight, headerFixed, headerOffset, progress, progressing, pullSearchHeightShared, scrollPosition, setHeaderHeight, snapBackHeaderShared } = (0, import_react14.useContext)(ScrollViewContext);
2630
+ const { settings } = (0, import_react14.useContext)(ScrollViewSettingsContext);
2332
2631
  const effectiveBackActionFixed = backActionFixed ?? settings.backActionFixed;
2333
2632
  const insets = (0, import_react_native_safe_area_context4.useSafeAreaInsets)();
2334
2633
  const theme = (0, import_react_native_paper3.useTheme)();
@@ -2340,23 +2639,25 @@ var ScrollViewHeader = ({ actionSize = 48, actionStyle, backAction, backActionFi
2340
2639
  }) => {
2341
2640
  if (headerHeight !== height) setHeaderHeight(height);
2342
2641
  };
2343
- const translateStyle = (0, import_react_native_reanimated10.useAnimatedStyle)(() => {
2642
+ const translateStyle = (0, import_react_native_reanimated12.useAnimatedStyle)(() => {
2344
2643
  if (headerFixed) return { transform: [{ translateY: 0 }] };
2345
2644
  if (snapBackHeaderShared.value) return { transform: [{ translateY: headerOffset.value }] };
2346
- const effective = scrollPosition.value + headerHeight - pullSearchHeightShared.value;
2645
+ const effective = scrollPosition.value + (headerHeight ?? 0) - pullSearchHeightShared.value;
2347
2646
  if (effective <= 0) return { transform: [{ translateY: 0 }] };
2348
2647
  return { transform: [{ translateY: -effective }] };
2349
2648
  }, [headerFixed, headerHeight]);
2350
- const blurStyle = (0, import_react_native_reanimated10.useAnimatedStyle)(() => {
2649
+ const blurStyle = (0, import_react_native_reanimated12.useAnimatedStyle)(() => {
2351
2650
  if (!headerHeight) return { height: 0 };
2352
2651
  if (headerFixed) return { height: headerHeight };
2353
- const translateY = snapBackHeaderShared.value ? headerOffset.value : -Math.max(0, scrollPosition.value + headerHeight - pullSearchHeightShared.value);
2354
- return { height: Math.max(headerHeight + translateY, top) };
2652
+ const h = headerHeight ?? 0;
2653
+ const translateY = snapBackHeaderShared.value ? headerOffset.value : -Math.max(0, scrollPosition.value + h - pullSearchHeightShared.value);
2654
+ return { height: Math.max(h + translateY, top) };
2355
2655
  }, [headerFixed, headerHeight, top]);
2356
- const progressStyle = (0, import_react_native_reanimated10.useAnimatedStyle)(() => {
2357
- if (headerFixed) return { top: headerHeight };
2358
- const slide = snapBackHeaderShared.value ? -headerOffset.value : Math.max(0, scrollPosition.value + headerHeight - pullSearchHeightShared.value);
2359
- return { top: Math.max(headerHeight - slide, top) };
2656
+ const progressStyle = (0, import_react_native_reanimated12.useAnimatedStyle)(() => {
2657
+ const h = headerHeight ?? 0;
2658
+ if (headerFixed) return { top: h };
2659
+ const slide = snapBackHeaderShared.value ? -headerOffset.value : Math.max(0, scrollPosition.value + h - pullSearchHeightShared.value);
2660
+ return { top: Math.max(h - slide, top) };
2360
2661
  }, [headerFixed, headerHeight, top]);
2361
2662
  const actionMargin = 4;
2362
2663
  const contentMinHeight = actionSize + 2 * actionMargin;
@@ -2366,12 +2667,11 @@ var ScrollViewHeader = ({ actionSize = 48, actionStyle, backAction, backActionFi
2366
2667
  const leadingStyle = { alignItems: "center", height: actionSize, justifyContent: "center", left: actionMargin, position: "absolute", top: actionTop, width: actionSize, zIndex: 3, ...actionShadow };
2367
2668
  const trailingStyle = { alignItems: "center", height: actionSize, justifyContent: "center", position: "absolute", right: actionMargin, top: actionTop, width: actionSize, zIndex: 3, ...actionShadow };
2368
2669
  return /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)(import_jsx_runtime9.Fragment, { children: [
2369
- /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(import_react_native_reanimated10.default.View, { pointerEvents: "none", style: [styles7.blur, blurStyle], children: headerHeight > 0 && /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(import_auto_paper4.BlurView, { blur, style: [styles7.blurInner, { height: headerHeight }] }) }),
2370
- /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)(import_react_native_reanimated10.default.View, { onLayout: handleLayout, pointerEvents: "box-none", style: [headerHeight === 0 ? styles7.headerInit : styles7.header, translateStyle], children: [
2371
- /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(import_react_native10.View, { style: [{ paddingTop: top }, style], children: /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)(import_react_native10.View, { style: [styles7.content, { minHeight: contentMinHeight }], children: [
2670
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(import_react_native_reanimated12.default.View, { pointerEvents: "none", style: [styles7.blur, blurStyle], children: headerHeight !== null && headerHeight > 0 && /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(import_auto_paper4.BlurView, { blur, style: [styles7.blurInner, { height: headerHeight }] }) }),
2671
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)(import_react_native_reanimated12.default.View, { onLayout: handleLayout, pointerEvents: "box-none", style: [headerHeight === null ? styles7.headerInit : styles7.header, translateStyle], children: [
2672
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(import_react_native10.View, { style: [{ paddingTop: top }, style], children: children ?? /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)(import_react_native10.View, { style: [styles7.content, { minHeight: contentMinHeight }], children: [
2372
2673
  /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(import_react_native10.View, { style: styles7.side }),
2373
2674
  /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(import_react_native10.View, { style: styles7.spacer }),
2374
- /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(import_react_native10.View, { style: styles7.side, children }),
2375
2675
  (centerContent || title || caption) && /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(import_react_native10.View, { style: [import_react_native10.StyleSheet.absoluteFill, styles7.titleContainer], pointerEvents: centerContent ? "box-none" : "none", children: centerContent ?? /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)(import_jsx_runtime9.Fragment, { children: [
2376
2676
  title && /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(import_react_native10.Text, { numberOfLines: 1, style: [styles7.title, { color: theme.colors.onSurface }], children: title }),
2377
2677
  caption && /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(import_react_native10.Text, { numberOfLines: 1, style: [styles7.caption, { color: theme.colors.onSurfaceVariant }], children: caption })
@@ -2379,18 +2679,18 @@ var ScrollViewHeader = ({ actionSize = 48, actionStyle, backAction, backActionFi
2379
2679
  ] }) }),
2380
2680
  !blur && /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(import_react_native10.View, { style: [styles7.divider, { backgroundColor: theme.colors.outlineVariant }] })
2381
2681
  ] }),
2382
- /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(import_react_native_reanimated10.default.View, { pointerEvents: "none", style: [styles7.progress, progressStyle], children: /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(import_react_native_paper3.ProgressBar, { indeterminate: progress === null, visible: progressing, progress: progress ?? void 0 }) }),
2383
- backAction && (effectiveBackActionFixed ? /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)(import_react_native_reanimated10.default.View, { style: leadingStyle, children: [
2682
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(import_react_native_reanimated12.default.View, { pointerEvents: "none", style: [styles7.progress, progressStyle], children: /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(import_react_native_paper3.ProgressBar, { indeterminate: progress === null, visible: progressing, progress: progress ?? void 0 }) }),
2683
+ backAction && (effectiveBackActionFixed ? /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)(import_react_native_reanimated12.default.View, { style: leadingStyle, children: [
2384
2684
  /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(ActionBg, { blur, style: actionStyle }),
2385
2685
  /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(import_react_native_paper3.Appbar.BackAction, { onPress: backAction, size: iconSize ?? actionSize / 2, style: buttonStyle })
2386
- ] }) : /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)(import_react_native_reanimated10.default.View, { style: [leadingStyle, translateStyle], children: [
2686
+ ] }) : /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)(import_react_native_reanimated12.default.View, { style: [leadingStyle, translateStyle], children: [
2387
2687
  /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(ActionBg, { blur, style: actionStyle }),
2388
2688
  /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(import_react_native_paper3.Appbar.BackAction, { onPress: backAction, size: iconSize ?? actionSize / 2, style: buttonStyle })
2389
2689
  ] })),
2390
2690
  trailingAction && (trailingActionFixed ? /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)(import_react_native10.View, { style: trailingStyle, children: [
2391
2691
  /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(ActionBg, { blur, style: actionStyle }),
2392
2692
  trailingAction
2393
- ] }) : /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)(import_react_native_reanimated10.default.View, { style: [trailingStyle, translateStyle], children: [
2693
+ ] }) : /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)(import_react_native_reanimated12.default.View, { style: [trailingStyle, translateStyle], children: [
2394
2694
  /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(ActionBg, { blur, style: actionStyle }),
2395
2695
  trailingAction
2396
2696
  ] }))
@@ -2414,62 +2714,77 @@ var styles7 = import_react_native10.StyleSheet.create({
2414
2714
 
2415
2715
  // src/ScrollViewProvider.tsx
2416
2716
  var import_auto_paper5 = require("@rific/auto-paper");
2417
- var import_react14 = require("react");
2717
+ var import_react15 = require("react");
2418
2718
  var import_react_native11 = require("react-native");
2419
- var import_react_native_reanimated11 = require("react-native-reanimated");
2719
+ var import_react_native_reanimated13 = require("react-native-reanimated");
2420
2720
  var import_jsx_runtime10 = require("react/jsx-runtime");
2421
2721
  var ScrollViewProvider = ({ blur, children, fixed = false, footerFixed, headerFixed, snapBack, snapBackFooter, snapBackHeader, tabBarHeight = 60 }) => {
2422
- const { settings } = (0, import_react14.useContext)(ScrollViewSettingsContext);
2722
+ const { settings } = (0, import_react15.useContext)(ScrollViewSettingsContext);
2423
2723
  const effectiveBlur = (0, import_auto_paper5.useBlur)(blur);
2424
2724
  const effectiveSnapBack = snapBack ?? settings.snapBack;
2425
2725
  const effectiveSnapBackHeader = snapBackHeader ?? settings.snapBackHeader;
2426
2726
  const effectiveSnapBackFooter = snapBackFooter ?? settings.snapBackFooter;
2427
- const [headerHeight, setHeaderHeightState] = (0, import_react14.useState)(0);
2428
- const [footerHeight, setFooterHeightState] = (0, import_react14.useState)(0);
2429
- const [progress, setProgress] = (0, import_react14.useState)(null);
2430
- const [progressing, setProgressing] = (0, import_react14.useState)(false);
2431
- const scrollPosition = (0, import_react_native_reanimated11.useSharedValue)(0);
2432
- const headerHeightShared = (0, import_react_native_reanimated11.useSharedValue)(0);
2433
- const footerHeightShared = (0, import_react_native_reanimated11.useSharedValue)(0);
2434
- const headerOffset = (0, import_react_native_reanimated11.useSharedValue)(0);
2435
- const footerOffset = (0, import_react_native_reanimated11.useSharedValue)(0);
2436
- const pullSearchHeightShared = (0, import_react_native_reanimated11.useSharedValue)(0);
2437
- const snapBackHeaderShared = (0, import_react_native_reanimated11.useSharedValue)(false);
2438
- const snapBackFooterShared = (0, import_react_native_reanimated11.useSharedValue)(false);
2439
- (0, import_react14.useEffect)(() => {
2727
+ const [headerHeight, setHeaderHeightState] = (0, import_react15.useState)(null);
2728
+ const [footerHeight, setFooterHeightState] = (0, import_react15.useState)(0);
2729
+ const [progress, setProgress] = (0, import_react15.useState)(null);
2730
+ const [progressing, setProgressing] = (0, import_react15.useState)(false);
2731
+ const scrollPosition = (0, import_react_native_reanimated13.useSharedValue)(0);
2732
+ const listGeneration = (0, import_react_native_reanimated13.useSharedValue)(0);
2733
+ const jsListGeneration = (0, import_react15.useRef)(0);
2734
+ const headerHeightShared = (0, import_react_native_reanimated13.useSharedValue)(0);
2735
+ const footerHeightShared = (0, import_react_native_reanimated13.useSharedValue)(0);
2736
+ const headerOffset = (0, import_react_native_reanimated13.useSharedValue)(0);
2737
+ const footerOffset = (0, import_react_native_reanimated13.useSharedValue)(0);
2738
+ const pullSearchHeightShared = (0, import_react_native_reanimated13.useSharedValue)(0);
2739
+ const snapBackHeaderShared = (0, import_react_native_reanimated13.useSharedValue)(false);
2740
+ const snapBackFooterShared = (0, import_react_native_reanimated13.useSharedValue)(false);
2741
+ (0, import_react15.useEffect)(() => {
2440
2742
  snapBackHeaderShared.value = effectiveSnapBackHeader ?? effectiveSnapBack;
2441
2743
  snapBackFooterShared.value = effectiveSnapBackFooter ?? effectiveSnapBack;
2442
2744
  }, [effectiveSnapBack, effectiveSnapBackFooter, effectiveSnapBackHeader, snapBackFooterShared, snapBackHeaderShared]);
2443
- const setHeaderHeight = (0, import_react14.useCallback)(
2745
+ const setHeaderHeight = (0, import_react15.useCallback)(
2444
2746
  (h) => {
2445
2747
  setHeaderHeightState(h);
2446
- headerHeightShared.value = h;
2748
+ headerHeightShared.value = h ?? 0;
2447
2749
  },
2448
2750
  [headerHeightShared]
2449
2751
  );
2450
- const setFooterHeight = (0, import_react14.useCallback)(
2752
+ const onListUnmount = (0, import_react15.useCallback)(() => {
2753
+ listGeneration.value += 1;
2754
+ }, [listGeneration]);
2755
+ const onJsListUnmount = (0, import_react15.useCallback)(() => {
2756
+ jsListGeneration.current += 1;
2757
+ }, []);
2758
+ (0, import_react15.useEffect)(() => {
2759
+ if (!__DEV__ || headerHeight !== null) return;
2760
+ const timeout = setTimeout(() => {
2761
+ 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.");
2762
+ }, 3e3);
2763
+ return () => clearTimeout(timeout);
2764
+ }, [headerHeight]);
2765
+ const setFooterHeight = (0, import_react15.useCallback)(
2451
2766
  (h) => {
2452
2767
  setFooterHeightState(h);
2453
2768
  footerHeightShared.value = h;
2454
2769
  },
2455
2770
  [footerHeightShared]
2456
2771
  );
2457
- const scrollHeight = (0, import_react14.useMemo)(() => import_react_native11.Dimensions.get("window").height - headerHeight - footerHeight, [headerHeight, footerHeight]);
2772
+ const scrollHeight = (0, import_react15.useMemo)(() => import_react_native11.Dimensions.get("window").height - (headerHeight ?? 0) - footerHeight, [headerHeight, footerHeight]);
2458
2773
  const effectiveHeaderFixed = fixed || (headerFixed ?? settings.headerFixed);
2459
2774
  const effectiveFooterFixed = fixed || (footerFixed ?? settings.footerFixed);
2460
- const value = (0, import_react14.useMemo)(() => ({ 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]);
2775
+ const value = (0, import_react15.useMemo)(() => ({ 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]);
2461
2776
  return /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(ScrollViewContext.Provider, { value, children });
2462
2777
  };
2463
2778
 
2464
2779
  // src/ScrollViewSettingsProvider.tsx
2465
- var import_react15 = require("react");
2780
+ var import_react16 = require("react");
2466
2781
  var import_jsx_runtime11 = require("react/jsx-runtime");
2467
2782
  var ScrollViewSettingsProvider = ({ children, onChange, initialValue }) => {
2468
- const [settings, setSettings] = (0, import_react15.useState)(() => ({
2783
+ const [settings, setSettings] = (0, import_react16.useState)(() => ({
2469
2784
  ...defaultScrollViewSettings,
2470
2785
  ...initialValue
2471
2786
  }));
2472
- const set2 = (0, import_react15.useCallback)(
2787
+ const set2 = (0, import_react16.useCallback)(
2473
2788
  (patch) => {
2474
2789
  setSettings((prev) => {
2475
2790
  const next = { ...prev, ...patch };
@@ -2479,33 +2794,33 @@ var ScrollViewSettingsProvider = ({ children, onChange, initialValue }) => {
2479
2794
  },
2480
2795
  [onChange]
2481
2796
  );
2482
- const ctx = (0, import_react15.useMemo)(() => ({ settings, set: set2 }), [settings, set2]);
2797
+ const ctx = (0, import_react16.useMemo)(() => ({ settings, set: set2 }), [settings, set2]);
2483
2798
  return /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(ScrollViewSettingsContext.Provider, { value: ctx, children });
2484
2799
  };
2485
2800
 
2486
2801
  // src/SectionList.tsx
2487
- var import_react17 = require("react");
2802
+ var import_react18 = require("react");
2488
2803
  var import_react_native12 = require("react-native");
2489
2804
  var import_react_native_gesture_handler4 = require("react-native-gesture-handler");
2490
- var import_react_native_reanimated13 = __toESM(require("react-native-reanimated"));
2805
+ var import_react_native_reanimated15 = __toESM(require("react-native-reanimated"));
2491
2806
 
2492
2807
  // src/internal/useStickyHeaders.ts
2493
- var import_react16 = require("react");
2494
- var import_react_native_reanimated12 = require("react-native-reanimated");
2808
+ var import_react17 = require("react");
2809
+ var import_react_native_reanimated14 = require("react-native-reanimated");
2495
2810
  var import_react_native_safe_area_context5 = require("react-native-safe-area-context");
2496
2811
  function useStickyHeaders(headerFixed, enabled) {
2497
- const { headerHeightShared, headerOffset, pullSearchHeightShared, scrollPosition, snapBackHeaderShared } = (0, import_react16.useContext)(ScrollViewContext);
2812
+ const { headerHeightShared, headerOffset, pullSearchHeightShared, scrollPosition, snapBackHeaderShared } = (0, import_react17.useContext)(ScrollViewContext);
2498
2813
  const insetsTop = (0, import_react_native_safe_area_context5.useSafeAreaInsets)().top;
2499
- const sectionYPositions = (0, import_react_native_reanimated12.useSharedValue)([]);
2500
- const sectionHeights = (0, import_react_native_reanimated12.useSharedValue)([]);
2501
- const [activeIndex, setActiveIndex] = (0, import_react16.useState)(-1);
2502
- const contentIndexShared = (0, import_react_native_reanimated12.useSharedValue)(-1);
2503
- (0, import_react16.useLayoutEffect)(() => {
2814
+ const sectionYPositions = (0, import_react_native_reanimated14.useSharedValue)([]);
2815
+ const sectionHeights = (0, import_react_native_reanimated14.useSharedValue)([]);
2816
+ const [activeIndex, setActiveIndex] = (0, import_react17.useState)(-1);
2817
+ const contentIndexShared = (0, import_react_native_reanimated14.useSharedValue)(-1);
2818
+ (0, import_react17.useLayoutEffect)(() => {
2504
2819
  contentIndexShared.value = activeIndex;
2505
2820
  }, [activeIndex, contentIndexShared]);
2506
- const positionsCache = (0, import_react16.useRef)([]);
2507
- const heightsCache = (0, import_react16.useRef)([]);
2508
- (0, import_react_native_reanimated12.useAnimatedReaction)(
2821
+ const positionsCache = (0, import_react17.useRef)([]);
2822
+ const heightsCache = (0, import_react17.useRef)([]);
2823
+ (0, import_react_native_reanimated14.useAnimatedReaction)(
2509
2824
  () => {
2510
2825
  if (!enabled) return -1;
2511
2826
  const positions = sectionYPositions.value;
@@ -2530,11 +2845,11 @@ function useStickyHeaders(headerFixed, enabled) {
2530
2845
  return active;
2531
2846
  },
2532
2847
  (curr, prev) => {
2533
- if (curr !== prev) (0, import_react_native_reanimated12.runOnJS)(setActiveIndex)(curr);
2848
+ if (curr !== prev) (0, import_react_native_reanimated14.runOnJS)(setActiveIndex)(curr);
2534
2849
  },
2535
2850
  [enabled, headerFixed, insetsTop]
2536
2851
  );
2537
- const clipStyle = (0, import_react_native_reanimated12.useAnimatedStyle)(() => {
2852
+ const clipStyle = (0, import_react_native_reanimated14.useAnimatedStyle)(() => {
2538
2853
  const hHeight = headerHeightShared.value;
2539
2854
  const scrollPos = scrollPosition.value;
2540
2855
  let stickyTop;
@@ -2548,7 +2863,7 @@ function useStickyHeaders(headerFixed, enabled) {
2548
2863
  }
2549
2864
  return { top: stickyTop };
2550
2865
  }, [enabled, headerFixed, insetsTop]);
2551
- const overlayStyle = (0, import_react_native_reanimated12.useAnimatedStyle)(() => {
2866
+ const overlayStyle = (0, import_react_native_reanimated14.useAnimatedStyle)(() => {
2552
2867
  const hidden = { opacity: 0, top: 0, transform: [{ translateY: 0 }] };
2553
2868
  if (!enabled) return hidden;
2554
2869
  const index = contentIndexShared.value;
@@ -2576,7 +2891,7 @@ function useStickyHeaders(headerFixed, enabled) {
2576
2891
  }
2577
2892
  return { opacity: 1, top: 0, transform: [{ translateY }] };
2578
2893
  }, [enabled, headerFixed, insetsTop]);
2579
- const measureHeader = (0, import_react16.useCallback)(
2894
+ const measureHeader = (0, import_react17.useCallback)(
2580
2895
  (index, pageY, height) => {
2581
2896
  const contentY = pageY + scrollPosition.value;
2582
2897
  positionsCache.current[index] = contentY;
@@ -2586,7 +2901,7 @@ function useStickyHeaders(headerFixed, enabled) {
2586
2901
  },
2587
2902
  [scrollPosition, sectionHeights, sectionYPositions]
2588
2903
  );
2589
- const resetPositions = (0, import_react16.useCallback)(
2904
+ const resetPositions = (0, import_react17.useCallback)(
2590
2905
  (count) => {
2591
2906
  positionsCache.current = Array.from({ length: count }, () => -1);
2592
2907
  sectionYPositions.value = positionsCache.current.slice();
@@ -2600,18 +2915,27 @@ function useStickyHeaders(headerFixed, enabled) {
2600
2915
 
2601
2916
  // src/SectionList.tsx
2602
2917
  var import_jsx_runtime12 = require("react/jsx-runtime");
2603
- var AnimatedSectionList = (0, import_react_native_reanimated13.createAnimatedComponent)(import_react_native12.SectionList);
2918
+ var AnimatedSectionList = import_react_native_reanimated15.default.createAnimatedComponent(import_react_native12.SectionList);
2604
2919
  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 }) => {
2605
- const scrollView = (0, import_react17.useRef)(null);
2920
+ const scrollView = (0, import_react18.useRef)(null);
2606
2921
  const { stickySectionHeadersEnabled = true, renderSectionHeader, ...passThroughProps } = props;
2607
2922
  const { chipHidden, chipStyle, containerStyle, contentInset, contentOffset, footerFixed, headerFixed, headerHeight } = useScrollList({ footerFixed: footerFixedProp, headerFixed: headerFixedProp, keyboardAware, pullSearchHeight, style });
2923
+ const { listGeneration, onListUnmount } = (0, import_react18.useContext)(ScrollViewContext);
2924
+ const capturedGeneration = (0, import_react_native_reanimated15.useSharedValue)(-1);
2925
+ (0, import_react18.useLayoutEffect)(() => {
2926
+ const generation = listGeneration.value;
2927
+ (0, import_react_native_reanimated15.runOnUI)((gen) => {
2928
+ "worklet";
2929
+ capturedGeneration.value = gen;
2930
+ })(generation);
2931
+ }, [capturedGeneration, listGeneration]);
2608
2932
  const usesCustomSticky = stickySectionHeadersEnabled && renderSectionHeader != null;
2609
2933
  const { activeIndex, clipStyle, measureHeader, overlayStyle, resetPositions } = useStickyHeaders(headerFixed, usesCustomSticky);
2610
- (0, import_react17.useLayoutEffect)(() => {
2934
+ (0, import_react18.useLayoutEffect)(() => {
2611
2935
  if (usesCustomSticky) resetPositions(sections.length);
2612
2936
  }, [sections, usesCustomSticky, resetPositions, headerHeight]);
2613
- const sectionViewRefs = (0, import_react17.useRef)(/* @__PURE__ */ new Map());
2614
- const wrappedRenderSectionHeader = (0, import_react17.useMemo)(() => {
2937
+ const sectionViewRefs = (0, import_react18.useRef)(/* @__PURE__ */ new Map());
2938
+ const wrappedRenderSectionHeader = (0, import_react18.useMemo)(() => {
2615
2939
  if (!usesCustomSticky) return renderSectionHeader ?? void 0;
2616
2940
  return (info) => {
2617
2941
  const idx = sections.findIndex((s) => s === info.section);
@@ -2632,7 +2956,8 @@ var SectionListInner = ({ chipProps, chipThreshold, contentContainerStyle: exter
2632
2956
  );
2633
2957
  };
2634
2958
  }, [usesCustomSticky, renderSectionHeader, sections, measureHeader]);
2635
- const scrollTo = (0, import_react17.useCallback)((offset, animated) => {
2959
+ (0, import_react18.useLayoutEffect)(() => () => onListUnmount(), [onListUnmount]);
2960
+ const scrollTo = (0, import_react18.useCallback)((offset, animated) => {
2636
2961
  scrollView.current?.getScrollResponder()?.scrollTo({ y: offset, animated });
2637
2962
  }, []);
2638
2963
  const {
@@ -2650,7 +2975,7 @@ var SectionListInner = ({ chipProps, chipThreshold, contentContainerStyle: exter
2650
2975
  pullSearchHeight,
2651
2976
  scrollTo
2652
2977
  });
2653
- const activeListHeader = (0, import_react17.useMemo)(() => {
2978
+ const activeListHeader = (0, import_react18.useMemo)(() => {
2654
2979
  if (!renderFilters) return pullSearchHeader;
2655
2980
  const Inner = pullSearchHeader;
2656
2981
  return () => /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)(import_jsx_runtime12.Fragment, { children: [
@@ -2658,8 +2983,8 @@ var SectionListInner = ({ chipProps, chipThreshold, contentContainerStyle: exter
2658
2983
  renderFilters
2659
2984
  ] });
2660
2985
  }, [pullSearchHeader, renderFilters]);
2661
- const pullSearchSettled = (0, import_react17.useRef)(false);
2662
- (0, import_react17.useEffect)(() => {
2986
+ const pullSearchSettled = (0, import_react18.useRef)(false);
2987
+ (0, import_react18.useEffect)(() => {
2663
2988
  if (!usesCustomSticky || activeListHeader == null || pullSearchSettled.current) return;
2664
2989
  pullSearchSettled.current = true;
2665
2990
  const handle = requestAnimationFrame(() => {
@@ -2671,25 +2996,21 @@ var SectionListInner = ({ chipProps, chipThreshold, contentContainerStyle: exter
2671
2996
  });
2672
2997
  return () => cancelAnimationFrame(handle);
2673
2998
  }, [activeListHeader, usesCustomSticky, measureHeader]);
2674
- const contentContainerStyle = (0, import_react17.useMemo)(() => [{ minHeight: import_react_native12.Dimensions.get("window").height - contentInset.top - contentInset.bottom + pullSearchMinHeight }, externalContentContainerStyle], [contentInset.bottom, contentInset.top, externalContentContainerStyle, pullSearchMinHeight]);
2675
- const onPullSearchZoneEnter = (0, import_react17.useCallback)(() => {
2676
- if (!pullSearchHeight) return;
2677
- scrollTo(-contentInset.top + pullSearchHeight, true);
2678
- }, [contentInset.top, pullSearchHeight, scrollTo]);
2679
- const handleScroll = useScrollHandler({ chipHidden, chipThreshold, footerFixed, headerFixed, onPullSearchZoneEnter: pullSearchHeight ? onPullSearchZoneEnter : void 0 });
2680
- const handleScrollToTop = (0, import_react17.useCallback)(() => {
2999
+ const contentContainerStyle = (0, import_react18.useMemo)(() => [{ minHeight: import_react_native12.Dimensions.get("window").height - contentInset.top - contentInset.bottom + pullSearchMinHeight }, externalContentContainerStyle], [contentInset.bottom, contentInset.top, externalContentContainerStyle, pullSearchMinHeight]);
3000
+ const handleScroll = useScrollHandler({ capturedGeneration, chipHidden, chipThreshold, footerFixed, headerFixed, listGeneration });
3001
+ const handleScrollToTop = (0, import_react18.useCallback)(() => {
2681
3002
  const offset = pullSearchHeight ? -contentInset.top + pullSearchHeight : -contentInset.top;
2682
3003
  scrollView.current?.getScrollResponder()?.scrollTo({ y: offset, animated: true });
2683
3004
  }, [contentInset.top, pullSearchHeight]);
2684
- const handleContentSizeChange = (0, import_react17.useCallback)(
3005
+ const handleContentSizeChange = (0, import_react18.useCallback)(
2685
3006
  (w, h) => {
2686
3007
  if (onContentSizeChange) onContentSizeChange(w, h);
2687
3008
  },
2688
3009
  [onContentSizeChange]
2689
3010
  );
2690
- const refreshControl = (0, import_react17.useMemo)(() => onRefresh ? /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(RefreshControl, { onRefresh, refreshing: refreshing ?? false }) : /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(RefreshControl, {}), [onRefresh, refreshing]);
2691
- const nativeGesture = (0, import_react17.useMemo)(() => import_react_native_gesture_handler4.Gesture.Native(), []);
2692
- const combinedGesture = (0, import_react17.useMemo)(() => gesture !== void 0 ? import_react_native_gesture_handler4.Gesture.Simultaneous(gesture, nativeGesture) : nativeGesture, [gesture, nativeGesture]);
3011
+ const refreshControl = (0, import_react18.useMemo)(() => onRefresh ? /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(RefreshControl, { onRefresh, refreshing: refreshing ?? false }) : /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(RefreshControl, {}), [onRefresh, refreshing]);
3012
+ const nativeGesture = (0, import_react18.useMemo)(() => import_react_native_gesture_handler4.Gesture.Native(), []);
3013
+ const combinedGesture = (0, import_react18.useMemo)(() => gesture !== void 0 ? import_react_native_gesture_handler4.Gesture.Simultaneous(gesture, nativeGesture) : nativeGesture, [gesture, nativeGesture]);
2693
3014
  const detectorGesture = gesture !== void 0 ? combinedGesture : void 0;
2694
3015
  const content = /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)(import_react_native12.View, { style: containerStyle, children: [
2695
3016
  /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
@@ -2720,11 +3041,11 @@ var SectionListInner = ({ chipProps, chipThreshold, contentContainerStyle: exter
2720
3041
  stickySectionHeadersEnabled: false,
2721
3042
  windowSize
2722
3043
  },
2723
- headerHeight > 0 ? 1 : 0
3044
+ headerHeight !== null && headerHeight > 0 ? 1 : 0
2724
3045
  ),
2725
3046
  hiddenHeader && /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(import_react_native12.View, { pointerEvents: "none", style: styles8.measureContainer, children: hiddenHeader }),
2726
3047
  /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(ScrollViewChip, { chipProps, onChipPress, onPress: handleScrollToTop, style: chipStyle }),
2727
- usesCustomSticky && /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(import_react_native_reanimated13.default.View, { pointerEvents: "none", style: [styles8.stickyClip, clipStyle], children: activeIndex >= 0 && activeIndex < sections.length && /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(import_react_native_reanimated13.default.View, { style: [styles8.stickyOverlay, overlayStyle], children: renderSectionHeader({ section: sections[activeIndex] }) }) })
3048
+ usesCustomSticky && /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(import_react_native_reanimated15.default.View, { pointerEvents: "none", style: [styles8.stickyClip, clipStyle], children: activeIndex >= 0 && activeIndex < sections.length && /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(import_react_native_reanimated15.default.View, { style: [styles8.stickyOverlay, overlayStyle], children: renderSectionHeader({ section: sections[activeIndex] }) }) })
2728
3049
  ] });
2729
3050
  return detectorGesture ? /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(import_react_native_gesture_handler4.GestureDetector, { gesture: detectorGesture, children: content }) : content;
2730
3051
  };
@@ -2733,18 +3054,18 @@ var styles8 = {
2733
3054
  stickyClip: { bottom: 0, left: 0, overflow: "hidden", position: "absolute", right: 0, zIndex: 2 },
2734
3055
  stickyOverlay: { left: 0, position: "absolute", right: 0 }
2735
3056
  };
2736
- var SectionList = (0, import_react17.memo)(SectionListInner);
3057
+ var SectionList = (0, import_react18.memo)(SectionListInner);
2737
3058
 
2738
3059
  // src/useScrollView.ts
2739
- var import_react18 = require("react");
3060
+ var import_react19 = require("react");
2740
3061
  var useScrollView = () => {
2741
- const { progress, progressing, scrollHeight, scrollPosition, setProgress, setProgressing } = (0, import_react18.useContext)(ScrollViewContext);
3062
+ const { progress, progressing, scrollHeight, scrollPosition, setProgress, setProgressing } = (0, import_react19.useContext)(ScrollViewContext);
2742
3063
  return { progress, progressing, scrollHeight, scrollPosition, setProgress, setProgressing };
2743
3064
  };
2744
3065
 
2745
3066
  // src/useScrollViewSettings.ts
2746
- var import_react19 = require("react");
2747
- var useScrollViewSettings = () => (0, import_react19.useContext)(ScrollViewSettingsContext);
3067
+ var import_react20 = require("react");
3068
+ var useScrollViewSettings = () => (0, import_react20.useContext)(ScrollViewSettingsContext);
2748
3069
  // Annotate the CommonJS export names for ESM import in node:
2749
3070
  0 && (module.exports = {
2750
3071
  CustomList,