react-native-header-motion 1.0.0-beta.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 +44 -522
- 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/utils/refreshControl.d.ts +12 -12
- package/package.json +6 -5
package/README.md
CHANGED
|
@@ -1,172 +1,69 @@
|
|
|
1
1
|
# React Native Header Motion
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
Scroll-driven animated headers for React Native.
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
React Native Header Motion gives you the plumbing for collapsible, progress-driven headers without forcing a prebuilt UI on you. It measures the header, derives a shared `progress` value, keeps multiple scrollables in sync, and bridges that state into navigation-rendered headers when needed.
|
|
6
6
|
|
|
7
|
-
|
|
8
|
-
- [React Native Gesture Handler](https://docs.swmansion.com/react-native-gesture-handler/docs/)
|
|
9
|
-
|
|
10
|
-
All credit for the underlying animation engine, worklets, gestures, and low-level primitives goes to those libraries. This package focuses on composing them into a specific higher-level use case: header motion and scroll orchestration.
|
|
11
|
-
|
|
12
|
-
This library does not ship a predesigned "collapsible header" UI. It gives you the pieces to:
|
|
13
|
-
|
|
14
|
-
- measure the parts of a header that matter
|
|
15
|
-
- derive a shared `progress` value from scroll
|
|
16
|
-
- keep multiple scrollables in sync when one header is shared across them
|
|
17
|
-
- bridge that state into navigation-rendered headers
|
|
7
|
+
Built on top of:
|
|
18
8
|
|
|
19
|
-
|
|
9
|
+
- [React Native Reanimated](https://docs.swmansion.com/react-native-reanimated/)
|
|
10
|
+
- [React Native Gesture Handler](https://docs.swmansion.com/react-native-gesture-handler/docs/)
|
|
11
|
+
- [React Native Worklets](https://docs.swmansion.com/react-native-worklets/docs/)
|
|
20
12
|
|
|
21
13
|
<div align="center">
|
|
22
14
|
<img src="https://github.com/user-attachments/assets/b673349a-f26a-4cc8-bfe1-60d77deb4390" width="70%" />
|
|
23
15
|
</div>
|
|
24
16
|
|
|
25
|
-
##
|
|
26
|
-
|
|
27
|
-
- If you are upgrading from `v0.3.x`, read [MIGRATION-v1.md](./MIGRATION-v1.md).
|
|
28
|
-
- If you are still on the pre-v1 API and need the old docs, use the `v0` README:
|
|
29
|
-
[README on branch `v0`](https://github.com/pawicao/react-native-header-motion/blob/v0/README.md)
|
|
17
|
+
## Documentation
|
|
30
18
|
|
|
31
|
-
|
|
19
|
+
Full documentation lives here:
|
|
32
20
|
|
|
33
|
-
|
|
21
|
+
- [Docs home](https://pawicao.github.io/react-native-header-motion/)
|
|
22
|
+
- [Overview](https://pawicao.github.io/react-native-header-motion/docs/overview)
|
|
23
|
+
- [Installation](https://pawicao.github.io/react-native-header-motion/docs/installation)
|
|
24
|
+
- [Quick Start](https://pawicao.github.io/react-native-header-motion/docs/quick-start)
|
|
25
|
+
- [Guides](https://pawicao.github.io/react-native-header-motion/docs/guides/dynamic-header-measurement)
|
|
26
|
+
- [API Reference](https://pawicao.github.io/react-native-header-motion/docs/api/header-motion)
|
|
34
27
|
|
|
35
|
-
|
|
36
|
-
- Context-first header API built around `HeaderMotion.Header` and `HeaderMotion.Header.Dynamic`
|
|
37
|
-
- Explicit navigation bridging with `HeaderMotion.Bridge` and `HeaderMotion.NavigationBridge`
|
|
38
|
-
- Narrower `useMotionProgress()` that focuses on `progress` and `progressThreshold`
|
|
39
|
-
- Reusable custom-scrollable factory via `createHeaderMotionScrollable()`
|
|
40
|
-
- It's now easier than ever to wire up LegendList and FlashList to Header Motion!
|
|
41
|
-
- `react-native-gesture-handler` added to the peer dependency surface
|
|
42
|
-
|
|
43
|
-
## What this library is good at
|
|
28
|
+
## What it helps with
|
|
44
29
|
|
|
45
30
|
- Scroll-driven animated headers
|
|
46
|
-
- Shared header state across tabs
|
|
47
|
-
- Navigation headers
|
|
48
|
-
-
|
|
31
|
+
- Shared header state across tabs, pagers, and multiple scrollables
|
|
32
|
+
- Navigation-rendered headers in Expo Router or React Navigation
|
|
33
|
+
- Custom scrollables via `createHeaderMotionScrollable()`
|
|
34
|
+
- Optional header panning
|
|
49
35
|
|
|
50
|
-
## What
|
|
36
|
+
## What it is not
|
|
51
37
|
|
|
52
38
|
- A fully styled header component
|
|
53
39
|
- A page layout framework
|
|
54
40
|
- A general-purpose animation abstraction on top of Reanimated
|
|
55
41
|
|
|
56
|
-
## Requirements
|
|
57
|
-
|
|
58
|
-
Your app must provide:
|
|
59
|
-
|
|
60
|
-
- `react-native-gesture-handler >= 2.0.0`
|
|
61
|
-
- `react-native-reanimated >= 4.0.0`
|
|
62
|
-
- `react-native-worklets >= 0.4.0`
|
|
63
|
-
|
|
64
|
-
These are peer dependencies.
|
|
65
|
-
|
|
66
42
|
## Installation
|
|
67
43
|
|
|
68
44
|
```bash
|
|
69
|
-
npm
|
|
45
|
+
npm install react-native-header-motion
|
|
70
46
|
```
|
|
71
47
|
|
|
72
|
-
|
|
48
|
+
Peer dependencies:
|
|
73
49
|
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
50
|
+
- `react-native-reanimated@^4.0.0`
|
|
51
|
+
- `react-native-gesture-handler@^2.0.0`
|
|
52
|
+
- `react-native-worklets@>=0.4.0`
|
|
77
53
|
|
|
78
|
-
Then
|
|
54
|
+
Then complete the standard setup for:
|
|
79
55
|
|
|
80
|
-
- [Reanimated](https://docs.swmansion.com/react-native-reanimated/docs/fundamentals/getting-started/#installation)
|
|
56
|
+
- [Reanimated and Worklets](https://docs.swmansion.com/react-native-reanimated/docs/fundamentals/getting-started/#installation)
|
|
81
57
|
- [Gesture Handler](https://docs.swmansion.com/react-native-gesture-handler/docs/fundamentals/installation)
|
|
82
|
-
- [Worklets](https://docs.swmansion.com/react-native-reanimated/docs/fundamentals/getting-started/#installation)
|
|
83
|
-
|
|
84
|
-
## Mental model
|
|
85
|
-
|
|
86
|
-
There are four concepts to understand:
|
|
87
|
-
|
|
88
|
-
### 1. `progress`
|
|
89
|
-
|
|
90
|
-
`progress` is a `SharedValue<number>`.
|
|
91
|
-
|
|
92
|
-
- `0` means "expanded"
|
|
93
|
-
- `1` means "collapsed"
|
|
94
|
-
|
|
95
|
-
Most header animations should be derived from this value.
|
|
96
|
-
|
|
97
|
-
### 2. `progressThreshold`
|
|
98
|
-
|
|
99
|
-
`progressThreshold` is the collapse distance in pixels.
|
|
100
|
-
|
|
101
|
-
It can be:
|
|
102
|
-
|
|
103
|
-
- a fixed number
|
|
104
|
-
- a function derived from the measured dynamic part of the header
|
|
105
|
-
|
|
106
|
-
At runtime, `useMotionProgress()` gives you `progressThreshold` as a `SharedValue<number>`.
|
|
107
|
-
|
|
108
|
-
In practice, `progress` is calculated by mapping scroll distance across that threshold:
|
|
109
58
|
|
|
110
|
-
|
|
111
|
-
- at the threshold, `progress` reaches `1`
|
|
112
|
-
- past the threshold, behavior depends on `progressExtrapolation`
|
|
59
|
+
For other package managers and full setup notes, see the [installation guide](https://pawicao.github.io/react-native-header-motion/docs/installation).
|
|
113
60
|
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
The library measures two different things:
|
|
117
|
-
|
|
118
|
-
- the total header height
|
|
119
|
-
- the dynamic part of the header that should define the collapse distance
|
|
120
|
-
|
|
121
|
-
`HeaderMotion.Header` wires the total-height measurement.
|
|
122
|
-
|
|
123
|
-
`HeaderMotion.Header.Dynamic` wires the dynamic measurement.
|
|
124
|
-
|
|
125
|
-
In many designs:
|
|
126
|
-
|
|
127
|
-
- the sticky/top part stays visible
|
|
128
|
-
- the dynamic part slides away
|
|
129
|
-
- the dynamic part is what should feed `progressThreshold`
|
|
130
|
-
|
|
131
|
-
### 4. Navigation headers are a separate tree
|
|
132
|
-
|
|
133
|
-
When a navigation library renders a header outside your screen subtree, it cannot read the `HeaderMotion` context directly.
|
|
134
|
-
|
|
135
|
-
That is why the library has:
|
|
136
|
-
|
|
137
|
-
- `HeaderMotion.Bridge`
|
|
138
|
-
- `HeaderMotion.NavigationBridge`
|
|
139
|
-
|
|
140
|
-
Use them only to move HeaderMotion context across that boundary.
|
|
141
|
-
|
|
142
|
-
## Recommended integration order
|
|
143
|
-
|
|
144
|
-
The library allows (and requires) you to integrate your scrollables with headers to provide animation behavior.
|
|
145
|
-
|
|
146
|
-
Use the simplest integration that fits your case:
|
|
147
|
-
|
|
148
|
-
1. `HeaderMotion.ScrollView` or `HeaderMotion.FlatList` - exported directly from the library
|
|
149
|
-
2. `createHeaderMotionScrollable()` - to easily create custom integrated scrollables on top of other scrollables (e.g. LegendList or FlashList)
|
|
150
|
-
3. `HeaderMotion.ScrollManager` / `useScrollManager()` - for even more custom scenarios
|
|
151
|
-
|
|
152
|
-
For custom scrollables, prefer `createHeaderMotionScrollable()` first.
|
|
153
|
-
|
|
154
|
-
Use the scroll managers only when the factory approach is not flexible enough.
|
|
155
|
-
|
|
156
|
-
## Quick start: navigation header
|
|
157
|
-
|
|
158
|
-
This is the core v1 pattern when your header is rendered by Expo Router / React Navigation.
|
|
61
|
+
## Quick example
|
|
159
62
|
|
|
160
63
|
```tsx
|
|
161
|
-
import HeaderMotion
|
|
64
|
+
import HeaderMotion from 'react-native-header-motion';
|
|
162
65
|
import { Stack } from 'expo-router';
|
|
163
|
-
import {
|
|
164
|
-
import Animated, {
|
|
165
|
-
Extrapolation,
|
|
166
|
-
interpolate,
|
|
167
|
-
useAnimatedStyle,
|
|
168
|
-
} from 'react-native-reanimated';
|
|
169
|
-
import { useSafeAreaInsets } from 'react-native-safe-area-context';
|
|
66
|
+
import { View } from 'react-native';
|
|
170
67
|
|
|
171
68
|
export default function Screen() {
|
|
172
69
|
return (
|
|
@@ -191,413 +88,38 @@ export default function Screen() {
|
|
|
191
88
|
}
|
|
192
89
|
|
|
193
90
|
function AppHeader() {
|
|
194
|
-
const { progress, progressThreshold } = useMotionProgress();
|
|
195
|
-
const insets = useSafeAreaInsets();
|
|
196
|
-
|
|
197
|
-
const containerStyle = useAnimatedStyle(() => {
|
|
198
|
-
const threshold = progressThreshold.get();
|
|
199
|
-
|
|
200
|
-
return {
|
|
201
|
-
transform: [
|
|
202
|
-
{
|
|
203
|
-
translateY: interpolate(
|
|
204
|
-
progress.get(),
|
|
205
|
-
[0, 1],
|
|
206
|
-
[0, -threshold],
|
|
207
|
-
Extrapolation.CLAMP
|
|
208
|
-
),
|
|
209
|
-
},
|
|
210
|
-
],
|
|
211
|
-
};
|
|
212
|
-
});
|
|
213
|
-
|
|
214
|
-
return (
|
|
215
|
-
<HeaderMotion.Header
|
|
216
|
-
style={[styles.header, { paddingTop: insets.top }, containerStyle]}
|
|
217
|
-
>
|
|
218
|
-
<HeaderMotion.Header.Dynamic>
|
|
219
|
-
{/* collapsible part */}
|
|
220
|
-
</HeaderMotion.Header.Dynamic>
|
|
221
|
-
|
|
222
|
-
<View>{/* sticky part */}</View>
|
|
223
|
-
</HeaderMotion.Header>
|
|
224
|
-
);
|
|
225
|
-
}
|
|
226
|
-
|
|
227
|
-
const styles = StyleSheet.create({
|
|
228
|
-
header: {
|
|
229
|
-
backgroundColor: '#304077',
|
|
230
|
-
},
|
|
231
|
-
});
|
|
232
|
-
```
|
|
233
|
-
|
|
234
|
-
## Quick start: inline header inside the screen
|
|
235
|
-
|
|
236
|
-
If your animated header lives in the same subtree as `HeaderMotion`, you do not need bridging at all.
|
|
237
|
-
|
|
238
|
-
```tsx
|
|
239
|
-
function Screen() {
|
|
240
|
-
return (
|
|
241
|
-
<HeaderMotion>
|
|
242
|
-
<InlineHeader />
|
|
243
|
-
<HeaderMotion.ScrollView>{/* content */}</HeaderMotion.ScrollView>
|
|
244
|
-
</HeaderMotion>
|
|
245
|
-
);
|
|
246
|
-
}
|
|
247
|
-
|
|
248
|
-
function InlineHeader() {
|
|
249
|
-
const { progress, progressThreshold } = useMotionProgress();
|
|
250
|
-
|
|
251
91
|
return (
|
|
252
92
|
<HeaderMotion.Header>
|
|
253
93
|
<HeaderMotion.Header.Dynamic>
|
|
254
|
-
{/* collapsible
|
|
94
|
+
<View>{/* collapsible content */}</View>
|
|
255
95
|
</HeaderMotion.Header.Dynamic>
|
|
256
|
-
</HeaderMotion.Header>
|
|
257
|
-
);
|
|
258
|
-
}
|
|
259
|
-
```
|
|
260
|
-
|
|
261
|
-
## Shared header across multiple scrollables
|
|
262
|
-
|
|
263
|
-
If one header is shared across tabs or pager pages:
|
|
264
|
-
|
|
265
|
-
1. Create an active scroll id with `useActiveScrollId()`
|
|
266
|
-
2. Pass `activeScrollId.sv` to `HeaderMotion`
|
|
267
|
-
3. Give each scrollable a unique `scrollId`
|
|
268
|
-
|
|
269
|
-
```tsx
|
|
270
|
-
import { useRef } from 'react';
|
|
271
|
-
import PagerView from 'react-native-pager-view';
|
|
272
96
|
|
|
273
|
-
|
|
274
|
-
[0, 'A'],
|
|
275
|
-
[1, 'B'],
|
|
276
|
-
]);
|
|
277
|
-
|
|
278
|
-
function Screen() {
|
|
279
|
-
const [activeScrollId, setActiveScrollId] = useActiveScrollId<'A' | 'B'>('A');
|
|
280
|
-
const pagerRef = useRef<PagerView>(null);
|
|
281
|
-
|
|
282
|
-
return (
|
|
283
|
-
<HeaderMotion activeScrollId={activeScrollId.sv}>
|
|
284
|
-
<HeaderMotion.Bridge>
|
|
285
|
-
{(ctx) => (
|
|
286
|
-
<Stack.Screen
|
|
287
|
-
options={{
|
|
288
|
-
header: () => (
|
|
289
|
-
<HeaderMotion.NavigationBridge value={ctx}>
|
|
290
|
-
<Header />
|
|
291
|
-
</HeaderMotion.NavigationBridge>
|
|
292
|
-
),
|
|
293
|
-
}}
|
|
294
|
-
/>
|
|
295
|
-
)}
|
|
296
|
-
</HeaderMotion.Bridge>
|
|
297
|
-
|
|
298
|
-
<PagerView
|
|
299
|
-
ref={pagerRef}
|
|
300
|
-
style={{ flex: 1 }}
|
|
301
|
-
initialPage={0}
|
|
302
|
-
onPageSelected={(e) => {
|
|
303
|
-
setActiveScrollId(indexToKey.get(e.nativeEvent.position)!);
|
|
304
|
-
}}
|
|
305
|
-
>
|
|
306
|
-
<View key="A">
|
|
307
|
-
<HeaderMotion.ScrollView scrollId="A">
|
|
308
|
-
{/* page A content */}
|
|
309
|
-
</HeaderMotion.ScrollView>
|
|
310
|
-
</View>
|
|
311
|
-
|
|
312
|
-
<View key="B">
|
|
313
|
-
<HeaderMotion.ScrollView scrollId="B">
|
|
314
|
-
{/* page B content */}
|
|
315
|
-
</HeaderMotion.ScrollView>
|
|
316
|
-
</View>
|
|
317
|
-
</PagerView>
|
|
318
|
-
</HeaderMotion>
|
|
319
|
-
);
|
|
320
|
-
}
|
|
321
|
-
```
|
|
322
|
-
|
|
323
|
-
## Header panning
|
|
324
|
-
|
|
325
|
-
Sometimes the header itself takes up a large part of the screen, so forcing the user to move their finger back down to the scrollable can feel awkward.
|
|
326
|
-
|
|
327
|
-
In those cases, you can make the header surface itself drive the scroll interaction as well:
|
|
328
|
-
|
|
329
|
-
```tsx
|
|
330
|
-
function Header() {
|
|
331
|
-
return (
|
|
332
|
-
<HeaderMotion.Header pannable>
|
|
333
|
-
<HeaderMotion.Header.Dynamic>
|
|
334
|
-
{/* collapsible content */}
|
|
335
|
-
</HeaderMotion.Header.Dynamic>
|
|
97
|
+
<View>{/* sticky content */}</View>
|
|
336
98
|
</HeaderMotion.Header>
|
|
337
99
|
);
|
|
338
100
|
}
|
|
339
101
|
```
|
|
340
102
|
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
### Default export: `HeaderMotion`
|
|
344
|
-
|
|
345
|
-
Compound component with:
|
|
346
|
-
|
|
347
|
-
- `HeaderMotion.Header`
|
|
348
|
-
- `HeaderMotion.Bridge`
|
|
349
|
-
- `HeaderMotion.NavigationBridge`
|
|
350
|
-
- `HeaderMotion.ScrollView`
|
|
351
|
-
- `HeaderMotion.FlatList`
|
|
352
|
-
- `HeaderMotion.ScrollManager`
|
|
353
|
-
|
|
354
|
-
Provider props:
|
|
355
|
-
|
|
356
|
-
- `progressThreshold?: number | ((measuredDynamic: number) => number)`: collapse distance in pixels; when passed as a function, it is derived from the value measured by `HeaderMotion.Header.Dynamic`
|
|
357
|
-
- `measureDynamic?: (e) => number`: controls what value is read from the dynamic section's layout event; defaults to its height
|
|
358
|
-
- `measureDynamicMode?: 'mount' | 'update'`: `'mount'` measures once; `'update'` re-measures when the dynamic section lays out again
|
|
359
|
-
- `activeScrollId?: SharedValue<string>`: identifies which scrollable currently owns header progress in multi-scroll setups
|
|
360
|
-
- `progressExtrapolation?: ExtrapolationType`: controls how `progress` behaves outside the normal collapse range
|
|
361
|
-
|
|
362
|
-
### `HeaderMotion.Header`
|
|
363
|
-
|
|
364
|
-
Main header container.
|
|
365
|
-
|
|
366
|
-
Responsibilities:
|
|
367
|
-
|
|
368
|
-
- measures total header height
|
|
369
|
-
- applies overlay positioning by default
|
|
370
|
-
- can make the header surface pannable
|
|
371
|
-
|
|
372
|
-
Props:
|
|
373
|
-
|
|
374
|
-
- all normal `Animated.View` props in default mode: styles, accessibility props, pointer events, and other normal animated view props work as expected
|
|
375
|
-
- `overlay?: boolean`: keeps the header absolutely positioned above content; disable only if you intentionally want it in normal layout flow
|
|
376
|
-
- `pannable?: boolean`: allows dragging directly on the header surface to continue the scroll interaction
|
|
377
|
-
- `panDecayConfig?: WithDecayConfig | ((event) => WithDecayConfig)`: customizes the momentum animation after a header pan ends
|
|
378
|
-
- `withGestureHandlerRootView?: boolean`: wraps the gesture subtree in `GestureHandlerRootView` when that part of the tree is not already under one
|
|
379
|
-
- `asChild?: boolean`: injects the total-height measurement into a single child instead of rendering the default `Animated.View`
|
|
380
|
-
|
|
381
|
-
Use `asChild` when you want to inject the total-height measurement into a single child instead of rendering the default `Animated.View`.
|
|
382
|
-
|
|
383
|
-
### `HeaderMotion.Header.Dynamic`
|
|
384
|
-
|
|
385
|
-
Marks the part of the header whose layout should define the collapsible distance.
|
|
386
|
-
|
|
387
|
-
Use this for the section that visually disappears during collapse.
|
|
388
|
-
|
|
389
|
-
Props:
|
|
390
|
-
|
|
391
|
-
- all normal `Animated.View` props in default mode: use these as you would on any animated view
|
|
392
|
-
- `asChild?: boolean`: injects the dynamic measurement into a single child instead of rendering the default `Animated.View`
|
|
393
|
-
|
|
394
|
-
### `HeaderMotion.Bridge`
|
|
395
|
-
|
|
396
|
-
Reads the current HeaderMotion context and exposes it through a render function.
|
|
397
|
-
|
|
398
|
-
Use it to move the context into a navigation-rendered header subtree.
|
|
399
|
-
|
|
400
|
-
Props:
|
|
401
|
-
|
|
402
|
-
- `children: (value) => ReactNode`: receives the bridged HeaderMotion context value that should usually be passed into `HeaderMotion.NavigationBridge`
|
|
403
|
-
|
|
404
|
-
### `HeaderMotion.NavigationBridge`
|
|
405
|
-
|
|
406
|
-
Re-provides a previously captured HeaderMotion context value in another subtree.
|
|
407
|
-
|
|
408
|
-
Use it together with `HeaderMotion.Bridge`.
|
|
409
|
-
|
|
410
|
-
Props:
|
|
411
|
-
|
|
412
|
-
- `value`: the bridged HeaderMotion context captured by `HeaderMotion.Bridge`
|
|
413
|
-
- `children`: the subtree that should regain access to HeaderMotion context
|
|
414
|
-
|
|
415
|
-
### `HeaderMotion.ScrollView`
|
|
416
|
-
|
|
417
|
-
Pre-wired `Animated.ScrollView`.
|
|
418
|
-
|
|
419
|
-
Supports:
|
|
420
|
-
|
|
421
|
-
- `scrollId?: string`: unique id for this scrollable when one header is shared across multiple scrollables
|
|
422
|
-
- `headerOffsetStrategy?: 'padding' | 'margin' | 'top' | 'translate' | 'none'`: controls how content is pushed below the measured header
|
|
423
|
-
- `ensureScrollableContentMinHeight?: boolean`: experimental fallback for short content that otherwise could not scroll far enough to collapse the header fully
|
|
424
|
-
- `animatedRef?: AnimatedRef`: lets you reuse your own animated ref instead of letting HeaderMotion create one
|
|
425
|
-
|
|
426
|
-
### `HeaderMotion.FlatList`
|
|
427
|
-
|
|
428
|
-
Pre-wired `Animated.FlatList`.
|
|
429
|
-
|
|
430
|
-
Supports the same HeaderMotion-specific props as `HeaderMotion.ScrollView`.
|
|
431
|
-
|
|
432
|
-
### `createHeaderMotionScrollable(Component, options?)`
|
|
433
|
-
|
|
434
|
-
Factory for creating reusable HeaderMotion-aware wrappers around custom scrollables.
|
|
435
|
-
|
|
436
|
-
Prefer this over the scroll managers whenever it is enough.
|
|
437
|
-
|
|
438
|
-
Useful options:
|
|
439
|
-
|
|
440
|
-
- `displayName`: custom component name shown in React DevTools
|
|
441
|
-
- `isComponentAnimated`: set this when the input component is already animated and should not be wrapped again
|
|
442
|
-
- `contentContainerMode: 'children' | 'renderScrollComponent'`: tells HeaderMotion how to inject content offsetting for that scrollable shape
|
|
443
|
-
|
|
444
|
-
Use:
|
|
445
|
-
|
|
446
|
-
- `'children'` for ScrollView-like components
|
|
447
|
-
- `'renderScrollComponent'` for FlatList-like components
|
|
448
|
-
|
|
449
|
-
Examples:
|
|
450
|
-
|
|
451
|
-
`FlashList`
|
|
452
|
-
|
|
453
|
-
```tsx
|
|
454
|
-
import { FlashList } from '@shopify/flash-list';
|
|
455
|
-
import { createHeaderMotionScrollable } from 'react-native-header-motion';
|
|
456
|
-
|
|
457
|
-
const HeaderMotionFlashList = createHeaderMotionScrollable(FlashList, {
|
|
458
|
-
displayName: 'HeaderMotionFlashList',
|
|
459
|
-
contentContainerMode: 'renderScrollComponent',
|
|
460
|
-
});
|
|
461
|
-
```
|
|
462
|
-
|
|
463
|
-
`LegendList`
|
|
103
|
+
In a real header, use `useMotionProgress()` to drive your Reanimated styles. See the [Quick Start](https://pawicao.github.io/react-native-header-motion/docs/quick-start) for the full walkthrough, animation examples, and styling details.
|
|
464
104
|
|
|
465
|
-
|
|
466
|
-
import { LegendList } from '@legendapp/list';
|
|
467
|
-
import { createHeaderMotionScrollable } from 'react-native-header-motion';
|
|
468
|
-
|
|
469
|
-
const HeaderMotionLegendList = createHeaderMotionScrollable(LegendList, {
|
|
470
|
-
displayName: 'HeaderMotionLegendList',
|
|
471
|
-
isComponentAnimated: true,
|
|
472
|
-
contentContainerMode: 'renderScrollComponent',
|
|
473
|
-
});
|
|
474
|
-
```
|
|
475
|
-
|
|
476
|
-
### `HeaderMotion.ScrollManager`
|
|
477
|
-
|
|
478
|
-
Render-prop fallback for complex custom integrations.
|
|
479
|
-
|
|
480
|
-
Most code should prefer `createHeaderMotionScrollable()`.
|
|
481
|
-
|
|
482
|
-
Use `ScrollManager` only when you need a custom composition that the factory API cannot express cleanly.
|
|
483
|
-
|
|
484
|
-
Props:
|
|
485
|
-
|
|
486
|
-
- `scrollId?: string`: unique id for this scrollable when one header is shared across multiple scrollables
|
|
487
|
-
- `children`: render function that receives `scrollableProps` and `headerMotionContext`
|
|
488
|
-
- plus the same refresh / ref options accepted by `useScrollManager()`
|
|
489
|
-
|
|
490
|
-
### Hooks
|
|
491
|
-
|
|
492
|
-
#### `useMotionProgress()`
|
|
493
|
-
|
|
494
|
-
Returns:
|
|
495
|
-
|
|
496
|
-
- `progress`: `SharedValue<number>` that typically moves from `0` at expanded state to `1` at collapsed state
|
|
497
|
-
- `progressThreshold`: `SharedValue<number>` representing the collapse distance in pixels
|
|
498
|
-
|
|
499
|
-
This is the primary animation hook for header UI.
|
|
500
|
-
|
|
501
|
-
#### `useHeaderMotionBridge()`
|
|
502
|
-
|
|
503
|
-
Returns the full internal bridge value.
|
|
504
|
-
|
|
505
|
-
Most app code should not need this. Prefer `useMotionProgress()` unless you are explicitly bridging context across a tree boundary.
|
|
506
|
-
|
|
507
|
-
Returns:
|
|
508
|
-
|
|
509
|
-
- full HeaderMotion context value, including measurement callbacks and scroll synchronization internals
|
|
510
|
-
|
|
511
|
-
#### `useActiveScrollId(initialId)`
|
|
512
|
-
|
|
513
|
-
Returns:
|
|
514
|
-
|
|
515
|
-
- `{ state, sv }`: `state` is the React value for UI logic, `sv` is the matching shared value for HeaderMotion
|
|
516
|
-
- setter function: updates both in sync
|
|
517
|
-
|
|
518
|
-
Use this for multi-scroll setups.
|
|
519
|
-
|
|
520
|
-
#### `useScrollManager(scrollId?, options?)`
|
|
521
|
-
|
|
522
|
-
Hook-level fallback for complex custom scrollables.
|
|
523
|
-
|
|
524
|
-
Most code should prefer `createHeaderMotionScrollable()`.
|
|
525
|
-
|
|
526
|
-
Parameters:
|
|
527
|
-
|
|
528
|
-
- `scrollId`: unique id for this scrollable when one header is shared across multiple scrollables
|
|
529
|
-
- `options`: optional ref, refresh, and event-handler configuration
|
|
530
|
-
|
|
531
|
-
Returns:
|
|
532
|
-
|
|
533
|
-
- `scrollableProps`: props to spread onto the scrollable itself, including the managed ref, scroll handlers, and resolved refresh control
|
|
534
|
-
- `headerMotionContext`: layout values for offsetting content below the measured header, including `originalHeaderHeight` and optional `contentContainerMinHeight`
|
|
535
|
-
|
|
536
|
-
## Notes
|
|
537
|
-
|
|
538
|
-
### Why `HeaderMotion.Header` is absolute by default
|
|
539
|
-
|
|
540
|
-
Headers rendered by navigation are often easier to animate and interact with when they are visually overlayed above content rather than participating in normal layout flow.
|
|
541
|
-
|
|
542
|
-
That is why `overlay` defaults to `true`.
|
|
543
|
-
|
|
544
|
-
Disable it only when you intentionally want the header in normal layout flow.
|
|
545
|
-
|
|
546
|
-
### `ensureScrollableContentMinHeight` (experimental)
|
|
547
|
-
|
|
548
|
-
This is available on the pre-wired scrollables and the custom-scrollable APIs.
|
|
549
|
-
|
|
550
|
-
It is useful when content is too short to naturally scroll through the full collapse distance.
|
|
551
|
-
|
|
552
|
-
This feature is still experimental.
|
|
553
|
-
|
|
554
|
-
### Scroll event frequency
|
|
555
|
-
|
|
556
|
-
`scrollEventThrottle` is intentionally not managed by this library.
|
|
557
|
-
|
|
558
|
-
Pass it directly to your scrollable when you need it.
|
|
559
|
-
|
|
560
|
-
If you run into performance issues, try adjusting `scrollEventThrottle` to reduce how many scroll events this library processes.
|
|
561
|
-
|
|
562
|
-
### Refresh control
|
|
563
|
-
|
|
564
|
-
If you use `HeaderMotion.ScrollView` or `HeaderMotion.FlatList`, your refresh-control usage stays the same as in React Native.
|
|
565
|
-
|
|
566
|
-
If you use `HeaderMotion.ScrollManager` directly for custom integrations, pass refresh-related props to `ScrollManager` itself:
|
|
567
|
-
|
|
568
|
-
- `refreshControl`
|
|
569
|
-
- `refreshing`
|
|
570
|
-
- `onRefresh`
|
|
571
|
-
- optional `progressViewOffset`
|
|
572
|
-
|
|
573
|
-
This matters because scrollable positioning affects refresh-control behavior and needs to stay coupled with the measured header height.
|
|
574
|
-
|
|
575
|
-
Platform support note:
|
|
105
|
+
## Version notes
|
|
576
106
|
|
|
577
|
-
-
|
|
578
|
-
-
|
|
579
|
-
- iOS behavior is still not fully deterministic.
|
|
580
|
-
- `progressViewOffset` does not seem to be reliable on iOS in all scenarios.
|
|
581
|
-
- Other iOS approaches tried so far introduced different issues.
|
|
582
|
-
- Additional iOS support improvements are planned for future releases.
|
|
107
|
+
- Upgrading from `v0.3.x`? Read [MIGRATION-v1.md](./MIGRATION-v1.md).
|
|
108
|
+
- Need the old API docs? See the [README on branch `v0`](https://github.com/pawicao/react-native-header-motion/blob/v0/README.md).
|
|
583
109
|
|
|
584
|
-
##
|
|
110
|
+
## Example app
|
|
585
111
|
|
|
586
|
-
|
|
112
|
+
The repository includes an Expo Router example app covering simple headers, navigation bridging, shared headers across pages, custom scrollables, overscroll, pull to refresh, and more.
|
|
587
113
|
|
|
588
|
-
|
|
114
|
+
See:
|
|
589
115
|
|
|
590
|
-
- [`example
|
|
591
|
-
- [
|
|
592
|
-
- [`example/src/app/legend-list.tsx`](./example/src/app/legend-list.tsx)
|
|
593
|
-
- [`example/src/app/pager-header-pan.tsx`](./example/src/app/pager-header-pan.tsx)
|
|
594
|
-
- [`example/src/app/collapsible-pager.tsx`](./example/src/app/collapsible-pager.tsx)
|
|
116
|
+
- [`example/`](./example/)
|
|
117
|
+
- [Example app docs](https://pawicao.github.io/react-native-header-motion/docs/other/example-app)
|
|
595
118
|
|
|
596
119
|
## Contributing
|
|
597
120
|
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
Code of conduct: see [CODE_OF_CONDUCT.md](./CODE_OF_CONDUCT.md)
|
|
121
|
+
- [Contributing guide](./CONTRIBUTING.md)
|
|
122
|
+
- [Code of conduct](./CODE_OF_CONDUCT.md)
|
|
601
123
|
|
|
602
124
|
## License
|
|
603
125
|
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"docusaurus.config.d.ts","sourceRoot":"","sources":["../../../docs/docusaurus.config.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,mBAAmB,CAAC;AAGhD,QAAA,MAAM,MAAM,EAAE,MAwIb,CAAC;AAEF,eAAe,MAAM,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"sidebars.d.ts","sourceRoot":"","sources":["../../../docs/sidebars.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,iCAAiC,CAAC;AAEtE,QAAA,MAAM,QAAQ,EAAE,cA8Ef,CAAC;AAEF,eAAe,QAAQ,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../../docs/src/pages/index.tsx"],"names":[],"mappings":"AA0LA,MAAM,CAAC,OAAO,UAAU,IAAI,IAAI,KAAK,CAAC,GAAG,CAAC,OAAO,CAUhD"}
|
|
@@ -7,7 +7,7 @@ declare const AnimatedRefreshControl: import("react-native-reanimated/lib/typesc
|
|
|
7
7
|
onLayout?: ((event: import("react-native").LayoutChangeEvent) => unknown) | undefined;
|
|
8
8
|
onMagicTap?: (() => unknown) | undefined;
|
|
9
9
|
onAccessibilityEscape?: (() => unknown) | undefined;
|
|
10
|
-
}>, "
|
|
10
|
+
}>, "id" | "style" | "children" | "tabIndex" | "role" | "aria-busy" | "aria-checked" | "aria-disabled" | "aria-expanded" | "aria-hidden" | "aria-label" | "aria-labelledby" | "aria-live" | "aria-modal" | "aria-selected" | "aria-valuemax" | "aria-valuemin" | "aria-valuenow" | "aria-valuetext" | "onFocus" | "onFocusCapture" | "onBlur" | "onBlurCapture" | "onClick" | "onClickCapture" | "onMouseEnter" | "onMouseLeave" | "onTouchCancel" | "onTouchCancelCapture" | "onTouchEnd" | "onTouchEndCapture" | "onTouchMove" | "onTouchMoveCapture" | "onTouchStart" | "onTouchStartCapture" | "onPointerDown" | "onPointerDownCapture" | "onPointerMove" | "onPointerMoveCapture" | "onPointerUp" | "onPointerUpCapture" | "onPointerCancel" | "onPointerCancelCapture" | "onPointerEnter" | "onPointerLeave" | "onPointerOver" | "onPointerOverCapture" | "onPointerOut" | "onPointerOutCapture" | "onGotPointerCapture" | "onGotPointerCaptureCapture" | "onLostPointerCapture" | "onLostPointerCaptureCapture" | "onMoveShouldSetResponder" | "onMoveShouldSetResponderCapture" | "onResponderGrant" | "onResponderMove" | "onResponderReject" | "onResponderRelease" | "onResponderStart" | "onResponderEnd" | "onResponderTerminate" | "onResponderTerminationRequest" | "onStartShouldSetResponder" | "onStartShouldSetResponderCapture" | "onPointerEnterCapture" | "onPointerLeaveCapture" | "nativeBackgroundAndroid" | "nativeForegroundAndroid" | "renderToHardwareTextureAndroid" | "hasTVPreferredFocus" | "nextFocusDown" | "nextFocusForward" | "nextFocusLeft" | "nextFocusRight" | "nextFocusUp" | "focusable" | "shouldRasterizeIOS" | "accessibilityIgnoresInvertColors" | "accessibilityViewIsModal" | "accessibilityShowsLargeContentViewer" | "accessibilityLargeContentTitle" | "accessibilityElementsHidden" | "accessibilityLanguage" | "accessibilityRespondsToUserInteraction" | "accessible" | "accessibilityLabel" | "accessibilityHint" | "accessibilityRole" | "accessibilityState" | "accessibilityValue" | "accessibilityActions" | "accessibilityLabelledBy" | "accessibilityLiveRegion" | "importantForAccessibility" | "screenReaderFocusable" | "collapsable" | "collapsableChildren" | "testID" | "nativeID" | "needsOffscreenAlphaCompositing" | "hitSlop" | "pointerEvents" | "removeClippedSubviews" | "experimental_accessibilityOrder"> & Omit<Readonly<{
|
|
11
11
|
onMoveShouldSetResponder?: ((e: import("react-native").GestureResponderEvent) => boolean) | undefined;
|
|
12
12
|
onMoveShouldSetResponderCapture?: ((e: import("react-native").GestureResponderEvent) => boolean) | undefined;
|
|
13
13
|
onResponderGrant?: ((e: import("react-native").GestureResponderEvent) => void | boolean) | undefined;
|
|
@@ -20,10 +20,10 @@ declare const AnimatedRefreshControl: import("react-native-reanimated/lib/typesc
|
|
|
20
20
|
onResponderTerminationRequest?: ((e: import("react-native").GestureResponderEvent) => boolean) | undefined;
|
|
21
21
|
onStartShouldSetResponder?: ((e: import("react-native").GestureResponderEvent) => boolean) | undefined;
|
|
22
22
|
onStartShouldSetResponderCapture?: ((e: import("react-native").GestureResponderEvent) => boolean) | undefined;
|
|
23
|
-
}>, "
|
|
23
|
+
}>, "id" | "style" | "children" | "tabIndex" | "role" | "aria-busy" | "aria-checked" | "aria-disabled" | "aria-expanded" | "aria-hidden" | "aria-label" | "aria-labelledby" | "aria-live" | "aria-modal" | "aria-selected" | "aria-valuemax" | "aria-valuemin" | "aria-valuenow" | "aria-valuetext" | "onFocus" | "onFocusCapture" | "onBlur" | "onBlurCapture" | "onClick" | "onClickCapture" | "onMouseEnter" | "onMouseLeave" | "onTouchCancel" | "onTouchCancelCapture" | "onTouchEnd" | "onTouchEndCapture" | "onTouchMove" | "onTouchMoveCapture" | "onTouchStart" | "onTouchStartCapture" | "onPointerDown" | "onPointerDownCapture" | "onPointerMove" | "onPointerMoveCapture" | "onPointerUp" | "onPointerUpCapture" | "onPointerCancel" | "onPointerCancelCapture" | "onPointerEnter" | "onPointerLeave" | "onPointerOver" | "onPointerOverCapture" | "onPointerOut" | "onPointerOutCapture" | "onGotPointerCapture" | "onGotPointerCaptureCapture" | "onLostPointerCapture" | "onLostPointerCaptureCapture" | "onPointerEnterCapture" | "onPointerLeaveCapture" | "nativeBackgroundAndroid" | "nativeForegroundAndroid" | "renderToHardwareTextureAndroid" | "hasTVPreferredFocus" | "nextFocusDown" | "nextFocusForward" | "nextFocusLeft" | "nextFocusRight" | "nextFocusUp" | "focusable" | "shouldRasterizeIOS" | "accessibilityIgnoresInvertColors" | "accessibilityViewIsModal" | "accessibilityShowsLargeContentViewer" | "accessibilityLargeContentTitle" | "accessibilityElementsHidden" | "accessibilityLanguage" | "accessibilityRespondsToUserInteraction" | "accessible" | "accessibilityLabel" | "accessibilityHint" | "accessibilityRole" | "accessibilityState" | "accessibilityValue" | "accessibilityActions" | "accessibilityLabelledBy" | "accessibilityLiveRegion" | "importantForAccessibility" | "screenReaderFocusable" | "collapsable" | "collapsableChildren" | "testID" | "nativeID" | "needsOffscreenAlphaCompositing" | "hitSlop" | "pointerEvents" | "removeClippedSubviews" | "experimental_accessibilityOrder"> & Omit<Readonly<{
|
|
24
24
|
onMouseEnter?: ((event: import("react-native").MouseEvent) => void) | undefined;
|
|
25
25
|
onMouseLeave?: ((event: import("react-native").MouseEvent) => void) | undefined;
|
|
26
|
-
}>, "
|
|
26
|
+
}>, "id" | "style" | "children" | "tabIndex" | "role" | "aria-busy" | "aria-checked" | "aria-disabled" | "aria-expanded" | "aria-hidden" | "aria-label" | "aria-labelledby" | "aria-live" | "aria-modal" | "aria-selected" | "aria-valuemax" | "aria-valuemin" | "aria-valuenow" | "aria-valuetext" | "onFocus" | "onFocusCapture" | "onBlur" | "onBlurCapture" | "onClick" | "onClickCapture" | "onTouchCancel" | "onTouchCancelCapture" | "onTouchEnd" | "onTouchEndCapture" | "onTouchMove" | "onTouchMoveCapture" | "onTouchStart" | "onTouchStartCapture" | "onPointerDown" | "onPointerDownCapture" | "onPointerMove" | "onPointerMoveCapture" | "onPointerUp" | "onPointerUpCapture" | "onPointerCancel" | "onPointerCancelCapture" | "onPointerEnter" | "onPointerLeave" | "onPointerOver" | "onPointerOverCapture" | "onPointerOut" | "onPointerOutCapture" | "onGotPointerCapture" | "onGotPointerCaptureCapture" | "onLostPointerCapture" | "onLostPointerCaptureCapture" | "onPointerEnterCapture" | "onPointerLeaveCapture" | "nativeBackgroundAndroid" | "nativeForegroundAndroid" | "renderToHardwareTextureAndroid" | "hasTVPreferredFocus" | "nextFocusDown" | "nextFocusForward" | "nextFocusLeft" | "nextFocusRight" | "nextFocusUp" | "focusable" | "shouldRasterizeIOS" | "accessibilityIgnoresInvertColors" | "accessibilityViewIsModal" | "accessibilityShowsLargeContentViewer" | "accessibilityLargeContentTitle" | "accessibilityElementsHidden" | "accessibilityLanguage" | "accessibilityRespondsToUserInteraction" | "accessible" | "accessibilityLabel" | "accessibilityHint" | "accessibilityRole" | "accessibilityState" | "accessibilityValue" | "accessibilityActions" | "accessibilityLabelledBy" | "accessibilityLiveRegion" | "importantForAccessibility" | "screenReaderFocusable" | "collapsable" | "collapsableChildren" | "testID" | "nativeID" | "needsOffscreenAlphaCompositing" | "hitSlop" | "pointerEvents" | "removeClippedSubviews" | "experimental_accessibilityOrder"> & Omit<Readonly<{
|
|
27
27
|
onClick?: ((event: import("react-native").PointerEvent) => void) | undefined;
|
|
28
28
|
onClickCapture?: ((event: import("react-native").PointerEvent) => void) | undefined;
|
|
29
29
|
onPointerEnter?: ((event: import("react-native").PointerEvent) => void) | undefined;
|
|
@@ -46,12 +46,12 @@ declare const AnimatedRefreshControl: import("react-native-reanimated/lib/typesc
|
|
|
46
46
|
onGotPointerCaptureCapture?: ((e: import("react-native").PointerEvent) => void) | undefined;
|
|
47
47
|
onLostPointerCapture?: ((e: import("react-native").PointerEvent) => void) | undefined;
|
|
48
48
|
onLostPointerCaptureCapture?: ((e: import("react-native").PointerEvent) => void) | undefined;
|
|
49
|
-
}>, "
|
|
49
|
+
}>, "id" | "style" | "children" | "tabIndex" | "role" | "aria-busy" | "aria-checked" | "aria-disabled" | "aria-expanded" | "aria-hidden" | "aria-label" | "aria-labelledby" | "aria-live" | "aria-modal" | "aria-selected" | "aria-valuemax" | "aria-valuemin" | "aria-valuenow" | "aria-valuetext" | "onFocus" | "onFocusCapture" | "onBlur" | "onBlurCapture" | "onClick" | "onTouchCancel" | "onTouchCancelCapture" | "onTouchEnd" | "onTouchEndCapture" | "onTouchMove" | "onTouchMoveCapture" | "onTouchStart" | "onTouchStartCapture" | "nativeBackgroundAndroid" | "nativeForegroundAndroid" | "renderToHardwareTextureAndroid" | "hasTVPreferredFocus" | "nextFocusDown" | "nextFocusForward" | "nextFocusLeft" | "nextFocusRight" | "nextFocusUp" | "focusable" | "shouldRasterizeIOS" | "accessibilityIgnoresInvertColors" | "accessibilityViewIsModal" | "accessibilityShowsLargeContentViewer" | "accessibilityLargeContentTitle" | "accessibilityElementsHidden" | "accessibilityLanguage" | "accessibilityRespondsToUserInteraction" | "accessible" | "accessibilityLabel" | "accessibilityHint" | "accessibilityRole" | "accessibilityState" | "accessibilityValue" | "accessibilityActions" | "accessibilityLabelledBy" | "accessibilityLiveRegion" | "importantForAccessibility" | "screenReaderFocusable" | "collapsable" | "collapsableChildren" | "testID" | "nativeID" | "needsOffscreenAlphaCompositing" | "hitSlop" | "pointerEvents" | "removeClippedSubviews" | "experimental_accessibilityOrder"> & Omit<Readonly<{
|
|
50
50
|
onBlur?: ((event: import("react-native").BlurEvent) => void) | undefined;
|
|
51
51
|
onBlurCapture?: ((event: import("react-native").BlurEvent) => void) | undefined;
|
|
52
52
|
onFocus?: ((event: import("react-native").FocusEvent) => void) | undefined;
|
|
53
53
|
onFocusCapture?: ((event: import("react-native").FocusEvent) => void) | undefined;
|
|
54
|
-
}>, "
|
|
54
|
+
}>, "id" | "style" | "children" | "tabIndex" | "role" | "aria-busy" | "aria-checked" | "aria-disabled" | "aria-expanded" | "aria-hidden" | "aria-label" | "aria-labelledby" | "aria-live" | "aria-modal" | "aria-selected" | "aria-valuemax" | "aria-valuemin" | "aria-valuenow" | "aria-valuetext" | "onClick" | "onTouchCancel" | "onTouchCancelCapture" | "onTouchEnd" | "onTouchEndCapture" | "onTouchMove" | "onTouchMoveCapture" | "onTouchStart" | "onTouchStartCapture" | "nativeBackgroundAndroid" | "nativeForegroundAndroid" | "renderToHardwareTextureAndroid" | "hasTVPreferredFocus" | "nextFocusDown" | "nextFocusForward" | "nextFocusLeft" | "nextFocusRight" | "nextFocusUp" | "focusable" | "shouldRasterizeIOS" | "accessibilityIgnoresInvertColors" | "accessibilityViewIsModal" | "accessibilityShowsLargeContentViewer" | "accessibilityLargeContentTitle" | "accessibilityElementsHidden" | "accessibilityLanguage" | "accessibilityRespondsToUserInteraction" | "accessible" | "accessibilityLabel" | "accessibilityHint" | "accessibilityRole" | "accessibilityState" | "accessibilityValue" | "accessibilityActions" | "accessibilityLabelledBy" | "accessibilityLiveRegion" | "importantForAccessibility" | "screenReaderFocusable" | "collapsable" | "collapsableChildren" | "testID" | "nativeID" | "needsOffscreenAlphaCompositing" | "hitSlop" | "pointerEvents" | "removeClippedSubviews" | "experimental_accessibilityOrder"> & Omit<Readonly<{
|
|
55
55
|
onTouchCancel?: ((e: import("react-native").GestureResponderEvent) => void) | undefined;
|
|
56
56
|
onTouchCancelCapture?: ((e: import("react-native").GestureResponderEvent) => void) | undefined;
|
|
57
57
|
onTouchEnd?: ((e: import("react-native").GestureResponderEvent) => void) | undefined;
|
|
@@ -60,7 +60,7 @@ declare const AnimatedRefreshControl: import("react-native-reanimated/lib/typesc
|
|
|
60
60
|
onTouchMoveCapture?: ((e: import("react-native").GestureResponderEvent) => void) | undefined;
|
|
61
61
|
onTouchStart?: ((e: import("react-native").GestureResponderEvent) => void) | undefined;
|
|
62
62
|
onTouchStartCapture?: ((e: import("react-native").GestureResponderEvent) => void) | undefined;
|
|
63
|
-
}>, "
|
|
63
|
+
}>, "id" | "style" | "children" | "tabIndex" | "role" | "aria-busy" | "aria-checked" | "aria-disabled" | "aria-expanded" | "aria-hidden" | "aria-label" | "aria-labelledby" | "aria-live" | "aria-modal" | "aria-selected" | "aria-valuemax" | "aria-valuemin" | "aria-valuenow" | "aria-valuetext" | "onClick" | "nativeBackgroundAndroid" | "nativeForegroundAndroid" | "renderToHardwareTextureAndroid" | "hasTVPreferredFocus" | "nextFocusDown" | "nextFocusForward" | "nextFocusLeft" | "nextFocusRight" | "nextFocusUp" | "focusable" | "shouldRasterizeIOS" | "accessibilityIgnoresInvertColors" | "accessibilityViewIsModal" | "accessibilityShowsLargeContentViewer" | "accessibilityLargeContentTitle" | "accessibilityElementsHidden" | "accessibilityLanguage" | "accessibilityRespondsToUserInteraction" | "accessible" | "accessibilityLabel" | "accessibilityHint" | "accessibilityRole" | "accessibilityState" | "accessibilityValue" | "accessibilityActions" | "accessibilityLabelledBy" | "accessibilityLiveRegion" | "importantForAccessibility" | "screenReaderFocusable" | "collapsable" | "collapsableChildren" | "testID" | "nativeID" | "needsOffscreenAlphaCompositing" | "hitSlop" | "pointerEvents" | "removeClippedSubviews" | "experimental_accessibilityOrder"> & Omit<Readonly<{
|
|
64
64
|
nativeBackgroundAndroid?: import("react-native/types_generated/Libraries/Components/View/ViewPropTypes").AndroidDrawable | undefined;
|
|
65
65
|
nativeForegroundAndroid?: import("react-native/types_generated/Libraries/Components/View/ViewPropTypes").AndroidDrawable | undefined;
|
|
66
66
|
renderToHardwareTextureAndroid?: boolean | undefined;
|
|
@@ -73,16 +73,16 @@ declare const AnimatedRefreshControl: import("react-native-reanimated/lib/typesc
|
|
|
73
73
|
focusable?: boolean | undefined;
|
|
74
74
|
tabIndex?: 0 | -1;
|
|
75
75
|
onClick?: ((event: import("react-native").GestureResponderEvent) => unknown) | undefined;
|
|
76
|
-
}>, "
|
|
76
|
+
}>, "id" | "style" | "children" | "role" | "aria-busy" | "aria-checked" | "aria-disabled" | "aria-expanded" | "aria-hidden" | "aria-label" | "aria-labelledby" | "aria-live" | "aria-modal" | "aria-selected" | "aria-valuemax" | "aria-valuemin" | "aria-valuenow" | "aria-valuetext" | "shouldRasterizeIOS" | "accessibilityIgnoresInvertColors" | "accessibilityViewIsModal" | "accessibilityShowsLargeContentViewer" | "accessibilityLargeContentTitle" | "accessibilityElementsHidden" | "accessibilityLanguage" | "accessibilityRespondsToUserInteraction" | "accessible" | "accessibilityLabel" | "accessibilityHint" | "accessibilityRole" | "accessibilityState" | "accessibilityValue" | "accessibilityActions" | "accessibilityLabelledBy" | "accessibilityLiveRegion" | "importantForAccessibility" | "screenReaderFocusable" | "collapsable" | "collapsableChildren" | "testID" | "nativeID" | "needsOffscreenAlphaCompositing" | "hitSlop" | "pointerEvents" | "removeClippedSubviews" | "experimental_accessibilityOrder"> & Omit<Readonly<{
|
|
77
77
|
shouldRasterizeIOS?: boolean | undefined;
|
|
78
|
-
}>, "
|
|
78
|
+
}>, "id" | "style" | "children" | "role" | "aria-busy" | "aria-checked" | "aria-disabled" | "aria-expanded" | "aria-hidden" | "aria-label" | "aria-labelledby" | "aria-live" | "aria-modal" | "aria-selected" | "aria-valuemax" | "aria-valuemin" | "aria-valuenow" | "aria-valuetext" | "accessibilityIgnoresInvertColors" | "accessibilityViewIsModal" | "accessibilityShowsLargeContentViewer" | "accessibilityLargeContentTitle" | "accessibilityElementsHidden" | "accessibilityLanguage" | "accessibilityRespondsToUserInteraction" | "accessible" | "accessibilityLabel" | "accessibilityHint" | "accessibilityRole" | "accessibilityState" | "accessibilityValue" | "accessibilityActions" | "accessibilityLabelledBy" | "accessibilityLiveRegion" | "importantForAccessibility" | "screenReaderFocusable" | "collapsable" | "collapsableChildren" | "testID" | "nativeID" | "needsOffscreenAlphaCompositing" | "hitSlop" | "pointerEvents" | "removeClippedSubviews" | "experimental_accessibilityOrder"> & Omit<Readonly<Omit<Readonly<{
|
|
79
79
|
accessibilityLabelledBy?: (string | undefined) | (Array<string> | undefined);
|
|
80
80
|
"aria-labelledby"?: string | undefined;
|
|
81
81
|
accessibilityLiveRegion?: ("none" | "polite" | "assertive") | undefined;
|
|
82
82
|
"aria-live"?: ("polite" | "assertive" | "off") | undefined;
|
|
83
83
|
importantForAccessibility?: ("auto" | "yes" | "no" | "no-hide-descendants") | undefined;
|
|
84
84
|
screenReaderFocusable?: boolean;
|
|
85
|
-
}>, "
|
|
85
|
+
}>, "role" | "aria-busy" | "aria-checked" | "aria-disabled" | "aria-expanded" | "aria-hidden" | "aria-label" | "aria-modal" | "aria-selected" | "aria-valuemax" | "aria-valuemin" | "aria-valuenow" | "aria-valuetext" | "accessibilityIgnoresInvertColors" | "accessibilityViewIsModal" | "accessibilityShowsLargeContentViewer" | "accessibilityLargeContentTitle" | "accessibilityElementsHidden" | "accessibilityLanguage" | "accessibilityRespondsToUserInteraction" | "accessible" | "accessibilityLabel" | "accessibilityHint" | "accessibilityRole" | "accessibilityState" | "accessibilityValue" | "accessibilityActions"> & Omit<Readonly<{
|
|
86
86
|
accessibilityIgnoresInvertColors?: boolean | undefined;
|
|
87
87
|
accessibilityViewIsModal?: boolean | undefined;
|
|
88
88
|
accessibilityShowsLargeContentViewer?: boolean | undefined;
|
|
@@ -91,7 +91,7 @@ declare const AnimatedRefreshControl: import("react-native-reanimated/lib/typesc
|
|
|
91
91
|
accessibilityElementsHidden?: boolean | undefined;
|
|
92
92
|
accessibilityLanguage?: string | undefined;
|
|
93
93
|
accessibilityRespondsToUserInteraction?: boolean | undefined;
|
|
94
|
-
}>, "
|
|
94
|
+
}>, "role" | "aria-busy" | "aria-checked" | "aria-disabled" | "aria-expanded" | "aria-hidden" | "aria-label" | "aria-selected" | "aria-valuemax" | "aria-valuemin" | "aria-valuenow" | "aria-valuetext" | "accessible" | "accessibilityLabel" | "accessibilityHint" | "accessibilityRole" | "accessibilityState" | "accessibilityValue" | "accessibilityActions"> & {
|
|
95
95
|
accessible?: boolean | undefined;
|
|
96
96
|
accessibilityLabel?: string | undefined;
|
|
97
97
|
accessibilityHint?: string | undefined;
|
|
@@ -111,8 +111,8 @@ declare const AnimatedRefreshControl: import("react-native-reanimated/lib/typesc
|
|
|
111
111
|
"aria-expanded"?: boolean | undefined;
|
|
112
112
|
"aria-selected"?: boolean | undefined;
|
|
113
113
|
"aria-hidden"?: boolean | undefined;
|
|
114
|
-
}>, "
|
|
115
|
-
children?:
|
|
114
|
+
}>, "id" | "style" | "children" | "collapsable" | "collapsableChildren" | "testID" | "nativeID" | "needsOffscreenAlphaCompositing" | "hitSlop" | "pointerEvents" | "removeClippedSubviews" | "experimental_accessibilityOrder"> & Omit<Readonly<{
|
|
115
|
+
children?: import("react").ReactNode;
|
|
116
116
|
style?: import("react-native/types_generated/Libraries/StyleSheet/StyleSheet").ViewStyleProp | undefined;
|
|
117
117
|
collapsable?: boolean | undefined;
|
|
118
118
|
collapsableChildren?: boolean | undefined;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "react-native-header-motion",
|
|
3
|
-
"version": "1.0.0
|
|
3
|
+
"version": "1.0.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",
|
|
@@ -38,7 +38,7 @@
|
|
|
38
38
|
"typecheck": "tsc",
|
|
39
39
|
"test": "jest",
|
|
40
40
|
"release": "release-it --only-version",
|
|
41
|
-
"lint": "eslint
|
|
41
|
+
"lint": "eslint src example docs/src docs/*.ts *.js"
|
|
42
42
|
},
|
|
43
43
|
"keywords": [
|
|
44
44
|
"react-native",
|
|
@@ -54,7 +54,7 @@
|
|
|
54
54
|
"bugs": {
|
|
55
55
|
"url": "https://github.com/pawicao/react-native-header-motion/issues"
|
|
56
56
|
},
|
|
57
|
-
"homepage": "https://github.
|
|
57
|
+
"homepage": "https://pawicao.github.io/react-native-header-motion/",
|
|
58
58
|
"publishConfig": {
|
|
59
59
|
"registry": "https://registry.npmjs.org/"
|
|
60
60
|
},
|
|
@@ -89,11 +89,12 @@
|
|
|
89
89
|
"react": "*",
|
|
90
90
|
"react-native": "*",
|
|
91
91
|
"react-native-gesture-handler": "^2.0.0",
|
|
92
|
-
"react-native-reanimated": "
|
|
92
|
+
"react-native-reanimated": "^4.0.0",
|
|
93
93
|
"react-native-worklets": ">=0.4.0"
|
|
94
94
|
},
|
|
95
95
|
"workspaces": [
|
|
96
|
-
"example"
|
|
96
|
+
"example",
|
|
97
|
+
"docs"
|
|
97
98
|
],
|
|
98
99
|
"packageManager": "yarn@4.11.0",
|
|
99
100
|
"react-native-builder-bob": {
|