react-native-header-motion 0.1.1 → 0.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +58 -0
- package/lib/module/components/FlatList.js +12 -2
- package/lib/module/components/FlatList.js.map +1 -1
- package/lib/module/components/ScrollManager.js +13 -2
- package/lib/module/components/ScrollManager.js.map +1 -1
- package/lib/module/components/ScrollView.js +8 -0
- package/lib/module/components/ScrollView.js.map +1 -1
- package/lib/module/hooks/refreshControl.js +31 -0
- package/lib/module/hooks/refreshControl.js.map +1 -0
- package/lib/module/hooks/useScrollManager.js +20 -4
- package/lib/module/hooks/useScrollManager.js.map +1 -1
- package/lib/typescript/src/components/FlatList.d.ts +8 -5
- package/lib/typescript/src/components/FlatList.d.ts.map +1 -1
- package/lib/typescript/src/components/ScrollManager.d.ts +14 -2
- package/lib/typescript/src/components/ScrollManager.d.ts.map +1 -1
- package/lib/typescript/src/components/ScrollView.d.ts +7 -2
- package/lib/typescript/src/components/ScrollView.d.ts.map +1 -1
- package/lib/typescript/src/hooks/refreshControl.d.ts +13 -0
- package/lib/typescript/src/hooks/refreshControl.d.ts.map +1 -0
- package/lib/typescript/src/hooks/useScrollManager.d.ts +18 -1
- package/lib/typescript/src/hooks/useScrollManager.d.ts.map +1 -1
- package/lib/typescript/src/types.d.ts +3 -1
- package/lib/typescript/src/types.d.ts.map +1 -1
- package/package.json +1 -1
- package/src/components/FlatList.tsx +23 -9
- package/src/components/ScrollManager.tsx +26 -2
- package/src/components/ScrollView.tsx +17 -2
- package/src/hooks/refreshControl.ts +55 -0
- package/src/hooks/useScrollManager.ts +41 -2
- package/src/types.ts +7 -1
package/README.md
CHANGED
|
@@ -407,6 +407,15 @@ Supports `scrollId?: string` for multi-scroll scenarios.
|
|
|
407
407
|
|
|
408
408
|
Render-prop API for custom scrollables (pager pages, 3rd party lists, etc.).
|
|
409
409
|
|
|
410
|
+
If you use `HeaderMotion.ScrollManager` directly for custom integrations, pass refresh-related props to `ScrollManager` (instead of your inner scrollable):
|
|
411
|
+
|
|
412
|
+
- `refreshControl`
|
|
413
|
+
- `refreshing`
|
|
414
|
+
- `onRefresh`
|
|
415
|
+
- optional `progressViewOffset` if you want to force your offset.
|
|
416
|
+
|
|
417
|
+
This is required, as the positioning of scrollables is affecting Refresh Control and has to be coupled with the header heights.
|
|
418
|
+
|
|
410
419
|
```tsx
|
|
411
420
|
<HeaderMotion.ScrollManager scrollId="A">
|
|
412
421
|
{(
|
|
@@ -424,6 +433,31 @@ Render-prop API for custom scrollables (pager pages, 3rd party lists, etc.).
|
|
|
424
433
|
</HeaderMotion.ScrollManager>
|
|
425
434
|
```
|
|
426
435
|
|
|
436
|
+
Refresh example with explicit props on `ScrollManager`:
|
|
437
|
+
|
|
438
|
+
```tsx
|
|
439
|
+
<HeaderMotion.ScrollManager
|
|
440
|
+
scrollId="A"
|
|
441
|
+
refreshing={refreshing}
|
|
442
|
+
onRefresh={onRefresh}
|
|
443
|
+
>
|
|
444
|
+
{(
|
|
445
|
+
{ onScroll, refreshControl: managedRefreshControl, ...scrollableProps },
|
|
446
|
+
{ originalHeaderHeight, minHeightContentContainerStyle }
|
|
447
|
+
) => (
|
|
448
|
+
<Animated.ScrollView
|
|
449
|
+
{...scrollableProps}
|
|
450
|
+
onScroll={onScroll}
|
|
451
|
+
refreshControl={managedRefreshControl}
|
|
452
|
+
contentContainerStyle={[
|
|
453
|
+
minHeightContentContainerStyle,
|
|
454
|
+
{ paddingTop: originalHeaderHeight },
|
|
455
|
+
]}
|
|
456
|
+
/>
|
|
457
|
+
)}
|
|
458
|
+
</HeaderMotion.ScrollManager>
|
|
459
|
+
```
|
|
460
|
+
|
|
427
461
|
### Hooks
|
|
428
462
|
|
|
429
463
|
#### `useMotionProgress()`
|
|
@@ -469,6 +503,30 @@ Reanimated-powered, absolutely positioned header base.
|
|
|
469
503
|
- `WithCollapsibleHeaderProps` – convenience type for headers using motion progress props.
|
|
470
504
|
- `WithCollapsiblePagedHeaderProps` – like above, plus `activeTab` and `onTabChange`.
|
|
471
505
|
|
|
506
|
+
## Additional notes
|
|
507
|
+
|
|
508
|
+
### Refresh Control (v.0.3.0+)
|
|
509
|
+
|
|
510
|
+
Refresh control support was improved in `v0.3.0+`.
|
|
511
|
+
|
|
512
|
+
- If you use `HeaderMotion.ScrollView` or `HeaderMotion.FlatList`, your refresh-control usage stays the same as in React Native.
|
|
513
|
+
- If you use `HeaderMotion.ScrollManager` directly for custom integrations, pass refresh-related props to `ScrollManager`:
|
|
514
|
+
- `refreshControl`
|
|
515
|
+
- `refreshing`
|
|
516
|
+
- `onRefresh`
|
|
517
|
+
- optional `progressViewOffset`
|
|
518
|
+
|
|
519
|
+
This is important because scrollable positioning affects refresh-control behavior and needs to stay coupled with measured header height.
|
|
520
|
+
|
|
521
|
+
#### Platform support note:
|
|
522
|
+
|
|
523
|
+
- Support for Refresh Control is currently partial.
|
|
524
|
+
- Android works well with the current implementation.
|
|
525
|
+
- iOS behavior is still not fully deterministic.
|
|
526
|
+
- `progressViewOffset` does not seem to be reliable on iOS in all scenarios.
|
|
527
|
+
- Other iOS approaches tried so far introduced different issues.
|
|
528
|
+
- Additional iOS support improvements are planned for future releases.
|
|
529
|
+
|
|
472
530
|
## Contributing
|
|
473
531
|
|
|
474
532
|
- Development workflow: see [CONTRIBUTING.md](CONTRIBUTING.md)
|
|
@@ -23,13 +23,20 @@ import { jsx as _jsx } from "react/jsx-runtime";
|
|
|
23
23
|
*/
|
|
24
24
|
export function HeaderMotionFlatList({
|
|
25
25
|
scrollId,
|
|
26
|
+
animatedRef,
|
|
26
27
|
contentContainerStyle,
|
|
27
28
|
...props
|
|
28
29
|
}) {
|
|
29
30
|
return /*#__PURE__*/_jsx(HeaderMotionScrollManager, {
|
|
30
31
|
scrollId: scrollId,
|
|
32
|
+
animatedRef: animatedRef,
|
|
33
|
+
refreshControl: props.refreshControl,
|
|
34
|
+
refreshing: props.refreshing,
|
|
35
|
+
onRefresh: props.onRefresh,
|
|
36
|
+
progressViewOffset: props.progressViewOffset,
|
|
31
37
|
children: ({
|
|
32
38
|
onScroll,
|
|
39
|
+
refreshControl: managedRefreshControl,
|
|
33
40
|
...scrollViewProps
|
|
34
41
|
}, {
|
|
35
42
|
originalHeaderHeight,
|
|
@@ -38,8 +45,11 @@ export function HeaderMotionFlatList({
|
|
|
38
45
|
...scrollViewProps,
|
|
39
46
|
...props,
|
|
40
47
|
onScroll: onScroll,
|
|
41
|
-
|
|
42
|
-
|
|
48
|
+
...(managedRefreshControl && {
|
|
49
|
+
refreshControl: managedRefreshControl
|
|
50
|
+
}),
|
|
51
|
+
renderScrollComponent: scrollComponentProps => /*#__PURE__*/_jsx(AnimatedScrollContainer, {
|
|
52
|
+
...scrollComponentProps,
|
|
43
53
|
contentContainerStyle: [minHeightContentContainerStyle, {
|
|
44
54
|
paddingTop: originalHeaderHeight
|
|
45
55
|
}, contentContainerStyle]
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"names":["forwardRef","Animated","HeaderMotionScrollManager","jsx","_jsx","HeaderMotionFlatList","scrollId","contentContainerStyle","props","children","onScroll","scrollViewProps","originalHeaderHeight","minHeightContentContainerStyle","FlatList","renderScrollComponent","
|
|
1
|
+
{"version":3,"names":["forwardRef","Animated","HeaderMotionScrollManager","jsx","_jsx","HeaderMotionFlatList","scrollId","animatedRef","contentContainerStyle","props","refreshControl","refreshing","onRefresh","progressViewOffset","children","onScroll","managedRefreshControl","scrollViewProps","originalHeaderHeight","minHeightContentContainerStyle","FlatList","renderScrollComponent","scrollComponentProps","AnimatedScrollContainer","paddingTop","rest","ref","ScrollView","View","style"],"sourceRoot":"../../../src","sources":["components/FlatList.tsx"],"mappings":";;AAAA,SAASA,UAAU,QAAgD,OAAO;AAC1E,OAAOC,QAAQ,MAA4B,yBAAyB;AACpE,SAASC,yBAAyB,QAAQ,oBAAiB;AAAC,SAAAC,GAAA,IAAAC,IAAA;AAmB5D;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,OAAO,SAASC,oBAAoBA,CAAU;EAC5CC,QAAQ;EACRC,WAAW;EACXC,qBAAqB;EACrB,GAAGC;AACyB,CAAC,EAAE;EAC/B,oBACEL,IAAA,CAACF,yBAAyB;IACxBI,QAAQ,EAAEA,QAAS;IACnBC,WAAW,EAAEA,WAAY;IACzBG,cAAc,EAAED,KAAK,CAACC,cAAe;IACrCC,UAAU,EAAEF,KAAK,CAACE,UAAW;IAC7BC,SAAS,EAAEH,KAAK,CAACG,SAAU;IAC3BC,kBAAkB,EAAEJ,KAAK,CAACI,kBAAmB;IAAAC,QAAA,EAE5CA,CACC;MAAEC,QAAQ;MAAEL,cAAc,EAAEM,qBAAqB;MAAE,GAAGC;IAAgB,CAAC,EACvE;MAAEC,oBAAoB;MAAEC;IAA+B,CAAC,kBAExDf,IAAA,CAACH,QAAQ,CAACmB,QAAQ;MAAA,GACZH,eAAe;MAAA,GACfR,KAAK;MACTM,QAAQ,EAAEA,QAAS;MAAA,IACdC,qBAAqB,IAAI;QAC5BN,cAAc,EAAEM;MAClB,CAAC;MACDK,qBAAqB,EAAGC,oBAAoB,iBAC1ClB,IAAA,CAACmB,uBAAuB;QAAA,GAClBD,oBAAoB;QACxBd,qBAAqB,EAAE,CACrBW,8BAA8B,EAC9B;UAAEK,UAAU,EAAEN;QAAqB,CAAC,EACpCV,qBAAqB;MACrB,CACH;IACD,CACH;EACF,CACwB,CAAC;AAEhC;AAEA,MAAMe,uBAAuB,gBAAGvB,UAAU,CAGxC,CAAC;EAAEc,QAAQ;EAAEN,qBAAqB;EAAE,GAAGiB;AAAK,CAAC,EAAEC,GAAG,KAAK;EACvD,oBACEtB,IAAA,CAACH,QAAQ,CAAC0B,UAAU;IAAA,GAAKF,IAAI;IAAEC,GAAG,EAAEA,GAAI;IAAAZ,QAAA,eACtCV,IAAA,CAACH,QAAQ,CAAC2B,IAAI;MAACC,KAAK,EAAErB,qBAAsB;MAAAM,QAAA,EAAEA;IAAQ,CAAgB;EAAC,CACpD,CAAC;AAE1B,CAAC,CAAC","ignoreList":[]}
|
|
@@ -25,7 +25,12 @@ import { useScrollManager } from "../hooks/index.js";
|
|
|
25
25
|
*/
|
|
26
26
|
export function HeaderMotionScrollManager({
|
|
27
27
|
children,
|
|
28
|
-
scrollId
|
|
28
|
+
scrollId,
|
|
29
|
+
animatedRef,
|
|
30
|
+
refreshControl,
|
|
31
|
+
refreshing,
|
|
32
|
+
onRefresh,
|
|
33
|
+
progressViewOffset
|
|
29
34
|
}) {
|
|
30
35
|
if (typeof children !== 'function') {
|
|
31
36
|
throw new Error('HeaderMotion.ScrollManager only accepts render function as the only child.');
|
|
@@ -33,7 +38,13 @@ export function HeaderMotionScrollManager({
|
|
|
33
38
|
const {
|
|
34
39
|
scrollableProps,
|
|
35
40
|
headerMotionContext
|
|
36
|
-
} = useScrollManager(scrollId
|
|
41
|
+
} = useScrollManager(scrollId, {
|
|
42
|
+
animatedRef,
|
|
43
|
+
refreshControl,
|
|
44
|
+
refreshing,
|
|
45
|
+
onRefresh,
|
|
46
|
+
progressViewOffset
|
|
47
|
+
});
|
|
37
48
|
return children(scrollableProps, headerMotionContext);
|
|
38
49
|
}
|
|
39
50
|
//# sourceMappingURL=ScrollManager.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"names":["useScrollManager","HeaderMotionScrollManager","children","scrollId","Error","scrollableProps","headerMotionContext"],"sourceRoot":"../../../src","sources":["components/ScrollManager.tsx"],"mappings":";;AAAA,SAASA,gBAAgB,QAAQ,mBAAU;
|
|
1
|
+
{"version":3,"names":["useScrollManager","HeaderMotionScrollManager","children","scrollId","animatedRef","refreshControl","refreshing","onRefresh","progressViewOffset","Error","scrollableProps","headerMotionContext"],"sourceRoot":"../../../src","sources":["components/ScrollManager.tsx"],"mappings":";;AAAA,SAASA,gBAAgB,QAAQ,mBAAU;AAmC3C;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,OAAO,SAASC,yBAAyBA,CAAC;EACxCC,QAAQ;EACRC,QAAQ;EACRC,WAAW;EACXC,cAAc;EACdC,UAAU;EACVC,SAAS;EACTC;AAC8B,CAAC,EAAE;EACjC,IAAI,OAAON,QAAQ,KAAK,UAAU,EAAE;IAClC,MAAM,IAAIO,KAAK,CACb,4EACF,CAAC;EACH;EAEA,MAAM;IAAEC,eAAe;IAAEC;EAAoB,CAAC,GAAGX,gBAAgB,CAACG,QAAQ,EAAE;IAC1EC,WAAW;IACXC,cAAc;IACdC,UAAU;IACVC,SAAS;IACTC;EACF,CAAC,CAAC;EAEF,OAAON,QAAQ,CAACQ,eAAe,EAAEC,mBAAmB,CAAC;AACvD","ignoreList":[]}
|
|
@@ -19,14 +19,19 @@ import { jsx as _jsx } from "react/jsx-runtime";
|
|
|
19
19
|
*/
|
|
20
20
|
export function HeaderMotionScrollView({
|
|
21
21
|
scrollId,
|
|
22
|
+
animatedRef,
|
|
22
23
|
children,
|
|
23
24
|
contentContainerStyle,
|
|
25
|
+
refreshControl,
|
|
24
26
|
...props
|
|
25
27
|
}) {
|
|
26
28
|
return /*#__PURE__*/_jsx(HeaderMotionScrollManager, {
|
|
27
29
|
scrollId: scrollId,
|
|
30
|
+
animatedRef: animatedRef,
|
|
31
|
+
refreshControl: refreshControl,
|
|
28
32
|
children: ({
|
|
29
33
|
onScroll,
|
|
34
|
+
refreshControl: managedRefreshControl,
|
|
30
35
|
...scrollViewProps
|
|
31
36
|
}, {
|
|
32
37
|
originalHeaderHeight,
|
|
@@ -35,6 +40,9 @@ export function HeaderMotionScrollView({
|
|
|
35
40
|
...scrollViewProps,
|
|
36
41
|
...props,
|
|
37
42
|
onScroll: onScroll,
|
|
43
|
+
...(managedRefreshControl && {
|
|
44
|
+
refreshControl: managedRefreshControl
|
|
45
|
+
}),
|
|
38
46
|
children: /*#__PURE__*/_jsx(Animated.View, {
|
|
39
47
|
style: [minHeightContentContainerStyle, {
|
|
40
48
|
paddingTop: originalHeaderHeight
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"names":["Animated","HeaderMotionScrollManager","jsx","_jsx","HeaderMotionScrollView","scrollId","children","contentContainerStyle","props","onScroll","scrollViewProps","originalHeaderHeight","minHeightContentContainerStyle","ScrollView","View","style","paddingTop"],"sourceRoot":"../../../src","sources":["components/ScrollView.tsx"],"mappings":";;AAAA,OAAOA,QAAQ,
|
|
1
|
+
{"version":3,"names":["Animated","HeaderMotionScrollManager","jsx","_jsx","HeaderMotionScrollView","scrollId","animatedRef","children","contentContainerStyle","refreshControl","props","onScroll","managedRefreshControl","scrollViewProps","originalHeaderHeight","minHeightContentContainerStyle","ScrollView","View","style","paddingTop"],"sourceRoot":"../../../src","sources":["components/ScrollView.tsx"],"mappings":";;AAAA,OAAOA,QAAQ,MAGR,yBAAyB;AAChC,SAASC,yBAAyB,QAAQ,oBAAiB;AAAC,SAAAC,GAAA,IAAAC,IAAA;AAe5D;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,OAAO,SAASC,sBAAsBA,CAAC;EACrCC,QAAQ;EACRC,WAAW;EACXC,QAAQ;EACRC,qBAAqB;EACrBC,cAAc;EACd,GAAGC;AACwB,CAAC,EAAE;EAC9B,oBACEP,IAAA,CAACF,yBAAyB;IACxBI,QAAQ,EAAEA,QAAS;IACnBC,WAAW,EAAEA,WAAY;IACzBG,cAAc,EAAEA,cAAe;IAAAF,QAAA,EAE9BA,CACC;MAAEI,QAAQ;MAAEF,cAAc,EAAEG,qBAAqB;MAAE,GAAGC;IAAgB,CAAC,EACvE;MAAEC,oBAAoB;MAAEC;IAA+B,CAAC,kBAExDZ,IAAA,CAACH,QAAQ,CAACgB,UAAU;MAAA,GACdH,eAAe;MAAA,GACfH,KAAK;MACTC,QAAQ,EAAEA,QAAS;MAAA,IACdC,qBAAqB,IAAI;QAC5BH,cAAc,EAAEG;MAClB,CAAC;MAAAL,QAAA,eAEDJ,IAAA,CAACH,QAAQ,CAACiB,IAAI;QACZC,KAAK,EAAE,CACLH,8BAA8B,EAC9B;UAAEI,UAAU,EAAEL;QAAqB,CAAC,EACpCN,qBAAqB,CACrB;QAAAD,QAAA,EAEDA;MAAQ,CACI;IAAC,CACG;EACtB,CACwB,CAAC;AAEhC","ignoreList":[]}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
import { cloneElement, createElement, isValidElement } from 'react';
|
|
4
|
+
import { RefreshControl } from 'react-native';
|
|
5
|
+
function injectProgressViewOffset(refreshControl, progressViewOffset) {
|
|
6
|
+
if (refreshControl.props.progressViewOffset !== undefined) {
|
|
7
|
+
return refreshControl;
|
|
8
|
+
}
|
|
9
|
+
return /*#__PURE__*/cloneElement(refreshControl, {
|
|
10
|
+
progressViewOffset: progressViewOffset
|
|
11
|
+
});
|
|
12
|
+
}
|
|
13
|
+
export function resolveRefreshControl({
|
|
14
|
+
refreshControl,
|
|
15
|
+
refreshing,
|
|
16
|
+
onRefresh,
|
|
17
|
+
progressViewOffset
|
|
18
|
+
}) {
|
|
19
|
+
if (refreshControl) {
|
|
20
|
+
return /*#__PURE__*/isValidElement(refreshControl) ? injectProgressViewOffset(refreshControl, progressViewOffset) : undefined;
|
|
21
|
+
}
|
|
22
|
+
if (!onRefresh) {
|
|
23
|
+
return undefined;
|
|
24
|
+
}
|
|
25
|
+
return /*#__PURE__*/createElement(RefreshControl, {
|
|
26
|
+
refreshing: refreshing ?? false,
|
|
27
|
+
onRefresh: onRefresh,
|
|
28
|
+
progressViewOffset: progressViewOffset
|
|
29
|
+
});
|
|
30
|
+
}
|
|
31
|
+
//# sourceMappingURL=refreshControl.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"names":["cloneElement","createElement","isValidElement","RefreshControl","injectProgressViewOffset","refreshControl","progressViewOffset","props","undefined","resolveRefreshControl","refreshing","onRefresh"],"sourceRoot":"../../../src","sources":["hooks/refreshControl.ts"],"mappings":";;AAAA,SACEA,YAAY,EACZC,aAAa,EACbC,cAAc,QAET,OAAO;AACd,SAASC,cAAc,QAAkC,cAAc;AAYvE,SAASC,wBAAwBA,CAC/BC,cAAiD,EACjDC,kBAAuC,EACvC;EACA,IAAID,cAAc,CAACE,KAAK,CAACD,kBAAkB,KAAKE,SAAS,EAAE;IACzD,OAAOH,cAAc;EACvB;EAEA,oBAAOL,YAAY,CAACK,cAAc,EAAE;IAClCC,kBAAkB,EAAEA;EACtB,CAAC,CAAC;AACJ;AAEA,OAAO,SAASG,qBAAqBA,CAAC;EACpCJ,cAAc;EACdK,UAAU;EACVC,SAAS;EACTL;AAC4B,CAAC,EAEjB;EACZ,IAAID,cAAc,EAAE;IAClB,OAAO,aAAAH,cAAc,CAAsBG,cAAc,CAAC,GACtDD,wBAAwB,CAACC,cAAc,EAAEC,kBAAkB,CAAC,GAC5DE,SAAS;EACf;EAEA,IAAI,CAACG,SAAS,EAAE;IACd,OAAOH,SAAS;EAClB;EAEA,oBAAOP,aAAa,CAACE,cAAc,EAAE;IACnCO,UAAU,EAAGA,UAAU,IAAgB,KAAK;IAC5CC,SAAS,EAAEA,SAAuB;IAClCL,kBAAkB,EAAEA;EACtB,CAAC,CAAC;AACJ","ignoreList":[]}
|
|
@@ -5,6 +5,7 @@ import { measure, scrollTo, useAnimatedReaction, useAnimatedRef, useAnimatedScro
|
|
|
5
5
|
import { RuntimeKind, scheduleOnUI } from 'react-native-worklets';
|
|
6
6
|
import { HeaderMotionContext } from "../context.js";
|
|
7
7
|
import { DEFAULT_SCROLL_ID, getInitialScrollValue } from "../utils/index.js";
|
|
8
|
+
import { resolveRefreshControl } from "./refreshControl.js";
|
|
8
9
|
|
|
9
10
|
/**
|
|
10
11
|
* Hook that manages scroll tracking and synchronization for header animations.
|
|
@@ -19,6 +20,9 @@ import { DEFAULT_SCROLL_ID, getInitialScrollValue } from "../utils/index.js";
|
|
|
19
20
|
*
|
|
20
21
|
* @param scrollId - Optional unique identifier for the related scrollable.
|
|
21
22
|
* Use when you have multiple scrollables (e.g., in tabs).
|
|
23
|
+
* @param options - Optional configuration object.
|
|
24
|
+
* @param options.animatedRef - Optional animated ref to use instead of creating one internally.
|
|
25
|
+
* Useful when you need access to the scroll view ref from outside.
|
|
22
26
|
* @returns Configuration object containing:
|
|
23
27
|
* - `scrollableProps`: Props to apply to scrollable component (onScroll, scrollEventThrottle, ref)
|
|
24
28
|
* - `headerMotionContext`: Header context values (originalHeaderHeight, minHeightContentContainerStyle)
|
|
@@ -40,7 +44,8 @@ import { DEFAULT_SCROLL_ID, getInitialScrollValue } from "../utils/index.js";
|
|
|
40
44
|
* }
|
|
41
45
|
* ```
|
|
42
46
|
*/
|
|
43
|
-
|
|
47
|
+
|
|
48
|
+
export function useScrollManager(scrollId, options) {
|
|
44
49
|
const ctxValue = useContext(HeaderMotionContext);
|
|
45
50
|
if (!ctxValue) {
|
|
46
51
|
throw new Error('useScrollManager must be used within a HeaderMotion component');
|
|
@@ -53,8 +58,12 @@ export function useScrollManager(scrollId) {
|
|
|
53
58
|
originalHeaderHeight
|
|
54
59
|
} = ctxValue;
|
|
55
60
|
const id = scrollId ?? DEFAULT_SCROLL_ID;
|
|
56
|
-
const
|
|
57
|
-
|
|
61
|
+
const localRef = useAnimatedRef(); // TODO: better typing
|
|
62
|
+
const animatedRef = options?.animatedRef ?? localRef;
|
|
63
|
+
const refreshControl = options?.refreshControl;
|
|
64
|
+
const refreshing = options?.refreshing;
|
|
65
|
+
const onRefresh = options?.onRefresh;
|
|
66
|
+
const progressViewOffset = options?.progressViewOffset ?? originalHeaderHeight;
|
|
58
67
|
useEffect(() => {
|
|
59
68
|
return () => {
|
|
60
69
|
scheduleOnUI(scrollIdToDelete => {
|
|
@@ -133,10 +142,17 @@ export function useScrollManager(scrollId) {
|
|
|
133
142
|
minHeight: measurement.height + progressThreshold
|
|
134
143
|
};
|
|
135
144
|
});
|
|
145
|
+
const resolvedRefreshControl = resolveRefreshControl({
|
|
146
|
+
refreshControl,
|
|
147
|
+
refreshing,
|
|
148
|
+
onRefresh,
|
|
149
|
+
progressViewOffset
|
|
150
|
+
});
|
|
136
151
|
const scrollableProps = {
|
|
137
152
|
onScroll,
|
|
138
153
|
scrollEventThrottle: 16,
|
|
139
|
-
ref: animatedRef
|
|
154
|
+
ref: animatedRef,
|
|
155
|
+
refreshControl: resolvedRefreshControl
|
|
140
156
|
};
|
|
141
157
|
const headerMotionContext = {
|
|
142
158
|
originalHeaderHeight,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"names":["useContext","useCallback","useEffect","measure","scrollTo","useAnimatedReaction","useAnimatedRef","useAnimatedScrollHandler","useAnimatedStyle","RuntimeKind","scheduleOnUI","HeaderMotionContext","DEFAULT_SCROLL_ID","getInitialScrollValue","useScrollManager","scrollId","ctxValue","Error","scrollValues","progress","activeScrollId","progressThreshold","originalHeaderHeight","id","animatedRef","scrollIdToDelete","modify","value","newProgress","oldProgress","currentActiveScrollId","get","newCur","scrollValue","progressDiff","current","newMin","min","scrollHandler","e","activeScrollIdValue","oldCurrent","oldMin","isCollapsed","newCurrent","contentOffset","y","Math","max","onScroll","minHeightContentContainerStyle","globalThis","__RUNTIME_KIND","ReactNative","measurement","minHeight","height","scrollableProps","scrollEventThrottle","ref","headerMotionContext"],"sourceRoot":"../../../src","sources":["hooks/useScrollManager.ts"],"mappings":";;AAAA,SAASA,UAAU,EAAEC,WAAW,EAAEC,SAAS,QAAQ,OAAO;AAC1D,SACEC,OAAO,EACPC,QAAQ,EACRC,mBAAmB,EACnBC,cAAc,EACdC,wBAAwB,EACxBC,gBAAgB,
|
|
1
|
+
{"version":3,"names":["useContext","useCallback","useEffect","measure","scrollTo","useAnimatedReaction","useAnimatedRef","useAnimatedScrollHandler","useAnimatedStyle","RuntimeKind","scheduleOnUI","HeaderMotionContext","DEFAULT_SCROLL_ID","getInitialScrollValue","resolveRefreshControl","useScrollManager","scrollId","options","ctxValue","Error","scrollValues","progress","activeScrollId","progressThreshold","originalHeaderHeight","id","localRef","animatedRef","refreshControl","refreshing","onRefresh","progressViewOffset","scrollIdToDelete","modify","value","newProgress","oldProgress","currentActiveScrollId","get","newCur","scrollValue","progressDiff","current","newMin","min","scrollHandler","e","activeScrollIdValue","oldCurrent","oldMin","isCollapsed","newCurrent","contentOffset","y","Math","max","onScroll","minHeightContentContainerStyle","globalThis","__RUNTIME_KIND","ReactNative","measurement","minHeight","height","resolvedRefreshControl","scrollableProps","scrollEventThrottle","ref","headerMotionContext"],"sourceRoot":"../../../src","sources":["hooks/useScrollManager.ts"],"mappings":"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","ignoreList":[]}
|
|
@@ -1,12 +1,16 @@
|
|
|
1
1
|
import { type ComponentProps } from 'react';
|
|
2
|
-
import Animated from 'react-native-reanimated';
|
|
3
|
-
type
|
|
4
|
-
export type HeaderMotionFlatListProps<T = any> = AnimatedFlatListProps<T> & {
|
|
2
|
+
import Animated, { type AnimatedRef } from 'react-native-reanimated';
|
|
3
|
+
export type HeaderMotionFlatListProps<T = any> = ComponentProps<typeof Animated.FlatList<T>> & {
|
|
5
4
|
/**
|
|
6
5
|
* Optional unique identifier for this scroll view.
|
|
7
6
|
* Use this when you have multiple scroll views (e.g. in tabs) to track them separately.
|
|
8
7
|
*/
|
|
9
8
|
scrollId?: string;
|
|
9
|
+
/**
|
|
10
|
+
* Optional animated ref to use for the flat list.
|
|
11
|
+
* When provided, the scroll manager will use this ref instead of creating its own.
|
|
12
|
+
*/
|
|
13
|
+
animatedRef?: AnimatedRef<any>;
|
|
10
14
|
};
|
|
11
15
|
/**
|
|
12
16
|
* Animated FlatList component that integrates with HeaderMotion.
|
|
@@ -25,6 +29,5 @@ export type HeaderMotionFlatListProps<T = any> = AnimatedFlatListProps<T> & {
|
|
|
25
29
|
* </HeaderMotion>
|
|
26
30
|
* ```
|
|
27
31
|
*/
|
|
28
|
-
export declare function HeaderMotionFlatList<T = any>({ scrollId, contentContainerStyle, ...props }: HeaderMotionFlatListProps<T>): import("react/jsx-runtime").JSX.Element;
|
|
29
|
-
export {};
|
|
32
|
+
export declare function HeaderMotionFlatList<T = any>({ scrollId, animatedRef, contentContainerStyle, ...props }: HeaderMotionFlatListProps<T>): import("react/jsx-runtime").JSX.Element;
|
|
30
33
|
//# sourceMappingURL=FlatList.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"FlatList.d.ts","sourceRoot":"","sources":["../../../../src/components/FlatList.tsx"],"names":[],"mappings":"AAAA,OAAO,EAAc,KAAK,cAAc,EAAqB,MAAM,OAAO,CAAC;AAC3E,OAAO,QAAQ,MAAM,yBAAyB,CAAC;
|
|
1
|
+
{"version":3,"file":"FlatList.d.ts","sourceRoot":"","sources":["../../../../src/components/FlatList.tsx"],"names":[],"mappings":"AAAA,OAAO,EAAc,KAAK,cAAc,EAAqB,MAAM,OAAO,CAAC;AAC3E,OAAO,QAAQ,EAAE,EAAE,KAAK,WAAW,EAAE,MAAM,yBAAyB,CAAC;AAKrE,MAAM,MAAM,yBAAyB,CAAC,CAAC,GAAG,GAAG,IAAI,cAAc,CAC7D,OAAO,QAAQ,CAAC,QAAQ,CAAC,CAAC,CAAC,CAC5B,GAAG;IACF;;;OAGG;IACH,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB;;;OAGG;IACH,WAAW,CAAC,EAAE,WAAW,CAAC,GAAG,CAAC,CAAC;CAChC,CAAC;AAEF;;;;;;;;;;;;;;;;GAgBG;AACH,wBAAgB,oBAAoB,CAAC,CAAC,GAAG,GAAG,EAAE,EAC5C,QAAQ,EACR,WAAW,EACX,qBAAqB,EACrB,GAAG,KAAK,EACT,EAAE,yBAAyB,CAAC,CAAC,CAAC,2CAmC9B"}
|
|
@@ -1,12 +1,24 @@
|
|
|
1
1
|
import type { ScrollManagerConfig } from '../types';
|
|
2
|
+
import type { ResolveRefreshControlOptions } from '../hooks/refreshControl';
|
|
2
3
|
import type { ReactNode } from 'react';
|
|
4
|
+
import type { AnimatedRef } from 'react-native-reanimated';
|
|
3
5
|
type ScrollManagerRenderChildren = (scrollableProps: ScrollManagerConfig['scrollableProps'], options: ScrollManagerConfig['headerMotionContext']) => ReactNode;
|
|
4
|
-
export interface HeaderMotionScrollManagerProps {
|
|
6
|
+
export interface HeaderMotionScrollManagerProps extends Omit<ResolveRefreshControlOptions, 'progressViewOffset'> {
|
|
5
7
|
/**
|
|
6
8
|
* Optional unique identifier for this scroll view.
|
|
7
9
|
* Use this when you have multiple scroll views (e.g., in tabs) to track them separately.
|
|
8
10
|
*/
|
|
9
11
|
scrollId?: string;
|
|
12
|
+
/**
|
|
13
|
+
* Optional animated ref to use for the scroll view.
|
|
14
|
+
* When provided, the scroll manager will use this ref instead of creating its own.
|
|
15
|
+
*/
|
|
16
|
+
animatedRef?: AnimatedRef<any>;
|
|
17
|
+
/**
|
|
18
|
+
* Optional refresh progress offset override.
|
|
19
|
+
* When provided, it takes precedence over the automatic offset based on header height.
|
|
20
|
+
*/
|
|
21
|
+
progressViewOffset?: ResolveRefreshControlOptions['progressViewOffset'];
|
|
10
22
|
/**
|
|
11
23
|
* Render function that receives scroll props and header context.
|
|
12
24
|
* Use this to create custom scroll implementations that integrate with HeaderMotion.
|
|
@@ -35,6 +47,6 @@ export interface HeaderMotionScrollManagerProps {
|
|
|
35
47
|
* </HeaderMotion>
|
|
36
48
|
* ```
|
|
37
49
|
*/
|
|
38
|
-
export declare function HeaderMotionScrollManager({ children, scrollId, }: HeaderMotionScrollManagerProps): ReactNode;
|
|
50
|
+
export declare function HeaderMotionScrollManager({ children, scrollId, animatedRef, refreshControl, refreshing, onRefresh, progressViewOffset, }: HeaderMotionScrollManagerProps): ReactNode;
|
|
39
51
|
export {};
|
|
40
52
|
//# sourceMappingURL=ScrollManager.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ScrollManager.d.ts","sourceRoot":"","sources":["../../../../src/components/ScrollManager.tsx"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,UAAU,CAAC;AACpD,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,OAAO,CAAC;
|
|
1
|
+
{"version":3,"file":"ScrollManager.d.ts","sourceRoot":"","sources":["../../../../src/components/ScrollManager.tsx"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,UAAU,CAAC;AACpD,OAAO,KAAK,EAAE,4BAA4B,EAAE,MAAM,yBAAyB,CAAC;AAC5E,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,OAAO,CAAC;AACvC,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,yBAAyB,CAAC;AAE3D,KAAK,2BAA2B,GAAG,CACjC,eAAe,EAAE,mBAAmB,CAAC,iBAAiB,CAAC,EACvD,OAAO,EAAE,mBAAmB,CAAC,qBAAqB,CAAC,KAChD,SAAS,CAAC;AAEf,MAAM,WAAW,8BACf,SAAQ,IAAI,CAAC,4BAA4B,EAAE,oBAAoB,CAAC;IAChE;;;OAGG;IACH,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB;;;OAGG;IACH,WAAW,CAAC,EAAE,WAAW,CAAC,GAAG,CAAC,CAAC;IAC/B;;;OAGG;IACH,kBAAkB,CAAC,EAAE,4BAA4B,CAAC,oBAAoB,CAAC,CAAC;IACxE;;;OAGG;IACH,QAAQ,EAAE,2BAA2B,CAAC;CACvC;AAED;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,wBAAgB,yBAAyB,CAAC,EACxC,QAAQ,EACR,QAAQ,EACR,WAAW,EACX,cAAc,EACd,UAAU,EACV,SAAS,EACT,kBAAkB,GACnB,EAAE,8BAA8B,aAgBhC"}
|
|
@@ -1,10 +1,15 @@
|
|
|
1
|
-
import { type AnimatedScrollViewProps } from 'react-native-reanimated';
|
|
1
|
+
import { type AnimatedRef, type AnimatedScrollViewProps } from 'react-native-reanimated';
|
|
2
2
|
export type HeaderMotionScrollViewProps = AnimatedScrollViewProps & {
|
|
3
3
|
/**
|
|
4
4
|
* Optional unique identifier for this scroll view.
|
|
5
5
|
* Use this when you have multiple scroll views (e.g. in tabs) to track them separately.
|
|
6
6
|
*/
|
|
7
7
|
scrollId?: string;
|
|
8
|
+
/**
|
|
9
|
+
* Optional animated ref to use for the scroll view.
|
|
10
|
+
* When provided, the scroll manager will use this ref instead of creating its own.
|
|
11
|
+
*/
|
|
12
|
+
animatedRef?: AnimatedRef<any>;
|
|
8
13
|
};
|
|
9
14
|
/**
|
|
10
15
|
* Animated ScrollView component that integrates with HeaderMotion.
|
|
@@ -20,5 +25,5 @@ export type HeaderMotionScrollViewProps = AnimatedScrollViewProps & {
|
|
|
20
25
|
* </HeaderMotion>
|
|
21
26
|
* ```
|
|
22
27
|
*/
|
|
23
|
-
export declare function HeaderMotionScrollView({ scrollId, children, contentContainerStyle, ...props }: HeaderMotionScrollViewProps): import("react/jsx-runtime").JSX.Element;
|
|
28
|
+
export declare function HeaderMotionScrollView({ scrollId, animatedRef, children, contentContainerStyle, refreshControl, ...props }: HeaderMotionScrollViewProps): import("react/jsx-runtime").JSX.Element;
|
|
24
29
|
//# sourceMappingURL=ScrollView.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ScrollView.d.ts","sourceRoot":"","sources":["../../../../src/components/ScrollView.tsx"],"names":[],"mappings":"AAAA,OAAiB,EACf,KAAK,uBAAuB,EAC7B,MAAM,yBAAyB,CAAC;AAGjC,MAAM,MAAM,2BAA2B,GAAG,uBAAuB,GAAG;IAClE;;;OAGG;IACH,QAAQ,CAAC,EAAE,MAAM,CAAC;
|
|
1
|
+
{"version":3,"file":"ScrollView.d.ts","sourceRoot":"","sources":["../../../../src/components/ScrollView.tsx"],"names":[],"mappings":"AAAA,OAAiB,EACf,KAAK,WAAW,EAChB,KAAK,uBAAuB,EAC7B,MAAM,yBAAyB,CAAC;AAGjC,MAAM,MAAM,2BAA2B,GAAG,uBAAuB,GAAG;IAClE;;;OAGG;IACH,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB;;;OAGG;IACH,WAAW,CAAC,EAAE,WAAW,CAAC,GAAG,CAAC,CAAC;CAChC,CAAC;AAEF;;;;;;;;;;;;;GAaG;AACH,wBAAgB,sBAAsB,CAAC,EACrC,QAAQ,EACR,WAAW,EACX,QAAQ,EACR,qBAAqB,EACrB,cAAc,EACd,GAAG,KAAK,EACT,EAAE,2BAA2B,2CAgC7B"}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { type ReactElement } from 'react';
|
|
2
|
+
import { type RefreshControlProps } from 'react-native';
|
|
3
|
+
import type { SharedValue } from 'react-native-reanimated';
|
|
4
|
+
type MaybeShared<T> = T | SharedValue<T | undefined>;
|
|
5
|
+
export interface ResolveRefreshControlOptions {
|
|
6
|
+
refreshControl?: MaybeShared<ReactElement<RefreshControlProps>>;
|
|
7
|
+
refreshing?: MaybeShared<boolean>;
|
|
8
|
+
onRefresh?: MaybeShared<() => void>;
|
|
9
|
+
progressViewOffset: MaybeShared<number>;
|
|
10
|
+
}
|
|
11
|
+
export declare function resolveRefreshControl({ refreshControl, refreshing, onRefresh, progressViewOffset, }: ResolveRefreshControlOptions): ReactElement<RefreshControlProps> | undefined;
|
|
12
|
+
export {};
|
|
13
|
+
//# sourceMappingURL=refreshControl.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"refreshControl.d.ts","sourceRoot":"","sources":["../../../../src/hooks/refreshControl.ts"],"names":[],"mappings":"AAAA,OAAO,EAIL,KAAK,YAAY,EAClB,MAAM,OAAO,CAAC;AACf,OAAO,EAAkB,KAAK,mBAAmB,EAAE,MAAM,cAAc,CAAC;AACxE,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,yBAAyB,CAAC;AAE3D,KAAK,WAAW,CAAC,CAAC,IAAI,CAAC,GAAG,WAAW,CAAC,CAAC,GAAG,SAAS,CAAC,CAAC;AAErD,MAAM,WAAW,4BAA4B;IAC3C,cAAc,CAAC,EAAE,WAAW,CAAC,YAAY,CAAC,mBAAmB,CAAC,CAAC,CAAC;IAChE,UAAU,CAAC,EAAE,WAAW,CAAC,OAAO,CAAC,CAAC;IAClC,SAAS,CAAC,EAAE,WAAW,CAAC,MAAM,IAAI,CAAC,CAAC;IACpC,kBAAkB,EAAE,WAAW,CAAC,MAAM,CAAC,CAAC;CACzC;AAeD,wBAAgB,qBAAqB,CAAC,EACpC,cAAc,EACd,UAAU,EACV,SAAS,EACT,kBAAkB,GACnB,EAAE,4BAA4B,GAC3B,YAAY,CAAC,mBAAmB,CAAC,GACjC,SAAS,CAgBZ"}
|
|
@@ -1,4 +1,6 @@
|
|
|
1
|
+
import { type AnimatedRef } from 'react-native-reanimated';
|
|
1
2
|
import type { ScrollManagerConfig } from '../types';
|
|
3
|
+
import { type ResolveRefreshControlOptions } from './refreshControl';
|
|
2
4
|
/**
|
|
3
5
|
* Hook that manages scroll tracking and synchronization for header animations.
|
|
4
6
|
* Returns props to apply to scrollable components and additional values that help with adjusting styling of the scrollables to header's dimensions.
|
|
@@ -12,6 +14,9 @@ import type { ScrollManagerConfig } from '../types';
|
|
|
12
14
|
*
|
|
13
15
|
* @param scrollId - Optional unique identifier for the related scrollable.
|
|
14
16
|
* Use when you have multiple scrollables (e.g., in tabs).
|
|
17
|
+
* @param options - Optional configuration object.
|
|
18
|
+
* @param options.animatedRef - Optional animated ref to use instead of creating one internally.
|
|
19
|
+
* Useful when you need access to the scroll view ref from outside.
|
|
15
20
|
* @returns Configuration object containing:
|
|
16
21
|
* - `scrollableProps`: Props to apply to scrollable component (onScroll, scrollEventThrottle, ref)
|
|
17
22
|
* - `headerMotionContext`: Header context values (originalHeaderHeight, minHeightContentContainerStyle)
|
|
@@ -33,5 +38,17 @@ import type { ScrollManagerConfig } from '../types';
|
|
|
33
38
|
* }
|
|
34
39
|
* ```
|
|
35
40
|
*/
|
|
36
|
-
export
|
|
41
|
+
export interface UseScrollManagerOptions extends Omit<ResolveRefreshControlOptions, 'progressViewOffset'> {
|
|
42
|
+
/**
|
|
43
|
+
* Optional animated ref to use instead of creating one internally.
|
|
44
|
+
* Useful when you need access to the scroll view ref from outside.
|
|
45
|
+
*/
|
|
46
|
+
animatedRef?: AnimatedRef<any>;
|
|
47
|
+
/**
|
|
48
|
+
* Optional refresh progress offset override.
|
|
49
|
+
* When provided, it takes precedence over the automatic offset based on header height.
|
|
50
|
+
*/
|
|
51
|
+
progressViewOffset?: ResolveRefreshControlOptions['progressViewOffset'];
|
|
52
|
+
}
|
|
53
|
+
export declare function useScrollManager(scrollId?: string, options?: UseScrollManagerOptions): ScrollManagerConfig;
|
|
37
54
|
//# sourceMappingURL=useScrollManager.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"useScrollManager.d.ts","sourceRoot":"","sources":["../../../../src/hooks/useScrollManager.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"useScrollManager.d.ts","sourceRoot":"","sources":["../../../../src/hooks/useScrollManager.ts"],"names":[],"mappings":"AACA,OAAO,EAOL,KAAK,WAAW,EAEjB,MAAM,yBAAyB,CAAC;AAGjC,OAAO,KAAK,EAAE,mBAAmB,EAAgB,MAAM,UAAU,CAAC;AAElE,OAAO,EAEL,KAAK,4BAA4B,EAClC,MAAM,kBAAkB,CAAC;AAE1B;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAoCG;AACH,MAAM,WAAW,uBACf,SAAQ,IAAI,CAAC,4BAA4B,EAAE,oBAAoB,CAAC;IAChE;;;OAGG;IACH,WAAW,CAAC,EAAE,WAAW,CAAC,GAAG,CAAC,CAAC;IAC/B;;;OAGG;IACH,kBAAkB,CAAC,EAAE,4BAA4B,CAAC,oBAAoB,CAAC,CAAC;CACzE;AAED,wBAAgB,gBAAgB,CAC9B,QAAQ,CAAC,EAAE,MAAM,EACjB,OAAO,CAAC,EAAE,uBAAuB,GAChC,mBAAmB,CAsJrB"}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
import type {
|
|
1
|
+
import type { ReactElement } from 'react';
|
|
2
|
+
import type { LayoutChangeEvent, RefreshControlProps, ScrollViewProps } from 'react-native';
|
|
2
3
|
import type { AnimatedRef, SharedValue } from 'react-native-reanimated';
|
|
3
4
|
import { DEFAULT_SCROLL_ID } from './utils/defaults';
|
|
4
5
|
export type Progress = SharedValue<number>;
|
|
@@ -36,6 +37,7 @@ export interface ScrollManagerHeaderMotionContext {
|
|
|
36
37
|
}
|
|
37
38
|
export interface ScrollManagerConfig {
|
|
38
39
|
scrollableProps: Required<Pick<ScrollViewProps, 'onScroll' | 'scrollEventThrottle'>> & {
|
|
40
|
+
refreshControl?: ReactElement<RefreshControlProps>;
|
|
39
41
|
ref: AnimatedRef<any>;
|
|
40
42
|
};
|
|
41
43
|
headerMotionContext: ScrollManagerHeaderMotionContext;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../../src/types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../../src/types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,OAAO,CAAC;AAC1C,OAAO,KAAK,EACV,iBAAiB,EACjB,mBAAmB,EACnB,eAAe,EAChB,MAAM,cAAc,CAAC;AACtB,OAAO,KAAK,EAAE,WAAW,EAAE,WAAW,EAAE,MAAM,yBAAyB,CAAC;AACxE,OAAO,EAAE,iBAAiB,EAAE,MAAM,kBAAkB,CAAC;AAErD,MAAM,MAAM,QAAQ,GAAG,WAAW,CAAC,MAAM,CAAC,CAAC;AAE3C,MAAM,MAAM,iBAAiB,GACzB,MAAM,GACN,CAAC,CAAC,mBAAmB,EAAE,MAAM,KAAK,MAAM,CAAC,CAAC;AAC9C,MAAM,MAAM,qBAAqB,GAAG,CAAC,CAAC,EAAE,iBAAiB,KAAK,MAAM,CAAC;AACrE,MAAM,MAAM,2BAA2B,GAAG,CAAC,CAAC,EAAE,iBAAiB,KAAK,IAAI,CAAC;AAEzE,MAAM,MAAM,oBAAoB,CAAC,CAAC,SAAS,MAAM,GAAG,MAAM,IAAI;IAC5D,KAAK,EAAE,CAAC,CAAC;IACT,EAAE,EAAE,WAAW,CAAC,CAAC,CAAC,CAAC;CACpB,CAAC;AACF,MAAM,MAAM,iBAAiB,CAAC,CAAC,SAAS,MAAM,IAAI,CAAC,KAAK,EAAE,CAAC,KAAK,IAAI,CAAC;AAErE,MAAM,WAAW,WAAW;IAC1B,GAAG,EAAE,MAAM,CAAC;IACZ,OAAO,EAAE,MAAM,CAAC;CACjB;AACD,MAAM,MAAM,YAAY,GAAG,MAAM,CAAC,MAAM,EAAE,WAAW,CAAC,GAAG;KACtD,GAAG,IAAI,OAAO,iBAAiB,CAAC,CAAC,EAAE,WAAW;CAChD,CAAC;AAEF,MAAM,MAAM,0BAA0B,CACpC,CAAC,SAAS,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,IACzD,CAAC,GAAG,cAAc,CAAC;AAEvB,MAAM,MAAM,+BAA+B,CACzC,GAAG,SAAS,MAAM,GAAG,MAAM,EAC3B,CAAC,SAAS,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,IACzD,0BAA0B,CAAC,CAAC,CAAC,GAAG;IAClC,WAAW,EAAE,CAAC,MAAM,EAAE,GAAG,KAAK,IAAI,CAAC;IACnC,SAAS,EAAE,GAAG,CAAC;CAChB,CAAC;AAEF,MAAM,WAAW,cAAc;IAC7B,QAAQ,EAAE,QAAQ,CAAC;IACnB,iBAAiB,EAAE,MAAM,CAAC;IAC1B,kBAAkB,EAAE,2BAA2B,CAAC;IAChD,cAAc,EAAE,2BAA2B,CAAC;CAC7C;AAED,MAAM,WAAW,gCAAgC;IAC/C,oBAAoB,EAAE,MAAM,CAAC;IAC7B,8BAA8B,EAC1B,EAAE,GACF;QACE,SAAS,EAAE,MAAM,CAAC;KACnB,CAAC;CACP;AAED,MAAM,WAAW,mBAAmB;IAClC,eAAe,EAAE,QAAQ,CACvB,IAAI,CAAC,eAAe,EAAE,UAAU,GAAG,qBAAqB,CAAC,CAC1D,GAAG;QACF,cAAc,CAAC,EAAE,YAAY,CAAC,mBAAmB,CAAC,CAAC;QACnD,GAAG,EAAE,WAAW,CAAC,GAAG,CAAC,CAAC;KACvB,CAAC;IACF,mBAAmB,EAAE,gCAAgC,CAAC;CACvD"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "react-native-header-motion",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.3.0",
|
|
4
4
|
"description": "Smooth, animated collapsible headers with scroll-based motion control in React Native",
|
|
5
5
|
"main": "./lib/module/index.js",
|
|
6
6
|
"types": "./lib/typescript/src/index.d.ts",
|
|
@@ -1,19 +1,22 @@
|
|
|
1
1
|
import { forwardRef, type ComponentProps, type ComponentRef } from 'react';
|
|
2
|
-
import Animated from 'react-native-reanimated';
|
|
2
|
+
import Animated, { type AnimatedRef } from 'react-native-reanimated';
|
|
3
3
|
import { HeaderMotionScrollManager } from './ScrollManager';
|
|
4
4
|
|
|
5
5
|
import type { ScrollViewProps } from 'react-native';
|
|
6
6
|
|
|
7
|
-
type
|
|
7
|
+
export type HeaderMotionFlatListProps<T = any> = ComponentProps<
|
|
8
8
|
typeof Animated.FlatList<T>
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
export type HeaderMotionFlatListProps<T = any> = AnimatedFlatListProps<T> & {
|
|
9
|
+
> & {
|
|
12
10
|
/**
|
|
13
11
|
* Optional unique identifier for this scroll view.
|
|
14
12
|
* Use this when you have multiple scroll views (e.g. in tabs) to track them separately.
|
|
15
13
|
*/
|
|
16
14
|
scrollId?: string;
|
|
15
|
+
/**
|
|
16
|
+
* Optional animated ref to use for the flat list.
|
|
17
|
+
* When provided, the scroll manager will use this ref instead of creating its own.
|
|
18
|
+
*/
|
|
19
|
+
animatedRef?: AnimatedRef<any>;
|
|
17
20
|
};
|
|
18
21
|
|
|
19
22
|
/**
|
|
@@ -35,22 +38,33 @@ export type HeaderMotionFlatListProps<T = any> = AnimatedFlatListProps<T> & {
|
|
|
35
38
|
*/
|
|
36
39
|
export function HeaderMotionFlatList<T = any>({
|
|
37
40
|
scrollId,
|
|
41
|
+
animatedRef,
|
|
38
42
|
contentContainerStyle,
|
|
39
43
|
...props
|
|
40
44
|
}: HeaderMotionFlatListProps<T>) {
|
|
41
45
|
return (
|
|
42
|
-
<HeaderMotionScrollManager
|
|
46
|
+
<HeaderMotionScrollManager
|
|
47
|
+
scrollId={scrollId}
|
|
48
|
+
animatedRef={animatedRef}
|
|
49
|
+
refreshControl={props.refreshControl}
|
|
50
|
+
refreshing={props.refreshing}
|
|
51
|
+
onRefresh={props.onRefresh}
|
|
52
|
+
progressViewOffset={props.progressViewOffset}
|
|
53
|
+
>
|
|
43
54
|
{(
|
|
44
|
-
{ onScroll, ...scrollViewProps },
|
|
55
|
+
{ onScroll, refreshControl: managedRefreshControl, ...scrollViewProps },
|
|
45
56
|
{ originalHeaderHeight, minHeightContentContainerStyle }
|
|
46
57
|
) => (
|
|
47
58
|
<Animated.FlatList
|
|
48
59
|
{...scrollViewProps}
|
|
49
60
|
{...props}
|
|
50
61
|
onScroll={onScroll}
|
|
51
|
-
|
|
62
|
+
{...(managedRefreshControl && {
|
|
63
|
+
refreshControl: managedRefreshControl,
|
|
64
|
+
})}
|
|
65
|
+
renderScrollComponent={(scrollComponentProps) => (
|
|
52
66
|
<AnimatedScrollContainer
|
|
53
|
-
{...
|
|
67
|
+
{...scrollComponentProps}
|
|
54
68
|
contentContainerStyle={[
|
|
55
69
|
minHeightContentContainerStyle,
|
|
56
70
|
{ paddingTop: originalHeaderHeight },
|
|
@@ -1,18 +1,31 @@
|
|
|
1
1
|
import { useScrollManager } from '../hooks';
|
|
2
2
|
import type { ScrollManagerConfig } from '../types';
|
|
3
|
+
import type { ResolveRefreshControlOptions } from '../hooks/refreshControl';
|
|
3
4
|
import type { ReactNode } from 'react';
|
|
5
|
+
import type { AnimatedRef } from 'react-native-reanimated';
|
|
4
6
|
|
|
5
7
|
type ScrollManagerRenderChildren = (
|
|
6
8
|
scrollableProps: ScrollManagerConfig['scrollableProps'],
|
|
7
9
|
options: ScrollManagerConfig['headerMotionContext']
|
|
8
10
|
) => ReactNode;
|
|
9
11
|
|
|
10
|
-
export interface HeaderMotionScrollManagerProps
|
|
12
|
+
export interface HeaderMotionScrollManagerProps
|
|
13
|
+
extends Omit<ResolveRefreshControlOptions, 'progressViewOffset'> {
|
|
11
14
|
/**
|
|
12
15
|
* Optional unique identifier for this scroll view.
|
|
13
16
|
* Use this when you have multiple scroll views (e.g., in tabs) to track them separately.
|
|
14
17
|
*/
|
|
15
18
|
scrollId?: string;
|
|
19
|
+
/**
|
|
20
|
+
* Optional animated ref to use for the scroll view.
|
|
21
|
+
* When provided, the scroll manager will use this ref instead of creating its own.
|
|
22
|
+
*/
|
|
23
|
+
animatedRef?: AnimatedRef<any>;
|
|
24
|
+
/**
|
|
25
|
+
* Optional refresh progress offset override.
|
|
26
|
+
* When provided, it takes precedence over the automatic offset based on header height.
|
|
27
|
+
*/
|
|
28
|
+
progressViewOffset?: ResolveRefreshControlOptions['progressViewOffset'];
|
|
16
29
|
/**
|
|
17
30
|
* Render function that receives scroll props and header context.
|
|
18
31
|
* Use this to create custom scroll implementations that integrate with HeaderMotion.
|
|
@@ -45,6 +58,11 @@ export interface HeaderMotionScrollManagerProps {
|
|
|
45
58
|
export function HeaderMotionScrollManager({
|
|
46
59
|
children,
|
|
47
60
|
scrollId,
|
|
61
|
+
animatedRef,
|
|
62
|
+
refreshControl,
|
|
63
|
+
refreshing,
|
|
64
|
+
onRefresh,
|
|
65
|
+
progressViewOffset,
|
|
48
66
|
}: HeaderMotionScrollManagerProps) {
|
|
49
67
|
if (typeof children !== 'function') {
|
|
50
68
|
throw new Error(
|
|
@@ -52,7 +70,13 @@ export function HeaderMotionScrollManager({
|
|
|
52
70
|
);
|
|
53
71
|
}
|
|
54
72
|
|
|
55
|
-
const { scrollableProps, headerMotionContext } = useScrollManager(scrollId
|
|
73
|
+
const { scrollableProps, headerMotionContext } = useScrollManager(scrollId, {
|
|
74
|
+
animatedRef,
|
|
75
|
+
refreshControl,
|
|
76
|
+
refreshing,
|
|
77
|
+
onRefresh,
|
|
78
|
+
progressViewOffset,
|
|
79
|
+
});
|
|
56
80
|
|
|
57
81
|
return children(scrollableProps, headerMotionContext);
|
|
58
82
|
}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import Animated, {
|
|
2
|
+
type AnimatedRef,
|
|
2
3
|
type AnimatedScrollViewProps,
|
|
3
4
|
} from 'react-native-reanimated';
|
|
4
5
|
import { HeaderMotionScrollManager } from './ScrollManager';
|
|
@@ -9,6 +10,11 @@ export type HeaderMotionScrollViewProps = AnimatedScrollViewProps & {
|
|
|
9
10
|
* Use this when you have multiple scroll views (e.g. in tabs) to track them separately.
|
|
10
11
|
*/
|
|
11
12
|
scrollId?: string;
|
|
13
|
+
/**
|
|
14
|
+
* Optional animated ref to use for the scroll view.
|
|
15
|
+
* When provided, the scroll manager will use this ref instead of creating its own.
|
|
16
|
+
*/
|
|
17
|
+
animatedRef?: AnimatedRef<any>;
|
|
12
18
|
};
|
|
13
19
|
|
|
14
20
|
/**
|
|
@@ -27,20 +33,29 @@ export type HeaderMotionScrollViewProps = AnimatedScrollViewProps & {
|
|
|
27
33
|
*/
|
|
28
34
|
export function HeaderMotionScrollView({
|
|
29
35
|
scrollId,
|
|
36
|
+
animatedRef,
|
|
30
37
|
children,
|
|
31
38
|
contentContainerStyle,
|
|
39
|
+
refreshControl,
|
|
32
40
|
...props
|
|
33
41
|
}: HeaderMotionScrollViewProps) {
|
|
34
42
|
return (
|
|
35
|
-
<HeaderMotionScrollManager
|
|
43
|
+
<HeaderMotionScrollManager
|
|
44
|
+
scrollId={scrollId}
|
|
45
|
+
animatedRef={animatedRef}
|
|
46
|
+
refreshControl={refreshControl}
|
|
47
|
+
>
|
|
36
48
|
{(
|
|
37
|
-
{ onScroll, ...scrollViewProps },
|
|
49
|
+
{ onScroll, refreshControl: managedRefreshControl, ...scrollViewProps },
|
|
38
50
|
{ originalHeaderHeight, minHeightContentContainerStyle }
|
|
39
51
|
) => (
|
|
40
52
|
<Animated.ScrollView
|
|
41
53
|
{...scrollViewProps}
|
|
42
54
|
{...props}
|
|
43
55
|
onScroll={onScroll}
|
|
56
|
+
{...(managedRefreshControl && {
|
|
57
|
+
refreshControl: managedRefreshControl,
|
|
58
|
+
})}
|
|
44
59
|
>
|
|
45
60
|
<Animated.View
|
|
46
61
|
style={[
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
import {
|
|
2
|
+
cloneElement,
|
|
3
|
+
createElement,
|
|
4
|
+
isValidElement,
|
|
5
|
+
type ReactElement,
|
|
6
|
+
} from 'react';
|
|
7
|
+
import { RefreshControl, type RefreshControlProps } from 'react-native';
|
|
8
|
+
import type { SharedValue } from 'react-native-reanimated';
|
|
9
|
+
|
|
10
|
+
type MaybeShared<T> = T | SharedValue<T | undefined>;
|
|
11
|
+
|
|
12
|
+
export interface ResolveRefreshControlOptions {
|
|
13
|
+
refreshControl?: MaybeShared<ReactElement<RefreshControlProps>>;
|
|
14
|
+
refreshing?: MaybeShared<boolean>;
|
|
15
|
+
onRefresh?: MaybeShared<() => void>;
|
|
16
|
+
progressViewOffset: MaybeShared<number>;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
function injectProgressViewOffset(
|
|
20
|
+
refreshControl: ReactElement<RefreshControlProps>,
|
|
21
|
+
progressViewOffset: MaybeShared<number>
|
|
22
|
+
) {
|
|
23
|
+
if (refreshControl.props.progressViewOffset !== undefined) {
|
|
24
|
+
return refreshControl;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
return cloneElement(refreshControl, {
|
|
28
|
+
progressViewOffset: progressViewOffset as number,
|
|
29
|
+
});
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
export function resolveRefreshControl({
|
|
33
|
+
refreshControl,
|
|
34
|
+
refreshing,
|
|
35
|
+
onRefresh,
|
|
36
|
+
progressViewOffset,
|
|
37
|
+
}: ResolveRefreshControlOptions):
|
|
38
|
+
| ReactElement<RefreshControlProps>
|
|
39
|
+
| undefined {
|
|
40
|
+
if (refreshControl) {
|
|
41
|
+
return isValidElement<RefreshControlProps>(refreshControl)
|
|
42
|
+
? injectProgressViewOffset(refreshControl, progressViewOffset)
|
|
43
|
+
: undefined;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
if (!onRefresh) {
|
|
47
|
+
return undefined;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
return createElement(RefreshControl, {
|
|
51
|
+
refreshing: (refreshing as boolean) ?? false,
|
|
52
|
+
onRefresh: onRefresh as () => void,
|
|
53
|
+
progressViewOffset: progressViewOffset as number,
|
|
54
|
+
}) as unknown as ReactElement<RefreshControlProps>;
|
|
55
|
+
}
|
|
@@ -6,12 +6,17 @@ import {
|
|
|
6
6
|
useAnimatedRef,
|
|
7
7
|
useAnimatedScrollHandler,
|
|
8
8
|
useAnimatedStyle,
|
|
9
|
+
type AnimatedRef,
|
|
9
10
|
type ScrollHandler,
|
|
10
11
|
} from 'react-native-reanimated';
|
|
11
12
|
import { RuntimeKind, scheduleOnUI } from 'react-native-worklets';
|
|
12
13
|
import { HeaderMotionContext } from '../context';
|
|
13
14
|
import type { ScrollManagerConfig, ScrollValues } from '../types';
|
|
14
15
|
import { DEFAULT_SCROLL_ID, getInitialScrollValue } from '../utils';
|
|
16
|
+
import {
|
|
17
|
+
resolveRefreshControl,
|
|
18
|
+
type ResolveRefreshControlOptions,
|
|
19
|
+
} from './refreshControl';
|
|
15
20
|
|
|
16
21
|
/**
|
|
17
22
|
* Hook that manages scroll tracking and synchronization for header animations.
|
|
@@ -26,6 +31,9 @@ import { DEFAULT_SCROLL_ID, getInitialScrollValue } from '../utils';
|
|
|
26
31
|
*
|
|
27
32
|
* @param scrollId - Optional unique identifier for the related scrollable.
|
|
28
33
|
* Use when you have multiple scrollables (e.g., in tabs).
|
|
34
|
+
* @param options - Optional configuration object.
|
|
35
|
+
* @param options.animatedRef - Optional animated ref to use instead of creating one internally.
|
|
36
|
+
* Useful when you need access to the scroll view ref from outside.
|
|
29
37
|
* @returns Configuration object containing:
|
|
30
38
|
* - `scrollableProps`: Props to apply to scrollable component (onScroll, scrollEventThrottle, ref)
|
|
31
39
|
* - `headerMotionContext`: Header context values (originalHeaderHeight, minHeightContentContainerStyle)
|
|
@@ -47,7 +55,24 @@ import { DEFAULT_SCROLL_ID, getInitialScrollValue } from '../utils';
|
|
|
47
55
|
* }
|
|
48
56
|
* ```
|
|
49
57
|
*/
|
|
50
|
-
export
|
|
58
|
+
export interface UseScrollManagerOptions
|
|
59
|
+
extends Omit<ResolveRefreshControlOptions, 'progressViewOffset'> {
|
|
60
|
+
/**
|
|
61
|
+
* Optional animated ref to use instead of creating one internally.
|
|
62
|
+
* Useful when you need access to the scroll view ref from outside.
|
|
63
|
+
*/
|
|
64
|
+
animatedRef?: AnimatedRef<any>;
|
|
65
|
+
/**
|
|
66
|
+
* Optional refresh progress offset override.
|
|
67
|
+
* When provided, it takes precedence over the automatic offset based on header height.
|
|
68
|
+
*/
|
|
69
|
+
progressViewOffset?: ResolveRefreshControlOptions['progressViewOffset'];
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
export function useScrollManager(
|
|
73
|
+
scrollId?: string,
|
|
74
|
+
options?: UseScrollManagerOptions
|
|
75
|
+
): ScrollManagerConfig {
|
|
51
76
|
const ctxValue = useContext(HeaderMotionContext);
|
|
52
77
|
if (!ctxValue) {
|
|
53
78
|
throw new Error(
|
|
@@ -64,7 +89,13 @@ export function useScrollManager(scrollId?: string): ScrollManagerConfig {
|
|
|
64
89
|
} = ctxValue;
|
|
65
90
|
const id = scrollId ?? DEFAULT_SCROLL_ID;
|
|
66
91
|
|
|
67
|
-
const
|
|
92
|
+
const localRef = useAnimatedRef<any>(); // TODO: better typing
|
|
93
|
+
const animatedRef = options?.animatedRef ?? localRef;
|
|
94
|
+
const refreshControl = options?.refreshControl;
|
|
95
|
+
const refreshing = options?.refreshing;
|
|
96
|
+
const onRefresh = options?.onRefresh;
|
|
97
|
+
const progressViewOffset =
|
|
98
|
+
options?.progressViewOffset ?? originalHeaderHeight;
|
|
68
99
|
|
|
69
100
|
useEffect(() => {
|
|
70
101
|
return () => {
|
|
@@ -172,10 +203,18 @@ export function useScrollManager(scrollId?: string): ScrollManagerConfig {
|
|
|
172
203
|
};
|
|
173
204
|
});
|
|
174
205
|
|
|
206
|
+
const resolvedRefreshControl = resolveRefreshControl({
|
|
207
|
+
refreshControl,
|
|
208
|
+
refreshing,
|
|
209
|
+
onRefresh,
|
|
210
|
+
progressViewOffset,
|
|
211
|
+
});
|
|
212
|
+
|
|
175
213
|
const scrollableProps = {
|
|
176
214
|
onScroll,
|
|
177
215
|
scrollEventThrottle: 16,
|
|
178
216
|
ref: animatedRef,
|
|
217
|
+
refreshControl: resolvedRefreshControl,
|
|
179
218
|
};
|
|
180
219
|
const headerMotionContext = {
|
|
181
220
|
originalHeaderHeight,
|
package/src/types.ts
CHANGED
|
@@ -1,4 +1,9 @@
|
|
|
1
|
-
import type {
|
|
1
|
+
import type { ReactElement } from 'react';
|
|
2
|
+
import type {
|
|
3
|
+
LayoutChangeEvent,
|
|
4
|
+
RefreshControlProps,
|
|
5
|
+
ScrollViewProps,
|
|
6
|
+
} from 'react-native';
|
|
2
7
|
import type { AnimatedRef, SharedValue } from 'react-native-reanimated';
|
|
3
8
|
import { DEFAULT_SCROLL_ID } from './utils/defaults';
|
|
4
9
|
|
|
@@ -56,6 +61,7 @@ export interface ScrollManagerConfig {
|
|
|
56
61
|
scrollableProps: Required<
|
|
57
62
|
Pick<ScrollViewProps, 'onScroll' | 'scrollEventThrottle'>
|
|
58
63
|
> & {
|
|
64
|
+
refreshControl?: ReactElement<RefreshControlProps>;
|
|
59
65
|
ref: AnimatedRef<any>; // TODO: better typing
|
|
60
66
|
};
|
|
61
67
|
headerMotionContext: ScrollManagerHeaderMotionContext;
|