react-native-header-motion 0.1.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.
Files changed (90) hide show
  1. package/LICENSE +20 -0
  2. package/README.md +479 -0
  3. package/lib/module/components/FlatList.js +64 -0
  4. package/lib/module/components/FlatList.js.map +1 -0
  5. package/lib/module/components/Header.js +19 -0
  6. package/lib/module/components/Header.js.map +1 -0
  7. package/lib/module/components/HeaderBase.js +59 -0
  8. package/lib/module/components/HeaderBase.js.map +1 -0
  9. package/lib/module/components/HeaderMotion.js +84 -0
  10. package/lib/module/components/HeaderMotion.js.map +1 -0
  11. package/lib/module/components/ScrollManager.js +39 -0
  12. package/lib/module/components/ScrollManager.js.map +1 -0
  13. package/lib/module/components/ScrollView.js +47 -0
  14. package/lib/module/components/ScrollView.js.map +1 -0
  15. package/lib/module/components/index.js +9 -0
  16. package/lib/module/components/index.js.map +1 -0
  17. package/lib/module/context.js +5 -0
  18. package/lib/module/context.js.map +1 -0
  19. package/lib/module/hooks/index.js +6 -0
  20. package/lib/module/hooks/index.js.map +1 -0
  21. package/lib/module/hooks/useActiveScrollId.js +47 -0
  22. package/lib/module/hooks/useActiveScrollId.js.map +1 -0
  23. package/lib/module/hooks/useMotionProgress.js +58 -0
  24. package/lib/module/hooks/useMotionProgress.js.map +1 -0
  25. package/lib/module/hooks/useScrollManager.js +150 -0
  26. package/lib/module/hooks/useScrollManager.js.map +1 -0
  27. package/lib/module/index.js +42 -0
  28. package/lib/module/index.js.map +1 -0
  29. package/lib/module/package.json +1 -0
  30. package/lib/module/types.js +4 -0
  31. package/lib/module/types.js.map +1 -0
  32. package/lib/module/utils/defaults.js +10 -0
  33. package/lib/module/utils/defaults.js.map +1 -0
  34. package/lib/module/utils/index.js +5 -0
  35. package/lib/module/utils/index.js.map +1 -0
  36. package/lib/module/utils/values.js +11 -0
  37. package/lib/module/utils/values.js.map +1 -0
  38. package/lib/typescript/package.json +1 -0
  39. package/lib/typescript/src/components/FlatList.d.ts +30 -0
  40. package/lib/typescript/src/components/FlatList.d.ts.map +1 -0
  41. package/lib/typescript/src/components/Header.d.ts +19 -0
  42. package/lib/typescript/src/components/Header.d.ts.map +1 -0
  43. package/lib/typescript/src/components/HeaderBase.d.ts +34 -0
  44. package/lib/typescript/src/components/HeaderBase.d.ts.map +1 -0
  45. package/lib/typescript/src/components/HeaderMotion.d.ts +52 -0
  46. package/lib/typescript/src/components/HeaderMotion.d.ts.map +1 -0
  47. package/lib/typescript/src/components/ScrollManager.d.ts +40 -0
  48. package/lib/typescript/src/components/ScrollManager.d.ts.map +1 -0
  49. package/lib/typescript/src/components/ScrollView.d.ts +24 -0
  50. package/lib/typescript/src/components/ScrollView.d.ts.map +1 -0
  51. package/lib/typescript/src/components/index.d.ts +7 -0
  52. package/lib/typescript/src/components/index.d.ts.map +1 -0
  53. package/lib/typescript/src/context.d.ts +14 -0
  54. package/lib/typescript/src/context.d.ts.map +1 -0
  55. package/lib/typescript/src/hooks/index.d.ts +4 -0
  56. package/lib/typescript/src/hooks/index.d.ts.map +1 -0
  57. package/lib/typescript/src/hooks/useActiveScrollId.d.ts +32 -0
  58. package/lib/typescript/src/hooks/useActiveScrollId.d.ts.map +1 -0
  59. package/lib/typescript/src/hooks/useMotionProgress.d.ts +38 -0
  60. package/lib/typescript/src/hooks/useMotionProgress.d.ts.map +1 -0
  61. package/lib/typescript/src/hooks/useScrollManager.d.ts +37 -0
  62. package/lib/typescript/src/hooks/useScrollManager.d.ts.map +1 -0
  63. package/lib/typescript/src/index.d.ts +51 -0
  64. package/lib/typescript/src/index.d.ts.map +1 -0
  65. package/lib/typescript/src/types.d.ts +43 -0
  66. package/lib/typescript/src/types.d.ts.map +1 -0
  67. package/lib/typescript/src/utils/defaults.d.ts +6 -0
  68. package/lib/typescript/src/utils/defaults.d.ts.map +1 -0
  69. package/lib/typescript/src/utils/index.d.ts +3 -0
  70. package/lib/typescript/src/utils/index.d.ts.map +1 -0
  71. package/lib/typescript/src/utils/values.d.ts +3 -0
  72. package/lib/typescript/src/utils/values.d.ts.map +1 -0
  73. package/package.json +164 -0
  74. package/src/components/FlatList.tsx +72 -0
  75. package/src/components/Header.tsx +30 -0
  76. package/src/components/HeaderBase.tsx +51 -0
  77. package/src/components/HeaderMotion.tsx +183 -0
  78. package/src/components/ScrollManager.tsx +58 -0
  79. package/src/components/ScrollView.tsx +58 -0
  80. package/src/components/index.ts +6 -0
  81. package/src/context.ts +20 -0
  82. package/src/hooks/index.ts +3 -0
  83. package/src/hooks/useActiveScrollId.ts +59 -0
  84. package/src/hooks/useMotionProgress.ts +56 -0
  85. package/src/hooks/useScrollManager.ts +186 -0
  86. package/src/index.ts +76 -0
  87. package/src/types.ts +62 -0
  88. package/src/utils/defaults.ts +16 -0
  89. package/src/utils/index.ts +2 -0
  90. package/src/utils/values.ts +6 -0
package/LICENSE ADDED
@@ -0,0 +1,20 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2025 Oskar Pawica
4
+ Permission is hereby granted, free of charge, to any person obtaining a copy
5
+ of this software and associated documentation files (the "Software"), to deal
6
+ in the Software without restriction, including without limitation the rights
7
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8
+ copies of the Software, and to permit persons to whom the Software is
9
+ furnished to do so, subject to the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be included in all
12
+ copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
20
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,479 @@
1
+ # React Native Header Motion
2
+
3
+ High-level APIs for **orchestrating header motion** driven by scroll — built on top of [**React Native Reanimated**](https://docs.swmansion.com/react-native-reanimated/).
4
+
5
+ This library is **100% a wrapper around Reanimated**. All the credit for the underlying animation engine, worklets, and primitives goes to **Reanimated** (and `react-native-worklets`). This package focuses on a specific use case: **header motion + scroll orchestration** (including multi-scroll/tab scenarios).
6
+
7
+ ## What this is (and isn’t)
8
+
9
+ **✅ This is**
10
+
11
+ - A small set of components + hooks that expose a single `progress` shared value and a few measurement helpers.
12
+ - A scroll orchestration layer that can keep multiple scrollables in sync (e.g. tabs + pager).
13
+
14
+ **❌ This is NOT**
15
+
16
+ - An out-of-the-box “collapsible header” component with a baked-in look.
17
+
18
+ You build any header motion you want by animating based on `progress`.
19
+
20
+ ## Requirements (peer dependencies)
21
+
22
+ You must have these installed in your app:
23
+
24
+ - `react-native-reanimated` **>= 4.0.0**
25
+ - `react-native-worklets` **>= 0.4.0**
26
+
27
+ This package declares them as peer dependencies, so your app owns those versions. Remember to install a version of Worklets compatible with your version of Reanimated.
28
+
29
+ ## Installation
30
+
31
+ ```bash
32
+ npm i react-native-header-motion
33
+ ```
34
+
35
+ or
36
+
37
+ ```bash
38
+ yarn add react-native-header-motion
39
+ ```
40
+
41
+ ### Reanimated setup
42
+
43
+ Follow the official Reanimated [installation instructions](https://docs.swmansion.com/react-native-reanimated/docs/fundamentals/getting-started/#installation) for your environment (Expo / bare RN).
44
+
45
+ ## Mental model
46
+
47
+ There are three key concepts:
48
+
49
+ ### 1) `progress` (SharedValue)
50
+
51
+ `progress` is a Reanimated `SharedValue<number>` that represents the normalized progress of your header animation.
52
+
53
+ - `0` → animation start (initial state)
54
+ - `1` → animation end (final state)
55
+
56
+ ### 2) `progressThreshold`
57
+
58
+ `progressThreshold` is the distance needed for `progress` to move from `0 → 1`.
59
+
60
+ You can provide it as:
61
+
62
+ - a number, or
63
+ - a function `(measuredDynamic) => threshold`
64
+
65
+ If you provide a function, it uses the value measured by `measureDynamic`.
66
+
67
+ ### 3) Measurement functions
68
+
69
+ The library gives you two measurement callbacks that you pass to your header layout:
70
+
71
+ - `measureTotalHeight` – attach to the _outer_ header container to measure the total header height. Scrollables use this to add `paddingTop` so content starts below the header.
72
+ - `measureDynamic` – attach to the part of the header that determines the threshold (often the animated/dynamic portion).
73
+
74
+ ## Why `HeaderMotion.Header` exists
75
+
76
+ When you pass a `header` component to React Navigation / Expo Router, that header is rendered by the navigator in a different part of the React tree.
77
+
78
+ Because of that, the navigation header **cannot read the `HeaderMotion` context**, so calling `useMotionProgress()` inside that header would throw.
79
+
80
+ `HeaderMotion.Header` solves this by acting as a **bridge**: it runs inside the provider, reads context, and passes the values to your navigation header via a render function.
81
+
82
+ ## Why `HeaderBase` / `AnimatedHeaderBase` uses absolute positioning
83
+
84
+ Navigation headers are special:
85
+
86
+ - Even with `headerTransparent: true`, the navigator can still reserve layout space for the header container.
87
+ - If you animate with translations without absolute positioning, you can end up with:
88
+ - content below becoming unclickable (an invisible parent header still sits on top), or
89
+ - content hidden under the header container.
90
+
91
+ `HeaderBase` and `AnimatedHeaderBase` are **absolutely positioned** to avoid those layout traps, which is especially important when you use transforms/translations.
92
+
93
+ ## When to use components vs hooks
94
+
95
+ You can use either style; pick based on your integration needs:
96
+
97
+ - Prefer **components** when you want a “batteries included” wiring:
98
+
99
+ - `HeaderMotion.ScrollView` / `HeaderMotion.FlatList` for common scrollables
100
+ - `HeaderMotion.ScrollManager` for custom scrollables via render-props
101
+
102
+ - Prefer **hooks** when you want to build your own wrappers:
103
+ - `useScrollManager()` (same engine as `HeaderMotion.ScrollManager`, but hook-based)
104
+ - `useMotionProgress()` when your header is inside the provider tree
105
+
106
+ Also:
107
+
108
+ - Use `HeaderMotion.Header` when your header is rendered by navigation.
109
+ - Use `useMotionProgress` when your header is rendered inside the same tree as `HeaderMotion`.
110
+
111
+ ## Examples
112
+
113
+ ### Example app
114
+
115
+ Examples live in the example app: `example/`. They demonstrate a few cases, from simple animations, to scroll orchestration and persisted header animation state between different tabs (e.g. with `react-native-pager-view`).
116
+
117
+ Those examples use Expo Router as the navigation library, but it should be fairly simple to do the same with plain React Navigation.
118
+
119
+ ### Expo Router
120
+
121
+ This is the core pattern used in the example app (`example/src/app/simple.tsx`).
122
+
123
+ ```tsx
124
+ import HeaderMotion, {
125
+ AnimatedHeaderBase,
126
+ type WithCollapsibleHeaderProps,
127
+ } from 'react-native-header-motion';
128
+ import { Stack } from 'expo-router';
129
+ import Animated, {
130
+ Extrapolation,
131
+ interpolate,
132
+ useAnimatedStyle,
133
+ } from 'react-native-reanimated';
134
+ import { useSafeAreaInsets } from 'react-native-safe-area-context';
135
+ import { View } from 'react-native';
136
+
137
+ export default function Screen() {
138
+ return (
139
+ <HeaderMotion>
140
+ <HeaderMotion.Header>
141
+ {(headerProps) => (
142
+ <Stack.Screen
143
+ options={{
144
+ header: () => <MyHeader {...headerProps} />,
145
+ }}
146
+ />
147
+ )}
148
+ </HeaderMotion.Header>
149
+
150
+ <HeaderMotion.ScrollView>
151
+ {/* your scrollable content */}
152
+ </HeaderMotion.ScrollView>
153
+ </HeaderMotion>
154
+ );
155
+ }
156
+
157
+ function MyHeader({
158
+ progress,
159
+ measureTotalHeight,
160
+ measureDynamic,
161
+ progressThreshold,
162
+ }: WithCollapsibleHeaderProps) {
163
+ const insets = useSafeAreaInsets();
164
+
165
+ const containerStyle = useAnimatedStyle(() => {
166
+ const translateY = interpolate(
167
+ progress.value,
168
+ [0, 1],
169
+ [0, -progressThreshold],
170
+ Extrapolation.CLAMP
171
+ );
172
+ return { transform: [{ translateY }] };
173
+ });
174
+
175
+ return (
176
+ <AnimatedHeaderBase
177
+ onLayout={measureTotalHeight}
178
+ style={[{ paddingTop: insets.top }, containerStyle]}
179
+ >
180
+ <Animated.View onLayout={measureDynamic}>
181
+ {/* “dynamic” part of the header */}
182
+ </Animated.View>
183
+
184
+ <View>{/* "regular" part of the header */}</View>
185
+ </AnimatedHeaderBase>
186
+ );
187
+ }
188
+ ```
189
+
190
+ ### React Navigation
191
+
192
+ In React Navigation you typically configure headers via `navigation.setOptions()`.
193
+
194
+ Important: the header itself can’t call `useMotionProgress()`, so we still use `HeaderMotion.Header` as a bridge.
195
+
196
+ ```tsx
197
+ import React from 'react';
198
+ import HeaderMotion, {
199
+ AnimatedHeaderBase,
200
+ type WithCollapsibleHeaderProps,
201
+ } from 'react-native-header-motion';
202
+ import { useNavigation } from '@react-navigation/native';
203
+ import Animated, {
204
+ Extrapolation,
205
+ interpolate,
206
+ useAnimatedStyle,
207
+ } from 'react-native-reanimated';
208
+ import { View } from 'react-native';
209
+
210
+ export function MyScreen() {
211
+ return (
212
+ <HeaderMotion>
213
+ <HeaderMotion.Header>
214
+ {(headerProps) => (
215
+ <NavigationHeaderInstaller headerProps={headerProps} />
216
+ )}
217
+ </HeaderMotion.Header>
218
+ <HeaderMotion.ScrollView>{/* content */}</HeaderMotion.ScrollView>
219
+ </HeaderMotion>
220
+ );
221
+ }
222
+
223
+ function NavigationHeaderInstaller({
224
+ headerProps,
225
+ }: {
226
+ headerProps: WithCollapsibleHeaderProps;
227
+ }) {
228
+ const navigation = useNavigation();
229
+
230
+ React.useLayoutEffect(() => {
231
+ navigation.setOptions({
232
+ header: () => <MyHeader {...headerProps} />,
233
+ });
234
+ }, [navigation, headerProps]);
235
+
236
+ return null;
237
+ }
238
+
239
+ function MyHeader({
240
+ progress,
241
+ measureTotalHeight,
242
+ measureDynamic,
243
+ progressThreshold,
244
+ }: WithCollapsibleHeaderProps) {
245
+ const insets = useSafeAreaInsets();
246
+
247
+ const containerStyle = useAnimatedStyle(() => {
248
+ const translateY = interpolate(
249
+ progress.value,
250
+ [0, 1],
251
+ [0, -progressThreshold],
252
+ Extrapolation.CLAMP
253
+ );
254
+ return { transform: [{ translateY }] };
255
+ });
256
+
257
+ return (
258
+ <AnimatedHeaderBase
259
+ onLayout={measureTotalHeight}
260
+ style={[{ paddingTop: insets.top }, containerStyle]}
261
+ >
262
+ <Animated.View onLayout={measureDynamic}>
263
+ {/* “dynamic” part of the header */}
264
+ </Animated.View>
265
+
266
+ <View>{/* "regular" part of the header */}</View>
267
+ </AnimatedHeaderBase>
268
+ );
269
+ }
270
+ ```
271
+
272
+ ### Tabs / pager: synchronizing multiple scrollables
273
+
274
+ If you have multiple scrollables (e.g. pages in `react-native-pager-view`), you can keep a single header progress by:
275
+
276
+ 1. Creating a shared “active scroll id” using `useActiveScrollId()`
277
+ 2. Passing `activeScrollId.sv` to `<HeaderMotion activeScrollId={...} />`
278
+ 3. Rendering each page scrollable with a unique `scrollId`
279
+
280
+ The example app shows this pattern in `example/src/app/collapsible-pager.tsx` using `HeaderMotion.ScrollManager`.
281
+
282
+ ### Keeping the native header (back button/title) + custom animated header below
283
+
284
+ Sometimes you want to keep the native navigation header for back buttons + title, but still animate a custom header section below it.
285
+
286
+ In that case:
287
+
288
+ - set `headerTransparent: true`
289
+ - do **not** provide a custom `header` component
290
+ - render your animated header content _inside the screen_ under the native header
291
+
292
+ Sketch:
293
+
294
+ ```tsx
295
+ import HeaderMotion, {
296
+ AnimatedHeaderBase,
297
+ useMotionProgress,
298
+ } from 'react-native-header-motion';
299
+ import { Stack } from 'expo-router';
300
+ import Animated, {
301
+ Extrapolation,
302
+ interpolate,
303
+ useAnimatedStyle,
304
+ } from 'react-native-reanimated';
305
+ import { View } from 'react-native';
306
+
307
+ export default function Screen() {
308
+ return (
309
+ <>
310
+ <Stack.Screen options={{ headerTransparent: true }} />
311
+ <HeaderMotion>
312
+ <InlineAnimatedHeader />
313
+ <HeaderMotion.ScrollView>
314
+ {/* rest of content */}
315
+ </HeaderMotion.ScrollView>
316
+ </HeaderMotion>
317
+ </>
318
+ );
319
+ }
320
+
321
+ function InlineAnimatedHeader() {
322
+ const { progress, measureTotalHeight, measureDynamic, progressThreshold } =
323
+ useMotionProgress();
324
+
325
+ const containerStyle = useAnimatedStyle(() => {
326
+ const translateY = interpolate(
327
+ progress.value,
328
+ [0, 1],
329
+ [0, -progressThreshold],
330
+ Extrapolation.CLAMP
331
+ );
332
+ return { transform: [{ translateY }] };
333
+ });
334
+
335
+ return (
336
+ <AnimatedHeaderBase onLayout={measureTotalHeight} style={containerStyle}>
337
+ <Animated.View onLayout={measureDynamic}>
338
+ {/* custom animated header content below the native header */}
339
+ </Animated.View>
340
+ <View>{/* sticky part */}</View>
341
+ </AnimatedHeaderBase>
342
+ );
343
+ }
344
+ ```
345
+
346
+ ## API
347
+
348
+ The package exports a default compound component plus hooks, types, and a couple base components.
349
+
350
+ ### `HeaderMotion` (default export)
351
+
352
+ `HeaderMotion` is a compound component:
353
+
354
+ - `HeaderMotion` (provider)
355
+ - `HeaderMotion.Header` (bridge for navigation headers)
356
+ - `HeaderMotion.ScrollView` (pre-wired Animated.ScrollView)
357
+ - `HeaderMotion.FlatList` (pre-wired Animated.FlatList)
358
+ - `HeaderMotion.ScrollManager` (render-prop API for custom scrollables)
359
+
360
+ #### Props
361
+
362
+ - `progressThreshold?: number | (measuredDynamic: number) => number`
363
+ - Defines how many pixels correspond to `progress` going from `0` to `1`.
364
+ - If you pass a function, it uses the value measured from `measureDynamic`.
365
+ - `measureDynamic?: (e) => number`
366
+ - What value to read from the `onLayout` event (defaults to `height`).
367
+ - `measureDynamicMode?: 'mount' | 'update'`
368
+ - Whether `measureDynamic` updates only once or on every layout recalculation.
369
+ - `activeScrollId?: SharedValue<string>`
370
+ - Enables multi-scroll orchestration (tabs/pager).
371
+ - `progressExtrapolation?: ExtrapolationType`
372
+ - Controls how progress behaves outside the threshold range (useful for overscroll).
373
+
374
+ #### `HeaderMotion.Header`
375
+
376
+ Render-prop component that passes motion progress props to a header you render via navigation.
377
+
378
+ ```tsx
379
+ <HeaderMotion.Header>
380
+ {(headerProps) => /* pass headerProps into navigation header */}
381
+ </HeaderMotion.Header>
382
+ ```
383
+
384
+ Use this instead of `useMotionProgress()` when your header is rendered by React Navigation / Expo Router.
385
+
386
+ #### `HeaderMotion.ScrollView`
387
+
388
+ Animated ScrollView wired with:
389
+
390
+ - `onScroll` handler
391
+ - `ref`
392
+ - automatic `paddingTop` based on measured header height
393
+
394
+ Supports `scrollId?: string` for multi-scroll scenarios.
395
+
396
+ #### `HeaderMotion.FlatList`
397
+
398
+ Animated FlatList wired similarly to the ScrollView.
399
+
400
+ Supports `scrollId?: string` for multi-scroll scenarios.
401
+
402
+ #### `HeaderMotion.ScrollManager`
403
+
404
+ Render-prop API for custom scrollables (pager pages, 3rd party lists, etc.).
405
+
406
+ ```tsx
407
+ <HeaderMotion.ScrollManager scrollId="A">
408
+ {(
409
+ scrollableProps,
410
+ { originalHeaderHeight, minHeightContentContainerStyle }
411
+ ) => (
412
+ <Animated.ScrollView
413
+ {...scrollableProps}
414
+ contentContainerStyle={[
415
+ minHeightContentContainerStyle,
416
+ { paddingTop: originalHeaderHeight },
417
+ ]}
418
+ />
419
+ )}
420
+ </HeaderMotion.ScrollManager>
421
+ ```
422
+
423
+ ### Hooks
424
+
425
+ #### `useMotionProgress()`
426
+
427
+ Returns:
428
+
429
+ - `progress` (`SharedValue<number>`)
430
+ - `progressThreshold` (`number`)
431
+ - `measureTotalHeight` (`onLayout` callback)
432
+ - `measureDynamic` (`onLayout` callback)
433
+
434
+ Only use inside the `HeaderMotion` provider tree.
435
+
436
+ #### `useScrollManager(scrollId?)`
437
+
438
+ Lower-level orchestration hook that powers the component APIs. Returns:
439
+
440
+ - `scrollableProps`: `{ onScroll, scrollEventThrottle, ref }`
441
+ - `headerMotionContext`:
442
+ - `originalHeaderHeight`
443
+ - `minHeightContentContainerStyle` (helps when content is shorter than the threshold)
444
+
445
+ #### `useActiveScrollId(initialId)`
446
+
447
+ Helper for multi-scroll scenarios (tabs/pager). Returns:
448
+
449
+ - `[active, setActive]`
450
+ - `active.state` (React state)
451
+ - `active.sv` (SharedValue)
452
+
453
+ ### Base components
454
+
455
+ #### `HeaderBase`
456
+
457
+ Non-animated absolutely positioned header base.
458
+
459
+ #### `AnimatedHeaderBase`
460
+
461
+ Reanimated-powered, absolutely positioned header base.
462
+
463
+ ### Types
464
+
465
+ - `WithCollapsibleHeaderProps` – convenience type for headers using motion progress props.
466
+ - `WithCollapsiblePagedHeaderProps` – like above, plus `activeTab` and `onTabChange`.
467
+
468
+ ## Contributing
469
+
470
+ - Development workflow: see [CONTRIBUTING.md](CONTRIBUTING.md)
471
+ - Code of conduct: see [CODE_OF_CONDUCT.md](CODE_OF_CONDUCT.md)
472
+
473
+ ## License
474
+
475
+ MIT
476
+
477
+ ---
478
+
479
+ Made with [create-react-native-library](https://github.com/callstack/react-native-builder-bob)
@@ -0,0 +1,64 @@
1
+ "use strict";
2
+
3
+ import { forwardRef } from 'react';
4
+ import { ScrollView } from 'react-native';
5
+ import Animated from 'react-native-reanimated';
6
+ import { HeaderMotionScrollManager } from "./ScrollManager.js";
7
+ import { jsx as _jsx } from "react/jsx-runtime";
8
+ /**
9
+ * Animated FlatList component that integrates with HeaderMotion.
10
+ * Automatically handles scroll tracking and header animation synchronization.
11
+ * Must be used within a HeaderMotion component.
12
+ *
13
+ * @template T - The type of items in the FlatList
14
+ *
15
+ * @example
16
+ * ```tsx
17
+ * <HeaderMotion>
18
+ * <HeaderMotion.FlatList
19
+ * data={items}
20
+ * renderItem={({ item }) => <Text>{item}</Text>}
21
+ * />
22
+ * </HeaderMotion>
23
+ * ```
24
+ */
25
+ export function HeaderMotionFlatList({
26
+ scrollId,
27
+ ...props
28
+ }) {
29
+ return /*#__PURE__*/_jsx(HeaderMotionScrollManager, {
30
+ scrollId: scrollId,
31
+ children: ({
32
+ onScroll,
33
+ ...scrollViewProps
34
+ }, {
35
+ originalHeaderHeight,
36
+ minHeightContentContainerStyle
37
+ }) => /*#__PURE__*/_jsx(Animated.FlatList, {
38
+ ...scrollViewProps,
39
+ ...props,
40
+ onScroll: onScroll,
41
+ renderScrollComponent: propsz => /*#__PURE__*/_jsx(AnimatedScrollContainer, {
42
+ ...propsz
43
+ }),
44
+ contentContainerStyle: [minHeightContentContainerStyle, {
45
+ paddingTop: originalHeaderHeight
46
+ }, props.contentContainerStyle]
47
+ })
48
+ });
49
+ }
50
+ const AnimatedScrollContainer = /*#__PURE__*/forwardRef(({
51
+ children,
52
+ contentContainerStyle,
53
+ ...rest
54
+ }, ref) => {
55
+ return /*#__PURE__*/_jsx(ScrollView, {
56
+ ...rest,
57
+ ref: ref,
58
+ children: /*#__PURE__*/_jsx(Animated.View, {
59
+ style: contentContainerStyle,
60
+ children: children
61
+ })
62
+ });
63
+ });
64
+ //# sourceMappingURL=FlatList.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"names":["forwardRef","ScrollView","Animated","HeaderMotionScrollManager","jsx","_jsx","HeaderMotionFlatList","scrollId","props","children","onScroll","scrollViewProps","originalHeaderHeight","minHeightContentContainerStyle","FlatList","renderScrollComponent","propsz","AnimatedScrollContainer","contentContainerStyle","paddingTop","rest","ref","View","style"],"sourceRoot":"../../../src","sources":["components/FlatList.tsx"],"mappings":";;AAAA,SAASA,UAAU,QAAgD,OAAO;AAC1E,SAASC,UAAU,QAA8B,cAAc;AAC/D,OAAOC,QAAQ,MAAM,yBAAyB;AAC9C,SAASC,yBAAyB,QAAQ,oBAAiB;AAAC,SAAAC,GAAA,IAAAC,IAAA;AAc5D;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,OAAO,SAASC,oBAAoBA,CAAU;EAC5CC,QAAQ;EACR,GAAGC;AACyB,CAAC,EAAE;EAC/B,oBACEH,IAAA,CAACF,yBAAyB;IAACI,QAAQ,EAAEA,QAAS;IAAAE,QAAA,EAC3CA,CACC;MAAEC,QAAQ;MAAE,GAAGC;IAAgB,CAAC,EAChC;MAAEC,oBAAoB;MAAEC;IAA+B,CAAC,kBAExDR,IAAA,CAACH,QAAQ,CAACY,QAAQ;MAAA,GACZH,eAAe;MAAA,GACfH,KAAK;MACTE,QAAQ,EAAEA,QAAS;MACnBK,qBAAqB,EAAGC,MAAM,iBAC5BX,IAAA,CAACY,uBAAuB;QAAA,GAAKD;MAAM,CAAG,CACtC;MACFE,qBAAqB,EAAE,CACrBL,8BAA8B,EAC9B;QAAEM,UAAU,EAAEP;MAAqB,CAAC,EACpCJ,KAAK,CAACU,qBAAqB;IAC3B,CACH;EACF,CACwB,CAAC;AAEhC;AAEA,MAAMD,uBAAuB,gBAAGjB,UAAU,CAGxC,CAAC;EAAES,QAAQ;EAAES,qBAAqB;EAAE,GAAGE;AAAK,CAAC,EAAEC,GAAG,KAAK;EACvD,oBACEhB,IAAA,CAACJ,UAAU;IAAA,GAAKmB,IAAI;IAAEC,GAAG,EAAEA,GAAI;IAAAZ,QAAA,eAC7BJ,IAAA,CAACH,QAAQ,CAACoB,IAAI;MAACC,KAAK,EAAEL,qBAAsB;MAAAT,QAAA,EAAEA;IAAQ,CAAgB;EAAC,CAC7D,CAAC;AAEjB,CAAC,CAAC","ignoreList":[]}
@@ -0,0 +1,19 @@
1
+ "use strict";
2
+
3
+ import { useMotionProgress } from "../hooks/useMotionProgress.js";
4
+ /**
5
+ * Header component for providing motion progress properties to animated headers.
6
+ * Must be used within a HeaderMotion component.
7
+ *
8
+ * Use to pass props to the header components in React Navigation / Expo Router, which cannot access HeaderMotion's context and `useMotionProgress` otherwise.`
9
+ */
10
+ export function HeaderMotionHeader({
11
+ children
12
+ }) {
13
+ if (typeof children !== 'function') {
14
+ throw new Error('HeaderMotion.Header only accepts render function as the only child.');
15
+ }
16
+ const motionProgressProps = useMotionProgress();
17
+ return children(motionProgressProps);
18
+ }
19
+ //# sourceMappingURL=Header.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"names":["useMotionProgress","HeaderMotionHeader","children","Error","motionProgressProps"],"sourceRoot":"../../../src","sources":["components/Header.tsx"],"mappings":";;AAAA,SAASA,iBAAiB,QAAQ,+BAA4B;AAc9D;AACA;AACA;AACA;AACA;AACA;AACA,OAAO,SAASC,kBAAkBA,CAAC;EAAEC;AAAkC,CAAC,EAAE;EACxE,IAAI,OAAOA,QAAQ,KAAK,UAAU,EAAE;IAClC,MAAM,IAAIC,KAAK,CACb,qEACF,CAAC;EACH;EAEA,MAAMC,mBAAmB,GAAGJ,iBAAiB,CAAC,CAAC;EAC/C,OAAOE,QAAQ,CAACE,mBAAmB,CAAC;AACtC","ignoreList":[]}
@@ -0,0 +1,59 @@
1
+ "use strict";
2
+
3
+ import { StyleSheet, View } from 'react-native';
4
+ import Animated from 'react-native-reanimated';
5
+ import { jsx as _jsx } from "react/jsx-runtime";
6
+ /**
7
+ * Base header component with absolute positioning.
8
+ * Provides a foundation for building headers that need to be positioned absolutely.
9
+ *
10
+ * @example
11
+ * ```tsx
12
+ * <HeaderBase
13
+ * onLayout={measureTotalHeight}
14
+ * >
15
+ * ...
16
+ * </HeaderBase>
17
+ * ```
18
+ */
19
+ export function HeaderBase({
20
+ style,
21
+ ...rest
22
+ }) {
23
+ return /*#__PURE__*/_jsx(View, {
24
+ style: [style, styles.container],
25
+ ...rest
26
+ });
27
+ }
28
+
29
+ /**
30
+ * Animated version of HeaderBase using Reanimated's Animated.View.
31
+ * Use this when you need to animate the header based on scroll progress.
32
+ *
33
+ * @example
34
+ * ```tsx
35
+ * <AnimatedHeaderBase
36
+ * onLayout={measureTotalHeight}
37
+ * style={[{ paddingTop: insets.top }, animatedStyle]}
38
+ * >
39
+ * ...
40
+ * </AnimatedHeaderBase>
41
+ * ```
42
+ */
43
+ export function AnimatedHeaderBase({
44
+ style,
45
+ ...rest
46
+ }) {
47
+ return /*#__PURE__*/_jsx(Animated.View, {
48
+ style: [style, styles.container],
49
+ ...rest
50
+ });
51
+ }
52
+ const styles = StyleSheet.create({
53
+ container: {
54
+ position: 'absolute',
55
+ left: 0,
56
+ right: 0
57
+ }
58
+ });
59
+ //# sourceMappingURL=HeaderBase.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"names":["StyleSheet","View","Animated","jsx","_jsx","HeaderBase","style","rest","styles","container","AnimatedHeaderBase","create","position","left","right"],"sourceRoot":"../../../src","sources":["components/HeaderBase.tsx"],"mappings":";;AAAA,SAASA,UAAU,EAAEC,IAAI,QAAwB,cAAc;AAC/D,OAAOC,QAAQ,MAA8B,yBAAyB;AAAC,SAAAC,GAAA,IAAAC,IAAA;AAKvE;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,OAAO,SAASC,UAAUA,CAAC;EAAEC,KAAK;EAAE,GAAGC;AAAsB,CAAC,EAAE;EAC9D,oBAAOH,IAAA,CAACH,IAAI;IAACK,KAAK,EAAE,CAACA,KAAK,EAAEE,MAAM,CAACC,SAAS,CAAE;IAAA,GAAKF;EAAI,CAAG,CAAC;AAC7D;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,OAAO,SAASG,kBAAkBA,CAAC;EACjCJ,KAAK;EACL,GAAGC;AACoB,CAAC,EAAE;EAC1B,oBAAOH,IAAA,CAACF,QAAQ,CAACD,IAAI;IAACK,KAAK,EAAE,CAACA,KAAK,EAAEE,MAAM,CAACC,SAAS,CAAE;IAAA,GAAKF;EAAI,CAAG,CAAC;AACtE;AAEA,MAAMC,MAAM,GAAGR,UAAU,CAACW,MAAM,CAAC;EAC/BF,SAAS,EAAE;IACTG,QAAQ,EAAE,UAAU;IACpBC,IAAI,EAAE,CAAC;IACPC,KAAK,EAAE;EACT;AACF,CAAC,CAAC","ignoreList":[]}