react-native-header-motion 1.0.0-alpha.0 → 1.0.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 +65 -528
- package/lib/module/components/Bridge.js +16 -0
- package/lib/module/components/Bridge.js.map +1 -0
- package/lib/module/components/FlatList.js +5 -54
- package/lib/module/components/FlatList.js.map +1 -1
- package/lib/module/components/Header.js +71 -13
- package/lib/module/components/Header.js.map +1 -1
- package/lib/module/components/HeaderDynamic.js +34 -0
- package/lib/module/components/HeaderDynamic.js.map +1 -0
- package/lib/module/components/HeaderMotion.js +14 -20
- package/lib/module/components/HeaderMotion.js.map +1 -1
- package/lib/module/components/HeaderPanBoundary.js +54 -0
- package/lib/module/components/HeaderPanBoundary.js.map +1 -0
- package/lib/module/components/NavigationBridge.js +20 -0
- package/lib/module/components/NavigationBridge.js.map +1 -0
- package/lib/module/components/ScrollManager.js +19 -7
- package/lib/module/components/ScrollManager.js.map +1 -1
- package/lib/module/components/ScrollView.js +6 -39
- package/lib/module/components/ScrollView.js.map +1 -1
- package/lib/module/components/createHeaderMotionScrollable.js +136 -0
- package/lib/module/components/createHeaderMotionScrollable.js.map +1 -0
- package/lib/module/components/index.js +3 -1
- package/lib/module/components/index.js.map +1 -1
- package/lib/module/context.js +8 -1
- package/lib/module/context.js.map +1 -1
- package/lib/module/hooks/index.js +1 -0
- package/lib/module/hooks/index.js.map +1 -1
- package/lib/module/hooks/useActiveScrollId.js +7 -6
- package/lib/module/hooks/useActiveScrollId.js.map +1 -1
- package/lib/module/hooks/useConsumerScrollHandlers.js +86 -0
- package/lib/module/hooks/useConsumerScrollHandlers.js.map +1 -0
- package/lib/module/hooks/useHeaderMotionBridge.js +14 -0
- package/lib/module/hooks/useHeaderMotionBridge.js.map +1 -0
- package/lib/module/hooks/useMotionProgress.js +12 -42
- package/lib/module/hooks/useMotionProgress.js.map +1 -1
- package/lib/module/hooks/useMotionProgress.test.js +56 -0
- package/lib/module/hooks/useMotionProgress.test.js.map +1 -0
- package/lib/module/hooks/useScrollManager.js +168 -87
- package/lib/module/hooks/useScrollManager.js.map +1 -1
- package/lib/module/index.js +21 -18
- package/lib/module/index.js.map +1 -1
- package/lib/module/utils/defaults.js +2 -1
- package/lib/module/utils/defaults.js.map +1 -1
- package/lib/module/utils/header.js +24 -0
- package/lib/module/utils/header.js.map +1 -0
- package/lib/module/utils/headerOffsetStyle.js +31 -0
- package/lib/module/utils/headerOffsetStyle.js.map +1 -0
- package/lib/module/utils/index.js +2 -0
- package/lib/module/utils/index.js.map +1 -1
- package/lib/typescript/docs/docusaurus.config.d.ts +4 -0
- package/lib/typescript/docs/docusaurus.config.d.ts.map +1 -0
- package/lib/typescript/docs/sidebars.d.ts +4 -0
- package/lib/typescript/docs/sidebars.d.ts.map +1 -0
- package/lib/typescript/docs/src/pages/index.d.ts +2 -0
- package/lib/typescript/docs/src/pages/index.d.ts.map +1 -0
- package/lib/typescript/src/components/Bridge.d.ts +19 -0
- package/lib/typescript/src/components/Bridge.d.ts.map +1 -0
- package/lib/typescript/src/components/FlatList.d.ts +7 -15
- package/lib/typescript/src/components/FlatList.d.ts.map +1 -1
- package/lib/typescript/src/components/Header.d.ts +73 -12
- package/lib/typescript/src/components/Header.d.ts.map +1 -1
- package/lib/typescript/src/components/HeaderDynamic.d.ts +11 -0
- package/lib/typescript/src/components/HeaderDynamic.d.ts.map +1 -0
- package/lib/typescript/src/components/HeaderMotion.d.ts +38 -23
- package/lib/typescript/src/components/HeaderMotion.d.ts.map +1 -1
- package/lib/typescript/src/components/HeaderPanBoundary.d.ts +11 -0
- package/lib/typescript/src/components/HeaderPanBoundary.d.ts.map +1 -0
- package/lib/typescript/src/components/NavigationBridge.d.ts +19 -0
- package/lib/typescript/src/components/NavigationBridge.d.ts.map +1 -0
- package/lib/typescript/src/components/ScrollManager.d.ts +13 -9
- package/lib/typescript/src/components/ScrollManager.d.ts.map +1 -1
- package/lib/typescript/src/components/ScrollView.d.ts +7 -14
- package/lib/typescript/src/components/ScrollView.d.ts.map +1 -1
- package/lib/typescript/src/components/createHeaderMotionScrollable.d.ts +86 -0
- package/lib/typescript/src/components/createHeaderMotionScrollable.d.ts.map +1 -0
- package/lib/typescript/src/components/index.d.ts +3 -1
- package/lib/typescript/src/components/index.d.ts.map +1 -1
- package/lib/typescript/src/context.d.ts +3 -17
- package/lib/typescript/src/context.d.ts.map +1 -1
- package/lib/typescript/src/hooks/index.d.ts +1 -0
- package/lib/typescript/src/hooks/index.d.ts.map +1 -1
- package/lib/typescript/src/hooks/useActiveScrollId.d.ts +7 -6
- package/lib/typescript/src/hooks/useActiveScrollId.d.ts.map +1 -1
- 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/useHeaderMotionBridge.d.ts +10 -0
- package/lib/typescript/src/hooks/useHeaderMotionBridge.d.ts.map +1 -0
- package/lib/typescript/src/hooks/useMotionProgress.d.ts +8 -25
- package/lib/typescript/src/hooks/useMotionProgress.d.ts.map +1 -1
- package/lib/typescript/src/hooks/useMotionProgress.test.d.ts +2 -0
- package/lib/typescript/src/hooks/useMotionProgress.test.d.ts.map +1 -0
- package/lib/typescript/src/hooks/useScrollManager.d.ts +61 -29
- package/lib/typescript/src/hooks/useScrollManager.d.ts.map +1 -1
- package/lib/typescript/src/index.d.ts +56 -26
- package/lib/typescript/src/index.d.ts.map +1 -1
- package/lib/typescript/src/types.d.ts +54 -17
- package/lib/typescript/src/types.d.ts.map +1 -1
- package/lib/typescript/src/utils/defaults.d.ts +3 -2
- package/lib/typescript/src/utils/defaults.d.ts.map +1 -1
- package/lib/typescript/src/utils/header.d.ts +10 -0
- package/lib/typescript/src/utils/header.d.ts.map +1 -0
- package/lib/typescript/src/utils/headerOffsetStyle.d.ts +19 -0
- package/lib/typescript/src/utils/headerOffsetStyle.d.ts.map +1 -0
- package/lib/typescript/src/utils/index.d.ts +2 -0
- package/lib/typescript/src/utils/index.d.ts.map +1 -1
- package/lib/typescript/src/utils/refreshControl.d.ts +12 -12
- package/package.json +12 -5
- package/src/components/Bridge.tsx +29 -0
- package/src/components/FlatList.tsx +18 -76
- package/src/components/Header.tsx +159 -23
- package/src/components/HeaderDynamic.tsx +45 -0
- package/src/components/HeaderMotion.tsx +47 -50
- package/src/components/HeaderPanBoundary.tsx +92 -0
- package/src/components/NavigationBridge.tsx +30 -0
- package/src/components/ScrollManager.tsx +23 -11
- package/src/components/ScrollView.tsx +16 -60
- package/src/components/createHeaderMotionScrollable.tsx +438 -0
- package/src/components/index.ts +3 -1
- package/src/context.ts +11 -24
- package/src/hooks/index.ts +1 -0
- package/src/hooks/useActiveScrollId.ts +7 -6
- package/src/hooks/useConsumerScrollHandlers.ts +148 -0
- package/src/hooks/useHeaderMotionBridge.ts +15 -0
- package/src/hooks/useMotionProgress.test.ts +67 -0
- package/src/hooks/useMotionProgress.ts +12 -45
- package/src/hooks/useScrollManager.ts +251 -114
- package/src/index.ts +82 -36
- package/src/types.ts +81 -29
- package/src/utils/defaults.ts +7 -1
- package/src/utils/header.tsx +52 -0
- package/src/utils/headerOffsetStyle.ts +40 -0
- package/src/utils/index.ts +2 -0
- package/lib/module/components/HeaderBase.js +0 -107
- package/lib/module/components/HeaderBase.js.map +0 -1
- package/lib/typescript/src/components/HeaderBase.d.ts +0 -41
- package/lib/typescript/src/components/HeaderBase.d.ts.map +0 -1
- package/src/components/HeaderBase.tsx +0 -140
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { useCallback, useRef, useEffect, useMemo } from 'react';
|
|
1
|
+
import { useCallback, useRef, useEffect, useMemo, useState } from 'react';
|
|
2
2
|
import {
|
|
3
3
|
Extrapolation,
|
|
4
4
|
interpolate,
|
|
@@ -52,53 +52,68 @@ const resolveScrollIdForProgress = (
|
|
|
52
52
|
|
|
53
53
|
export interface HeaderMotionProps<T extends string> {
|
|
54
54
|
/**
|
|
55
|
-
*
|
|
56
|
-
*
|
|
55
|
+
* Distance that maps the active scrollable from `progress = 0`
|
|
56
|
+
* to `progress = 1`.
|
|
57
57
|
*
|
|
58
|
-
*
|
|
58
|
+
* Use a number when the collapse distance is fixed. Use a function when the
|
|
59
|
+
* distance should depend on what `measureDynamic` reads from
|
|
60
|
+
* `HeaderMotion.Header.Dynamic`.
|
|
61
|
+
*
|
|
62
|
+
* A common pattern is to measure the height of the part of the header that
|
|
63
|
+
* should disappear and use that as the threshold.
|
|
59
64
|
*/
|
|
60
65
|
progressThreshold?: ProgressThreshold;
|
|
61
66
|
/**
|
|
62
|
-
*
|
|
63
|
-
*
|
|
64
|
-
* Receives the layout change event from React Native.
|
|
67
|
+
* Reads the value that should define the "collapsible" part of the header.
|
|
65
68
|
*
|
|
66
|
-
* This
|
|
69
|
+
* This is called from `HeaderMotion.Header.Dynamic` on layout. The returned
|
|
70
|
+
* number feeds `progressThreshold` when you provide that prop as a function.
|
|
67
71
|
*
|
|
68
|
-
*
|
|
72
|
+
* By default, the library measures the dynamic section's height. Override
|
|
73
|
+
* this when the collapse distance should be based on something else, for
|
|
74
|
+
* example width or a derived value from the layout event.
|
|
69
75
|
*/
|
|
70
76
|
measureDynamic?: MeasureAnimatedHeader;
|
|
71
77
|
/**
|
|
72
|
-
*
|
|
78
|
+
* Controls when `measureDynamic` is allowed to update.
|
|
79
|
+
*
|
|
73
80
|
* - 'mount': Only measure once on mount
|
|
74
|
-
* - 'update':
|
|
81
|
+
* - 'update': Re-measure whenever `HeaderMotion.Header.Dynamic` lays out again
|
|
82
|
+
*
|
|
83
|
+
* Use `'mount'` for stable headers. Use `'update'` when the dynamic section
|
|
84
|
+
* can change size after mount, for example after async data loads or content
|
|
85
|
+
* expansion.
|
|
86
|
+
*
|
|
75
87
|
* @default 'mount'
|
|
76
88
|
*/
|
|
77
89
|
measureDynamicMode?: 'update' | 'mount';
|
|
78
90
|
/**
|
|
79
|
-
* Shared value
|
|
80
|
-
*
|
|
91
|
+
* Shared value that tells HeaderMotion which scrollable currently owns the
|
|
92
|
+
* header progress in multi-scroll setups.
|
|
93
|
+
*
|
|
94
|
+
* Pass this when one header is shared across multiple scrollables, such as
|
|
95
|
+
* tabs or pager pages. Each scrollable should also get its own `scrollId`.
|
|
81
96
|
*/
|
|
82
97
|
activeScrollId?: SharedValue<T>;
|
|
83
98
|
/**
|
|
84
|
-
*
|
|
85
|
-
*
|
|
99
|
+
* Controls how `progress` behaves outside the `[0, threshold]` range.
|
|
100
|
+
*
|
|
101
|
+
* The default clamps the value between `0` and `1`. Relax this if you want
|
|
102
|
+
* to animate overscroll or other out-of-range states.
|
|
86
103
|
*
|
|
87
|
-
* You may want to modify it to achieve some animations for the overscroll scenarios.
|
|
88
104
|
* @default Extrapolation.CLAMP
|
|
89
105
|
*/
|
|
90
106
|
progressExtrapolation?: ExtrapolationType;
|
|
91
|
-
/**
|
|
92
|
-
* @default false
|
|
93
|
-
*/
|
|
94
|
-
enableHeaderPan?: boolean;
|
|
95
|
-
/** Child components that will have access to the header motion context */
|
|
107
|
+
/** Descendants that should participate in the shared header-motion state. */
|
|
96
108
|
children: ReactNode;
|
|
97
109
|
}
|
|
98
110
|
|
|
99
111
|
/**
|
|
100
|
-
*
|
|
101
|
-
*
|
|
112
|
+
* Root provider for a header-motion setup.
|
|
113
|
+
*
|
|
114
|
+
* It tracks the measured header layout, the active scroll position, and the
|
|
115
|
+
* derived `progress` shared value consumed by your animated header UI.
|
|
116
|
+
*
|
|
102
117
|
* @template T - The type of scroll ID string
|
|
103
118
|
*/
|
|
104
119
|
function HeaderMotionContextProvider<T extends string>({
|
|
@@ -107,11 +122,10 @@ function HeaderMotionContextProvider<T extends string>({
|
|
|
107
122
|
measureDynamicMode = 'mount',
|
|
108
123
|
activeScrollId,
|
|
109
124
|
progressExtrapolation = Extrapolation.CLAMP,
|
|
110
|
-
enableHeaderPan = false,
|
|
111
125
|
children,
|
|
112
126
|
}: HeaderMotionProps<T>) {
|
|
113
127
|
const dynamicMeasurement = useSharedValue<number | undefined>(undefined);
|
|
114
|
-
const originalHeaderHeight =
|
|
128
|
+
const [originalHeaderHeight, setOriginalHeaderHeight] = useState(0);
|
|
115
129
|
const progressThresholdValue = useSharedValue(
|
|
116
130
|
typeof progressThreshold === 'number' ? progressThreshold : Infinity
|
|
117
131
|
);
|
|
@@ -131,11 +145,11 @@ function HeaderMotionContextProvider<T extends string>({
|
|
|
131
145
|
}
|
|
132
146
|
|
|
133
147
|
dynamicMeasurement.set(measured);
|
|
134
|
-
|
|
148
|
+
const nextThreshold =
|
|
135
149
|
typeof progressThreshold === 'number'
|
|
136
150
|
? progressThreshold
|
|
137
|
-
: progressThreshold(measured)
|
|
138
|
-
);
|
|
151
|
+
: progressThreshold(measured);
|
|
152
|
+
progressThresholdValue.set(nextThreshold);
|
|
139
153
|
},
|
|
140
154
|
[
|
|
141
155
|
measureDynamicMode,
|
|
@@ -153,21 +167,17 @@ function HeaderMotionContextProvider<T extends string>({
|
|
|
153
167
|
}
|
|
154
168
|
|
|
155
169
|
const measured = dynamicMeasurement.get();
|
|
156
|
-
|
|
157
|
-
measured === undefined ? Infinity : progressThreshold(measured)
|
|
158
|
-
);
|
|
170
|
+
const nextThreshold =
|
|
171
|
+
measured === undefined ? Infinity : progressThreshold(measured);
|
|
172
|
+
progressThresholdValue.set(nextThreshold);
|
|
159
173
|
}, [progressThreshold, dynamicMeasurement, progressThresholdValue]);
|
|
160
174
|
|
|
161
175
|
const measureTotalHeight = useCallback<MeasureAnimatedHeaderAndSet>(
|
|
162
176
|
(e) => {
|
|
163
177
|
const measuredValue = e.nativeEvent.layout.height;
|
|
164
|
-
|
|
165
|
-
return;
|
|
166
|
-
}
|
|
167
|
-
|
|
168
|
-
originalHeaderHeight.set(measuredValue);
|
|
178
|
+
setOriginalHeaderHeight(measuredValue);
|
|
169
179
|
},
|
|
170
|
-
[
|
|
180
|
+
[setOriginalHeaderHeight]
|
|
171
181
|
);
|
|
172
182
|
|
|
173
183
|
const scrollValues = useSharedValue<ScrollValues>({
|
|
@@ -212,24 +222,13 @@ function HeaderMotionContextProvider<T extends string>({
|
|
|
212
222
|
// were not propagating reliably, while it works for refs. Revisit later.
|
|
213
223
|
// We need to be updating the scrollTo on active scroll ID changes and doing it via state would cause re-renders.
|
|
214
224
|
// It's a bit of an anti-pattern to use refs for this as well, but I am yet to figure out a better way to pass those if SV won't work.
|
|
215
|
-
const animatedHeaderBaseProps = useMemo(
|
|
216
|
-
() => ({
|
|
217
|
-
enableHeaderPan,
|
|
218
|
-
scrollToRef,
|
|
219
|
-
headerPanMomentumOffset,
|
|
220
|
-
}),
|
|
221
|
-
[enableHeaderPan, headerPanMomentumOffset]
|
|
222
|
-
);
|
|
223
|
-
|
|
224
225
|
const ctxValue = useMemo(
|
|
225
226
|
() => ({
|
|
226
227
|
progress,
|
|
227
228
|
originalHeaderHeight,
|
|
228
229
|
measureDynamic: setOrUpdateDynamicMeasurement,
|
|
229
230
|
measureTotalHeight,
|
|
230
|
-
enableHeaderPan,
|
|
231
231
|
headerPanMomentumOffset,
|
|
232
|
-
animatedHeaderBaseProps,
|
|
233
232
|
progressThreshold: progressThresholdValue,
|
|
234
233
|
scrollValues,
|
|
235
234
|
scrollToRef,
|
|
@@ -239,9 +238,7 @@ function HeaderMotionContextProvider<T extends string>({
|
|
|
239
238
|
originalHeaderHeight,
|
|
240
239
|
progress,
|
|
241
240
|
measureTotalHeight,
|
|
242
|
-
enableHeaderPan,
|
|
243
241
|
headerPanMomentumOffset,
|
|
244
|
-
animatedHeaderBaseProps,
|
|
245
242
|
setOrUpdateDynamicMeasurement,
|
|
246
243
|
scrollValues,
|
|
247
244
|
activeScrollId,
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
import { useMemo, type ReactElement } from 'react';
|
|
2
|
+
import { Platform } from 'react-native';
|
|
3
|
+
import {
|
|
4
|
+
Gesture,
|
|
5
|
+
GestureDetector,
|
|
6
|
+
GestureHandlerRootView,
|
|
7
|
+
} from 'react-native-gesture-handler';
|
|
8
|
+
import { useAnimatedReaction, withDecay } from 'react-native-reanimated';
|
|
9
|
+
import type {
|
|
10
|
+
HeaderPanDecayConfig,
|
|
11
|
+
HeaderPanDecayEvent,
|
|
12
|
+
HeaderMotionBridgeValue,
|
|
13
|
+
} from '../types';
|
|
14
|
+
|
|
15
|
+
const PLATFORM_PANNING_ENABLED = Platform.select({
|
|
16
|
+
default: true,
|
|
17
|
+
android: false,
|
|
18
|
+
});
|
|
19
|
+
|
|
20
|
+
type HeaderPanBoundaryProps = Pick<
|
|
21
|
+
HeaderMotionBridgeValue,
|
|
22
|
+
'scrollToRef' | 'headerPanMomentumOffset'
|
|
23
|
+
> & {
|
|
24
|
+
children: ReactElement;
|
|
25
|
+
pannable?: boolean;
|
|
26
|
+
panDecayConfig?: HeaderPanDecayConfig;
|
|
27
|
+
withGestureHandlerRootView?: boolean;
|
|
28
|
+
};
|
|
29
|
+
|
|
30
|
+
export function HeaderPanBoundary({
|
|
31
|
+
children,
|
|
32
|
+
pannable = false,
|
|
33
|
+
panDecayConfig,
|
|
34
|
+
scrollToRef,
|
|
35
|
+
headerPanMomentumOffset,
|
|
36
|
+
withGestureHandlerRootView = false,
|
|
37
|
+
}: HeaderPanBoundaryProps) {
|
|
38
|
+
useAnimatedReaction(
|
|
39
|
+
() => headerPanMomentumOffset.get(),
|
|
40
|
+
(offset, prevOffset) => {
|
|
41
|
+
if (offset !== null) {
|
|
42
|
+
const dy = offset - (prevOffset ?? 0);
|
|
43
|
+
scrollToRef.current?.(dy);
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
);
|
|
47
|
+
|
|
48
|
+
const isPanEnabled = PLATFORM_PANNING_ENABLED && pannable;
|
|
49
|
+
|
|
50
|
+
const pan = useMemo(
|
|
51
|
+
() =>
|
|
52
|
+
Gesture.Pan()
|
|
53
|
+
.enabled(isPanEnabled)
|
|
54
|
+
.onChange((e) => {
|
|
55
|
+
const dy = e.changeY;
|
|
56
|
+
scrollToRef.current?.(dy);
|
|
57
|
+
})
|
|
58
|
+
.onEnd((e) => {
|
|
59
|
+
const resolvedConfig = resolvePanDecayConfig(panDecayConfig, e);
|
|
60
|
+
headerPanMomentumOffset.set(
|
|
61
|
+
withDecay(resolvedConfig, () => headerPanMomentumOffset.set(null))
|
|
62
|
+
);
|
|
63
|
+
})
|
|
64
|
+
.shouldCancelWhenOutside(false),
|
|
65
|
+
[headerPanMomentumOffset, isPanEnabled, panDecayConfig, scrollToRef]
|
|
66
|
+
);
|
|
67
|
+
|
|
68
|
+
const content = <GestureDetector gesture={pan}>{children}</GestureDetector>;
|
|
69
|
+
|
|
70
|
+
if (!withGestureHandlerRootView) {
|
|
71
|
+
return content;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
return <GestureHandlerRootView>{content}</GestureHandlerRootView>;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
function resolvePanDecayConfig(
|
|
78
|
+
panDecayConfig: HeaderPanDecayConfig | undefined,
|
|
79
|
+
event: HeaderPanDecayEvent
|
|
80
|
+
) {
|
|
81
|
+
'worklet';
|
|
82
|
+
|
|
83
|
+
const resolvedConfig =
|
|
84
|
+
typeof panDecayConfig === 'function'
|
|
85
|
+
? panDecayConfig(event)
|
|
86
|
+
: panDecayConfig;
|
|
87
|
+
|
|
88
|
+
return {
|
|
89
|
+
...resolvedConfig,
|
|
90
|
+
velocity: resolvedConfig?.velocity ?? event.velocityY,
|
|
91
|
+
};
|
|
92
|
+
}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import type { ReactNode } from 'react';
|
|
2
|
+
import { HeaderMotionContext } from '../context';
|
|
3
|
+
import type { HeaderMotionBridgeValue } from '../types';
|
|
4
|
+
|
|
5
|
+
export interface HeaderMotionNavigationBridgeProps {
|
|
6
|
+
/**
|
|
7
|
+
* Previously captured HeaderMotion context value to re-provide in another
|
|
8
|
+
* subtree.
|
|
9
|
+
*/
|
|
10
|
+
value: HeaderMotionBridgeValue;
|
|
11
|
+
/** Subtree that should regain access to HeaderMotion context. */
|
|
12
|
+
children: ReactNode;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* Re-provides HeaderMotion context in a different part of the React tree.
|
|
17
|
+
*
|
|
18
|
+
* This is primarily useful for navigation libraries that render headers outside
|
|
19
|
+
* the screen subtree where `HeaderMotion` itself lives.
|
|
20
|
+
*/
|
|
21
|
+
export function NavigationBridge({
|
|
22
|
+
value,
|
|
23
|
+
children,
|
|
24
|
+
}: HeaderMotionNavigationBridgeProps) {
|
|
25
|
+
return (
|
|
26
|
+
<HeaderMotionContext.Provider value={value}>
|
|
27
|
+
{children}
|
|
28
|
+
</HeaderMotionContext.Provider>
|
|
29
|
+
);
|
|
30
|
+
}
|
|
@@ -12,23 +12,27 @@ export interface HeaderMotionScrollManagerProps<
|
|
|
12
12
|
TRef extends InstanceOrElement = any
|
|
13
13
|
> extends UseScrollManagerOptions<TRef> {
|
|
14
14
|
/**
|
|
15
|
-
*
|
|
16
|
-
*
|
|
15
|
+
* Unique identifier for this scrollable in multi-scroll setups.
|
|
16
|
+
*
|
|
17
|
+
* Omit it for single-scroll screens.
|
|
17
18
|
*/
|
|
18
19
|
scrollId?: string;
|
|
19
20
|
/**
|
|
20
|
-
* Render function that receives
|
|
21
|
-
*
|
|
21
|
+
* Render function that receives:
|
|
22
|
+
* - the props to spread onto your scrollable
|
|
23
|
+
* - the layout values needed to offset content below the header
|
|
22
24
|
*/
|
|
23
25
|
children: ScrollManagerRenderChildren<TRef>;
|
|
24
26
|
}
|
|
25
27
|
|
|
26
28
|
/**
|
|
27
|
-
*
|
|
28
|
-
* Must be used within a HeaderMotion component.
|
|
29
|
+
* Render-prop wrapper around `useScrollManager()`.
|
|
29
30
|
*
|
|
30
|
-
*
|
|
31
|
-
*
|
|
31
|
+
* **Most code should prefer `createHeaderMotionScrollable()` instead.**
|
|
32
|
+
*
|
|
33
|
+
* Use `ScrollManager` only when the factory approach is not enough and you
|
|
34
|
+
* still need HeaderMotion to manage a custom scrollable through render-prop
|
|
35
|
+
* composition.
|
|
32
36
|
*
|
|
33
37
|
* @example
|
|
34
38
|
* ```tsx
|
|
@@ -45,9 +49,7 @@ export interface HeaderMotionScrollManagerProps<
|
|
|
45
49
|
* </HeaderMotion>
|
|
46
50
|
* ```
|
|
47
51
|
*/
|
|
48
|
-
export function
|
|
49
|
-
TRef extends InstanceOrElement = any
|
|
50
|
-
>({
|
|
52
|
+
export function ScrollManager<TRef extends InstanceOrElement = any>({
|
|
51
53
|
children,
|
|
52
54
|
scrollId,
|
|
53
55
|
animatedRef,
|
|
@@ -55,6 +57,11 @@ export function HeaderMotionScrollManager<
|
|
|
55
57
|
refreshing,
|
|
56
58
|
onRefresh,
|
|
57
59
|
progressViewOffset,
|
|
60
|
+
onScroll,
|
|
61
|
+
onScrollBeginDrag,
|
|
62
|
+
onScrollEndDrag,
|
|
63
|
+
onMomentumScrollBegin,
|
|
64
|
+
onMomentumScrollEnd,
|
|
58
65
|
}: HeaderMotionScrollManagerProps<TRef>) {
|
|
59
66
|
if (typeof children !== 'function') {
|
|
60
67
|
throw new Error(
|
|
@@ -70,6 +77,11 @@ export function HeaderMotionScrollManager<
|
|
|
70
77
|
refreshing,
|
|
71
78
|
onRefresh,
|
|
72
79
|
progressViewOffset,
|
|
80
|
+
onScroll,
|
|
81
|
+
onScrollBeginDrag,
|
|
82
|
+
onScrollEndDrag,
|
|
83
|
+
onMomentumScrollBegin,
|
|
84
|
+
onMomentumScrollEnd,
|
|
73
85
|
}
|
|
74
86
|
);
|
|
75
87
|
|
|
@@ -1,21 +1,18 @@
|
|
|
1
|
+
import type { ReactElement } from 'react';
|
|
1
2
|
import Animated, {
|
|
2
|
-
type AnimatedRef,
|
|
3
3
|
type AnimatedScrollViewProps,
|
|
4
4
|
} from 'react-native-reanimated';
|
|
5
|
-
import {
|
|
5
|
+
import {
|
|
6
|
+
createHeaderMotionScrollable,
|
|
7
|
+
type HeaderMotionScrollableOwnProps,
|
|
8
|
+
} from './createHeaderMotionScrollable';
|
|
6
9
|
|
|
7
|
-
export type HeaderMotionScrollViewProps = AnimatedScrollViewProps &
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
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<Animated.ScrollView> | AnimatedRef;
|
|
18
|
-
};
|
|
10
|
+
export type HeaderMotionScrollViewProps = AnimatedScrollViewProps &
|
|
11
|
+
HeaderMotionScrollableOwnProps<Animated.ScrollView>;
|
|
12
|
+
|
|
13
|
+
type HeaderMotionScrollViewComponent = (
|
|
14
|
+
props: HeaderMotionScrollViewProps
|
|
15
|
+
) => ReactElement | null;
|
|
19
16
|
|
|
20
17
|
/**
|
|
21
18
|
* Animated ScrollView component that integrates with HeaderMotion.
|
|
@@ -31,49 +28,8 @@ export type HeaderMotionScrollViewProps = AnimatedScrollViewProps & {
|
|
|
31
28
|
* </HeaderMotion>
|
|
32
29
|
* ```
|
|
33
30
|
*/
|
|
34
|
-
export
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
refreshControl,
|
|
40
|
-
...props
|
|
41
|
-
}: HeaderMotionScrollViewProps) {
|
|
42
|
-
return (
|
|
43
|
-
<HeaderMotionScrollManager
|
|
44
|
-
scrollId={scrollId}
|
|
45
|
-
animatedRef={animatedRef as AnimatedRef<Animated.ScrollView>}
|
|
46
|
-
refreshControl={refreshControl}
|
|
47
|
-
>
|
|
48
|
-
{(
|
|
49
|
-
{
|
|
50
|
-
onScroll,
|
|
51
|
-
ref,
|
|
52
|
-
refreshControl: managedRefreshControl,
|
|
53
|
-
...scrollViewProps
|
|
54
|
-
},
|
|
55
|
-
{ originalHeaderHeight, minHeightContentContainerStyle }
|
|
56
|
-
) => (
|
|
57
|
-
<Animated.ScrollView
|
|
58
|
-
{...scrollViewProps}
|
|
59
|
-
{...props}
|
|
60
|
-
ref={ref}
|
|
61
|
-
onScroll={onScroll}
|
|
62
|
-
{...(managedRefreshControl && {
|
|
63
|
-
refreshControl: managedRefreshControl,
|
|
64
|
-
})}
|
|
65
|
-
>
|
|
66
|
-
<Animated.View
|
|
67
|
-
style={[
|
|
68
|
-
minHeightContentContainerStyle,
|
|
69
|
-
{ paddingTop: originalHeaderHeight },
|
|
70
|
-
contentContainerStyle,
|
|
71
|
-
]}
|
|
72
|
-
>
|
|
73
|
-
{children}
|
|
74
|
-
</Animated.View>
|
|
75
|
-
</Animated.ScrollView>
|
|
76
|
-
)}
|
|
77
|
-
</HeaderMotionScrollManager>
|
|
78
|
-
);
|
|
79
|
-
}
|
|
31
|
+
export const ScrollView = createHeaderMotionScrollable(Animated.ScrollView, {
|
|
32
|
+
displayName: 'HeaderMotion.ScrollView',
|
|
33
|
+
contentContainerMode: 'children',
|
|
34
|
+
isComponentAnimated: true,
|
|
35
|
+
}) as HeaderMotionScrollViewComponent;
|