@legendapp/list 3.0.0-beta.21 → 3.0.0-beta.23

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/index.native.mjs CHANGED
@@ -10,7 +10,7 @@ var Text = Text$1;
10
10
 
11
11
  // src/state/getContentInsetEnd.ts
12
12
  function getContentInsetEnd(state) {
13
- var _a3;
13
+ var _a3, _b;
14
14
  const { props } = state;
15
15
  const horizontal = props.horizontal;
16
16
  let contentInset = props.contentInset;
@@ -24,7 +24,16 @@ function getContentInsetEnd(state) {
24
24
  }
25
25
  }
26
26
  }
27
- return (horizontal ? contentInset == null ? void 0 : contentInset.right : contentInset == null ? void 0 : contentInset.bottom) || 0;
27
+ const baseInset = contentInset != null ? contentInset : state.nativeContentInset;
28
+ const overrideInset = (_b = state.contentInsetOverride) != null ? _b : void 0;
29
+ if (overrideInset) {
30
+ const mergedInset = { top: 0, left: 0, right: 0, bottom: 0, ...baseInset, ...overrideInset };
31
+ return (horizontal ? mergedInset.right : mergedInset.bottom) || 0;
32
+ }
33
+ if (baseInset) {
34
+ return (horizontal ? baseInset.right : baseInset.bottom) || 0;
35
+ }
36
+ return 0;
28
37
  }
29
38
 
30
39
  // src/state/getContentSize.ts
@@ -3006,7 +3015,7 @@ function handleLayout(ctx, layout, setCanRender) {
3006
3015
 
3007
3016
  // src/core/onScroll.ts
3008
3017
  function onScroll(ctx, event) {
3009
- var _a3, _b, _c;
3018
+ var _a3, _b, _c, _d;
3010
3019
  const state = ctx.state;
3011
3020
  const {
3012
3021
  scrollProcessingEnabled,
@@ -3018,6 +3027,15 @@ function onScroll(ctx, event) {
3018
3027
  if (((_b = (_a3 = event.nativeEvent) == null ? void 0 : _a3.contentSize) == null ? void 0 : _b.height) === 0 && ((_c = event.nativeEvent.contentSize) == null ? void 0 : _c.width) === 0) {
3019
3028
  return;
3020
3029
  }
3030
+ let insetChanged = false;
3031
+ if ((_d = event.nativeEvent) == null ? void 0 : _d.contentInset) {
3032
+ const { contentInset } = event.nativeEvent;
3033
+ const prevInset = state.nativeContentInset;
3034
+ if (!prevInset || prevInset.top !== contentInset.top || prevInset.bottom !== contentInset.bottom || prevInset.left !== contentInset.left || prevInset.right !== contentInset.right) {
3035
+ state.nativeContentInset = contentInset;
3036
+ insetChanged = true;
3037
+ }
3038
+ }
3021
3039
  let newScroll = event.nativeEvent.contentOffset[state.props.horizontal ? "x" : "y"];
3022
3040
  if (state.scrollingTo) {
3023
3041
  const maxOffset = clampScrollOffset(ctx, newScroll);
@@ -3033,7 +3051,7 @@ function onScroll(ctx, event) {
3033
3051
  }
3034
3052
  }
3035
3053
  state.scrollPending = newScroll;
3036
- updateScroll(ctx, newScroll);
3054
+ updateScroll(ctx, newScroll, insetChanged);
3037
3055
  if (state.scrollingTo) {
3038
3056
  checkFinishedScroll(ctx);
3039
3057
  }
@@ -3206,7 +3224,10 @@ function updateOneItemSize(ctx, itemKey, sizeObj) {
3206
3224
  if (!averages) {
3207
3225
  averages = averageSizes[itemType] = { avg: 0, num: 0 };
3208
3226
  }
3209
- if (prevSizeKnown !== void 0 && prevSizeKnown > 0) {
3227
+ if (averages.num === 0) {
3228
+ averages.avg = size;
3229
+ averages.num++;
3230
+ } else if (prevSizeKnown !== void 0 && prevSizeKnown > 0) {
3210
3231
  averages.avg += (size - prevSizeKnown) / averages.num;
3211
3232
  } else {
3212
3233
  averages.avg = (averages.avg * averages.num + size) / (averages.num + 1);
@@ -3310,7 +3331,7 @@ function createImperativeHandle(ctx) {
3310
3331
  getScrollResponder: () => refScroller.current.getScrollResponder(),
3311
3332
  getState: () => ({
3312
3333
  activeStickyIndex: peek$(ctx, "activeStickyIndex"),
3313
- contentLength: state.totalSize,
3334
+ contentLength: getContentSize(ctx),
3314
3335
  data: state.props.data,
3315
3336
  elementAtIndex: (index) => {
3316
3337
  var _a3;
@@ -3326,6 +3347,7 @@ function createImperativeHandle(ctx) {
3326
3347
  positions: state.positions,
3327
3348
  scroll: state.scroll,
3328
3349
  scrollLength: state.scrollLength,
3350
+ scrollVelocity: getScrollVelocity(state),
3329
3351
  sizeAtIndex: (index) => state.sizesKnown.get(getId(state, index)),
3330
3352
  sizes: state.sizesKnown,
3331
3353
  start: state.startNoBuffer,
@@ -3363,6 +3385,10 @@ function createImperativeHandle(ctx) {
3363
3385
  }
3364
3386
  },
3365
3387
  scrollToOffset: (params) => scrollTo(ctx, params),
3388
+ reportContentInset: (inset) => {
3389
+ state.contentInsetOverride = inset != null ? inset : void 0;
3390
+ updateScroll(ctx, state.scroll, true);
3391
+ },
3366
3392
  setScrollProcessingEnabled: (enabled) => {
3367
3393
  state.scrollProcessingEnabled = enabled;
3368
3394
  },
@@ -3607,7 +3633,14 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
3607
3633
  maintainVisibleContentPositionProp
3608
3634
  );
3609
3635
  const [renderNum, setRenderNum] = useState(0);
3610
- const initialScrollProp = initialScrollAtEnd ? { index: Math.max(0, dataProp.length - 1), viewOffset: -stylePaddingBottomState, viewPosition: 1 } : initialScrollIndexProp || initialScrollOffsetProp ? typeof initialScrollIndexProp === "object" ? { index: initialScrollIndexProp.index || 0, viewOffset: initialScrollIndexProp.viewOffset || 0 } : { index: initialScrollIndexProp || 0, viewOffset: initialScrollOffsetProp || 0 } : void 0;
3636
+ const initialScrollProp = initialScrollAtEnd ? { index: Math.max(0, dataProp.length - 1), viewOffset: -stylePaddingBottomState, viewPosition: 1 } : initialScrollIndexProp || initialScrollOffsetProp ? typeof initialScrollIndexProp === "object" ? {
3637
+ index: initialScrollIndexProp.index || 0,
3638
+ viewOffset: initialScrollIndexProp.viewOffset || (initialScrollIndexProp.viewPosition === 1 ? -stylePaddingBottomState : 0),
3639
+ viewPosition: initialScrollIndexProp.viewPosition || 0
3640
+ } : {
3641
+ index: initialScrollIndexProp || 0,
3642
+ viewOffset: initialScrollOffsetProp || 0
3643
+ } : void 0;
3611
3644
  const [canRender, setCanRender] = React2.useState(!IsNewArchitecture);
3612
3645
  const ctx = useStateContext();
3613
3646
  ctx.columnWrapperStyle = columnWrapperStyle || (contentContainerStyle ? createColumnWrapperStyle(contentContainerStyle) : void 0);
@@ -3645,6 +3678,7 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
3645
3678
  columns: /* @__PURE__ */ new Map(),
3646
3679
  containerItemKeys: /* @__PURE__ */ new Map(),
3647
3680
  containerItemTypes: /* @__PURE__ */ new Map(),
3681
+ contentInsetOverride: void 0,
3648
3682
  dataChangeNeedsScrollUpdate: false,
3649
3683
  didColumnsChange: false,
3650
3684
  didDataChange: false,
@@ -3674,6 +3708,7 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
3674
3708
  lastScrollDelta: 0,
3675
3709
  loadStartTime: Date.now(),
3676
3710
  minIndexSizeChanged: 0,
3711
+ nativeContentInset: void 0,
3677
3712
  nativeMarginTop: 0,
3678
3713
  positions: /* @__PURE__ */ new Map(),
3679
3714
  props: {},
package/keyboard.d.mts CHANGED
@@ -4,13 +4,10 @@ import { ReanimatedScrollEvent } from 'react-native-reanimated/lib/typescript/ho
4
4
  import { LegendListRef } from '@legendapp/list';
5
5
  import { AnimatedLegendListProps } from '@legendapp/list/reanimated';
6
6
 
7
- declare const KeyboardAvoidingLegendList: <ItemT>(props: Omit<AnimatedLegendListProps<ItemT>, "onScroll" | "contentInset"> & {
7
+ declare const KeyboardAvoidingLegendList: <ItemT>(props: Omit<AnimatedLegendListProps<ItemT>, "onScroll" | "automaticallyAdjustContentInsets" | "contentInset"> & {
8
8
  onScroll?: (event: ReanimatedScrollEvent) => void;
9
9
  contentInset?: Insets | undefined;
10
- safeAreaInsets?: {
11
- top: number;
12
- bottom: number;
13
- };
10
+ safeAreaInsetBottom?: number;
14
11
  } & React.RefAttributes<LegendListRef>) => React.ReactNode;
15
12
 
16
13
  export { KeyboardAvoidingLegendList, KeyboardAvoidingLegendList as LegendList };
package/keyboard.d.ts CHANGED
@@ -4,13 +4,10 @@ import { ReanimatedScrollEvent } from 'react-native-reanimated/lib/typescript/ho
4
4
  import { LegendListRef } from '@legendapp/list';
5
5
  import { AnimatedLegendListProps } from '@legendapp/list/reanimated';
6
6
 
7
- declare const KeyboardAvoidingLegendList: <ItemT>(props: Omit<AnimatedLegendListProps<ItemT>, "onScroll" | "contentInset"> & {
7
+ declare const KeyboardAvoidingLegendList: <ItemT>(props: Omit<AnimatedLegendListProps<ItemT>, "onScroll" | "automaticallyAdjustContentInsets" | "contentInset"> & {
8
8
  onScroll?: (event: ReanimatedScrollEvent) => void;
9
9
  contentInset?: Insets | undefined;
10
- safeAreaInsets?: {
11
- top: number;
12
- bottom: number;
13
- };
10
+ safeAreaInsetBottom?: number;
14
11
  } & React.RefAttributes<LegendListRef>) => React.ReactNode;
15
12
 
16
13
  export { KeyboardAvoidingLegendList, KeyboardAvoidingLegendList as LegendList };
package/keyboard.js CHANGED
@@ -28,9 +28,6 @@ var React__namespace = /*#__PURE__*/_interopNamespace(React);
28
28
 
29
29
  // src/integrations/keyboard.tsx
30
30
 
31
- // src/constants-platform.ts
32
- var IsNewArchitecture = true;
33
-
34
31
  // src/utils/helpers.ts
35
32
  function isFunction(obj) {
36
33
  return typeof obj === "function";
@@ -58,17 +55,18 @@ var clampProgress = (progress) => {
58
55
  "worklet";
59
56
  return Math.min(1, Math.max(0, progress));
60
57
  };
61
- var calculateKeyboardInset = (height, safeAreaInsetBottom, isNewArchitecture) => {
62
- "worklet";
63
- return Math.max(0, height - safeAreaInsetBottom) ;
64
- };
65
- var calculateEndPaddingInset = (keyboardHeight, alignItemsAtEndPadding) => {
58
+ var calculateKeyboardInset = (height, safeAreaInsetBottom) => {
66
59
  "worklet";
67
- return Math.min(keyboardHeight, alignItemsAtEndPadding);
60
+ return Math.max(0, height - safeAreaInsetBottom);
68
61
  };
69
- var calculateTopInset = (safeAreaInsetTop, isNewArchitecture, extraTopInset) => {
62
+ var calculateEffectiveKeyboardHeight = (keyboardHeight, contentLength, scrollLength, alignItemsAtEnd) => {
70
63
  "worklet";
71
- return (isNewArchitecture ? 0 : safeAreaInsetTop * 2) + extraTopInset;
64
+ if (alignItemsAtEnd) {
65
+ return keyboardHeight;
66
+ } else {
67
+ const availableSpace = Math.max(0, scrollLength - contentLength);
68
+ return Math.max(0, keyboardHeight - availableSpace);
69
+ }
72
70
  };
73
71
  var calculateKeyboardTargetOffset = (startOffset, keyboardHeight, isOpening, progress) => {
74
72
  "worklet";
@@ -82,10 +80,11 @@ var KeyboardAvoidingLegendList = React.forwardRef(function KeyboardAvoidingLegen
82
80
  horizontal,
83
81
  onMetricsChange: onMetricsChangeProp,
84
82
  onScroll: onScrollProp,
85
- safeAreaInsets = { bottom: 0, top: 0 },
83
+ safeAreaInsetBottom = 0,
86
84
  style: styleProp,
87
85
  ...rest
88
86
  } = props;
87
+ const { alignItemsAtEnd } = props;
89
88
  const styleFlattened = reactNative.StyleSheet.flatten(styleProp);
90
89
  const refLegendList = React.useRef(null);
91
90
  const combinedRef = useCombinedRef(forwardedRef, refLegendList);
@@ -96,16 +95,19 @@ var KeyboardAvoidingLegendList = React.forwardRef(function KeyboardAvoidingLegen
96
95
  const animatedOffsetY = reactNativeReanimated.useSharedValue(null);
97
96
  const scrollOffsetAtKeyboardStart = reactNativeReanimated.useSharedValue(0);
98
97
  const mode = reactNativeReanimated.useSharedValue("idle");
99
- const keyboardInset = reactNativeReanimated.useSharedValue({ bottom: 0, top: 0 });
98
+ const keyboardInset = reactNativeReanimated.useSharedValue(0);
100
99
  const keyboardHeight = reactNativeReanimated.useSharedValue(0);
100
+ const contentLength = reactNativeReanimated.useSharedValue(0);
101
+ const scrollLength = reactNativeReanimated.useSharedValue(0);
101
102
  const alignItemsAtEndPadding = reactNativeReanimated.useSharedValue(0);
102
103
  const isOpening = reactNativeReanimated.useSharedValue(false);
103
104
  const didInteractive = reactNativeReanimated.useSharedValue(false);
104
- const { top: safeAreaInsetTop, bottom: safeAreaInsetBottom } = safeAreaInsets;
105
105
  const isKeyboardOpen = reactNativeReanimated.useSharedValue(false);
106
106
  const scrollHandler = reactNativeReanimated.useAnimatedScrollHandler(
107
107
  (event) => {
108
- scrollOffsetY.set(event.contentOffset[horizontal ? "x" : "y"]);
108
+ if (mode.get() !== "running" || didInteractive.get()) {
109
+ scrollOffsetY.set(event.contentOffset[horizontal ? "x" : "y"]);
110
+ }
109
111
  if (onScrollProp) {
110
112
  reactNativeReanimated.runOnJS(onScrollProp)(event);
111
113
  }
@@ -119,36 +121,23 @@ var KeyboardAvoidingLegendList = React.forwardRef(function KeyboardAvoidingLegen
119
121
  },
120
122
  [refLegendList]
121
123
  );
124
+ const updateScrollMetrics = React.useCallback(() => {
125
+ var _a;
126
+ const state = (_a = refLegendList.current) == null ? void 0 : _a.getState();
127
+ if (!state) {
128
+ return;
129
+ }
130
+ contentLength.set(state.contentLength);
131
+ scrollLength.set(state.scrollLength);
132
+ }, [contentLength, scrollLength]);
122
133
  const handleMetricsChange = React.useCallback(
123
134
  (metrics) => {
135
+ updateScrollMetrics();
124
136
  const nextPadding = metrics.alignItemsAtEndPadding || 0;
125
137
  alignItemsAtEndPadding.set(nextPadding);
126
- if (!horizontal) {
127
- reactNativeReanimated.runOnUI((padding, safeInsetTop, isNewArchitecture) => {
128
- "worklet";
129
- if (!isKeyboardOpen.get()) {
130
- return;
131
- }
132
- const vKeyboardHeight = keyboardHeight.get();
133
- const vTopInset = calculateEndPaddingInset(vKeyboardHeight, padding);
134
- const topInset = calculateTopInset(safeInsetTop, isNewArchitecture, vTopInset);
135
- keyboardInset.set({
136
- bottom: keyboardInset.get().bottom,
137
- top: topInset
138
- });
139
- })(nextPadding, safeAreaInsetTop, IsNewArchitecture);
140
- }
141
138
  onMetricsChangeProp == null ? void 0 : onMetricsChangeProp(metrics);
142
139
  },
143
- [
144
- alignItemsAtEndPadding,
145
- horizontal,
146
- isKeyboardOpen,
147
- keyboardHeight,
148
- keyboardInset,
149
- onMetricsChangeProp,
150
- safeAreaInsetTop
151
- ]
140
+ [alignItemsAtEndPadding, onMetricsChangeProp, updateScrollMetrics]
152
141
  );
153
142
  reactNativeKeyboardController.useKeyboardHandler(
154
143
  // biome-ignore assist/source/useSortedKeys: prefer start/move/end
@@ -177,18 +166,11 @@ var KeyboardAvoidingLegendList = React.forwardRef(function KeyboardAvoidingLegen
177
166
  }
178
167
  mode.set("running");
179
168
  if (!didInteractive.get()) {
180
- if (!isAndroid && !IsNewArchitecture) {
181
- keyboardInset.set({
182
- bottom: keyboardInset.get().bottom,
183
- // Legacy iOS uses a doubled top inset to keep content below the status bar.
184
- top: calculateTopInset(safeAreaInsetTop, IsNewArchitecture, 0)
185
- });
186
- }
187
169
  didInteractive.set(true);
188
170
  }
189
171
  if (isAndroid && !horizontal) {
190
172
  const newInset = calculateKeyboardInset(event.height, safeAreaInsetBottom);
191
- keyboardInset.set({ bottom: newInset, top: safeAreaInsetTop * 2 });
173
+ keyboardInset.set(newInset);
192
174
  }
193
175
  },
194
176
  onMove: (event) => {
@@ -197,11 +179,15 @@ var KeyboardAvoidingLegendList = React.forwardRef(function KeyboardAvoidingLegen
197
179
  const progress = clampProgress(event.progress);
198
180
  const vIsOpening = isOpening.get();
199
181
  const vKeyboardHeight = keyboardHeight.get();
200
- const vAlignItemsPadding = alignItemsAtEndPadding.get();
201
- const vTopInset = calculateEndPaddingInset(vKeyboardHeight, vAlignItemsPadding);
182
+ const vEffectiveKeyboardHeight = calculateEffectiveKeyboardHeight(
183
+ vKeyboardHeight,
184
+ contentLength.get(),
185
+ scrollLength.get(),
186
+ alignItemsAtEnd
187
+ );
202
188
  const targetOffset = calculateKeyboardTargetOffset(
203
189
  scrollOffsetAtKeyboardStart.get(),
204
- vKeyboardHeight,
190
+ vEffectiveKeyboardHeight,
205
191
  vIsOpening,
206
192
  progress
207
193
  );
@@ -209,16 +195,7 @@ var KeyboardAvoidingLegendList = React.forwardRef(function KeyboardAvoidingLegen
209
195
  animatedOffsetY.set(targetOffset);
210
196
  if (!horizontal) {
211
197
  const newInset = calculateKeyboardInset(event.height, safeAreaInsetBottom);
212
- const topInset = calculateTopInset(
213
- safeAreaInsetTop,
214
- IsNewArchitecture,
215
- vIsOpening ? vTopInset : 0
216
- );
217
- keyboardInset.set({
218
- bottom: newInset,
219
- // Add top padding only while opening to keep end-aligned items visible.
220
- top: topInset
221
- });
198
+ keyboardInset.set(newInset);
222
199
  }
223
200
  }
224
201
  },
@@ -230,13 +207,17 @@ var KeyboardAvoidingLegendList = React.forwardRef(function KeyboardAvoidingLegen
230
207
  if (vMode === "running") {
231
208
  const progress = clampProgress(event.progress);
232
209
  const vKeyboardHeight = keyboardHeight.get();
233
- const vAlignItemsPadding = alignItemsAtEndPadding.get();
234
- const vTopInset = calculateEndPaddingInset(vKeyboardHeight, vAlignItemsPadding);
210
+ const vEffectiveKeyboardHeight = calculateEffectiveKeyboardHeight(
211
+ vKeyboardHeight,
212
+ contentLength.get(),
213
+ scrollLength.get(),
214
+ alignItemsAtEnd
215
+ );
235
216
  const vIsOpening = isOpening.get();
236
217
  if (!wasInteractive) {
237
218
  const targetOffset = calculateKeyboardTargetOffset(
238
219
  scrollOffsetAtKeyboardStart.get(),
239
- vKeyboardHeight,
220
+ vEffectiveKeyboardHeight,
240
221
  vIsOpening,
241
222
  progress
242
223
  );
@@ -248,16 +229,7 @@ var KeyboardAvoidingLegendList = React.forwardRef(function KeyboardAvoidingLegen
248
229
  isKeyboardOpen.set(event.height > 0);
249
230
  if (!horizontal) {
250
231
  const newInset = calculateKeyboardInset(event.height, safeAreaInsetBottom);
251
- const topInset = calculateTopInset(
252
- safeAreaInsetTop,
253
- IsNewArchitecture,
254
- event.height > 0 ? vTopInset : 0
255
- );
256
- keyboardInset.set({
257
- bottom: newInset,
258
- // Preserve end-aligned padding only while the keyboard is visible.
259
- top: topInset
260
- });
232
+ keyboardInset.set(newInset);
261
233
  if (newInset <= 0) {
262
234
  animatedOffsetY.set(scrollOffsetY.get());
263
235
  }
@@ -265,7 +237,7 @@ var KeyboardAvoidingLegendList = React.forwardRef(function KeyboardAvoidingLegen
265
237
  }
266
238
  }
267
239
  },
268
- [scrollViewRef, safeAreaInsetBottom]
240
+ [alignItemsAtEnd, safeAreaInsetBottom, scrollViewRef]
269
241
  );
270
242
  const animatedProps = reactNativeReanimated.useAnimatedProps(() => {
271
243
  "worklet";
@@ -277,24 +249,21 @@ var KeyboardAvoidingLegendList = React.forwardRef(function KeyboardAvoidingLegen
277
249
  y: vAnimatedOffsetY
278
250
  }
279
251
  };
280
- const { top: keyboardInsetTop, bottom: keyboardInsetBottom } = keyboardInset.get();
252
+ const keyboardInsetBottom = keyboardInset.get();
281
253
  return isIos ? Object.assign(baseProps, {
282
254
  contentInset: {
283
255
  bottom: ((_a = contentInsetProp == null ? void 0 : contentInsetProp.bottom) != null ? _a : 0) + (horizontal ? 0 : keyboardInsetBottom),
284
256
  left: (_b = contentInsetProp == null ? void 0 : contentInsetProp.left) != null ? _b : 0,
285
257
  right: (_c = contentInsetProp == null ? void 0 : contentInsetProp.right) != null ? _c : 0,
286
- top: ((_d = contentInsetProp == null ? void 0 : contentInsetProp.top) != null ? _d : 0) - keyboardInsetTop
258
+ top: (_d = contentInsetProp == null ? void 0 : contentInsetProp.top) != null ? _d : 0
287
259
  }
288
260
  }) : baseProps;
289
261
  });
290
262
  const style = isAndroid ? reactNativeReanimated.useAnimatedStyle(
291
- () => {
292
- var _a;
293
- return {
294
- ...styleFlattened || {},
295
- marginBottom: (_a = keyboardInset.get().bottom) != null ? _a : 0
296
- };
297
- },
263
+ () => ({
264
+ ...styleFlattened || {},
265
+ marginBottom: keyboardInset.get()
266
+ }),
298
267
  [styleProp, keyboardInset]
299
268
  ) : void 0;
300
269
  return /* @__PURE__ */ React__namespace.createElement(
@@ -302,6 +271,7 @@ var KeyboardAvoidingLegendList = React.forwardRef(function KeyboardAvoidingLegen
302
271
  {
303
272
  ...rest,
304
273
  animatedProps,
274
+ automaticallyAdjustContentInsets: false,
305
275
  keyboardDismissMode: "interactive",
306
276
  onMetricsChange: handleMetricsChange,
307
277
  onScroll: scrollHandler,