react-native-header-motion 0.2.0 → 0.4.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 +22 -4
- package/lib/module/components/FlatList.js.map +1 -1
- package/lib/module/components/ScrollManager.js +20 -2
- package/lib/module/components/ScrollManager.js.map +1 -1
- package/lib/module/components/ScrollView.js +18 -2
- 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/useConsumerScrollHandlers.js +86 -0
- package/lib/module/hooks/useConsumerScrollHandlers.js.map +1 -0
- package/lib/module/hooks/useScrollManager.js +37 -4
- package/lib/module/hooks/useScrollManager.js.map +1 -1
- package/lib/typescript/src/components/FlatList.d.ts +2 -4
- package/lib/typescript/src/components/FlatList.d.ts.map +1 -1
- package/lib/typescript/src/components/ScrollManager.d.ts +9 -2
- package/lib/typescript/src/components/ScrollManager.d.ts.map +1 -1
- package/lib/typescript/src/components/ScrollView.d.ts +1 -1
- 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/useConsumerScrollHandlers.d.ts +64 -0
- package/lib/typescript/src/hooks/useConsumerScrollHandlers.d.ts.map +1 -0
- package/lib/typescript/src/hooks/useScrollManager.d.ts +8 -1
- package/lib/typescript/src/hooks/useScrollManager.d.ts.map +1 -1
- package/lib/typescript/src/types.d.ts +6 -1
- package/lib/typescript/src/types.d.ts.map +1 -1
- package/package.json +1 -1
- package/src/components/FlatList.tsx +31 -9
- package/src/components/ScrollManager.tsx +28 -1
- package/src/components/ScrollView.tsx +25 -3
- package/src/hooks/refreshControl.ts +55 -0
- package/src/hooks/useConsumerScrollHandlers.ts +148 -0
- package/src/hooks/useScrollManager.ts +52 -4
- package/src/types.ts +11 -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)
|
|
@@ -25,13 +25,28 @@ export function HeaderMotionFlatList({
|
|
|
25
25
|
scrollId,
|
|
26
26
|
animatedRef,
|
|
27
27
|
contentContainerStyle,
|
|
28
|
+
onScroll,
|
|
29
|
+
onScrollBeginDrag,
|
|
30
|
+
onScrollEndDrag,
|
|
31
|
+
onMomentumScrollBegin,
|
|
32
|
+
onMomentumScrollEnd,
|
|
28
33
|
...props
|
|
29
34
|
}) {
|
|
30
35
|
return /*#__PURE__*/_jsx(HeaderMotionScrollManager, {
|
|
31
36
|
scrollId: scrollId,
|
|
32
37
|
animatedRef: animatedRef,
|
|
38
|
+
refreshControl: props.refreshControl,
|
|
39
|
+
refreshing: props.refreshing,
|
|
40
|
+
onRefresh: props.onRefresh,
|
|
41
|
+
progressViewOffset: props.progressViewOffset,
|
|
42
|
+
onScroll: onScroll,
|
|
43
|
+
onScrollBeginDrag: onScrollBeginDrag,
|
|
44
|
+
onScrollEndDrag: onScrollEndDrag,
|
|
45
|
+
onMomentumScrollBegin: onMomentumScrollBegin,
|
|
46
|
+
onMomentumScrollEnd: onMomentumScrollEnd,
|
|
33
47
|
children: ({
|
|
34
|
-
onScroll,
|
|
48
|
+
onScroll: managedOnScroll,
|
|
49
|
+
refreshControl: managedRefreshControl,
|
|
35
50
|
...scrollViewProps
|
|
36
51
|
}, {
|
|
37
52
|
originalHeaderHeight,
|
|
@@ -39,9 +54,12 @@ export function HeaderMotionFlatList({
|
|
|
39
54
|
}) => /*#__PURE__*/_jsx(Animated.FlatList, {
|
|
40
55
|
...scrollViewProps,
|
|
41
56
|
...props,
|
|
42
|
-
onScroll:
|
|
43
|
-
|
|
44
|
-
|
|
57
|
+
onScroll: managedOnScroll,
|
|
58
|
+
...(managedRefreshControl && {
|
|
59
|
+
refreshControl: managedRefreshControl
|
|
60
|
+
}),
|
|
61
|
+
renderScrollComponent: scrollComponentProps => /*#__PURE__*/_jsx(AnimatedScrollContainer, {
|
|
62
|
+
...scrollComponentProps,
|
|
45
63
|
contentContainerStyle: [minHeightContentContainerStyle, {
|
|
46
64
|
paddingTop: originalHeaderHeight
|
|
47
65
|
}, contentContainerStyle]
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"names":["forwardRef","Animated","HeaderMotionScrollManager","jsx","_jsx","HeaderMotionFlatList","scrollId","animatedRef","contentContainerStyle","props","children","
|
|
1
|
+
{"version":3,"names":["forwardRef","Animated","HeaderMotionScrollManager","jsx","_jsx","HeaderMotionFlatList","scrollId","animatedRef","contentContainerStyle","onScroll","onScrollBeginDrag","onScrollEndDrag","onMomentumScrollBegin","onMomentumScrollEnd","props","refreshControl","refreshing","onRefresh","progressViewOffset","children","managedOnScroll","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;EACrBC,QAAQ;EACRC,iBAAiB;EACjBC,eAAe;EACfC,qBAAqB;EACrBC,mBAAmB;EACnB,GAAGC;AACyB,CAAC,EAAE;EAC/B,oBACEV,IAAA,CAACF,yBAAyB;IACxBI,QAAQ,EAAEA,QAAS;IACnBC,WAAW,EAAEA,WAAY;IACzBQ,cAAc,EAAED,KAAK,CAACC,cAAe;IACrCC,UAAU,EAAEF,KAAK,CAACE,UAAW;IAC7BC,SAAS,EAAEH,KAAK,CAACG,SAAU;IAC3BC,kBAAkB,EAAEJ,KAAK,CAACI,kBAAmB;IAC7CT,QAAQ,EAAEA,QAAS;IACnBC,iBAAiB,EAAEA,iBAAkB;IACrCC,eAAe,EAAEA,eAAgB;IACjCC,qBAAqB,EAAEA,qBAAsB;IAC7CC,mBAAmB,EAAEA,mBAAoB;IAAAM,QAAA,EAExCA,CACC;MACEV,QAAQ,EAAEW,eAAe;MACzBL,cAAc,EAAEM,qBAAqB;MACrC,GAAGC;IACL,CAAC,EACD;MAAEC,oBAAoB;MAAEC;IAA+B,CAAC,kBAExDpB,IAAA,CAACH,QAAQ,CAACwB,QAAQ;MAAA,GACZH,eAAe;MAAA,GACfR,KAAK;MACTL,QAAQ,EAAEW,eAAgB;MAAA,IACrBC,qBAAqB,IAAI;QAC5BN,cAAc,EAAEM;MAClB,CAAC;MACDK,qBAAqB,EAAGC,oBAAoB,iBAC1CvB,IAAA,CAACwB,uBAAuB;QAAA,GAClBD,oBAAoB;QACxBnB,qBAAqB,EAAE,CACrBgB,8BAA8B,EAC9B;UAAEK,UAAU,EAAEN;QAAqB,CAAC,EACpCf,qBAAqB;MACrB,CACH;IACD,CACH;EACF,CACwB,CAAC;AAEhC;AAEA,MAAMoB,uBAAuB,gBAAG5B,UAAU,CAGxC,CAAC;EAAEmB,QAAQ;EAAEX,qBAAqB;EAAE,GAAGsB;AAAK,CAAC,EAAEC,GAAG,KAAK;EACvD,oBACE3B,IAAA,CAACH,QAAQ,CAAC+B,UAAU;IAAA,GAAKF,IAAI;IAAEC,GAAG,EAAEA,GAAI;IAAAZ,QAAA,eACtCf,IAAA,CAACH,QAAQ,CAACgC,IAAI;MAACC,KAAK,EAAE1B,qBAAsB;MAAAW,QAAA,EAAEA;IAAQ,CAAgB;EAAC,CACpD,CAAC;AAE1B,CAAC,CAAC","ignoreList":[]}
|
|
@@ -26,7 +26,16 @@ import { useScrollManager } from "../hooks/index.js";
|
|
|
26
26
|
export function HeaderMotionScrollManager({
|
|
27
27
|
children,
|
|
28
28
|
scrollId,
|
|
29
|
-
animatedRef
|
|
29
|
+
animatedRef,
|
|
30
|
+
refreshControl,
|
|
31
|
+
refreshing,
|
|
32
|
+
onRefresh,
|
|
33
|
+
progressViewOffset,
|
|
34
|
+
onScroll,
|
|
35
|
+
onScrollBeginDrag,
|
|
36
|
+
onScrollEndDrag,
|
|
37
|
+
onMomentumScrollBegin,
|
|
38
|
+
onMomentumScrollEnd
|
|
30
39
|
}) {
|
|
31
40
|
if (typeof children !== 'function') {
|
|
32
41
|
throw new Error('HeaderMotion.ScrollManager only accepts render function as the only child.');
|
|
@@ -35,7 +44,16 @@ export function HeaderMotionScrollManager({
|
|
|
35
44
|
scrollableProps,
|
|
36
45
|
headerMotionContext
|
|
37
46
|
} = useScrollManager(scrollId, {
|
|
38
|
-
animatedRef
|
|
47
|
+
animatedRef,
|
|
48
|
+
refreshControl,
|
|
49
|
+
refreshing,
|
|
50
|
+
onRefresh,
|
|
51
|
+
progressViewOffset,
|
|
52
|
+
onScroll,
|
|
53
|
+
onScrollBeginDrag,
|
|
54
|
+
onScrollEndDrag,
|
|
55
|
+
onMomentumScrollBegin,
|
|
56
|
+
onMomentumScrollEnd
|
|
39
57
|
});
|
|
40
58
|
return children(scrollableProps, headerMotionContext);
|
|
41
59
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"names":["useScrollManager","HeaderMotionScrollManager","children","scrollId","animatedRef","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","onScroll","onScrollBeginDrag","onScrollEndDrag","onMomentumScrollBegin","onMomentumScrollEnd","Error","scrollableProps","headerMotionContext"],"sourceRoot":"../../../src","sources":["components/ScrollManager.tsx"],"mappings":";;AAAA,SAASA,gBAAgB,QAAQ,mBAAU;AAqC3C;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,kBAAkB;EAClBC,QAAQ;EACRC,iBAAiB;EACjBC,eAAe;EACfC,qBAAqB;EACrBC;AAC8B,CAAC,EAAE;EACjC,IAAI,OAAOX,QAAQ,KAAK,UAAU,EAAE;IAClC,MAAM,IAAIY,KAAK,CACb,4EACF,CAAC;EACH;EAEA,MAAM;IAAEC,eAAe;IAAEC;EAAoB,CAAC,GAAGhB,gBAAgB,CAACG,QAAQ,EAAE;IAC1EC,WAAW;IACXC,cAAc;IACdC,UAAU;IACVC,SAAS;IACTC,kBAAkB;IAClBC,QAAQ;IACRC,iBAAiB;IACjBC,eAAe;IACfC,qBAAqB;IACrBC;EACF,CAAC,CAAC;EAEF,OAAOX,QAAQ,CAACa,eAAe,EAAEC,mBAAmB,CAAC;AACvD","ignoreList":[]}
|
|
@@ -22,13 +22,26 @@ export function HeaderMotionScrollView({
|
|
|
22
22
|
animatedRef,
|
|
23
23
|
children,
|
|
24
24
|
contentContainerStyle,
|
|
25
|
+
refreshControl,
|
|
26
|
+
onScroll,
|
|
27
|
+
onScrollBeginDrag,
|
|
28
|
+
onScrollEndDrag,
|
|
29
|
+
onMomentumScrollBegin,
|
|
30
|
+
onMomentumScrollEnd,
|
|
25
31
|
...props
|
|
26
32
|
}) {
|
|
27
33
|
return /*#__PURE__*/_jsx(HeaderMotionScrollManager, {
|
|
28
34
|
scrollId: scrollId,
|
|
29
35
|
animatedRef: animatedRef,
|
|
36
|
+
refreshControl: refreshControl,
|
|
37
|
+
onScroll: onScroll,
|
|
38
|
+
onScrollBeginDrag: onScrollBeginDrag,
|
|
39
|
+
onScrollEndDrag: onScrollEndDrag,
|
|
40
|
+
onMomentumScrollBegin: onMomentumScrollBegin,
|
|
41
|
+
onMomentumScrollEnd: onMomentumScrollEnd,
|
|
30
42
|
children: ({
|
|
31
|
-
onScroll,
|
|
43
|
+
onScroll: managedOnScroll,
|
|
44
|
+
refreshControl: managedRefreshControl,
|
|
32
45
|
...scrollViewProps
|
|
33
46
|
}, {
|
|
34
47
|
originalHeaderHeight,
|
|
@@ -36,7 +49,10 @@ export function HeaderMotionScrollView({
|
|
|
36
49
|
}) => /*#__PURE__*/_jsx(Animated.ScrollView, {
|
|
37
50
|
...scrollViewProps,
|
|
38
51
|
...props,
|
|
39
|
-
onScroll:
|
|
52
|
+
onScroll: managedOnScroll,
|
|
53
|
+
...(managedRefreshControl && {
|
|
54
|
+
refreshControl: managedRefreshControl
|
|
55
|
+
}),
|
|
40
56
|
children: /*#__PURE__*/_jsx(Animated.View, {
|
|
41
57
|
style: [minHeightContentContainerStyle, {
|
|
42
58
|
paddingTop: originalHeaderHeight
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"names":["Animated","HeaderMotionScrollManager","jsx","_jsx","HeaderMotionScrollView","scrollId","animatedRef","children","contentContainerStyle","
|
|
1
|
+
{"version":3,"names":["Animated","HeaderMotionScrollManager","jsx","_jsx","HeaderMotionScrollView","scrollId","animatedRef","children","contentContainerStyle","refreshControl","onScroll","onScrollBeginDrag","onScrollEndDrag","onMomentumScrollBegin","onMomentumScrollEnd","props","managedOnScroll","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;EACdC,QAAQ;EACRC,iBAAiB;EACjBC,eAAe;EACfC,qBAAqB;EACrBC,mBAAmB;EACnB,GAAGC;AACwB,CAAC,EAAE;EAC9B,oBACEZ,IAAA,CAACF,yBAAyB;IACxBI,QAAQ,EAAEA,QAAS;IACnBC,WAAW,EAAEA,WAAY;IACzBG,cAAc,EAAEA,cAAe;IAC/BC,QAAQ,EAAEA,QAAS;IACnBC,iBAAiB,EAAEA,iBAAkB;IACrCC,eAAe,EAAEA,eAAgB;IACjCC,qBAAqB,EAAEA,qBAAsB;IAC7CC,mBAAmB,EAAEA,mBAAoB;IAAAP,QAAA,EAExCA,CACC;MACEG,QAAQ,EAAEM,eAAe;MACzBP,cAAc,EAAEQ,qBAAqB;MACrC,GAAGC;IACL,CAAC,EACD;MAAEC,oBAAoB;MAAEC;IAA+B,CAAC,kBAExDjB,IAAA,CAACH,QAAQ,CAACqB,UAAU;MAAA,GACdH,eAAe;MAAA,GACfH,KAAK;MACTL,QAAQ,EAAEM,eAAgB;MAAA,IACrBC,qBAAqB,IAAI;QAC5BR,cAAc,EAAEQ;MAClB,CAAC;MAAAV,QAAA,eAEDJ,IAAA,CAACH,QAAQ,CAACsB,IAAI;QACZC,KAAK,EAAE,CACLH,8BAA8B,EAC9B;UAAEI,UAAU,EAAEL;QAAqB,CAAC,EACpCX,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":[]}
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
import { useCallback } from 'react';
|
|
4
|
+
import { useComposedEventHandler } from 'react-native-reanimated';
|
|
5
|
+
import { scheduleOnRN } from 'react-native-worklets';
|
|
6
|
+
export function useConsumerScrollHandlers({
|
|
7
|
+
onScroll,
|
|
8
|
+
onScrollBeginDrag,
|
|
9
|
+
onScrollEndDrag,
|
|
10
|
+
onMomentumScrollBegin,
|
|
11
|
+
onMomentumScrollEnd
|
|
12
|
+
}) {
|
|
13
|
+
const consumerOnScroll = typeof onScroll === 'function' ? onScroll : undefined;
|
|
14
|
+
const consumerOnScrollBeginDrag = typeof onScrollBeginDrag === 'function' ? onScrollBeginDrag : undefined;
|
|
15
|
+
const consumerOnScrollEndDrag = typeof onScrollEndDrag === 'function' ? onScrollEndDrag : undefined;
|
|
16
|
+
const consumerOnMomentumScrollBegin = typeof onMomentumScrollBegin === 'function' ? onMomentumScrollBegin : undefined;
|
|
17
|
+
const consumerOnMomentumScrollEnd = typeof onMomentumScrollEnd === 'function' ? onMomentumScrollEnd : undefined;
|
|
18
|
+
const onScrollBridge = useCallback(event => {
|
|
19
|
+
'worklet';
|
|
20
|
+
|
|
21
|
+
if (!consumerOnScroll) {
|
|
22
|
+
return;
|
|
23
|
+
}
|
|
24
|
+
scheduleOnRN(consumerOnScroll, {
|
|
25
|
+
nativeEvent: event
|
|
26
|
+
});
|
|
27
|
+
}, [consumerOnScroll]);
|
|
28
|
+
const onBeginDragBridge = useCallback(event => {
|
|
29
|
+
'worklet';
|
|
30
|
+
|
|
31
|
+
if (!consumerOnScrollBeginDrag) {
|
|
32
|
+
return;
|
|
33
|
+
}
|
|
34
|
+
scheduleOnRN(consumerOnScrollBeginDrag, {
|
|
35
|
+
nativeEvent: event
|
|
36
|
+
});
|
|
37
|
+
}, [consumerOnScrollBeginDrag]);
|
|
38
|
+
const onEndDragBridge = useCallback(event => {
|
|
39
|
+
'worklet';
|
|
40
|
+
|
|
41
|
+
if (!consumerOnScrollEndDrag) {
|
|
42
|
+
return;
|
|
43
|
+
}
|
|
44
|
+
scheduleOnRN(consumerOnScrollEndDrag, {
|
|
45
|
+
nativeEvent: event
|
|
46
|
+
});
|
|
47
|
+
}, [consumerOnScrollEndDrag]);
|
|
48
|
+
const onMomentumBeginBridge = useCallback(event => {
|
|
49
|
+
'worklet';
|
|
50
|
+
|
|
51
|
+
if (!consumerOnMomentumScrollBegin) {
|
|
52
|
+
return;
|
|
53
|
+
}
|
|
54
|
+
scheduleOnRN(consumerOnMomentumScrollBegin, {
|
|
55
|
+
nativeEvent: event
|
|
56
|
+
});
|
|
57
|
+
}, [consumerOnMomentumScrollBegin]);
|
|
58
|
+
const onMomentumEndBridge = useCallback(event => {
|
|
59
|
+
'worklet';
|
|
60
|
+
|
|
61
|
+
if (!consumerOnMomentumScrollEnd) {
|
|
62
|
+
return;
|
|
63
|
+
}
|
|
64
|
+
scheduleOnRN(consumerOnMomentumScrollEnd, {
|
|
65
|
+
nativeEvent: event
|
|
66
|
+
});
|
|
67
|
+
}, [consumerOnMomentumScrollEnd]);
|
|
68
|
+
return {
|
|
69
|
+
onScroll: onScrollBridge,
|
|
70
|
+
onBeginDrag: onBeginDragBridge,
|
|
71
|
+
onEndDrag: onEndDragBridge,
|
|
72
|
+
onMomentumBegin: onMomentumBeginBridge,
|
|
73
|
+
onMomentumEnd: onMomentumEndBridge
|
|
74
|
+
};
|
|
75
|
+
}
|
|
76
|
+
export function useScrollHandlerComposition(ownScrollHandler, consumerScrollHandler) {
|
|
77
|
+
// TODO: I guess the typing here could be more precise
|
|
78
|
+
const consumerWorkletHandler = isAnimatedScrollHandler(consumerScrollHandler) ? consumerScrollHandler : null;
|
|
79
|
+
return useComposedEventHandler([ownScrollHandler, consumerWorkletHandler]);
|
|
80
|
+
}
|
|
81
|
+
function isAnimatedScrollHandler(handler) {
|
|
82
|
+
// FUTURE: we could be checking just by typeof handler === 'object'?
|
|
83
|
+
// This seems safer for now, unless Reanimated changes this shape. Revisit
|
|
84
|
+
return !!handler && 'workletEventHandler' in handler;
|
|
85
|
+
}
|
|
86
|
+
//# sourceMappingURL=useConsumerScrollHandlers.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"names":["useCallback","useComposedEventHandler","scheduleOnRN","useConsumerScrollHandlers","onScroll","onScrollBeginDrag","onScrollEndDrag","onMomentumScrollBegin","onMomentumScrollEnd","consumerOnScroll","undefined","consumerOnScrollBeginDrag","consumerOnScrollEndDrag","consumerOnMomentumScrollBegin","consumerOnMomentumScrollEnd","onScrollBridge","event","nativeEvent","onBeginDragBridge","onEndDragBridge","onMomentumBeginBridge","onMomentumEndBridge","onBeginDrag","onEndDrag","onMomentumBegin","onMomentumEnd","useScrollHandlerComposition","ownScrollHandler","consumerScrollHandler","consumerWorkletHandler","isAnimatedScrollHandler","handler"],"sourceRoot":"../../../src","sources":["hooks/useConsumerScrollHandlers.ts"],"mappings":";;AAAA,SAASA,WAAW,QAAQ,OAAO;AACnC,SACEC,uBAAuB,QAIlB,yBAAyB;AAChC,SAASC,YAAY,QAAQ,uBAAuB;AAwBpD,OAAO,SAASC,yBAAyBA,CAAC;EACxCC,QAAQ;EACRC,iBAAiB;EACjBC,eAAe;EACfC,qBAAqB;EACrBC;AAC2B,CAAC,EAAyB;EACrD,MAAMC,gBAAgB,GACpB,OAAOL,QAAQ,KAAK,UAAU,GACzBA,QAAQ,GACTM,SAAS;EACf,MAAMC,yBAAyB,GAC7B,OAAON,iBAAiB,KAAK,UAAU,GAClCA,iBAAiB,GAClBK,SAAS;EACf,MAAME,uBAAuB,GAC3B,OAAON,eAAe,KAAK,UAAU,GAChCA,eAAe,GAChBI,SAAS;EACf,MAAMG,6BAA6B,GACjC,OAAON,qBAAqB,KAAK,UAAU,GACtCA,qBAAqB,GACtBG,SAAS;EACf,MAAMI,2BAA2B,GAC/B,OAAON,mBAAmB,KAAK,UAAU,GACpCA,mBAAmB,GACpBE,SAAS;EAEf,MAAMK,cAAc,GAAGf,WAAW,CAC/BgB,KAAkB,IAAK;IACtB,SAAS;;IACT,IAAI,CAACP,gBAAgB,EAAE;MACrB;IACF;IACAP,YAAY,CAACO,gBAAgB,EAAE;MAAEQ,WAAW,EAAED;IAAM,CAAY,CAAC;EACnE,CAAC,EACD,CAACP,gBAAgB,CACnB,CAAC;EAED,MAAMS,iBAAiB,GAAGlB,WAAW,CAClCgB,KAAkB,IAAK;IACtB,SAAS;;IACT,IAAI,CAACL,yBAAyB,EAAE;MAC9B;IACF;IACAT,YAAY,CAACS,yBAAyB,EAAE;MACtCM,WAAW,EAAED;IACf,CAAY,CAAC;EACf,CAAC,EACD,CAACL,yBAAyB,CAC5B,CAAC;EAED,MAAMQ,eAAe,GAAGnB,WAAW,CAChCgB,KAAkB,IAAK;IACtB,SAAS;;IACT,IAAI,CAACJ,uBAAuB,EAAE;MAC5B;IACF;IACAV,YAAY,CAACU,uBAAuB,EAAE;MAAEK,WAAW,EAAED;IAAM,CAAY,CAAC;EAC1E,CAAC,EACD,CAACJ,uBAAuB,CAC1B,CAAC;EAED,MAAMQ,qBAAqB,GAAGpB,WAAW,CACtCgB,KAAkB,IAAK;IACtB,SAAS;;IACT,IAAI,CAACH,6BAA6B,EAAE;MAClC;IACF;IACAX,YAAY,CAACW,6BAA6B,EAAE;MAC1CI,WAAW,EAAED;IACf,CAAY,CAAC;EACf,CAAC,EACD,CAACH,6BAA6B,CAChC,CAAC;EAED,MAAMQ,mBAAmB,GAAGrB,WAAW,CACpCgB,KAAkB,IAAK;IACtB,SAAS;;IACT,IAAI,CAACF,2BAA2B,EAAE;MAChC;IACF;IACAZ,YAAY,CAACY,2BAA2B,EAAE;MACxCG,WAAW,EAAED;IACf,CAAY,CAAC;EACf,CAAC,EACD,CAACF,2BAA2B,CAC9B,CAAC;EAED,OAAO;IACLV,QAAQ,EAAEW,cAAc;IACxBO,WAAW,EAAEJ,iBAAiB;IAC9BK,SAAS,EAAEJ,eAAe;IAC1BK,eAAe,EAAEJ,qBAAqB;IACtCK,aAAa,EAAEJ;EACjB,CAAC;AACH;AAEA,OAAO,SAASK,2BAA2BA,CACzCC,gBAA8D,EAC9DC,qBAA6D,EAC7D;EACA;EACA,MAAMC,sBAAsB,GAAGC,uBAAuB,CAACF,qBAAqB,CAAC,GACxEA,qBAAqB,GACtB,IAAI;EAER,OAAO3B,uBAAuB,CAAC,CAAC0B,gBAAgB,EAAEE,sBAAsB,CAAC,CAAC;AAC5E;AAEA,SAASC,uBAAuBA,CAC9BC,OAA+C,EACR;EACvC;EACA;EACA,OAAO,CAAC,CAACA,OAAO,IAAI,qBAAqB,IAAIA,OAAO;AACtD","ignoreList":[]}
|
|
@@ -5,6 +5,8 @@ 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";
|
|
9
|
+
import { useConsumerScrollHandlers, useScrollHandlerComposition } from "./useConsumerScrollHandlers.js";
|
|
8
10
|
|
|
9
11
|
/**
|
|
10
12
|
* Hook that manages scroll tracking and synchronization for header animations.
|
|
@@ -59,6 +61,23 @@ export function useScrollManager(scrollId, options) {
|
|
|
59
61
|
const id = scrollId ?? DEFAULT_SCROLL_ID;
|
|
60
62
|
const localRef = useAnimatedRef(); // TODO: better typing
|
|
61
63
|
const animatedRef = options?.animatedRef ?? localRef;
|
|
64
|
+
const refreshControl = options?.refreshControl;
|
|
65
|
+
const refreshing = options?.refreshing;
|
|
66
|
+
const onRefresh = options?.onRefresh;
|
|
67
|
+
const progressViewOffset = options?.progressViewOffset ?? originalHeaderHeight;
|
|
68
|
+
const {
|
|
69
|
+
onScroll,
|
|
70
|
+
onBeginDrag,
|
|
71
|
+
onEndDrag,
|
|
72
|
+
onMomentumBegin,
|
|
73
|
+
onMomentumEnd
|
|
74
|
+
} = useConsumerScrollHandlers({
|
|
75
|
+
onScroll: options?.onScroll,
|
|
76
|
+
onScrollBeginDrag: options?.onScrollBeginDrag,
|
|
77
|
+
onScrollEndDrag: options?.onScrollEndDrag,
|
|
78
|
+
onMomentumScrollBegin: options?.onMomentumScrollBegin,
|
|
79
|
+
onMomentumScrollEnd: options?.onMomentumScrollEnd
|
|
80
|
+
});
|
|
62
81
|
useEffect(() => {
|
|
63
82
|
return () => {
|
|
64
83
|
scheduleOnUI(scrollIdToDelete => {
|
|
@@ -105,6 +124,7 @@ export function useScrollManager(scrollId, options) {
|
|
|
105
124
|
const scrollHandler = useCallback(e => {
|
|
106
125
|
'worklet';
|
|
107
126
|
|
|
127
|
+
onScroll?.(e);
|
|
108
128
|
scrollValues.modify(value => {
|
|
109
129
|
if (!value[id]) {
|
|
110
130
|
return value;
|
|
@@ -123,8 +143,14 @@ export function useScrollManager(scrollId, options) {
|
|
|
123
143
|
}
|
|
124
144
|
return value;
|
|
125
145
|
});
|
|
126
|
-
}, [scrollValues, id, activeScrollId, progressThreshold]);
|
|
127
|
-
const
|
|
146
|
+
}, [scrollValues, id, activeScrollId, progressThreshold, onScroll]);
|
|
147
|
+
const animatedScrollHandler = useAnimatedScrollHandler({
|
|
148
|
+
onScroll: scrollHandler,
|
|
149
|
+
onBeginDrag,
|
|
150
|
+
onEndDrag,
|
|
151
|
+
onMomentumBegin,
|
|
152
|
+
onMomentumEnd
|
|
153
|
+
});
|
|
128
154
|
const minHeightContentContainerStyle = useAnimatedStyle(() => {
|
|
129
155
|
if (globalThis.__RUNTIME_KIND === RuntimeKind.ReactNative) {
|
|
130
156
|
return {};
|
|
@@ -137,10 +163,17 @@ export function useScrollManager(scrollId, options) {
|
|
|
137
163
|
minHeight: measurement.height + progressThreshold
|
|
138
164
|
};
|
|
139
165
|
});
|
|
166
|
+
const resolvedRefreshControl = resolveRefreshControl({
|
|
167
|
+
refreshControl,
|
|
168
|
+
refreshing,
|
|
169
|
+
onRefresh,
|
|
170
|
+
progressViewOffset
|
|
171
|
+
});
|
|
140
172
|
const scrollableProps = {
|
|
141
|
-
onScroll,
|
|
173
|
+
onScroll: useScrollHandlerComposition(animatedScrollHandler, options?.onScroll),
|
|
142
174
|
scrollEventThrottle: 16,
|
|
143
|
-
ref: animatedRef
|
|
175
|
+
ref: animatedRef,
|
|
176
|
+
refreshControl: resolvedRefreshControl
|
|
144
177
|
};
|
|
145
178
|
const headerMotionContext = {
|
|
146
179
|
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","options","ctxValue","Error","scrollValues","progress","activeScrollId","progressThreshold","originalHeaderHeight","id","localRef","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","
|
|
1
|
+
{"version":3,"names":["useContext","useCallback","useEffect","measure","scrollTo","useAnimatedReaction","useAnimatedRef","useAnimatedScrollHandler","useAnimatedStyle","RuntimeKind","scheduleOnUI","HeaderMotionContext","DEFAULT_SCROLL_ID","getInitialScrollValue","resolveRefreshControl","useConsumerScrollHandlers","useScrollHandlerComposition","useScrollManager","scrollId","options","ctxValue","Error","scrollValues","progress","activeScrollId","progressThreshold","originalHeaderHeight","id","localRef","animatedRef","refreshControl","refreshing","onRefresh","progressViewOffset","onScroll","onBeginDrag","onEndDrag","onMomentumBegin","onMomentumEnd","onScrollBeginDrag","onScrollEndDrag","onMomentumScrollBegin","onMomentumScrollEnd","scrollIdToDelete","modify","value","newProgress","oldProgress","currentActiveScrollId","get","newCur","scrollValue","progressDiff","current","newMin","min","scrollHandler","e","activeScrollIdValue","oldCurrent","oldMin","isCollapsed","newCurrent","contentOffset","y","Math","max","animatedScrollHandler","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,7 +1,6 @@
|
|
|
1
1
|
import { type ComponentProps } from 'react';
|
|
2
2
|
import Animated, { type AnimatedRef } from 'react-native-reanimated';
|
|
3
|
-
type
|
|
4
|
-
export type HeaderMotionFlatListProps<T = any> = AnimatedFlatListProps<T> & {
|
|
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.
|
|
@@ -30,6 +29,5 @@ export type HeaderMotionFlatListProps<T = any> = AnimatedFlatListProps<T> & {
|
|
|
30
29
|
* </HeaderMotion>
|
|
31
30
|
* ```
|
|
32
31
|
*/
|
|
33
|
-
export declare function HeaderMotionFlatList<T = any>({ scrollId, animatedRef, contentContainerStyle, ...props }: HeaderMotionFlatListProps<T>): import("react/jsx-runtime").JSX.Element;
|
|
34
|
-
export {};
|
|
32
|
+
export declare function HeaderMotionFlatList<T = any>({ scrollId, animatedRef, contentContainerStyle, onScroll, onScrollBeginDrag, onScrollEndDrag, onMomentumScrollBegin, onMomentumScrollEnd, ...props }: HeaderMotionFlatListProps<T>): import("react/jsx-runtime").JSX.Element;
|
|
35
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,EAAE,EAAE,KAAK,WAAW,EAAE,MAAM,yBAAyB,CAAC;AAKrE,
|
|
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,QAAQ,EACR,iBAAiB,EACjB,eAAe,EACf,qBAAqB,EACrB,mBAAmB,EACnB,GAAG,KAAK,EACT,EAAE,yBAAyB,CAAC,CAAC,CAAC,2CA4C9B"}
|
|
@@ -1,8 +1,10 @@
|
|
|
1
1
|
import type { ScrollManagerConfig } from '../types';
|
|
2
|
+
import type { ResolveRefreshControlOptions } from '../hooks/refreshControl';
|
|
2
3
|
import type { ReactNode } from 'react';
|
|
3
4
|
import type { AnimatedRef } from 'react-native-reanimated';
|
|
5
|
+
import type { ConsumerScrollEventHandlers } from '../hooks/useConsumerScrollHandlers';
|
|
4
6
|
type ScrollManagerRenderChildren = (scrollableProps: ScrollManagerConfig['scrollableProps'], options: ScrollManagerConfig['headerMotionContext']) => ReactNode;
|
|
5
|
-
export interface HeaderMotionScrollManagerProps {
|
|
7
|
+
export interface HeaderMotionScrollManagerProps extends Omit<ResolveRefreshControlOptions, 'progressViewOffset'>, ConsumerScrollEventHandlers {
|
|
6
8
|
/**
|
|
7
9
|
* Optional unique identifier for this scroll view.
|
|
8
10
|
* Use this when you have multiple scroll views (e.g., in tabs) to track them separately.
|
|
@@ -13,6 +15,11 @@ export interface HeaderMotionScrollManagerProps {
|
|
|
13
15
|
* When provided, the scroll manager will use this ref instead of creating its own.
|
|
14
16
|
*/
|
|
15
17
|
animatedRef?: AnimatedRef<any>;
|
|
18
|
+
/**
|
|
19
|
+
* Optional refresh progress offset override.
|
|
20
|
+
* When provided, it takes precedence over the automatic offset based on header height.
|
|
21
|
+
*/
|
|
22
|
+
progressViewOffset?: ResolveRefreshControlOptions['progressViewOffset'];
|
|
16
23
|
/**
|
|
17
24
|
* Render function that receives scroll props and header context.
|
|
18
25
|
* Use this to create custom scroll implementations that integrate with HeaderMotion.
|
|
@@ -41,6 +48,6 @@ export interface HeaderMotionScrollManagerProps {
|
|
|
41
48
|
* </HeaderMotion>
|
|
42
49
|
* ```
|
|
43
50
|
*/
|
|
44
|
-
export declare function HeaderMotionScrollManager({ children, scrollId, animatedRef, }: HeaderMotionScrollManagerProps): ReactNode;
|
|
51
|
+
export declare function HeaderMotionScrollManager({ children, scrollId, animatedRef, refreshControl, refreshing, onRefresh, progressViewOffset, onScroll, onScrollBeginDrag, onScrollEndDrag, onMomentumScrollBegin, onMomentumScrollEnd, }: HeaderMotionScrollManagerProps): ReactNode;
|
|
45
52
|
export {};
|
|
46
53
|
//# 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;AACvC,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,yBAAyB,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;AAC3D,OAAO,KAAK,EAAE,2BAA2B,EAAE,MAAM,oCAAoC,CAAC;AAEtF,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,EAC9D,2BAA2B;IAC7B;;;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,EAClB,QAAQ,EACR,iBAAiB,EACjB,eAAe,EACf,qBAAqB,EACrB,mBAAmB,GACpB,EAAE,8BAA8B,aAqBhC"}
|
|
@@ -25,5 +25,5 @@ export type HeaderMotionScrollViewProps = AnimatedScrollViewProps & {
|
|
|
25
25
|
* </HeaderMotion>
|
|
26
26
|
* ```
|
|
27
27
|
*/
|
|
28
|
-
export declare function HeaderMotionScrollView({ scrollId, animatedRef, children, contentContainerStyle, ...props }: HeaderMotionScrollViewProps): import("react/jsx-runtime").JSX.Element;
|
|
28
|
+
export declare function HeaderMotionScrollView({ scrollId, animatedRef, children, contentContainerStyle, refreshControl, onScroll, onScrollBeginDrag, onScrollEndDrag, onMomentumScrollBegin, onMomentumScrollEnd, ...props }: HeaderMotionScrollViewProps): import("react/jsx-runtime").JSX.Element;
|
|
29
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,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,GAAG,KAAK,EACT,EAAE,2BAA2B,
|
|
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,QAAQ,EACR,iBAAiB,EACjB,eAAe,EACf,qBAAqB,EACrB,mBAAmB,EACnB,GAAG,KAAK,EACT,EAAE,2BAA2B,2CAyC7B"}
|
|
@@ -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"}
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
import { type AnimatedScrollViewProps, type ScrollHandler, type ScrollHandlerProcessed } from 'react-native-reanimated';
|
|
2
|
+
import type { ScrollHandlerContext } from '../types';
|
|
3
|
+
type AnimatedScrollViewOnScroll = AnimatedScrollViewProps['onScroll'];
|
|
4
|
+
type ScrollEvent = Parameters<ScrollHandler<Record<string, unknown>>>[0];
|
|
5
|
+
export type ConsumerScrollEventHandlers = Pick<AnimatedScrollViewProps, 'onScroll' | 'onScrollBeginDrag' | 'onScrollEndDrag' | 'onMomentumScrollBegin' | 'onMomentumScrollEnd'>;
|
|
6
|
+
export interface ConsumerScrollBridges {
|
|
7
|
+
onScroll?: (event: ScrollEvent) => void;
|
|
8
|
+
onBeginDrag?: (event: ScrollEvent) => void;
|
|
9
|
+
onEndDrag?: (event: ScrollEvent) => void;
|
|
10
|
+
onMomentumBegin?: (event: ScrollEvent) => void;
|
|
11
|
+
onMomentumEnd?: (event: ScrollEvent) => void;
|
|
12
|
+
}
|
|
13
|
+
export declare function useConsumerScrollHandlers({ onScroll, onScrollBeginDrag, onScrollEndDrag, onMomentumScrollBegin, onMomentumScrollEnd, }: ConsumerScrollEventHandlers): ConsumerScrollBridges;
|
|
14
|
+
export declare function useScrollHandlerComposition(ownScrollHandler: ScrollHandlerProcessed<ScrollHandlerContext>, consumerScrollHandler: AnimatedScrollViewOnScroll | undefined): (event: Readonly<{
|
|
15
|
+
bubbles: boolean | undefined;
|
|
16
|
+
cancelable: boolean | undefined;
|
|
17
|
+
currentTarget: number | import("react-native").HostInstance;
|
|
18
|
+
defaultPrevented: boolean | undefined;
|
|
19
|
+
dispatchConfig: Readonly<{
|
|
20
|
+
registrationName: string;
|
|
21
|
+
}>;
|
|
22
|
+
eventPhase: number | undefined;
|
|
23
|
+
preventDefault: () => void;
|
|
24
|
+
isDefaultPrevented: () => boolean;
|
|
25
|
+
stopPropagation: () => void;
|
|
26
|
+
isPropagationStopped: () => boolean;
|
|
27
|
+
isTrusted: boolean | undefined;
|
|
28
|
+
nativeEvent: Readonly<{
|
|
29
|
+
contentInset: Readonly<{
|
|
30
|
+
bottom: number;
|
|
31
|
+
left: number;
|
|
32
|
+
right: number;
|
|
33
|
+
top: number;
|
|
34
|
+
}>;
|
|
35
|
+
contentOffset: Readonly<{
|
|
36
|
+
y: number;
|
|
37
|
+
x: number;
|
|
38
|
+
}>;
|
|
39
|
+
contentSize: Readonly<{
|
|
40
|
+
height: number;
|
|
41
|
+
width: number;
|
|
42
|
+
}>;
|
|
43
|
+
layoutMeasurement: Readonly<{
|
|
44
|
+
height: number;
|
|
45
|
+
width: number;
|
|
46
|
+
}>;
|
|
47
|
+
velocity?: Readonly<{
|
|
48
|
+
y: number;
|
|
49
|
+
x: number;
|
|
50
|
+
}>;
|
|
51
|
+
zoomScale?: number;
|
|
52
|
+
responderIgnoreScroll?: boolean;
|
|
53
|
+
targetContentOffset?: Readonly<{
|
|
54
|
+
y: number;
|
|
55
|
+
x: number;
|
|
56
|
+
}>;
|
|
57
|
+
}>;
|
|
58
|
+
persist: () => void;
|
|
59
|
+
target: (number | undefined) | import("react-native").HostInstance;
|
|
60
|
+
timeStamp: number;
|
|
61
|
+
type: string | undefined;
|
|
62
|
+
}>, context?: ScrollHandlerContext | undefined) => void;
|
|
63
|
+
export {};
|
|
64
|
+
//# sourceMappingURL=useConsumerScrollHandlers.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"useConsumerScrollHandlers.d.ts","sourceRoot":"","sources":["../../../../src/hooks/useConsumerScrollHandlers.ts"],"names":[],"mappings":"AACA,OAAO,EAEL,KAAK,uBAAuB,EAC5B,KAAK,aAAa,EAClB,KAAK,sBAAsB,EAC5B,MAAM,yBAAyB,CAAC;AAEjC,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,UAAU,CAAC;AAGrD,KAAK,0BAA0B,GAAG,uBAAuB,CAAC,UAAU,CAAC,CAAC;AACtE,KAAK,WAAW,GAAG,UAAU,CAAC,aAAa,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;AAEzE,MAAM,MAAM,2BAA2B,GAAG,IAAI,CAC5C,uBAAuB,EACrB,UAAU,GACV,mBAAmB,GACnB,iBAAiB,GACjB,uBAAuB,GACvB,qBAAqB,CACxB,CAAC;AAEF,MAAM,WAAW,qBAAqB;IACpC,QAAQ,CAAC,EAAE,CAAC,KAAK,EAAE,WAAW,KAAK,IAAI,CAAC;IACxC,WAAW,CAAC,EAAE,CAAC,KAAK,EAAE,WAAW,KAAK,IAAI,CAAC;IAC3C,SAAS,CAAC,EAAE,CAAC,KAAK,EAAE,WAAW,KAAK,IAAI,CAAC;IACzC,eAAe,CAAC,EAAE,CAAC,KAAK,EAAE,WAAW,KAAK,IAAI,CAAC;IAC/C,aAAa,CAAC,EAAE,CAAC,KAAK,EAAE,WAAW,KAAK,IAAI,CAAC;CAC9C;AAED,wBAAgB,yBAAyB,CAAC,EACxC,QAAQ,EACR,iBAAiB,EACjB,eAAe,EACf,qBAAqB,EACrB,mBAAmB,GACpB,EAAE,2BAA2B,GAAG,qBAAqB,CA0FrD;AAED,wBAAgB,2BAA2B,CACzC,gBAAgB,EAAE,sBAAsB,CAAC,oBAAoB,CAAC,EAC9D,qBAAqB,EAAE,0BAA0B,GAAG,SAAS;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;wDAQ9D"}
|
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
import { type AnimatedRef } from 'react-native-reanimated';
|
|
2
2
|
import type { ScrollManagerConfig } from '../types';
|
|
3
|
+
import { type ResolveRefreshControlOptions } from './refreshControl';
|
|
4
|
+
import { type ConsumerScrollEventHandlers } from './useConsumerScrollHandlers';
|
|
3
5
|
/**
|
|
4
6
|
* Hook that manages scroll tracking and synchronization for header animations.
|
|
5
7
|
* Returns props to apply to scrollable components and additional values that help with adjusting styling of the scrollables to header's dimensions.
|
|
@@ -37,12 +39,17 @@ import type { ScrollManagerConfig } from '../types';
|
|
|
37
39
|
* }
|
|
38
40
|
* ```
|
|
39
41
|
*/
|
|
40
|
-
export interface UseScrollManagerOptions {
|
|
42
|
+
export interface UseScrollManagerOptions extends Omit<ResolveRefreshControlOptions, 'progressViewOffset'>, ConsumerScrollEventHandlers {
|
|
41
43
|
/**
|
|
42
44
|
* Optional animated ref to use instead of creating one internally.
|
|
43
45
|
* Useful when you need access to the scroll view ref from outside.
|
|
44
46
|
*/
|
|
45
47
|
animatedRef?: AnimatedRef<any>;
|
|
48
|
+
/**
|
|
49
|
+
* Optional refresh progress offset override.
|
|
50
|
+
* When provided, it takes precedence over the automatic offset based on header height.
|
|
51
|
+
*/
|
|
52
|
+
progressViewOffset?: ResolveRefreshControlOptions['progressViewOffset'];
|
|
46
53
|
}
|
|
47
54
|
export declare function useScrollManager(scrollId?: string, options?: UseScrollManagerOptions): ScrollManagerConfig;
|
|
48
55
|
//# sourceMappingURL=useScrollManager.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
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;
|
|
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;AAC1B,OAAO,EAGL,KAAK,2BAA2B,EACjC,MAAM,6BAA6B,CAAC;AAErC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAoCG;AACH,MAAM,WAAW,uBACf,SAAQ,IAAI,CAAC,4BAA4B,EAAE,oBAAoB,CAAC,EAC9D,2BAA2B;IAC7B;;;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,CAyKrB"}
|
|
@@ -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,8 +37,12 @@ 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;
|
|
42
44
|
}
|
|
45
|
+
export type ScrollHandlerContext = {
|
|
46
|
+
lastOffset: number | undefined;
|
|
47
|
+
};
|
|
43
48
|
//# sourceMappingURL=types.d.ts.map
|
|
@@ -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;AAED,MAAM,MAAM,oBAAoB,GAAG;IACjC,UAAU,EAAE,MAAM,GAAG,SAAS,CAAC;CAChC,CAAC"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "react-native-header-motion",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.4.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",
|
|
@@ -4,11 +4,9 @@ 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.
|
|
@@ -42,21 +40,45 @@ export function HeaderMotionFlatList<T = any>({
|
|
|
42
40
|
scrollId,
|
|
43
41
|
animatedRef,
|
|
44
42
|
contentContainerStyle,
|
|
43
|
+
onScroll,
|
|
44
|
+
onScrollBeginDrag,
|
|
45
|
+
onScrollEndDrag,
|
|
46
|
+
onMomentumScrollBegin,
|
|
47
|
+
onMomentumScrollEnd,
|
|
45
48
|
...props
|
|
46
49
|
}: HeaderMotionFlatListProps<T>) {
|
|
47
50
|
return (
|
|
48
|
-
<HeaderMotionScrollManager
|
|
51
|
+
<HeaderMotionScrollManager
|
|
52
|
+
scrollId={scrollId}
|
|
53
|
+
animatedRef={animatedRef}
|
|
54
|
+
refreshControl={props.refreshControl}
|
|
55
|
+
refreshing={props.refreshing}
|
|
56
|
+
onRefresh={props.onRefresh}
|
|
57
|
+
progressViewOffset={props.progressViewOffset}
|
|
58
|
+
onScroll={onScroll}
|
|
59
|
+
onScrollBeginDrag={onScrollBeginDrag}
|
|
60
|
+
onScrollEndDrag={onScrollEndDrag}
|
|
61
|
+
onMomentumScrollBegin={onMomentumScrollBegin}
|
|
62
|
+
onMomentumScrollEnd={onMomentumScrollEnd}
|
|
63
|
+
>
|
|
49
64
|
{(
|
|
50
|
-
{
|
|
65
|
+
{
|
|
66
|
+
onScroll: managedOnScroll,
|
|
67
|
+
refreshControl: managedRefreshControl,
|
|
68
|
+
...scrollViewProps
|
|
69
|
+
},
|
|
51
70
|
{ originalHeaderHeight, minHeightContentContainerStyle }
|
|
52
71
|
) => (
|
|
53
72
|
<Animated.FlatList
|
|
54
73
|
{...scrollViewProps}
|
|
55
74
|
{...props}
|
|
56
|
-
onScroll={
|
|
57
|
-
|
|
75
|
+
onScroll={managedOnScroll}
|
|
76
|
+
{...(managedRefreshControl && {
|
|
77
|
+
refreshControl: managedRefreshControl,
|
|
78
|
+
})}
|
|
79
|
+
renderScrollComponent={(scrollComponentProps) => (
|
|
58
80
|
<AnimatedScrollContainer
|
|
59
|
-
{...
|
|
81
|
+
{...scrollComponentProps}
|
|
60
82
|
contentContainerStyle={[
|
|
61
83
|
minHeightContentContainerStyle,
|
|
62
84
|
{ paddingTop: originalHeaderHeight },
|
|
@@ -1,14 +1,18 @@
|
|
|
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';
|
|
4
5
|
import type { AnimatedRef } from 'react-native-reanimated';
|
|
6
|
+
import type { ConsumerScrollEventHandlers } from '../hooks/useConsumerScrollHandlers';
|
|
5
7
|
|
|
6
8
|
type ScrollManagerRenderChildren = (
|
|
7
9
|
scrollableProps: ScrollManagerConfig['scrollableProps'],
|
|
8
10
|
options: ScrollManagerConfig['headerMotionContext']
|
|
9
11
|
) => ReactNode;
|
|
10
12
|
|
|
11
|
-
export interface HeaderMotionScrollManagerProps
|
|
13
|
+
export interface HeaderMotionScrollManagerProps
|
|
14
|
+
extends Omit<ResolveRefreshControlOptions, 'progressViewOffset'>,
|
|
15
|
+
ConsumerScrollEventHandlers {
|
|
12
16
|
/**
|
|
13
17
|
* Optional unique identifier for this scroll view.
|
|
14
18
|
* Use this when you have multiple scroll views (e.g., in tabs) to track them separately.
|
|
@@ -19,6 +23,11 @@ export interface HeaderMotionScrollManagerProps {
|
|
|
19
23
|
* When provided, the scroll manager will use this ref instead of creating its own.
|
|
20
24
|
*/
|
|
21
25
|
animatedRef?: AnimatedRef<any>;
|
|
26
|
+
/**
|
|
27
|
+
* Optional refresh progress offset override.
|
|
28
|
+
* When provided, it takes precedence over the automatic offset based on header height.
|
|
29
|
+
*/
|
|
30
|
+
progressViewOffset?: ResolveRefreshControlOptions['progressViewOffset'];
|
|
22
31
|
/**
|
|
23
32
|
* Render function that receives scroll props and header context.
|
|
24
33
|
* Use this to create custom scroll implementations that integrate with HeaderMotion.
|
|
@@ -52,6 +61,15 @@ export function HeaderMotionScrollManager({
|
|
|
52
61
|
children,
|
|
53
62
|
scrollId,
|
|
54
63
|
animatedRef,
|
|
64
|
+
refreshControl,
|
|
65
|
+
refreshing,
|
|
66
|
+
onRefresh,
|
|
67
|
+
progressViewOffset,
|
|
68
|
+
onScroll,
|
|
69
|
+
onScrollBeginDrag,
|
|
70
|
+
onScrollEndDrag,
|
|
71
|
+
onMomentumScrollBegin,
|
|
72
|
+
onMomentumScrollEnd,
|
|
55
73
|
}: HeaderMotionScrollManagerProps) {
|
|
56
74
|
if (typeof children !== 'function') {
|
|
57
75
|
throw new Error(
|
|
@@ -61,6 +79,15 @@ export function HeaderMotionScrollManager({
|
|
|
61
79
|
|
|
62
80
|
const { scrollableProps, headerMotionContext } = useScrollManager(scrollId, {
|
|
63
81
|
animatedRef,
|
|
82
|
+
refreshControl,
|
|
83
|
+
refreshing,
|
|
84
|
+
onRefresh,
|
|
85
|
+
progressViewOffset,
|
|
86
|
+
onScroll,
|
|
87
|
+
onScrollBeginDrag,
|
|
88
|
+
onScrollEndDrag,
|
|
89
|
+
onMomentumScrollBegin,
|
|
90
|
+
onMomentumScrollEnd,
|
|
64
91
|
});
|
|
65
92
|
|
|
66
93
|
return children(scrollableProps, headerMotionContext);
|
|
@@ -36,18 +36,40 @@ export function HeaderMotionScrollView({
|
|
|
36
36
|
animatedRef,
|
|
37
37
|
children,
|
|
38
38
|
contentContainerStyle,
|
|
39
|
+
refreshControl,
|
|
40
|
+
onScroll,
|
|
41
|
+
onScrollBeginDrag,
|
|
42
|
+
onScrollEndDrag,
|
|
43
|
+
onMomentumScrollBegin,
|
|
44
|
+
onMomentumScrollEnd,
|
|
39
45
|
...props
|
|
40
46
|
}: HeaderMotionScrollViewProps) {
|
|
41
47
|
return (
|
|
42
|
-
<HeaderMotionScrollManager
|
|
48
|
+
<HeaderMotionScrollManager
|
|
49
|
+
scrollId={scrollId}
|
|
50
|
+
animatedRef={animatedRef}
|
|
51
|
+
refreshControl={refreshControl}
|
|
52
|
+
onScroll={onScroll}
|
|
53
|
+
onScrollBeginDrag={onScrollBeginDrag}
|
|
54
|
+
onScrollEndDrag={onScrollEndDrag}
|
|
55
|
+
onMomentumScrollBegin={onMomentumScrollBegin}
|
|
56
|
+
onMomentumScrollEnd={onMomentumScrollEnd}
|
|
57
|
+
>
|
|
43
58
|
{(
|
|
44
|
-
{
|
|
59
|
+
{
|
|
60
|
+
onScroll: managedOnScroll,
|
|
61
|
+
refreshControl: managedRefreshControl,
|
|
62
|
+
...scrollViewProps
|
|
63
|
+
},
|
|
45
64
|
{ originalHeaderHeight, minHeightContentContainerStyle }
|
|
46
65
|
) => (
|
|
47
66
|
<Animated.ScrollView
|
|
48
67
|
{...scrollViewProps}
|
|
49
68
|
{...props}
|
|
50
|
-
onScroll={
|
|
69
|
+
onScroll={managedOnScroll}
|
|
70
|
+
{...(managedRefreshControl && {
|
|
71
|
+
refreshControl: managedRefreshControl,
|
|
72
|
+
})}
|
|
51
73
|
>
|
|
52
74
|
<Animated.View
|
|
53
75
|
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
|
+
}
|
|
@@ -0,0 +1,148 @@
|
|
|
1
|
+
import { useCallback } from 'react';
|
|
2
|
+
import {
|
|
3
|
+
useComposedEventHandler,
|
|
4
|
+
type AnimatedScrollViewProps,
|
|
5
|
+
type ScrollHandler,
|
|
6
|
+
type ScrollHandlerProcessed,
|
|
7
|
+
} from 'react-native-reanimated';
|
|
8
|
+
import { scheduleOnRN } from 'react-native-worklets';
|
|
9
|
+
import type { ScrollHandlerContext } from '../types';
|
|
10
|
+
|
|
11
|
+
type ConsumerScrollHandler = (event: unknown) => void;
|
|
12
|
+
type AnimatedScrollViewOnScroll = AnimatedScrollViewProps['onScroll'];
|
|
13
|
+
type ScrollEvent = Parameters<ScrollHandler<Record<string, unknown>>>[0];
|
|
14
|
+
|
|
15
|
+
export type ConsumerScrollEventHandlers = Pick<
|
|
16
|
+
AnimatedScrollViewProps,
|
|
17
|
+
| 'onScroll'
|
|
18
|
+
| 'onScrollBeginDrag'
|
|
19
|
+
| 'onScrollEndDrag'
|
|
20
|
+
| 'onMomentumScrollBegin'
|
|
21
|
+
| 'onMomentumScrollEnd'
|
|
22
|
+
>;
|
|
23
|
+
|
|
24
|
+
export interface ConsumerScrollBridges {
|
|
25
|
+
onScroll?: (event: ScrollEvent) => void;
|
|
26
|
+
onBeginDrag?: (event: ScrollEvent) => void;
|
|
27
|
+
onEndDrag?: (event: ScrollEvent) => void;
|
|
28
|
+
onMomentumBegin?: (event: ScrollEvent) => void;
|
|
29
|
+
onMomentumEnd?: (event: ScrollEvent) => void;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
export function useConsumerScrollHandlers({
|
|
33
|
+
onScroll,
|
|
34
|
+
onScrollBeginDrag,
|
|
35
|
+
onScrollEndDrag,
|
|
36
|
+
onMomentumScrollBegin,
|
|
37
|
+
onMomentumScrollEnd,
|
|
38
|
+
}: ConsumerScrollEventHandlers): ConsumerScrollBridges {
|
|
39
|
+
const consumerOnScroll =
|
|
40
|
+
typeof onScroll === 'function'
|
|
41
|
+
? (onScroll as ConsumerScrollHandler)
|
|
42
|
+
: undefined;
|
|
43
|
+
const consumerOnScrollBeginDrag =
|
|
44
|
+
typeof onScrollBeginDrag === 'function'
|
|
45
|
+
? (onScrollBeginDrag as ConsumerScrollHandler)
|
|
46
|
+
: undefined;
|
|
47
|
+
const consumerOnScrollEndDrag =
|
|
48
|
+
typeof onScrollEndDrag === 'function'
|
|
49
|
+
? (onScrollEndDrag as ConsumerScrollHandler)
|
|
50
|
+
: undefined;
|
|
51
|
+
const consumerOnMomentumScrollBegin =
|
|
52
|
+
typeof onMomentumScrollBegin === 'function'
|
|
53
|
+
? (onMomentumScrollBegin as ConsumerScrollHandler)
|
|
54
|
+
: undefined;
|
|
55
|
+
const consumerOnMomentumScrollEnd =
|
|
56
|
+
typeof onMomentumScrollEnd === 'function'
|
|
57
|
+
? (onMomentumScrollEnd as ConsumerScrollHandler)
|
|
58
|
+
: undefined;
|
|
59
|
+
|
|
60
|
+
const onScrollBridge = useCallback(
|
|
61
|
+
(event: ScrollEvent) => {
|
|
62
|
+
'worklet';
|
|
63
|
+
if (!consumerOnScroll) {
|
|
64
|
+
return;
|
|
65
|
+
}
|
|
66
|
+
scheduleOnRN(consumerOnScroll, { nativeEvent: event } as unknown);
|
|
67
|
+
},
|
|
68
|
+
[consumerOnScroll]
|
|
69
|
+
);
|
|
70
|
+
|
|
71
|
+
const onBeginDragBridge = useCallback(
|
|
72
|
+
(event: ScrollEvent) => {
|
|
73
|
+
'worklet';
|
|
74
|
+
if (!consumerOnScrollBeginDrag) {
|
|
75
|
+
return;
|
|
76
|
+
}
|
|
77
|
+
scheduleOnRN(consumerOnScrollBeginDrag, {
|
|
78
|
+
nativeEvent: event,
|
|
79
|
+
} as unknown);
|
|
80
|
+
},
|
|
81
|
+
[consumerOnScrollBeginDrag]
|
|
82
|
+
);
|
|
83
|
+
|
|
84
|
+
const onEndDragBridge = useCallback(
|
|
85
|
+
(event: ScrollEvent) => {
|
|
86
|
+
'worklet';
|
|
87
|
+
if (!consumerOnScrollEndDrag) {
|
|
88
|
+
return;
|
|
89
|
+
}
|
|
90
|
+
scheduleOnRN(consumerOnScrollEndDrag, { nativeEvent: event } as unknown);
|
|
91
|
+
},
|
|
92
|
+
[consumerOnScrollEndDrag]
|
|
93
|
+
);
|
|
94
|
+
|
|
95
|
+
const onMomentumBeginBridge = useCallback(
|
|
96
|
+
(event: ScrollEvent) => {
|
|
97
|
+
'worklet';
|
|
98
|
+
if (!consumerOnMomentumScrollBegin) {
|
|
99
|
+
return;
|
|
100
|
+
}
|
|
101
|
+
scheduleOnRN(consumerOnMomentumScrollBegin, {
|
|
102
|
+
nativeEvent: event,
|
|
103
|
+
} as unknown);
|
|
104
|
+
},
|
|
105
|
+
[consumerOnMomentumScrollBegin]
|
|
106
|
+
);
|
|
107
|
+
|
|
108
|
+
const onMomentumEndBridge = useCallback(
|
|
109
|
+
(event: ScrollEvent) => {
|
|
110
|
+
'worklet';
|
|
111
|
+
if (!consumerOnMomentumScrollEnd) {
|
|
112
|
+
return;
|
|
113
|
+
}
|
|
114
|
+
scheduleOnRN(consumerOnMomentumScrollEnd, {
|
|
115
|
+
nativeEvent: event,
|
|
116
|
+
} as unknown);
|
|
117
|
+
},
|
|
118
|
+
[consumerOnMomentumScrollEnd]
|
|
119
|
+
);
|
|
120
|
+
|
|
121
|
+
return {
|
|
122
|
+
onScroll: onScrollBridge,
|
|
123
|
+
onBeginDrag: onBeginDragBridge,
|
|
124
|
+
onEndDrag: onEndDragBridge,
|
|
125
|
+
onMomentumBegin: onMomentumBeginBridge,
|
|
126
|
+
onMomentumEnd: onMomentumEndBridge,
|
|
127
|
+
};
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
export function useScrollHandlerComposition(
|
|
131
|
+
ownScrollHandler: ScrollHandlerProcessed<ScrollHandlerContext>,
|
|
132
|
+
consumerScrollHandler: AnimatedScrollViewOnScroll | undefined
|
|
133
|
+
) {
|
|
134
|
+
// TODO: I guess the typing here could be more precise
|
|
135
|
+
const consumerWorkletHandler = isAnimatedScrollHandler(consumerScrollHandler)
|
|
136
|
+
? (consumerScrollHandler as ScrollHandlerProcessed<ScrollHandlerContext>)
|
|
137
|
+
: null;
|
|
138
|
+
|
|
139
|
+
return useComposedEventHandler([ownScrollHandler, consumerWorkletHandler]);
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
function isAnimatedScrollHandler(
|
|
143
|
+
handler: AnimatedScrollViewOnScroll | undefined
|
|
144
|
+
): handler is AnimatedScrollViewOnScroll {
|
|
145
|
+
// FUTURE: we could be checking just by typeof handler === 'object'?
|
|
146
|
+
// This seems safer for now, unless Reanimated changes this shape. Revisit
|
|
147
|
+
return !!handler && 'workletEventHandler' in handler;
|
|
148
|
+
}
|
|
@@ -13,6 +13,15 @@ import { RuntimeKind, scheduleOnUI } from 'react-native-worklets';
|
|
|
13
13
|
import { HeaderMotionContext } from '../context';
|
|
14
14
|
import type { ScrollManagerConfig, ScrollValues } from '../types';
|
|
15
15
|
import { DEFAULT_SCROLL_ID, getInitialScrollValue } from '../utils';
|
|
16
|
+
import {
|
|
17
|
+
resolveRefreshControl,
|
|
18
|
+
type ResolveRefreshControlOptions,
|
|
19
|
+
} from './refreshControl';
|
|
20
|
+
import {
|
|
21
|
+
useConsumerScrollHandlers,
|
|
22
|
+
useScrollHandlerComposition,
|
|
23
|
+
type ConsumerScrollEventHandlers,
|
|
24
|
+
} from './useConsumerScrollHandlers';
|
|
16
25
|
|
|
17
26
|
/**
|
|
18
27
|
* Hook that manages scroll tracking and synchronization for header animations.
|
|
@@ -51,12 +60,19 @@ import { DEFAULT_SCROLL_ID, getInitialScrollValue } from '../utils';
|
|
|
51
60
|
* }
|
|
52
61
|
* ```
|
|
53
62
|
*/
|
|
54
|
-
export interface UseScrollManagerOptions
|
|
63
|
+
export interface UseScrollManagerOptions
|
|
64
|
+
extends Omit<ResolveRefreshControlOptions, 'progressViewOffset'>,
|
|
65
|
+
ConsumerScrollEventHandlers {
|
|
55
66
|
/**
|
|
56
67
|
* Optional animated ref to use instead of creating one internally.
|
|
57
68
|
* Useful when you need access to the scroll view ref from outside.
|
|
58
69
|
*/
|
|
59
70
|
animatedRef?: AnimatedRef<any>;
|
|
71
|
+
/**
|
|
72
|
+
* Optional refresh progress offset override.
|
|
73
|
+
* When provided, it takes precedence over the automatic offset based on header height.
|
|
74
|
+
*/
|
|
75
|
+
progressViewOffset?: ResolveRefreshControlOptions['progressViewOffset'];
|
|
60
76
|
}
|
|
61
77
|
|
|
62
78
|
export function useScrollManager(
|
|
@@ -81,6 +97,20 @@ export function useScrollManager(
|
|
|
81
97
|
|
|
82
98
|
const localRef = useAnimatedRef<any>(); // TODO: better typing
|
|
83
99
|
const animatedRef = options?.animatedRef ?? localRef;
|
|
100
|
+
const refreshControl = options?.refreshControl;
|
|
101
|
+
const refreshing = options?.refreshing;
|
|
102
|
+
const onRefresh = options?.onRefresh;
|
|
103
|
+
const progressViewOffset =
|
|
104
|
+
options?.progressViewOffset ?? originalHeaderHeight;
|
|
105
|
+
|
|
106
|
+
const { onScroll, onBeginDrag, onEndDrag, onMomentumBegin, onMomentumEnd } =
|
|
107
|
+
useConsumerScrollHandlers({
|
|
108
|
+
onScroll: options?.onScroll,
|
|
109
|
+
onScrollBeginDrag: options?.onScrollBeginDrag,
|
|
110
|
+
onScrollEndDrag: options?.onScrollEndDrag,
|
|
111
|
+
onMomentumScrollBegin: options?.onMomentumScrollBegin,
|
|
112
|
+
onMomentumScrollEnd: options?.onMomentumScrollEnd,
|
|
113
|
+
});
|
|
84
114
|
|
|
85
115
|
useEffect(() => {
|
|
86
116
|
return () => {
|
|
@@ -142,6 +172,7 @@ export function useScrollManager(
|
|
|
142
172
|
const scrollHandler = useCallback<ScrollHandler>(
|
|
143
173
|
(e) => {
|
|
144
174
|
'worklet';
|
|
175
|
+
onScroll?.(e);
|
|
145
176
|
|
|
146
177
|
scrollValues.modify((value) => {
|
|
147
178
|
if (!value[id]) {
|
|
@@ -167,10 +198,16 @@ export function useScrollManager(
|
|
|
167
198
|
return value;
|
|
168
199
|
});
|
|
169
200
|
},
|
|
170
|
-
[scrollValues, id, activeScrollId, progressThreshold]
|
|
201
|
+
[scrollValues, id, activeScrollId, progressThreshold, onScroll]
|
|
171
202
|
);
|
|
172
203
|
|
|
173
|
-
const
|
|
204
|
+
const animatedScrollHandler = useAnimatedScrollHandler({
|
|
205
|
+
onScroll: scrollHandler,
|
|
206
|
+
onBeginDrag,
|
|
207
|
+
onEndDrag,
|
|
208
|
+
onMomentumBegin,
|
|
209
|
+
onMomentumEnd,
|
|
210
|
+
});
|
|
174
211
|
|
|
175
212
|
const minHeightContentContainerStyle = useAnimatedStyle(() => {
|
|
176
213
|
if (globalThis.__RUNTIME_KIND === RuntimeKind.ReactNative) {
|
|
@@ -188,10 +225,21 @@ export function useScrollManager(
|
|
|
188
225
|
};
|
|
189
226
|
});
|
|
190
227
|
|
|
228
|
+
const resolvedRefreshControl = resolveRefreshControl({
|
|
229
|
+
refreshControl,
|
|
230
|
+
refreshing,
|
|
231
|
+
onRefresh,
|
|
232
|
+
progressViewOffset,
|
|
233
|
+
});
|
|
234
|
+
|
|
191
235
|
const scrollableProps = {
|
|
192
|
-
onScroll
|
|
236
|
+
onScroll: useScrollHandlerComposition(
|
|
237
|
+
animatedScrollHandler,
|
|
238
|
+
options?.onScroll
|
|
239
|
+
),
|
|
193
240
|
scrollEventThrottle: 16,
|
|
194
241
|
ref: animatedRef,
|
|
242
|
+
refreshControl: resolvedRefreshControl,
|
|
195
243
|
};
|
|
196
244
|
const headerMotionContext = {
|
|
197
245
|
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,7 +61,12 @@ 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;
|
|
62
68
|
}
|
|
69
|
+
|
|
70
|
+
export type ScrollHandlerContext = {
|
|
71
|
+
lastOffset: number | undefined;
|
|
72
|
+
};
|